@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
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripted in-process child sessions for tests.
|
|
3
|
+
*
|
|
4
|
+
* Foreground runs and the detached async runner create their children through
|
|
5
|
+
* `ChildSessionFactory`. This factory replays the responses `createMockPi()`
|
|
6
|
+
* queues, so one `mockPi.onCall(...)` feeds whichever launch path a test
|
|
7
|
+
* exercises. A scripted `stdoutRaw` is parsed as JSON event lines so older
|
|
8
|
+
* fixtures still replay; there is no stdout transport. `stderr` only names the
|
|
9
|
+
* failure a non-zero `exitCode` raises.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
15
|
+
import type { ChildSession, ChildSessionEvent, ChildSessionFactory, ChildSessionLaunch } from "../../src/runs/shared/child-session.ts";
|
|
16
|
+
import { isChildWatchdogStatusEvent } from "../../src/watchdog/child-status.ts";
|
|
17
|
+
|
|
18
|
+
export interface FakeChildResponse {
|
|
19
|
+
output?: string;
|
|
20
|
+
stderr?: string;
|
|
21
|
+
exitCode?: number;
|
|
22
|
+
delay?: number;
|
|
23
|
+
waitForPath?: string;
|
|
24
|
+
keepAliveAfterFinalMessageMs?: number;
|
|
25
|
+
jsonl?: unknown[];
|
|
26
|
+
/** Raw JSON lines; parsed into events for the in-process child without acceptance-report injection. */
|
|
27
|
+
stdoutRaw?: string;
|
|
28
|
+
steps?: Array<{ delay?: number; waitForPath?: string; jsonl?: unknown[]; stdoutRaw?: string }>;
|
|
29
|
+
/** Emits one assistant message with a JSON object of the listed names mapped to null; in-process children have no launch environment. */
|
|
30
|
+
echoEnv?: string[];
|
|
31
|
+
matchArgIncludes?: string | string[];
|
|
32
|
+
/** Files the fake child writes before emitting output, standing in for its write-tool side effects. */
|
|
33
|
+
writeFiles?: Array<{ path: string; content: string }>;
|
|
34
|
+
/** Captures structured output through the runtime config without emitting a structured_output tool event. */
|
|
35
|
+
structuredOutputCapture?: unknown;
|
|
36
|
+
structuredOutputAcceptanceReport?: unknown;
|
|
37
|
+
/** Captures structured output and emits the structured_output tool events. */
|
|
38
|
+
structuredOutput?: unknown;
|
|
39
|
+
runtimeAcknowledgedExtensions?: unknown;
|
|
40
|
+
missingTools?: string[];
|
|
41
|
+
/** Rejects the session creation itself (a startup failure). */
|
|
42
|
+
createError?: string;
|
|
43
|
+
/** Keeps the run open until the parent aborts it. */
|
|
44
|
+
hangUntilAbort?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface FakeChildSessionRecord {
|
|
48
|
+
launch: ChildSessionLaunch;
|
|
49
|
+
task: string | undefined;
|
|
50
|
+
steers: Array<{ text: string; mode: "steer" | "followUp" }>;
|
|
51
|
+
aborted: boolean;
|
|
52
|
+
disposed: boolean;
|
|
53
|
+
settled: boolean;
|
|
54
|
+
session?: ChildSession;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface FakeChildSessions {
|
|
58
|
+
readonly sessions: FakeChildSessionRecord[];
|
|
59
|
+
readonly factory: ChildSessionFactory;
|
|
60
|
+
readonly disposeCalls: number;
|
|
61
|
+
reset(): void;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function listPendingFiles(dir: string): string[] {
|
|
65
|
+
try {
|
|
66
|
+
return fs.readdirSync(dir).filter((name) => name.startsWith("pending-") && name.endsWith(".json")).sort();
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readJson(filePath: string): FakeChildResponse | undefined {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8")) as FakeChildResponse;
|
|
75
|
+
} catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function responseMatches(response: FakeChildResponse, haystack: string): boolean {
|
|
81
|
+
const matcher = response.matchArgIncludes;
|
|
82
|
+
if (matcher === undefined) return true;
|
|
83
|
+
const needles = Array.isArray(matcher) ? matcher : [matcher];
|
|
84
|
+
return needles.every((needle) => typeof needle === "string" && haystack.includes(needle));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function claimResponse(dir: string, fileName: string): FakeChildResponse | undefined {
|
|
88
|
+
const sourcePath = path.join(dir, fileName);
|
|
89
|
+
const targetPath = path.join(dir, fileName.replace(/^pending-/, "consumed-"));
|
|
90
|
+
try {
|
|
91
|
+
fs.renameSync(sourcePath, targetPath);
|
|
92
|
+
return JSON.parse(fs.readFileSync(targetPath, "utf-8")) as FakeChildResponse;
|
|
93
|
+
} catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function claimNextResponse(dir: string, haystack: string): FakeChildResponse {
|
|
99
|
+
for (const withMatcher of [true, false]) {
|
|
100
|
+
for (const fileName of listPendingFiles(dir)) {
|
|
101
|
+
const response = readJson(path.join(dir, fileName));
|
|
102
|
+
if (!response || Object.prototype.hasOwnProperty.call(response, "matchArgIncludes") !== withMatcher) continue;
|
|
103
|
+
if (!responseMatches(response, haystack)) continue;
|
|
104
|
+
const claimed = claimResponse(dir, fileName);
|
|
105
|
+
if (claimed) return claimed;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const fallback = readJson(path.join(dir, "default-response.json"));
|
|
109
|
+
if (fallback && responseMatches(fallback, haystack)) return fallback;
|
|
110
|
+
return { output: "ok" };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Synthetic argv in the shape existing call assertions read; a child session receives no argv. */
|
|
114
|
+
export function fakeChildArgs(launch: ChildSessionLaunch, task: string): string[] {
|
|
115
|
+
const args: string[] = ["--mode", "json", "-p"];
|
|
116
|
+
if (launch.storage.kind === "file") args.push("--session", launch.storage.sessionFile);
|
|
117
|
+
else if (launch.storage.kind === "dir") args.push("--session-dir", launch.storage.sessionDir);
|
|
118
|
+
else if (launch.storage.kind === "memory") args.push("--no-session");
|
|
119
|
+
if (launch.model) args.push("--model", launch.model);
|
|
120
|
+
if (launch.tools) {
|
|
121
|
+
if (launch.tools.length > 0) args.push("--tools", launch.tools.join(","));
|
|
122
|
+
else args.push("--no-tools");
|
|
123
|
+
} else if (launch.excludeTools?.length) args.push("--exclude-tools", launch.excludeTools.join(","));
|
|
124
|
+
if (!launch.ambientExtensions) args.push("--no-extensions");
|
|
125
|
+
for (const extensionPath of launch.extensionPaths) args.push("--extension", extensionPath);
|
|
126
|
+
if (launch.noContextFiles) args.push("--no-context-files");
|
|
127
|
+
if (launch.noSkills) args.push("--no-skills");
|
|
128
|
+
if (launch.systemPrompt !== undefined) args.push("--system-prompt", launch.systemPrompt);
|
|
129
|
+
if (launch.appendSystemPrompt !== undefined) args.push("--append-system-prompt", launch.appendSystemPrompt);
|
|
130
|
+
args.push(task);
|
|
131
|
+
return args;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function defaultAssistantMessage(output: string, model: string | undefined): unknown {
|
|
135
|
+
return {
|
|
136
|
+
type: "message_end",
|
|
137
|
+
message: {
|
|
138
|
+
role: "assistant",
|
|
139
|
+
content: [{ type: "text", text: output }],
|
|
140
|
+
model: model ?? "mock/test-model",
|
|
141
|
+
stopReason: "stop",
|
|
142
|
+
usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, cost: { total: 0.001 } },
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function defaultAcceptanceReport(): string {
|
|
148
|
+
return [
|
|
149
|
+
"```acceptance-report",
|
|
150
|
+
JSON.stringify({
|
|
151
|
+
criteriaSatisfied: [
|
|
152
|
+
{ id: "criterion-1", status: "satisfied", evidence: "mock acceptance evidence" },
|
|
153
|
+
{ id: "criterion-2", status: "satisfied", evidence: "mock acceptance evidence" },
|
|
154
|
+
],
|
|
155
|
+
changedFiles: ["mock-file.ts"],
|
|
156
|
+
testsAddedOrUpdated: ["mock-file.test.ts"],
|
|
157
|
+
commandsRun: [{ command: "mock validation", result: "passed", summary: "passed" }],
|
|
158
|
+
validationOutput: ["mock validation passed"],
|
|
159
|
+
residualRisks: [],
|
|
160
|
+
noStagedFiles: true,
|
|
161
|
+
reviewFindings: [],
|
|
162
|
+
manualNotes: "mock run completed",
|
|
163
|
+
notes: "mock run completed",
|
|
164
|
+
}),
|
|
165
|
+
"```",
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function withAcceptanceReport(output: string, prompt: string): string {
|
|
170
|
+
if (!prompt.includes("## Acceptance Contract") || output.includes("```acceptance-report")) return output;
|
|
171
|
+
return `${output}\n${defaultAcceptanceReport()}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function sleep(ms: number, until?: Promise<void>): Promise<void> {
|
|
175
|
+
const wait = new Promise<void>((resolve) => setTimeout(resolve, ms));
|
|
176
|
+
return until ? Promise.race([wait, until]) : wait;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parseRawStdout(response: FakeChildResponse): unknown[] {
|
|
180
|
+
const chunks: Buffer[] = [];
|
|
181
|
+
if (typeof response.stdoutRaw === "string") chunks.push(Buffer.from(response.stdoutRaw, "utf-8"));
|
|
182
|
+
const entries: unknown[] = [];
|
|
183
|
+
for (const line of Buffer.concat(chunks).toString("utf-8").split("\n")) {
|
|
184
|
+
if (!line.trim()) continue;
|
|
185
|
+
try {
|
|
186
|
+
entries.push(JSON.parse(line));
|
|
187
|
+
} catch {
|
|
188
|
+
// Non-JSON stdout is not an event for an in-process child.
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return entries;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Model id without a `:thinking` suffix, as the child would report it. */
|
|
195
|
+
function reportedModel(model: string | undefined): string | undefined {
|
|
196
|
+
if (!model) return undefined;
|
|
197
|
+
const colon = model.lastIndexOf(":");
|
|
198
|
+
if (colon === -1) return model;
|
|
199
|
+
const suffix = model.slice(colon + 1);
|
|
200
|
+
return ["off", "minimal", "low", "medium", "high", "xhigh"].includes(suffix) ? model.slice(0, colon) : model;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function createFakeChildSessions(queueDir: () => string): FakeChildSessions {
|
|
204
|
+
const sessions: FakeChildSessionRecord[] = [];
|
|
205
|
+
let disposeCalls = 0;
|
|
206
|
+
const factory: ChildSessionFactory = {
|
|
207
|
+
async create(launch) {
|
|
208
|
+
const record: FakeChildSessionRecord = { launch, task: undefined, steers: [], aborted: false, disposed: false, settled: false };
|
|
209
|
+
sessions.push(record);
|
|
210
|
+
const listeners = new Set<(event: ChildSessionEvent) => void>();
|
|
211
|
+
const messages: AgentMessage[] = [];
|
|
212
|
+
const model = reportedModel(launch.model);
|
|
213
|
+
let abortResolve: (() => void) | undefined;
|
|
214
|
+
const abortedPromise = new Promise<void>((resolve) => { abortResolve = resolve; });
|
|
215
|
+
const sessionId = randomUUID();
|
|
216
|
+
let sessionFile: string | undefined;
|
|
217
|
+
if (launch.storage.kind === "file") {
|
|
218
|
+
sessionFile = launch.storage.sessionFile;
|
|
219
|
+
fs.mkdirSync(path.dirname(sessionFile), { recursive: true });
|
|
220
|
+
fs.writeFileSync(sessionFile, "", { flag: "a" });
|
|
221
|
+
} else if (launch.storage.kind === "dir") {
|
|
222
|
+
// A real session writes its file on the first message; the scripted child leaves the directory empty.
|
|
223
|
+
fs.mkdirSync(launch.storage.sessionDir, { recursive: true });
|
|
224
|
+
}
|
|
225
|
+
/** Steers are also appended to `steers.jsonl` in the queue dir so a test can observe a child hosted in another process. */
|
|
226
|
+
const recordSteer = (text: string, mode: "steer" | "followUp"): void => {
|
|
227
|
+
try {
|
|
228
|
+
fs.appendFileSync(path.join(queueDir(), "steers.jsonl"), `${JSON.stringify({ sessionId, text, mode })}\n`, "utf-8");
|
|
229
|
+
} catch {
|
|
230
|
+
// Observability for tests only.
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
const emit = (event: unknown): void => {
|
|
234
|
+
const typed = event as ChildSessionEvent & { message?: AgentMessage };
|
|
235
|
+
if ((typed.type === "message_end" || typed.type === "tool_result_end") && typed.message) messages.push(typed.message);
|
|
236
|
+
for (const listener of [...listeners]) listener(typed);
|
|
237
|
+
};
|
|
238
|
+
let sawProviderError = false;
|
|
239
|
+
const emitEntries = async (entries: unknown[], task: string, injectAcceptance = true): Promise<void> => {
|
|
240
|
+
for (const entry of entries) {
|
|
241
|
+
if (record.aborted) return;
|
|
242
|
+
const typed = entry as { type?: string; message?: { content?: Array<{ type?: string; text?: string }>; errorMessage?: string; stopReason?: string } };
|
|
243
|
+
if (typed?.type === "message_end" && injectAcceptance) {
|
|
244
|
+
const textPart = typed.message?.content?.find?.((part) => part?.type === "text");
|
|
245
|
+
const providerError = Boolean(typed.message?.errorMessage || typed.message?.stopReason === "error");
|
|
246
|
+
if (providerError) sawProviderError = true;
|
|
247
|
+
// Native acceptance lives in system resources, not the compactable task.
|
|
248
|
+
if (!providerError && textPart && typeof textPart.text === "string" && (!sawProviderError || textPart.text.trim())) textPart.text = withAcceptanceReport(textPart.text, [launch.systemPrompt, launch.appendSystemPrompt, task].join("\n"));
|
|
249
|
+
}
|
|
250
|
+
// A real child's watchdog hook reports through the host's sink, not the session stream.
|
|
251
|
+
if (isChildWatchdogStatusEvent(entry)) launch.runtime.watchdogStatus?.(entry);
|
|
252
|
+
else emit(entry);
|
|
253
|
+
await Promise.resolve();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const waitForReleasePath = async (releasePath: string | undefined): Promise<void> => {
|
|
257
|
+
if (typeof releasePath !== "string") return;
|
|
258
|
+
const deadline = Date.now() + 30_000;
|
|
259
|
+
while (!fs.existsSync(releasePath)) {
|
|
260
|
+
if (record.aborted) return;
|
|
261
|
+
if (Date.now() >= deadline) throw new Error(`Timed out waiting for mock release path: ${releasePath}`);
|
|
262
|
+
await sleep(20, abortedPromise);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const runScript = async (response: FakeChildResponse, task: string): Promise<void> => {
|
|
266
|
+
if (typeof response.delay === "number" && response.delay > 0) await sleep(response.delay, abortedPromise);
|
|
267
|
+
await waitForReleasePath(response.waitForPath);
|
|
268
|
+
if (record.aborted) return;
|
|
269
|
+
for (const file of response.writeFiles ?? []) {
|
|
270
|
+
if (!file || typeof file.path !== "string" || typeof file.content !== "string") continue;
|
|
271
|
+
const target = path.resolve(launch.cwd, file.path);
|
|
272
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
273
|
+
fs.writeFileSync(target, file.content, "utf-8");
|
|
274
|
+
}
|
|
275
|
+
if (Object.prototype.hasOwnProperty.call(response, "structuredOutputCapture")) {
|
|
276
|
+
launch.runtime.structuredOutput?.capture(
|
|
277
|
+
response.structuredOutputCapture,
|
|
278
|
+
Object.prototype.hasOwnProperty.call(response, "structuredOutputAcceptanceReport") ? response.structuredOutputAcceptanceReport : undefined,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (Object.prototype.hasOwnProperty.call(response, "runtimeAcknowledgedExtensions")) {
|
|
282
|
+
const raw = response.runtimeAcknowledgedExtensions as { ids?: unknown } | unknown[];
|
|
283
|
+
const ids = Array.isArray(raw) ? raw : Array.isArray(raw?.ids) ? raw.ids : [];
|
|
284
|
+
launch.runtime.runtimeAcknowledgements?.(ids.filter((id): id is string => typeof id === "string"));
|
|
285
|
+
}
|
|
286
|
+
if (Array.isArray(response.missingTools) && response.missingTools.length > 0) {
|
|
287
|
+
const required = launch.runtime.requiredTools ?? [];
|
|
288
|
+
const missing = response.missingTools.filter((name) => required.includes(name));
|
|
289
|
+
if (missing.length > 0) {
|
|
290
|
+
launch.runtime.toolDiagnostic?.({
|
|
291
|
+
...(launch.runtime.agent ? { agent: launch.runtime.agent } : {}),
|
|
292
|
+
required,
|
|
293
|
+
available: required.filter((name) => !missing.includes(name)),
|
|
294
|
+
missing,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
emit({ type: "agent_start" });
|
|
299
|
+
if (Array.isArray(response.steps) && response.steps.length > 0) {
|
|
300
|
+
for (const step of response.steps) {
|
|
301
|
+
if (typeof step?.delay === "number" && step.delay > 0) await sleep(step.delay, abortedPromise);
|
|
302
|
+
await waitForReleasePath(step?.waitForPath);
|
|
303
|
+
if (record.aborted) return;
|
|
304
|
+
if (Array.isArray(step?.jsonl)) await emitEntries(step.jsonl, task);
|
|
305
|
+
await emitEntries(parseRawStdout(step ?? {}), task, false);
|
|
306
|
+
}
|
|
307
|
+
} else if (Array.isArray(response.jsonl) && response.jsonl.length > 0) {
|
|
308
|
+
await emitEntries(response.jsonl, task);
|
|
309
|
+
} else if (response.stdoutRaw !== undefined) {
|
|
310
|
+
await emitEntries(parseRawStdout(response), task, false);
|
|
311
|
+
} else if (Array.isArray(response.echoEnv) && response.echoEnv.length > 0) {
|
|
312
|
+
await emitEntries([defaultAssistantMessage(JSON.stringify(Object.fromEntries(response.echoEnv.map((key) => [key, null]))), model)], task);
|
|
313
|
+
} else if (typeof response.output === "string") {
|
|
314
|
+
await emitEntries([defaultAssistantMessage(response.output, model)], task);
|
|
315
|
+
}
|
|
316
|
+
if (Object.prototype.hasOwnProperty.call(response, "structuredOutput")) {
|
|
317
|
+
launch.runtime.structuredOutput?.capture(response.structuredOutput, undefined);
|
|
318
|
+
emit({ type: "tool_execution_start", toolName: "structured_output", args: { value: response.structuredOutput } });
|
|
319
|
+
emit({ type: "tool_result_end", message: { role: "toolResult", toolName: "structured_output", content: [{ type: "text", text: "Structured output captured." }] } });
|
|
320
|
+
emit({ type: "tool_execution_end", toolName: "structured_output" });
|
|
321
|
+
}
|
|
322
|
+
if (record.aborted) return;
|
|
323
|
+
emit({ type: "agent_end", messages: [...messages], willRetry: false });
|
|
324
|
+
emit({ type: "agent_settled" });
|
|
325
|
+
if (typeof response.keepAliveAfterFinalMessageMs === "number" && response.keepAliveAfterFinalMessageMs > 0) {
|
|
326
|
+
await Promise.race([sleep(response.keepAliveAfterFinalMessageMs), abortedPromise]);
|
|
327
|
+
}
|
|
328
|
+
if (response.hangUntilAbort) await abortedPromise;
|
|
329
|
+
if (typeof response.exitCode === "number" && response.exitCode !== 0) {
|
|
330
|
+
throw new Error(response.stderr?.trim() || `mock child exited with code ${response.exitCode}`);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
const session: ChildSession = {
|
|
334
|
+
subscribe(listener) {
|
|
335
|
+
listeners.add(listener);
|
|
336
|
+
return () => listeners.delete(listener);
|
|
337
|
+
},
|
|
338
|
+
async prompt(text) {
|
|
339
|
+
record.task = text;
|
|
340
|
+
const systemPrompt = launch.systemPrompt ?? launch.appendSystemPrompt ?? "";
|
|
341
|
+
const args = fakeChildArgs(launch, text);
|
|
342
|
+
const response = claimNextResponse(queueDir(), `${args.join("\n")}\n${systemPrompt}`);
|
|
343
|
+
const dir = queueDir();
|
|
344
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
345
|
+
const callPath = path.join(dir, `call-${Date.now()}-${process.pid}-${Math.random().toString(16).slice(2)}.json`);
|
|
346
|
+
fs.writeFileSync(callPath, JSON.stringify({
|
|
347
|
+
args,
|
|
348
|
+
effectiveArgs: args,
|
|
349
|
+
cwd: launch.cwd,
|
|
350
|
+
systemPrompts: launch.systemPrompt !== undefined
|
|
351
|
+
? [{ mode: "--system-prompt", text: launch.systemPrompt }]
|
|
352
|
+
: launch.appendSystemPrompt !== undefined
|
|
353
|
+
? [{ mode: "--append-system-prompt", text: launch.appendSystemPrompt }]
|
|
354
|
+
: [],
|
|
355
|
+
requiredChildTools: launch.runtime.requiredTools ?? [],
|
|
356
|
+
launch: {
|
|
357
|
+
cwd: launch.cwd,
|
|
358
|
+
storage: launch.storage,
|
|
359
|
+
model: launch.model,
|
|
360
|
+
tools: launch.tools,
|
|
361
|
+
excludeTools: launch.excludeTools,
|
|
362
|
+
extensionPaths: launch.extensionPaths,
|
|
363
|
+
ambientExtensions: launch.ambientExtensions,
|
|
364
|
+
hooks: launch.hooks.map((hook) => hook.name),
|
|
365
|
+
noSkills: launch.noSkills,
|
|
366
|
+
noContextFiles: launch.noContextFiles,
|
|
367
|
+
processEnv: launch.processEnv,
|
|
368
|
+
},
|
|
369
|
+
runtime: { ...launch.runtime, structuredOutput: launch.runtime.structuredOutput ? { schema: launch.runtime.structuredOutput.schema, acceptanceReport: launch.runtime.structuredOutput.acceptanceReport } : undefined },
|
|
370
|
+
}), "utf-8");
|
|
371
|
+
if (response.createError) throw new Error(response.createError);
|
|
372
|
+
try {
|
|
373
|
+
await runScript(response, text);
|
|
374
|
+
} finally {
|
|
375
|
+
record.settled = true;
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
async steer(text) {
|
|
379
|
+
record.steers.push({ text, mode: "steer" });
|
|
380
|
+
recordSteer(text, "steer");
|
|
381
|
+
},
|
|
382
|
+
async followUp(text) {
|
|
383
|
+
record.steers.push({ text, mode: "followUp" });
|
|
384
|
+
recordSteer(text, "followUp");
|
|
385
|
+
},
|
|
386
|
+
async abort() {
|
|
387
|
+
record.aborted = true;
|
|
388
|
+
abortResolve?.();
|
|
389
|
+
},
|
|
390
|
+
async dispose() {
|
|
391
|
+
record.disposed = true;
|
|
392
|
+
},
|
|
393
|
+
get messages() { return messages; },
|
|
394
|
+
get sessionFile() { return sessionFile; },
|
|
395
|
+
get sessionId() { return sessionId; },
|
|
396
|
+
get modelId() { return model; },
|
|
397
|
+
};
|
|
398
|
+
record.session = session;
|
|
399
|
+
return session;
|
|
400
|
+
},
|
|
401
|
+
async dispose() {
|
|
402
|
+
disposeCalls += 1;
|
|
403
|
+
for (const record of sessions) {
|
|
404
|
+
if (record.session?.detached) continue;
|
|
405
|
+
if (record.session) record.session.shutDown = true;
|
|
406
|
+
await record.session?.abort();
|
|
407
|
+
record.aborted = true;
|
|
408
|
+
record.disposed = true;
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
return {
|
|
413
|
+
sessions,
|
|
414
|
+
factory,
|
|
415
|
+
get disposeCalls() { return disposeCalls; },
|
|
416
|
+
reset() { sessions.length = 0; },
|
|
417
|
+
};
|
|
418
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Separate caller process: deliberately no local workflow/controller state.
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { createSubagentExecutor } from "../../src/runs/foreground/subagent-executor.ts";
|
|
4
|
+
import { makeMinimalCtx, createEventBus } from "./helpers.ts";
|
|
5
|
+
import { currentCompletionOwnerId } from "../../src/shared/completion-owner.ts";
|
|
6
|
+
const [cwd, sessionFile, paramsJson, output, policy = "auto"] = process.argv.slice(2);
|
|
7
|
+
const state = { baseCwd: cwd, currentSessionId: null, asyncJobs: new Map(), foregroundControls: new Map(), lastForegroundControlId: null };
|
|
8
|
+
const executor = createSubagentExecutor({ pi: { events: createEventBus(), getSessionName: () => undefined }, state, config: { authorityPolicy: { steerRun: policy } }, asyncByDefault: false, tempArtifactsDir: cwd, getSubagentSessionRoot: () => cwd, expandTilde: p => p, discoverAgents: () => ({ agents: [] }), kill: () => { throw new Error("foreign steering must not probe/kill an owner"); } });
|
|
9
|
+
const ctx = makeMinimalCtx(cwd);
|
|
10
|
+
ctx.sessionManager.getSessionFile = () => sessionFile;
|
|
11
|
+
ctx.sessionManager.getSessionId = () => "different-runtime-id-in-caller";
|
|
12
|
+
const result = await executor.execute("foreign-steer", { action: "steer", message: "B actual tool route", ...JSON.parse(paramsJson) }, new AbortController().signal, undefined, ctx);
|
|
13
|
+
fs.writeFileSync(output, JSON.stringify({ pid: process.pid, sessionId: state.currentSessionId, completionOwnerId: currentCompletionOwnerId(), controllers: state.workflowControllers?.size ?? 0, controls: state.foregroundControls.size, result }));
|
|
@@ -1,42 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scripted child sessions for tests.
|
|
3
|
+
*
|
|
4
|
+
* `install()` replaces the process-wide foreground `ChildSessionFactory` with
|
|
5
|
+
* a scripted one and names the runner-side scripted factory module for
|
|
6
|
+
* background launches. Both replay the responses queued with `onCall()` from
|
|
7
|
+
* one directory, so a test scripts a child the same way whichever launch path
|
|
8
|
+
* it exercises.
|
|
9
|
+
*/
|
|
1
10
|
import * as fs from "node:fs";
|
|
2
11
|
import * as os from "node:os";
|
|
3
12
|
import * as path from "node:path";
|
|
4
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { setChildSessionFactory, setChildSessionFactoryModule } from "../../src/runs/shared/child-session.ts";
|
|
15
|
+
import { createFakeChildSessions, type FakeChildResponse, type FakeChildSessionRecord } from "./fake-child-session.ts";
|
|
5
16
|
|
|
6
|
-
|
|
7
|
-
output?: string;
|
|
8
|
-
stderr?: string;
|
|
9
|
-
exitCode?: number;
|
|
10
|
-
signal?: NodeJS.Signals;
|
|
11
|
-
delay?: number;
|
|
12
|
-
waitForPath?: string;
|
|
13
|
-
keepAliveAfterFinalMessageMs?: number;
|
|
14
|
-
ignoreSigterm?: boolean;
|
|
15
|
-
jsonl?: unknown[];
|
|
16
|
-
stdoutRaw?: string;
|
|
17
|
-
stdoutBase64Chunks?: string[];
|
|
18
|
-
steps?: Array<{
|
|
19
|
-
delay?: number;
|
|
20
|
-
waitForPath?: string;
|
|
21
|
-
jsonl?: unknown[];
|
|
22
|
-
stdoutRaw?: string;
|
|
23
|
-
stdoutBase64Chunks?: string[];
|
|
24
|
-
stderr?: string;
|
|
25
|
-
}>;
|
|
26
|
-
echoEnv?: string[];
|
|
27
|
-
missingTools?: string[];
|
|
28
|
-
matchArgIncludes?: string | string[];
|
|
29
|
-
/** Files the mock child writes to disk before emitting output, standing in for its write-tool side effects. */
|
|
30
|
-
writeFiles?: Array<{ path: string; content: string }>;
|
|
31
|
-
/** Writes the structured-output capture file without emitting a structured_output tool event. */
|
|
32
|
-
structuredOutputCapture?: unknown;
|
|
33
|
-
structuredOutputAcceptanceReport?: unknown;
|
|
34
|
-
structuredOutputAcceptanceReportRaw?: string;
|
|
35
|
-
runtimeAcknowledgedExtensions?: unknown;
|
|
36
|
-
}
|
|
17
|
+
export type MockPiResponse = FakeChildResponse;
|
|
37
18
|
|
|
38
19
|
export interface MockPi {
|
|
39
20
|
readonly dir: string;
|
|
21
|
+
/** In-process foreground child sessions created since the last reset, in launch order. */
|
|
22
|
+
readonly sessions: FakeChildSessionRecord[];
|
|
23
|
+
/** Number of times the foreground child session factory was disposed. */
|
|
24
|
+
readonly disposeCalls: number;
|
|
40
25
|
install(): void;
|
|
41
26
|
uninstall(): void;
|
|
42
27
|
onCall(response: MockPiResponse): void;
|
|
@@ -44,8 +29,7 @@ export interface MockPi {
|
|
|
44
29
|
callCount(): number;
|
|
45
30
|
}
|
|
46
31
|
|
|
47
|
-
const
|
|
48
|
-
const SCRIPT_PATH = path.join(__dirname, "mock-pi-script.mjs");
|
|
32
|
+
const RUNNER_FACTORY_MODULE = path.join(path.dirname(fileURLToPath(import.meta.url)), "runner-child-session-factory.ts");
|
|
49
33
|
const CALL_PREFIX = "call-";
|
|
50
34
|
const DEFAULT_RESPONSE_FILE = "default-response.json";
|
|
51
35
|
const QUEUED_PREFIX = "pending-";
|
|
@@ -54,11 +38,6 @@ function ensureDir(dir: string): void {
|
|
|
54
38
|
fs.mkdirSync(dir, { recursive: true });
|
|
55
39
|
}
|
|
56
40
|
|
|
57
|
-
function writeExecutable(filePath: string, content: string): void {
|
|
58
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
59
|
-
fs.chmodSync(filePath, 0o755);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
41
|
function listQueueFiles(queueDir: string, prefix: string): string[] {
|
|
63
42
|
try {
|
|
64
43
|
return fs.readdirSync(queueDir)
|
|
@@ -73,62 +52,36 @@ export function createMockPi(): MockPi {
|
|
|
73
52
|
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-mock-cli-"));
|
|
74
53
|
let queueGeneration = 0;
|
|
75
54
|
let queueDir = path.join(rootDir, `queue-${queueGeneration}`);
|
|
76
|
-
const binDir = path.join(rootDir, "bin");
|
|
77
|
-
const piPackageDir = path.join(rootDir, "pi-package");
|
|
78
|
-
const cliScriptPath = path.join(piPackageDir, "dist", "cli.mjs");
|
|
79
55
|
ensureDir(queueDir);
|
|
80
|
-
ensureDir(binDir);
|
|
81
|
-
ensureDir(path.dirname(cliScriptPath));
|
|
82
|
-
fs.copyFileSync(SCRIPT_PATH, cliScriptPath);
|
|
83
|
-
fs.writeFileSync(
|
|
84
|
-
path.join(piPackageDir, "package.json"),
|
|
85
|
-
JSON.stringify({ name: "@selesai/code" }),
|
|
86
|
-
"utf-8",
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const shellScriptPath = path.join(binDir, "pi");
|
|
90
|
-
const cmdScriptPath = path.join(binDir, "pi.cmd");
|
|
91
|
-
writeExecutable(shellScriptPath, `#!/bin/sh\nexec "${process.execPath}" "${cliScriptPath}" "$@"\n`);
|
|
92
|
-
writeExecutable(cmdScriptPath, `@echo off\r\n"${process.execPath}" "${cliScriptPath}" %*\r\n`);
|
|
93
56
|
|
|
57
|
+
const fakeSessions = createFakeChildSessions(() => queueDir);
|
|
94
58
|
let installed = false;
|
|
95
59
|
let nextSequence = 0;
|
|
96
|
-
let originalPath: string | undefined;
|
|
97
|
-
let originalPiBinary: string | undefined;
|
|
98
|
-
let originalArgv1: string | undefined;
|
|
99
60
|
let originalQueueEnv: string | undefined;
|
|
100
61
|
|
|
101
62
|
return {
|
|
102
63
|
get dir() {
|
|
103
64
|
return queueDir;
|
|
104
65
|
},
|
|
66
|
+
get sessions() {
|
|
67
|
+
return fakeSessions.sessions;
|
|
68
|
+
},
|
|
69
|
+
get disposeCalls() {
|
|
70
|
+
return fakeSessions.disposeCalls;
|
|
71
|
+
},
|
|
105
72
|
install() {
|
|
106
73
|
if (installed) return;
|
|
107
74
|
installed = true;
|
|
108
|
-
|
|
109
|
-
|
|
75
|
+
setChildSessionFactory(fakeSessions.factory);
|
|
76
|
+
setChildSessionFactoryModule(RUNNER_FACTORY_MODULE);
|
|
110
77
|
originalQueueEnv = process.env.MOCK_PI_QUEUE_DIR;
|
|
111
|
-
process.env.PATH = `${binDir}${path.delimiter}${originalPath ?? ""}`;
|
|
112
|
-
if (process.platform === "win32") {
|
|
113
|
-
delete process.env.SELESAI_SUBAGENT_SELESAI_BINARY;
|
|
114
|
-
originalArgv1 = process.argv[1];
|
|
115
|
-
process.argv[1] = cliScriptPath;
|
|
116
|
-
} else {
|
|
117
|
-
process.env.SELESAI_SUBAGENT_SELESAI_BINARY = shellScriptPath;
|
|
118
|
-
}
|
|
119
78
|
process.env.MOCK_PI_QUEUE_DIR = queueDir;
|
|
120
79
|
},
|
|
121
80
|
uninstall() {
|
|
122
81
|
if (!installed) return;
|
|
123
82
|
installed = false;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (originalPiBinary === undefined) delete process.env.SELESAI_SUBAGENT_SELESAI_BINARY;
|
|
127
|
-
else process.env.SELESAI_SUBAGENT_SELESAI_BINARY = originalPiBinary;
|
|
128
|
-
if (process.platform === "win32") {
|
|
129
|
-
if (originalArgv1 === undefined) delete process.argv[1];
|
|
130
|
-
else process.argv[1] = originalArgv1;
|
|
131
|
-
}
|
|
83
|
+
setChildSessionFactory(undefined);
|
|
84
|
+
setChildSessionFactoryModule(undefined);
|
|
132
85
|
if (originalQueueEnv === undefined) delete process.env.MOCK_PI_QUEUE_DIR;
|
|
133
86
|
else process.env.MOCK_PI_QUEUE_DIR = originalQueueEnv;
|
|
134
87
|
try {
|
|
@@ -147,6 +100,7 @@ export function createMockPi(): MockPi {
|
|
|
147
100
|
},
|
|
148
101
|
reset() {
|
|
149
102
|
nextSequence = 0;
|
|
103
|
+
fakeSessions.reset();
|
|
150
104
|
queueGeneration += 1;
|
|
151
105
|
queueDir = path.join(rootDir, `queue-${queueGeneration}`);
|
|
152
106
|
ensureDir(queueDir);
|
|
@@ -156,4 +110,4 @@ export function createMockPi(): MockPi {
|
|
|
156
110
|
return listQueueFiles(queueDir, CALL_PREFIX).length;
|
|
157
111
|
},
|
|
158
112
|
};
|
|
159
|
-
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import registerSubagentNotify, { type CompletionNotification } from "../../src/runs/background/notify.ts";
|
|
3
|
+
|
|
4
|
+
const callbacks = new Map<number, () => void>();
|
|
5
|
+
let timerId = 0;
|
|
6
|
+
let now = 0;
|
|
7
|
+
let failSend = false;
|
|
8
|
+
const state = { currentSessionId: "session-a", completionOwnerId: "owner-a" };
|
|
9
|
+
const sent: unknown[] = [];
|
|
10
|
+
const pi = {
|
|
11
|
+
events: { on: () => () => {} },
|
|
12
|
+
sendMessage(message: unknown, options: unknown) {
|
|
13
|
+
if (failSend) throw new Error("PRIVATE_ERROR_CREDENTIAL");
|
|
14
|
+
sent.push({ message, options });
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
const notifier = registerSubagentNotify(pi as never, state, {
|
|
18
|
+
now: () => now,
|
|
19
|
+
timers: {
|
|
20
|
+
setTimeout(callback) { callbacks.set(++timerId, callback); return timerId; },
|
|
21
|
+
clearTimeout(handle) { callbacks.delete(handle as number); },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const result = (id: string, extra: Partial<CompletionNotification> = {}): CompletionNotification => ({
|
|
25
|
+
id, runId: id, sessionId: "session-a", completionOwnerId: "owner-a", success: true,
|
|
26
|
+
summary: "PRIVATE_OUTPUT", task: "PRIVATE_TASK", credential: "PRIVATE_CREDENTIAL",
|
|
27
|
+
sessionFile: "/PRIVATE_SESSION_PATH", ...extra,
|
|
28
|
+
});
|
|
29
|
+
const flush = () => { for (const callback of [...callbacks.values()]) callback(); };
|
|
30
|
+
assert.equal(await notifier.deliver(result("missing", { sessionId: undefined })), false);
|
|
31
|
+
assert.equal(await notifier.deliver(result("foreground-other", { source: "foreground", sessionId: "other" })), false);
|
|
32
|
+
assert.equal(await notifier.deliver(result("foreign", { completionOwnerId: "other" })), false);
|
|
33
|
+
assert.equal(await notifier.deliver(result("relayed", { intercomDelivered: true })), true);
|
|
34
|
+
assert.equal(sent.length, 0);
|
|
35
|
+
const first = result("first");
|
|
36
|
+
const firstDelivery = notifier.deliver(first);
|
|
37
|
+
assert.equal(notifier.deliver(first), firstDelivery);
|
|
38
|
+
const secondDelivery = notifier.deliver(result("second"));
|
|
39
|
+
assert.equal(sent.length, 0);
|
|
40
|
+
assert.equal(notifier.hasPendingDelivery(), true);
|
|
41
|
+
flush();
|
|
42
|
+
assert.equal(await firstDelivery, true);
|
|
43
|
+
assert.equal(await secondDelivery, true);
|
|
44
|
+
assert.equal(sent.length, 1); // grouped, not dropped
|
|
45
|
+
now = 600_000;
|
|
46
|
+
assert.equal(await notifier.deliver(first), true); // inclusive TTL boundary
|
|
47
|
+
assert.equal(sent.length, 1);
|
|
48
|
+
now++;
|
|
49
|
+
const expired = notifier.deliver(first);
|
|
50
|
+
flush();
|
|
51
|
+
assert.equal(await expired, true);
|
|
52
|
+
assert.equal(sent.length, 2);
|
|
53
|
+
const lostOwnership = notifier.deliver(result("owner-changed"));
|
|
54
|
+
state.completionOwnerId = "other";
|
|
55
|
+
flush();
|
|
56
|
+
assert.equal(await lostOwnership, false);
|
|
57
|
+
state.completionOwnerId = "owner-a";
|
|
58
|
+
failSend = true;
|
|
59
|
+
assert.equal(await notifier.deliver(result("retry", { success: false })), false);
|
|
60
|
+
failSend = false;
|
|
61
|
+
assert.equal(await notifier.deliver(result("retry", { success: false })), true);
|
|
62
|
+
assert.equal(await notifier.deliver(result("foreground", { source: "foreground" })), true);
|
|
63
|
+
// Attention still flushes held successes before its own immediate send.
|
|
64
|
+
const held = notifier.deliver(result("held"));
|
|
65
|
+
assert.equal(await notifier.deliver(result("attention", { success: false })), true);
|
|
66
|
+
assert.equal(await held, true);
|
|
67
|
+
const abandoned = notifier.deliver(result("abandoned"));
|
|
68
|
+
notifier.dispose();
|
|
69
|
+
assert.equal(await abandoned, false);
|
|
70
|
+
assert.equal(notifier.hasPendingDelivery(), false);
|
|
71
|
+
assert.equal(callbacks.size, 0);
|
|
72
|
+
assert.equal(await notifier.deliver(result("after-dispose")), false);
|
|
73
|
+
// Malformed/large identity cannot create multiline or unbounded trace records.
|
|
74
|
+
await notifier.deliver(result("unsafe\n\u001b[31m" + "x".repeat(10_000)));
|
|
75
|
+
// Disabled diagnostics must not even read diagnostic identity fields at a guard.
|
|
76
|
+
if (!process.env.NODE_DEBUG) {
|
|
77
|
+
const unread = result("unread");
|
|
78
|
+
Object.defineProperty(unread, "runId", { get() { throw new Error("diagnostic metadata read while disabled"); } });
|
|
79
|
+
assert.equal(await notifier.deliver(unread), false);
|
|
80
|
+
}
|
|
81
|
+
const immediate = registerSubagentNotify(pi as never, state, { batchConfig: { enabled: false } });
|
|
82
|
+
assert.equal(await immediate.deliver(result("unbatched", { triggerTurn: false })), true);
|
|
83
|
+
immediate.dispose();
|
|
84
|
+
console.log(JSON.stringify(sent));
|