HAR-9329 - fix pdfjs icons in final build - #410
Merged
Conversation
superdoc-bot Bot
pushed a commit
that referenced
this pull request
Jul 14, 2026
β¦teps/run tiers (#410) * feat(sdk): custom-actions authoring kit β canonical ActionSpec with steps/run tiers (SD-3553/SD-3317) Customers extend the core preset with their own named actions using only installed packages: defineAction/extendPreset/composePreset (Node) and define_action/extend_preset/compose_preset (Python), now public API. - steps tier (recommended): declarative composition of built-in core actions with {{arg}} templating; inherits target resolution, receipts, verification - run tier: native function in the caller's process with synthesized truth-telling receipts (pre/post revision, partialMutation, recovery) - source tier kept internal (execute_code path), unadvertised pending the safety-flag work - coherent excludeActions across tools/prompt/dispatch for custom presets (custom names handled by the wrapper, built-ins forwarded to the base) - provider dialects fixed for vercel (flat inputSchema shape) - cross-language parity enforced: templating text forms, defaults, receipt shapes, and a drift-guard test parsing Node's action registry - build-superdoc-action agent skill (SKILL.md + live-verified example packs in Python and JavaScript) at packages/sdk/skills/, distributed via GitHub Cross-reviewed by two independent models; all findings fixed and re-verified (Node 297 tests, Python 38 kit tests + live smoke, e2e against the CLI host). * fix(sdk): PR #410 review round β compose coherence, reserved names, defaults ordering - includeCoreActions is now a dispatch boundary too: guessed/stale calls to built-ins outside the allowlist are refused (excludedBy: includeCoreActions) - composed enum rebuild injects only ACTIVE custom names, so excludeActions naming a custom action can no longer leak it back into the enum - shared splitCustomExclusions/throwExcludedAction helpers replace the duplicated wrapper logic in extendPreset/composePreset (both languages) - custom action names may not shadow reserved tool names (superdoc_execute_code etc.) - schema defaults are applied BEFORE validation, so required-with-default args are satisfiable by the default, as documented - Python PresetDescriptor protocol declares exclude_actions on get_system_prompt - examples: policy_pack.py explicit-null comments crash fixed (parity with the JS twin), table_borders.py encodes lineWeightPt > 0, SKILL.md notes pagination * fix(sdk): compose allowlist narrows description/args natively; CI host-test guard - includeCoreActions now derives an exclusion list forwarded to the base, so core narrows the enum, grouped description, AND advertised argument properties in one place β the hand-rolled filterAgentActionEnum/_filter_enum rebuilders are deleted (they narrowed only the enum, teaching the model actions the preset refuses) - python tests that exercise the real core preset (host proxy) are marked requires_host and skip when no CLI binary is resolvable β fixes CI SDK validate on runners without the platform companion package - rename vars -> arg_values in the python runners (shadowed builtin, Ruff A001) - reserved-tool-names drift guard: python set verified against Node's AGENT_TOOL_NAMES catalog declaration * fix(sdk): synthesize perform_action for custom-only presets When the base preset drops superdoc_perform_action entirely (empty includeCoreActions allowlist, or every built-in excluded), active custom actions were advertised in the prompt and dispatchable β but carried by no tool. Both wrappers now synthesize a provider-shaped custom-only definition (enum + unioned args + description) so a curated custom-only preset stays callable. Tests in both languages. * fix(sdk): grammar in synthesized perform_action description (a action -> an action) * fix(sdk): standalone presets do not route customs through perform_action In standalone mode custom actions are advertised as their own provider tools, so the perform_action route to them was an unadvertised-but-working path β violating the advertised-surface == dispatchable-surface boundary this kit enforces everywhere else. The route is now gated to merged mode; standalone calls fall through to the base, which rejects the unknown action name. Tests in both languages. * fix(sdk): python enum validation treats explicit None as a value, not an absence Node validates enum args whenever the key is present (explicit null is rejected with a teaching error); the Python mirror's 'is not None' guard let {'mode': None} through β and past schema defaults, which only fill MISSING keys β handing the action a live None. Python now validates on key presence (Node parity). Explicit-null rejection pinned by tests in both languages. * refactor(sdk): drop source tier from the kit; move footnotes fixture to tests The kit now exposes two execution tiers β steps (declarative) and run (native). The in-host source tier (JS via superdoc_execute_code) is removed until the code-act execution path ships with its safety envelope; one comment in define.ts's module header records that rationale. Removes buildActionCode + runSourceAction (Node) and build_action_code + the source dispatch branch + dead _map_receipt (Python), and the source-specific tests. The footnotes pack was only ever a test fixture but lived in src/actions/ and shipped in dist. It's now a run-tier fixture under __tests__/fixtures (Node) and tests/ (Python), so it no longer ships; the unit + e2e/smoke suites import it from the new location. Also strips SD-xxxx ticket numbers from code comments. Verified: Node 299 tests + tsc + build + e2e; Python custom-actions 42 + smoke; run-tier footnotes exercised against the real CLI host. * fix(sdk): keep an action-named argument in standalone custom actions runCustomAction stripped the 'action' key unconditionally, but that key is the superdoc_perform_action discriminator ONLY on the merged route. A standalone custom action is its own tool, where 'action' can be a legitimate declared argument β stripping it dropped the value before defaults/validation, so the tool advertised as callable then failed or ran with the arg missing. The strip now happens only on the perform_action route (both languages); tests pin a standalone action whose schema requires an 'action' argument. * fix(sdk): narrow the composed tool catalog with includeCoreActions getTools and getSystemPrompt already narrow the superdoc_perform_action enum to the includeCoreActions allowlist, but getCatalog returned the base's full surface β so getToolCatalog(composedPreset) reported built-in actions the preset's enum, prompt, and dispatch all refuse. The catalog's perform_action row now narrows the same way (both languages); tests assert an out-of-allowlist built-in is absent from the composed catalog. * refactor(sdk): rename skill to superdoc-custom-actions; async + cleanup + preset-name guidance Rename build-superdoc-action -> superdoc-custom-actions (folder + frontmatter), matching the vendor-prefixed, domain-noun convention (Anthropic authoring guidance + marketplace practice); groups with future SuperDoc skills and reads clearly as an install id. Also folds in skill-content fixes validated by live customer-simulation runs: - async run-tier guidance: an app on AsyncSuperDocClient / dispatch_async needs an run that awaits every doc.* call (a sync body yields 'coroutine' object is not subscriptable) β Β§1 async example + Β§6 pitfall - 'clean up when you finish' step: remove verification scratch (opened/saved .docx, .superdoc-state/.superdoc-cli-state, out.docx, test __pycache__), keeping the app's real sample docs - default extended-preset id is custom_superdoc_preset (not a codename), in the Β§2 instruction and every example * fix(sdk): narrow composed catalog row beyond the enum (Node + Python) composePreset / compose_preset get_catalog narrowed only the superdoc_perform_action action enum while spreading the base row's full description and every action's argument properties. With includeCoreActions the enum, system prompt, and dispatch all narrowed, but getToolCatalog() still advertised inputs (e.g. create_table's rows/columns) for actions the preset refuses β breaking the "advertised == dispatchable" invariant for catalog-driven UIs/validators. Rebuild the catalog's perform_action row from the SAME narrowing getTools drives, so enum, description, and argument properties shrink together with one implementation (no second copy to drift): - Node: export buildPerformActionDefinition and build the row from the included built-ins (canonical ACTION_NAMES_LIST order). - Python: source the row from the base's own get_tools(exclude_actions=...) (the host builder); when every built-in is excluded the host drops the tool, so drop the catalog row too (custom actions stay their own rows). Tests: strengthen the catalog-narrowing tests to assert args + description narrow, not just the enum, and add a no-custom case asserting the catalog row equals the advertised getTools tool. Node 43/43 (bun), Python 45/45. Addresses PR #410 review comment (narrow composed catalog schemas beyond enum). * fix(sdk): don't inherit surface exclusions into custom steps (Node + Python) A steps-tier custom action composes built-in actions. When a toolkit hides a built-in via excludeActions (to stop the model calling it directly) but still advertises a custom action whose steps compose that built-in, the surface exclusion was forwarded into the internal step dispatch β core's defense-in-depth then refused the step, so the advertised custom action returned a failed receipt. Surface exclusions govern the MODEL's direct calls (enforced at the top-level dispatch), not an author's curated composition. Strip them before internal step dispatch: - Node: runStepsAction strips excludeActions from invokeOptions. - Python: _run_steps_action(_async) no longer forwards exclude_actions into the base step dispatch; the now-dead param is dropped from _run_custom_action too. The top-level guard is unchanged: a direct model call to the hidden built-in is still refused. Regression tests in both languages assert (a) an advertised steps action composing an excluded built-in still succeeds, (b) internal steps receive no exclusions, and (c) a direct call to the hidden built-in is refused. Verified: Node 44/44 + e2e 2/2, Python 46/46 + smoke. Addresses PR #410 review comment (do not pass surface exclusions into custom steps). * feat(sdk): one-call createAgentToolkit({actions}) + custom-actions hardening createAgentToolkit / create_agent_toolkit now accept `actions` (plus optional `base` / `includeCoreActions`) and build an ephemeral extended preset internally β custom actions with no registerPreset and no preset id to thread through dispatch. The advanced extendPreset / composePreset + `preset` path still works unchanged. Hardening from review + live QA: - type the run() doc handle (BoundDocApi) instead of unknown - reject Zod / non-JSON-Schema `input` with a clear error instead of silently dropping its types - reject conflicting arg names, but only on incompatible type/enum β a shared built-in arg name with an extra description/default is allowed; the compare is order-insensitive - authoring skill: teach the one-call path as primary (register/extend demoted to an advanced fallback), add a flat dispatch example and the run-tier failed-receipt shape, add a shared-arg-name pitfall; fix the reference_pack occurrence falsy-zero bug and an example list-key typo Tests: Node 51 unit + 2 e2e, Python 51 unit + smoke. Verified end-to-end in the async collaborative demo app. Claude-Session: https://claude.ai/code/session_01SraPwksmphffxhhY6Mp9iL * fix(sdk): snapshot custom-action arrays so the preset surface stays immutable extendPreset/composePreset aliased the caller's `actions` array β getTools read it live while the byName dispatch map was snapshotted at construction, so mutating the array afterward (hot-reload / action discovery) desynced the advertised surface from the dispatchable one. Copy the array at construction, matching Python (which already snapshots via list()/tuple()). Node-only; Python was already immune. Regression test: mutating the caller's array after build leaves the advertised surface unchanged. Node 52, Python 51. Claude-Session: https://claude.ai/code/session_01SraPwksmphffxhhY6Mp9iL * fix(sdk): tighten arg-conflict check; Python provider validation + parity; dispatch tests Review follow-ups on the custom-actions kit: - Arg-name conflict check now flags ANY structural difference, not just type/enum: a differing default, limit, pattern, nested shape, and the one-sided-enum case all conflict. Only documentation (description / title / examples / $comment) may differ, so reusing a built-in arg name with your own description still works. Node + Python. - Python: restore up-front provider validation in the one-call `actions` path (it was building tools without the check `choose_tools` does), and align input-schema handling with Node (wrap a bare properties bag). - Tests: run a custom action through the toolkit's RETURNED dispatch (Node e2e + Python), plus one-sided-enum-rejected and description-only-allowed cases. - Skill: reference_pack `caseSensitive` drops its default to match the built-in arg; the section 6 pitfall documents the stricter shared-arg rule. Node 53 unit + 3 e2e, Python 53. Claude-Session: https://claude.ai/code/session_01SraPwksmphffxhhY6Mp9iL * fix(sdk): guard custom-action inputSchema without `properties` An open object schema like `{ type: 'object', additionalProperties: true }` passes isJsonSchemaObject (which only checks `type`) but carries no `properties`, so the merge loop's `Object.entries(action.inputSchema.properties)` threw and took down the whole preset's getTools(). - coerceInputSchema normalizes a valid object schema missing `properties` to {} β honors the declared-required type and fixes every consumer at the source. - The merge loop now guards with `?? {}`, matching its twin (synthesizePerformAction) and the Python mirror, which already guards everywhere. - Test: an open-schema action normalizes to {} and merges via chooseTools without throwing. Node 54 unit. Claude-Session: https://claude.ai/code/session_01SraPwksmphffxhhY6Mp9iL Ported-From-Source-Repo: superdoc/orbit Ported-From-Source-Commit: 47d05fe0d25b2026ade83e143e3a8d6f595c2c0c Ported-Public-Prefix: superdoc/public
superdoc-bot Bot
pushed a commit
that referenced
this pull request
Jul 15, 2026
### Chores - 1.21.0 [skip ci] - 0.22.0 [skip ci] - 1.44.2 [skip ci] ### Bug Fixes - preserve empty toolkit base values - drop empty perform-action catalog rows - publish GitHub releases for stable versions only ### Features - add package-scoped AI release notes to all six public packages (#508) - custom-actions authoring kit β canonical ActionSpec with steps/run tiers (#410)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.