@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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resource-meta 类型族测试(v5 §11.1)
|
|
3
|
+
*
|
|
4
|
+
* 编译期 + 运行期判别 ResourceMeta 联合。
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
|
|
8
|
+
import type { AgentMeta, ResourceMeta, WorkflowMeta } from "../resource-meta.ts";
|
|
9
|
+
|
|
10
|
+
describe("ResourceMeta 类型族", () => {
|
|
11
|
+
it("WorkflowMeta 判别 kind='workflow'", () => {
|
|
12
|
+
const wf: WorkflowMeta = {
|
|
13
|
+
kind: "workflow",
|
|
14
|
+
name: "chain",
|
|
15
|
+
description: "test",
|
|
16
|
+
phases: ["a"],
|
|
17
|
+
};
|
|
18
|
+
expect(wf.kind).toBe("workflow");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("AgentMeta 判别 kind='agent'", () => {
|
|
22
|
+
const ag: AgentMeta = {
|
|
23
|
+
kind: "agent",
|
|
24
|
+
name: "reviewer",
|
|
25
|
+
description: "review",
|
|
26
|
+
};
|
|
27
|
+
expect(ag.kind).toBe("agent");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("联合类型 kind 判别 narrowing", () => {
|
|
31
|
+
function isWorkflow(m: ResourceMeta): m is WorkflowMeta {
|
|
32
|
+
return m.kind === "workflow";
|
|
33
|
+
}
|
|
34
|
+
const wf: ResourceMeta = { kind: "workflow", name: "x", description: "", phases: [] };
|
|
35
|
+
const ag: ResourceMeta = { kind: "agent", name: "y", description: "" };
|
|
36
|
+
expect(isWorkflow(wf)).toBe(true);
|
|
37
|
+
expect(isWorkflow(ag)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("可选字段缺省为 undefined", () => {
|
|
41
|
+
const wf: WorkflowMeta = {
|
|
42
|
+
kind: "workflow",
|
|
43
|
+
name: "x",
|
|
44
|
+
description: "",
|
|
45
|
+
phases: [],
|
|
46
|
+
};
|
|
47
|
+
expect(wf.parameters).toBeUndefined();
|
|
48
|
+
expect(wf.usage).toBeUndefined();
|
|
49
|
+
expect(wf.when).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stringifySchemaCached(IF7/#13)— WeakMap 引用级缓存单测(TC6/DM4)。
|
|
3
|
+
*
|
|
4
|
+
* 断言面(design IF7 契约):
|
|
5
|
+
* - 同对象二调返回同字符串引用(缓存命中,非等值新串)
|
|
6
|
+
* - compact/pretty 分别缓存(互不覆写)
|
|
7
|
+
* - 不同对象互不污染
|
|
8
|
+
* - 输出与直接 JSON.stringify 逐字节一致
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it, vi } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { stringifySchemaCached } from "../schema-jsonify.ts";
|
|
13
|
+
|
|
14
|
+
describe("stringifySchemaCached — WeakMap 引用级缓存", () => {
|
|
15
|
+
const schema = { type: "object", properties: { n: { type: "number" } } };
|
|
16
|
+
|
|
17
|
+
it("输出与直接 JSON.stringify 逐字节一致(compact / pretty)", () => {
|
|
18
|
+
expect(stringifySchemaCached(schema, "compact")).toBe(JSON.stringify(schema));
|
|
19
|
+
expect(stringifySchemaCached(schema, "pretty")).toBe(JSON.stringify(schema, null, 2));
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("同对象二调返回同字符串引用(缓存命中)", () => {
|
|
23
|
+
const a = stringifySchemaCached(schema, "compact");
|
|
24
|
+
const b = stringifySchemaCached(schema, "compact");
|
|
25
|
+
expect(b).toBe(a); // toBe = 引用相等(非仅等值)
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("同对象跨格式:compact 与 pretty 分别缓存且各自命中", () => {
|
|
29
|
+
const c1 = stringifySchemaCached(schema, "compact");
|
|
30
|
+
const p1 = stringifySchemaCached(schema, "pretty");
|
|
31
|
+
const c2 = stringifySchemaCached(schema, "compact");
|
|
32
|
+
const p2 = stringifySchemaCached(schema, "pretty");
|
|
33
|
+
expect(c2).toBe(c1);
|
|
34
|
+
expect(p2).toBe(p1);
|
|
35
|
+
expect(c1).not.toBe(p1); // 两种格式输出不同,分别缓存
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("不同对象互不污染(等值异引用各持条目)", () => {
|
|
39
|
+
const s1 = { type: "object" };
|
|
40
|
+
const s2 = { type: "object" }; // 等值但异引用
|
|
41
|
+
const r1 = stringifySchemaCached(s1, "compact");
|
|
42
|
+
const r2 = stringifySchemaCached(s2, "compact");
|
|
43
|
+
expect(r1).toBe(r2); // 等值(内容相同)
|
|
44
|
+
// s2 未命中 s1 的条目(否则 spy 场景下 s2 的 stringify 调用会被跳过——
|
|
45
|
+
// 引用级隔离由「同引用返回同串」+「等值异引用各自 stringify」共同保证)
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("缓存命中路径不再调 JSON.stringify(spy 计数)", () => {
|
|
49
|
+
const spy = vi.spyOn(JSON, "stringify");
|
|
50
|
+
try {
|
|
51
|
+
const obj = { spy: true };
|
|
52
|
+
stringifySchemaCached(obj, "pretty");
|
|
53
|
+
stringifySchemaCached(obj, "pretty"); // 第二次应命中缓存
|
|
54
|
+
const calls = spy.mock.calls.filter((c) => c[0] === obj);
|
|
55
|
+
expect(calls).toHaveLength(1);
|
|
56
|
+
} finally {
|
|
57
|
+
spy.mockRestore();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("pretty 缩进锚定:indent=2 与 resolver formatSchemaInstruction 渲染同源", () => {
|
|
62
|
+
const out = stringifySchemaCached({ type: "object" }, "pretty");
|
|
63
|
+
expect(out).toBe('{\n "type": "object"\n}');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// [review 修复] TS lib 盲区回归:schema 含 toJSON: () => undefined 钩子时
|
|
67
|
+
// JSON.stringify 运行时返回 undefined,违反声明的 string 返回类型——现 fail-loud
|
|
68
|
+
// 抛含恢复指引的错误,不静默返回 undefined / 垃圾串。
|
|
69
|
+
it("[review 修复] schema 含 toJSON: () => undefined → 抛含恢复指引的错误(compact / pretty)", () => {
|
|
70
|
+
const bad = { type: "object", toJSON: () => undefined };
|
|
71
|
+
expect(() => stringifySchemaCached(bad, "compact")).toThrowError(
|
|
72
|
+
/stringifySchemaCached: JSON\.stringify returned undefined.*Recovery:/s,
|
|
73
|
+
);
|
|
74
|
+
expect(() => stringifySchemaCached(bad, "pretty")).toThrowError(
|
|
75
|
+
/toJSON hook returning undefined.*Recovery:/s,
|
|
76
|
+
);
|
|
77
|
+
// 正常 schema 不受影响(toJSON 返回合法值时按其返回值序列化)
|
|
78
|
+
const good = { type: "object", toJSON: () => ({ type: "string" }) };
|
|
79
|
+
expect(stringifySchemaCached(good, "compact")).toBe('{"type":"string"}');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// src/shared/__tests__/timer-delay.test.ts
|
|
2
|
+
//
|
|
3
|
+
// assertSafeTimerDelay 纯函数契约——三个 timer 挂载入口(budgetTimeMs /
|
|
4
|
+
// XYZ_SUBAGENT_SPAWN_WATCHDOG_MS / idleTimeoutMs)统一在值流入 setTimeout 前调
|
|
5
|
+
// 用本函数 fail-fast,契约由本文件锁定。
|
|
6
|
+
//
|
|
7
|
+
// [F-3 NaN 穿透修复] 旧实现只挡 `> MAX`:NaN 的 `NaN > MAX` 为 false 静默放行,
|
|
8
|
+
// setTimeout(fn, NaN) 被 Node 塌缩为 1ms 立即触发(watchdog 刚启动就误杀)。
|
|
9
|
+
// 非有限数(NaN/±Infinity)现在同样 fail-fast,且错误消息区分两种情况。
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { MAX_TIMER_DELAY_MS, assertSafeTimerDelay } from "../timer-delay.ts";
|
|
13
|
+
|
|
14
|
+
describe("assertSafeTimerDelay", () => {
|
|
15
|
+
it("安全域内的有限值放行(0 与上限值不抛)", () => {
|
|
16
|
+
expect(() => assertSafeTimerDelay(0, "t")).not.toThrow();
|
|
17
|
+
expect(() => assertSafeTimerDelay(1000, "t")).not.toThrow();
|
|
18
|
+
expect(() => assertSafeTimerDelay(MAX_TIMER_DELAY_MS, "t")).not.toThrow();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("[F-3] NaN → fail-fast throw(旧实现 NaN > MAX 为 false 静默放行)", () => {
|
|
22
|
+
expect(() => assertSafeTimerDelay(Number.NaN, "budgetTimeMs")).toThrowError(/NaN/);
|
|
23
|
+
expect(() => assertSafeTimerDelay(Number.NaN, "budgetTimeMs")).toThrowError(/not a finite number/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("[F-3] ±Infinity → fail-fast throw", () => {
|
|
27
|
+
expect(() => assertSafeTimerDelay(Number.POSITIVE_INFINITY, "t")).toThrowError(/not a finite number/);
|
|
28
|
+
expect(() => assertSafeTimerDelay(Number.NEGATIVE_INFINITY, "t")).toThrowError(/not a finite number/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("非有限值错误消息含来源标识与上游修复指引(非 clamp 指引)", () => {
|
|
32
|
+
expect(() => assertSafeTimerDelay(Number.NaN, "idleTimeoutMs")).toThrowError(/idleTimeoutMs/);
|
|
33
|
+
expect(() => assertSafeTimerDelay(Number.NaN, "idleTimeoutMs")).toThrowError(/fix the upstream computation/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("超出上限 → fail-fast throw,消息含上限值与 clamp 恢复指引(原语义不变)", () => {
|
|
37
|
+
expect(() => assertSafeTimerDelay(MAX_TIMER_DELAY_MS + 1, "budgetTimeMs")).toThrowError(/2147483647/);
|
|
38
|
+
expect(() => assertSafeTimerDelay(3_000_000_000, "budgetTimeMs")).toThrowError(/Recovery: clamp/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("两种错误可区分:非有限值消息不含 clamp 指引,溢出消息不含 upstream 指引", () => {
|
|
42
|
+
const nonFiniteMsg = (() => {
|
|
43
|
+
try {
|
|
44
|
+
assertSafeTimerDelay(Number.NaN, "t");
|
|
45
|
+
return "";
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return (err as Error).message;
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
const overflowMsg = (() => {
|
|
51
|
+
try {
|
|
52
|
+
assertSafeTimerDelay(MAX_TIMER_DELAY_MS + 1, "t");
|
|
53
|
+
return "";
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return (err as Error).message;
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
expect(nonFiniteMsg).not.toContain("clamp");
|
|
59
|
+
expect(overflowMsg).not.toContain("fix the upstream computation");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/shared/agent-event.ts
|
|
2
|
+
//
|
|
3
|
+
// AgentEvent 类型唯一出口(两包合并后 import 收口)。
|
|
4
|
+
//
|
|
5
|
+
// 合并前 AgentEvent 分别定义在 execution/types.ts(subagents 侧)和
|
|
6
|
+
// orchestration/live/types.ts(workflow 侧)——两份定义完全一致但独立维护。
|
|
7
|
+
// 合并后本文件是单一 source of truth——re-export execution/types.ts 的 AgentEvent,
|
|
8
|
+
// orchestration 层通过本文件引用(替代 orchestration/live/types.ts 的旧副本)。
|
|
9
|
+
//
|
|
10
|
+
// wave-3 删 orchestration/live/types.ts 后,AgentEvent 的唯一定义在 execution/types.ts。
|
|
11
|
+
// 本 re-export 保持「shared/ 是类型共享层」的架构约定。
|
|
12
|
+
|
|
13
|
+
export type { AgentEvent } from "../execution/types.ts";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentRef / workflowRef 统一路径解析(S2)。
|
|
3
|
+
*
|
|
4
|
+
* 设计(agent-ref-path-redesign D1):资源引用唯一形态 = 绝对路径。
|
|
5
|
+
* - agentRef → .md 文件绝对路径(注入段 <available_subagents> 的 <location>)
|
|
6
|
+
* - workflowRef → .js 文件绝对路径(注入段 <available_workflows> 的 <location>)
|
|
7
|
+
* - 支持 `~/` 前缀展开(homedir);相对路径不认(注入段给绝对路径,模型照抄零歧义)
|
|
8
|
+
*
|
|
9
|
+
* 名字不再是引用——只是注入段展示标签。执行侧拿到引用后一律 normalizeRef →
|
|
10
|
+
* 文件加载(AgentRegistry.loadByPath / WorkflowScriptRegistry.getPath)。
|
|
11
|
+
*/
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { isAbsolute, join } from "node:path";
|
|
14
|
+
|
|
15
|
+
/** home 目录简写前缀(`~/`),展开为 homedir 绝对路径。 */
|
|
16
|
+
const HOME_DIR_PREFIX = "~/";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 归一化资源引用:~ 展开 + 绝对路径校验。
|
|
20
|
+
*
|
|
21
|
+
* @param ref 原始引用(注入段 location / 工具参数值)
|
|
22
|
+
* @param ext 期望扩展名(如 ".md" / ".js"),不匹配返回 null
|
|
23
|
+
* @returns 归一化绝对路径;非法(空/相对路径/扩展名不符)返回 null
|
|
24
|
+
*/
|
|
25
|
+
export function normalizeRef(ref: string, ext?: string): string | null {
|
|
26
|
+
const trimmed = ref.trim();
|
|
27
|
+
if (!trimmed) return null;
|
|
28
|
+
|
|
29
|
+
const expanded = trimmed.startsWith(HOME_DIR_PREFIX)
|
|
30
|
+
? join(homedir(), trimmed.slice(HOME_DIR_PREFIX.length))
|
|
31
|
+
: trimmed;
|
|
32
|
+
if (!isAbsolute(expanded)) return null; // 相对路径无基准(注入段给绝对路径)
|
|
33
|
+
|
|
34
|
+
if (ext !== undefined && !expanded.endsWith(ext)) return null;
|
|
35
|
+
return expanded;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** agentRef 扩展名。 */
|
|
39
|
+
export const AGENT_REF_EXT = ".md";
|
|
40
|
+
/** workflowRef 扩展名。 */
|
|
41
|
+
export const WORKFLOW_REF_EXT = ".js";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* agent ref 的显示名:basename + 去 .md 扩展名(`/a/b/worker.md` → `worker`)。
|
|
45
|
+
*
|
|
46
|
+
* agentRef 是绝对路径,UI 显示层(TUI tool block 标题 / list / 完成通知、GUI list item /
|
|
47
|
+
* pending 通知 name)统一经本函数取短名,避免长路径挤占显示宽度。数据层不动——
|
|
48
|
+
* record.agent / env 注入(PI_SUBAGENT_AGENT)/ 持久化 / LLM 通知文本保持完整路径。
|
|
49
|
+
*
|
|
50
|
+
* 非路径值(DEFAULT_AGENT_NAME "general-purpose")与无 .md 后缀的值原样返回。
|
|
51
|
+
* 手动 split(/[\\/]) 而非 path.basename:跨平台统一(macOS 的 path.basename
|
|
52
|
+
* 不切 Windows `\` 分隔符,反之类推),且本模块避免引入平台分支。
|
|
53
|
+
*/
|
|
54
|
+
export function displayAgentName(ref: string): string {
|
|
55
|
+
const base = ref.split(/[\\/]/).pop() ?? ref;
|
|
56
|
+
return base.endsWith(AGENT_REF_EXT) ? base.slice(0, -AGENT_REF_EXT.length) : base;
|
|
57
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta Parser — 资源元数据统一解析器(v5 §4.2 / IF1 + IF2)
|
|
3
|
+
*
|
|
4
|
+
* 两个变体,职责分离(R8-F1:discovery 的 fail-safe null 与 generate 的 linePos 需求互斥,
|
|
5
|
+
* 单一函数无法兼顾):
|
|
6
|
+
* - parseResourceMeta(IF1,discovery 用):fail-safe,任何失败返 null,不抛。
|
|
7
|
+
* 供 config-loader / registry / 两 injector / agent-registry 调用(4 parser 收敛为 1)。
|
|
8
|
+
* - parseResourceMetaDetailed(IF2,generate 闭环用):失败返 {ok:false, error, linePos},
|
|
9
|
+
* linePos 取自 eemeli/yaml YAMLParseError.linePos[0]([P-yaml] 探针实测:
|
|
10
|
+
* e.linePos 是 [start,end] 数组,取 [0] 作起止点),供 actionGenerate 报行列给 LLM 自纠正。
|
|
11
|
+
*
|
|
12
|
+
* 格式(v5 §7 / DM4):
|
|
13
|
+
* - workflow (.js):块注释 `/* @pi-meta <YAML> * /`(单星,非 JSDoc),WORKFLOW_META_RE 提取。
|
|
14
|
+
* - agent (.md):frontmatter `--- <YAML> ---`,FRONTMATTER_RE 提取。
|
|
15
|
+
* - 无 legacy fallback(D1):const meta 旧格式 → extractBlock 取不到块 → null。
|
|
16
|
+
*
|
|
17
|
+
* [P-yaml] 探针已验证:eemeli/yaml 2.9.0 的 YAMLParseError.linePos = [{line,col},{line,col}]。
|
|
18
|
+
*
|
|
19
|
+
* exec-review 修复(major-1 + minor-2..8):
|
|
20
|
+
* - 正则闭合符(星斜杠)必须独占行首,防止 YAML 正文里中途出现的星斜杠(如 usage 块标量
|
|
21
|
+
* 或 patternProperties 正则)截断块致 parameters 等字段静默丢失(§2.3 failure-A 同形态)。
|
|
22
|
+
* - typecheckMeta 严格化:kind 专属字段不可串类(workflow 不许 examples、agent 不许 phases),
|
|
23
|
+
* description 必填,phase detail 非字符串/parameters 非对象均 reject(消除「静默丢弃非法字段」)。
|
|
24
|
+
* - 区分「未找到块」(undefined) 与「块为空」(""),IF2 给可操作错误。
|
|
25
|
+
* - FRONTMATTER_RE 兼容 CRLF。
|
|
26
|
+
*
|
|
27
|
+
* 层归属:shared(L2 统一解析器)。
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { parse as parseYaml } from "yaml";
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
AgentMeta,
|
|
34
|
+
ResourceKind,
|
|
35
|
+
ResourceMeta,
|
|
36
|
+
RoutingExample,
|
|
37
|
+
WorkflowMeta,
|
|
38
|
+
} from "./resource-meta.ts";
|
|
39
|
+
|
|
40
|
+
// ── 格式提取正则 ──────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* workflow @pi-meta 块注释:单星块注释(非 JSDoc `/**`),内容为 YAML。
|
|
44
|
+
* 闭合符(星斜杠)必须独占行首、列 0——防止 YAML 正文里中途出现的星斜杠(如 usage 块标量
|
|
45
|
+
* 内的 see-星斜杠-for、或 patternProperties 正则含星后接斜杠)截断块致后续字段静默丢失(major-1)。
|
|
46
|
+
* 格式规范要求闭合符在列 0(v5 §7),故列 0 闭合不损失合法用例。
|
|
47
|
+
*/
|
|
48
|
+
const WORKFLOW_META_RE = /\/\*\s*@pi-meta\s*\n([\s\S]*?)\n\*\//;
|
|
49
|
+
|
|
50
|
+
/** agent frontmatter:标准 YAML frontmatter,兼容 CRLF(minor-7)。 */
|
|
51
|
+
const FRONTMATTER_RE = /---\r?\n([\s\S]*?)\r?\n---/;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 按资源种类取 meta 块文本(YAML 体)。
|
|
55
|
+
* @returns 未匹配返 undefined;匹配返字符串(可能为空 "")——调用方据 undefined 区分「未找到」。
|
|
56
|
+
*/
|
|
57
|
+
function extractBlock(content: string, kind: ResourceKind): string | undefined {
|
|
58
|
+
const re = kind === "workflow" ? WORKFLOW_META_RE : FRONTMATTER_RE;
|
|
59
|
+
return re.exec(content)?.[1];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── 类型校验(手写,非 ajv;meta 结构简单)──────────────────
|
|
63
|
+
|
|
64
|
+
function isNonEmptyString(v: unknown): v is string {
|
|
65
|
+
return typeof v === "string" && v.length > 0;
|
|
66
|
+
}
|
|
67
|
+
function isString(v: unknown): v is string {
|
|
68
|
+
return typeof v === "string";
|
|
69
|
+
}
|
|
70
|
+
function isPlainObject(v: unknown): v is Record<string, unknown> {
|
|
71
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 把 parseYaml 结果(unknown)校验为类型化 ResourceMeta,失败返 null(语义非法,非语法错)。
|
|
76
|
+
* 严格化(exec-review minor-2..5):kind 专属字段不可串类、description 必填、
|
|
77
|
+
* phase detail 非字符串/parameters 非对象均 reject(消除「静默丢弃非法字段」)。
|
|
78
|
+
*/
|
|
79
|
+
function typecheckMeta(raw: unknown, kind: ResourceKind): ResourceMeta | null {
|
|
80
|
+
if (!isPlainObject(raw)) return null;
|
|
81
|
+
const o = raw;
|
|
82
|
+
|
|
83
|
+
// 公共必填:name 非空字符串、description 必须是字符串(minor-3:缺 description reject)
|
|
84
|
+
if (!isNonEmptyString(o.name)) return null;
|
|
85
|
+
if (!isString(o.description)) return null;
|
|
86
|
+
const when = isString(o.when) ? o.when : undefined;
|
|
87
|
+
const notFor = isString(o.notFor) ? o.notFor : undefined;
|
|
88
|
+
|
|
89
|
+
if (kind === "workflow") {
|
|
90
|
+
// minor-2:agent 专属字段不可出现在 workflow(串类 reject)
|
|
91
|
+
if (o.examples !== undefined || o.tools !== undefined || o.model !== undefined || o.engine !== undefined) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// phases 必填数组,元素为 string | {title:string, detail?:string}
|
|
95
|
+
if (!Array.isArray(o.phases)) return null;
|
|
96
|
+
const phases: WorkflowMeta["phases"] = [];
|
|
97
|
+
for (const p of o.phases) {
|
|
98
|
+
if (isString(p)) {
|
|
99
|
+
phases.push(p);
|
|
100
|
+
} else if (isPlainObject(p) && isNonEmptyString(p.title)) {
|
|
101
|
+
// minor-4:detail 存在但非字符串 → reject(不再静默丢弃)
|
|
102
|
+
if (p.detail !== undefined && !isString(p.detail)) return null;
|
|
103
|
+
phases.push(isString(p.detail) ? { title: p.title, detail: p.detail } : { title: p.title });
|
|
104
|
+
} else {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// minor-5:parameters 存在但非 plain object → reject(不再静默当 undefined)
|
|
109
|
+
const parameters = o.parameters;
|
|
110
|
+
if (parameters !== undefined && !isPlainObject(parameters)) return null;
|
|
111
|
+
const usage = isString(o.usage) ? o.usage : undefined;
|
|
112
|
+
|
|
113
|
+
const meta: WorkflowMeta = {
|
|
114
|
+
kind: "workflow",
|
|
115
|
+
name: o.name,
|
|
116
|
+
description: o.description,
|
|
117
|
+
phases,
|
|
118
|
+
...(parameters !== undefined ? { parameters: parameters as Record<string, unknown> } : {}),
|
|
119
|
+
...(usage !== undefined ? { usage } : {}),
|
|
120
|
+
...(when !== undefined ? { when } : {}),
|
|
121
|
+
...(notFor !== undefined ? { notFor } : {}),
|
|
122
|
+
};
|
|
123
|
+
return meta;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// kind === "agent"
|
|
127
|
+
// minor-2:workflow 专属字段不可出现在 agent(串类 reject)
|
|
128
|
+
if (o.phases !== undefined || o.parameters !== undefined || o.usage !== undefined) return null;
|
|
129
|
+
let examples: RoutingExample[] | undefined;
|
|
130
|
+
if (o.examples !== undefined) {
|
|
131
|
+
if (!Array.isArray(o.examples)) return null;
|
|
132
|
+
const exs: RoutingExample[] = [];
|
|
133
|
+
for (const e of o.examples) {
|
|
134
|
+
if (
|
|
135
|
+
isPlainObject(e) &&
|
|
136
|
+
isString(e.match) &&
|
|
137
|
+
isString(e.action) &&
|
|
138
|
+
typeof e.positive === "boolean"
|
|
139
|
+
) {
|
|
140
|
+
exs.push({ match: e.match, action: e.action, positive: e.positive });
|
|
141
|
+
} else {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
examples = exs;
|
|
146
|
+
}
|
|
147
|
+
let tools: string[] | undefined;
|
|
148
|
+
if (o.tools !== undefined) {
|
|
149
|
+
if (Array.isArray(o.tools)) {
|
|
150
|
+
if (!o.tools.every(isString)) return null;
|
|
151
|
+
tools = o.tools as string[];
|
|
152
|
+
} else if (isString(o.tools)) {
|
|
153
|
+
// 兼容 agent .md 的逗号分隔字符串约定(如 `tools: read, bash, grep`)
|
|
154
|
+
const parts = o.tools.split(",").map((s) => s.trim()).filter(Boolean);
|
|
155
|
+
tools = parts.length > 0 ? parts : undefined;
|
|
156
|
+
} else {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const model = isString(o.model) ? o.model : undefined;
|
|
161
|
+
const engine = isString(o.engine) ? o.engine : undefined;
|
|
162
|
+
|
|
163
|
+
const meta: AgentMeta = {
|
|
164
|
+
kind: "agent",
|
|
165
|
+
name: o.name,
|
|
166
|
+
description: o.description,
|
|
167
|
+
...(examples !== undefined ? { examples } : {}),
|
|
168
|
+
...(tools !== undefined ? { tools } : {}),
|
|
169
|
+
...(model !== undefined ? { model } : {}),
|
|
170
|
+
...(engine !== undefined ? { engine } : {}),
|
|
171
|
+
...(when !== undefined ? { when } : {}),
|
|
172
|
+
...(notFor !== undefined ? { notFor } : {}),
|
|
173
|
+
};
|
|
174
|
+
return meta;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── IF1: parseResourceMeta(discovery,fail-safe null)─────────────────
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 统一 meta 解析入口(discovery 用)。仅认新格式,无 legacy fallback。
|
|
181
|
+
* 任何失败(缺块 / YAML 语法错 / 类型校验失败)→ return null(不抛)。
|
|
182
|
+
* discovery fail-safe:单文件解析失败仅让该资源 available=false,不阻塞其他资源。
|
|
183
|
+
*/
|
|
184
|
+
export function parseResourceMeta(
|
|
185
|
+
content: string,
|
|
186
|
+
kind: ResourceKind,
|
|
187
|
+
): ResourceMeta | null {
|
|
188
|
+
const block = extractBlock(content, kind);
|
|
189
|
+
if (block === undefined) return null;
|
|
190
|
+
try {
|
|
191
|
+
const raw = parseYaml(block);
|
|
192
|
+
return typecheckMeta(raw, kind);
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** 从 eemeli/yaml 拋错提取 linePos 起点(类型守卫,避免 unsafe cast)。[P-yaml] 实测 e.linePos = [{line,col},{line,col}]。*/
|
|
199
|
+
function getYamlLinePos(e: unknown): { line: number; col: number } | undefined {
|
|
200
|
+
if (e !== null && typeof e === "object" && "linePos" in e) {
|
|
201
|
+
const lp = (e as Record<string, unknown>).linePos;
|
|
202
|
+
if (Array.isArray(lp) && lp.length > 0) {
|
|
203
|
+
const first = lp[0];
|
|
204
|
+
if (first !== null && first !== undefined && typeof first === "object"
|
|
205
|
+
&& "line" in first && "col" in first) {
|
|
206
|
+
const f = first as Record<string, unknown>;
|
|
207
|
+
if (typeof f["line"] === "number" && typeof f["col"] === "number") {
|
|
208
|
+
return { line: f["line"], col: f["col"] };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── IF2: parseResourceMetaDetailed(generate 闭环,返 linePos)─────────
|
|
217
|
+
|
|
218
|
+
export type DetailedResult =
|
|
219
|
+
| { ok: true; meta: ResourceMeta }
|
|
220
|
+
| { ok: false; error: string; linePos?: { line: number; col: number } };
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* generate 闭环专用。失败时返回 error + linePos(取自 eemeli/yaml YAMLParseError.linePos[0]),
|
|
224
|
+
* 供 actionGenerate 报「生成 YAML 错在 line X col Y」给 LLM 自纠正(ERR4)。
|
|
225
|
+
* discovery 不用此(保持 fail-safe null)。
|
|
226
|
+
*
|
|
227
|
+
* [P-yaml] 探针实测:e.linePos 是 [{line,col},{line,col}](start+end),取 [0] 作起点。
|
|
228
|
+
*
|
|
229
|
+
* minor-6:区分「未找到块」(undefined) 与「块为空/非法」(""),给可操作错误。
|
|
230
|
+
* minor-8:typecheckMeta 包进 try(与 IF1 对称,防御 typecheck 未来抛错)。
|
|
231
|
+
*/
|
|
232
|
+
export function parseResourceMetaDetailed(
|
|
233
|
+
content: string,
|
|
234
|
+
kind: ResourceKind,
|
|
235
|
+
): DetailedResult {
|
|
236
|
+
const block = extractBlock(content, kind);
|
|
237
|
+
if (block === undefined) {
|
|
238
|
+
return { ok: false, error: "未找到 meta 块(缺少 /* @pi-meta */ 或 frontmatter,或闭合 */ 不在行首)" };
|
|
239
|
+
}
|
|
240
|
+
let raw: unknown;
|
|
241
|
+
try {
|
|
242
|
+
raw = parseYaml(block);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
// eemeli/yaml YAMLParseError:e.linePos = [{line,col},{line,col}]([P-yaml] 实测)
|
|
245
|
+
const linePos = getYamlLinePos(e);
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
error: e instanceof Error ? e.message.split("\n")[0] : String(e),
|
|
249
|
+
...(linePos !== undefined ? { linePos } : {}),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
const meta = typecheckMeta(raw, kind);
|
|
254
|
+
if (!meta) {
|
|
255
|
+
return { ok: false, error: "meta 类型校验失败(缺 name/description、phases 非法、kind 字段串类或可选字段类型错)" };
|
|
256
|
+
}
|
|
257
|
+
return { ok: true, meta };
|
|
258
|
+
} catch (e) {
|
|
259
|
+
return { ok: false, error: `meta 类型校验异常: ${e instanceof Error ? e.message : String(e)}` };
|
|
260
|
+
}
|
|
261
|
+
}
|