@zhushanwen/subagent-core 0.2.0
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/README.md +114 -0
- package/dist/chunk-3VOERJPJ.js +22 -0
- package/dist/chunk-A4IVZWVX.js +31 -0
- package/dist/execution/engine/engines/zcode/constants.cjs +54 -0
- package/dist/execution/engine/engines/zcode/constants.d.cts +31 -0
- package/dist/execution/engine/engines/zcode/constants.d.ts +31 -0
- package/dist/execution/engine/engines/zcode/constants.js +22 -0
- package/dist/execution/engine/engines/zcode/reader.cjs +276 -0
- package/dist/execution/engine/engines/zcode/reader.d.cts +20 -0
- package/dist/execution/engine/engines/zcode/reader.d.ts +20 -0
- package/dist/execution/engine/engines/zcode/reader.js +239 -0
- package/dist/execution/engine/paths.cjs +55 -0
- package/dist/execution/engine/paths.d.cts +8 -0
- package/dist/execution/engine/paths.d.ts +8 -0
- package/dist/execution/engine/paths.js +26 -0
- package/dist/execution/relay-env.cjs +62 -0
- package/dist/execution/relay-env.d.cts +33 -0
- package/dist/execution/relay-env.d.ts +33 -0
- package/dist/execution/relay-env.js +20 -0
- package/dist/index.cjs +1852 -0
- package/dist/index.d.cts +1184 -0
- package/dist/index.d.ts +1184 -0
- package/dist/index.js +1810 -0
- package/dist/types-BxyAidGf.d.cts +875 -0
- package/dist/types-BxyAidGf.d.ts +875 -0
- package/package.json +99 -0
- package/src/__tests__/agent-opts-resolver-schema-prompt.test.ts +142 -0
- package/src/__tests__/append-system-prompt-assembly.test.ts +242 -0
- package/src/__tests__/builtin-workflows-structure.test.ts +180 -0
- package/src/__tests__/derive-closed-display-parity.test.ts +93 -0
- package/src/__tests__/fr4-get-state-handshake.test.ts +125 -0
- package/src/__tests__/fr8-orphan-recovery.test.ts +259 -0
- package/src/__tests__/m2-append-content-probe.test.ts +73 -0
- package/src/__tests__/manifest-store.test.ts +368 -0
- package/src/__tests__/record-store-cache.test.ts +335 -0
- package/src/__tests__/record-store-index.test.ts +560 -0
- package/src/__tests__/record-store-last-line.test.ts +129 -0
- package/src/__tests__/review-fix-loop-script.test.ts +288 -0
- package/src/__tests__/review-fix-loop-utils.test.ts +2377 -0
- package/src/__tests__/robustness-low-batch1.test.ts +286 -0
- package/src/__tests__/robustness-medium-batch1.test.ts +69 -0
- package/src/__tests__/robustness-medium-batch2.test.ts +65 -0
- package/src/__tests__/robustness-medium-batch3.test.ts +30 -0
- package/src/__tests__/robustness-medium-batch4.test.ts +358 -0
- package/src/__tests__/session-runner.test.ts +69 -0
- package/src/__tests__/smoke.test.ts +9 -0
- package/src/__tests__/stream-sink.test.ts +203 -0
- package/src/core/__tests__/host-services.test.ts +144 -0
- package/src/core/__tests__/logger.test.ts +78 -0
- package/src/core/__tests__/notify-ports.test.ts +104 -0
- package/src/core/host-services.ts +97 -0
- package/src/core/logger.ts +47 -0
- package/src/core/notify-ports.ts +126 -0
- package/src/execution/__tests__/__fixtures__/notifier-golden-snapshots.json +53 -0
- package/src/execution/__tests__/__fixtures__/truncline.snapshot.json +1 -0
- package/src/execution/__tests__/agent-result-mapper.test.ts +150 -0
- package/src/execution/__tests__/alive-store.test.ts +147 -0
- package/src/execution/__tests__/ask-user-transit-e2e.test.ts +490 -0
- package/src/execution/__tests__/channel-registry-handshake.test.ts +242 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +610 -0
- package/src/execution/__tests__/chatmode-first-round-closure-spawn.test.ts +190 -0
- package/src/execution/__tests__/concurrency-pool.test.ts +250 -0
- package/src/execution/__tests__/config.test.ts +302 -0
- package/src/execution/__tests__/delivery-methods.test.ts +424 -0
- package/src/execution/__tests__/dialog-queue.test.ts +299 -0
- package/src/execution/__tests__/epipe-fallback.test.ts +241 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +243 -0
- package/src/execution/__tests__/execute-nesting.test.ts +323 -0
- package/src/execution/__tests__/execute-options-mapper.test.ts +200 -0
- package/src/execution/__tests__/execution-record.test.ts +1394 -0
- package/src/execution/__tests__/explicit-agent-ref-guard.test.ts +171 -0
- package/src/execution/__tests__/finalize-record.test.ts +367 -0
- package/src/execution/__tests__/finalized-marker.test.ts +104 -0
- package/src/execution/__tests__/format-schema-instruction.test.ts +169 -0
- package/src/execution/__tests__/gc-timer.test.ts +184 -0
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +254 -0
- package/src/execution/__tests__/helpers/mock-extension-api.ts +30 -0
- package/src/execution/__tests__/helpers/spawn-mock.ts +242 -0
- package/src/execution/__tests__/host-mode.test.ts +87 -0
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +211 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +383 -0
- package/src/execution/__tests__/lifecycle-predicates.test.ts +116 -0
- package/src/execution/__tests__/manifest-parentid.test.ts +117 -0
- package/src/execution/__tests__/model-resolver.test.ts +465 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +287 -0
- package/src/execution/__tests__/nested-visibility.test.ts +325 -0
- package/src/execution/__tests__/output-collector.test.ts +358 -0
- package/src/execution/__tests__/path-encoding.test.ts +104 -0
- package/src/execution/__tests__/pi-invocation.test.ts +134 -0
- package/src/execution/__tests__/record-store.test.ts +1057 -0
- package/src/execution/__tests__/records-cwd-isolation.test.ts +91 -0
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +339 -0
- package/src/execution/__tests__/recursive-visibility-env.test.ts +273 -0
- package/src/execution/__tests__/relay-env.test.ts +42 -0
- package/src/execution/__tests__/resource-policy.test.ts +109 -0
- package/src/execution/__tests__/rpc-mode.test.ts +89 -0
- package/src/execution/__tests__/run-and-finalize-chatmode.test.ts +267 -0
- package/src/execution/__tests__/run-spawn-chatmode-settled.test.ts +253 -0
- package/src/execution/__tests__/run-spawn-edges.test.ts +687 -0
- package/src/execution/__tests__/run-spawn-integration.test.ts +933 -0
- package/src/execution/__tests__/run-spawn-resume.test.ts +322 -0
- package/src/execution/__tests__/run-spawn-rpc-mode.test.ts +196 -0
- package/src/execution/__tests__/run-spawn-stdout-callback-throw.test.ts +199 -0
- package/src/execution/__tests__/session-context-resolver.test.ts +167 -0
- package/src/execution/__tests__/session-file-gc.test.ts +293 -0
- package/src/execution/__tests__/session-reconstructor.test.ts +379 -0
- package/src/execution/__tests__/session-runner-epipe.test.ts +178 -0
- package/src/execution/__tests__/session-runner-schema-env.test.ts +350 -0
- package/src/execution/__tests__/spawn-args.test.ts +445 -0
- package/src/execution/__tests__/spawn-event-adapter-rpc.test.ts +189 -0
- package/src/execution/__tests__/spawn-event-adapter.test.ts +167 -0
- package/src/execution/__tests__/spawn-worktree-guidance.test.ts +207 -0
- package/src/execution/__tests__/spawned-children.test.ts +92 -0
- package/src/execution/__tests__/start-sync-model-guard.test.ts +150 -0
- package/src/execution/__tests__/stdin-writer.test.ts +462 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +261 -0
- package/src/execution/__tests__/subagent-service-abort.test.ts +60 -0
- package/src/execution/__tests__/subagent-service-message-close.test.ts +629 -0
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +180 -0
- package/src/execution/__tests__/subagent-service.test.ts +788 -0
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +310 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +612 -0
- package/src/execution/__tests__/temp-prompt.test.ts +53 -0
- package/src/execution/__tests__/timeout-integration.test.ts +615 -0
- package/src/execution/__tests__/tombstone-store.test.ts +73 -0
- package/src/execution/__tests__/turn-limiter-semantics.test.ts +194 -0
- package/src/execution/__tests__/turn-limiter.test.ts +65 -0
- package/src/execution/__tests__/ui-channels.test.ts +187 -0
- package/src/execution/__tests__/ui-interaction-model.test.ts +67 -0
- package/src/execution/__tests__/ui-request-handler-factory.test.ts +369 -0
- package/src/execution/__tests__/ui-request-handler.test.ts +204 -0
- package/src/execution/__tests__/ui-request-observability.test.ts +113 -0
- package/src/execution/__tests__/ui-request-queue.test.ts +133 -0
- package/src/execution/__tests__/worktree-manager.test.ts +633 -0
- package/src/execution/__tests__/worktree-pid-registration.integration.test.ts +229 -0
- package/src/execution/__tests__/worktree-reconcile.integration.test.ts +181 -0
- package/src/execution/__tests__/worktree-registry.test.ts +199 -0
- package/src/execution/agent-registry.ts +199 -0
- package/src/execution/agent-result-mapper.ts +90 -0
- package/src/execution/alive-store.ts +119 -0
- package/src/execution/argv-mirror.ts +113 -0
- package/src/execution/best-effort.ts +38 -0
- package/src/execution/channel-registry-access.ts +144 -0
- package/src/execution/concurrency-pool.ts +116 -0
- package/src/execution/config.ts +165 -0
- package/src/execution/dialog-queue.ts +330 -0
- package/src/execution/engine/__tests__/common/data-dir.test.ts +78 -0
- package/src/execution/engine/__tests__/common/errors.test.ts +132 -0
- package/src/execution/engine/__tests__/common/event-journal.test.ts +177 -0
- package/src/execution/engine/__tests__/common/kill-chain.test.ts +192 -0
- package/src/execution/engine/__tests__/common/nesting-guard.test.ts +81 -0
- package/src/execution/engine/__tests__/common/persona-router.test.ts +123 -0
- package/src/execution/engine/__tests__/common/pool-manager.test.ts +154 -0
- package/src/execution/engine/__tests__/common/schema-emulation.test.ts +128 -0
- package/src/execution/engine/__tests__/conformance/__fixtures__/pi-golden-events.json +28 -0
- package/src/execution/engine/__tests__/conformance/agent-event-invariants.ts +141 -0
- package/src/execution/engine/__tests__/conformance/contract.abort.test.ts +109 -0
- package/src/execution/engine/__tests__/conformance/contract.agent-events.test.ts +101 -0
- package/src/execution/engine/__tests__/conformance/contract.probe.test.ts +77 -0
- package/src/execution/engine/__tests__/conformance/contract.read-degradation.test.ts +104 -0
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +201 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.pi.test.ts +76 -0
- package/src/execution/engine/__tests__/conformance/golden-replay.zcode.test.ts +79 -0
- package/src/execution/engine/__tests__/engine-discovery.test.ts +87 -0
- package/src/execution/engine/__tests__/model-prompt.test.ts +197 -0
- package/src/execution/engine/__tests__/paths.test.ts +39 -0
- package/src/execution/engine/__tests__/registry.test.ts +120 -0
- package/src/execution/engine/__tests__/routing.test.ts +231 -0
- package/src/execution/engine/common/data-dir.ts +66 -0
- package/src/execution/engine/common/errors.ts +183 -0
- package/src/execution/engine/common/event-journal.ts +254 -0
- package/src/execution/engine/common/journal-replay.ts +62 -0
- package/src/execution/engine/common/kill-chain.ts +221 -0
- package/src/execution/engine/common/nesting-guard.ts +50 -0
- package/src/execution/engine/common/persona-router.ts +108 -0
- package/src/execution/engine/common/pool-manager.ts +226 -0
- package/src/execution/engine/common/schema-emulation.ts +189 -0
- package/src/execution/engine/common/session-view-projection.ts +51 -0
- package/src/execution/engine/engine-discovery.ts +65 -0
- package/src/execution/engine/engines/pi/__tests__/pi-engine.test.ts +469 -0
- package/src/execution/engine/engines/pi/__tests__/reader.test.ts +155 -0
- package/src/execution/engine/engines/pi/__tests__/task-spec-mapper.test.ts +164 -0
- package/src/execution/engine/engines/pi/pi-engine.ts +415 -0
- package/src/execution/engine/engines/pi/reader.ts +48 -0
- package/src/execution/engine/engines/pi/registration.ts +35 -0
- package/src/execution/engine/engines/pi/task-spec-mapper.ts +100 -0
- package/src/execution/engine/engines/zcode/__tests__/__fixtures__/zcode-golden-spawn.json +39 -0
- package/src/execution/engine/engines/zcode/__tests__/launcher.test.ts +150 -0
- package/src/execution/engine/engines/zcode/__tests__/parser.test.ts +246 -0
- package/src/execution/engine/engines/zcode/__tests__/preparer.test.ts +228 -0
- package/src/execution/engine/engines/zcode/__tests__/reader.test.ts +210 -0
- package/src/execution/engine/engines/zcode/__tests__/registration.test.ts +71 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.live.test.ts +127 -0
- package/src/execution/engine/engines/zcode/__tests__/zcode-engine.test.ts +580 -0
- package/src/execution/engine/engines/zcode/constants.ts +43 -0
- package/src/execution/engine/engines/zcode/golden-sample.ts +39 -0
- package/src/execution/engine/engines/zcode/launcher.ts +161 -0
- package/src/execution/engine/engines/zcode/parser.ts +436 -0
- package/src/execution/engine/engines/zcode/preparer.ts +363 -0
- package/src/execution/engine/engines/zcode/reader.ts +381 -0
- package/src/execution/engine/engines/zcode/registration.ts +37 -0
- package/src/execution/engine/engines/zcode/zcode-engine.ts +658 -0
- package/src/execution/engine/host-task-spec.ts +47 -0
- package/src/execution/engine/model-prompt.ts +158 -0
- package/src/execution/engine/paths.ts +42 -0
- package/src/execution/engine/port.ts +153 -0
- package/src/execution/engine/registry.ts +133 -0
- package/src/execution/engine/routing.ts +218 -0
- package/src/execution/engine/types.ts +309 -0
- package/src/execution/execute-options-mapper.ts +115 -0
- package/src/execution/execution-record.ts +984 -0
- package/src/execution/finalize-record.ts +254 -0
- package/src/execution/finalized-marker.ts +70 -0
- package/src/execution/get-state-handshake.ts +104 -0
- package/src/execution/host-mode.ts +56 -0
- package/src/execution/idle-gc.ts +47 -0
- package/src/execution/lifecycle-manager.ts +513 -0
- package/src/execution/lifecycle-predicates.ts +65 -0
- package/src/execution/manifest-store.ts +256 -0
- package/src/execution/model-config-service.ts +250 -0
- package/src/execution/model-resolver.ts +248 -0
- package/src/execution/notifier.ts +372 -0
- package/src/execution/notify-ledger.ts +580 -0
- package/src/execution/output-collector.ts +228 -0
- package/src/execution/path-encoding.ts +60 -0
- package/src/execution/pi-invocation.ts +120 -0
- package/src/execution/record-entry.ts +132 -0
- package/src/execution/record-store.ts +1265 -0
- package/src/execution/relay-env.ts +37 -0
- package/src/execution/session-context-resolver.ts +64 -0
- package/src/execution/session-file-gc.ts +120 -0
- package/src/execution/session-pending.ts +170 -0
- package/src/execution/session-reconstructor.ts +678 -0
- package/src/execution/session-runner.ts +1789 -0
- package/src/execution/sessions-index.ts +304 -0
- package/src/execution/spawn-event-adapter.ts +363 -0
- package/src/execution/stdin-writer.ts +198 -0
- package/src/execution/stream-sink.ts +126 -0
- package/src/execution/subagent-service.ts +2268 -0
- package/src/execution/subprocess-agent-runner.ts +317 -0
- package/src/execution/temp-prompt.ts +62 -0
- package/src/execution/tombstone-store.ts +72 -0
- package/src/execution/turn-limiter.ts +102 -0
- package/src/execution/types.ts +1023 -0
- package/src/execution/ui-channels.ts +216 -0
- package/src/execution/ui-interaction-model.ts +48 -0
- package/src/execution/ui-request-handler-factory.ts +243 -0
- package/src/execution/ui-request-observability.ts +81 -0
- package/src/execution/ui-request-queue.ts +184 -0
- package/src/execution/worktree-manager.ts +688 -0
- package/src/execution/worktree-registry.ts +279 -0
- package/src/index.ts +87 -0
- package/src/orchestration/__tests__/__fixtures__/worker-template.snapshot.txt +349 -0
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +202 -0
- package/src/orchestration/__tests__/agent-call-stream.test.ts +152 -0
- package/src/orchestration/__tests__/args-validator.test.ts +143 -0
- package/src/orchestration/__tests__/config-loader.test.ts +503 -0
- package/src/orchestration/__tests__/error-recovery-handlers.test.ts +809 -0
- package/src/orchestration/__tests__/error-recovery-postmessage-defense.test.ts +404 -0
- package/src/orchestration/__tests__/error-recovery-serialize-failed-result.test.ts +56 -0
- package/src/orchestration/__tests__/error-recovery-workflow-call.test.ts +166 -0
- package/src/orchestration/__tests__/execute-agent-call.test.ts +451 -0
- package/src/orchestration/__tests__/launcher-nested-workflow.test.ts +600 -0
- package/src/orchestration/__tests__/lifecycle-runid-injection.test.ts +96 -0
- package/src/orchestration/__tests__/lifecycle.test.ts +659 -0
- package/src/orchestration/__tests__/non-cloneable-return-e2e.test.ts +95 -0
- package/src/orchestration/__tests__/review-fix-loop-scriptpath-failfast.test.ts +183 -0
- package/src/orchestration/__tests__/script-lint.test.ts +831 -0
- package/src/orchestration/__tests__/skill-discovery.test.ts +210 -0
- package/src/orchestration/__tests__/test-mocks.ts +197 -0
- package/src/orchestration/__tests__/worker-exit-without-result.test.ts +368 -0
- package/src/orchestration/__tests__/worker-host.test.ts +120 -0
- package/src/orchestration/__tests__/worker-returnmeta-passthrough.test.ts +164 -0
- package/src/orchestration/__tests__/worker-script-builder-runtime.test.ts +563 -0
- package/src/orchestration/__tests__/worker-script-builder.test.ts +309 -0
- package/src/orchestration/__tests__/worker-script-template-snapshot.test.ts +129 -0
- package/src/orchestration/__tests__/workflow-nesting-e2e.test.ts +317 -0
- package/src/orchestration/__tests__/workflow-script-lint-memo.test.ts +110 -0
- package/src/orchestration/agent-opts-resolver.ts +174 -0
- package/src/orchestration/args-validator.ts +127 -0
- package/src/orchestration/config-loader.ts +316 -0
- package/src/orchestration/error-recovery.ts +1018 -0
- package/src/orchestration/execute-agent-call.ts +256 -0
- package/src/orchestration/launcher.ts +455 -0
- package/src/orchestration/lifecycle.ts +399 -0
- package/src/orchestration/models/__tests__/budget.test.ts +307 -0
- package/src/orchestration/models/agent-call.ts +83 -0
- package/src/orchestration/models/budget.ts +118 -0
- package/src/orchestration/models/ports.ts +164 -0
- package/src/orchestration/models/run-runtime.ts +104 -0
- package/src/orchestration/models/run-spec.ts +83 -0
- package/src/orchestration/models/run-state.ts +44 -0
- package/src/orchestration/models/trace.ts +183 -0
- package/src/orchestration/models/types.ts +301 -0
- package/src/orchestration/models/workflow-run.ts +254 -0
- package/src/orchestration/models/workflow-script-registry.ts +35 -0
- package/src/orchestration/models/workflow-script.ts +117 -0
- package/src/orchestration/script-lint.ts +766 -0
- package/src/orchestration/skill-discovery.ts +135 -0
- package/src/orchestration/worker-handle.ts +115 -0
- package/src/orchestration/worker-host.ts +98 -0
- package/src/orchestration/worker-script-builder.ts +421 -0
- package/src/orchestration/workflow-files.ts +85 -0
- package/src/orchestration/workflow-script-registry-impl.ts +133 -0
- package/src/shared/__tests__/agent-ref.test.ts +34 -0
- package/src/shared/__tests__/meta-parser.test.ts +304 -0
- package/src/shared/__tests__/model-ref.test.ts +306 -0
- package/src/shared/__tests__/resource-discovery-manifest-cache.test.ts +288 -0
- package/src/shared/__tests__/resource-discovery.test.ts +749 -0
- package/src/shared/__tests__/resource-meta.test.ts +51 -0
- package/src/shared/__tests__/schema-jsonify.test.ts +81 -0
- package/src/shared/__tests__/timer-delay.test.ts +61 -0
- package/src/shared/agent-event.ts +13 -0
- package/src/shared/agent-ref.ts +57 -0
- package/src/shared/meta-parser.ts +261 -0
- package/src/shared/model-ref.ts +286 -0
- package/src/shared/resource-discovery.ts +835 -0
- package/src/shared/resource-meta.ts +65 -0
- package/src/shared/schema-env.ts +44 -0
- package/src/shared/schema-jsonify.ts +58 -0
- package/src/shared/timer-delay.ts +54 -0
- package/src/shared/xml-injection.ts +35 -0
- package/workflows/README.md +81 -0
- package/workflows/_shared/agent-refs.cjs +40 -0
- package/workflows/chain.js +137 -0
- package/workflows/map-reduce.js +180 -0
- package/workflows/parallel.js +154 -0
- package/workflows/review-fix-loop-utils.cjs +1542 -0
- package/workflows/review-fix-loop.js +1605 -0
- package/workflows/scatter-gather.js +184 -0
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhushanwen/subagent-core",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Cross-host shared subagent execution layer and workflow orchestration core (dual-form npm package: TS source for workspace, dist ESM+CJS for npm)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"main": "dist/index.cjs",
|
|
11
|
+
"module": "dist/index.js",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"default": "./dist/index.cjs"
|
|
22
|
+
},
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./engines/zcode/reader": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/execution/engine/engines/zcode/reader.d.ts",
|
|
28
|
+
"default": "./dist/execution/engine/engines/zcode/reader.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./dist/execution/engine/engines/zcode/reader.d.cts",
|
|
32
|
+
"default": "./dist/execution/engine/engines/zcode/reader.cjs"
|
|
33
|
+
},
|
|
34
|
+
"default": "./dist/execution/engine/engines/zcode/reader.js"
|
|
35
|
+
},
|
|
36
|
+
"./engines/zcode/constants": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/execution/engine/engines/zcode/constants.d.ts",
|
|
39
|
+
"default": "./dist/execution/engine/engines/zcode/constants.js"
|
|
40
|
+
},
|
|
41
|
+
"require": {
|
|
42
|
+
"types": "./dist/execution/engine/engines/zcode/constants.d.cts",
|
|
43
|
+
"default": "./dist/execution/engine/engines/zcode/constants.cjs"
|
|
44
|
+
},
|
|
45
|
+
"default": "./dist/execution/engine/engines/zcode/constants.js"
|
|
46
|
+
},
|
|
47
|
+
"./engine/paths": {
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/execution/engine/paths.d.ts",
|
|
50
|
+
"default": "./dist/execution/engine/paths.js"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/execution/engine/paths.d.cts",
|
|
54
|
+
"default": "./dist/execution/engine/paths.cjs"
|
|
55
|
+
},
|
|
56
|
+
"default": "./dist/execution/engine/paths.js"
|
|
57
|
+
},
|
|
58
|
+
"./relay-env": {
|
|
59
|
+
"import": {
|
|
60
|
+
"types": "./dist/execution/relay-env.d.ts",
|
|
61
|
+
"default": "./dist/execution/relay-env.js"
|
|
62
|
+
},
|
|
63
|
+
"require": {
|
|
64
|
+
"types": "./dist/execution/relay-env.d.cts",
|
|
65
|
+
"default": "./dist/execution/relay-env.cjs"
|
|
66
|
+
},
|
|
67
|
+
"default": "./dist/execution/relay-env.js"
|
|
68
|
+
},
|
|
69
|
+
"./workflows/*": {
|
|
70
|
+
"require": "./workflows/*",
|
|
71
|
+
"import": "./workflows/*",
|
|
72
|
+
"default": "./workflows/*"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"$comment": "D5 公共 API 面定稿(docs/design/subagent-core-package-extraction.md §3.3 D5):公共面 = 主入口 barrel + 4 条语义子入口(./engines/zcode/reader、./engines/zcode/constants、./engine/paths、./relay-env)+ ./workflows/* 资产子入口。exports 面 = semver 契约,收窄不放宽。双形态(D4):workspace TS 消费走 import 条件 -> src(与 extension-protocol 先例同型);npm 消费走 publishConfig 发布面(npm publish 时整体替换 exports):import -> dist ESM、require -> dist CJS。require 条件指向 dist CJS 是本仓首例,D4 依据:zsw 宿主为纯 CJS(node>=20),而 core 依赖的 @xyz-agent/extension-protocol npm dist 仅 ESM(.mjs),node 20 下 require() 加载 ESM 不可靠——因此对 extension-protocol 设 noExternal 防御性 bundle 边界(当前 entry 闭包无其运行时引用、dist 实测零常量命中;未来 entry 一旦引入运行时引用即 bundle),绝不让外部 ESM 依赖出现在 require 链上;require 分支的 types 用 .d.cts 对应。开发态 require 条件即指向 dist:dist 是 require 语义的真实载体,未 build 时 require 失败属预期(build 前置)。语义子入口精确条目优先于 `./*` 通配(Node 规范:exact match beats pattern),`./*` -> src 通配仅为仓内壳侧深路径消费保留(壳侧 `<pkg>/<域>/<路径>.ts` 形态 import,量级百处、以 grep 实测为准,不在此固化易过期数字),发布面刻意不带该通配——npm 消费面收窄到上列受控入口。workflows/ 资产 .cjs/.js 不编译(D1:src=dist 同字节直发),require/import 条件同径。tsup 多入口保形输出见 tsup.config.ts(dist/<entry> 与 src 同构,d.ts/d.cts 全覆盖)。",
|
|
76
|
+
"files": [
|
|
77
|
+
"src/",
|
|
78
|
+
"workflows/",
|
|
79
|
+
"dist/"
|
|
80
|
+
],
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"ajv": "^8.20.0",
|
|
83
|
+
"proper-lockfile": "^4.1.2",
|
|
84
|
+
"yaml": "^2.9.0",
|
|
85
|
+
"@xyz-agent/extension-protocol": "0.7.0"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@types/node": "^24.0.0",
|
|
89
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
90
|
+
"tsup": "^8.5.1",
|
|
91
|
+
"typescript": "^5.7.0",
|
|
92
|
+
"vitest": "^4.1.8"
|
|
93
|
+
},
|
|
94
|
+
"scripts": {
|
|
95
|
+
"build": "tsup",
|
|
96
|
+
"test": "vitest run",
|
|
97
|
+
"typecheck": "tsc --noEmit"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// agent-opts-resolver schema prompt wording lock (suggestion #4).
|
|
2
|
+
//
|
|
3
|
+
// M2 修正后 resolveAgentOpts 把 schema structured-output 指令内容直传进
|
|
4
|
+
// appendSystemPrompt(不再写临时文件、不再 push 路径)。本测试锁死 SO 指令措辞 +
|
|
5
|
+
// 验证 agent 正文不经此通道(防双重注入回归)。
|
|
6
|
+
//
|
|
7
|
+
// 覆盖 design.json TC1(措辞)/ TC2(无 schema)/ TC3(不处理 agent ref)。
|
|
8
|
+
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
|
|
11
|
+
import type { AgentCallOpts } from "../orchestration/models/types.ts";
|
|
12
|
+
import { resolveAgentOpts } from "../orchestration/agent-opts-resolver.ts";
|
|
13
|
+
|
|
14
|
+
describe("resolveAgentOpts schema prompt wording + M2 content passthrough", () => {
|
|
15
|
+
it("schema 提供时 appendSystemPrompt 含 SO 指令内容(TC1 措辞锁)", () => {
|
|
16
|
+
const opts: AgentCallOpts = {
|
|
17
|
+
prompt: "do the thing",
|
|
18
|
+
schema: { type: "object" },
|
|
19
|
+
agent: "/abs/path/worker.md",
|
|
20
|
+
};
|
|
21
|
+
const result = resolveAgentOpts(opts);
|
|
22
|
+
|
|
23
|
+
expect(result.error).toBeUndefined();
|
|
24
|
+
expect(result.opts.appendSystemPrompt).toBeDefined();
|
|
25
|
+
expect(result.opts.appendSystemPrompt!.length).toBe(1);
|
|
26
|
+
|
|
27
|
+
const content = result.opts.appendSystemPrompt![0];
|
|
28
|
+
// 措辞锁(单参数口径):工具参数即结果数据本身,schema 已无独立 data 参数
|
|
29
|
+
expect(content).toContain("Your call arguments ARE the result data itself");
|
|
30
|
+
expect(content).toContain("Your result must conform to this schema:");
|
|
31
|
+
expect(content).toContain("The system validates them against the schema above automatically");
|
|
32
|
+
// 反向回流锁:旧双参数口径文案("do NOT pass a schema parameter" / "ONLY the data
|
|
33
|
+
// parameter")一旦回流立即失败
|
|
34
|
+
expect(content).not.toMatch(
|
|
35
|
+
/do NOT pass a .schema. parameter|ONLY the .data. parameter/i,
|
|
36
|
+
);
|
|
37
|
+
// 内容直传(M2 fix):非路径
|
|
38
|
+
expect(content).not.toMatch(/\/tmp\/|\/var\/folders\//);
|
|
39
|
+
expect(content).not.toMatch(/\.md$/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("无 schema 无 skill 时 appendSystemPrompt undefined(TC2)", () => {
|
|
43
|
+
const opts: AgentCallOpts = {
|
|
44
|
+
prompt: "x",
|
|
45
|
+
agent: "/abs/path/worker.md",
|
|
46
|
+
description: "test",
|
|
47
|
+
slug: "test",
|
|
48
|
+
};
|
|
49
|
+
const result = resolveAgentOpts(opts);
|
|
50
|
+
|
|
51
|
+
expect(result.error).toBeUndefined();
|
|
52
|
+
expect(result.opts.appendSystemPrompt).toBeUndefined();
|
|
53
|
+
// agent ref 原样保留(交 resolveIdentity 处理)
|
|
54
|
+
expect(result.opts.agent).toBe("/abs/path/worker.md");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("resolveAgentOpts 不处理 agent ref——不存在路径不报错(TC3)", () => {
|
|
58
|
+
const opts: AgentCallOpts = {
|
|
59
|
+
prompt: "x",
|
|
60
|
+
agent: "/nonexistent/agent.md",
|
|
61
|
+
};
|
|
62
|
+
const result = resolveAgentOpts(opts);
|
|
63
|
+
|
|
64
|
+
// resolveAgentOpts 不校验 agent 路径(已移交 resolveIdentity)
|
|
65
|
+
expect(result.error).toBeUndefined();
|
|
66
|
+
expect(result.opts.agent).toBe("/nonexistent/agent.md");
|
|
67
|
+
// model/thinkingLevel 不被 resolveAgentOpts 修改(agent 层级提升已消除)
|
|
68
|
+
expect(result.opts.model).toBeUndefined();
|
|
69
|
+
expect(result.opts.thinkingLevel).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("sets schemaEnv from the provided schema (PI_WORKFLOW_SCHEMA contract)", () => {
|
|
73
|
+
const schema: Record<string, unknown> = {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: { n: { type: "number" } },
|
|
76
|
+
};
|
|
77
|
+
const opts: AgentCallOpts = { prompt: "x", schema };
|
|
78
|
+
const result = resolveAgentOpts(opts);
|
|
79
|
+
|
|
80
|
+
expect(result.opts.schemaEnv).toBe(JSON.stringify(schema));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("无 schema 时 schemaEnv undefined", () => {
|
|
84
|
+
const opts: AgentCallOpts = { prompt: "x" };
|
|
85
|
+
const result = resolveAgentOpts(opts);
|
|
86
|
+
|
|
87
|
+
expect(result.opts.schemaEnv).toBeUndefined();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// [U3] 根类型条件化:ASP 文案与 structured-output 工具 description 同源互斥——
|
|
91
|
+
// object 根口径「arguments ARE the data」;非 object 根参数层实为 {value} 包装,
|
|
92
|
+
// 文案必须告知 {value: <data>} 契约,两形态文案互斥不可共存。
|
|
93
|
+
describe("根类型条件化文案(U3:与工具 description 同源互斥)", () => {
|
|
94
|
+
it("object 根 → arguments 即 data 口径,无 {value} 包装语汇", () => {
|
|
95
|
+
const opts: AgentCallOpts = {
|
|
96
|
+
prompt: "x",
|
|
97
|
+
schema: { type: "object", properties: { n: { type: "number" } } },
|
|
98
|
+
};
|
|
99
|
+
const content = resolveAgentOpts(opts).opts.appendSystemPrompt![0];
|
|
100
|
+
|
|
101
|
+
expect(content).toContain("Your call arguments ARE the result data itself");
|
|
102
|
+
expect(content).not.toContain("{value:");
|
|
103
|
+
expect(content).not.toContain("value.");
|
|
104
|
+
// Rules 首条同样是 object 根口径
|
|
105
|
+
expect(content).toContain(
|
|
106
|
+
"- Call the structured-output tool with your result data as its arguments.",
|
|
107
|
+
);
|
|
108
|
+
expect(content).not.toContain("{value: <your result data>}");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("非 object 根(array)→ {value} 包装契约语汇,无 arguments 即 data 口径", () => {
|
|
112
|
+
const opts: AgentCallOpts = {
|
|
113
|
+
prompt: "x",
|
|
114
|
+
schema: { type: "array", items: { type: "string" } },
|
|
115
|
+
};
|
|
116
|
+
const content = resolveAgentOpts(opts).opts.appendSystemPrompt![0];
|
|
117
|
+
|
|
118
|
+
// 包装契约与 value. 错误路径前缀(与工具 description 同语汇)
|
|
119
|
+
expect(content).toContain("{value: <data>}");
|
|
120
|
+
expect(content).toContain("value.");
|
|
121
|
+
expect(content).toContain("that prefix addresses the wrapper, not your data");
|
|
122
|
+
// 互斥:object 根专属文案不得出现
|
|
123
|
+
expect(content).not.toContain("Your call arguments ARE the result data itself");
|
|
124
|
+
expect(content).not.toContain(
|
|
125
|
+
"- Call the structured-output tool with your result data as its arguments.",
|
|
126
|
+
);
|
|
127
|
+
// Rules 首条切换为包装口径
|
|
128
|
+
expect(content).toContain("- Call the structured-output tool with `{value: <your result data>}`.");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("组合根(anyOf,可能接受非 object 值)→ 按非 object 包装口径", () => {
|
|
132
|
+
const opts: AgentCallOpts = {
|
|
133
|
+
prompt: "x",
|
|
134
|
+
schema: { anyOf: [{ type: "string" }, { type: "number" }] },
|
|
135
|
+
};
|
|
136
|
+
const content = resolveAgentOpts(opts).opts.appendSystemPrompt![0];
|
|
137
|
+
|
|
138
|
+
expect(content).toContain("{value: <data>}");
|
|
139
|
+
expect(content).not.toContain("Your call arguments ARE the result data itself");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* writeAppendSystemPromptFile 组装矩阵(W2 移交 CRAP 43.1;私有函数,经公共
|
|
3
|
+
* runSpawn 驱动,与 spawn-worktree-guidance.test.ts 同 mock 模式——mock
|
|
4
|
+
* temp-prompt 捕获 appendParts.join 后的完整 content)。
|
|
5
|
+
*
|
|
6
|
+
* 覆盖分支(session-runner.ts g. 段):
|
|
7
|
+
* - agentConfig.systemPrompt / appendSystemPrompt 片段 / maxTurns wrap-up hint 的注入与顺序
|
|
8
|
+
* - ask_user RPC 提示的 mode 门控(rpc=gui 响应 / json=headless 不注入)
|
|
9
|
+
* - maxTurns 缺省/0 → 不注入 wrap-up hint
|
|
10
|
+
* - fork depth(env block Depth 行)与 ask_user+wrap-up 的组合互不干扰
|
|
11
|
+
*
|
|
12
|
+
* worktree 分支(WORKTREE_GUIDANCE_PROMPT)已由 spawn-worktree-guidance.test.ts
|
|
13
|
+
* 覆盖,此处不重复。
|
|
14
|
+
*/
|
|
15
|
+
import type { PassThrough } from "node:stream";
|
|
16
|
+
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
18
|
+
|
|
19
|
+
const captured = vi.hoisted(() => ({ content: "", calls: 0 }));
|
|
20
|
+
|
|
21
|
+
vi.mock("node:child_process", async () => {
|
|
22
|
+
const { EventEmitter } = await import("node:events");
|
|
23
|
+
const { PassThrough } = await import("node:stream");
|
|
24
|
+
|
|
25
|
+
class FakeChild extends EventEmitter {
|
|
26
|
+
pid = 12345;
|
|
27
|
+
stdout = new PassThrough();
|
|
28
|
+
stdin = new PassThrough();
|
|
29
|
+
stderr = new PassThrough();
|
|
30
|
+
killed = false;
|
|
31
|
+
killSignal: string | undefined;
|
|
32
|
+
kill(sig?: string): boolean {
|
|
33
|
+
this.killed = true;
|
|
34
|
+
this.killSignal = sig;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
spawn: vi.fn(() => new FakeChild()),
|
|
41
|
+
execFileSync: vi.fn(() => ""),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
vi.mock("node:fs", async () => {
|
|
46
|
+
const actual = await import("node:fs");
|
|
47
|
+
return {
|
|
48
|
+
default: {
|
|
49
|
+
...actual,
|
|
50
|
+
mkdirSync: vi.fn(),
|
|
51
|
+
existsSync: vi.fn(() => false),
|
|
52
|
+
appendFileSync: vi.fn(),
|
|
53
|
+
writeFileSync: vi.fn(),
|
|
54
|
+
readdirSync: vi.fn(() => []),
|
|
55
|
+
},
|
|
56
|
+
mkdirSync: vi.fn(),
|
|
57
|
+
existsSync: vi.fn(() => false),
|
|
58
|
+
appendFileSync: vi.fn(),
|
|
59
|
+
writeFileSync: vi.fn(),
|
|
60
|
+
readdirSync: vi.fn(() => []),
|
|
61
|
+
promises: actual.promises,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
vi.mock("../execution/alive-store.ts", () => ({
|
|
66
|
+
writeAliveMarker: vi.fn(),
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
vi.mock("../execution/temp-prompt.ts", () => ({
|
|
70
|
+
writePromptToTempFile: vi.fn(async (agent: string, content: string) => {
|
|
71
|
+
captured.calls++;
|
|
72
|
+
captured.content = content;
|
|
73
|
+
const safeName = agent.replace(/[^\w.-]+/g, "_");
|
|
74
|
+
return { dir: `/tmp/fake-${safeName}`, filePath: `/tmp/fake-${safeName}/prompt.md` };
|
|
75
|
+
}),
|
|
76
|
+
cleanupTempPrompt: vi.fn(async () => {}),
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
import { spawn } from "node:child_process";
|
|
80
|
+
|
|
81
|
+
import { createRecord } from "../execution/execution-record.ts";
|
|
82
|
+
import { runSpawn, type RunOptions, type SessionRunnerContext } from "../execution/session-runner.ts";
|
|
83
|
+
|
|
84
|
+
const mockSpawn = vi.mocked(spawn);
|
|
85
|
+
|
|
86
|
+
interface FakeChild {
|
|
87
|
+
pid: number;
|
|
88
|
+
stdout: PassThrough;
|
|
89
|
+
stderr: PassThrough;
|
|
90
|
+
killed: boolean;
|
|
91
|
+
killSignal: string | undefined;
|
|
92
|
+
emit(event: string, ...args: unknown[]): boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getLastSpawnedChild(): FakeChild {
|
|
96
|
+
const result = mockSpawn.mock.results.at(-1);
|
|
97
|
+
if (!result) throw new Error("spawn was not called yet");
|
|
98
|
+
return result.value as FakeChild;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** 等待第 targetCount 次 spawn 发生(同测试多次 runSpawn 时按基线递增等待)。 */
|
|
102
|
+
async function waitForSpawn(targetCount = 1, timeoutMs = 1000): Promise<void> {
|
|
103
|
+
const start = Date.now();
|
|
104
|
+
while (mockSpawn.mock.results.length < targetCount) {
|
|
105
|
+
if (Date.now() - start > timeoutMs) {
|
|
106
|
+
throw new Error(`spawn #${targetCount} not called within ${timeoutMs}ms`);
|
|
107
|
+
}
|
|
108
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let seq = 0;
|
|
113
|
+
|
|
114
|
+
function makeRecord() {
|
|
115
|
+
seq += 1;
|
|
116
|
+
return createRecord(`append-asm-${seq}`, {
|
|
117
|
+
agent: "general-purpose",
|
|
118
|
+
model: "test/model",
|
|
119
|
+
mode: "sync",
|
|
120
|
+
task: "test task",
|
|
121
|
+
startedAt: Date.now(),
|
|
122
|
+
rootSessionId: "s1",
|
|
123
|
+
parentRecordId: undefined,
|
|
124
|
+
depth: 0,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function makeRunOpts(overrides: Partial<RunOptions> = {}): RunOptions {
|
|
129
|
+
return {
|
|
130
|
+
resolved: { model: { provider: "test", id: "model" }, thinkingLevel: undefined },
|
|
131
|
+
agentConfig: undefined,
|
|
132
|
+
appendSystemPrompt: undefined,
|
|
133
|
+
skillPath: undefined,
|
|
134
|
+
schema: undefined,
|
|
135
|
+
maxTurns: undefined,
|
|
136
|
+
graceTurns: undefined,
|
|
137
|
+
signal: undefined,
|
|
138
|
+
onEvent: undefined,
|
|
139
|
+
...overrides,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function makeCtx(overrides: Partial<SessionRunnerContext> = {}): SessionRunnerContext {
|
|
144
|
+
return {
|
|
145
|
+
cwd: "/fake/cwd",
|
|
146
|
+
agentDir: "/fake/agent",
|
|
147
|
+
skillDirs: [],
|
|
148
|
+
mainCwd: "/fake/cwd",
|
|
149
|
+
sessionRootId: "root-session-test",
|
|
150
|
+
rootCwd: "/fake/cwd",
|
|
151
|
+
...overrides,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** 跑一次 runSpawn 并捕获 appendSystemPrompt content(完成后释放子进程)。 */
|
|
156
|
+
async function captureAppendContent(opts: RunOptions, ctx: SessionRunnerContext): Promise<string> {
|
|
157
|
+
const baseline = mockSpawn.mock.results.length;
|
|
158
|
+
const resultPromise = runSpawn(makeRecord(), "test task", opts, ctx);
|
|
159
|
+
await waitForSpawn(baseline + 1);
|
|
160
|
+
const content = captured.content;
|
|
161
|
+
const child = getLastSpawnedChild();
|
|
162
|
+
child.emit("close", 0);
|
|
163
|
+
await resultPromise;
|
|
164
|
+
return content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
describe("writeAppendSystemPromptFile 组装(经 runSpawn 驱动)", () => {
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
vi.clearAllMocks();
|
|
170
|
+
captured.content = "";
|
|
171
|
+
captured.calls = 0;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
afterEach(() => {
|
|
175
|
+
vi.restoreAllMocks();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("全片段组装:env block → agent body → 调用方片段 → wrap-up hint,顺序稳定", async () => {
|
|
179
|
+
const content = await captureAppendContent(
|
|
180
|
+
makeRunOpts({
|
|
181
|
+
agentConfig: { systemPrompt: "AGENT-BODY-MARKER" },
|
|
182
|
+
appendSystemPrompt: ["FRAG-ONE", "FRAG-TWO"],
|
|
183
|
+
maxTurns: 5,
|
|
184
|
+
}),
|
|
185
|
+
makeCtx(),
|
|
186
|
+
);
|
|
187
|
+
expect(content).toContain("AGENT-BODY-MARKER");
|
|
188
|
+
expect(content).toContain("FRAG-ONE");
|
|
189
|
+
expect(content).toContain("FRAG-TWO");
|
|
190
|
+
// maxTurns>0 → 注入 wrap-up 收尾提示(rpc steer 未接通的启动期补偿)
|
|
191
|
+
expect(content).toContain("You have a turn limit");
|
|
192
|
+
// 顺序:env block 最前,agent body 在调用方片段前,hint 在其后
|
|
193
|
+
const envIdx = content.indexOf("--- environment");
|
|
194
|
+
const bodyIdx = content.indexOf("AGENT-BODY-MARKER");
|
|
195
|
+
const fragIdx = content.indexOf("FRAG-ONE");
|
|
196
|
+
const hintIdx = content.indexOf("You have a turn limit");
|
|
197
|
+
expect(envIdx).toBeLessThan(bodyIdx);
|
|
198
|
+
expect(bodyIdx).toBeLessThan(fragIdx);
|
|
199
|
+
expect(fragIdx).toBeLessThan(hintIdx);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("maxTurns 缺省/0 → 不注入 wrap-up hint(不限时不预告收尾)", async () => {
|
|
203
|
+
const undefinedCase = await captureAppendContent(makeRunOpts(), makeCtx());
|
|
204
|
+
expect(undefinedCase).not.toContain("You have a turn limit");
|
|
205
|
+
const zeroCase = await captureAppendContent(makeRunOpts({ maxTurns: 0 }), makeCtx());
|
|
206
|
+
expect(zeroCase).not.toContain("You have a turn limit");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("ask_user RPC 提示 mode 门控:rpc(gui 响应)注入,json(headless)不注入", async () => {
|
|
210
|
+
const opts = makeRunOpts({ agentConfig: { tools: ["ask_user"] } });
|
|
211
|
+
const rpcContent = await captureAppendContent(opts, makeCtx({ mode: "rpc" }));
|
|
212
|
+
expect(rpcContent).toContain("ask_user Tool Availability");
|
|
213
|
+
const jsonContent = await captureAppendContent(opts, makeCtx({ mode: "json" }));
|
|
214
|
+
// headless 无 UI 通道,注入会误导 LLM(W4 守卫)
|
|
215
|
+
expect(jsonContent).not.toContain("ask_user Tool Availability");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("mode 响应但 tools 不含 ask_user → 不注入", async () => {
|
|
219
|
+
const content = await captureAppendContent(
|
|
220
|
+
makeRunOpts({ agentConfig: { tools: ["read", "bash"] } }),
|
|
221
|
+
makeCtx({ mode: "rpc" }),
|
|
222
|
+
);
|
|
223
|
+
expect(content).not.toContain("ask_user Tool Availability");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("fork 场景:parentForkDepth 传入 → env block 带 Depth 行(buildEnvBlock 取 max 语义)", async () => {
|
|
227
|
+
const content = await captureAppendContent(
|
|
228
|
+
makeRunOpts({ fork: true, parentForkDepth: 2 }),
|
|
229
|
+
makeCtx(),
|
|
230
|
+
);
|
|
231
|
+
expect(content).toContain("Depth:");
|
|
232
|
+
// 无 fork 无嵌套时不出现 Depth 行
|
|
233
|
+
const plain = await captureAppendContent(makeRunOpts(), makeCtx());
|
|
234
|
+
expect(plain).not.toContain("Depth:");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("env block 恒为第一段:最小配置也落盘(writePromptToTempFile 必被调用)", async () => {
|
|
238
|
+
await captureAppendContent(makeRunOpts(), makeCtx());
|
|
239
|
+
expect(captured.calls).toBe(1);
|
|
240
|
+
expect(captured.content).toContain("Working directory: /fake/cwd");
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// U2 + E1: 内置 workflow 脚本 null guard 覆盖 + 文件结构一致性
|
|
2
|
+
//
|
|
3
|
+
// U2: agent() 失败时 resolve 成空字符串/undefined(设计行为),脚本直接访问
|
|
4
|
+
// 返回值属性会 TypeError。验证 4 个内置脚本的 agent() 返回值属性访问均含
|
|
5
|
+
// null guard(?. 或 ??),不遗留裸 .property 访问。
|
|
6
|
+
//
|
|
7
|
+
// E1: workflows/ 目录含 4 个 .js 文件,每个 meta.name 与文件名 stem 一致。
|
|
8
|
+
|
|
9
|
+
import { readdirSync,readFileSync } from "node:fs";
|
|
10
|
+
import { dirname,join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
import { parseResourceMeta } from "../shared/meta-parser.ts";
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it } from "vitest";
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const WORKFLOWS_DIR = join(__dirname, "../../workflows");
|
|
19
|
+
|
|
20
|
+
const SCRIPTS = ["chain.js", "parallel.js", "scatter-gather.js", "map-reduce.js", "review-fix-loop.js"] as const;
|
|
21
|
+
|
|
22
|
+
function readScript(name: string): string {
|
|
23
|
+
return readFileSync(join(WORKFLOWS_DIR, name), "utf-8");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 提取 meta.name 值(经 IF1 parseResourceMeta,m2 收敛)。 */
|
|
27
|
+
function extractMetaName(src: string): string | null {
|
|
28
|
+
const meta = parseResourceMeta(src, "workflow");
|
|
29
|
+
return meta && meta.kind === "workflow" ? meta.name : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("E1: 内置 workflow 文件结构一致性", () => {
|
|
33
|
+
it("workflows/ 目录含 5 个 .js 文件", () => {
|
|
34
|
+
const files = readdirSync(WORKFLOWS_DIR).filter((f) => f.endsWith(".js"));
|
|
35
|
+
expect(files.sort()).toEqual([...SCRIPTS].sort());
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it.each(SCRIPTS)("meta.name 与文件名 stem 一致:%s", (filename) => {
|
|
39
|
+
const src = readScript(filename);
|
|
40
|
+
const metaName = extractMetaName(src);
|
|
41
|
+
const stem = filename.replace(/\.js$/, "");
|
|
42
|
+
expect(metaName).toBe(stem);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("U2: 内置脚本 agent() 返回值属性访问含 null guard", () => {
|
|
47
|
+
// 策略:检查脚本中 ?. 和 ?? 的出现次数 >= agent() 调用次数(每个 agent() 返回值
|
|
48
|
+
// 至少有一处属性访问需 guard)。更精确地:脚本不应有裸的 `变量.属性` 访问
|
|
49
|
+
// agent() 返回值——但区分 agent() 返回值和局部对象属性访问需要 AST 分析,
|
|
50
|
+
// 这里用启发式:验证含 agent() 结果属性访问的脚本都含 ?. 模式(null guard 已存在)。
|
|
51
|
+
//
|
|
52
|
+
// 注意:parallel.js 的 aggregate 段为纯代码 concat(无 LLM 聚合 agent()),
|
|
53
|
+
// 其 parallel() 多结果由 W4 的 r.status === "failed" / typeof / Array.isArray 守卫,
|
|
54
|
+
// 不再用 ?.,故不纳入此 ?. 启发式检查。
|
|
55
|
+
// map-reduce 的 reduce、scatter-gather 的 gather 恢复为 LLM agent() 调用,
|
|
56
|
+
// 故 map-reduce.js 重新纳入此检查。
|
|
57
|
+
|
|
58
|
+
// 仍含 agent() 返回值属性访问、需 ?. guard 的脚本:chain(analysis/plan/final)、
|
|
59
|
+
// scatter-gather(split 的 subtasks、gather 的 mergedResult/completeness)、
|
|
60
|
+
// map-reduce(reduce 的 reduced/stats)、review-fix-loop(aggRaw?.length 等)。
|
|
61
|
+
const GUARD_SCRIPTS = ["chain.js", "scatter-gather.js", "map-reduce.js", "review-fix-loop.js"] as const;
|
|
62
|
+
|
|
63
|
+
it.each(GUARD_SCRIPTS)("%s 含 optional chaining(?.)null guard 模式", (filename) => {
|
|
64
|
+
const src = readScript(filename);
|
|
65
|
+
// chain.js 至少有 6 处 ?. guard;scatter-gather.js 至少有 split/gather 多处;
|
|
66
|
+
// map-reduce.js 至少有 reduce 的 reduced/stats 多处
|
|
67
|
+
expect(src).toContain("?.");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("chain.js 的 analysis/plan/final 属性访问均含 null guard", () => {
|
|
71
|
+
const src = readScript("chain.js");
|
|
72
|
+
// analysis 的 insights/keyPoints
|
|
73
|
+
expect(src).toContain("analysis?.insights");
|
|
74
|
+
expect(src).toContain("analysis?.keyPoints");
|
|
75
|
+
// plan 的 plan/actions
|
|
76
|
+
expect(src).toContain("plan?.plan");
|
|
77
|
+
expect(src).toContain("plan?.actions");
|
|
78
|
+
// final 的 summary/recommendation
|
|
79
|
+
expect(src).toContain("final?.summary");
|
|
80
|
+
expect(src).toContain("final?.recommendation");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("scatter-gather.js 的 split/gathered 属性访问含 null guard", () => {
|
|
84
|
+
const src = readScript("scatter-gather.js");
|
|
85
|
+
// split 的 subtasks
|
|
86
|
+
expect(src).toContain("split?.subtasks");
|
|
87
|
+
// gather(gatheredResult)的 mergedResult / completeness
|
|
88
|
+
expect(src).toContain("gatheredResult?.mergedResult");
|
|
89
|
+
expect(src).toContain("gatheredResult?.completeness");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("map-reduce.js 的 reduced 属性访问含 null guard", () => {
|
|
93
|
+
const src = readScript("map-reduce.js");
|
|
94
|
+
// reduce(reducedResult)的 reduced / stats
|
|
95
|
+
expect(src).toContain("reducedResult?.reduced");
|
|
96
|
+
expect(src).toContain("reducedResult?.stats");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it.each(SCRIPTS)("%s 不含裸 analysis.insights/plan.plan 等无 guard 访问", (filename) => {
|
|
100
|
+
const src = readScript(filename);
|
|
101
|
+
// 检查不存在 "变量.属性" 形式的裸访问(不含 ?. 的)
|
|
102
|
+
// 排除 schema 对象定义里的 properties.xxx 和 JSON.stringify 等合法用法
|
|
103
|
+
// 只检查 agent() 返回值变量名后的裸属性访问
|
|
104
|
+
const agentVars = ["analysis", "plan", "final", "split", "gatheredResult", "reducedResult"];
|
|
105
|
+
for (const v of agentVars) {
|
|
106
|
+
// 匹配 `变量.属性`(非 `变量?.属性`),但排除变量声明和赋值左侧
|
|
107
|
+
const bareAccess = new RegExp(`[^?\\w.]${v}\\.[a-zA-Z]`);
|
|
108
|
+
// 排除 schema properties 定义中的合法用法(如 properties: { insights: ... })
|
|
109
|
+
// 这里只关注 prompt 拼接和 outcome 构造中的裸访问
|
|
110
|
+
const lines = src.split("\n");
|
|
111
|
+
for (const line of lines) {
|
|
112
|
+
// 跳过 schema 定义行(含 type: / properties: / description:)
|
|
113
|
+
if (/^\s*(type|properties|description|required|items):/.test(line)) continue;
|
|
114
|
+
// 跳过 const 声明行(如 `const plan = await agent(...)`)
|
|
115
|
+
if (/^\s*const\s+\w+\s*=/.test(line) && line.includes("agent(")) continue;
|
|
116
|
+
if (bareAccess.test(line)) {
|
|
117
|
+
// 检查这个裸访问是否真的在属性读取位置(而非变量定义)
|
|
118
|
+
const match = line.match(bareAccess);
|
|
119
|
+
if (match && !line.includes(`?.`)) {
|
|
120
|
+
// 确认是 agent() 返回值的属性访问(同一行有拼接/赋值上下文)
|
|
121
|
+
// 如果行里同时有 ?. 版本,说明是 guard 后的,不算裸访问
|
|
122
|
+
expect(line).toMatch(new RegExp(`${v}\\?\\.`));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// W4: parallel() 降级返回值 {status:'failed'} 兼容 + 结果字段守卫 + spread 消除
|
|
131
|
+
describe("W4: parallel() failed-status 兼容 + 结果字段守卫 + spread 消除", () => {
|
|
132
|
+
// W1 后 worker parallel() 可能返回 {status:'failed', error:'...'},脚本 for 循环
|
|
133
|
+
// 必须识别此对象并标记失败(不能误判为成功)。验证三个含 parallel() 的脚本。
|
|
134
|
+
const PARALLEL_SCRIPTS = ["parallel.js", "scatter-gather.js", "map-reduce.js"] as const;
|
|
135
|
+
|
|
136
|
+
it.each(PARALLEL_SCRIPTS)("%s 含 r.status === \"failed\" 失败识别(W1 兼容)", (filename) => {
|
|
137
|
+
const src = readScript(filename);
|
|
138
|
+
// 三个脚本都应有 `r.status === "failed"` 检查(识别 W1 的降级对象)
|
|
139
|
+
expect(src).toContain('r.status === "failed"');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("parallel.js perPerspective 不再用 spread 透传 agent 字段(spread 消除)", () => {
|
|
143
|
+
const src = readScript("parallel.js");
|
|
144
|
+
// W4 后显式取 score/findings,不再 ...r
|
|
145
|
+
expect(src).not.toContain("...r");
|
|
146
|
+
// 显式字段守卫存在
|
|
147
|
+
expect(src).toContain("typeof r.score === \"number\"");
|
|
148
|
+
expect(src).toContain("Array.isArray(r.findings)");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("scatter-gather.js r.result 字段守卫存在", () => {
|
|
152
|
+
const src = readScript("scatter-gather.js");
|
|
153
|
+
expect(src).toContain("typeof r.result === \"string\"");
|
|
154
|
+
expect(src).toContain("\"(无结果)\"");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("map-reduce.js r.mapped 字段守卫存在", () => {
|
|
158
|
+
const src = readScript("map-reduce.js");
|
|
159
|
+
expect(src).toContain("typeof r.mapped === \"string\"");
|
|
160
|
+
expect(src).toContain("\"(无结果)\"");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("parallel.js perspectives 元素类型校验存在", () => {
|
|
164
|
+
const src = readScript("parallel.js");
|
|
165
|
+
expect(src).toContain("perspectives.some");
|
|
166
|
+
expect(src).toContain("typeof p !== \"string\"");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("scatter-gather.js subtasks 元素结构校验存在", () => {
|
|
170
|
+
const src = readScript("scatter-gather.js");
|
|
171
|
+
expect(src).toContain("subtasks.some");
|
|
172
|
+
expect(src).toContain("typeof s.name !== \"string\"");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("chain.js task 类型校验拒绝非字符串与空白", () => {
|
|
176
|
+
const src = readScript("chain.js");
|
|
177
|
+
expect(src).toContain("typeof task !== \"string\"");
|
|
178
|
+
expect(src).toContain("task.trim() === \"\"");
|
|
179
|
+
});
|
|
180
|
+
});
|