@selesai/code 0.2.1 → 0.3.1
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/dist/config.d.ts +4 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -45
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +8 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/diagnostics.d.ts +1 -0
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/diagnostics.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +101 -51
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/package-manager.d.ts +10 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +93 -8
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/package-manager.test.d.ts +2 -0
- package/dist/core/package-manager.test.d.ts.map +1 -0
- package/dist/core/package-manager.test.js +20 -0
- package/dist/core/package-manager.test.js.map +1 -0
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +10 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/defaults/models.json +64 -113
- package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
- package/dist/extensions/caveman/index.js +116 -0
- package/dist/extensions/caveman/package.json +8 -0
- package/dist/extensions/caveman/test/extension.test.js +185 -0
- package/dist/extensions/caveman/test/helpers.test.js +67 -0
- package/dist/extensions/copy-turn.ts +2 -2
- package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
- package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
- package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
- package/dist/extensions/pi-intercom/config.ts +2 -2
- package/dist/extensions/pi-intercom/index.ts +2 -2
- package/dist/extensions/pi-intercom/package.json +2 -2
- package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
- package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
- package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +2 -2
- package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
- package/dist/extensions/pi-powerline-footer/index.ts +40 -47
- package/dist/extensions/pi-powerline-footer/package.json +2 -2
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +10 -10
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +1 -1
- package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +2 -2
- package/dist/extensions/pi-powerline-footer/theme.ts +6 -6
- package/dist/extensions/pi-powerline-footer/tps.ts +1 -1
- package/dist/extensions/pi-powerline-footer/types.ts +7 -7
- package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
- package/dist/extensions/pi-powerline-footer/working-vibes.ts +52 -59
- package/dist/extensions/pi-rewind-hook/index.ts +1 -1
- package/dist/extensions/pi-rewind-hook/package.json +1 -1
- package/dist/extensions/pi-subagents/README.md +224 -52
- package/dist/extensions/pi-subagents/agents/architect.md +188 -0
- package/dist/extensions/pi-subagents/agents/builder.md +120 -0
- package/dist/extensions/pi-subagents/agents/commentator.md +131 -0
- package/dist/extensions/pi-subagents/agents/explorer.md +51 -0
- package/dist/extensions/pi-subagents/agents/recapper.md +21 -0
- package/dist/extensions/pi-subagents/install.mjs +93 -0
- package/dist/extensions/pi-subagents/package.json +4 -5
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
- package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
- package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
- package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
- package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
- package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
- package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
- package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
- package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
- package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
- package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
- package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +108 -17
- package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
- package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
- package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
- package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
- package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
- package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
- package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
- package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
- package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
- package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
- package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
- package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
- package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
- package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
- package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
- package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
- package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
- package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
- package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
- package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
- package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
- package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
- package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
- package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
- package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
- package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
- package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
- package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
- package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
- package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
- package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
- package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
- package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
- package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
- package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
- package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
- package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
- package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
- package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
- package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
- package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
- package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
- package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
- package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
- package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
- package/dist/extensions/pi-web-agent/package.json +2 -2
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +2 -2
- package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
- package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +1 -1
- package/dist/extensions/pi-web-agent/src/extension.ts +1 -1
- package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
- package/dist/extensions/question/helpers.ts +3 -3
- package/dist/extensions/question/index.ts +2 -2
- package/dist/extensions/question/navigation.ts +2 -2
- package/dist/extensions/question/package.json +2 -2
- package/dist/extensions/question/question-list.ts +2 -2
- package/dist/extensions/question/tests/question-list.test.ts +2 -2
- package/dist/extensions/question/tui-adapter.ts +2 -2
- package/dist/extensions/question/types.ts +2 -2
- package/dist/extensions/question/ui-protocol.ts +2 -2
- package/dist/extensions/rtk.ts +2 -2
- package/dist/extensions/tokenin-onboarding.ts +2 -2
- package/dist/extensions/undo.ts +1 -1
- package/dist/extensions/web-agent-onboarding.ts +3 -3
- package/dist/extensions/workflow/adapter.ts +16 -8
- package/dist/extensions/workflow/extension.ts +1 -1
- package/dist/extensions/workflow/package.json +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +19 -5
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/skills/caveman/SKILL.md +49 -0
- package/dist/skills/handoff-text/SKILL.md +13 -0
- package/dist/utils/thinking-tags.d.ts +7 -0
- package/dist/utils/thinking-tags.d.ts.map +1 -0
- package/dist/utils/thinking-tags.js +63 -0
- package/dist/utils/thinking-tags.js.map +1 -0
- package/docs/docs.json +4 -0
- package/docs/extensions.md +2 -0
- package/docs/settings.md +15 -0
- package/docs/shared-host-extensions.md +109 -0
- package/package.json +2 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
|
|
4
|
+
interface SlashLiveStateModule {
|
|
5
|
+
applySlashUpdate?: typeof import("../../src/slash/slash-live-state.ts").applySlashUpdate;
|
|
6
|
+
buildSlashInitialResult?: typeof import("../../src/slash/slash-live-state.ts").buildSlashInitialResult;
|
|
7
|
+
clearSlashSnapshots?: typeof import("../../src/slash/slash-live-state.ts").clearSlashSnapshots;
|
|
8
|
+
finalizeSlashResult?: typeof import("../../src/slash/slash-live-state.ts").finalizeSlashResult;
|
|
9
|
+
getSlashRenderableSnapshot?: typeof import("../../src/slash/slash-live-state.ts").getSlashRenderableSnapshot;
|
|
10
|
+
restoreSlashFinalSnapshots?: typeof import("../../src/slash/slash-live-state.ts").restoreSlashFinalSnapshots;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let applySlashUpdate: SlashLiveStateModule["applySlashUpdate"];
|
|
14
|
+
let buildSlashInitialResult: SlashLiveStateModule["buildSlashInitialResult"];
|
|
15
|
+
let clearSlashSnapshots: SlashLiveStateModule["clearSlashSnapshots"];
|
|
16
|
+
let finalizeSlashResult: SlashLiveStateModule["finalizeSlashResult"];
|
|
17
|
+
let getSlashRenderableSnapshot: SlashLiveStateModule["getSlashRenderableSnapshot"];
|
|
18
|
+
let restoreSlashFinalSnapshots: SlashLiveStateModule["restoreSlashFinalSnapshots"];
|
|
19
|
+
let available = true;
|
|
20
|
+
try {
|
|
21
|
+
({
|
|
22
|
+
applySlashUpdate,
|
|
23
|
+
buildSlashInitialResult,
|
|
24
|
+
clearSlashSnapshots,
|
|
25
|
+
finalizeSlashResult,
|
|
26
|
+
getSlashRenderableSnapshot,
|
|
27
|
+
restoreSlashFinalSnapshots,
|
|
28
|
+
} = await import("../../src/slash/slash-live-state.ts") as SlashLiveStateModule);
|
|
29
|
+
} catch {
|
|
30
|
+
available = false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("slash live state", { skip: !available ? "slash-live-state.ts not importable" : undefined }, () => {
|
|
34
|
+
it("streams progress updates into the visible slash snapshot", () => {
|
|
35
|
+
clearSlashSnapshots!();
|
|
36
|
+
const details = buildSlashInitialResult!("req-1", {
|
|
37
|
+
agent: "scout",
|
|
38
|
+
task: "scan codebase",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
applySlashUpdate!("req-1", {
|
|
42
|
+
requestId: "req-1",
|
|
43
|
+
currentTool: "find",
|
|
44
|
+
toolCount: 2,
|
|
45
|
+
progress: [{
|
|
46
|
+
agent: "scout",
|
|
47
|
+
status: "running",
|
|
48
|
+
task: "scan codebase",
|
|
49
|
+
currentTool: "find",
|
|
50
|
+
currentToolArgs: '{"pattern":"**/*.ts"}',
|
|
51
|
+
recentTools: [{ tool: "ls", args: '{"path":"."}', endMs: 10 }],
|
|
52
|
+
recentOutput: ["src/index.ts", "src/render.ts"],
|
|
53
|
+
toolCount: 2,
|
|
54
|
+
tokens: 120,
|
|
55
|
+
durationMs: 400,
|
|
56
|
+
}],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const snapshot = getSlashRenderableSnapshot!(details);
|
|
60
|
+
const progress = snapshot.result.details.results[0]?.progress;
|
|
61
|
+
assert.equal(progress?.currentTool, "find");
|
|
62
|
+
assert.deepEqual(progress?.recentOutput, ["src/index.ts", "src/render.ts"]);
|
|
63
|
+
assert.equal(snapshot.version > 0, true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("creates stable placeholders for a 40-step worker/reviewer chain", () => {
|
|
67
|
+
clearSlashSnapshots!();
|
|
68
|
+
const chain = Array.from({ length: 40 }, (_, index) => ({
|
|
69
|
+
agent: index % 2 === 0 ? "worker" : "reviewer",
|
|
70
|
+
...(index === 0 ? { task: "Start long chain" } : {}),
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const details = buildSlashInitialResult!("req-long-chain", { chain });
|
|
74
|
+
|
|
75
|
+
assert.equal(details.result.details.mode, "chain");
|
|
76
|
+
assert.equal(details.result.details.results.length, 40);
|
|
77
|
+
assert.equal(details.result.details.progress?.length, 40);
|
|
78
|
+
assert.equal(details.result.details.chainAgents?.length, 40);
|
|
79
|
+
assert.equal(details.result.details.totalSteps, 40);
|
|
80
|
+
assert.equal(details.result.details.currentStepIndex, 0);
|
|
81
|
+
assert.equal(details.result.details.results[0]?.progress?.status, "running");
|
|
82
|
+
assert.equal(details.result.details.results[39]?.agent, "reviewer");
|
|
83
|
+
assert.equal(details.result.details.results[39]?.progress?.index, 39);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("prefers finalized snapshots and restores them from persisted custom messages", () => {
|
|
87
|
+
clearSlashSnapshots!();
|
|
88
|
+
const details = buildSlashInitialResult!("req-2", {
|
|
89
|
+
agent: "scout",
|
|
90
|
+
task: "scan codebase",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const finalDetails = finalizeSlashResult!({
|
|
94
|
+
requestId: "req-2",
|
|
95
|
+
result: {
|
|
96
|
+
content: [{ type: "text", text: "Done." }],
|
|
97
|
+
details: {
|
|
98
|
+
mode: "single",
|
|
99
|
+
results: [{
|
|
100
|
+
agent: "scout",
|
|
101
|
+
task: "scan codebase",
|
|
102
|
+
exitCode: 0,
|
|
103
|
+
messages: [],
|
|
104
|
+
usage: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 1 },
|
|
105
|
+
}],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
isError: false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const liveFinal = getSlashRenderableSnapshot!(details);
|
|
112
|
+
assert.equal((liveFinal.result.content[0] as { text: string }).text, "Done.");
|
|
113
|
+
|
|
114
|
+
clearSlashSnapshots!();
|
|
115
|
+
restoreSlashFinalSnapshots!([
|
|
116
|
+
{
|
|
117
|
+
type: "custom_message",
|
|
118
|
+
customType: "subagent-slash-result",
|
|
119
|
+
display: true,
|
|
120
|
+
details: finalDetails,
|
|
121
|
+
},
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const restored = getSlashRenderableSnapshot!(details);
|
|
125
|
+
assert.equal((restored.result.content[0] as { text: string }).text, "Done.");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for chain template resolution and variable substitution.
|
|
3
|
+
*
|
|
4
|
+
* These test the pure logic of how {task}, {previous}, and {chain_dir}
|
|
5
|
+
* variables get resolved in chain steps. Uses dynamic import since
|
|
6
|
+
* settings.ts transitively depends on pi packages.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it } from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
import { createTempDir, removeTempDir, tryImport } from "../support/helpers.ts";
|
|
14
|
+
|
|
15
|
+
// Top-level await
|
|
16
|
+
const settings = await tryImport<any>("./src/shared/settings.ts");
|
|
17
|
+
const skills = await tryImport<any>("./src/agents/skills.ts");
|
|
18
|
+
const available = !!(settings && skills);
|
|
19
|
+
|
|
20
|
+
const resolveChainTemplates = settings?.resolveChainTemplates;
|
|
21
|
+
const buildChainInstructions = settings?.buildChainInstructions;
|
|
22
|
+
const resolveStepBehavior = settings?.resolveStepBehavior;
|
|
23
|
+
const resolveParallelBehaviors = settings?.resolveParallelBehaviors;
|
|
24
|
+
const suppressProgressForReadOnlyTask = settings?.suppressProgressForReadOnlyTask;
|
|
25
|
+
const taskDisallowsFileUpdates = settings?.taskDisallowsFileUpdates;
|
|
26
|
+
const isParallelStep = settings?.isParallelStep;
|
|
27
|
+
const createChainDir = settings?.createChainDir;
|
|
28
|
+
const normalizeSkillInput = skills?.normalizeSkillInput;
|
|
29
|
+
|
|
30
|
+
describe("resolveChainTemplates", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
31
|
+
it("uses step task for first step", () => {
|
|
32
|
+
const chain = [
|
|
33
|
+
{ agent: "a", task: "Analyze {task}" },
|
|
34
|
+
{ agent: "b" },
|
|
35
|
+
];
|
|
36
|
+
const templates = resolveChainTemplates(chain);
|
|
37
|
+
assert.equal(templates[0], "Analyze {task}");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("defaults to {previous} for subsequent steps without task", () => {
|
|
41
|
+
const chain = [
|
|
42
|
+
{ agent: "a", task: "Start" },
|
|
43
|
+
{ agent: "b" },
|
|
44
|
+
{ agent: "c" },
|
|
45
|
+
];
|
|
46
|
+
const templates = resolveChainTemplates(chain);
|
|
47
|
+
assert.equal(templates[1], "{previous}");
|
|
48
|
+
assert.equal(templates[2], "{previous}");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("preserves explicit task on later steps", () => {
|
|
52
|
+
const chain = [
|
|
53
|
+
{ agent: "a", task: "Start" },
|
|
54
|
+
{ agent: "b", task: "Custom task for B" },
|
|
55
|
+
];
|
|
56
|
+
const templates = resolveChainTemplates(chain);
|
|
57
|
+
assert.equal(templates[1], "Custom task for B");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("handles parallel steps", () => {
|
|
61
|
+
const chain = [
|
|
62
|
+
{
|
|
63
|
+
parallel: [
|
|
64
|
+
{ agent: "a", task: "Review auth" },
|
|
65
|
+
{ agent: "b", task: "Review data" },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
const templates = resolveChainTemplates(chain);
|
|
70
|
+
assert.ok(Array.isArray(templates[0]), "parallel step templates should be an array");
|
|
71
|
+
const parallelTemplates = templates[0] as string[];
|
|
72
|
+
assert.equal(parallelTemplates[0], "Review auth");
|
|
73
|
+
assert.equal(parallelTemplates[1], "Review data");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("mixed sequential + parallel", () => {
|
|
77
|
+
const chain = [
|
|
78
|
+
{ agent: "scout", task: "Scan" },
|
|
79
|
+
{
|
|
80
|
+
parallel: [
|
|
81
|
+
{ agent: "rev-a", task: "Deep review A" },
|
|
82
|
+
{ agent: "rev-b" },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{ agent: "writer" },
|
|
86
|
+
];
|
|
87
|
+
const templates = resolveChainTemplates(chain);
|
|
88
|
+
assert.equal(templates[0], "Scan");
|
|
89
|
+
assert.ok(Array.isArray(templates[1]));
|
|
90
|
+
assert.equal(templates[2], "{previous}");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("isParallelStep", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
95
|
+
it("returns true for parallel steps", () => {
|
|
96
|
+
assert.ok(isParallelStep({ parallel: [{ agent: "a", task: "t" }] }));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("returns false for sequential steps", () => {
|
|
100
|
+
assert.ok(!isParallelStep({ agent: "a", task: "t" }));
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("normalizeSkillInput", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
105
|
+
it("returns undefined for undefined input", () => {
|
|
106
|
+
assert.equal(normalizeSkillInput(undefined), undefined);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("returns undefined for true (use default)", () => {
|
|
110
|
+
assert.equal(normalizeSkillInput(true), undefined);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("returns false for false (disable)", () => {
|
|
114
|
+
assert.equal(normalizeSkillInput(false), false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("splits comma-separated string", () => {
|
|
118
|
+
assert.deepEqual(normalizeSkillInput("web-search,pdf"), ["web-search", "pdf"]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("passes through array", () => {
|
|
122
|
+
assert.deepEqual(normalizeSkillInput(["a", "b"]), ["a", "b"]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("deduplicates", () => {
|
|
126
|
+
assert.deepEqual(normalizeSkillInput(["a", "b", "a"]), ["a", "b"]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("trims whitespace", () => {
|
|
130
|
+
assert.deepEqual(normalizeSkillInput(" a , b "), ["a", "b"]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("filters empty strings", () => {
|
|
134
|
+
assert.deepEqual(normalizeSkillInput(",a,,b,"), ["a", "b"]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("resolveStepBehavior", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
139
|
+
it("returns agent defaults when no overrides", () => {
|
|
140
|
+
// Uses agentConfig.output, .defaultReads, .defaultProgress
|
|
141
|
+
const config = { name: "test", output: "report.md", defaultProgress: true, defaultReads: ["input.md"] };
|
|
142
|
+
const behavior = resolveStepBehavior(config, {});
|
|
143
|
+
assert.equal(behavior.output, "report.md");
|
|
144
|
+
assert.equal(behavior.progress, true);
|
|
145
|
+
assert.deepEqual(behavior.reads, ["input.md"]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("step overrides take precedence", () => {
|
|
149
|
+
const config = { name: "test", output: "report.md" };
|
|
150
|
+
const behavior = resolveStepBehavior(config, { output: "custom.md" });
|
|
151
|
+
assert.equal(behavior.output, "custom.md");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("defaults outputMode to inline unless a step overrides it", () => {
|
|
155
|
+
const inlineBehavior = resolveStepBehavior({ name: "test", output: "report.md" }, {});
|
|
156
|
+
assert.equal(inlineBehavior.outputMode, "inline");
|
|
157
|
+
|
|
158
|
+
const stepOverrideBehavior = resolveStepBehavior({ name: "test", output: "report.md" }, { outputMode: "file-only" });
|
|
159
|
+
assert.equal(stepOverrideBehavior.outputMode, "file-only");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("false disables output", () => {
|
|
163
|
+
const config = { name: "test", output: "report.md" };
|
|
164
|
+
const behavior = resolveStepBehavior(config, { output: false });
|
|
165
|
+
assert.equal(behavior.output, false);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("string false disables output defensively", () => {
|
|
169
|
+
const config = { name: "test", output: "report.md" };
|
|
170
|
+
const behavior = resolveStepBehavior(config, { output: "false" });
|
|
171
|
+
assert.equal(behavior.output, false);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("defaults to false when agent has no config", () => {
|
|
175
|
+
const config = { name: "test" };
|
|
176
|
+
const behavior = resolveStepBehavior(config, {});
|
|
177
|
+
assert.equal(behavior.output, false);
|
|
178
|
+
assert.equal(behavior.reads, false);
|
|
179
|
+
assert.equal(behavior.progress, false);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("resolveParallelBehaviors", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
184
|
+
it("string false agent default disables output in chain parallel tasks", () => {
|
|
185
|
+
const behaviors = resolveParallelBehaviors(
|
|
186
|
+
[{ agent: "reviewer", task: "Review" }],
|
|
187
|
+
[{ name: "reviewer", output: "false" }],
|
|
188
|
+
0,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
assert.equal(behaviors[0]?.output, false);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("read-only progress suppression", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
196
|
+
it("suppresses progress for review-only or no-edit tasks", () => {
|
|
197
|
+
const behavior = { reads: undefined, output: false, outputMode: "inline", progress: true, skills: undefined };
|
|
198
|
+
|
|
199
|
+
assert.equal(taskDisallowsFileUpdates("Review-only. Do not edit files."), true);
|
|
200
|
+
assert.equal(taskDisallowsFileUpdates("Implement read-only mode for config files."), false);
|
|
201
|
+
assert.equal(taskDisallowsFileUpdates("This task is not read-only; edit files."), false);
|
|
202
|
+
assert.equal(suppressProgressForReadOnlyTask(behavior, "Review-only. Do not edit files.").progress, false);
|
|
203
|
+
assert.equal(suppressProgressForReadOnlyTask(behavior, "{task}", "Review-only. Do not edit files.").progress, false);
|
|
204
|
+
assert.equal(suppressProgressForReadOnlyTask(behavior, "Implement the approved fix.").progress, true);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("buildChainInstructions", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
209
|
+
it("adds [Read from:] prefix for reads", () => {
|
|
210
|
+
const behavior = { reads: ["context.md"], output: false, outputMode: "inline", progress: false, skills: undefined };
|
|
211
|
+
const dir = createTempDir("chain-test-");
|
|
212
|
+
try {
|
|
213
|
+
const { prefix } = buildChainInstructions(behavior, dir, false);
|
|
214
|
+
assert.ok(prefix.includes("[Read from:"), `should have Read instruction: ${prefix}`);
|
|
215
|
+
assert.ok(prefix.includes("context.md"), "should reference the file");
|
|
216
|
+
} finally {
|
|
217
|
+
removeTempDir(dir);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("adds [Write to:] prefix for output", () => {
|
|
222
|
+
const behavior = { reads: undefined, output: "output.md", outputMode: "inline", progress: false, skills: undefined };
|
|
223
|
+
const dir = createTempDir("chain-test-");
|
|
224
|
+
try {
|
|
225
|
+
const { prefix } = buildChainInstructions(behavior, dir, false);
|
|
226
|
+
assert.ok(prefix.includes("[Write to:"), `should have Write instruction: ${prefix}`);
|
|
227
|
+
assert.ok(prefix.includes("output.md"), "should reference the file");
|
|
228
|
+
} finally {
|
|
229
|
+
removeTempDir(dir);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("adds progress instructions in suffix for first progress step", () => {
|
|
234
|
+
const behavior = { reads: undefined, output: false, outputMode: "inline", progress: true, skills: undefined };
|
|
235
|
+
const dir = createTempDir("chain-test-");
|
|
236
|
+
try {
|
|
237
|
+
const { suffix } = buildChainInstructions(behavior, dir, true);
|
|
238
|
+
assert.ok(
|
|
239
|
+
suffix.includes("progress.md"),
|
|
240
|
+
`should reference progress.md: ${suffix}`,
|
|
241
|
+
);
|
|
242
|
+
assert.ok(
|
|
243
|
+
suffix.includes("Create") || suffix.includes("maintain"),
|
|
244
|
+
`should say create/maintain for first progress step: ${suffix}`,
|
|
245
|
+
);
|
|
246
|
+
} finally {
|
|
247
|
+
removeTempDir(dir);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it("includes previous output in suffix when not in template", () => {
|
|
252
|
+
const behavior = { reads: undefined, output: false, outputMode: "inline", progress: false, skills: undefined };
|
|
253
|
+
const dir = createTempDir("chain-test-");
|
|
254
|
+
try {
|
|
255
|
+
const { suffix } = buildChainInstructions(behavior, dir, false, "Previous step output here");
|
|
256
|
+
assert.ok(
|
|
257
|
+
suffix.includes("Previous step output here"),
|
|
258
|
+
"should include previous output",
|
|
259
|
+
);
|
|
260
|
+
} finally {
|
|
261
|
+
removeTempDir(dir);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("returns empty prefix/suffix when no behavior configured", () => {
|
|
266
|
+
const behavior = { reads: undefined, output: false, outputMode: "inline", progress: false, skills: undefined };
|
|
267
|
+
const dir = createTempDir("chain-test-");
|
|
268
|
+
try {
|
|
269
|
+
const { prefix, suffix } = buildChainInstructions(behavior, dir, false);
|
|
270
|
+
assert.equal(prefix, "");
|
|
271
|
+
assert.equal(suffix, "");
|
|
272
|
+
} finally {
|
|
273
|
+
removeTempDir(dir);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe("createChainDir", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
279
|
+
it("creates directory with runId", () => {
|
|
280
|
+
const dir = createChainDir("test-run-123");
|
|
281
|
+
try {
|
|
282
|
+
assert.ok(fs.existsSync(dir));
|
|
283
|
+
assert.ok(dir.includes("test-run-123"));
|
|
284
|
+
} finally {
|
|
285
|
+
removeTempDir(dir);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("uses custom base when provided", () => {
|
|
290
|
+
const base = createTempDir("chain-base-");
|
|
291
|
+
try {
|
|
292
|
+
const dir = createChainDir("run-abc", base);
|
|
293
|
+
assert.ok(fs.existsSync(dir));
|
|
294
|
+
assert.ok(dir.startsWith(base) || dir === base);
|
|
295
|
+
} finally {
|
|
296
|
+
removeTempDir(base);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { tryImport } from "../support/helpers.ts";
|
|
4
|
+
|
|
5
|
+
interface TopLevelAsyncModule {
|
|
6
|
+
applyForceTopLevelAsyncOverride<T extends { async?: boolean; clarify?: boolean }>(
|
|
7
|
+
params: T,
|
|
8
|
+
depth: number,
|
|
9
|
+
forceTopLevelAsync: boolean,
|
|
10
|
+
): T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const mod = await tryImport<TopLevelAsyncModule>("./src/runs/background/top-level-async.ts");
|
|
14
|
+
const available = !!mod;
|
|
15
|
+
|
|
16
|
+
describe("force top-level async helper", { skip: !available ? "pi packages not available" : undefined }, () => {
|
|
17
|
+
it("forces top-level calls async and disables clarify", () => {
|
|
18
|
+
const params = { async: false, clarify: true, agent: "worker" };
|
|
19
|
+
const next = mod!.applyForceTopLevelAsyncOverride(params, 0, true);
|
|
20
|
+
assert.notEqual(next, params);
|
|
21
|
+
assert.equal(next.async, true);
|
|
22
|
+
assert.equal(next.clarify, false);
|
|
23
|
+
assert.equal(next.agent, "worker");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("leaves nested calls unchanged", () => {
|
|
27
|
+
const params = { async: false, clarify: true };
|
|
28
|
+
const next = mod!.applyForceTopLevelAsyncOverride(params, 1, true);
|
|
29
|
+
assert.equal(next, params);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("leaves top-level calls unchanged when the feature is off", () => {
|
|
33
|
+
const params = { async: false, clarify: true };
|
|
34
|
+
const next = mod!.applyForceTopLevelAsyncOverride(params, 0, false);
|
|
35
|
+
assert.equal(next, params);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { createMockPi as _createMockPi } from "./mock-pi.ts";
|
|
6
|
+
import type { MockPi } from "./mock-pi.ts";
|
|
7
|
+
|
|
8
|
+
export type { MockPi };
|
|
9
|
+
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
export function createMockPi(): MockPi {
|
|
13
|
+
return _createMockPi();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createTempDir(prefix = "pi-subagent-test-"): string {
|
|
17
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function removeTempDir(dir: string): void {
|
|
21
|
+
try {
|
|
22
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createEventBus() {
|
|
27
|
+
const listeners = new Map<string, Set<(payload: unknown) => void>>();
|
|
28
|
+
return {
|
|
29
|
+
on(channel: string, handler: (payload: unknown) => void) {
|
|
30
|
+
const channelListeners = listeners.get(channel) ?? new Set();
|
|
31
|
+
channelListeners.add(handler);
|
|
32
|
+
listeners.set(channel, channelListeners);
|
|
33
|
+
return () => {
|
|
34
|
+
channelListeners.delete(handler);
|
|
35
|
+
if (channelListeners.size === 0) listeners.delete(channel);
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
emit(channel: string, payload: unknown) {
|
|
39
|
+
for (const handler of listeners.get(channel) ?? []) handler(payload);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface AgentConfig {
|
|
45
|
+
name: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
systemPrompt?: string;
|
|
48
|
+
model?: string;
|
|
49
|
+
fallbackModels?: string[];
|
|
50
|
+
tools?: string[];
|
|
51
|
+
extensions?: string[];
|
|
52
|
+
subagentOnlyExtensions?: string[];
|
|
53
|
+
skills?: string[];
|
|
54
|
+
thinking?: string;
|
|
55
|
+
systemPromptMode?: string;
|
|
56
|
+
inheritProjectContext?: boolean;
|
|
57
|
+
inheritSkills?: boolean;
|
|
58
|
+
scope?: string;
|
|
59
|
+
output?: string | false;
|
|
60
|
+
reads?: string[] | false;
|
|
61
|
+
progress?: boolean;
|
|
62
|
+
mcpDirectTools?: string[];
|
|
63
|
+
maxSubagentDepth?: number;
|
|
64
|
+
completionGuard?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function makeAgentConfigs(names: string[]): AgentConfig[] {
|
|
68
|
+
return names.map((name) => ({
|
|
69
|
+
name,
|
|
70
|
+
description: `Test agent: ${name}`,
|
|
71
|
+
systemPrompt: "",
|
|
72
|
+
systemPromptMode: "replace",
|
|
73
|
+
inheritProjectContext: false,
|
|
74
|
+
inheritSkills: false,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function makeAgent(name: string, overrides: Partial<AgentConfig> = {}): AgentConfig {
|
|
79
|
+
return {
|
|
80
|
+
name,
|
|
81
|
+
description: `Test agent: ${name}`,
|
|
82
|
+
systemPrompt: "",
|
|
83
|
+
systemPromptMode: "replace",
|
|
84
|
+
inheritProjectContext: false,
|
|
85
|
+
inheritSkills: false,
|
|
86
|
+
...overrides,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface MinimalCtx {
|
|
91
|
+
cwd: string;
|
|
92
|
+
hasUI: boolean;
|
|
93
|
+
ui: Record<string, never>;
|
|
94
|
+
sessionManager: {
|
|
95
|
+
getSessionId: () => string;
|
|
96
|
+
getSessionFile: () => string | null;
|
|
97
|
+
};
|
|
98
|
+
modelRegistry: {
|
|
99
|
+
getAvailable: () => Array<{ provider: string; id: string }>;
|
|
100
|
+
};
|
|
101
|
+
model?: { provider: string; id?: string };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function makeMinimalCtx(cwd: string): MinimalCtx {
|
|
105
|
+
return {
|
|
106
|
+
cwd,
|
|
107
|
+
hasUI: false,
|
|
108
|
+
ui: {},
|
|
109
|
+
sessionManager: {
|
|
110
|
+
getSessionId: () => "session-123",
|
|
111
|
+
getSessionFile: () => null,
|
|
112
|
+
},
|
|
113
|
+
modelRegistry: {
|
|
114
|
+
getAvailable: () => [],
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Try to dynamically import a module.
|
|
121
|
+
* - Bare specifiers are imported as-is.
|
|
122
|
+
* - Relative paths (e.g., "./src/shared/utils.ts") are resolved from the project root.
|
|
123
|
+
*
|
|
124
|
+
* Only swallows MODULE_NOT_FOUND / ERR_MODULE_NOT_FOUND when the missing module
|
|
125
|
+
* is exactly the requested bare specifier (expected optional dependency).
|
|
126
|
+
* All other errors are rethrown to avoid hiding real breakage.
|
|
127
|
+
*/
|
|
128
|
+
export async function tryImport<T>(specifier: string): Promise<T | null> {
|
|
129
|
+
const isBare = !(specifier.startsWith(".") || specifier.startsWith("/"));
|
|
130
|
+
try {
|
|
131
|
+
if (!isBare) {
|
|
132
|
+
const projectRoot = path.resolve(__dirname, "..", "..");
|
|
133
|
+
const abs = path.resolve(projectRoot, specifier);
|
|
134
|
+
const url = pathToFileURL(abs).href;
|
|
135
|
+
return await import(url) as T;
|
|
136
|
+
}
|
|
137
|
+
return await import(specifier) as T;
|
|
138
|
+
} catch (error: unknown) {
|
|
139
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
140
|
+
? (error as { code?: unknown }).code
|
|
141
|
+
: undefined;
|
|
142
|
+
const isModuleNotFound = code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND";
|
|
143
|
+
if (isBare && isModuleNotFound) {
|
|
144
|
+
const msg = typeof error === "object" && error !== null && "message" in error
|
|
145
|
+
? String((error as { message?: unknown }).message ?? "")
|
|
146
|
+
: "";
|
|
147
|
+
const missing = msg.match(/Cannot find (?:package|module) ['\"]([^'\"]+)['\"]/i)?.[1];
|
|
148
|
+
if (missing === specifier || msg.includes(`'${specifier}'`) || msg.includes(`\"${specifier}\"`)) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const events = {
|
|
157
|
+
assistantMessage(text: string, model = "mock/test-model"): object {
|
|
158
|
+
return {
|
|
159
|
+
type: "message_end",
|
|
160
|
+
message: {
|
|
161
|
+
role: "assistant",
|
|
162
|
+
content: [{ type: "text", text }],
|
|
163
|
+
model,
|
|
164
|
+
stopReason: "stop",
|
|
165
|
+
usage: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, cost: { total: 0.001 } },
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
toolStart(toolName: string, args: Record<string, unknown> = {}): object {
|
|
171
|
+
return { type: "tool_execution_start", toolName, args };
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
toolEnd(toolName: string): object {
|
|
175
|
+
return { type: "tool_execution_end", toolName };
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
toolResult(toolName: string, text: string, isError = false): object {
|
|
179
|
+
return {
|
|
180
|
+
type: "tool_result_end",
|
|
181
|
+
message: {
|
|
182
|
+
role: "toolResult",
|
|
183
|
+
toolName,
|
|
184
|
+
isError,
|
|
185
|
+
content: [{ type: "text", text }],
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
};
|