@selesai/code 0.13.0 → 0.13.2
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 +22 -0
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.js +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/extensions/package.json +1 -2
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
- package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
- package/dist/extensions/pi-hermes-memory/README.md +643 -0
- package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
- package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
- package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
- package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
- package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
- package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
- package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
- package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
- package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
- package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
- package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
- package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
- package/dist/extensions/pi-hermes-memory/package.json +66 -0
- package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
- package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
- package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
- package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
- package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
- package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
- package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
- package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
- package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
- package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
- package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
- package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
- package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
- package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
- package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
- package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
- package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
- package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
- package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
- package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
- package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
- package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
- package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
- package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
- package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
- package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
- package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
- package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
- package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
- package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
- package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
- package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
- package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
- package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
- package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
- package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
- package/dist/extensions/pi-rewind-hook/README.md +4 -1
- package/dist/extensions/pi-rewind-hook/index.ts +151 -21
- package/dist/extensions/pi-rewind-hook/package.json +5 -3
- package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
- package/dist/extensions/pi-subagents/AGENTS.md +5 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
- package/dist/extensions/pi-subagents/README.md +12 -6
- package/dist/extensions/pi-subagents/VISION.md +81 -0
- package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
- package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
- package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
- package/dist/extensions/pi-subagents/docs/agents.md +488 -0
- package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
- package/dist/extensions/pi-subagents/docs/models.md +27 -4
- package/dist/extensions/pi-subagents/docs/observability.md +10 -9
- package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
- package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
- package/dist/extensions/pi-subagents/package-lock.json +389 -2
- package/dist/extensions/pi-subagents/package.json +3 -1
- package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
- package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
- package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
- package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
- package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
- package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
- package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
- package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
- package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
- package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
- package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -7
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
- package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
- package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
- package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
- package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
- package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
- package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
- package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
- package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
- package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
- package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
- package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
- package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
- package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
- package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
- package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
- package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
- package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
- package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
- package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
- package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
- package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
- package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
- package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
- package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
- package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
- package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
- package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
- package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
- package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
- package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +136 -6
- package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
- package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
- package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
- package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
- package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
- package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
- package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
- package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
- package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -7
- package/dist/skills/workflow/SKILL.md +65 -50
- package/package.json +112 -110
- package/dist/extensions/enable-readonly-tools.test.ts +0 -63
- package/dist/extensions/enable-readonly-tools.ts +0 -24
- package/dist/extensions/model-prompt-injector/config.json +0 -16
- package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
- package/dist/extensions/model-prompt-injector/index.ts +0 -148
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { planMcpDirectToolGrant } from "../../src/runs/shared/mcp-direct-tool-grant.ts";
|
|
4
|
+
|
|
5
|
+
test("plans server grants from explicit metadata facts and preserves resource filtering", () => {
|
|
6
|
+
const grant = planMcpDirectToolGrant({
|
|
7
|
+
selectors: ["browser-mcp"],
|
|
8
|
+
servers: {
|
|
9
|
+
"browser-mcp": { excludeTools: ["browser_click"] },
|
|
10
|
+
},
|
|
11
|
+
metadata: {
|
|
12
|
+
"browser-mcp": {
|
|
13
|
+
tools: [{ name: "click" }, { name: "navigate" }],
|
|
14
|
+
resources: [{ name: "Console Logs", uri: "resource://console" }],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
toolPrefix: "server",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
assert.deepEqual(grant, {
|
|
21
|
+
selections: [
|
|
22
|
+
{ name: "browser_mcp_navigate", selector: "browser-mcp/navigate" },
|
|
23
|
+
{ name: "browser_mcp_get_console_logs", selector: "browser-mcp/get_console_logs" },
|
|
24
|
+
],
|
|
25
|
+
unresolvedSelectors: [],
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("plans tool-specific grants and reports unresolved selectors deterministically", () => {
|
|
30
|
+
const grant = planMcpDirectToolGrant({
|
|
31
|
+
selectors: ["github/search_repositories", "missing"],
|
|
32
|
+
servers: {
|
|
33
|
+
github: {},
|
|
34
|
+
},
|
|
35
|
+
metadata: {
|
|
36
|
+
github: { tools: [{ name: "search_repositories" }, { name: "create_issue" }] },
|
|
37
|
+
},
|
|
38
|
+
toolPrefix: "none",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
assert.deepEqual(grant, {
|
|
42
|
+
selections: [{ name: "search_repositories", selector: "github/search_repositories" }],
|
|
43
|
+
unresolvedSelectors: ["missing"],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("fails closed for missing or stale source facts without consulting ambient state", () => {
|
|
48
|
+
const grant = planMcpDirectToolGrant({
|
|
49
|
+
selectors: ["github", "github/search_repositories"],
|
|
50
|
+
servers: {
|
|
51
|
+
github: {},
|
|
52
|
+
},
|
|
53
|
+
metadata: {},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
assert.deepEqual(grant, {
|
|
57
|
+
selections: [],
|
|
58
|
+
unresolvedSelectors: ["github", "github/search_repositories"],
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("enforces server includeTools before exclusions for tools and generated resources", () => {
|
|
63
|
+
const grant = planMcpDirectToolGrant({
|
|
64
|
+
selectors: ["demo"],
|
|
65
|
+
servers: {
|
|
66
|
+
demo: {
|
|
67
|
+
includeTools: ["get_*", "demo_list_records"],
|
|
68
|
+
excludeTools: ["get_secret", "demo_get_private*"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
metadata: {
|
|
72
|
+
demo: {
|
|
73
|
+
tools: [
|
|
74
|
+
{ name: "get_public" },
|
|
75
|
+
{ name: "get_secret" },
|
|
76
|
+
{ name: "list_records" },
|
|
77
|
+
{ name: "other" },
|
|
78
|
+
],
|
|
79
|
+
resources: [
|
|
80
|
+
{ name: "Run Book", uri: "resource://run-book" },
|
|
81
|
+
{ name: "Private Notes", uri: "resource://private-notes" },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
toolPrefix: "server",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
assert.deepEqual(grant.selections, [
|
|
89
|
+
{ name: "demo_get_public", selector: "demo/get_public" },
|
|
90
|
+
{ name: "demo_list_records", selector: "demo/list_records" },
|
|
91
|
+
{ name: "demo_get_run_book", selector: "demo/get_run_book" },
|
|
92
|
+
]);
|
|
93
|
+
assert.deepEqual(grant.unresolvedSelectors, []);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("matches include and exclude patterns against raw and alternate prefix names", () => {
|
|
97
|
+
for (const [toolPrefix, expectedName] of [
|
|
98
|
+
["server", "demo_mcp_search-records"],
|
|
99
|
+
["short", "demo_search-records"],
|
|
100
|
+
["none", "search-records"],
|
|
101
|
+
] as const) {
|
|
102
|
+
const allowed = planMcpDirectToolGrant({
|
|
103
|
+
selectors: ["demo-mcp"],
|
|
104
|
+
servers: { "demo-mcp": { includeTools: ["demo_mcp_search_records"] } },
|
|
105
|
+
metadata: { "demo-mcp": { tools: [{ name: "search-records" }] } },
|
|
106
|
+
toolPrefix,
|
|
107
|
+
});
|
|
108
|
+
assert.deepEqual(allowed.selections, [{ name: expectedName, selector: "demo-mcp/search-records" }]);
|
|
109
|
+
|
|
110
|
+
const excluded = planMcpDirectToolGrant({
|
|
111
|
+
selectors: ["demo-mcp"],
|
|
112
|
+
servers: { "demo-mcp": { includeTools: ["demo_mcp_search_records"], excludeTools: ["search_records"] } },
|
|
113
|
+
metadata: { "demo-mcp": { tools: [{ name: "search-records" }] } },
|
|
114
|
+
toolPrefix,
|
|
115
|
+
});
|
|
116
|
+
assert.deepEqual(excluded.selections, []);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("supports adapter-compatible question-mark patterns", () => {
|
|
121
|
+
const grant = planMcpDirectToolGrant({
|
|
122
|
+
selectors: ["demo"],
|
|
123
|
+
servers: { demo: { includeTools: ["get_????"], excludeTools: ["get_?ecret"] } },
|
|
124
|
+
metadata: { demo: { tools: [{ name: "get_wiki" }, { name: "get_secret" }, { name: "get_longer" }] } },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
assert.deepEqual(grant.selections, [{ name: "demo_get_wiki", selector: "demo/get_wiki" }]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("matches adapter mcp-prefixed policy names", () => {
|
|
131
|
+
const grant = planMcpDirectToolGrant({
|
|
132
|
+
selectors: ["demo"],
|
|
133
|
+
servers: { demo: { includeTools: ["mcp_search_records"] } },
|
|
134
|
+
metadata: { demo: { tools: [{ name: "search_records" }, { name: "list_records" }] } },
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
assert.deepEqual(grant.selections, [{ name: "demo_search_records", selector: "demo/search_records" }]);
|
|
138
|
+
});
|
|
@@ -136,7 +136,7 @@ describe("mission launch lifecycle", () => {
|
|
|
136
136
|
config: test.missionConfig,
|
|
137
137
|
});
|
|
138
138
|
assert.ok(binding);
|
|
139
|
-
const missionDir = path.join(worktreeRoot, ".
|
|
139
|
+
const missionDir = path.join(worktreeRoot, ".pi-subagents", "missions");
|
|
140
140
|
assert.throws(() => prepareMissionLaunch({
|
|
141
141
|
params: { missionId: binding.missionId, task: "Prepare worktree work" },
|
|
142
142
|
projectRoot: worktreeRoot,
|
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
4
4
|
import {
|
|
5
5
|
clearExclusions,
|
|
6
|
+
DEFAULT_MODEL_EXCLUSION_TTL_MS,
|
|
6
7
|
filterFallbackCandidates,
|
|
7
8
|
flushPersist,
|
|
8
9
|
getExcludedCount,
|
|
@@ -11,13 +12,16 @@ import {
|
|
|
11
12
|
parseModelKey,
|
|
12
13
|
recordModelFailure,
|
|
13
14
|
reloadFromDisk,
|
|
15
|
+
MAX_MODEL_EXCLUSION_TTL_MS,
|
|
14
16
|
setDefaultTTL,
|
|
17
|
+
type ModelExclusion,
|
|
15
18
|
} from "../../src/runs/shared/model-exclusions.ts";
|
|
16
19
|
|
|
17
20
|
// The exclusion store is a process-wide singleton persisted under TEMP_ROOT_DIR
|
|
18
21
|
// (isolated per test run by test/support/isolated-temp-root.mjs). Clear it
|
|
19
22
|
// before/after each test so cases don't leak state into each other.
|
|
20
23
|
beforeEach(() => {
|
|
24
|
+
setDefaultTTL(DEFAULT_MODEL_EXCLUSION_TTL_MS);
|
|
21
25
|
fs.rmSync(getExclusionsFilePath(), { force: true });
|
|
22
26
|
clearExclusions();
|
|
23
27
|
});
|
|
@@ -61,6 +65,16 @@ describe("model exclusions — TTL expiry", () => {
|
|
|
61
65
|
it("rejects invalid default TTLs", () => {
|
|
62
66
|
assert.throws(() => setDefaultTTL(0), /finite positive/);
|
|
63
67
|
assert.throws(() => setDefaultTTL(Number.POSITIVE_INFINITY), /finite positive/);
|
|
68
|
+
assert.throws(() => setDefaultTTL(MAX_MODEL_EXCLUSION_TTL_MS + 1), /no greater than/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("keeps the maximum configured expiry representable", () => {
|
|
72
|
+
setDefaultTTL(MAX_MODEL_EXCLUSION_TTL_MS);
|
|
73
|
+
recordModelFailure({ modelId: "gpt-4", provider: "openai" });
|
|
74
|
+
reloadFromDisk();
|
|
75
|
+
const entry = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
|
|
76
|
+
assert.doesNotThrow(() => new Date(entry.expiresAt).toISOString());
|
|
77
|
+
assert.equal(isExcluded("gpt-4", "openai"), true);
|
|
64
78
|
});
|
|
65
79
|
|
|
66
80
|
it("drops an exclusion after its TTL elapses", async () => {
|
|
@@ -69,6 +83,23 @@ describe("model exclusions — TTL expiry", () => {
|
|
|
69
83
|
await new Promise((r) => setTimeout(r, 150));
|
|
70
84
|
assert.equal(isExcluded("gpt-4", "openai"), false);
|
|
71
85
|
});
|
|
86
|
+
|
|
87
|
+
it("shortens active exclusions without synchronously persisting or extending expiries", () => {
|
|
88
|
+
recordModelFailure({ modelId: "gpt-4", provider: "openai", reason: "503", ttlMs: 60_000 });
|
|
89
|
+
const before = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
|
|
90
|
+
setDefaultTTL(30_000, { shortenExisting: true });
|
|
91
|
+
const beforeFlush = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
|
|
92
|
+
assert.equal(beforeFlush.expiresAt, before.expiresAt);
|
|
93
|
+
flushPersist();
|
|
94
|
+
const shortened = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
|
|
95
|
+
assert.equal(shortened.expiresAt, shortened.recordedAt + 30_000);
|
|
96
|
+
|
|
97
|
+
setDefaultTTL(120_000, { shortenExisting: true });
|
|
98
|
+
flushPersist();
|
|
99
|
+
const notExtended = JSON.parse(fs.readFileSync(getExclusionsFilePath(), "utf-8")).exclusions[0] as ModelExclusion;
|
|
100
|
+
assert.equal(notExtended.expiresAt, shortened.expiresAt);
|
|
101
|
+
assert.ok(shortened.expiresAt < before.expiresAt);
|
|
102
|
+
});
|
|
72
103
|
});
|
|
73
104
|
|
|
74
105
|
describe("model exclusions — parseModelKey", () => {
|
|
@@ -119,6 +150,18 @@ describe("model exclusions — filtering fallback candidates", () => {
|
|
|
119
150
|
const filtered = filterFallbackCandidates(candidates);
|
|
120
151
|
assert.deepEqual(filtered, ["anthropic/claude-3", "openai/gpt-4"]);
|
|
121
152
|
});
|
|
153
|
+
|
|
154
|
+
it("reports the cached reason and expiry for skipped candidates", () => {
|
|
155
|
+
const skipped: Array<{ candidate: string; exclusion: Readonly<ModelExclusion> }> = [];
|
|
156
|
+
recordModelFailure({ modelId: "gpt-4", provider: "openai", reason: "503 unavailable" });
|
|
157
|
+
const filtered = filterFallbackCandidates(["openai/gpt-4", "anthropic/claude-3"], {
|
|
158
|
+
onExcluded: (candidate, exclusion) => skipped.push({ candidate, exclusion }),
|
|
159
|
+
});
|
|
160
|
+
assert.deepEqual(filtered, ["anthropic/claude-3"]);
|
|
161
|
+
assert.equal(skipped[0]?.candidate, "openai/gpt-4");
|
|
162
|
+
assert.equal(skipped[0]?.exclusion.reason, "503 unavailable");
|
|
163
|
+
assert.ok((skipped[0]?.exclusion.expiresAt ?? 0) > Date.now());
|
|
164
|
+
});
|
|
122
165
|
});
|
|
123
166
|
|
|
124
167
|
describe("model exclusions — persistence", () => {
|
|
@@ -164,4 +207,52 @@ describe("model exclusions — persistence", () => {
|
|
|
164
207
|
assert.equal(errors.length, 1);
|
|
165
208
|
assert.match(String(errors[0]?.[0]), /Failed to load exclusions/);
|
|
166
209
|
});
|
|
210
|
+
|
|
211
|
+
it("rejects persisted exclusions with invalid timestamps", () => {
|
|
212
|
+
for (const [field, value] of [
|
|
213
|
+
["expiresAt", null],
|
|
214
|
+
["expiresAt", 0],
|
|
215
|
+
["expiresAt", 9_000_000_000_000_000],
|
|
216
|
+
["recordedAt", null],
|
|
217
|
+
["recordedAt", -1],
|
|
218
|
+
["recordedAt", 9_000_000_000_000_000],
|
|
219
|
+
] as const) {
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
const entry = { modelId: "gpt-4", provider: "openai", reason: "503", recordedAt: now, expiresAt: now + 60_000, [field]: value };
|
|
222
|
+
fs.writeFileSync(getExclusionsFilePath(), JSON.stringify({ version: 1, exclusions: [entry] }), "utf-8");
|
|
223
|
+
const originalError = console.error;
|
|
224
|
+
const errors: unknown[][] = [];
|
|
225
|
+
console.error = (...args: unknown[]) => errors.push(args);
|
|
226
|
+
try {
|
|
227
|
+
reloadFromDisk();
|
|
228
|
+
} finally {
|
|
229
|
+
console.error = originalError;
|
|
230
|
+
}
|
|
231
|
+
assert.equal(getExcludedCount(), 0);
|
|
232
|
+
assert.equal(errors.length, 1);
|
|
233
|
+
assert.match(String(errors[0]?.[1]), new RegExp(`invalid ${field}`));
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("rejects persisted exclusions with non-string reasons", () => {
|
|
238
|
+
for (const reason of [null, 503, { bad: true }, ["503"]]) {
|
|
239
|
+
const now = Date.now();
|
|
240
|
+
fs.writeFileSync(getExclusionsFilePath(), JSON.stringify({
|
|
241
|
+
version: 1,
|
|
242
|
+
exclusions: [{ modelId: "gpt-4", provider: "openai", reason, recordedAt: now, expiresAt: now + 60_000 }],
|
|
243
|
+
}), "utf-8");
|
|
244
|
+
const originalError = console.error;
|
|
245
|
+
const errors: unknown[][] = [];
|
|
246
|
+
console.error = (...args: unknown[]) => errors.push(args);
|
|
247
|
+
try {
|
|
248
|
+
reloadFromDisk();
|
|
249
|
+
} finally {
|
|
250
|
+
console.error = originalError;
|
|
251
|
+
}
|
|
252
|
+
assert.equal(getExcludedCount(), 0);
|
|
253
|
+
assert.equal(errors.length, 1);
|
|
254
|
+
assert.match(String(errors[0]?.[1]), /invalid reason/);
|
|
255
|
+
assert.deepEqual(filterFallbackCandidates(["openai/gpt-4", "anthropic/claude-3"]), ["openai/gpt-4", "anthropic/claude-3"]);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
167
258
|
});
|
|
@@ -6,13 +6,14 @@ import {
|
|
|
6
6
|
formatSubagentModelVerificationError,
|
|
7
7
|
isContextOverflow,
|
|
8
8
|
isRetryableModelFailure,
|
|
9
|
+
isRetryableModelFailureAttempt,
|
|
9
10
|
normalizeModelSegment,
|
|
10
11
|
recordRetryableModelFailure,
|
|
11
12
|
resolveEffectiveSubagentModel,
|
|
12
13
|
resolveModelCandidate,
|
|
13
14
|
resolveSubagentModelOverride,
|
|
14
15
|
} from "../../src/runs/shared/model-fallback.ts";
|
|
15
|
-
import { clearExclusions } from "../../src/runs/shared/model-exclusions.ts";
|
|
16
|
+
import { clearExclusions, recordModelFailure } from "../../src/runs/shared/model-exclusions.ts";
|
|
16
17
|
import { resolveModelScopesForAgent } from "../../src/runs/shared/model-scope.ts";
|
|
17
18
|
|
|
18
19
|
beforeEach(() => clearExclusions());
|
|
@@ -53,6 +54,38 @@ describe("model fallback helpers", () => {
|
|
|
53
54
|
);
|
|
54
55
|
});
|
|
55
56
|
|
|
57
|
+
it("accepts a bare leaf reported by an Anthropic gateway driver", () => {
|
|
58
|
+
const gatewayModels = [{
|
|
59
|
+
provider: "bifrost-anthropic",
|
|
60
|
+
id: "vertex/claude-fable-5",
|
|
61
|
+
fullId: "bifrost-anthropic/vertex/claude-fable-5",
|
|
62
|
+
}];
|
|
63
|
+
assert.equal(
|
|
64
|
+
formatSubagentModelVerificationError(
|
|
65
|
+
"bifrost-anthropic/vertex/claude-fable-5:high",
|
|
66
|
+
"claude-fable-5",
|
|
67
|
+
gatewayModels,
|
|
68
|
+
),
|
|
69
|
+
undefined,
|
|
70
|
+
);
|
|
71
|
+
assert.match(
|
|
72
|
+
formatSubagentModelVerificationError(
|
|
73
|
+
"bifrost-anthropic/vertex/claude-fable-5:high",
|
|
74
|
+
"wrong-provider/claude-fable-5",
|
|
75
|
+
gatewayModels,
|
|
76
|
+
) ?? "",
|
|
77
|
+
/model_verification_failed/,
|
|
78
|
+
);
|
|
79
|
+
assert.match(
|
|
80
|
+
formatSubagentModelVerificationError(
|
|
81
|
+
"bifrost-anthropic/vertex/claude-fable-5:high",
|
|
82
|
+
"claude-sonnet-4",
|
|
83
|
+
gatewayModels,
|
|
84
|
+
) ?? "",
|
|
85
|
+
/model_verification_failed/,
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
56
89
|
it("resolves unique owner/name ids when the owner is not a registered provider", () => {
|
|
57
90
|
const registry = [
|
|
58
91
|
...availableModels,
|
|
@@ -125,12 +158,21 @@ describe("model fallback helpers", () => {
|
|
|
125
158
|
});
|
|
126
159
|
|
|
127
160
|
it("excludes a candidate after a retryable model failure is recorded", () => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
const warnings: string[] = [];
|
|
162
|
+
const originalWarn = console.warn;
|
|
163
|
+
console.warn = (message: unknown) => warnings.push(String(message));
|
|
164
|
+
try {
|
|
165
|
+
recordRetryableModelFailure("openai/gpt-5-mini", "rate limit exceeded for Bearer secret-token-value");
|
|
166
|
+
assert.deepEqual(
|
|
167
|
+
buildModelCandidates("gpt-5-mini", ["anthropic/claude-sonnet-4"], availableModels),
|
|
168
|
+
["anthropic/claude-sonnet-4"],
|
|
169
|
+
);
|
|
170
|
+
} finally {
|
|
171
|
+
console.warn = originalWarn;
|
|
172
|
+
}
|
|
173
|
+
assert.equal(warnings.length, 1);
|
|
174
|
+
assert.match(warnings[0]!, /Skipping model 'openai\/gpt-5-mini'.*reason: rate limit exceeded for \[redacted\]; expires: \d{4}-\d{2}-\d{2}T/);
|
|
175
|
+
assert.doesNotMatch(warnings[0]!, /secret-token-value/);
|
|
134
176
|
});
|
|
135
177
|
|
|
136
178
|
it("does not exclude a candidate after a task or tool failure", () => {
|
|
@@ -190,7 +232,12 @@ describe("model fallback helpers", () => {
|
|
|
190
232
|
assert.equal(isRetryableModelFailure("Subagent produced no output (possible model cold-start or empty response)."), true);
|
|
191
233
|
assert.equal(isRetryableModelFailure("model load failed"), true);
|
|
192
234
|
assert.equal(isRetryableModelFailure("Stream ended without finish_reason"), true);
|
|
235
|
+
assert.equal(isRetryableModelFailure("Connection error"), true);
|
|
236
|
+
assert.equal(isRetryableModelFailure("APIConnectionError: Connection closed."), true);
|
|
237
|
+
assert.equal(isRetryableModelFailure("Connection reset by peer"), true);
|
|
193
238
|
assert.equal(isRetryableModelFailure("Request timed out."), true);
|
|
239
|
+
assert.equal(isRetryableModelFailure("internal server error"), true);
|
|
240
|
+
assert.equal(isRetryableModelFailure("500"), true);
|
|
194
241
|
});
|
|
195
242
|
|
|
196
243
|
it("does not treat ordinary task/tool failures as retryable model failures", () => {
|
|
@@ -208,6 +255,13 @@ describe("model fallback helpers", () => {
|
|
|
208
255
|
assert.equal(isRetryableModelFailure("Provider error: bash failed (exit 1): request timed out"), true);
|
|
209
256
|
assert.equal(isRetryableModelFailure("bash failed (exit unknown): request timed out"), true);
|
|
210
257
|
});
|
|
258
|
+
|
|
259
|
+
it("does not retry raw process stderr after child activity", () => {
|
|
260
|
+
assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant" }], toolCount: 0 }), false);
|
|
261
|
+
assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant", errorMessage: "APIConnectionError: Connection closed." }], toolCount: 0 }), true);
|
|
262
|
+
assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [], toolCount: 0 }), true);
|
|
263
|
+
assert.equal(isRetryableModelFailureAttempt({ error: "APIConnectionError: Connection closed.", messages: [{ role: "assistant", errorMessage: "APIConnectionError: Connection closed." }], toolCount: 1 }), false);
|
|
264
|
+
});
|
|
211
265
|
});
|
|
212
266
|
|
|
213
267
|
describe("resolveSubagentModelOverride (cross-session inherit, issue #266)", () => {
|
|
@@ -264,6 +318,17 @@ describe("resolveSubagentModelOverride (cross-session inherit, issue #266)", ()
|
|
|
264
318
|
);
|
|
265
319
|
});
|
|
266
320
|
|
|
321
|
+
it("fails visibly when an explicit model is excluded instead of falling back", () => {
|
|
322
|
+
recordModelFailure({ modelId: "gpt-5-mini", provider: "openai", reason: "rate limit" });
|
|
323
|
+
assert.throws(
|
|
324
|
+
() => resolveEffectiveSubagentModel("openai/gpt-5-mini", undefined, parentModel, availableModels),
|
|
325
|
+
(error: unknown) => {
|
|
326
|
+
const message = String(error);
|
|
327
|
+
return message.includes("openai/gpt-5-mini") && message.includes("rate limit") && message.includes("expires:");
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
});
|
|
331
|
+
|
|
267
332
|
it("resolves owner/name frontmatter models against the registry", () => {
|
|
268
333
|
const models = [
|
|
269
334
|
...availableModels,
|
|
@@ -88,6 +88,27 @@ describe("tracked mutation evidence", () => {
|
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
it("does not invoke repository fsmonitor while collecting mutation evidence", () => {
|
|
92
|
+
withRepo((repo) => {
|
|
93
|
+
const marker = path.join(repo, "fsmonitor-invoked");
|
|
94
|
+
const hook = path.join(repo, "fsmonitor-hook.cjs");
|
|
95
|
+
fs.writeFileSync(hook, `require("node:fs").appendFileSync(${JSON.stringify(marker)}, "invoked\\n");\n`, "utf-8");
|
|
96
|
+
git(repo, ["config", "core.fsmonitor", `${JSON.stringify(process.execPath)} ${JSON.stringify(hook)}`]);
|
|
97
|
+
git(repo, ["status", "--short"]);
|
|
98
|
+
assert.equal(fs.existsSync(marker), true);
|
|
99
|
+
fs.rmSync(marker);
|
|
100
|
+
fs.writeFileSync(path.join(repo, "tracked.txt"), "dirty before child\n", "utf-8");
|
|
101
|
+
|
|
102
|
+
const snapshot = snapshotTrackedMutations(repo);
|
|
103
|
+
fs.writeFileSync(path.join(repo, "tracked.txt"), "dirty after child\n", "utf-8");
|
|
104
|
+
const evidence = collectTrackedMutationEvidence(snapshot, repo);
|
|
105
|
+
|
|
106
|
+
assert.equal(snapshot.unavailable, undefined);
|
|
107
|
+
assert.equal(evidence.attemptedMutation, true);
|
|
108
|
+
assert.equal(fs.existsSync(marker), false);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
91
112
|
it("uses tracked evidence as completion guard mutation proof", () => {
|
|
92
113
|
const guard = evaluateCompletionMutationGuard({
|
|
93
114
|
agent: "worker",
|
|
@@ -333,7 +333,7 @@ describe("nested event parsing and projection", () => {
|
|
|
333
333
|
ts: 100,
|
|
334
334
|
parentRunId: "root-run",
|
|
335
335
|
parentStepIndex: 1,
|
|
336
|
-
child: { ...child("nested-valid-tokens", "running", 100), totalTokens: { input: 10, output: 15, total: 25 } },
|
|
336
|
+
child: { ...child("nested-valid-tokens", "running", 100), totalTokens: { input: 10, output: 15, total: 25, window: 30, windowPeak: 40 } },
|
|
337
337
|
});
|
|
338
338
|
fs.writeFileSync(path.join(route.eventSink, "0000000000200-invalid-tokens.json"), `${JSON.stringify({
|
|
339
339
|
type: "subagent.nested.updated",
|
|
@@ -347,7 +347,7 @@ describe("nested event parsing and projection", () => {
|
|
|
347
347
|
|
|
348
348
|
const registry = projectNestedEvents(route);
|
|
349
349
|
|
|
350
|
-
assert.deepEqual(registry.children.find((item) => item.id === "nested-valid-tokens")?.totalTokens, { input: 10, output: 15, total: 25 });
|
|
350
|
+
assert.deepEqual(registry.children.find((item) => item.id === "nested-valid-tokens")?.totalTokens, { input: 10, output: 15, total: 25, window: 30, windowPeak: 40 });
|
|
351
351
|
assert.equal(registry.children.find((item) => item.id === "nested-invalid-tokens")?.totalTokens, undefined);
|
|
352
352
|
});
|
|
353
353
|
|
|
@@ -472,3 +472,21 @@ describe("nested event parsing and projection", () => {
|
|
|
472
472
|
assert.equal(secondProjection.children[0]?.state, "complete");
|
|
473
473
|
});
|
|
474
474
|
});
|
|
475
|
+
|
|
476
|
+
describe("nested session-name projection", () => {
|
|
477
|
+
it("keeps bounded root and step session names from async status", () => {
|
|
478
|
+
const summary = nestedSummaryFromAsyncStatus({
|
|
479
|
+
runId: "child-run",
|
|
480
|
+
mode: "single",
|
|
481
|
+
state: "running",
|
|
482
|
+
startedAt: 1,
|
|
483
|
+
steps: [{
|
|
484
|
+
agent: "reviewer",
|
|
485
|
+
sessionName: "reviewer: Inspect the changed auth middleware",
|
|
486
|
+
status: "running",
|
|
487
|
+
}],
|
|
488
|
+
}, "/tmp/child-run", { id: "child-run", parentRunId: "root-run", depth: 1, ts: 1 });
|
|
489
|
+
assert.equal(summary.sessionName, "reviewer: Inspect the changed auth middleware");
|
|
490
|
+
assert.equal(summary.steps?.[0]?.sessionName, "reviewer: Inspect the changed auth middleware");
|
|
491
|
+
});
|
|
492
|
+
});
|
|
@@ -10,6 +10,7 @@ import registerSubagentNotify, {
|
|
|
10
10
|
type SubagentNotifyDetails,
|
|
11
11
|
} from "../../src/runs/background/notify.ts";
|
|
12
12
|
import { SUBAGENT_ASYNC_COMPLETE_EVENT, SUBAGENT_FOREGROUND_COMPLETE_EVENT } from "../../src/shared/types.ts";
|
|
13
|
+
import { createResultDeliveryOwnership } from "../../src/runs/background/result-delivery-ownership.ts";
|
|
13
14
|
|
|
14
15
|
const COMPLETION_OWNER_ID = "completion-owner-a";
|
|
15
16
|
|
|
@@ -159,6 +160,47 @@ describe("registerSubagentNotify", () => {
|
|
|
159
160
|
assert.equal(sent.length, 0);
|
|
160
161
|
});
|
|
161
162
|
|
|
163
|
+
it("accepts only explicitly claimed predecessor-session completions", async () => {
|
|
164
|
+
const events = createEventBus();
|
|
165
|
+
const sent: unknown[] = [];
|
|
166
|
+
const pi = { events, sendMessage(message: unknown) { sent.push(message); } };
|
|
167
|
+
const state = { currentSessionId: "session-old" as string | null, completionOwnerId: COMPLETION_OWNER_ID };
|
|
168
|
+
const ownership = createResultDeliveryOwnership(state);
|
|
169
|
+
assert.equal(ownership.claimPredecessor("session-old", "session-old"), true);
|
|
170
|
+
state.currentSessionId = "session-new";
|
|
171
|
+
const notifier = registerSubagentNotify(pi as never, state, { batchConfig: { enabled: false }, ownership });
|
|
172
|
+
try {
|
|
173
|
+
assert.equal(await notifier.deliver(completionResult({ id: "predecessor", sessionId: "session-old" })), true);
|
|
174
|
+
assert.equal(await notifier.deliver(completionResult({ id: "foreign", sessionId: "session-foreign" })), false);
|
|
175
|
+
assert.equal(await notifier.deliver(completionResult({ id: "wrong-owner", sessionId: "session-old", completionOwnerId: "other-owner" })), false);
|
|
176
|
+
assert.equal(sent.length, 1);
|
|
177
|
+
} finally {
|
|
178
|
+
notifier.dispose();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("rechecks session ownership before emitting a delayed batch", async () => {
|
|
183
|
+
const clock = createFakeClock();
|
|
184
|
+
const events = createEventBus();
|
|
185
|
+
const sent: unknown[] = [];
|
|
186
|
+
const pi = { events, sendMessage(message: unknown) { sent.push(message); } };
|
|
187
|
+
const state = { currentSessionId: "session-a" as string | null, completionOwnerId: COMPLETION_OWNER_ID };
|
|
188
|
+
const notifier = registerSubagentNotify(pi as never, state, {
|
|
189
|
+
batchConfig: { enabled: true, debounceMs: 150, maxWaitMs: 1000, stragglerDebounceMs: 75, stragglerMaxWaitMs: 400, stragglerWindowMs: 2000 },
|
|
190
|
+
timers: clock.api,
|
|
191
|
+
now: clock.now,
|
|
192
|
+
});
|
|
193
|
+
try {
|
|
194
|
+
const pending = notifier.deliver(completionResult({ id: "delayed-unclaimed" }));
|
|
195
|
+
state.currentSessionId = "session-b";
|
|
196
|
+
clock.advance(150);
|
|
197
|
+
assert.equal(await pending, false);
|
|
198
|
+
assert.equal(sent.length, 0);
|
|
199
|
+
} finally {
|
|
200
|
+
notifier.dispose();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
162
204
|
it("does not wake the session when background delivery explicitly disables triggerTurn", async () => {
|
|
163
205
|
const { notifier, sent } = createPi("session-a");
|
|
164
206
|
assert.equal(await notifier.deliver(completionResult({ id: "direct-silent", triggerTurn: false })), true);
|
|
@@ -320,7 +362,7 @@ describe("registerSubagentNotify", () => {
|
|
|
320
362
|
assert.deepEqual(sent[0], {
|
|
321
363
|
message: {
|
|
322
364
|
customType: "subagent-notify",
|
|
323
|
-
content: "Background task paused: **workflow**\n\nRun 'detaches' detached for intercom coordination.\n\nWorkflow run: workflow-1\nChild runs: detaches=child-1 (paused)",
|
|
365
|
+
content: "Background task paused: **workflow**\n\nRun 'detaches' detached for intercom coordination.\n\nChild outputs:\n- key=detaches run=child-1 status=paused\n Saved output: unavailable\n Preview: unavailable (no safe inline output)\n\nWorkflow run: workflow-1\nChild runs: detaches=child-1 (paused)",
|
|
324
366
|
display: true,
|
|
325
367
|
},
|
|
326
368
|
options: { triggerTurn: true },
|
|
@@ -549,6 +591,89 @@ describe("completion formatting helpers", () => {
|
|
|
549
591
|
assert.equal(parsed?.reconciledFromDetachedChild, "child-2");
|
|
550
592
|
});
|
|
551
593
|
|
|
594
|
+
it("shows bounded child output paths and previews before workflow correlation metadata", () => {
|
|
595
|
+
const details = buildCompletionDetails({
|
|
596
|
+
id: "workflow-stopped",
|
|
597
|
+
runId: "workflow-stopped",
|
|
598
|
+
mode: "workflow",
|
|
599
|
+
agent: "workflow",
|
|
600
|
+
success: false,
|
|
601
|
+
state: "stopped",
|
|
602
|
+
summary: "Workflow stopped after one child completed.",
|
|
603
|
+
results: [
|
|
604
|
+
{
|
|
605
|
+
workflowKey: "review",
|
|
606
|
+
runId: "child-review",
|
|
607
|
+
agent: "worker",
|
|
608
|
+
success: true,
|
|
609
|
+
outputState: "present",
|
|
610
|
+
outputReference: "/tmp/review.md",
|
|
611
|
+
artifactPaths: { outputPath: "/tmp/legacy-review-path" },
|
|
612
|
+
output: `\u001b[31mReview heading\u001b[0m\n${"x".repeat(5_000)}`,
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
workflowKey: "stopped",
|
|
616
|
+
agent: "worker",
|
|
617
|
+
stopped: true,
|
|
618
|
+
outputState: "absent",
|
|
619
|
+
artifactPaths: { outputPath: "/tmp/stopped.md" },
|
|
620
|
+
},
|
|
621
|
+
],
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
assert.equal(details.status, "stopped");
|
|
625
|
+
assert.deepEqual(details.childOutputs?.map(({ workflowKey, runId, status, savedOutputPath }) => ({ workflowKey, runId, status, savedOutputPath })), [
|
|
626
|
+
{ workflowKey: "review", runId: "child-review", status: "completed", savedOutputPath: "/tmp/review.md" },
|
|
627
|
+
{ workflowKey: "stopped", runId: undefined, status: "stopped", savedOutputPath: undefined },
|
|
628
|
+
]);
|
|
629
|
+
assert.ok(Buffer.byteLength(details.childOutputs?.[0]?.preview ?? "", "utf8") <= 4 * 1024);
|
|
630
|
+
|
|
631
|
+
const content = formatSingleCompletion(details);
|
|
632
|
+
assert.match(content, /Child outputs:/);
|
|
633
|
+
assert.match(content, /key=review run=child-review status=completed/);
|
|
634
|
+
assert.match(content, /Saved output: \/tmp\/review\.md/);
|
|
635
|
+
assert.match(content, /Review heading/);
|
|
636
|
+
assert.doesNotMatch(content, /legacy-review-path/);
|
|
637
|
+
assert.match(content, /preview truncated/);
|
|
638
|
+
assert.match(content, /key=stopped run=unavailable status=stopped/);
|
|
639
|
+
assert.match(content, /Saved output: unavailable/);
|
|
640
|
+
assert.match(content, /Preview: unavailable \(no safe inline output\)/);
|
|
641
|
+
assert.doesNotMatch(content, /\u001b/);
|
|
642
|
+
assert.match(content, /Workflow run: workflow-stopped/);
|
|
643
|
+
assert.match(content, /Child runs: review=child-review \(completed\), stopped=unavailable \(stopped\)/);
|
|
644
|
+
|
|
645
|
+
const parsed = parseSubagentNotifyContent(content);
|
|
646
|
+
assert.equal(parsed?.workflowRunId, "workflow-stopped");
|
|
647
|
+
assert.match(parsed?.resultPreview ?? "", /Child outputs:/);
|
|
648
|
+
assert.match(parsed?.resultPreview ?? "", /Review heading/);
|
|
649
|
+
assert.doesNotMatch(parsed?.resultPreview ?? "", /Workflow run:/);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("does not label artifact-only paths as saved workflow output", () => {
|
|
653
|
+
const details = buildCompletionDetails({
|
|
654
|
+
id: "workflow-artifact-only",
|
|
655
|
+
runId: "workflow-artifact-only",
|
|
656
|
+
mode: "workflow",
|
|
657
|
+
agent: "workflow",
|
|
658
|
+
success: true,
|
|
659
|
+
results: [{
|
|
660
|
+
workflowKey: "artifact-only",
|
|
661
|
+
runId: "child-artifact-only",
|
|
662
|
+
agent: "worker",
|
|
663
|
+
success: true,
|
|
664
|
+
outputState: "present",
|
|
665
|
+
output: "inline artifact-only report",
|
|
666
|
+
artifactPaths: { outputPath: "/tmp/pi/async/child-artifact-only" },
|
|
667
|
+
}],
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
const content = formatSingleCompletion(details);
|
|
671
|
+
assert.match(content, /key=artifact-only run=child-artifact-only status=completed/);
|
|
672
|
+
assert.match(content, /Saved output: unavailable/);
|
|
673
|
+
assert.match(content, /inline artifact-only report/);
|
|
674
|
+
assert.doesNotMatch(content, /\/tmp\/pi\/async\/child-artifact-only/);
|
|
675
|
+
});
|
|
676
|
+
|
|
552
677
|
it("reports false when Pi rejects sendMessage synchronously", async () => {
|
|
553
678
|
const pi = { events: createEventBus(), sendMessage() { throw new Error("runtime inactive"); } };
|
|
554
679
|
const notifier = registerSubagentNotify(pi as never, { currentSessionId: "session-a" }, { batchConfig: { enabled: false } });
|
|
@@ -558,6 +683,9 @@ describe("completion formatting helpers", () => {
|
|
|
558
683
|
|
|
559
684
|
it("buildCompletionDetails derives paused and stopped statuses", () => {
|
|
560
685
|
assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, state: "paused", summary: "Paused after interrupt.", timestamp: 1 }).status, "paused");
|
|
686
|
+
assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, interrupted: true, summary: "interrupted", timestamp: 1 }).status, "paused");
|
|
687
|
+
const pausedWorkflow = buildCompletionDetails({ id: "workflow", agent: "workflow", mode: "workflow", state: "paused", results: [{ workflowKey: "failed", success: false }] });
|
|
688
|
+
assert.equal(pausedWorkflow.childOutputs?.[0]?.status, "failed");
|
|
561
689
|
assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "boom", exitCode: 1, timestamp: 1 }).status, "failed");
|
|
562
690
|
assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", exitCode: 1, processSignal: "SIGTERM", timestamp: 1 }).status, "stopped");
|
|
563
691
|
assert.equal(buildCompletionDetails({ id: "x", agent: "w", success: false, summary: "terminated", results: [{ success: false, exitCode: 1, processSignal: "SIGTERM" }], timestamp: 1 }).status, "stopped");
|
|
@@ -256,7 +256,7 @@ test("enabled tabs use a worktree sequence and successful Pi sessions get cleanu
|
|
|
256
256
|
assert.doesNotMatch(viewer, /(?:&|;)\s*exit(?:\s|$)/);
|
|
257
257
|
|
|
258
258
|
const progressDir = path.join(TEMP_ROOT_DIR, "orca-progress");
|
|
259
|
-
const manifestDir = path.join(dir, ".
|
|
259
|
+
const manifestDir = path.join(dir, ".pi-subagents", "views", "orca");
|
|
260
260
|
const manifestName = fs.readdirSync(manifestDir).find((name) => name.startsWith(`${runId}-2-`) && name.endsWith(".json"));
|
|
261
261
|
assert.ok(manifestName);
|
|
262
262
|
const manifest = await readManifestWithState(path.join(manifestDir, manifestName), "open");
|
|
@@ -272,7 +272,7 @@ test("enabled tabs use a worktree sequence and successful Pi sessions get cleanu
|
|
|
272
272
|
assert.match(text, /done output/);
|
|
273
273
|
const quotedSessionFile = `'${fs.realpathSync(sessionFile).replace(/'/g, `'"'"'`)}'`;
|
|
274
274
|
assert.ok(text.includes(`completed. To remove the Pi session for this run, run rm -- ${quotedSessionFile}`));
|
|
275
|
-
assert.doesNotMatch(text, /find ~\/\.
|
|
275
|
+
assert.doesNotMatch(text, /find ~\/\.selesai[\\/]agent\/sessions/);
|
|
276
276
|
|
|
277
277
|
const nestedCwd = path.join(dir, "packages", "app");
|
|
278
278
|
fs.mkdirSync(nestedCwd, { recursive: true });
|