@selesai/code 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/handoff-new.test.ts +8 -9
- package/dist/extensions/handoff-new.ts +6 -8
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +31 -0
- package/dist/extensions/pi-intercom/broker/spawn.ts +5 -3
- package/dist/extensions/pi-subagents/CHANGELOG.md +1367 -0
- package/dist/extensions/pi-subagents/README.md +602 -220
- package/dist/extensions/pi-subagents/banner.png +0 -0
- package/dist/extensions/pi-subagents/index.ts +1 -0
- package/dist/extensions/pi-subagents/install.mjs +3 -3
- package/dist/extensions/pi-subagents/package-lock.json +3405 -0
- package/dist/extensions/pi-subagents/package.json +26 -14
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +9 -9
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +3 -3
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +7 -7
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +6 -6
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +8 -8
- package/dist/extensions/pi-subagents/prompts/review-loop.md +13 -11
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +267 -176
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +89 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +19 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +218 -120
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +67 -13
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +2 -2
- package/dist/extensions/pi-subagents/src/agents/skills.ts +25 -12
- package/dist/extensions/pi-subagents/src/api/background-work.ts +197 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +17 -0
- package/dist/extensions/pi-subagents/src/api/delegation.ts +285 -0
- package/dist/extensions/pi-subagents/src/api/preflight.ts +399 -0
- package/dist/extensions/pi-subagents/src/extension/chain-validation.ts +165 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +7 -1
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +15 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -1
- package/dist/extensions/pi-subagents/src/extension/index.ts +113 -157
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +41 -4
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +45 -15
- package/dist/extensions/pi-subagents/src/extension/steering-notices.ts +35 -0
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -15
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -3
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +225 -29
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +11 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +8 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +452 -62
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +60 -9
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +198 -52
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +119 -21
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +67 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +16 -8
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +6 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +2 -11
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +260 -13
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +32 -6
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +171 -90
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +149 -88
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +14 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +33 -17
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +9 -1
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +38 -10
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +237 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +1297 -307
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +610 -0
- package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +26 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +22 -6
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +228 -140
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +452 -138
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +90 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +1001 -416
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +443 -132
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +38 -0
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +178 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +121 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +41 -93
- package/dist/extensions/pi-subagents/src/runs/shared/context-mode.ts +44 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +11 -9
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +36 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +44 -7
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +154 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +18 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +174 -54
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +38 -31
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +90 -5
- package/dist/extensions/pi-subagents/src/runs/shared/session-lease.ts +299 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +61 -6
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-budget.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +112 -7
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +14 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +149 -36
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +181 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +83 -0
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +11 -5
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +50 -4
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +63 -14
- package/dist/extensions/pi-subagents/src/shared/accessible-dir.ts +25 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +37 -7
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +18 -43
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +52 -0
- package/dist/extensions/pi-subagents/src/shared/env.ts +16 -0
- package/dist/extensions/pi-subagents/src/shared/file-system-retry.ts +47 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +28 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +123 -0
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +8 -5
- package/dist/extensions/pi-subagents/src/shared/settings.ts +9 -1
- package/dist/extensions/pi-subagents/src/shared/status-format.ts +7 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +522 -51
- package/dist/extensions/pi-subagents/src/shared/utils.ts +59 -53
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +604 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-json.ts +108 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +249 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +353 -345
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/selector.ts +147 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +265 -23
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +2 -2
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +428 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +362 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +472 -0
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +664 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +115 -31
- package/dist/extensions/pi-subagents/src/watchdog/change-signature.ts +220 -0
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +205 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +123 -0
- package/dist/extensions/pi-subagents/src/watchdog/lsp-diagnostics.ts +532 -0
- package/dist/extensions/pi-subagents/src/watchdog/model-selection.ts +167 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +117 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +433 -0
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +54 -0
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +298 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +714 -0
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +528 -0
- package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +155 -0
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +161 -0
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +188 -0
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +73 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +130 -2
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +439 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1632 -61
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +54 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +98 -1
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +179 -9
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +16 -9
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +6 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +589 -9
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +528 -21
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +227 -3
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +235 -7
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +172 -2
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1502 -18
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +993 -4
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +7 -1
- package/dist/extensions/pi-subagents/test/support/helpers.ts +32 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +59 -2
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +32 -6
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +25 -24
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +59 -35
- package/dist/extensions/pi-subagents/test/support/session-lease-child.mjs +42 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +703 -69
- package/dist/extensions/pi-subagents/test/unit/accessible-dir.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +15 -15
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +66 -66
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +478 -19
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +228 -7
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +198 -78
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +15 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +102 -7
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +439 -6
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +41 -1
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/chain-validation.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +82 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +46 -4
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +3 -1
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +9 -17
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +175 -3
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +10 -5
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +126 -6
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +219 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +913 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +13 -2
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +351 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +280 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +575 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +94 -1
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +157 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +367 -5
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +11 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +25 -3
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +341 -10
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +168 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +85 -7
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +243 -16
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +79 -36
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +174 -56
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +322 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +204 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +20 -12
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +117 -4
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +40 -7
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +48 -1
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +21 -3
- package/dist/extensions/pi-subagents/test/unit/selector.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/unit/session-lease.test.ts +326 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +101 -2
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +111 -1
- package/dist/extensions/pi-subagents/test/unit/spawn-budget.test.ts +124 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +6 -4
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +7 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/unit/steering-notices.test.ts +58 -0
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +25 -4
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +390 -25
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +837 -0
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +15 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +47 -1
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +56 -10
- package/dist/extensions/pi-subagents/test/unit/watchdog-change-signature.test.ts +387 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +110 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-emission-guard.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-lsp-diagnostics.test.ts +144 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-model-selection.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +76 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +863 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +290 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-turn-delta.test.ts +130 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +32 -4
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +5 -1
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +30 -0
- package/dist/extensions/question/batch.ts +39 -0
- package/dist/extensions/question/dialog-adapter.ts +23 -1
- package/dist/extensions/question/index.ts +237 -7
- package/dist/extensions/question/schemas.ts +28 -15
- package/dist/extensions/question/tests/batch.test.ts +26 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +23 -3
- package/dist/extensions/question/tui-adapter.ts +2 -3
- package/dist/extensions/question/types.ts +37 -0
- package/dist/extensions/question/ui-protocol.ts +3 -3
- package/dist/skills/batch-grill-me/SKILL.md +3 -1
- package/package.json +2 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const MAX_JSON_DEPTH = 64;
|
|
2
|
+
const MAX_JSON_ENTRIES = 100_000;
|
|
3
|
+
|
|
4
|
+
export type BoundedJsonClone =
|
|
5
|
+
| { ok: true; value: unknown; encodedBytes: number }
|
|
6
|
+
| { ok: false; reason: "invalid" | "too_large" };
|
|
7
|
+
|
|
8
|
+
/** Clone plain JSON data without invoking getters or toJSON hooks. */
|
|
9
|
+
export function cloneJsonWithinByteLimit(input: unknown, maxBytes: number): BoundedJsonClone {
|
|
10
|
+
let minimumBytes = 0;
|
|
11
|
+
let entries = 0;
|
|
12
|
+
const active = new WeakSet<object>();
|
|
13
|
+
|
|
14
|
+
const addMinimumBytes = (bytes: number): void => {
|
|
15
|
+
minimumBytes += bytes;
|
|
16
|
+
if (minimumBytes > maxBytes) throw new RangeError("too_large");
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const visit = (value: unknown, depth: number): unknown => {
|
|
20
|
+
if (depth > MAX_JSON_DEPTH) throw new TypeError("invalid");
|
|
21
|
+
if (value === null) {
|
|
22
|
+
addMinimumBytes(4);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === "boolean") {
|
|
26
|
+
addMinimumBytes(value ? 4 : 5);
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === "number") {
|
|
30
|
+
if (!Number.isFinite(value)) throw new TypeError("invalid");
|
|
31
|
+
const encoded = JSON.stringify(Object.is(value, -0) ? 0 : value);
|
|
32
|
+
addMinimumBytes(Buffer.byteLength(encoded, "utf8"));
|
|
33
|
+
return Object.is(value, -0) ? 0 : value;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "string") {
|
|
36
|
+
// Quoting and escaping can only increase the encoded size.
|
|
37
|
+
addMinimumBytes(Buffer.byteLength(value, "utf8") + 2);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
if (typeof value !== "object") throw new TypeError("invalid");
|
|
41
|
+
|
|
42
|
+
const object = value as object;
|
|
43
|
+
if (active.has(object)) throw new TypeError("invalid");
|
|
44
|
+
active.add(object);
|
|
45
|
+
try {
|
|
46
|
+
const prototype = Object.getPrototypeOf(object);
|
|
47
|
+
const keys = Reflect.ownKeys(object);
|
|
48
|
+
if (Array.isArray(object)) {
|
|
49
|
+
if (prototype !== Array.prototype) throw new TypeError("invalid");
|
|
50
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(object, "length");
|
|
51
|
+
if (!lengthDescriptor || !("value" in lengthDescriptor) || !Number.isSafeInteger(lengthDescriptor.value) || lengthDescriptor.value < 0) {
|
|
52
|
+
throw new TypeError("invalid");
|
|
53
|
+
}
|
|
54
|
+
const length = lengthDescriptor.value as number;
|
|
55
|
+
addMinimumBytes(2 + Math.max(0, length - 1));
|
|
56
|
+
const output: unknown[] = [];
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
if (typeof key === "symbol") throw new TypeError("invalid");
|
|
59
|
+
if (key === "length") continue;
|
|
60
|
+
if (!/^(?:0|[1-9][0-9]*)$/.test(key) || Number(key) >= length) throw new TypeError("invalid");
|
|
61
|
+
}
|
|
62
|
+
for (let index = 0; index < length; index++) {
|
|
63
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, String(index));
|
|
64
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new TypeError("invalid");
|
|
65
|
+
entries++;
|
|
66
|
+
if (entries > MAX_JSON_ENTRIES) throw new TypeError("invalid");
|
|
67
|
+
output.push(visit(descriptor.value, depth + 1));
|
|
68
|
+
}
|
|
69
|
+
return output;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError("invalid");
|
|
73
|
+
addMinimumBytes(2 + Math.max(0, keys.length - 1));
|
|
74
|
+
const output: Record<string, unknown> = {};
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
if (typeof key === "symbol") throw new TypeError("invalid");
|
|
77
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
78
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new TypeError("invalid");
|
|
79
|
+
entries++;
|
|
80
|
+
if (entries > MAX_JSON_ENTRIES) throw new TypeError("invalid");
|
|
81
|
+
addMinimumBytes(Buffer.byteLength(JSON.stringify(key), "utf8") + 1);
|
|
82
|
+
Object.defineProperty(output, key, {
|
|
83
|
+
value: visit(descriptor.value, depth + 1),
|
|
84
|
+
enumerable: true,
|
|
85
|
+
configurable: true,
|
|
86
|
+
writable: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return output;
|
|
90
|
+
} finally {
|
|
91
|
+
active.delete(object);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const value = visit(input, 0);
|
|
97
|
+
const encoded = JSON.stringify(value);
|
|
98
|
+
if (encoded === undefined) return { ok: false, reason: "invalid" };
|
|
99
|
+
const encodedBytes = Buffer.byteLength(encoded, "utf8");
|
|
100
|
+
if (encodedBytes > maxBytes) return { ok: false, reason: "too_large" };
|
|
101
|
+
return { ok: true, value, encodedBytes };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
reason: error instanceof RangeError && error.message === "too_large" ? "too_large" : "invalid",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SUBAGENT_DELEGATION_PROTOCOL_VERSION,
|
|
3
|
+
SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION,
|
|
4
|
+
type SubagentDelegationRequest,
|
|
5
|
+
type SubagentDelegationV2Request,
|
|
6
|
+
} from "../api/delegation.ts";
|
|
7
|
+
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
8
|
+
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
9
|
+
import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
|
|
10
|
+
import { cloneJsonWithinByteLimit } from "./delegation-json.ts";
|
|
11
|
+
|
|
12
|
+
export type SubagentDelegationParseResult =
|
|
13
|
+
| { ok: true; request: SubagentDelegationRequest | SubagentDelegationV2Request }
|
|
14
|
+
| { ok: false; version?: 1 | 2; requestId?: string; ownerRunId?: string; nodeId?: string; error: string };
|
|
15
|
+
|
|
16
|
+
const v1SupportedFields = new Set([
|
|
17
|
+
"version",
|
|
18
|
+
"requestId",
|
|
19
|
+
"agent",
|
|
20
|
+
"task",
|
|
21
|
+
"context",
|
|
22
|
+
"cwd",
|
|
23
|
+
"model",
|
|
24
|
+
"timeoutMs",
|
|
25
|
+
"turnBudget",
|
|
26
|
+
"toolBudget",
|
|
27
|
+
"skill",
|
|
28
|
+
"output",
|
|
29
|
+
"outputMode",
|
|
30
|
+
"outputSchema",
|
|
31
|
+
"agentContract",
|
|
32
|
+
"acceptance",
|
|
33
|
+
"artifacts",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const v2SupportedFields = new Set([
|
|
37
|
+
"version",
|
|
38
|
+
"requestId",
|
|
39
|
+
"ownerRunId",
|
|
40
|
+
"nodeId",
|
|
41
|
+
"agent",
|
|
42
|
+
"task",
|
|
43
|
+
"context",
|
|
44
|
+
"cwd",
|
|
45
|
+
"model",
|
|
46
|
+
"thinking",
|
|
47
|
+
"timeoutMs",
|
|
48
|
+
"turnBudget",
|
|
49
|
+
"toolBudget",
|
|
50
|
+
"skill",
|
|
51
|
+
"artifacts",
|
|
52
|
+
"result",
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const v2ThinkingLevels = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
56
|
+
const MAX_SCHEMA_BYTES = 64 * 1024;
|
|
57
|
+
const MAX_V2_TASK_BYTES = 1024 * 1024;
|
|
58
|
+
const MAX_V2_CWD_BYTES = 32 * 1024;
|
|
59
|
+
const MAX_V2_SHORT_TEXT_BYTES = 1024;
|
|
60
|
+
const MAX_V2_SKILL_ENTRIES = 256;
|
|
61
|
+
const MAX_V2_SKILL_AGGREGATE_BYTES = 64 * 1024;
|
|
62
|
+
|
|
63
|
+
function nonEmptyString(value: unknown): value is string {
|
|
64
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function validateId(value: unknown): string | undefined {
|
|
68
|
+
if (!nonEmptyString(value) || value.length > 256 || /[\r\n]/.test(value)) return undefined;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function validateSharedFields(
|
|
73
|
+
value: Record<string, unknown>,
|
|
74
|
+
identity: { requestId: string; version?: 1 | 2; ownerRunId?: string; nodeId?: string },
|
|
75
|
+
): SubagentDelegationParseResult | undefined {
|
|
76
|
+
if (!nonEmptyString(value.agent)) return { ok: false, ...identity, error: "Delegation agent must be a non-empty string." };
|
|
77
|
+
if (!nonEmptyString(value.task)) return { ok: false, ...identity, error: "Delegation task must be a non-empty string." };
|
|
78
|
+
if (value.context !== "fresh" && value.context !== "fork") {
|
|
79
|
+
return { ok: false, ...identity, error: "Delegation context must be fresh or fork." };
|
|
80
|
+
}
|
|
81
|
+
if (!nonEmptyString(value.cwd)) return { ok: false, ...identity, error: "Delegation cwd must be a non-empty string." };
|
|
82
|
+
if (value.model !== undefined && !nonEmptyString(value.model)) {
|
|
83
|
+
return { ok: false, ...identity, error: "model must be a non-empty string when provided." };
|
|
84
|
+
}
|
|
85
|
+
if (value.timeoutMs !== undefined && (typeof value.timeoutMs !== "number" || !Number.isInteger(value.timeoutMs) || value.timeoutMs < 1)) {
|
|
86
|
+
return { ok: false, ...identity, error: "timeoutMs must be an integer >= 1." };
|
|
87
|
+
}
|
|
88
|
+
if (identity.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION && value.timeoutMs !== undefined && value.timeoutMs > 2_147_483_647) {
|
|
89
|
+
return { ok: false, ...identity, error: "timeoutMs must be <= 2147483647 for delegation v2." };
|
|
90
|
+
}
|
|
91
|
+
const turnBudget = resolveTurnBudgetConfig(value.turnBudget);
|
|
92
|
+
if (turnBudget.error) return { ok: false, ...identity, error: turnBudget.error };
|
|
93
|
+
if (value.toolBudget && typeof value.toolBudget === "object" && !Array.isArray(value.toolBudget)) {
|
|
94
|
+
const unsupportedToolBudgetField = Object.keys(value.toolBudget).find((key) => key !== "soft" && key !== "hard" && key !== "block");
|
|
95
|
+
if (unsupportedToolBudgetField) {
|
|
96
|
+
return { ok: false, ...identity, error: `toolBudget.${unsupportedToolBudgetField} is not supported.` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const toolBudget = validateToolBudgetConfig(
|
|
100
|
+
value.toolBudget,
|
|
101
|
+
"toolBudget",
|
|
102
|
+
identity.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION ? { minimumHard: 0 } : undefined,
|
|
103
|
+
);
|
|
104
|
+
if (toolBudget.error) return { ok: false, ...identity, error: toolBudget.error };
|
|
105
|
+
if (value.skill !== undefined) {
|
|
106
|
+
const validSkill = typeof value.skill === "boolean"
|
|
107
|
+
|| nonEmptyString(value.skill)
|
|
108
|
+
|| (Array.isArray(value.skill) && value.skill.length > 0 && value.skill.every(nonEmptyString));
|
|
109
|
+
if (!validSkill) {
|
|
110
|
+
return { ok: false, ...identity, error: "skill must be a boolean, non-empty string, or non-empty string array." };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (value.artifacts !== undefined && typeof value.artifacts !== "boolean") {
|
|
114
|
+
return { ok: false, ...identity, error: "artifacts must be a boolean." };
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseV2(value: Record<string, unknown>, requestId: string): SubagentDelegationParseResult {
|
|
120
|
+
const ownerRunId = validateId(value.ownerRunId);
|
|
121
|
+
if (!ownerRunId) {
|
|
122
|
+
return { ok: false, version: 2, requestId, error: "Delegation ownerRunId must be a non-empty string of at most 256 characters without newlines." };
|
|
123
|
+
}
|
|
124
|
+
const nodeId = validateId(value.nodeId);
|
|
125
|
+
if (!nodeId) {
|
|
126
|
+
return { ok: false, version: 2, requestId, ownerRunId, error: "Delegation nodeId must be a non-empty string of at most 256 characters without newlines." };
|
|
127
|
+
}
|
|
128
|
+
const identity = { version: 2 as const, requestId, ownerRunId, nodeId };
|
|
129
|
+
const unsupportedField = Object.keys(value).find((key) => !v2SupportedFields.has(key));
|
|
130
|
+
if (unsupportedField) return { ok: false, ...identity, error: `Unsupported delegation field: ${unsupportedField}.` };
|
|
131
|
+
const sharedError = validateSharedFields(value, identity);
|
|
132
|
+
if (sharedError) return sharedError;
|
|
133
|
+
if (Buffer.byteLength(value.task as string, "utf8") > MAX_V2_TASK_BYTES) {
|
|
134
|
+
return { ok: false, ...identity, error: "Delegation task exceeds 1 MiB when UTF-8 encoded." };
|
|
135
|
+
}
|
|
136
|
+
if (Buffer.byteLength(value.cwd as string, "utf8") > MAX_V2_CWD_BYTES) {
|
|
137
|
+
return { ok: false, ...identity, error: "Delegation cwd exceeds 32 KiB when UTF-8 encoded." };
|
|
138
|
+
}
|
|
139
|
+
if (Buffer.byteLength(value.agent as string, "utf8") > MAX_V2_SHORT_TEXT_BYTES) {
|
|
140
|
+
return { ok: false, ...identity, error: "Delegation agent exceeds 1 KiB when UTF-8 encoded." };
|
|
141
|
+
}
|
|
142
|
+
if (typeof value.model === "string" && Buffer.byteLength(value.model, "utf8") > MAX_V2_SHORT_TEXT_BYTES) {
|
|
143
|
+
return { ok: false, ...identity, error: "Delegation model exceeds 1 KiB when UTF-8 encoded." };
|
|
144
|
+
}
|
|
145
|
+
const skillEntries = typeof value.skill === "string" ? [value.skill] : Array.isArray(value.skill) ? value.skill as string[] : [];
|
|
146
|
+
if (skillEntries.length > MAX_V2_SKILL_ENTRIES) {
|
|
147
|
+
return { ok: false, ...identity, error: "Delegation skill supports at most 256 entries." };
|
|
148
|
+
}
|
|
149
|
+
if (skillEntries.some((entry) => Buffer.byteLength(entry, "utf8") > MAX_V2_SHORT_TEXT_BYTES)) {
|
|
150
|
+
return { ok: false, ...identity, error: "Delegation skill entry exceeds 1 KiB when UTF-8 encoded." };
|
|
151
|
+
}
|
|
152
|
+
if (skillEntries.reduce((total, entry) => total + Buffer.byteLength(entry, "utf8"), 0) > MAX_V2_SKILL_AGGREGATE_BYTES) {
|
|
153
|
+
return { ok: false, ...identity, error: "Delegation skill entries exceed 64 KiB in aggregate when UTF-8 encoded." };
|
|
154
|
+
}
|
|
155
|
+
if (value.thinking !== undefined && (typeof value.thinking !== "string" || !v2ThinkingLevels.has(value.thinking))) {
|
|
156
|
+
return { ok: false, ...identity, error: "thinking must be one of off, minimal, low, medium, high, xhigh, or max." };
|
|
157
|
+
}
|
|
158
|
+
if (!value.result || typeof value.result !== "object" || Array.isArray(value.result)) {
|
|
159
|
+
return { ok: false, ...identity, error: "result must be { kind: \"text\" } or { kind: \"structured\", schema: object }." };
|
|
160
|
+
}
|
|
161
|
+
const result = value.result as Record<string, unknown>;
|
|
162
|
+
let structuredSchema: Record<string, unknown> | undefined;
|
|
163
|
+
if (result.kind === "text") {
|
|
164
|
+
const unsupportedResultField = Object.keys(result).find((key) => key !== "kind");
|
|
165
|
+
if (unsupportedResultField) return { ok: false, ...identity, error: `result.${unsupportedResultField} is not supported for text results.` };
|
|
166
|
+
} else if (result.kind === "structured") {
|
|
167
|
+
const unsupportedResultField = Object.keys(result).find((key) => key !== "kind" && key !== "schema");
|
|
168
|
+
if (unsupportedResultField) return { ok: false, ...identity, error: `result.${unsupportedResultField} is not supported for structured results.` };
|
|
169
|
+
if (!result.schema || typeof result.schema !== "object" || Array.isArray(result.schema)) {
|
|
170
|
+
return { ok: false, ...identity, error: "result.schema must be a JSON Schema object." };
|
|
171
|
+
}
|
|
172
|
+
const inspectedSchema = cloneJsonWithinByteLimit(result.schema, MAX_SCHEMA_BYTES);
|
|
173
|
+
if (!inspectedSchema.ok) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
...identity,
|
|
177
|
+
error: inspectedSchema.reason === "too_large"
|
|
178
|
+
? "result.schema exceeds 64 KiB when encoded."
|
|
179
|
+
: "result.schema must be plain JSON data.",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
structuredSchema = inspectedSchema.value as Record<string, unknown>;
|
|
183
|
+
} else {
|
|
184
|
+
return { ok: false, ...identity, error: "result.kind must be text or structured." };
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
ok: true,
|
|
188
|
+
request: {
|
|
189
|
+
...value,
|
|
190
|
+
result: structuredSchema
|
|
191
|
+
? { kind: "structured", schema: structuredSchema }
|
|
192
|
+
: { kind: "text" },
|
|
193
|
+
} as unknown as SubagentDelegationV2Request,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function parseV1(value: Record<string, unknown>, requestId: string): SubagentDelegationParseResult {
|
|
198
|
+
const unsupportedField = Object.keys(value).find((key) => !v1SupportedFields.has(key));
|
|
199
|
+
if (unsupportedField) return { ok: false, requestId, error: `Unsupported delegation field: ${unsupportedField}.` };
|
|
200
|
+
const sharedError = validateSharedFields(value, { requestId });
|
|
201
|
+
if (sharedError) return sharedError;
|
|
202
|
+
if (value.output !== undefined && typeof value.output !== "boolean" && !nonEmptyString(value.output)) {
|
|
203
|
+
return { ok: false, requestId, error: "output must be a boolean or non-empty string." };
|
|
204
|
+
}
|
|
205
|
+
if (value.outputMode !== undefined && value.outputMode !== "inline" && value.outputMode !== "file-only") {
|
|
206
|
+
return { ok: false, requestId, error: "outputMode must be inline or file-only." };
|
|
207
|
+
}
|
|
208
|
+
if (value.outputMode === "file-only" && !nonEmptyString(value.output)) {
|
|
209
|
+
return { ok: false, requestId, error: 'outputMode "file-only" requires output to be a non-empty path.' };
|
|
210
|
+
}
|
|
211
|
+
if (value.outputSchema !== undefined && (!value.outputSchema || typeof value.outputSchema !== "object" || Array.isArray(value.outputSchema))) {
|
|
212
|
+
return { ok: false, requestId, error: "outputSchema must be a JSON Schema object." };
|
|
213
|
+
}
|
|
214
|
+
if (value.agentContract !== undefined) {
|
|
215
|
+
if (!value.agentContract || typeof value.agentContract !== "object" || Array.isArray(value.agentContract)) return { ok: false, requestId, error: "agentContract must be an object." };
|
|
216
|
+
const contract = value.agentContract as Record<string, unknown>;
|
|
217
|
+
if (contract.version !== 1 || Object.keys(contract).some((key) => key !== "version")) return { ok: false, requestId, error: "agentContract must be { version: 1 }." };
|
|
218
|
+
}
|
|
219
|
+
const acceptanceErrors = validateAcceptanceInput(value.acceptance);
|
|
220
|
+
if (acceptanceErrors.length > 0) return { ok: false, requestId, error: acceptanceErrors.join(" ") };
|
|
221
|
+
return { ok: true, request: value as unknown as SubagentDelegationRequest };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function parseSubagentDelegationRequest(data: unknown): SubagentDelegationParseResult {
|
|
225
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
226
|
+
return { ok: false, error: "Delegation request must be an object." };
|
|
227
|
+
}
|
|
228
|
+
const value = data as Record<string, unknown>;
|
|
229
|
+
const requestId = validateId(value.requestId);
|
|
230
|
+
if (value.version !== SUBAGENT_DELEGATION_PROTOCOL_VERSION && value.version !== SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
|
|
231
|
+
return {
|
|
232
|
+
...(requestId ? { requestId } : {}),
|
|
233
|
+
ok: false,
|
|
234
|
+
error: `Unsupported delegation protocol version: ${String(value.version)}.`,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (!requestId) {
|
|
238
|
+
return { ok: false, error: "Delegation requestId must be a non-empty string of at most 256 characters without newlines." };
|
|
239
|
+
}
|
|
240
|
+
if (value.version === SUBAGENT_DELEGATION_V2_PROTOCOL_VERSION) {
|
|
241
|
+
// Preserve the v1 parser's historical diagnostic for a v1-shaped payload
|
|
242
|
+
// whose version alone was changed to 2.
|
|
243
|
+
if (!Object.hasOwn(value, "ownerRunId") && !Object.hasOwn(value, "nodeId") && !Object.hasOwn(value, "result")) {
|
|
244
|
+
return { ok: false, version: 2, requestId, error: "Unsupported delegation protocol version: 2." };
|
|
245
|
+
}
|
|
246
|
+
return parseV2(value, requestId);
|
|
247
|
+
}
|
|
248
|
+
return parseV1(value, requestId);
|
|
249
|
+
}
|