* fix(apps): detect the no-database failure by code or message
The recovery flow for "db command against an app that has no database"
keyed on one business code (500002759). The server has since renumbered
that case to 400002465, which silently disabled the flow: users now see
the raw internal message about workspace / app-id mapping and lose the
cloud-development recovery steps entirely.
Nothing catches the regression. There is no compile error, the unit tests
compare against the same constant they set, and the dry-run E2E does not
exercise a real response — the failure only shows up against a server that
has already renumbered.
Detect on code OR message instead. Both known codes are kept, plus narrow
lowercase markers of the server's internal wording. The two channels have
opposite failure modes: a code is precise but gets renumbered, a message
survives renumbering but breaks on rewording or localization. Requiring
either to match means one channel changing degrades nothing, and only a
simultaneous change of both regresses.
Markers stay deliberately narrow. "no db branch" in particular must not
also swallow env-pull's "invalid db branch" case, which needs its own
hint; a comment records that widening them requires a test proving the
neighbours still pass through.
Classification and the cause chain are untouched: the helper still mutates
the problem in place and returns the same error value.
* test(apps): assert the full typed-error contract in no-database cases
Review feedback: the new subtests checked only Message and Hint, so a
change that reclassified the failure — or replaced the error value and
dropped the cause chain — would still have passed.
Each case now asserts Category, Subtype and Code are untouched by the
rewrite, and that the helper returns the same error value. Inputs use a
concrete subtype rather than Unknown, so a clobbered classification is
actually observable. One new case wraps a cause and asserts errors.Is
still finds it through the rewrite.
Also covers the predicate's defensive nil guard, which withAppsHint cannot
reach on its own (ProblemOf returns ok=false for untyped errors), closing
the two uncovered lines the coverage report flagged. Both withAppsHint and
isAppNoDatabaseError are now at 100%.
* fix(errclass): classify the db-domain business codes
Three codes reaching the Apps db commands were absent from the Spark
table, so BuildAPIError fell through to the CategoryAPI + SubtypeUnknown
catch-all and the envelope carried no usable classification.
"App has no database yet" registers as Validation / FailedPrecondition:
the app resolves fine and the request is well-formed, but a prerequisite
the caller must create first is missing, so retrying unchanged can never
succeed. This moves its exit code from 1 to 2 — "fix the state" rather
than "the call failed" — and a test pins that so a future
reclassification has to be deliberate. Two codes cover it because the
server renumbered the case into the 4xx band; the legacy one stays for
older servers.
"Table does not exist" registers as API / NotFound, an ordinary
missing-resource lookup with no exit-code change. SubtypeNotFound has no
APIHint default, which matters here: the Apps layer fills its
command-scoped hint only when the classifier left Hint empty, so a
context-free default would displace the more actionable one. A test
guards that too.
* feat: accept frontend app-type in apps +create
* feat: accept frontend app-type filter in apps +list
* docs: clarify frontend app-type handling in apps +init
* docs: note FRONTEND in queryAppType comment
* docs: add frontend app-type guidance to lark-apps skill
* docs: add frontend app-type to lark-apps command references
SKILL.md's routing table already covered frontend, but the per-command
reference docs still enumerated only html/full_stack. Update create/list/get
enum values, add a frontend local-dev section, and note frontend in the
release-create entry so agents document the third app type consistently.
* docs: address CodeRabbit feedback on frontend app-type refs
- create.md: state the app-type enum is matched exactly (lowercase), drop
the incorrect claim that the CLI normalizes case
- local-dev.md: scope database debugging to full_stack (frontend/html have
no DB) and add the +release-get finished-status poll to the frontend flow
* docs: align app-type enum in apps E2E coverage and test comments
The E2E coverage table and two test comments still described the --app-type
enum as html/full_stack after frontend was added. Update them to
html/frontend/full_stack for consistency; assertions are unaffected (they
match on substrings, not the full enum set).
* docs(lark-apps): cloud-dev honors routed app_type instead of hardcoding full_stack
The main SKILL.md router declares app_type and dev-method orthogonal and
routes no-database interactive tools to frontend, but cloud-dev/create
references still hardcoded `+create --app-type full_stack` for cloud
generation. This forced a frontend-routed request into a full_stack app
(unrecoverable since apps have no +delete).
Align with the 2026-07-24 design decision: cloud generation also splits by
database need — full_stack when persistence is required, frontend by default
when unstated. Verified on BOE that a frontend app runs the full cloud
session+chat pipeline to completed.
Both are registered, reachable business domains — `lark-cli attendance
user_tasks query` and `lark-cli mindnotes nodes create` run, and both appear in
`auth login --domain` — but neither had an entry in
service_descriptions.json. GetServiceTitle/GetServiceDescription returned "",
so buildDomainMeta fell back to the typed service spec, which carries one
string for both languages. The result was visible in `--help`: mindnotes
rendered its Chinese description in the English domain list, and attendance
rendered "attendance record query", a lowercase fragment shown in both locales.
Both keep their own scope namespaces (attendance:task:readonly,
mindnote:node:create/read) and stay independent auth domains, so no
auth_domain is set. whiteboard remains the only domain folded into docs.
TestGetDomainMetadata_HasTitleAndDescription could not catch this: it asserts
on buildDomainMeta's output, which the fallback had already made non-empty.
The new reconciliation test walks EmbeddedServicesTyped plus AllShortcuts and
asserts both languages resolve from the config itself, before any fallback.
EmbeddedServicesTyped is the overlay-free parse, so the test does not depend on
what remote_meta.json happens to hold on the machine — a domain that only ever
arrives via remote overlay stays out of its reach.
Wrap the Drive file-copy endpoint as drive +copy. Accept a document URL
(recommended) or bare token + --type for the source; the target takes a
folder token, a folder URL, or the my_space constant, which resolves the
caller's My Space root folder via the root-folder-meta endpoint (absent
from platform metadata, works for both user and bot). Repeatable --extra
key=value pairs are forwarded verbatim for special copy semantics (e.g.
target_type=docx to convert a legacy doc during copy). Source and folder
tokens are validated with validate.ResourceName before path
interpolation. Reject wiki URLs/tokens with a typed validation error
whose hint carries a wiki +node-copy command template using a fixed
<node-token> placeholder, because a Drive copy of a wiki-backed document
would land in Drive space instead of the wiki tree. In bot mode the CLI
auto-grants the current CLI user full_access on the new copy (same
behavior as +upload/+import), reporting the outcome in the
permission_grant output field without failing the copy.
Declare docs:document:copy (the narrowest scope in the endpoint's any-of
set) plus a conditional drive:drive.metadata:readonly for my_space
resolution. Cover the shortcut with unit tests, dry-run e2e and a
self-contained live workflow (upload -> copy -> download-verify ->
my_space copy -> cleanup), and register it in
tests/cli_e2e/drive/coverage.md. Route copy intents in the lark-drive
skill to the shortcut instead of the raw files copy service command.
* feat(sheets): add --ai-only to +formula-verify for AI formula status polling
BE-2: +formula-verify gains --ai-only, mapping to verify_formula tool
input ai_only=true. AI formulas (AI_WRITE / AI_CLASSIFY / …) compute
asynchronously; --ai-only is a single-shot polling probe (no built-in
wait/timeout) that skips the ordinary 7-Excel-error scan and returns
current AI-formula compute status. Coexists with --sheet-id/--range and
honors --exit-on-error.
BE-4: mirror sheet-skill-spec SoT docs (AI formula list in
lark-sheets-formula-translation, --ai-only + async polling section in
lark-sheets-formula-verify) and regenerate flag_defs_gen.go.
Spec source: active@6fe4ea7389c6d0631dc8279ee7506c357d0600325e28ffabdc97d9a66339e762
* docs(sheets): mirror enriched AI formula params + --ai-only verify wording
Mirror from sheet-skill-spec SoT (ee/sheet-skill-spec MR!55):
- formula-translation: full AI function param details (range rules,
AI_TRANSLATE language keys, AI_EXTRACT type, AI_CLASSIFY modes,
AI_INFER/AI_IMPORTDATA array semantics)
- formula-verify: clarify --ai-only convergence expectation, drop
implementation-leaking phrasing
* docs(sheets): mirror unified AI() formula docs from spec
Sync the lark-sheets AI formula skill from sheet-skill-spec (SoT) after
the product change that merges all AI formulas into one AI() function:
- formula-translation reference: single =AI(prompt, [range]) function with
syntax (incl. variadic =AI(part1, part2, ...) concatenation), per-case
usage table (translate / sentiment / classify / extract / summarize /
rewrite / generate / cleanup / keywords / multi-cell prompt), and
prompt best practices
- formula-verify reference + --ai-only flag def: unified AI() wording
- flag_defs_gen.go regenerated from flag-defs.json
- lark_sheet_formula_verify.go: drop stale AI_WRITE / AI_CLASSIFY names
in the ai_only comment, reference the unified AI() function
* docs(sheets): sync lark-sheets skill from spec
Mirror the lark-sheets generated artifacts from sheet-skill-spec dwc/lark-sheet-ai:
- update AI formula verification delivery guidance
- add read-data inspection/profile helper scripts
- refresh generated flag definitions for +formula-verify --ai-only
* docs(sheets): sync updated AI formula guidance
Mirror the latest lark-sheets generated docs from sheet-skill-spec dwc/lark-sheet-ai after c292420:
- clarify that #ERROR/readback issues can be formula-write escaping failures rather than AI compute failures
- recommend +cells-set JSON formula writes for AI() formulas containing commas and quotes
- document <=1000-row serial batching for AI formula copy-to-range workflows
* docs(sheets): add license headers to helper scripts
Add the repository-standard copyright and SPDX headers to the generated lark-sheets helper scripts so the license-header check passes.
* docs(sheets): regenerate sheet flag defs
Regenerate shortcuts/sheets/flag_defs_gen.go after the latest sheet-skill-spec sync updated flag-defs.json.
* docs(sheets): revert lark-sheets skill sync
#2194 extended `im +messages-search` to `AuthTypes: {user, bot}` but left the
affordance example and the skill reference asserting user-only, so the
dual-identity guard added by #2199 fails on main.
Scoped repos created by Miaoda apps failed day-2 `git pull/push`
authentication because a stale, higher-priority credential helper (e.g.
macOS osxkeychain) served cached credentials instead of the lark-cli
helper. This isolates the URL-scoped credential helper chain and closes
correctness gaps found in review:
1. Helper-order isolation. Git's empty-helper ("") reset only clears
helpers that parse BEFORE the lark-cli section; a generic
credential.helper (or one from a later [include]) that parses AFTER it
still participated in get/store/erase. SetHelper now verifies, via the
faithful parse-order oracle (`git config --includes --show-origin -z
--list`), that the lark-cli helper is last in fill order. If not, it
repositions the section to the end of the writable file; if a later
helper lives in a file we must not edit, it fails closed with a
FailedPrecondition error and restores the prior state.
2. Concurrent read-modify-write. The read-modify-write of the writable
global config file is serialized across lark-cli processes by a new
cross-process lockGlobalConfig, ordered after lockApp and before
lockURL. That lock cannot stop an unrelated (non-lark-cli) process, so
the helper rewrite also no longer clears the whole key: it deletes only
the values observed in the ownership snapshot by exact match, leaving a
helper a third party inserted during the write window in place. The
readback then diverges from the expected state and SetHelper fails
closed with the foreign value preserved, instead of a whole-key
--unset-all silently deleting it.
3. Recoverable teardown. UnsetHelper now deletes useHttpPath before the
helper list so a mid-teardown failure leaves a lark-cli-recoverable
residue (never a useHttpPath-only orphan that blocks re-init), and
removes only lark-cli values from a mixed list, reporting that a
third-party helper remains rather than silently no-op'ing. The state
taxonomy gains Foreign/Partial/Mixed to drive these paths.
Reword the note under the cross-domain search commands so agents ask
the user to clarify an attendee's type when a name cannot be resolved
or is ambiguous, instead of guessing the type from the name shape
(e.g. treating an unmatched room name as a bot).
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Bind Slides lint diagnostics to exact source XML nodes through stable xml_path references.
Preserve correct paths for filtered, anonymous, nested, and duplicate-ID elements. Detect duplicate source element IDs across slides and block release with actionable guidance.
Document the new locator contract and add regression coverage for path accuracy, metadata compatibility, and duplicate-ID handling.
Adds AI-friendly output path handling to `slides +screenshot`.
- Supports `--output` for a single screenshot in both existing-slide and XML render modes.
- Validates selector count, conflicting output flags, unsafe paths, directories, whitespace, and unsupported extensions with structured errors.
- Reconciles the requested filename with the server’s actual PNG/JPEG format and reports the final path through `output`, `requested_output`, and `output_adjusted`.
- Avoids replacing existing screenshots by appending `_2`, `_3`, and subsequent suffixes.
- Keeps `--output-dir` for multi-page screenshots and preserves `--output-name` for render mode.
- Updates the Slides Skill with explicit `--slide-number` / `--slide-id` guidance and task-scoped screenshot directories.
- Adds unit, dry-run E2E, and live workflow coverage for validation, path handling, format adjustment, and collision behavior.
* docs(slides): route skill guidance back to +replace-pages until whole-page rollout completes
Partially reverts the skill portion of #2143 (56fd29e6). lark-cli now ships
+update-slide, but the backend capability it depends on is still rolling out;
on environments without it every call answers an invalid-param error. The skill
was routing all whole-page and multi-page edits there, which turns a staged
backend rollout into a hard failure for every agent that follows the guidance.
Only the guidance moves: whole-page work routes to +replace-pages again, its
reference page is restored (with the SML namespace examples migrated to https,
a tree-wide change that landed while the page was deleted), and
lark-slides-update-slide.md is removed so nothing steers a caller to a command
the backend may refuse.
The binary is untouched. +update-slide stays available for environments that
already have the capability, and +replace-pages keeps working everywhere.
Re-apply the skill routing by reverting this commit once the rollout completes.
* docs(slides): override premature update-slide guidance
* docs(slides): route single-page rebuilds to replace-pages
XML written into a field name this shortcut does not accept — most often
"content", because <shape> nests a <content> child — was silently dropped,
so the part failed the required-field check and reported "requires
non-empty replacement". That reads as "the value is empty", which sends
callers rewriting the value instead of the key.
Reject fields outside the action's own set and name the field the caller
most likely meant, with a correct one-liner attached as a hint. Matching
folds case and separators so "Content", "newXml" and "block-id" resolve
too, while the whitelist itself stays exact: the API accepts only
snake_case, so "Replacement" must be rejected rather than slip through.
Only block_replace and block_insert parts are checked, so missing /
str_replace / unknown actions keep their existing errors, and an
actually-empty payload still reports the non-empty wording.
The alias list covers only names that plausibly carry a fragment. A shape
attribute like "fill" is deliberately absent: whoever writes it means
"recolor this block", not "here is my XML", so answering did-you-mean
"replacement" would be guessing. The unknown-field error already names the
valid set, which is true under either reading.
Docs carry the same constraint at the three points a caller can hit first:
SKILL.md, the +replace-slide reference (warning + counter-examples + error
table), and the read-modify-write workflow. The --parts flag description
now spells the field names out instead of eliding them behind "...".
Note: this tightens parsing. Extra keys inside a part used to be ignored;
they are now rejected.
Register <embed> in the bundled Slides SML 2.0 schema, document its SVG
contract, and teach the lint/schema validator to handle XSD wildcards and
embed geometry. Keep embedded SVG payloads out of IconPark validation so
foreign-namespaced <icon> elements inside a payload are not misreported.
- require a slide ID or slide number for screenshot requests
- reject explicitly empty slide IDs
- remove unreachable dry-run validation
- document full-deck screenshot batching
- add unit and dry-run E2E coverage
* fix(slides): preserve requested lint input path
* fix(slides): migrate SML namespace from HTTP to HTTPS
- Change canonical namespace to https://www.larkoffice.com/sml/2.0
in protocol schema, production code, docs, and tests
- Keep HTTP and /sml/2.0 as legacy readback compat in validator
- Fix sml_prefixed_tag check to cover all accepted SML namespaces
- Add regression test for legacy HTTP namespace acceptance
Add agent-friendly aliases for slides +screenshot while preserving the canonical flag behavior.
- Support presentation and slide selector aliases, including --presentation-id, --slides, --slide-ids, --slide-numbers, and --slide.
- Route digits-only --slide values to page numbers and other values to slide IDs.
- Merge and deduplicate same-type selectors, reject mixed ID/number requests, and report the caller’s actual flag names in structured validation errors.
- Clarify selector exclusivity in the screenshot reference.
- Add unit, dry-run E2E, and self-contained live E2E coverage for aliases, validation, screenshot output, and cleanup.
* feat(apps): friendly error for db commands on an app with no database
Server code 500002759 (a db command run against an app that has not
initialized a database yet) previously surfaced with an internal-term
message and no actionable next step.
withAppsHint now special-cases this code: it rewrites the message to a
user-facing "this app does not have a database yet" and forces a
cloud-development recovery hint (session-list/create -> chat -> poll
session-get -> retry). Because every apps db command funnels through
withAppsHint and this code is db-endpoint-specific, the whole db command
family is covered without per-command changes.
* fix(apps): generalize no-database retry hint and harden its test
Address review feedback on the no-database (500002759) recovery hint:
- The hint's final step named `+db-table-list` specifically, which would
redirect the operation when a different db command triggered it. Replace
it with "retry the original db command" so the recovery flow always
resumes whatever the caller ran.
- Strengthen the unit test to assert the helper returns the same error
value (cause chain preserved) and leaves Category/Subtype/Code intact,
not just the rewritten Message/Hint.
* docs(base): clarify unsupported capability boundaries
Consolidate the retained Base guidance into generic capability rules.
Document unsupported view appearance settings, the current single-table copy contract, and unsupported field handling without carrying over same-name rename behavior or evaluation-specific command traces.
* docs(base): address capability review
Avoid an incomplete closed list of supported view properties and scope table-copy guidance to the user's requested resources.
Replace evaluation-trace blacklists with three focused capability contract checks.
* docs(base): keep generic unsupported field guidance
* docs(base): refine capability boundary contracts
* test(base): remove Markdown capability contract test
1. Delete the prose-fragment assertions for the shipped Base skill guidance
Add an in-place whole-page slide update shortcut with validation, aliases, docs, unit tests, and dry-run E2E coverage.
Deprecate the superseded +replace-pages: the binary keeps the command working for a deprecation window, with the replacement named in its --help description and in a `deprecated` field on every output (dry-run, validate-only and real runs), while the skill no longer routes to it. Multi-page updates now call +update-slide once per page. The XML/revision helpers it shared with +add-slide / +delete-slide move to slides_shared.go so its eventual removal cannot break them.
+add-slide and +delete-slide now declare --presentation through the shared presentation-ref flag, so they accept the same alias spellings (--token, --url, ...) as every other slides shortcut.
* feat(docx): refine docs history revert guidance
Restructure the docs history reference to add explicit safety
constraints for overwrite-based recovery and a revision_id fallback
path when no matching history version exists.
- Replace the '安全流程' section with '安全约束' covering overwrite
block-ID/comment loss, warning/partial_success verification, and
preserving original error classification.
- Consolidate the revert workflow: locate the target record via
+history-list, use history_version_id (never revision_id) for
+history-revert, and treat only 'done' as success.
- Add a revision_id-based body recovery flow using
docs +fetch --revision-id and docs +update --command overwrite with
optimistic locking and post-overwrite verification.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* docs: complete revision restore command parameters
Sync incremental change from Codebase MR !1340 commit e1b0e649:
complete the +history-revert fallback command parameters
(--doc / --revision-id / --content / --reference-map) in the
lark-doc history restore reference.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* docs: harden full revision overwrite recovery
Sync incremental change from Codebase MR !1340 commit bd66e0b5:
harden the full revision overwrite recovery flow in the lark-doc
history restore reference.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* docs: exhaust revision history pagination
Sync incremental change from Codebase MR !1340 commit 567f71ba:
exhaust revision history pagination in the lark-doc history restore
reference.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
* docs: skip redundant revision overwrite
Sync incremental change from Codebase MR !1340 commit fe278e9c:
skip redundant revision overwrite in the lark-doc history restore
reference.
Co-authored-by: TRAE CLI <noreply@bytedance.com>
---------
Co-authored-by: yballul-bytedance <273011618+yballul-bytedance@users.noreply.github.com>
Co-authored-by: TRAE CLI <noreply@bytedance.com>
Add two single-page slide shortcuts on top of the raw
xml_presentation.slide create/delete APIs.
slides +add-slide appends or inserts one page into an existing
presentation. It accepts --presentation as a token, a /slides/ URL or a
/wiki/ URL (resolved via wiki.spaces.get_node and checked for
obj_type=slides), takes the page XML through --slide as a literal, @file
or stdin so the document never has to be escaped into JSON and then into
the shell, and auto-uploads <img src="@./local.png"> placeholders,
replacing them with the returned file_token. Omitting --before-slide-id
appends to the end; the field is dropped from the body rather than sent
empty, which the backend rejects as an unknown slide.
slides +delete-slide removes one page by slide_id with the same
--presentation resolution. It is deliberately Risk "write" rather than
the raw command's high-risk-write, so it does not require --yes: it
targets a single explicit page and the deck keeps its version history.
Both take one page at a time so that batching stays an explicit loop and
every call has an unambiguous outcome.
The image placeholder validation used by +create is extracted into a
shared helper so both commands fail before any API call when a referenced
file is missing, is not a regular file or exceeds the 20 MB upload limit.
Covered by unit tests and by dry-run e2e tests through the built binary,
which is the only layer that proves a full <slide> document survives flag
parsing intact. Reference docs are added for both commands and the
existing slides skill docs now route to them.
* feat(base): clarify dashboard multi-block read pattern and arrange constraints
- SKILL.md: update routing table to say "一个或多个图表计算结果" for +dashboard-block-get-data; add three new Dashboard guidance bullets covering arrange precision limits, creation verification shortcut, and multi-block serial read pattern
- lark-base-dashboard-block-get-data.md: add multi-block serial loop example with safety note that IDs must come verbatim from +dashboard-block-list
- lark-base-dashboard.md: tighten arrange caution block (x/y/w/h wording, two new caution bullets); fix verification guideline to not call +dashboard-block-get-data just to confirm creation; add multi-block read hint after scenario 5
* fix(base): 补充分页读取组件指引
* fix(base): 避免批量读取掩盖失败
---------
Co-authored-by: yballul-bytedance <273011618+yballul-bytedance@users.noreply.github.com>