@selesai/code 0.13.15 → 0.13.18
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/CHANGELOG.md +37 -0
- package/README.md +2 -14
- package/dist/bun/cli.d.ts +3 -1
- package/dist/bun/cli.js +3 -9
- package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
- package/dist/bun/sandbox-env-setup.d.ts +1 -0
- package/dist/bun/sandbox-env-setup.js +3 -0
- package/dist/cli/auth-command.d.ts +23 -0
- package/dist/cli/auth-command.js +102 -0
- package/dist/cli/config-selector.js +2 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.js +11 -0
- package/dist/cli.js +2 -14
- package/dist/config.d.ts +3 -2
- package/dist/config.js +9 -4
- package/dist/core/agent-session-runtime.js +15 -7
- package/dist/core/agent-session.d.ts +2 -6
- package/dist/core/agent-session.js +15 -4
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -1
- package/dist/core/extensions/loader.js +3 -0
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/http-dispatcher.js +2 -0
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.js +4 -0
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/model-config.d.ts +15 -0
- package/dist/core/model-config.js +3 -0
- package/dist/core/model-runtime.d.ts +1 -0
- package/dist/core/model-runtime.js +5 -2
- package/dist/core/provider-composer.js +8 -2
- package/dist/core/session-export.d.ts +3 -0
- package/dist/core/session-export.js +31 -0
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.js +40 -13
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.js +4 -2
- package/dist/core/system-prompt.js +10 -10
- package/dist/core/tools/bash.js +3 -117
- package/dist/core/tools/edit.d.ts +1 -12
- package/dist/core/tools/edit.js +5 -163
- package/dist/core/tools/find.js +4 -55
- package/dist/core/tools/grep.js +4 -60
- package/dist/core/tools/ls.js +4 -49
- package/dist/core/tools/read.d.ts +4 -0
- package/dist/core/tools/read.js +10 -122
- package/dist/core/tools/renderers/bash.d.ts +11 -0
- package/dist/core/tools/renderers/bash.js +129 -0
- package/dist/core/tools/renderers/edit.d.ts +22 -0
- package/dist/core/tools/renderers/edit.js +166 -0
- package/dist/core/tools/renderers/find.d.ts +9 -0
- package/dist/core/tools/renderers/find.js +63 -0
- package/dist/core/tools/renderers/grep.d.ts +9 -0
- package/dist/core/tools/renderers/grep.js +68 -0
- package/dist/core/tools/renderers/index.d.ts +27 -0
- package/dist/core/tools/renderers/index.js +46 -0
- package/dist/core/tools/renderers/ls.d.ts +9 -0
- package/dist/core/tools/renderers/ls.js +57 -0
- package/dist/core/tools/renderers/read.d.ts +10 -0
- package/dist/core/tools/renderers/read.js +128 -0
- package/dist/core/tools/renderers/write.d.ts +9 -0
- package/dist/core/tools/renderers/write.js +152 -0
- package/dist/core/tools/write.js +5 -146
- package/dist/extensions/capability-gateway/index.ts +12 -1
- package/dist/extensions/capability-gateway/integration.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
- package/dist/extensions/pi-hermes-memory/README.md +16 -12
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
- package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
- package/dist/extensions/pi-hermes-memory/package.json +4 -4
- package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
- package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
- package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
- package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +16 -2
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
- package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
- package/dist/extensions/pi-intercom/index.ts +1 -1
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
- package/dist/extensions/pi-intercom/project-agent.ts +1 -6
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
- package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
- package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
- package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
- package/dist/extensions/pi-subagents/README.md +3 -3
- package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/docs/agents.md +32 -16
- package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
- package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
- package/dist/extensions/pi-subagents/docs/missions.md +2 -0
- package/dist/extensions/pi-subagents/docs/models.md +58 -1
- package/dist/extensions/pi-subagents/docs/observability.md +50 -12
- package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
- package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
- package/dist/extensions/pi-subagents/package-lock.json +747 -177
- package/dist/extensions/pi-subagents/package.json +6 -4
- package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
- package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
- package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
- package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
- package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
- package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
- package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
- package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
- package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
- package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
- package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
- package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
- package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
- package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
- package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
- package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
- package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
- package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
- package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
- package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
- package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
- package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
- package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
- package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
- package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
- package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
- package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
- package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
- package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
- package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
- package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
- package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
- package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
- package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
- package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
- package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
- package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
- package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
- package/dist/extensions/pi-web-agent/src/types.ts +1 -1
- package/dist/extensions/pi-zentui/README.md +17 -0
- package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
- package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
- package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
- package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
- package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
- package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
- package/dist/extensions/pi-zentui/package-lock.json +277 -517
- package/dist/extensions/pi-zentui/package.json +7 -4
- package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
- package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
- package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
- package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
- package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
- package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
- package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
- package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
- package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
- package/dist/index.d.ts +1 -1
- package/dist/main.js +4 -1
- package/dist/modes/interactive/chat-viewport.d.ts +19 -0
- package/dist/modes/interactive/chat-viewport.js +27 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
- package/dist/modes/interactive/components/assistant-message.js +30 -13
- package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
- package/dist/modes/interactive/components/custom-editor.js +39 -1
- package/dist/modes/interactive/components/first-time-setup.js +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
- package/dist/modes/interactive/components/markdown-transform.js +18 -0
- package/dist/modes/interactive/components/model-selector.js +19 -17
- package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -20
- package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
- package/dist/modes/interactive/components/settings-submenu.js +164 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
- package/dist/modes/interactive/components/status-indicator.js +10 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.js +75 -18
- package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
- package/dist/modes/interactive/components/tool-execution.js +35 -33
- package/dist/modes/interactive/components/trust-selector.js +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts +13 -15
- package/dist/modes/interactive/interactive-mode.js +144 -80
- package/dist/modes/interactive/session-share.d.ts +15 -0
- package/dist/modes/interactive/session-share.js +183 -0
- package/dist/modes/interactive/theme/dark.json +2 -1
- package/dist/modes/interactive/theme/light.json +2 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +5 -0
- package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
- package/dist/modes/interactive/theme/theme-json.js +129 -0
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -5
- package/dist/modes/interactive/theme/theme.js +18 -119
- package/dist/modes/interactive/tui-renderer.d.ts +20 -0
- package/dist/modes/interactive/tui-renderer.js +65 -0
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/management-http.d.ts +24 -0
- package/dist/utils/management-http.js +53 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.js +8 -0
- package/dist/utils/tools-manager.d.ts +1 -0
- package/dist/utils/tools-manager.js +43 -21
- package/docs/docs.json +0 -4
- package/package.json +4 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
- package/dist/skills/implanger/SKILL.md +0 -70
- package/dist/skills/output/SKILL.md +0 -49
- package/dist/skills/planger/SKILL.md +0 -167
- package/dist/skills/workflow/SKILL.md +0 -95
- package/docs/workflows.md +0 -18
- /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
|
@@ -5,11 +5,20 @@ import { Worker } from "node:worker_threads";
|
|
|
5
5
|
import { DEFAULT_GLOBAL_CONCURRENCY_LIMIT, Semaphore } from "../runs/shared/parallel-utils.ts";
|
|
6
6
|
import { HOST_STEP_MAX_COUNT } from "../runs/shared/host-step-status.ts";
|
|
7
7
|
import { classifyTaskMutationIntent } from "../runs/shared/task-intent.ts";
|
|
8
|
-
import
|
|
8
|
+
import { describeGateAcceptanceConflict } from "../runs/shared/acceptance.ts";
|
|
9
|
+
import type { AcceptanceRecoveryMetadata, HostStepNode, SingleResult } from "../shared/types.ts";
|
|
9
10
|
import { normalizeWorkflowHostCommandParams, type WorkflowHostCommandParams, type WorkflowHostCommandResult } from "./host-command.ts";
|
|
10
11
|
|
|
11
12
|
const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
13
|
+
const BASE_REF_VALIDATION_ERROR = "baseRef must be a valid Git ref: use HEAD or a supported named ref (for example, refs/heads/main). Full 40/64-character commit IDs and revision expressions are unsupported.";
|
|
14
|
+
function validGitRef(ref: unknown): ref is string {
|
|
15
|
+
if (typeof ref !== "string" || !ref || ref === "@" || Buffer.byteLength(ref, "utf-8") > 1024 || ref.startsWith("/") || ref.endsWith("/") || ref.includes("//") || ref.includes("..") || ref.includes("@{")) return false;
|
|
16
|
+
if (/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(ref)) return false;
|
|
17
|
+
if (/[[\]\\~^:?*\u0000-\u0020\u007f]/u.test(ref) || ref.endsWith(".") || ref.endsWith(".lock")) return false;
|
|
18
|
+
return ref.split("/").every((component) => component.length > 0 && component !== "." && component !== ".." && !component.startsWith(".") && !component.endsWith(".") && !component.endsWith(".lock"));
|
|
19
|
+
}
|
|
12
20
|
const requireFromPackage = createRequire(import.meta.url);
|
|
21
|
+
const WORKFLOW_ASSEMBLY_FLUSH_TIMEOUT_MS = 5_000;
|
|
13
22
|
|
|
14
23
|
export interface WorkflowScriptValidationError {
|
|
15
24
|
message: string;
|
|
@@ -49,6 +58,12 @@ let suppressNativePromiseConsumption = 0;
|
|
|
49
58
|
const activeNativePromises = [];
|
|
50
59
|
const pending = new Map();
|
|
51
60
|
const runKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
61
|
+
function validGitRef(ref) {
|
|
62
|
+
if (typeof ref !== "string" || !ref || ref === "@" || new TextEncoder().encode(ref).length > 1024 || ref.startsWith("/") || ref.endsWith("/") || ref.includes("//") || ref.includes("..") || ref.includes("@{")) return false;
|
|
63
|
+
if (/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu.test(ref)) return false;
|
|
64
|
+
if (/[[\]\\~^:?*\u0000-\u0020\u007f]/u.test(ref) || ref.endsWith(".") || ref.endsWith(".lock")) return false;
|
|
65
|
+
return ref.split("/").every((component) => component.length > 0 && component !== "." && component !== ".." && !component.startsWith(".") && !component.endsWith(".") && !component.endsWith(".lock"));
|
|
66
|
+
}
|
|
52
67
|
const trackedPromiseTrackers = new WeakMap();
|
|
53
68
|
const trackedPromiseTargets = new WeakMap();
|
|
54
69
|
let nativePromiseTrackers = new WeakMap();
|
|
@@ -61,6 +76,12 @@ function stableRunJson(value) {
|
|
|
61
76
|
return JSON.stringify(value) ?? "undefined";
|
|
62
77
|
}
|
|
63
78
|
|
|
79
|
+
function canonicalRunParams(params) {
|
|
80
|
+
if (params.gate === undefined || params.acceptance !== false) return params;
|
|
81
|
+
const { acceptance: _acceptance, ...withoutAcceptance } = params;
|
|
82
|
+
return withoutAcceptance;
|
|
83
|
+
}
|
|
84
|
+
|
|
64
85
|
function isDirectWorkflowScriptPromiseHandlerCall() {
|
|
65
86
|
const stack = new Error().stack;
|
|
66
87
|
if (typeof stack !== "string") return false;
|
|
@@ -432,7 +453,7 @@ function validateLaneSpecs(laneSpecs) {
|
|
|
432
453
|
validateLaneStageBounds(validationParams, stageLabel);
|
|
433
454
|
validateRunCall(generatedKey, validationParams, stageLabel, validationFingerprints);
|
|
434
455
|
const existingFingerprint = runFingerprints.get(generatedKey);
|
|
435
|
-
if (existingFingerprint !== undefined && (resume === "previous" || existingFingerprint !== stableRunJson(params))) {
|
|
456
|
+
if (existingFingerprint !== undefined && (resume === "previous" || existingFingerprint !== stableRunJson(canonicalRunParams(params)))) {
|
|
436
457
|
throw new Error("runs.lanes generated child key '" + generatedKey + "' is already used with incompatible launch params.");
|
|
437
458
|
}
|
|
438
459
|
stages.push({ key: stageKey, generatedKey, resume, params });
|
|
@@ -601,6 +622,19 @@ function validateLaneMetadata(value, label, workflowKey) {
|
|
|
601
622
|
}
|
|
602
623
|
}
|
|
603
624
|
|
|
625
|
+
function describeGateAcceptanceConflict(gate, acceptance) {
|
|
626
|
+
const render = (value) => {
|
|
627
|
+
let encoded;
|
|
628
|
+
try {
|
|
629
|
+
encoded = JSON.stringify(value) ?? String(value);
|
|
630
|
+
} catch {
|
|
631
|
+
encoded = String(value);
|
|
632
|
+
}
|
|
633
|
+
return encoded.length > 120 ? encoded.slice(0, 120) + "..." : encoded;
|
|
634
|
+
};
|
|
635
|
+
return " Both fields were present: gate=" + render(gate) + " acceptance=" + render(acceptance) + ".";
|
|
636
|
+
}
|
|
637
|
+
|
|
604
638
|
function validateRunCall(key, params, label, fingerprints) {
|
|
605
639
|
if (typeof key !== "string" || !runKeyPattern.test(key)) throw new Error(label + " has an invalid key.");
|
|
606
640
|
if (hostKeys.has(key)) throw new Error("Workflow key '" + key + "' is already used by runs.host.");
|
|
@@ -611,9 +645,10 @@ function validateRunCall(key, params, label, fingerprints) {
|
|
|
611
645
|
}
|
|
612
646
|
if (Object.prototype.hasOwnProperty.call(params, "clarify")) throw new Error(label + " does not support clarify UI.");
|
|
613
647
|
if (params.worktree !== undefined && typeof params.worktree !== "boolean") throw new Error(label + " worktree must be true or false.");
|
|
648
|
+
if (params.baseRef !== undefined && (typeof params.baseRef !== "string" || !validGitRef(params.baseRef))) throw new Error(label + " baseRef must be a valid Git ref: use HEAD or a supported named ref (for example, refs/heads/main). Full 40/64-character commit IDs and revision expressions are unsupported.");
|
|
614
649
|
validateLaneMetadata(params.lane, label + " lane", key);
|
|
615
650
|
if (params.gate !== undefined && (typeof params.gate !== "string" || !params.gate.trim())) throw new Error(label + " gate must be a non-empty command string.");
|
|
616
|
-
if (params.gate !== undefined && params.acceptance !== undefined) throw new Error(label + " gate cannot be combined with acceptance; use one gate command or acceptance.verify.");
|
|
651
|
+
if (params.gate !== undefined && params.acceptance !== undefined && params.acceptance !== false) throw new Error(label + " gate cannot be combined with acceptance; use one gate command or acceptance.verify." + describeGateAcceptanceConflict(params.gate, params.acceptance));
|
|
617
652
|
if (params.gate !== undefined && params.resume !== undefined) throw new Error(label + " gate is not supported with retained resume.");
|
|
618
653
|
if (params.extensionBindings !== undefined && params.resume !== undefined) throw new Error(label + " extensionBindings is not supported with retained resume; resume uses the original retained child binding.");
|
|
619
654
|
if (params.resume !== undefined && typeof params.resume !== "string") {
|
|
@@ -630,7 +665,7 @@ function validateRunCall(key, params, label, fingerprints) {
|
|
|
630
665
|
if (params.resume !== undefined && (typeof params.task !== "string" || !params.task.trim())) throw new Error(label + " resume requires a non-empty task follow-up.");
|
|
631
666
|
validateExtensionBindings(params.extensionBindings, label);
|
|
632
667
|
assertJsonValue(params, label + " params");
|
|
633
|
-
const fingerprint = stableRunJson(params);
|
|
668
|
+
const fingerprint = stableRunJson(canonicalRunParams(params));
|
|
634
669
|
const existing = fingerprints.get(key);
|
|
635
670
|
if (existing !== undefined && existing !== fingerprint) throw new Error("Duplicate workflow key '" + key + "' used with incompatible launch params.");
|
|
636
671
|
fingerprints.set(key, fingerprint);
|
|
@@ -1070,15 +1105,17 @@ export interface RunWorkflowScriptOptions {
|
|
|
1070
1105
|
oneUsePermit?: { claim: (key: string) => string | undefined };
|
|
1071
1106
|
timeoutMs?: number;
|
|
1072
1107
|
signal?: AbortSignal;
|
|
1108
|
+
/** Let an async workflow flush pure result assembly after reload once every child is terminal. */
|
|
1109
|
+
continueAfterAbortWhenChildrenSettled?: (abortError: Error) => boolean;
|
|
1073
1110
|
/** Maximum children executing concurrently within this workflow. Defaults to 20. */
|
|
1074
1111
|
globalConcurrencyLimit?: number;
|
|
1075
1112
|
admit?: (calls: Array<{ key: string; params: Record<string, unknown> }>) => void | Promise<void>;
|
|
1076
1113
|
launch: (key: string, params: Record<string, unknown>, signal: AbortSignal, admission: { admitted: boolean; batch: boolean }) => Promise<WorkflowScriptChildResult>;
|
|
1077
|
-
resolveResume?: (reference: WorkflowReceiptResumeReference, signal: AbortSignal) => string | WorkflowResolvedResumeReference | Promise<string | WorkflowResolvedResumeReference>;
|
|
1114
|
+
resolveResume?: (reference: WorkflowReceiptResumeReference | string, signal: AbortSignal, index?: number) => string | WorkflowResolvedResumeReference | Promise<string | WorkflowResolvedResumeReference>;
|
|
1078
1115
|
status: (keyOrRunId: string, signal: AbortSignal) => Promise<WorkflowScriptChildResult>;
|
|
1079
1116
|
steer?: (key: string, message: string, options: WorkflowSteerOptions, signal: AbortSignal) => Promise<WorkflowSteerResult>;
|
|
1080
1117
|
host?: (key: string, params: WorkflowHostCommandParams, signal: AbortSignal) => Promise<WorkflowHostCommandResult>;
|
|
1081
|
-
onHostStep?: (hostStep:
|
|
1118
|
+
onHostStep?: (hostStep: HostStepNode) => void;
|
|
1082
1119
|
state?: {
|
|
1083
1120
|
get: (key: string) => unknown | Promise<unknown>;
|
|
1084
1121
|
set: (key: string, value: unknown) => void | Promise<void>;
|
|
@@ -1354,6 +1391,12 @@ function stableJson(value: unknown): string {
|
|
|
1354
1391
|
return JSON.stringify(value) ?? "undefined";
|
|
1355
1392
|
}
|
|
1356
1393
|
|
|
1394
|
+
function canonicalRunParams(params: Record<string, unknown>): Record<string, unknown> {
|
|
1395
|
+
if (params.gate === undefined || params.acceptance !== false) return params;
|
|
1396
|
+
const { acceptance: _acceptance, ...withoutAcceptance } = params;
|
|
1397
|
+
return withoutAcceptance;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1357
1400
|
function validateKey(value: unknown, owner = "runs.run"): string {
|
|
1358
1401
|
if (typeof value !== "string" || !KEY_PATTERN.test(value)) {
|
|
1359
1402
|
throw new Error(`${owner} key must be 1-128 characters using letters, numbers, '.', '_' or '-', and start with a letter or number.`);
|
|
@@ -1460,7 +1503,7 @@ function definitelyNonJson(node: AstNode, normalizeUndefined = false): string |
|
|
|
1460
1503
|
if (node.type === "ObjectExpression" && Array.isArray(node.properties)) {
|
|
1461
1504
|
const values = new Map<string, AstNode>();
|
|
1462
1505
|
for (const property of node.properties) {
|
|
1463
|
-
if (!astNode(property) || property.type !== "Property" || !astNode(property.value)) return undefined;
|
|
1506
|
+
if (!astNode(property) || property.type !== "Property" || !astNode(property.value) || property.kind !== "init") return undefined;
|
|
1464
1507
|
const key = staticPropertyKey(property);
|
|
1465
1508
|
if (key === undefined) return undefined;
|
|
1466
1509
|
values.set(key, property.value);
|
|
@@ -1489,6 +1532,26 @@ function directObjectPropertyValue(node: AstNode, name: string): AstNode | undef
|
|
|
1489
1532
|
return value;
|
|
1490
1533
|
}
|
|
1491
1534
|
|
|
1535
|
+
function validateStaticBaseRef(params: AstNode, owner: string): WorkflowScriptValidationError[] {
|
|
1536
|
+
if (params.type !== "ObjectExpression" || !Array.isArray(params.properties)) return [];
|
|
1537
|
+
// Inspect the final definition only. A later spread or unknown key may overwrite it.
|
|
1538
|
+
for (let index = params.properties.length - 1; index >= 0; index--) {
|
|
1539
|
+
const property = params.properties[index];
|
|
1540
|
+
if (!astNode(property) || property.type !== "Property") return [];
|
|
1541
|
+
const key = staticPropertyKey(property);
|
|
1542
|
+
if (key === undefined) return [];
|
|
1543
|
+
if (key !== "baseRef") continue;
|
|
1544
|
+
if (property.kind !== "init" || !astNode(property.value)) return [];
|
|
1545
|
+
const valueNode = property.value;
|
|
1546
|
+
const value = literalString(valueNode);
|
|
1547
|
+
if ((value !== undefined || valueNode.type === "Literal") && !validGitRef(value)) {
|
|
1548
|
+
return [{ message: `${owner} ${BASE_REF_VALIDATION_ERROR}`, ...nodeLocation(valueNode) }];
|
|
1549
|
+
}
|
|
1550
|
+
return [];
|
|
1551
|
+
}
|
|
1552
|
+
return [];
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1492
1555
|
function directRunsAllKeys(call: AstNode): Array<{ key: string; node: AstNode }> {
|
|
1493
1556
|
const args = Array.isArray(call.arguments) ? call.arguments : [];
|
|
1494
1557
|
const items = astNode(args[0]) && args[0].type === "ArrayExpression" && Array.isArray(args[0].elements) ? args[0].elements : [];
|
|
@@ -1530,6 +1593,7 @@ export function validateWorkflowScript(script: string): WorkflowScriptValidation
|
|
|
1530
1593
|
const key = literalString(keyNode);
|
|
1531
1594
|
if (keyNode && key !== undefined && !KEY_PATTERN.test(key)) errors.push({ message: "runs.run key must be 1-128 characters using letters, numbers, '.', '_' or '-', and start with a letter or number.", ...nodeLocation(keyNode) });
|
|
1532
1595
|
if (astNode(args[1])) {
|
|
1596
|
+
errors.push(...validateStaticBaseRef(args[1], "runs.run"));
|
|
1533
1597
|
const message = definitelyNonJson(args[1]);
|
|
1534
1598
|
if (message) errors.push({ message: `runs.run params are invalid: ${message}.`, ...nodeLocation(args[1]) });
|
|
1535
1599
|
}
|
|
@@ -1539,6 +1603,7 @@ export function validateWorkflowScript(script: string): WorkflowScriptValidation
|
|
|
1539
1603
|
const args = Array.isArray(node.arguments) ? node.arguments : [];
|
|
1540
1604
|
if (astNode(args[0]) && args[0].type === "ArrayExpression" && Array.isArray(args[0].elements)) {
|
|
1541
1605
|
for (const item of args[0].elements) if (astNode(item)) {
|
|
1606
|
+
errors.push(...validateStaticBaseRef(item, "runs.all item"));
|
|
1542
1607
|
const message = definitelyNonJson(item);
|
|
1543
1608
|
if (message) errors.push({ message: `runs.all item params are invalid: ${message}.`, ...nodeLocation(item) });
|
|
1544
1609
|
}
|
|
@@ -1614,7 +1679,7 @@ function resolveWorkflowParserEntry(): string {
|
|
|
1614
1679
|
}
|
|
1615
1680
|
}
|
|
1616
1681
|
|
|
1617
|
-
const AUTO_RESUME_PARAM_KEYS = ["acceptance", "agentContract", "index", "intercomBridge", "label", "lane", "maxRuntimeMs", "output", "outputMode", "outputSchema", "phase", "skill", "skills", "task", "timeoutMs", "toolBudget", "worktree"] as const;
|
|
1682
|
+
const AUTO_RESUME_PARAM_KEYS = ["acceptance", "agentContract", "baseRef", "index", "intercomBridge", "label", "lane", "maxRuntimeMs", "output", "outputMode", "outputSchema", "phase", "skill", "skills", "task", "timeoutMs", "toolBudget", "worktree"] as const;
|
|
1618
1683
|
|
|
1619
1684
|
function isZeroUsage(usage: unknown): boolean {
|
|
1620
1685
|
if (!isRecord(usage)) return false;
|
|
@@ -1682,6 +1747,9 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1682
1747
|
let acceptanceRecoveryBarrier: { key: string } | undefined;
|
|
1683
1748
|
let settled = false;
|
|
1684
1749
|
let finishing = false;
|
|
1750
|
+
let assemblyAbortRequested = false;
|
|
1751
|
+
let assemblyFlushTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1752
|
+
let abortError: Error | undefined;
|
|
1685
1753
|
|
|
1686
1754
|
const partial = (): Omit<WorkflowScriptResult, "value"> => ({ emits, console: consoleEntries, trace, children: childOrder.flatMap((key) => {
|
|
1687
1755
|
const child = children.get(key);
|
|
@@ -1706,7 +1774,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1706
1774
|
console.error("Workflow onLanePlan callback failed:", error);
|
|
1707
1775
|
}
|
|
1708
1776
|
};
|
|
1709
|
-
const hostStepChanged = (hostStep:
|
|
1777
|
+
const hostStepChanged = (hostStep: HostStepNode) => {
|
|
1710
1778
|
try {
|
|
1711
1779
|
options.onHostStep?.(hostStep);
|
|
1712
1780
|
} catch (error) {
|
|
@@ -1750,6 +1818,10 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1750
1818
|
const finish = (outcome: { value: unknown } | { error: Error & { workflowErrorKind?: unknown } }) => {
|
|
1751
1819
|
if (settled || finishing) return;
|
|
1752
1820
|
finishing = true;
|
|
1821
|
+
if (assemblyFlushTimer !== undefined) {
|
|
1822
|
+
clearTimeout(assemblyFlushTimer);
|
|
1823
|
+
assemblyFlushTimer = undefined;
|
|
1824
|
+
}
|
|
1753
1825
|
childController.abort("error" in outcome ? outcome.error : new Error("Workflow script completed."));
|
|
1754
1826
|
void Promise.allSettled([...steers.values(), ...hostCalls.values()].map(({ promise }) => promise)).then(() => {
|
|
1755
1827
|
if (settled) return;
|
|
@@ -1781,6 +1853,26 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1781
1853
|
: typeof signalReason === "string"
|
|
1782
1854
|
? new Error(signalReason)
|
|
1783
1855
|
: new Error("Workflow script aborted.");
|
|
1856
|
+
if (finishing) return;
|
|
1857
|
+
abortError = error;
|
|
1858
|
+
const allChildrenSettled = launches.size > 0
|
|
1859
|
+
&& [...launches.keys()].every((key) => children.has(key));
|
|
1860
|
+
let mayFlushAssembly = false;
|
|
1861
|
+
try {
|
|
1862
|
+
mayFlushAssembly = options.continueAfterAbortWhenChildrenSettled?.(error) === true;
|
|
1863
|
+
} catch (callbackError) {
|
|
1864
|
+
const callbackMessage = callbackError instanceof Error ? callbackError.message : String(callbackError);
|
|
1865
|
+
return finish({ error: new Error(`Workflow assembly flush eligibility failed: ${callbackMessage}`) });
|
|
1866
|
+
}
|
|
1867
|
+
if (mayFlushAssembly && allChildrenSettled) {
|
|
1868
|
+
// A reloaded async workflow may already be past its last child launch.
|
|
1869
|
+
// Keep the worker alive for pure result assembly, but abort the child
|
|
1870
|
+
// signal so any later launch or side effect cannot use stale context.
|
|
1871
|
+
assemblyAbortRequested = true;
|
|
1872
|
+
childController.abort(error);
|
|
1873
|
+
assemblyFlushTimer = setTimeout(() => finish({ error }), WORKFLOW_ASSEMBLY_FLUSH_TIMEOUT_MS);
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1784
1876
|
for (const key of launches.keys()) {
|
|
1785
1877
|
if (children.has(key)) continue;
|
|
1786
1878
|
stoppedLaunches.add(key);
|
|
@@ -1873,6 +1965,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1873
1965
|
return;
|
|
1874
1966
|
}
|
|
1875
1967
|
if (message.type !== "call" || typeof message.callId !== "number" || typeof message.method !== "string" || !isRecord(message.args)) return;
|
|
1968
|
+
if (assemblyAbortRequested) return finish({ error: abortError ?? new Error("Workflow context was replaced or reloaded.") });
|
|
1876
1969
|
|
|
1877
1970
|
const respond = (promise: Promise<unknown>, responsePath?: string, onBoundaryError?: (error: unknown) => void) => {
|
|
1878
1971
|
void promise.then(
|
|
@@ -1977,7 +2070,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
1977
2070
|
if (!options.host) return respond(Promise.reject(new Error("runs.host is unavailable in this host context.")));
|
|
1978
2071
|
if (hostCalls.size >= HOST_STEP_MAX_COUNT) return respond(Promise.reject(new Error(`workflowScript supports at most ${HOST_STEP_MAX_COUNT} runs.host calls.`)));
|
|
1979
2072
|
const startedAt = Date.now();
|
|
1980
|
-
const startedStep:
|
|
2073
|
+
const startedStep: HostStepNode = {
|
|
1981
2074
|
version: 1,
|
|
1982
2075
|
kind: "host-step",
|
|
1983
2076
|
monitorKind: "command",
|
|
@@ -2038,7 +2131,7 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
2038
2131
|
}
|
|
2039
2132
|
return result;
|
|
2040
2133
|
});
|
|
2041
|
-
const fingerprint = stableJson(params);
|
|
2134
|
+
const fingerprint = stableJson(canonicalRunParams(params));
|
|
2042
2135
|
const existing = launches.get(key);
|
|
2043
2136
|
if (existing) {
|
|
2044
2137
|
if (existing.fingerprint !== fingerprint) return respond(Promise.reject(new Error(`Duplicate workflow key '${key}' used with incompatible launch params.`)));
|
|
@@ -2059,11 +2152,14 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
2059
2152
|
if (params.worktree !== undefined && typeof params.worktree !== "boolean") {
|
|
2060
2153
|
return respond(Promise.reject(new Error(`runs.run('${key}') worktree must be true or false.`)));
|
|
2061
2154
|
}
|
|
2155
|
+
if (params.baseRef !== undefined && (typeof params.baseRef !== "string" || !validGitRef(params.baseRef))) {
|
|
2156
|
+
return respond(Promise.reject(new Error(`runs.run('${key}') ${BASE_REF_VALIDATION_ERROR}`)));
|
|
2157
|
+
}
|
|
2062
2158
|
if (params.gate !== undefined && (typeof params.gate !== "string" || !params.gate.trim())) {
|
|
2063
2159
|
return respond(Promise.reject(new Error(`runs.run('${key}') gate must be a non-empty command string.`)));
|
|
2064
2160
|
}
|
|
2065
|
-
if (params.gate !== undefined && params.acceptance !== undefined) {
|
|
2066
|
-
return respond(Promise.reject(new Error(`runs.run('${key}') gate cannot be combined with acceptance; use one gate command or acceptance.verify.`)));
|
|
2161
|
+
if (params.gate !== undefined && params.acceptance !== undefined && params.acceptance !== false) {
|
|
2162
|
+
return respond(Promise.reject(new Error(`runs.run('${key}') gate cannot be combined with acceptance; use one gate command or acceptance.verify.` + describeGateAcceptanceConflict(params.gate, params.acceptance))));
|
|
2067
2163
|
}
|
|
2068
2164
|
if (params.gate !== undefined && params.resume !== undefined) {
|
|
2069
2165
|
return respond(Promise.reject(new Error(`runs.run('${key}') gate is not supported with retained resume.`)));
|
|
@@ -2110,10 +2206,11 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
2110
2206
|
const childStopController = new AbortController();
|
|
2111
2207
|
childStopControllers.set(key, childStopController);
|
|
2112
2208
|
const childSignal = combinedAbortSignal([childController.signal, childStopController.signal]);
|
|
2113
|
-
const
|
|
2209
|
+
const resumeInput = resumeReference ?? (typeof params.resume === "string" && options.resolveResume ? params.resume : undefined);
|
|
2210
|
+
const resolvedResumeValue = resumeInput
|
|
2114
2211
|
? await Promise.resolve().then(() => {
|
|
2115
2212
|
if (!options.resolveResume) throw new Error("Keyed workflow receipt resume is unavailable in this host.");
|
|
2116
|
-
return options.resolveResume(
|
|
2213
|
+
return options.resolveResume(resumeInput, childSignal, typeof params.index === "number" ? params.index : undefined);
|
|
2117
2214
|
})
|
|
2118
2215
|
: undefined;
|
|
2119
2216
|
const resolvedResume = typeof resolvedResumeValue === "string"
|
|
@@ -2129,6 +2226,10 @@ export async function runWorkflowScript(options: RunWorkflowScriptOptions): Prom
|
|
|
2129
2226
|
: [];
|
|
2130
2227
|
resolvedResumeLineage = [...new Set(lineage.length ? lineage : [resolvedResumeId!])];
|
|
2131
2228
|
if (resolvedResumeLineage.at(-1) !== resolvedResumeId) resolvedResumeLineage.push(resolvedResumeId!);
|
|
2229
|
+
if (typeof resumeInput === "string") {
|
|
2230
|
+
const predecessor = [...children.values()].find((child) => child.runId === resolvedResumeId);
|
|
2231
|
+
if (predecessor?.continuation && predecessor.continuation.runIds.at(-1) === resolvedResumeId) resolvedResumeLineage = predecessor.continuation.runIds;
|
|
2232
|
+
}
|
|
2132
2233
|
}
|
|
2133
2234
|
const launchParams = resolvedResumeId ? { ...params, resume: resolvedResumeId } : params;
|
|
2134
2235
|
await launchSemaphore.acquire();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { AsyncJobStep,
|
|
1
|
+
import type { AsyncJobStep, HostStepNode, WorkflowGraphNode, WorkflowGraphSnapshot, WorkflowPreflightLane, WorkflowPreflight } from "../shared/types.ts";
|
|
2
2
|
import { sanitizeDisplayText } from "../shared/display-text.ts";
|
|
3
|
-
import { workflowPreflightLaneForRuntimeKey } from "./workflow-preflight.ts";
|
|
3
|
+
import { workflowPreflightLaneForRuntimeKey as laneFor } from "./workflow-preflight.ts";
|
|
4
4
|
|
|
5
5
|
export type WorkflowChecklistState = "complete" | "running" | "queued" | "blocked" | "failed" | "paused" | "stopped";
|
|
6
6
|
|
|
@@ -62,12 +62,12 @@ export interface WorkflowChecklistItem {
|
|
|
62
62
|
toolCount?: number;
|
|
63
63
|
outputName?: string;
|
|
64
64
|
error?: string;
|
|
65
|
-
preflight?:
|
|
65
|
+
preflight?: WorkflowPreflightLane;
|
|
66
66
|
kind?: "child" | "host";
|
|
67
|
-
monitorKind?:
|
|
67
|
+
monitorKind?: HostStepNode["monitorKind"];
|
|
68
68
|
provider?: string;
|
|
69
69
|
role?: string;
|
|
70
|
-
verdict?:
|
|
70
|
+
verdict?: HostStepNode["verdict"];
|
|
71
71
|
target?: string;
|
|
72
72
|
reasonCode?: string;
|
|
73
73
|
stale?: boolean;
|
|
@@ -106,8 +106,8 @@ export interface WorkflowChecklistProjection {
|
|
|
106
106
|
export interface WorkflowChecklistInput {
|
|
107
107
|
graph?: WorkflowGraphSnapshot;
|
|
108
108
|
steps?: readonly WorkflowChecklistStep[] | readonly AsyncJobStep[];
|
|
109
|
-
hostSteps?: readonly
|
|
110
|
-
preflight?:
|
|
109
|
+
hostSteps?: readonly HostStepNode[];
|
|
110
|
+
preflight?: WorkflowPreflight;
|
|
111
111
|
trace?: readonly WorkflowChecklistTraceEntry[];
|
|
112
112
|
now?: number;
|
|
113
113
|
}
|
|
@@ -190,15 +190,11 @@ function duration(step: Pick<WorkflowChecklistStep, "durationMs" | "startedAt" |
|
|
|
190
190
|
return end === undefined ? undefined : Math.max(0, end - startedAt);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
function laneFor(preflight: WorkflowPreflightV1 | undefined, key: string, preferredKeys: readonly (string | undefined)[] = []): WorkflowPreflightLaneV1 | undefined {
|
|
194
|
-
return workflowPreflightLaneForRuntimeKey(preflight, key, preferredKeys);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
193
|
function stepKey(step: WorkflowChecklistStep): string | undefined {
|
|
198
194
|
return step.key ?? step.workflowKey ?? step.runId;
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
function stepItem(step: WorkflowChecklistStep, index: number, phase: string, key = stepKey(step) ?? `step-${index + 1}`, label = step.label ?? step.description ?? stepKey(step) ?? step.agent ?? key, preflight?:
|
|
197
|
+
function stepItem(step: WorkflowChecklistStep, index: number, phase: string, key = stepKey(step) ?? `step-${index + 1}`, label = step.label ?? step.description ?? stepKey(step) ?? step.agent ?? key, preflight?: WorkflowPreflightLane): WorkflowChecklistItem {
|
|
202
198
|
const state = checklistState(step);
|
|
203
199
|
return {
|
|
204
200
|
key: keyText(key, `step-${index + 1}`),
|
|
@@ -219,7 +215,7 @@ function stepItem(step: WorkflowChecklistStep, index: number, phase: string, key
|
|
|
219
215
|
};
|
|
220
216
|
}
|
|
221
217
|
|
|
222
|
-
function hostItem(host:
|
|
218
|
+
function hostItem(host: HostStepNode, phase: string, key = host.id): WorkflowChecklistItem {
|
|
223
219
|
const state = checklistState({ status: host.state, verdict: host.verdict, stale: host.freshness?.stale });
|
|
224
220
|
return {
|
|
225
221
|
key: keyText(key, "host-step"),
|
|
@@ -264,7 +260,7 @@ function traceSources(trace: readonly WorkflowChecklistTraceEntry[] | undefined)
|
|
|
264
260
|
return [...latest.values()];
|
|
265
261
|
}
|
|
266
262
|
|
|
267
|
-
function traceItem(entry: WorkflowChecklistTraceEntry, index: number, preflight:
|
|
263
|
+
function traceItem(entry: WorkflowChecklistTraceEntry, index: number, preflight: WorkflowPreflightLane | undefined): WorkflowChecklistItem {
|
|
268
264
|
const phase = keyText(preflight?.key ?? entry.generatedLaneKey ?? entry.phase, "Workflow");
|
|
269
265
|
const item = stepItem({ key: entry.key, label: entry.label, phase, agent: entry.agent, status: entry.state === "started" ? "running" : entry.state, durationMs: entry.durationMs, error: entry.error }, index, phase, entry.key, entry.label ?? entry.key, preflight);
|
|
270
266
|
if (entry.operation === "host") item.kind = "host";
|
|
@@ -284,7 +280,7 @@ function add(phases: Map<string, WorkflowChecklistPhase>, phase: string, item: W
|
|
|
284
280
|
phaseFor(phases, phase).items.push(item);
|
|
285
281
|
}
|
|
286
282
|
|
|
287
|
-
function mergeNodeStep(node: WorkflowGraphNode, step: WorkflowChecklistStep, phase: string, trace: WorkflowChecklistTraceEntry | undefined, preflight:
|
|
283
|
+
function mergeNodeStep(node: WorkflowGraphNode, step: WorkflowChecklistStep, phase: string, trace: WorkflowChecklistTraceEntry | undefined, preflight: WorkflowPreflightLane | undefined): WorkflowChecklistItem {
|
|
288
284
|
const state = checklistState(step);
|
|
289
285
|
const nodeState = checklistState({ status: node.status, acceptance: node.acceptanceStatus ? { status: node.acceptanceStatus } : undefined });
|
|
290
286
|
const status = TERMINAL_STATES.has(nodeState) && !TERMINAL_STATES.has(state)
|
|
@@ -402,11 +398,12 @@ export function formatWorkflowChecklistPhase(phase: WorkflowChecklistPhase): str
|
|
|
402
398
|
return counts.length ? `${phase.label} ${counts.join(" · ")}` : phase.label;
|
|
403
399
|
}
|
|
404
400
|
|
|
405
|
-
export function formatWorkflowChecklistBottleneck(item: WorkflowChecklistItem | undefined, options: { includeOutput?: boolean } = {}): string | undefined {
|
|
401
|
+
export function formatWorkflowChecklistBottleneck(item: WorkflowChecklistItem | undefined, options: { includeOutput?: boolean; includeError?: boolean } = {}): string | undefined {
|
|
406
402
|
if (!item) return undefined;
|
|
407
403
|
const identity = [item.label, item.agent && item.agent !== item.label ? item.agent : undefined].filter((value): value is string => Boolean(value)).join(" · ") || item.key;
|
|
408
404
|
const includeOutput = options.includeOutput ?? true;
|
|
409
|
-
const
|
|
405
|
+
const includeError = options.includeError ?? true;
|
|
406
|
+
const details = [item.context ? `(${item.context})` : undefined, item.currentTool ? `${item.currentTool}${item.durationMs !== undefined ? ` ${formatDurationText(item.durationMs)}` : ""}` : undefined, !item.currentTool && item.currentPath ? item.currentPath : undefined, !item.currentTool && item.durationMs !== undefined ? formatDurationText(item.durationMs) : undefined, item.toolCount !== undefined ? `${item.toolCount} tools` : undefined, includeOutput && item.outputName ? `out:${item.outputName}` : undefined, includeError && item.error ? `error:${item.error.replace(/\bOutput:/g, "output:")}` : undefined].filter((value): value is string => Boolean(value));
|
|
410
407
|
return [identity, ...details].join(" · ");
|
|
411
408
|
}
|
|
412
409
|
|
|
@@ -433,7 +430,7 @@ export function formatWorkflowChecklistText(projection: WorkflowChecklistProject
|
|
|
433
430
|
lines.push(`${indent} ${marker} ${formatWorkflowChecklistItem(item)}`);
|
|
434
431
|
}
|
|
435
432
|
}
|
|
436
|
-
const bottleneck = formatWorkflowChecklistBottleneck(projection.bottleneck);
|
|
433
|
+
const bottleneck = formatWorkflowChecklistBottleneck(projection.bottleneck, { includeError: options.includeItems === false });
|
|
437
434
|
if (bottleneck) lines.push(`${indent} bottleneck · ${bottleneck}`);
|
|
438
435
|
return lines;
|
|
439
436
|
}
|
|
@@ -1,9 +1,42 @@
|
|
|
1
|
-
import type { AsyncStatus,
|
|
1
|
+
import type { AgentProgress, AsyncStatus, WorkflowChildActivity, WorkflowChildSummary } from "../shared/types.ts";
|
|
2
2
|
import type { WorkflowScriptChildResult, WorkflowScriptTraceEntry } from "./scripted-workflow.ts";
|
|
3
3
|
|
|
4
4
|
const KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
5
5
|
const TERMINAL_STATES = new Set(["completed", "failed", "paused", "stopped", "rejected", "detached"]);
|
|
6
6
|
const MAX_REQUIRED_ID_BYTES = 4_096;
|
|
7
|
+
const ACTIVITY_COUNTERS = ["currentToolStartedAt", "lastActivityAt", "durationMs", "toolCount", "turnCount", "tokens", "inputTokens", "outputTokens"] as const;
|
|
8
|
+
type WorkflowChildLiveProgress = WorkflowChildActivity & Pick<AgentProgress, "agent" | "sessionName" | "model" | "thinking">;
|
|
9
|
+
|
|
10
|
+
export function workflowChildProgress(progress: AgentProgress): WorkflowChildLiveProgress {
|
|
11
|
+
return {
|
|
12
|
+
...workflowChildActivity(progress),
|
|
13
|
+
agent: bounded(progress.agent, 256) ?? "",
|
|
14
|
+
...(bounded(progress.sessionName, 256) ? { sessionName: progress.sessionName } : {}),
|
|
15
|
+
...(bounded(progress.model, 256) ? { model: progress.model } : {}),
|
|
16
|
+
...(bounded(progress.thinking, 32) ? { thinking: progress.thinking } : {}),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Fixed field count and a 256 UTF-8 byte tool name keep JSON activity below 2 KiB, including escaping. */
|
|
21
|
+
export function workflowChildActivity(progress: WorkflowChildActivity): WorkflowChildActivity {
|
|
22
|
+
const activity: WorkflowChildActivity = {};
|
|
23
|
+
if (bounded(progress.currentTool, 256)) activity.currentTool = progress.currentTool;
|
|
24
|
+
for (const field of ACTIVITY_COUNTERS) {
|
|
25
|
+
const value = progress[field];
|
|
26
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) activity[field] = value;
|
|
27
|
+
}
|
|
28
|
+
return activity;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseActivity(value: unknown): WorkflowChildActivity | undefined {
|
|
32
|
+
if (value === undefined) return undefined;
|
|
33
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("workflowChildren child activity is invalid.");
|
|
34
|
+
const input = value as Record<string, unknown>;
|
|
35
|
+
for (const [key, value] of Object.entries(input)) {
|
|
36
|
+
if (key === "currentTool" ? !bounded(value, 256) : !ACTIVITY_COUNTERS.includes(key as typeof ACTIVITY_COUNTERS[number]) || typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error("workflowChildren child activity is invalid.");
|
|
37
|
+
}
|
|
38
|
+
return { ...input } as WorkflowChildActivity;
|
|
39
|
+
}
|
|
7
40
|
|
|
8
41
|
function bounded(value: unknown, maxBytes: number): string | undefined {
|
|
9
42
|
if (typeof value !== "string" || !value.trim() || Buffer.byteLength(value, "utf8") > maxBytes) return undefined;
|
|
@@ -19,13 +52,15 @@ function requiredId(value: string, label: string): string {
|
|
|
19
52
|
export function workflowChildSummary(input: {
|
|
20
53
|
parentToolCallId: string;
|
|
21
54
|
workflowRunId: string;
|
|
22
|
-
workflowState:
|
|
55
|
+
workflowState: WorkflowChildSummary["workflowState"];
|
|
23
56
|
inventoryComplete: boolean;
|
|
24
57
|
trace?: WorkflowScriptTraceEntry[];
|
|
25
58
|
children?: WorkflowScriptChildResult[];
|
|
26
59
|
steps?: NonNullable<AsyncStatus["steps"]>;
|
|
27
|
-
|
|
28
|
-
|
|
60
|
+
/** In-memory snapshots from synchronous child callbacks, keyed by workflow key. */
|
|
61
|
+
progress?: ReadonlyMap<string, WorkflowChildLiveProgress>;
|
|
62
|
+
}): WorkflowChildSummary {
|
|
63
|
+
const rows = new Map<string, WorkflowChildSummary["children"][number]>();
|
|
29
64
|
for (const entry of input.trace ?? []) {
|
|
30
65
|
if (entry.operation !== "run" || !KEY_PATTERN.test(entry.key)) continue;
|
|
31
66
|
const previous = rows.get(entry.key);
|
|
@@ -83,6 +118,18 @@ export function workflowChildSummary(input: {
|
|
|
83
118
|
if (!TERMINAL_STATES.has(row.state)) rows.set(key, { ...row, state: input.workflowState === "stopped" ? "stopped" : "failed" });
|
|
84
119
|
}
|
|
85
120
|
}
|
|
121
|
+
for (const [key, progress] of input.progress ?? []) {
|
|
122
|
+
const row = rows.get(key);
|
|
123
|
+
if (!row || row.state !== "running") continue;
|
|
124
|
+
rows.set(key, {
|
|
125
|
+
...row,
|
|
126
|
+
...(bounded(progress.agent, 256) ? { agent: progress.agent } : {}),
|
|
127
|
+
...(bounded(progress.sessionName, 256) ? { sessionName: progress.sessionName } : {}),
|
|
128
|
+
...(bounded(progress.model, 256) ? { model: progress.model } : {}),
|
|
129
|
+
...(bounded(progress.thinking, 32) ? { thinking: progress.thinking } : {}),
|
|
130
|
+
activity: workflowChildActivity(progress),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
86
133
|
return {
|
|
87
134
|
version: 1,
|
|
88
135
|
parentToolCallId: requiredId(input.parentToolCallId, "parentToolCallId"),
|
|
@@ -93,7 +140,7 @@ export function workflowChildSummary(input: {
|
|
|
93
140
|
};
|
|
94
141
|
}
|
|
95
142
|
|
|
96
|
-
export function parseWorkflowChildSummary(value: unknown):
|
|
143
|
+
export function parseWorkflowChildSummary(value: unknown): WorkflowChildSummary | undefined {
|
|
97
144
|
if (value === undefined) return undefined;
|
|
98
145
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("workflowChildren must be an object.");
|
|
99
146
|
const input = value as Record<string, unknown>;
|
|
@@ -102,17 +149,19 @@ export function parseWorkflowChildSummary(value: unknown): WorkflowChildSummaryV
|
|
|
102
149
|
if (input.version !== 1 || typeof input.inventoryComplete !== "boolean" || !Array.isArray(input.children)) throw new Error("workflowChildren is invalid.");
|
|
103
150
|
const workflowState = input.workflowState;
|
|
104
151
|
if (workflowState !== "queued" && workflowState !== "running" && workflowState !== "completed" && workflowState !== "failed" && workflowState !== "paused" && workflowState !== "stopped") throw new Error("workflowChildren.workflowState is invalid.");
|
|
105
|
-
const children = input.children.map((row):
|
|
152
|
+
const children = input.children.map((row): WorkflowChildSummary["children"][number] => {
|
|
106
153
|
if (!row || typeof row !== "object" || Array.isArray(row)) throw new Error("workflowChildren child row is invalid.");
|
|
107
154
|
const child = row as Record<string, unknown>;
|
|
108
|
-
if (Object.keys(child).some((key) => !["childId", "runId", "agent", "sessionName", "model", "thinking", "state"].includes(key))) throw new Error("workflowChildren child row has unsupported fields.");
|
|
155
|
+
if (Object.keys(child).some((key) => !["childId", "runId", "agent", "sessionName", "model", "thinking", "state", "activity"].includes(key))) throw new Error("workflowChildren child row has unsupported fields.");
|
|
109
156
|
if (typeof child.childId !== "string" || !KEY_PATTERN.test(child.childId)) throw new Error("workflowChildren childId is invalid.");
|
|
110
157
|
const state = child.state;
|
|
111
158
|
if (state !== "pending" && state !== "running" && state !== "completed" && state !== "failed" && state !== "paused" && state !== "stopped" && state !== "rejected" && state !== "detached") throw new Error("workflowChildren child state is invalid.");
|
|
112
159
|
for (const [field, maxBytes] of [["runId", 256], ["agent", 256], ["sessionName", 256], ["model", 256], ["thinking", 32]] as const) {
|
|
113
160
|
if (child[field] !== undefined && bounded(child[field], maxBytes) === undefined) throw new Error(`workflowChildren child ${field} is invalid.`);
|
|
114
161
|
}
|
|
115
|
-
|
|
162
|
+
const activity = parseActivity(child.activity);
|
|
163
|
+
if (activity && state !== "running") throw new Error("workflowChildren child activity requires running state.");
|
|
164
|
+
return { childId: child.childId, state, ...(activity ? { activity } : {}), ...(child.runId ? { runId: child.runId as string } : {}), ...(child.agent ? { agent: child.agent as string } : {}), ...(child.sessionName ? { sessionName: child.sessionName as string } : {}), ...(child.model ? { model: child.model as string } : {}), ...(child.thinking ? { thinking: child.thinking as string } : {}) };
|
|
116
165
|
});
|
|
117
166
|
if (new Set(children.map((child) => child.childId)).size !== children.length) throw new Error("workflowChildren has duplicate childId values.");
|
|
118
167
|
if (typeof input.parentToolCallId !== "string") throw new Error("workflowChildren.parentToolCallId is invalid.");
|