@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
|
@@ -6,31 +6,13 @@ import { afterEach, describe, it } from "node:test";
|
|
|
6
6
|
import registerFanoutChildSubagentExtension from "../../src/extension/fanout-child.ts";
|
|
7
7
|
import { createSubagentExecutor, readNestedRecoveryDescriptor } from "../../src/runs/foreground/subagent-executor.ts";
|
|
8
8
|
import { createNestedRoute, findNestedControlResult, projectNestedEvents, readNestedControlRequests, readNestedControlResults, snapshotNestedEventFiles, writeNestedControlRequest, writeNestedControlResult, writeNestedEvent } from "../../src/runs/shared/nested-events.ts";
|
|
9
|
-
import {
|
|
10
|
-
SUBAGENT_CHILD_ENV,
|
|
11
|
-
SUBAGENT_FANOUT_CHILD_ENV,
|
|
12
|
-
SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV,
|
|
13
|
-
SUBAGENT_PARENT_CHILD_INDEX_ENV,
|
|
14
|
-
SUBAGENT_PARENT_CONTROL_INBOX_ENV,
|
|
15
|
-
SUBAGENT_PARENT_EVENT_SINK_ENV,
|
|
16
|
-
SUBAGENT_PARENT_ROOT_RUN_ID_ENV,
|
|
17
|
-
SUBAGENT_PARENT_RUN_ID_ENV,
|
|
18
|
-
} from "../../src/runs/shared/pi-args.ts";
|
|
9
|
+
import type { ChildRuntimeConfig } from "../../src/runs/shared/child-runtime-config.ts";
|
|
19
10
|
import { ASYNC_DIR, type SubagentState } from "../../src/shared/types.ts";
|
|
20
11
|
import { createRunFanoutBudget } from "../../src/runs/shared/run-fanout-budget.ts";
|
|
12
|
+
import { getArtifactPaths, getArtifactsDir } from "../../src/shared/artifacts.ts";
|
|
21
13
|
|
|
22
14
|
const routeRoots: string[] = [];
|
|
23
15
|
const fanoutListenerCleanupKey = "__piSubagentFanoutChildNestedControlInboxCleanups";
|
|
24
|
-
const savedEnv = {
|
|
25
|
-
[SUBAGENT_CHILD_ENV]: process.env[SUBAGENT_CHILD_ENV],
|
|
26
|
-
[SUBAGENT_FANOUT_CHILD_ENV]: process.env[SUBAGENT_FANOUT_CHILD_ENV],
|
|
27
|
-
[SUBAGENT_PARENT_EVENT_SINK_ENV]: process.env[SUBAGENT_PARENT_EVENT_SINK_ENV],
|
|
28
|
-
[SUBAGENT_PARENT_CONTROL_INBOX_ENV]: process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV],
|
|
29
|
-
[SUBAGENT_PARENT_ROOT_RUN_ID_ENV]: process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV],
|
|
30
|
-
[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV]: process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV],
|
|
31
|
-
[SUBAGENT_PARENT_RUN_ID_ENV]: process.env[SUBAGENT_PARENT_RUN_ID_ENV],
|
|
32
|
-
[SUBAGENT_PARENT_CHILD_INDEX_ENV]: process.env[SUBAGENT_PARENT_CHILD_INDEX_ENV],
|
|
33
|
-
};
|
|
34
16
|
|
|
35
17
|
afterEach(() => {
|
|
36
18
|
const globalStore = globalThis as Record<string, unknown>;
|
|
@@ -43,10 +25,6 @@ afterEach(() => {
|
|
|
43
25
|
}
|
|
44
26
|
delete globalStore[fanoutListenerCleanupKey];
|
|
45
27
|
for (const root of routeRoots.splice(0)) fs.rmSync(root, { recursive: true, force: true });
|
|
46
|
-
for (const [key, value] of Object.entries(savedEnv)) {
|
|
47
|
-
if (value === undefined) delete process.env[key];
|
|
48
|
-
else process.env[key] = value;
|
|
49
|
-
}
|
|
50
28
|
});
|
|
51
29
|
|
|
52
30
|
function createState(): SubagentState {
|
|
@@ -68,7 +46,7 @@ function createState(): SubagentState {
|
|
|
68
46
|
};
|
|
69
47
|
}
|
|
70
48
|
|
|
71
|
-
function createExecutor(state = createState(), agents: Array<Record<string, unknown>> = [], allowMutatingManagementActions = true, events: any = { emit() {}, on() { return () => {}; } }) {
|
|
49
|
+
function createExecutor(state = createState(), agents: Array<Record<string, unknown>> = [], allowMutatingManagementActions = true, events: any = { emit() {}, on() { return () => {}; } }, childRuntime?: ChildRuntimeConfig) {
|
|
72
50
|
return createSubagentExecutor({
|
|
73
51
|
pi: { events, getSessionName() { return "parent"; } } as any,
|
|
74
52
|
state,
|
|
@@ -79,6 +57,7 @@ function createExecutor(state = createState(), agents: Array<Record<string, unkn
|
|
|
79
57
|
expandTilde: (value) => value,
|
|
80
58
|
discoverAgents: () => ({ agents: agents as any }),
|
|
81
59
|
allowMutatingManagementActions,
|
|
60
|
+
...(childRuntime ? { childRuntime } : {}),
|
|
82
61
|
});
|
|
83
62
|
}
|
|
84
63
|
|
|
@@ -117,13 +96,16 @@ function stateWithNestedRoute(route: ReturnType<typeof createNestedRoute>): Suba
|
|
|
117
96
|
return state;
|
|
118
97
|
}
|
|
119
98
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
99
|
+
/** The child runtime config of a fanout child launched under `route` by `parentRunId`. */
|
|
100
|
+
function fanoutChildRuntime(route: ReturnType<typeof createNestedRoute>, parentRunId = route.rootRunId): ChildRuntimeConfig {
|
|
101
|
+
return {
|
|
102
|
+
fanoutChild: true,
|
|
103
|
+
depth: 1,
|
|
104
|
+
waitTool: { enabled: true },
|
|
105
|
+
fast: false,
|
|
106
|
+
nestedRoute: route,
|
|
107
|
+
nestedParent: { parentRunId, parentChildIndex: 0, depth: 1, path: [{ runId: parentRunId, stepIndex: 0 }] },
|
|
108
|
+
};
|
|
127
109
|
}
|
|
128
110
|
|
|
129
111
|
function text(result: Awaited<ReturnType<ReturnType<typeof createExecutor>["execute"]>>): string {
|
|
@@ -183,8 +165,15 @@ describe("nested control routing", () => {
|
|
|
183
165
|
try {
|
|
184
166
|
const route = createNestedRun("nested-foreground");
|
|
185
167
|
const state = createState();
|
|
168
|
+
state.artifactDirPreference = "project";
|
|
169
|
+
const artifactRoot = getArtifactsDir(null, root, "project");
|
|
170
|
+
fs.mkdirSync(artifactRoot, { recursive: true });
|
|
171
|
+
fs.writeFileSync(getArtifactPaths(artifactRoot, "root-control", "orchestrator", 0).transcriptPath,
|
|
172
|
+
`${JSON.stringify({ recordType: "message", role: "assistant", text: "LIVE_FOREGROUND_ACTIVITY" })}\n`);
|
|
186
173
|
state.foregroundControls.set("root-control", {
|
|
187
174
|
runId: "root-control",
|
|
175
|
+
sessionId: "session",
|
|
176
|
+
cwd: root,
|
|
188
177
|
mode: "single",
|
|
189
178
|
startedAt: 1,
|
|
190
179
|
updatedAt: 1,
|
|
@@ -205,6 +194,12 @@ describe("nested control routing", () => {
|
|
|
205
194
|
const transcript = await createExecutor(state).execute("transcript", { action: "status", id: "root-control", index: 0, view: "transcript" }, new AbortController().signal, undefined, ctx(root));
|
|
206
195
|
assert.equal(transcript.isError, undefined);
|
|
207
196
|
assert.match(text(transcript), /^Transcript target: run root-control · child 0\nSpawn budget:/);
|
|
197
|
+
assert.match(text(transcript), /LIVE_FOREGROUND_ACTIVITY/);
|
|
198
|
+
for (const hasUI of [false, true]) {
|
|
199
|
+
const implicit = await createExecutor(state).execute("implicit-transcript", { action: "status", view: "transcript" }, new AbortController().signal, undefined, { ...ctx(root), hasUI });
|
|
200
|
+
assert.equal(implicit.isError, undefined);
|
|
201
|
+
assert.match(text(implicit), /LIVE_FOREGROUND_ACTIVITY/);
|
|
202
|
+
}
|
|
208
203
|
} finally {
|
|
209
204
|
fs.rmSync(root, { recursive: true, force: true });
|
|
210
205
|
}
|
|
@@ -250,7 +245,6 @@ describe("nested control routing", () => {
|
|
|
250
245
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-nested-child-scope-"));
|
|
251
246
|
try {
|
|
252
247
|
const allowedRoute = createNestedRun("shared-nested");
|
|
253
|
-
setNestedRouteEnv(allowedRoute, "root-control");
|
|
254
248
|
const outsideRoute = createNestedRoute("root-outside");
|
|
255
249
|
routeRoots.push(path.dirname(outsideRoute.eventSink));
|
|
256
250
|
writeNestedEvent(outsideRoute, {
|
|
@@ -261,7 +255,7 @@ describe("nested control routing", () => {
|
|
|
261
255
|
child: { id: "shared-nested", parentRunId: "root-outside", parentStepIndex: 0, depth: 1, path: [{ runId: "root-outside", stepIndex: 0 }], state: "running", agent: "outside" },
|
|
262
256
|
});
|
|
263
257
|
|
|
264
|
-
const result = await createExecutor(createState(), [], false).execute("status", { action: "status", id: "shared-nested" }, new AbortController().signal, undefined, ctx(root));
|
|
258
|
+
const result = await createExecutor(createState(), [], false, undefined, fanoutChildRuntime(allowedRoute, "root-control")).execute("status", { action: "status", id: "shared-nested" }, new AbortController().signal, undefined, ctx(root));
|
|
265
259
|
|
|
266
260
|
assert.equal(result.isError, undefined);
|
|
267
261
|
assert.match(text(result), /Nested run: shared-nested/);
|
|
@@ -337,22 +331,33 @@ describe("nested control routing", () => {
|
|
|
337
331
|
}
|
|
338
332
|
});
|
|
339
333
|
|
|
340
|
-
it(
|
|
334
|
+
for (const workflow of [false, true]) it(`routes ${workflow ? "workflow string" : "action"} resume for live nested runs through the control inbox`, async () => {
|
|
341
335
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-nested-live-resume-"));
|
|
342
336
|
try {
|
|
343
337
|
const emitted: Array<{ name: string; payload: unknown }> = [];
|
|
344
338
|
const events = { emit(name: string, payload: unknown) { emitted.push({ name, payload }); }, on() { return () => {}; } };
|
|
345
339
|
const route = createNestedRun("nested-live-resume", "running", { intercomTarget: "attacker-target", leafIntercomTarget: "attacker-leaf" });
|
|
346
340
|
const executor = createExecutor(stateWithNestedRoute(route), [], true, events);
|
|
347
|
-
|
|
348
|
-
const
|
|
341
|
+
const responder = (async () => {
|
|
342
|
+
const deadline = Date.now() + 2_000;
|
|
343
|
+
let request = readNestedControlRequests(route)[0];
|
|
344
|
+
while (!request && Date.now() < deadline) {
|
|
345
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
346
|
+
request = readNestedControlRequests(route)[0];
|
|
347
|
+
}
|
|
349
348
|
assert.ok(request, "expected a nested resume request");
|
|
350
349
|
assert.equal(request.action, "resume");
|
|
351
350
|
assert.equal(request.message, "continue please");
|
|
352
351
|
writeNestedControlResult(route, { ts: Date.now(), requestId: request.requestId, targetRunId: request.targetRunId, ok: true, message: "nested resume accepted" });
|
|
353
|
-
}
|
|
352
|
+
})();
|
|
354
353
|
|
|
355
|
-
const
|
|
354
|
+
const execution = Promise.resolve().then(() => executor.execute("resume", workflow
|
|
355
|
+
? { async: false, workflowScript: `return runs.run("live", { resume: "nested-live-resume", task: "continue please", output: false });` }
|
|
356
|
+
: { action: "resume", id: "nested-live-resume", message: "continue please" }, new AbortController().signal, undefined, ctx(root)));
|
|
357
|
+
const [response, executed] = await Promise.allSettled([responder, execution]);
|
|
358
|
+
if (response.status === "rejected") throw response.reason;
|
|
359
|
+
if (executed.status === "rejected") throw executed.reason;
|
|
360
|
+
const result = executed.value;
|
|
356
361
|
|
|
357
362
|
assert.equal(result.isError, undefined);
|
|
358
363
|
assert.match(text(result), /nested resume accepted/);
|
|
@@ -407,13 +412,15 @@ describe("nested control routing", () => {
|
|
|
407
412
|
}
|
|
408
413
|
});
|
|
409
414
|
|
|
410
|
-
it(
|
|
415
|
+
for (const workflow of [false, true]) it(`rejects stopped nested runs before ${workflow ? "workflow string resume" : "action revival"}`, async () => {
|
|
411
416
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-nested-stopped-resume-"));
|
|
412
417
|
try {
|
|
413
418
|
const route = createNestedRun("nested-stopped-resume", "stopped", { sessionFile: path.join(root, "missing-session.jsonl") });
|
|
414
419
|
|
|
415
420
|
const result = await createExecutor(stateWithNestedRoute(route), [{ name: "worker", description: "Worker", prompt: "Do work" }])
|
|
416
|
-
.execute("resume",
|
|
421
|
+
.execute("resume", workflow
|
|
422
|
+
? { async: false, workflowScript: `return runs.run("stopped", { resume: "nested-stopped-resume", task: "continue", output: false });` }
|
|
423
|
+
: { action: "resume", id: "nested-stopped-resume", message: "continue" }, new AbortController().signal, undefined, ctx(root));
|
|
417
424
|
|
|
418
425
|
assert.equal(result.isError, true);
|
|
419
426
|
assert.match(text(result), /was stopped and cannot be resumed/);
|
|
@@ -468,13 +475,12 @@ describe("nested control routing", () => {
|
|
|
468
475
|
try {
|
|
469
476
|
const route = createNestedRoute("root-parent");
|
|
470
477
|
routeRoots.push(path.dirname(route.eventSink));
|
|
471
|
-
setNestedRouteEnv(route, "root-parent");
|
|
472
478
|
const throwingCtx = {
|
|
473
479
|
...ctx(root),
|
|
474
480
|
modelRegistry: { getAvailable() { throw new Error("model registry exploded"); } },
|
|
475
481
|
};
|
|
476
482
|
|
|
477
|
-
const result = await createExecutor(createState(), [{ name: "worker", description: "Worker", prompt: "Do work" }])
|
|
483
|
+
const result = await createExecutor(createState(), [{ name: "worker", description: "Worker", prompt: "Do work" }], true, undefined, fanoutChildRuntime(route, "root-parent"))
|
|
478
484
|
.execute("run", { agent: "worker", task: "go" }, new AbortController().signal, undefined, throwingCtx);
|
|
479
485
|
|
|
480
486
|
assert.equal(result.isError, true);
|
|
@@ -491,9 +497,7 @@ describe("nested control routing", () => {
|
|
|
491
497
|
it("keeps the fanout child control listener alive after control inbox polling errors", async () => {
|
|
492
498
|
const route = createNestedRoute("root-poll-error");
|
|
493
499
|
routeRoots.push(path.dirname(route.eventSink));
|
|
494
|
-
|
|
495
|
-
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
496
|
-
process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
|
|
500
|
+
const childRuntime = fanoutChildRuntime(route, "root-poll-error");
|
|
497
501
|
const pi = {
|
|
498
502
|
events: { emit() {}, on() { return () => {}; } },
|
|
499
503
|
registerTool() {},
|
|
@@ -507,7 +511,7 @@ describe("nested control routing", () => {
|
|
|
507
511
|
logged.push(args);
|
|
508
512
|
};
|
|
509
513
|
try {
|
|
510
|
-
registerFanoutChildSubagentExtension(pi);
|
|
514
|
+
registerFanoutChildSubagentExtension(pi, childRuntime);
|
|
511
515
|
await waitFor(() => logged.some((entry) => String(entry[0] ?? "").includes(route.controlInbox) && String(entry[0] ?? "").includes("root-poll-error")));
|
|
512
516
|
|
|
513
517
|
fs.rmSync(route.controlInbox, { force: true });
|
|
@@ -529,9 +533,7 @@ describe("nested control routing", () => {
|
|
|
529
533
|
it("keeps fanout child control requests when result writing fails and retries after recovery", async () => {
|
|
530
534
|
const route = createNestedRoute("root-result-write-fails");
|
|
531
535
|
routeRoots.push(path.dirname(route.eventSink));
|
|
532
|
-
|
|
533
|
-
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
534
|
-
process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
|
|
536
|
+
const childRuntime = fanoutChildRuntime(route, "root-result-write-fails");
|
|
535
537
|
const pi = {
|
|
536
538
|
events: { emit() {}, on() { return () => {}; } },
|
|
537
539
|
registerTool() {},
|
|
@@ -551,7 +553,7 @@ describe("nested control routing", () => {
|
|
|
551
553
|
logged.push(args);
|
|
552
554
|
};
|
|
553
555
|
try {
|
|
554
|
-
registerFanoutChildSubagentExtension(pi);
|
|
556
|
+
registerFanoutChildSubagentExtension(pi, childRuntime);
|
|
555
557
|
await waitFor(() => logged.some((entry) => String(entry[0] ?? "").includes("result-write-fails") && /keeping request for retry/.test(String(entry[0] ?? ""))));
|
|
556
558
|
assert.equal(fs.existsSync(requestPath), true);
|
|
557
559
|
|
|
@@ -567,9 +569,7 @@ describe("nested control routing", () => {
|
|
|
567
569
|
it("cleans the prior listener on reload and processes a resume request once", async () => {
|
|
568
570
|
const route = createNestedRoute("root-reload-listener");
|
|
569
571
|
routeRoots.push(path.dirname(route.eventSink));
|
|
570
|
-
|
|
571
|
-
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
572
|
-
process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
|
|
572
|
+
const childRuntime = fanoutChildRuntime(route, "root-reload-listener");
|
|
573
573
|
const registrations: string[] = [];
|
|
574
574
|
const activeIntervals = new Set<ReturnType<typeof setInterval>>();
|
|
575
575
|
const originalSetInterval = globalThis.setInterval;
|
|
@@ -591,25 +591,19 @@ describe("nested control routing", () => {
|
|
|
591
591
|
}) as any;
|
|
592
592
|
const firstPi = makePi();
|
|
593
593
|
|
|
594
|
-
registerFanoutChildSubagentExtension(firstPi);
|
|
595
|
-
registerFanoutChildSubagentExtension(firstPi);
|
|
596
|
-
registerFanoutChildSubagentExtension(makePi());
|
|
594
|
+
registerFanoutChildSubagentExtension(firstPi, childRuntime);
|
|
595
|
+
registerFanoutChildSubagentExtension(firstPi, childRuntime);
|
|
596
|
+
registerFanoutChildSubagentExtension(makePi(), childRuntime);
|
|
597
597
|
assert.equal(activeIntervals.size, 1);
|
|
598
598
|
|
|
599
599
|
const unrelatedRoute = createNestedRoute("root-unrelated-listener");
|
|
600
600
|
routeRoots.push(path.dirname(unrelatedRoute.eventSink));
|
|
601
|
-
|
|
602
|
-
registerFanoutChildSubagentExtension(makePi());
|
|
601
|
+
registerFanoutChildSubagentExtension(makePi(), fanoutChildRuntime(unrelatedRoute, "root-unrelated-listener"));
|
|
603
602
|
assert.equal(activeIntervals.size, 2);
|
|
604
603
|
|
|
605
|
-
|
|
606
|
-
delete process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV];
|
|
607
|
-
delete process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV];
|
|
608
|
-
delete process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV];
|
|
609
|
-
registerFanoutChildSubagentExtension(makePi());
|
|
604
|
+
registerFanoutChildSubagentExtension(makePi(), { ...childRuntime, nestedRoute: undefined });
|
|
610
605
|
assert.equal(activeIntervals.size, 2);
|
|
611
606
|
|
|
612
|
-
setNestedRouteEnv(route, "root-reload-listener");
|
|
613
607
|
writeNestedControlRequest(route, {
|
|
614
608
|
ts: Date.now(),
|
|
615
609
|
requestId: "reload-resume-once",
|
|
@@ -630,9 +624,7 @@ describe("nested control routing", () => {
|
|
|
630
624
|
it("negatively acknowledges ownerless fanout child control requests and removes them", async () => {
|
|
631
625
|
const route = createNestedRoute("root-ownerless");
|
|
632
626
|
routeRoots.push(path.dirname(route.eventSink));
|
|
633
|
-
|
|
634
|
-
process.env[SUBAGENT_CHILD_ENV] = "1";
|
|
635
|
-
process.env[SUBAGENT_FANOUT_CHILD_ENV] = "1";
|
|
627
|
+
const childRuntime = fanoutChildRuntime(route, "root-ownerless");
|
|
636
628
|
const pi = {
|
|
637
629
|
events: { emit() {}, on() { return () => {}; } },
|
|
638
630
|
registerTool() {},
|
|
@@ -645,11 +637,11 @@ describe("nested control routing", () => {
|
|
|
645
637
|
action: "interrupt",
|
|
646
638
|
});
|
|
647
639
|
|
|
648
|
-
registerFanoutChildSubagentExtension(pi);
|
|
640
|
+
registerFanoutChildSubagentExtension(pi, childRuntime);
|
|
649
641
|
await waitFor(() => readNestedControlResults(route).some((result) => result.requestId === "ownerless-request" && result.ok === false));
|
|
650
642
|
|
|
651
643
|
assert.equal(fs.existsSync(requestPath), false);
|
|
652
644
|
const result = readNestedControlResults(route).find((item) => item.requestId === "ownerless-request");
|
|
653
645
|
assert.match(result?.message ?? "", /not active/);
|
|
654
646
|
});
|
|
655
|
-
});
|
|
647
|
+
});
|
|
@@ -9,35 +9,15 @@ import {
|
|
|
9
9
|
hasLiveNestedDescendants,
|
|
10
10
|
nestedSummaryFromAsyncStatus,
|
|
11
11
|
parseNestedEventRecords,
|
|
12
|
+
inheritedNestedParentAddressOf,
|
|
13
|
+
inheritedNestedRouteOf,
|
|
12
14
|
projectNestedEvents,
|
|
13
|
-
resolveNestedParentAddressFromEnv,
|
|
14
|
-
resolveNestedRouteFromEnv,
|
|
15
15
|
updateAsyncJobNestedProjection,
|
|
16
16
|
updateForegroundNestedProjection,
|
|
17
17
|
writeNestedEvent,
|
|
18
18
|
} from "../../src/runs/shared/nested-events.ts";
|
|
19
|
-
import {
|
|
20
|
-
SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV,
|
|
21
|
-
SUBAGENT_PARENT_CHILD_INDEX_ENV,
|
|
22
|
-
SUBAGENT_PARENT_CONTROL_INBOX_ENV,
|
|
23
|
-
SUBAGENT_PARENT_DEPTH_ENV,
|
|
24
|
-
SUBAGENT_PARENT_EVENT_SINK_ENV,
|
|
25
|
-
SUBAGENT_PARENT_PATH_ENV,
|
|
26
|
-
SUBAGENT_PARENT_ROOT_RUN_ID_ENV,
|
|
27
|
-
SUBAGENT_PARENT_RUN_ID_ENV,
|
|
28
|
-
} from "../../src/runs/shared/pi-args.ts";
|
|
29
19
|
|
|
30
20
|
const routes: Array<{ eventSink: string }> = [];
|
|
31
|
-
const savedEnv = {
|
|
32
|
-
[SUBAGENT_PARENT_EVENT_SINK_ENV]: process.env[SUBAGENT_PARENT_EVENT_SINK_ENV],
|
|
33
|
-
[SUBAGENT_PARENT_CONTROL_INBOX_ENV]: process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV],
|
|
34
|
-
[SUBAGENT_PARENT_ROOT_RUN_ID_ENV]: process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV],
|
|
35
|
-
[SUBAGENT_PARENT_RUN_ID_ENV]: process.env[SUBAGENT_PARENT_RUN_ID_ENV],
|
|
36
|
-
[SUBAGENT_PARENT_CHILD_INDEX_ENV]: process.env[SUBAGENT_PARENT_CHILD_INDEX_ENV],
|
|
37
|
-
[SUBAGENT_PARENT_DEPTH_ENV]: process.env[SUBAGENT_PARENT_DEPTH_ENV],
|
|
38
|
-
[SUBAGENT_PARENT_PATH_ENV]: process.env[SUBAGENT_PARENT_PATH_ENV],
|
|
39
|
-
[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV]: process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV],
|
|
40
|
-
};
|
|
41
21
|
|
|
42
22
|
async function removeDirWithRetry(dir: string): Promise<void> {
|
|
43
23
|
let lastError: unknown;
|
|
@@ -57,10 +37,6 @@ afterEach(async () => {
|
|
|
57
37
|
for (const route of routes.splice(0)) {
|
|
58
38
|
await removeDirWithRetry(path.dirname(route.eventSink));
|
|
59
39
|
}
|
|
60
|
-
for (const [key, value] of Object.entries(savedEnv)) {
|
|
61
|
-
if (value === undefined) delete process.env[key];
|
|
62
|
-
else process.env[key] = value;
|
|
63
|
-
}
|
|
64
40
|
});
|
|
65
41
|
|
|
66
42
|
function trackRoute(rootRunId = "root-run") {
|
|
@@ -69,7 +45,7 @@ function trackRoute(rootRunId = "root-run") {
|
|
|
69
45
|
return route;
|
|
70
46
|
}
|
|
71
47
|
|
|
72
|
-
function child(id: string, state: "queued" | "running" | "complete" | "failed" | "paused", ts: number, parentRunId = "root-run") {
|
|
48
|
+
function child(id: string, state: "queued" | "running" | "complete" | "failed" | "paused" | "rejected", ts: number, parentRunId = "root-run") {
|
|
73
49
|
return {
|
|
74
50
|
id,
|
|
75
51
|
parentRunId,
|
|
@@ -123,17 +99,18 @@ describe("nested route index", () => {
|
|
|
123
99
|
});
|
|
124
100
|
|
|
125
101
|
describe("nested event route validation", () => {
|
|
126
|
-
it("resolves nested parent addresses
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
102
|
+
it("resolves nested parent addresses from the inherited child runtime", () => {
|
|
103
|
+
assert.deepEqual(inheritedNestedParentAddressOf({
|
|
104
|
+
nestedParent: {
|
|
105
|
+
parentRunId: "nested-parent",
|
|
106
|
+
parentChildIndex: 2,
|
|
107
|
+
depth: 3,
|
|
108
|
+
path: [
|
|
109
|
+
{ runId: "root-run", stepIndex: 0, agent: "root-agent" },
|
|
110
|
+
{ runId: "nested-parent", stepIndex: 2, agent: "nested-agent" },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
}), {
|
|
137
114
|
parentRunId: "nested-parent",
|
|
138
115
|
parentStepIndex: 2,
|
|
139
116
|
depth: 3,
|
|
@@ -142,26 +119,33 @@ describe("nested event route validation", () => {
|
|
|
142
119
|
{ runId: "nested-parent", stepIndex: 2, agent: "nested-agent" },
|
|
143
120
|
],
|
|
144
121
|
});
|
|
122
|
+
assert.deepEqual(inheritedNestedParentAddressOf({ nestedParent: { parentRunId: "nested-parent", depth: 1, path: [] } }), {
|
|
123
|
+
parentRunId: "nested-parent",
|
|
124
|
+
depth: 1,
|
|
125
|
+
path: [{ runId: "nested-parent" }],
|
|
126
|
+
});
|
|
145
127
|
});
|
|
146
128
|
|
|
147
|
-
it("ignores unsafe nested parent ids
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
assert.equal(resolveNestedParentAddressFromEnv(), undefined);
|
|
129
|
+
it("ignores unsafe nested parent ids and missing parents", () => {
|
|
130
|
+
assert.equal(inheritedNestedParentAddressOf({ nestedParent: { parentRunId: "../unsafe", parentChildIndex: 2, depth: 1, path: [] } }), undefined);
|
|
131
|
+
assert.equal(inheritedNestedParentAddressOf({}), undefined);
|
|
132
|
+
assert.equal(inheritedNestedParentAddressOf(undefined), undefined);
|
|
152
133
|
});
|
|
153
134
|
|
|
154
|
-
it("resolves only matching contained routes from
|
|
135
|
+
it("resolves only matching contained routes from the inherited child runtime", () => {
|
|
155
136
|
const route = trackRoute();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = route.rootRunId;
|
|
159
|
-
process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = route.capabilityToken;
|
|
160
|
-
|
|
161
|
-
assert.deepEqual(resolveNestedRouteFromEnv(), route);
|
|
137
|
+
assert.deepEqual(inheritedNestedRouteOf({ nestedRoute: route }), route);
|
|
138
|
+
assert.equal(inheritedNestedRouteOf({}), undefined);
|
|
162
139
|
|
|
163
|
-
|
|
164
|
-
|
|
140
|
+
const originalError = console.error;
|
|
141
|
+
const logged: unknown[] = [];
|
|
142
|
+
console.error = (...args: unknown[]) => { logged.push(args[1]); };
|
|
143
|
+
try {
|
|
144
|
+
assert.equal(inheritedNestedRouteOf({ nestedRoute: { ...route, capabilityToken: "wrong-token" } }), undefined);
|
|
145
|
+
} finally {
|
|
146
|
+
console.error = originalError;
|
|
147
|
+
}
|
|
148
|
+
assert.match(String(logged[0]), /capability token/);
|
|
165
149
|
});
|
|
166
150
|
});
|
|
167
151
|
|
|
@@ -326,6 +310,27 @@ describe("nested event parsing and projection", () => {
|
|
|
326
310
|
}]), true);
|
|
327
311
|
});
|
|
328
312
|
|
|
313
|
+
it("preserves rejected nested states across the event and registry boundary", () => {
|
|
314
|
+
const route = trackRoute("rejected-root");
|
|
315
|
+
writeNestedEvent(route, {
|
|
316
|
+
type: "subagent.nested.completed",
|
|
317
|
+
ts: 300,
|
|
318
|
+
parentRunId: route.rootRunId,
|
|
319
|
+
parentStepIndex: 0,
|
|
320
|
+
child: {
|
|
321
|
+
...child("rejected-child", "rejected", 300, route.rootRunId),
|
|
322
|
+
parentStepIndex: 0,
|
|
323
|
+
path: [{ runId: route.rootRunId, stepIndex: 0 }],
|
|
324
|
+
steps: [{ agent: "leaf", status: "rejected" }],
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const registry = projectNestedEvents(route);
|
|
329
|
+
assert.equal(registry.children[0]?.state, "rejected");
|
|
330
|
+
assert.equal(registry.children[0]?.steps?.[0]?.status, "rejected");
|
|
331
|
+
assert.equal(hasLiveNestedDescendants(registry.children), false);
|
|
332
|
+
});
|
|
333
|
+
|
|
329
334
|
it("accepts only complete numeric token usage at the nested event boundary", () => {
|
|
330
335
|
const route = trackRoute();
|
|
331
336
|
writeNestedEvent(route, {
|
|
@@ -489,4 +494,4 @@ describe("nested session-name projection", () => {
|
|
|
489
494
|
assert.equal(summary.sessionName, "reviewer: Inspect the changed auth middleware");
|
|
490
495
|
assert.equal(summary.steps?.[0]?.sessionName, "reviewer: Inspect the changed auth middleware");
|
|
491
496
|
});
|
|
492
|
-
});
|
|
497
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { it } from "node:test";
|
|
5
|
+
|
|
6
|
+
it("traces bounded completion reasons through NODE_DEBUG without changing delivery", () => {
|
|
7
|
+
const run = (debug: string) => {
|
|
8
|
+
const child = spawnSync(process.execPath, [
|
|
9
|
+
"--experimental-strip-types",
|
|
10
|
+
"--import", new URL("../support/isolated-temp-root.mjs", import.meta.url).href,
|
|
11
|
+
fileURLToPath(new URL("../support/notify-diagnostics-fixture.ts", import.meta.url)),
|
|
12
|
+
], { env: { ...process.env, NODE_DEBUG: debug, NODE_NO_WARNINGS: "1" }, encoding: "utf8" });
|
|
13
|
+
assert.equal(child.status, 0, child.stderr);
|
|
14
|
+
return child;
|
|
15
|
+
};
|
|
16
|
+
const disabled = run("");
|
|
17
|
+
const enabled = run("pi-subagents-notify");
|
|
18
|
+
assert.equal(disabled.stderr, "");
|
|
19
|
+
assert.equal(enabled.stdout, disabled.stdout);
|
|
20
|
+
assert.equal(JSON.parse(enabled.stdout).length, 7);
|
|
21
|
+
assert.deepEqual(JSON.parse(enabled.stdout).at(-1).options, { triggerTurn: false });
|
|
22
|
+
const records = enabled.stderr.trim().split("\n").map((line) => {
|
|
23
|
+
assert.ok(line.length < 512, line);
|
|
24
|
+
assert.match(line, /^PI-SUBAGENTS-NOTIFY \d+: /);
|
|
25
|
+
return JSON.parse(line.slice(line.indexOf("{")));
|
|
26
|
+
});
|
|
27
|
+
assert.deepEqual([...new Set(records.map((row) => row.reason))].sort(), [
|
|
28
|
+
"missing_session", "foreground_session_mismatch", "not_owned", "intercom_delivered",
|
|
29
|
+
"batch_deferred", "deduped_pending", "send_accepted", "deduped_ttl", "emit_not_owned",
|
|
30
|
+
"send_failed", "dispose_pending", "disposed",
|
|
31
|
+
].sort());
|
|
32
|
+
assert.deepEqual(records.filter((row) => row.id === "first").map((row) => row.reason), [
|
|
33
|
+
"batch_deferred", "deduped_pending", "send_accepted", "deduped_ttl", "batch_deferred", "send_accepted",
|
|
34
|
+
]);
|
|
35
|
+
assert.deepEqual(records.filter((row) => row.id === "unbatched").map((row) => row.reason), ["send_accepted"]);
|
|
36
|
+
for (const row of records) {
|
|
37
|
+
assert.deepEqual(Object.keys(row).sort(), ["id", "reason", "runId", "source"]);
|
|
38
|
+
assert.ok(row.id.length <= 128);
|
|
39
|
+
assert.ok(row.runId.length <= 128);
|
|
40
|
+
}
|
|
41
|
+
assert.doesNotMatch(enabled.stderr, /PRIVATE_|owner-a|session-a|\u001b/);
|
|
42
|
+
});
|
|
@@ -14,6 +14,39 @@ import { createResultDeliveryOwnership } from "../../src/runs/background/result-
|
|
|
14
14
|
|
|
15
15
|
const COMPLETION_OWNER_ID = "completion-owner-a";
|
|
16
16
|
|
|
17
|
+
it("keeps model-authored receipt lines in the preview, never in receipt metadata", () => {
|
|
18
|
+
for (const resultPreview of [
|
|
19
|
+
"Workflow receipt: /model/start.json\nKeep this output.",
|
|
20
|
+
"Before\nWorkflow receipt: /model/middle.json\nAfter",
|
|
21
|
+
"Before\n\nWorkflow receipt: /model/suffix.json",
|
|
22
|
+
]) {
|
|
23
|
+
for (const workflowReceiptPath of [undefined, "/published/receipt.json"]) {
|
|
24
|
+
const details: SubagentNotifyDetails = { agent: "workflow", status: "completed", resultPreview, workflowReceiptPath };
|
|
25
|
+
const parsed = parseSubagentNotifyContent(formatSingleCompletion(details));
|
|
26
|
+
assert.equal(parsed?.resultPreview, resultPreview);
|
|
27
|
+
assert.equal(parsed?.workflowReceiptPath, workflowReceiptPath);
|
|
28
|
+
const scheduled = parseSubagentNotifyContent(formatSingleCompletion({ ...details, scheduleOrigin: { id: "schedule-1" } }));
|
|
29
|
+
assert.equal(scheduled?.resultPreview, resultPreview);
|
|
30
|
+
assert.equal(scheduled?.workflowReceiptPath, workflowReceiptPath);
|
|
31
|
+
assert.deepEqual(scheduled?.scheduleOrigin, { id: "schedule-1" });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("surfaces published workflow receipts outside single and grouped previews", () => {
|
|
37
|
+
const workflowReceiptPath = "/opaque/receipt.json";
|
|
38
|
+
const details = buildCompletionDetails({ agent: "workflow", mode: "workflow", runId: "run-1", success: true, summary: "Completed", results: [{ runId: "child-1", output: "x".repeat(20_000) }], workflowReceipt: { path: workflowReceiptPath, receipt: {} } });
|
|
39
|
+
assert.equal(details.workflowReceiptPath, workflowReceiptPath);
|
|
40
|
+
const single = formatSingleCompletion(details);
|
|
41
|
+
assert.equal(single.split("\n")[1], `Workflow receipt: ${workflowReceiptPath}`);
|
|
42
|
+
assert.match(single, /\[preview truncated\]/);
|
|
43
|
+
assert.ok(single.includes(`Workflow receipt: ${workflowReceiptPath}`));
|
|
44
|
+
assert.ok(formatGroupedCompletion([details, details]).includes(`Workflow receipt: ${workflowReceiptPath}`));
|
|
45
|
+
const parsed = parseSubagentNotifyContent(single);
|
|
46
|
+
assert.equal(parsed?.workflowReceiptPath, workflowReceiptPath);
|
|
47
|
+
assert.doesNotMatch(parsed?.resultPreview ?? "", /Workflow receipt:/);
|
|
48
|
+
});
|
|
49
|
+
|
|
17
50
|
function createEventBus() {
|
|
18
51
|
const emitter = new EventEmitter();
|
|
19
52
|
return {
|
|
@@ -192,9 +225,11 @@ describe("registerSubagentNotify", () => {
|
|
|
192
225
|
});
|
|
193
226
|
try {
|
|
194
227
|
const pending = notifier.deliver(completionResult({ id: "delayed-unclaimed" }));
|
|
228
|
+
assert.equal(notifier.hasPendingDelivery(), true);
|
|
195
229
|
state.currentSessionId = "session-b";
|
|
196
230
|
clock.advance(150);
|
|
197
231
|
assert.equal(await pending, false);
|
|
232
|
+
assert.equal(notifier.hasPendingDelivery(), false);
|
|
198
233
|
assert.equal(sent.length, 0);
|
|
199
234
|
} finally {
|
|
200
235
|
notifier.dispose();
|
|
@@ -217,8 +252,10 @@ describe("registerSubagentNotify", () => {
|
|
|
217
252
|
const clock = createFakeClock();
|
|
218
253
|
const { notifier, sent } = createBatchingPi(clock);
|
|
219
254
|
const pending = notifier.deliver(completionResult({ id: "dispose-pending" }));
|
|
255
|
+
assert.equal(notifier.hasPendingDelivery(), true);
|
|
220
256
|
notifier.dispose();
|
|
221
257
|
assert.equal(await pending, false);
|
|
258
|
+
assert.equal(notifier.hasPendingDelivery(), false);
|
|
222
259
|
clock.advance(1000);
|
|
223
260
|
assert.equal(sent.length, 0);
|
|
224
261
|
});
|
|
@@ -710,6 +747,53 @@ describe("completion formatting helpers", () => {
|
|
|
710
747
|
assert.equal(details.status, "completed");
|
|
711
748
|
});
|
|
712
749
|
|
|
750
|
+
it("surfaces structured output in workflow previews and direct notices for degenerate text", () => {
|
|
751
|
+
const summary = "Workflow completed with 1 child run(s). Return: { answer: 42 } Emitted: ready Trace: 2 event(s).";
|
|
752
|
+
for (const output of ["</think>", " \n\t", " \n</think> \n"]) {
|
|
753
|
+
const result = {
|
|
754
|
+
id: "workflow-think-tag", agent: "workflow", success: true, summary,
|
|
755
|
+
results: [{
|
|
756
|
+
workflowKey: "review", runId: "child-review", agent: "delegate", success: true,
|
|
757
|
+
outputState: "present" as const, outputReference: "/tmp/review.json", output, structuredOutput: false,
|
|
758
|
+
}],
|
|
759
|
+
};
|
|
760
|
+
const details = buildCompletionDetails(result);
|
|
761
|
+
const content = formatSingleCompletion(details);
|
|
762
|
+
assert.equal(details.resultPreview, summary);
|
|
763
|
+
assert.ok(content.includes(summary));
|
|
764
|
+
assert.match(content, /key=review run=child-review status=completed/);
|
|
765
|
+
assert.match(content, /Saved output: \/tmp\/review\.json/);
|
|
766
|
+
assert.match(content, /Workflow run: workflow-think-tag/);
|
|
767
|
+
assert.match(content, /Child runs: review=child-review \(completed\)/);
|
|
768
|
+
assert.match(content, / \| false/);
|
|
769
|
+
const direct = buildCompletionDetails({ ...result, agent: "delegate", summary: `delegate:\n${output}` });
|
|
770
|
+
assert.match(formatSingleCompletion(direct), /Structured output:\nfalse/);
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it("preserves meaningful prose and direct diagnostics alongside structured output", () => {
|
|
775
|
+
const output = "Review complete </think> with notes.";
|
|
776
|
+
const child = { agent: "delegate", success: true, output, structuredOutput: { ok: true } };
|
|
777
|
+
const workflow = buildCompletionDetails({ id: "prose-run", agent: "workflow", success: true, results: [child] });
|
|
778
|
+
assert.equal(workflow.childOutputs?.[0]?.preview, output);
|
|
779
|
+
const direct = buildCompletionDetails({ id: "prose-run", agent: "delegate", success: true, summary: `delegate:\n${output}`, results: [child] });
|
|
780
|
+
assert.equal(direct.resultPreview, `delegate:\n${output}`);
|
|
781
|
+
const diagnostic = "delegate:\n</think>\nError: validation failed.";
|
|
782
|
+
const failed = buildCompletionDetails({ id: "error-run", agent: "delegate", success: false, summary: diagnostic, results: [{ ...child, success: false, output: "</think>" }] });
|
|
783
|
+
assert.equal(failed.resultPreview, diagnostic);
|
|
784
|
+
assert.match(formatSingleCompletion(failed), /Background task failed/);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it("retains tag text when structured output is unavailable or unserializable", () => {
|
|
788
|
+
for (const structuredOutput of [undefined, 1n]) {
|
|
789
|
+
const child = { agent: "delegate", success: true, output: "</think>", structuredOutput };
|
|
790
|
+
const workflow = buildCompletionDetails({ id: "fallback-run", agent: "workflow", success: true, results: [child] });
|
|
791
|
+
assert.equal(workflow.childOutputs?.[0]?.preview, "</think>");
|
|
792
|
+
const direct = buildCompletionDetails({ id: "fallback-run", agent: "delegate", success: true, summary: "delegate:\n</think>", results: [child] });
|
|
793
|
+
assert.equal(direct.resultPreview, "delegate:\n</think>");
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
713
797
|
it("surfaces direct structured output when the completion has no text output", () => {
|
|
714
798
|
const details = buildCompletionDetails({
|
|
715
799
|
id: "structured-run",
|