@selesai/code 0.13.15 → 0.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +2 -14
- package/dist/bun/cli.d.ts +3 -1
- package/dist/bun/cli.js +3 -9
- package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
- package/dist/bun/sandbox-env-setup.d.ts +1 -0
- package/dist/bun/sandbox-env-setup.js +3 -0
- package/dist/cli/auth-command.d.ts +23 -0
- package/dist/cli/auth-command.js +102 -0
- package/dist/cli/config-selector.js +2 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.js +11 -0
- package/dist/cli.js +2 -14
- package/dist/config.d.ts +3 -2
- package/dist/config.js +9 -4
- package/dist/core/agent-session-runtime.js +15 -7
- package/dist/core/agent-session.d.ts +2 -6
- package/dist/core/agent-session.js +15 -4
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -1
- package/dist/core/extensions/loader.js +3 -0
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/http-dispatcher.js +2 -0
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.js +4 -0
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/model-config.d.ts +15 -0
- package/dist/core/model-config.js +3 -0
- package/dist/core/model-runtime.d.ts +1 -0
- package/dist/core/model-runtime.js +5 -2
- package/dist/core/provider-composer.js +8 -2
- package/dist/core/session-export.d.ts +3 -0
- package/dist/core/session-export.js +31 -0
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.js +40 -13
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.js +4 -2
- package/dist/core/system-prompt.js +10 -10
- package/dist/core/tools/bash.js +3 -117
- package/dist/core/tools/edit.d.ts +1 -12
- package/dist/core/tools/edit.js +5 -163
- package/dist/core/tools/find.js +4 -55
- package/dist/core/tools/grep.js +4 -60
- package/dist/core/tools/ls.js +4 -49
- package/dist/core/tools/read.d.ts +4 -0
- package/dist/core/tools/read.js +10 -122
- package/dist/core/tools/renderers/bash.d.ts +11 -0
- package/dist/core/tools/renderers/bash.js +129 -0
- package/dist/core/tools/renderers/edit.d.ts +22 -0
- package/dist/core/tools/renderers/edit.js +166 -0
- package/dist/core/tools/renderers/find.d.ts +9 -0
- package/dist/core/tools/renderers/find.js +63 -0
- package/dist/core/tools/renderers/grep.d.ts +9 -0
- package/dist/core/tools/renderers/grep.js +68 -0
- package/dist/core/tools/renderers/index.d.ts +27 -0
- package/dist/core/tools/renderers/index.js +46 -0
- package/dist/core/tools/renderers/ls.d.ts +9 -0
- package/dist/core/tools/renderers/ls.js +57 -0
- package/dist/core/tools/renderers/read.d.ts +10 -0
- package/dist/core/tools/renderers/read.js +128 -0
- package/dist/core/tools/renderers/write.d.ts +9 -0
- package/dist/core/tools/renderers/write.js +152 -0
- package/dist/core/tools/write.js +5 -146
- package/dist/extensions/capability-gateway/index.ts +12 -1
- package/dist/extensions/capability-gateway/integration.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
- package/dist/extensions/pi-hermes-memory/README.md +16 -12
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
- package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
- package/dist/extensions/pi-hermes-memory/package.json +4 -4
- package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
- package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
- package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
- package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +16 -2
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
- package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
- package/dist/extensions/pi-intercom/index.ts +1 -1
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
- package/dist/extensions/pi-intercom/project-agent.ts +1 -6
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
- package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
- package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
- package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
- package/dist/extensions/pi-subagents/README.md +3 -3
- package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/docs/agents.md +32 -16
- package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
- package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
- package/dist/extensions/pi-subagents/docs/missions.md +2 -0
- package/dist/extensions/pi-subagents/docs/models.md +58 -1
- package/dist/extensions/pi-subagents/docs/observability.md +50 -12
- package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
- package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
- package/dist/extensions/pi-subagents/package-lock.json +747 -177
- package/dist/extensions/pi-subagents/package.json +6 -4
- package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
- package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
- package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
- package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
- package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
- package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
- package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
- package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
- package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
- package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
- package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
- package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
- package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
- package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
- package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
- package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
- package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
- package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
- package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
- package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
- package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
- package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
- package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
- package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
- package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
- package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
- package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
- package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
- package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
- package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
- package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
- package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
- package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
- package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
- package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
- package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
- package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
- package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
- package/dist/extensions/pi-web-agent/src/types.ts +1 -1
- package/dist/extensions/pi-zentui/README.md +17 -0
- package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
- package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
- package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
- package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
- package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
- package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
- package/dist/extensions/pi-zentui/package-lock.json +277 -517
- package/dist/extensions/pi-zentui/package.json +7 -4
- package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
- package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
- package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
- package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
- package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
- package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
- package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
- package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
- package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
- package/dist/index.d.ts +1 -1
- package/dist/main.js +4 -1
- package/dist/modes/interactive/chat-viewport.d.ts +19 -0
- package/dist/modes/interactive/chat-viewport.js +27 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
- package/dist/modes/interactive/components/assistant-message.js +30 -13
- package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
- package/dist/modes/interactive/components/custom-editor.js +39 -1
- package/dist/modes/interactive/components/first-time-setup.js +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
- package/dist/modes/interactive/components/markdown-transform.js +18 -0
- package/dist/modes/interactive/components/model-selector.js +19 -17
- package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -20
- package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
- package/dist/modes/interactive/components/settings-submenu.js +164 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
- package/dist/modes/interactive/components/status-indicator.js +10 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.js +75 -18
- package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
- package/dist/modes/interactive/components/tool-execution.js +35 -33
- package/dist/modes/interactive/components/trust-selector.js +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts +13 -15
- package/dist/modes/interactive/interactive-mode.js +144 -80
- package/dist/modes/interactive/session-share.d.ts +15 -0
- package/dist/modes/interactive/session-share.js +183 -0
- package/dist/modes/interactive/theme/dark.json +2 -1
- package/dist/modes/interactive/theme/light.json +2 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +5 -0
- package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
- package/dist/modes/interactive/theme/theme-json.js +129 -0
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -5
- package/dist/modes/interactive/theme/theme.js +18 -119
- package/dist/modes/interactive/tui-renderer.d.ts +20 -0
- package/dist/modes/interactive/tui-renderer.js +65 -0
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/management-http.d.ts +24 -0
- package/dist/utils/management-http.js +53 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.js +8 -0
- package/dist/utils/tools-manager.d.ts +1 -0
- package/dist/utils/tools-manager.js +43 -21
- package/docs/docs.json +0 -4
- package/package.json +4 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
- package/dist/skills/implanger/SKILL.md +0 -70
- package/dist/skills/output/SKILL.md +0 -49
- package/dist/skills/planger/SKILL.md +0 -167
- package/dist/skills/workflow/SKILL.md +0 -95
- package/docs/workflows.md +0 -18
- /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* observation channel, not a delivery acknowledgement.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { debuglog } from "node:util";
|
|
9
10
|
import type { ExtensionAPI } from "@selesai/code";
|
|
10
11
|
import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.ts";
|
|
11
12
|
import {
|
|
@@ -34,6 +35,7 @@ export interface SubagentNotifyChildOutput {
|
|
|
34
35
|
export type SubagentNotifyWatchdogBlocker = Pick<ChildWatchdogWarningSummary, "summary" | "addressed" | "stalemate"> & { agent: string };
|
|
35
36
|
|
|
36
37
|
export interface SubagentNotifyDetails {
|
|
38
|
+
workflowReceiptPath?: string;
|
|
37
39
|
agent: string;
|
|
38
40
|
status: "completed" | "failed" | "paused" | "stopped";
|
|
39
41
|
source?: "async" | "foreground";
|
|
@@ -123,6 +125,7 @@ export interface RegisterSubagentNotifyOptions {
|
|
|
123
125
|
|
|
124
126
|
export interface CompletionNotifier {
|
|
125
127
|
deliver(result: CompletionNotification): Promise<boolean>;
|
|
128
|
+
hasPendingDelivery(): boolean;
|
|
126
129
|
dispose(): void;
|
|
127
130
|
}
|
|
128
131
|
|
|
@@ -188,11 +191,17 @@ function structuredOutputText(value: unknown): string | undefined {
|
|
|
188
191
|
}
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
function isDegenerateOutput(text: string): boolean {
|
|
195
|
+
return !text.trim() || text.trim() === "</think>";
|
|
196
|
+
}
|
|
197
|
+
|
|
191
198
|
function childInlinePreview(child: CompletionChild): { preview?: string; truncated?: boolean; unavailableReason?: string } {
|
|
192
199
|
const output = typeof child.output === "string" ? child.output : "";
|
|
193
200
|
const outputReference = childSavedOutputPath(child);
|
|
194
201
|
const referenceOnly = Boolean(outputReference && output.trim().startsWith("Output saved to:"));
|
|
195
|
-
const raw = child.outputState === "absent" || referenceOnly
|
|
202
|
+
const raw = child.outputState === "absent" || referenceOnly
|
|
203
|
+
? structuredOutputText(child.structuredOutput)
|
|
204
|
+
: isDegenerateOutput(output) ? structuredOutputText(child.structuredOutput) ?? output : output;
|
|
196
205
|
if (!raw?.trim()) {
|
|
197
206
|
return { unavailableReason: referenceOnly ? "saved output is file-only" : "no safe inline output" };
|
|
198
207
|
}
|
|
@@ -282,6 +291,7 @@ export function formatSingleCompletion(details: SubagentNotifyDetails): string {
|
|
|
282
291
|
: undefined;
|
|
283
292
|
return [
|
|
284
293
|
`${taskKind} ${details.status}: **${details.agent}**${details.taskInfo ?? ""}`,
|
|
294
|
+
details.workflowReceiptPath ? `Workflow receipt: ${details.workflowReceiptPath}` : undefined,
|
|
285
295
|
"",
|
|
286
296
|
scheduleLine,
|
|
287
297
|
scheduleLine ? "" : undefined,
|
|
@@ -302,7 +312,11 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
302
312
|
const lines = content.split("\n");
|
|
303
313
|
const match = (lines[0] ?? "").match(/^(Background task|Detached foreground task) (completed|failed|paused|stopped): \*\*(.+?)\*\*(?:\s+(\([^)]*\)))?$/);
|
|
304
314
|
if (!match) return undefined;
|
|
305
|
-
|
|
315
|
+
// Only the header slot before the blank separator can carry a receipt.
|
|
316
|
+
// Identical lines anywhere in model output remain preview text.
|
|
317
|
+
const receiptHeader = lines[1]?.startsWith("Workflow receipt: ") && lines[2] === "";
|
|
318
|
+
const workflowReceiptPath = receiptHeader ? lines[1]!.slice("Workflow receipt: ".length) : undefined;
|
|
319
|
+
let body = lines.slice(receiptHeader ? 3 : 2);
|
|
306
320
|
// Restore the schedule origin so a re-rendered notice keeps its attribution and
|
|
307
321
|
// does not fold the line into the result preview.
|
|
308
322
|
const scheduleMatch = (body[0] ?? "").match(/^Scheduled run from \*\*(.+?)\*\* \(schedule (.+?)\)\.$/);
|
|
@@ -358,6 +372,7 @@ export function parseSubagentNotifyContent(content: string): SubagentNotifyDetai
|
|
|
358
372
|
...(match[1] === "Detached foreground task" ? { source: "foreground" as const } : {}),
|
|
359
373
|
...(match[4] ? { taskInfo: match[4] } : {}),
|
|
360
374
|
...(parsedScheduleOrigin ? { scheduleOrigin: parsedScheduleOrigin } : {}),
|
|
375
|
+
...(workflowReceiptPath ? { workflowReceiptPath } : {}),
|
|
361
376
|
resultPreview,
|
|
362
377
|
...(handoffPath ? { handoffPath } : {}),
|
|
363
378
|
...(workflowRunId ? { workflowRunId } : {}),
|
|
@@ -376,6 +391,7 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
|
|
|
376
391
|
if (!detail) continue;
|
|
377
392
|
const sessionLine = formatSessionLine(detail);
|
|
378
393
|
blocks.push(`${index + 1}. ${detail.agent}${detail.taskInfo ?? ""}${detail.scheduleOrigin ? ` — scheduled run from ${detail.scheduleOrigin.name ?? detail.scheduleOrigin.id} (schedule ${detail.scheduleOrigin.id})` : ""}`);
|
|
394
|
+
if (detail.workflowReceiptPath) blocks.push(`Workflow receipt: ${detail.workflowReceiptPath}`);
|
|
379
395
|
blocks.push(formatResultPreview(detail));
|
|
380
396
|
blocks.push(...formatWatchdogBlockerLines(detail));
|
|
381
397
|
if (detail.handoffPath) blocks.push(`Parallel handoff: ${detail.handoffPath}`);
|
|
@@ -386,7 +402,31 @@ export function formatGroupedCompletion(details: SubagentNotifyDetails[]): strin
|
|
|
386
402
|
return blocks.join("\n").trimEnd();
|
|
387
403
|
}
|
|
388
404
|
|
|
405
|
+
const notificationDebug = debuglog("pi-subagents-notify");
|
|
406
|
+
type TraceIdentity = Pick<CompletionNotification, "id" | "runId" | "source">;
|
|
407
|
+
type NotificationReason = "disposed" | "missing_session" | "foreground_session_mismatch" | "not_owned"
|
|
408
|
+
| "intercom_delivered" | "deduped_ttl" | "deduped_pending" | "batch_deferred"
|
|
409
|
+
| "emit_foreground_session_mismatch" | "emit_not_owned" | "send_accepted" | "send_failed" | "dispose_pending";
|
|
410
|
+
|
|
411
|
+
// Slice before sanitizing: diagnostic work and each identity are bounded even for
|
|
412
|
+
// malformed result metadata. Never include task/output, paths, or error bodies.
|
|
413
|
+
function traceIdentity(result: TraceIdentity): TraceIdentity {
|
|
414
|
+
const identity = (value: unknown) => typeof value === "string"
|
|
415
|
+
? value.slice(0, 128).replace(/[^a-zA-Z0-9_.:-]/g, "_") : undefined;
|
|
416
|
+
return { id: identity(result.id), runId: identity(result.runId), source: result.source === "foreground" ? "foreground" : "async" };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function traceNotification(reason: NotificationReason, result?: TraceIdentity): void {
|
|
420
|
+
if (!notificationDebug.enabled) return;
|
|
421
|
+
try {
|
|
422
|
+
notificationDebug("%s", JSON.stringify({ reason, ...(result ? traceIdentity(result) : {}) }));
|
|
423
|
+
} catch {
|
|
424
|
+
// Diagnostics must never change delivery acknowledgement.
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
389
428
|
interface PendingCompletion {
|
|
429
|
+
trace?: TraceIdentity;
|
|
390
430
|
key: string;
|
|
391
431
|
details: SubagentNotifyDetails;
|
|
392
432
|
sessionId: string;
|
|
@@ -447,6 +487,10 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
447
487
|
? result.parallelHandoff as { path?: unknown }
|
|
448
488
|
: undefined;
|
|
449
489
|
const handoffPath = typeof parallelHandoff?.path === "string" ? parallelHandoff.path : undefined;
|
|
490
|
+
const receipt = result.workflowReceipt;
|
|
491
|
+
const receiptPath = receipt && typeof receipt === "object" && !Array.isArray(receipt)
|
|
492
|
+
? (receipt as Record<string, unknown>).path : undefined;
|
|
493
|
+
const workflowReceiptPath = typeof receiptPath === "string" && receiptPath ? receiptPath : undefined;
|
|
450
494
|
const rawRunId = typeof result.runId === "string" ? result.runId : typeof result.id === "string" ? result.id : undefined;
|
|
451
495
|
const workflowRunId = (result.mode === "workflow" || agent === "workflow") && rawRunId ? rawRunId : undefined;
|
|
452
496
|
const directChild = !workflowRunId && result.results?.length === 1 ? result.results[0]! : undefined;
|
|
@@ -455,10 +499,17 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
455
499
|
: undefined;
|
|
456
500
|
const directSummary = summary.trim();
|
|
457
501
|
const directAgent = typeof directChild?.agent === "string" ? directChild.agent : agent;
|
|
502
|
+
const directSummaryBody = directAgent && summary.trimStart().startsWith(`${directAgent}:\n`)
|
|
503
|
+
? summary.trimStart().slice(directAgent.length + 2)
|
|
504
|
+
: directSummary;
|
|
505
|
+
const directDegenerateSummary = directChild
|
|
506
|
+
&& isDegenerateOutput(typeof directChild.output === "string" ? directChild.output : "")
|
|
507
|
+
&& isDegenerateOutput(directSummaryBody)
|
|
508
|
+
&& structuredOutputText(directChild.structuredOutput) !== undefined;
|
|
458
509
|
const directNoOutputSummary = directChild && (!directSummary
|
|
459
510
|
|| directSummary === "(no output)"
|
|
460
511
|
|| (directAgent && directSummary === `${directAgent}:\n(no output)`));
|
|
461
|
-
const resultPreview = directStructuredPreview && directNoOutputSummary
|
|
512
|
+
const resultPreview = directStructuredPreview && (directNoOutputSummary || directDegenerateSummary)
|
|
462
513
|
? `Structured output:\n${directStructuredPreview}`
|
|
463
514
|
: summary;
|
|
464
515
|
const childRuns = result.results?.flatMap((child) => {
|
|
@@ -516,6 +567,7 @@ export function buildCompletionDetails(result: CompletionNotification): Subagent
|
|
|
516
567
|
agent,
|
|
517
568
|
status,
|
|
518
569
|
...(scheduleOrigin ? { scheduleOrigin } : {}),
|
|
570
|
+
...(workflowReceiptPath ? { workflowReceiptPath } : {}),
|
|
519
571
|
...(result.source ? { source: result.source } : {}),
|
|
520
572
|
...(taskInfo ? { taskInfo } : {}),
|
|
521
573
|
resultPreview,
|
|
@@ -547,8 +599,9 @@ export default function registerSubagentNotify(
|
|
|
547
599
|
&& typeof completionOwnerId === "string"
|
|
548
600
|
&& completionOwnerId === state.completionOwnerId);
|
|
549
601
|
|
|
550
|
-
const settle = (items: PendingCompletion[], accepted: boolean) => {
|
|
602
|
+
const settle = (items: PendingCompletion[], accepted: boolean, reason?: NotificationReason) => {
|
|
551
603
|
for (const item of items) {
|
|
604
|
+
if (reason) traceNotification(reason, item.trace);
|
|
552
605
|
pending.delete(item.key);
|
|
553
606
|
if (accepted) markSeenWithTtl(seen, item.key, now(), ttlMs);
|
|
554
607
|
item.resolve(accepted);
|
|
@@ -561,10 +614,12 @@ export default function registerSubagentNotify(
|
|
|
561
614
|
const owned = item.details.source === "foreground"
|
|
562
615
|
? item.sessionId === state.currentSessionId
|
|
563
616
|
: ownsResult(item.sessionId, item.completionOwnerId);
|
|
617
|
+
if (!owned) traceNotification(item.details.source === "foreground" ? "emit_foreground_session_mismatch" : "emit_not_owned", item.trace);
|
|
564
618
|
(owned ? accepted : rejected).push(item);
|
|
565
619
|
}
|
|
566
620
|
settle(rejected, false);
|
|
567
|
-
|
|
621
|
+
const sent = sendCompletion(pi, accepted);
|
|
622
|
+
settle(accepted, sent, sent ? "send_accepted" : "send_failed");
|
|
568
623
|
};
|
|
569
624
|
const getBatcher = (result: CompletionNotification) => {
|
|
570
625
|
const key = completionBatchKey(result);
|
|
@@ -582,17 +637,35 @@ export default function registerSubagentNotify(
|
|
|
582
637
|
};
|
|
583
638
|
|
|
584
639
|
const deliver = (result: CompletionNotification): Promise<boolean> => {
|
|
585
|
-
if (disposed || typeof result.sessionId !== "string")
|
|
640
|
+
if (disposed || typeof result.sessionId !== "string") {
|
|
641
|
+
traceNotification(disposed ? "disposed" : "missing_session", result);
|
|
642
|
+
return Promise.resolve(false);
|
|
643
|
+
}
|
|
586
644
|
if (result.source === "foreground") {
|
|
587
|
-
if (result.sessionId !== state.currentSessionId)
|
|
588
|
-
|
|
589
|
-
|
|
645
|
+
if (result.sessionId !== state.currentSessionId) {
|
|
646
|
+
traceNotification("foreground_session_mismatch", result);
|
|
647
|
+
return Promise.resolve(false);
|
|
648
|
+
}
|
|
649
|
+
} else if (!ownsResult(result.sessionId, result.completionOwnerId)) {
|
|
650
|
+
traceNotification("not_owned", result);
|
|
651
|
+
return Promise.resolve(false);
|
|
652
|
+
}
|
|
653
|
+
if (result.intercomDelivered === true) {
|
|
654
|
+
traceNotification("intercom_delivered", result);
|
|
655
|
+
return Promise.resolve(true);
|
|
656
|
+
}
|
|
590
657
|
const key = buildCompletionKey(result, "notify");
|
|
591
658
|
const seenAt = seen.get(key);
|
|
592
|
-
if (seenAt !== undefined && now() - seenAt <= ttlMs)
|
|
659
|
+
if (seenAt !== undefined && now() - seenAt <= ttlMs) {
|
|
660
|
+
traceNotification("deduped_ttl", result);
|
|
661
|
+
return Promise.resolve(true);
|
|
662
|
+
}
|
|
593
663
|
if (seenAt !== undefined) seen.delete(key);
|
|
594
664
|
const inFlight = pending.get(key);
|
|
595
|
-
if (inFlight)
|
|
665
|
+
if (inFlight) {
|
|
666
|
+
traceNotification("deduped_pending", result);
|
|
667
|
+
return inFlight;
|
|
668
|
+
}
|
|
596
669
|
const details = buildCompletionDetails(result);
|
|
597
670
|
let resolve!: (accepted: boolean) => void;
|
|
598
671
|
const completion = new Promise<boolean>((settleCompletion) => { resolve = settleCompletion; });
|
|
@@ -605,6 +678,7 @@ export default function registerSubagentNotify(
|
|
|
605
678
|
triggerTurn: result.triggerTurn !== false,
|
|
606
679
|
resolve,
|
|
607
680
|
};
|
|
681
|
+
if (notificationDebug.enabled) item.trace = traceIdentity(result);
|
|
608
682
|
if (details.source === "foreground") {
|
|
609
683
|
emit([item]);
|
|
610
684
|
return completion;
|
|
@@ -615,6 +689,7 @@ export default function registerSubagentNotify(
|
|
|
615
689
|
emit([item]);
|
|
616
690
|
return completion;
|
|
617
691
|
}
|
|
692
|
+
if (batchConfig.enabled) traceNotification("batch_deferred", item.trace);
|
|
618
693
|
batcher.push(item);
|
|
619
694
|
return completion;
|
|
620
695
|
};
|
|
@@ -628,10 +703,11 @@ export default function registerSubagentNotify(
|
|
|
628
703
|
|
|
629
704
|
return {
|
|
630
705
|
deliver,
|
|
706
|
+
hasPendingDelivery: () => pending.size > 0,
|
|
631
707
|
dispose() {
|
|
632
708
|
if (disposed) return;
|
|
633
709
|
disposed = true;
|
|
634
|
-
for (const batcher of batchers.values()) settle(batcher.dispose(), false);
|
|
710
|
+
for (const batcher of batchers.values()) settle(batcher.dispose(), false, "dispose_pending");
|
|
635
711
|
batchers.clear();
|
|
636
712
|
for (const unsubscribe of [unsubscribeAsync, unsubscribeForeground]) {
|
|
637
713
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProcessTreeTerminal } from "../../shared/types.ts";
|
|
3
3
|
|
|
4
4
|
const DEFAULT_TERM_GRACE_MS = 3000;
|
|
5
5
|
const DEFAULT_KILL_VERIFY_MS = 1000;
|
|
@@ -52,25 +52,25 @@ async function waitUntilGroupTerminal(
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function observed(processGroupId: number):
|
|
55
|
+
function observed(processGroupId: number): ProcessTreeTerminal {
|
|
56
56
|
return { state: "observed", mechanism: "posix-process-group", processGroupId, verifiedAt: Date.now() };
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/** Owns one writer process group and arbitrates its cleanup exactly once. */
|
|
60
60
|
export interface OwnedProcessTreeController {
|
|
61
|
-
terminate(): Promise<
|
|
62
|
-
finishAfterWriterClose(): Promise<
|
|
61
|
+
terminate(): Promise<ProcessTreeTerminal>;
|
|
62
|
+
finishAfterWriterClose(): Promise<ProcessTreeTerminal>;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export function createOwnedProcessTreeController(
|
|
66
66
|
pid: number,
|
|
67
67
|
options: { termGraceMs?: number; killVerifyMs?: number } = {},
|
|
68
68
|
): OwnedProcessTreeController {
|
|
69
|
-
let termination: Promise<
|
|
69
|
+
let termination: Promise<ProcessTreeTerminal> | undefined;
|
|
70
70
|
const posixGroupOwned = process.platform !== "win32";
|
|
71
71
|
const target = posixGroupOwned ? -pid : pid;
|
|
72
72
|
|
|
73
|
-
const terminate = (): Promise<
|
|
73
|
+
const terminate = (): Promise<ProcessTreeTerminal> => {
|
|
74
74
|
if (termination) return termination;
|
|
75
75
|
termination = (async () => {
|
|
76
76
|
if (!posixGroupOwned) {
|
|
@@ -4,10 +4,10 @@ import { writeAtomicJson, writePrivateAtomicJson } from "../../shared/atomic-jso
|
|
|
4
4
|
import {
|
|
5
5
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
6
6
|
type AsyncStatus,
|
|
7
|
-
type
|
|
8
|
-
type
|
|
7
|
+
type CanonicalSessionTerminal,
|
|
8
|
+
type ProcessInstanceExit,
|
|
9
9
|
type ProcessTerminalReason,
|
|
10
|
-
type
|
|
10
|
+
type ProcessTerminal,
|
|
11
11
|
} from "../../shared/types.ts";
|
|
12
12
|
import { canonicalSessionId, inspectSessionLease } from "../shared/session-lease.ts";
|
|
13
13
|
import { releaseActiveRunIndex } from "./active-run-index.ts";
|
|
@@ -16,7 +16,7 @@ export interface ProcessTerminalCandidate {
|
|
|
16
16
|
version: 1;
|
|
17
17
|
runId: string;
|
|
18
18
|
runnerProcessInstanceId: string;
|
|
19
|
-
writers: Record<string,
|
|
19
|
+
writers: Record<string, ProcessInstanceExit[]>;
|
|
20
20
|
expectedWriters?: Record<string, number>;
|
|
21
21
|
sessionFile?: string;
|
|
22
22
|
revivalLeaseToken?: string;
|
|
@@ -34,7 +34,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
34
34
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): value is
|
|
37
|
+
function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): value is ProcessInstanceExit {
|
|
38
38
|
if (!isRecord(value)) return false;
|
|
39
39
|
if (typeof value.processInstanceId !== "string" || value.processInstanceId.length === 0) return false;
|
|
40
40
|
if (kind ? value.kind !== kind : (value.kind !== "runner" && value.kind !== "pi-writer")) return false;
|
|
@@ -56,7 +56,7 @@ function validProcessInstance(value: unknown, kind?: "runner" | "pi-writer"): va
|
|
|
56
56
|
&& (value.processTree.diagnostic === undefined || typeof value.processTree.diagnostic === "string");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function validInstance(value: unknown): value is
|
|
59
|
+
function validInstance(value: unknown): value is ProcessInstanceExit {
|
|
60
60
|
return validProcessInstance(value, "pi-writer");
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -78,7 +78,7 @@ export function readProcessTerminalCandidate(asyncDir: string): ProcessTerminalC
|
|
|
78
78
|
if (!isRecord(raw) || raw.version !== 1 || typeof raw.runId !== "string" || typeof raw.runnerProcessInstanceId !== "string" || !isRecord(raw.writers)) {
|
|
79
79
|
throw new Error(`Invalid process-terminal candidate in '${asyncDir}'.`);
|
|
80
80
|
}
|
|
81
|
-
const writers: Record<string,
|
|
81
|
+
const writers: Record<string, ProcessInstanceExit[]> = {};
|
|
82
82
|
for (const [index, entries] of Object.entries(raw.writers)) {
|
|
83
83
|
if (!Array.isArray(entries) || !entries.every(validInstance)) throw new Error(`Invalid writer process records for child '${index}'.`);
|
|
84
84
|
writers[index] = entries;
|
|
@@ -115,7 +115,7 @@ export function writeProcessTerminalCandidate(asyncDir: string, candidate: Proce
|
|
|
115
115
|
writePrivateAtomicJson(processTerminalCandidatePath(asyncDir), candidate);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
/** Establish ownership before authorizing a runner to
|
|
118
|
+
/** Establish ownership before authorizing a runner to start any child session. */
|
|
119
119
|
export function initializeProcessTerminal(asyncDir: string, runId: string, runnerProcessInstanceId: string): void {
|
|
120
120
|
writeProcessTerminalCandidate(asyncDir, {
|
|
121
121
|
version: 1,
|
|
@@ -137,7 +137,7 @@ export function markProcessTerminalCandidateLeaseRelease(asyncDir: string, token
|
|
|
137
137
|
writeProcessTerminalCandidate(asyncDir, { ...candidate, revivalLeaseReleaseAcknowledged: acknowledged });
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
function unknownProof(runId: string, runnerProcessInstanceId: string, reason: ProcessTerminalReason, diagnostic?: string):
|
|
140
|
+
function unknownProof(runId: string, runnerProcessInstanceId: string, reason: ProcessTerminalReason, diagnostic?: string): ProcessTerminal {
|
|
141
141
|
return { version: 1, state: "unknown", runId, runnerProcessInstanceId, reason, ...(diagnostic ? { diagnostic } : {}) };
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -147,7 +147,7 @@ function resumeDisposition(state: string | undefined, sessionFile: string | unde
|
|
|
147
147
|
return sessionFile && fs.existsSync(sessionFile) ? "resumable" : "unavailable";
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnType<typeof inspectSessionLease>):
|
|
150
|
+
function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnType<typeof inspectSessionLease>): CanonicalSessionTerminal | undefined {
|
|
151
151
|
if (!candidate.sessionFile || lease.state !== "free") return undefined;
|
|
152
152
|
if (candidate.revivalLeaseToken && candidate.revivalLeaseReleaseAcknowledged !== true) return undefined;
|
|
153
153
|
return {
|
|
@@ -158,7 +158,7 @@ function sessionProjection(candidate: ProcessTerminalCandidate, lease: ReturnTyp
|
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): raw is
|
|
161
|
+
function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): raw is ProcessTerminal {
|
|
162
162
|
if (!isRecord(raw) || raw.version !== 1 || !["pending", "observed", "unknown", "not-started"].includes(String(raw.state)) || typeof raw.runId !== "string" || !raw.runId || typeof raw.runnerProcessInstanceId !== "string" || !raw.runnerProcessInstanceId) {
|
|
163
163
|
throw new Error(`Invalid process-terminal proof in '${asyncDir}'.`);
|
|
164
164
|
}
|
|
@@ -177,21 +177,21 @@ function validateProof(raw: unknown, asyncDir: string, fallback?: { runId?: stri
|
|
|
177
177
|
return true;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
export function sanitizeProcessTerminal(value: unknown, fallback: { runId?: string; runnerProcessInstanceId?: string }, label = "status"):
|
|
180
|
+
export function sanitizeProcessTerminal(value: unknown, fallback: { runId?: string; runnerProcessInstanceId?: string }, label = "status"): ProcessTerminal | undefined {
|
|
181
181
|
if (value === undefined) return undefined;
|
|
182
182
|
try {
|
|
183
183
|
validateProof(value, label, fallback);
|
|
184
|
-
return value as
|
|
184
|
+
return value as ProcessTerminal;
|
|
185
185
|
} catch (error) {
|
|
186
186
|
return unknownProof(fallback.runId ?? label, fallback.runnerProcessInstanceId ?? "unknown", "proof-write-failed", errorMessage(error));
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
export function readProcessTerminal(asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }):
|
|
190
|
+
export function readProcessTerminal(asyncDir: string, fallback?: { runId?: string; runnerProcessInstanceId?: string }): ProcessTerminal | undefined {
|
|
191
191
|
try {
|
|
192
192
|
const raw = JSON.parse(fs.readFileSync(processTerminalPath(asyncDir), "utf-8")) as unknown;
|
|
193
193
|
validateProof(raw, asyncDir, fallback);
|
|
194
|
-
return raw as
|
|
194
|
+
return raw as ProcessTerminal;
|
|
195
195
|
} catch (error) {
|
|
196
196
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
197
197
|
return unknownProof(fallback?.runId ?? path.basename(asyncDir), fallback?.runnerProcessInstanceId ?? "unknown", "proof-write-failed", errorMessage(error));
|
|
@@ -199,12 +199,12 @@ export function readProcessTerminal(asyncDir: string, fallback?: { runId?: strin
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
function stepProcessTerminalProof(
|
|
202
|
-
proof:
|
|
202
|
+
proof: ProcessTerminal,
|
|
203
203
|
childIndex: number,
|
|
204
|
-
state:
|
|
205
|
-
records:
|
|
206
|
-
resumeDispositionValue:
|
|
207
|
-
):
|
|
204
|
+
state: ProcessTerminal["state"],
|
|
205
|
+
records: ProcessInstanceExit[],
|
|
206
|
+
resumeDispositionValue: ProcessTerminal["resumeDisposition"],
|
|
207
|
+
): ProcessTerminal {
|
|
208
208
|
const base = {
|
|
209
209
|
version: 1 as const,
|
|
210
210
|
runId: proof.runId,
|
|
@@ -221,7 +221,7 @@ function stepProcessTerminalProof(
|
|
|
221
221
|
return { ...base, state };
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
function overlayStatus(asyncDir: string, proof:
|
|
224
|
+
function overlayStatus(asyncDir: string, proof: ProcessTerminal, candidate?: ProcessTerminalCandidate): void {
|
|
225
225
|
const statusPath = path.join(asyncDir, "status.json");
|
|
226
226
|
try {
|
|
227
227
|
const status = JSON.parse(fs.readFileSync(statusPath, "utf-8")) as AsyncStatus;
|
|
@@ -244,13 +244,13 @@ export function finalizeProcessTerminal(
|
|
|
244
244
|
asyncDir: string,
|
|
245
245
|
runId: string,
|
|
246
246
|
runnerClose: RunnerCloseObservation,
|
|
247
|
-
):
|
|
247
|
+
): ProcessTerminal {
|
|
248
248
|
const existing = readProcessTerminal(asyncDir, { runId, runnerProcessInstanceId: runnerClose.processInstanceId });
|
|
249
249
|
if (existing && fs.existsSync(processTerminalPath(asyncDir))) {
|
|
250
250
|
if (existing.state === "observed" && existing.runId === runId && existing.runnerProcessInstanceId === runnerClose.processInstanceId) return existing;
|
|
251
251
|
if (existing.state === "unknown") return existing;
|
|
252
252
|
}
|
|
253
|
-
let proof:
|
|
253
|
+
let proof: ProcessTerminal;
|
|
254
254
|
let candidateForOverlay: ProcessTerminalCandidate | undefined;
|
|
255
255
|
try {
|
|
256
256
|
const candidate = readProcessTerminalCandidate(asyncDir);
|
|
@@ -279,7 +279,7 @@ export function finalizeProcessTerminal(
|
|
|
279
279
|
} else if (allWriters.some((writer) => writer.kind === "pi-writer" && writer.processTree.state !== "observed")) {
|
|
280
280
|
proof = unknownProof(runId, runnerClose.processInstanceId, "process-tree-unverified");
|
|
281
281
|
} else {
|
|
282
|
-
const runner:
|
|
282
|
+
const runner: ProcessInstanceExit = { kind: "runner", ...runnerClose };
|
|
283
283
|
const canonicalSession = session && sessionProjection(candidate, session);
|
|
284
284
|
proof = {
|
|
285
285
|
version: 1,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import type { SubagentState } from "../../shared/types.ts";
|
|
3
|
+
import { shouldUseNativeFsWatch } from "../../shared/watch-strategy.ts";
|
|
4
|
+
import { hasLiveNestedDescendants, projectNestedEvents } from "../shared/nested-events.ts";
|
|
5
|
+
|
|
6
|
+
interface RetainedNestedRouteTrackerOptions {
|
|
7
|
+
pollIntervalMs?: number;
|
|
8
|
+
platform?: NodeJS.Platform;
|
|
9
|
+
watch?: typeof fs.watch;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
13
|
+
const REFRESH_DEBOUNCE_MS = 25;
|
|
14
|
+
|
|
15
|
+
export function createRetainedNestedRouteTracker(
|
|
16
|
+
state: Pick<SubagentState, "retainedForegroundNestedRoutes">,
|
|
17
|
+
options: RetainedNestedRouteTrackerOptions = {},
|
|
18
|
+
): { track: (rootRunId: string) => void; clear: () => void } {
|
|
19
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
20
|
+
const watch = options.watch ?? fs.watch;
|
|
21
|
+
const watchers = new Map<string, fs.FSWatcher>();
|
|
22
|
+
const refreshTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
23
|
+
let poller: ReturnType<typeof setInterval> | undefined;
|
|
24
|
+
|
|
25
|
+
const close = (rootRunId: string): void => {
|
|
26
|
+
watchers.get(rootRunId)?.close();
|
|
27
|
+
watchers.delete(rootRunId);
|
|
28
|
+
const timer = refreshTimers.get(rootRunId);
|
|
29
|
+
if (timer) clearTimeout(timer);
|
|
30
|
+
refreshTimers.delete(rootRunId);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const refresh = (rootRunId: string): void => {
|
|
34
|
+
const retained = state.retainedForegroundNestedRoutes?.get(rootRunId);
|
|
35
|
+
if (!retained) {
|
|
36
|
+
close(rootRunId);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
if (hasLiveNestedDescendants(projectNestedEvents(retained).children)) return;
|
|
41
|
+
state.retainedForegroundNestedRoutes?.delete(rootRunId);
|
|
42
|
+
close(rootRunId);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(`Failed to refresh retained nested descendants for foreground run '${rootRunId}':`, error);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const scheduleRefresh = (rootRunId: string): void => {
|
|
49
|
+
if (refreshTimers.has(rootRunId)) return;
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
refreshTimers.delete(rootRunId);
|
|
52
|
+
refresh(rootRunId);
|
|
53
|
+
}, REFRESH_DEBOUNCE_MS);
|
|
54
|
+
timer.unref?.();
|
|
55
|
+
refreshTimers.set(rootRunId, timer);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const ensurePoller = (): void => {
|
|
59
|
+
if (poller || (state.retainedForegroundNestedRoutes?.size ?? 0) === 0) return;
|
|
60
|
+
poller = setInterval(() => {
|
|
61
|
+
for (const rootRunId of state.retainedForegroundNestedRoutes?.keys() ?? []) refresh(rootRunId);
|
|
62
|
+
if ((state.retainedForegroundNestedRoutes?.size ?? 0) > 0) return;
|
|
63
|
+
if (poller) clearInterval(poller);
|
|
64
|
+
poller = undefined;
|
|
65
|
+
}, pollIntervalMs);
|
|
66
|
+
poller.unref?.();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const track = (rootRunId: string): void => {
|
|
70
|
+
const retained = state.retainedForegroundNestedRoutes?.get(rootRunId);
|
|
71
|
+
if (!retained) return;
|
|
72
|
+
if (shouldUseNativeFsWatch("retained-nested-route-tracker", options.platform) && !watchers.has(rootRunId)) {
|
|
73
|
+
try {
|
|
74
|
+
const watcher = watch(retained.eventSink, () => scheduleRefresh(rootRunId));
|
|
75
|
+
watcher.on("error", () => close(rootRunId));
|
|
76
|
+
watcher.unref?.();
|
|
77
|
+
watchers.set(rootRunId, watcher);
|
|
78
|
+
} catch {
|
|
79
|
+
// The bounded safety poll below covers unsupported or failed watchers.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
scheduleRefresh(rootRunId);
|
|
83
|
+
ensurePoller();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const clear = (): void => {
|
|
87
|
+
if (poller) clearInterval(poller);
|
|
88
|
+
poller = undefined;
|
|
89
|
+
for (const rootRunId of watchers.keys()) close(rootRunId);
|
|
90
|
+
for (const timer of refreshTimers.values()) clearTimeout(timer);
|
|
91
|
+
refreshTimers.clear();
|
|
92
|
+
state.retainedForegroundNestedRoutes?.clear();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return { track, clear };
|
|
96
|
+
}
|