@fitchmultz/pi-subagents 0.0.0-stage → 0.44.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1654 -0
- package/LICENSE +21 -0
- package/README.md +1533 -2
- package/agents/context-builder.md +97 -0
- package/agents/debugger.md +51 -0
- package/agents/delegate.md +13 -0
- package/agents/fixer.md +37 -0
- package/agents/oracle.md +71 -0
- package/agents/planner.md +72 -0
- package/agents/researcher.md +51 -0
- package/agents/reviewer-claude.md +69 -0
- package/agents/reviewer-gpt.md +68 -0
- package/agents/reviewer-ponytail.md +82 -0
- package/agents/reviewer-security.md +81 -0
- package/agents/reviewer.md +69 -0
- package/agents/scout.md +66 -0
- package/agents/ui-designer.md +51 -0
- package/agents/watcher.md +26 -0
- package/agents/worker.md +46 -0
- package/agents/writer.md +36 -0
- package/dist/agents/agent-management.js +923 -0
- package/dist/agents/agent-scope.js +7 -0
- package/dist/agents/agent-selection.js +20 -0
- package/dist/agents/agent-serializer.js +100 -0
- package/dist/agents/agents.js +752 -0
- package/dist/agents/chain-serializer.js +367 -0
- package/dist/agents/frontmatter.js +21 -0
- package/dist/agents/identity.js +29 -0
- package/dist/agents/skills.js +570 -0
- package/dist/extension/build-info.js +1 -0
- package/dist/extension/compact-tools.js +112 -0
- package/dist/extension/config.js +22 -0
- package/dist/extension/control-notices.js +29 -0
- package/dist/extension/doctor.js +143 -0
- package/dist/extension/fanout-child.js +326 -0
- package/dist/extension/index.js +577 -0
- package/dist/extension/schemas.js +416 -0
- package/dist/extension/tool-input.js +20 -0
- package/dist/extension/tool-result.js +14 -0
- package/dist/history/canonical-result.js +81 -0
- package/dist/history/database.js +72 -0
- package/dist/history/index.js +199 -0
- package/dist/history/ingest.js +468 -0
- package/dist/history/queries.js +350 -0
- package/dist/history/store.js +216 -0
- package/dist/history/types.js +4 -0
- package/dist/history/worker.js +466 -0
- package/dist/intercom/intercom-bridge.js +112 -0
- package/dist/intercom/live-intercom.js +94 -0
- package/dist/intercom/result-intercom.js +373 -0
- package/dist/pi-intercom/agent-dir.js +10 -0
- package/dist/pi-intercom/bridge.js +750 -0
- package/dist/pi-intercom/broker/broker.js +426 -0
- package/dist/pi-intercom/broker/client.js +492 -0
- package/dist/pi-intercom/broker/framing.js +65 -0
- package/dist/pi-intercom/broker/paths.js +42 -0
- package/dist/pi-intercom/broker/pid.js +66 -0
- package/dist/pi-intercom/broker/spawn.js +221 -0
- package/dist/pi-intercom/config.js +102 -0
- package/dist/pi-intercom/index.js +2942 -0
- package/dist/pi-intercom/reply-tracker.js +182 -0
- package/dist/pi-intercom/session-targets.js +128 -0
- package/dist/pi-intercom/subagent-live-events.js +132 -0
- package/dist/pi-intercom/topics.js +157 -0
- package/dist/pi-intercom/types.js +163 -0
- package/dist/pi-intercom/ui/compose.js +269 -0
- package/dist/pi-intercom/ui/inline-message.js +80 -0
- package/dist/pi-intercom/ui/session-list.js +124 -0
- package/dist/runs/background/async-control.js +95 -0
- package/dist/runs/background/async-execution.js +676 -0
- package/dist/runs/background/async-job-tracker.js +383 -0
- package/dist/runs/background/async-result-file.js +58 -0
- package/dist/runs/background/async-resume.js +305 -0
- package/dist/runs/background/async-status.js +442 -0
- package/dist/runs/background/completion-dedupe.js +69 -0
- package/dist/runs/background/completion-delivery.js +245 -0
- package/dist/runs/background/notify.js +71 -0
- package/dist/runs/background/parallel-groups.js +55 -0
- package/dist/runs/background/result-watcher.js +426 -0
- package/dist/runs/background/run-id-resolver.js +64 -0
- package/dist/runs/background/run-status.js +382 -0
- package/dist/runs/background/stale-run-reconciler.js +335 -0
- package/dist/runs/background/subagent-runner-launcher.js +55 -0
- package/dist/runs/background/subagent-runner.js +1872 -0
- package/dist/runs/background/top-level-async.js +5 -0
- package/dist/runs/foreground/chain-clarify.js +1152 -0
- package/dist/runs/foreground/clarify-invocation.js +72 -0
- package/dist/runs/foreground/execution-input.js +423 -0
- package/dist/runs/foreground/foreground-control.js +817 -0
- package/dist/runs/foreground/question-control.js +88 -0
- package/dist/runs/foreground/run-async-path.js +205 -0
- package/dist/runs/foreground/subagent-executor.js +442 -0
- package/dist/runs/foreground/subagent-params.js +165 -0
- package/dist/runs/foreground/wait-run.js +141 -0
- package/dist/runs/shared/acceptance-contract.js +253 -0
- package/dist/runs/shared/acceptance-evaluation.js +256 -0
- package/dist/runs/shared/acceptance-finalization.js +280 -0
- package/dist/runs/shared/acceptance-reports.js +147 -0
- package/dist/runs/shared/acceptance.js +4 -0
- package/dist/runs/shared/chain-outputs.js +110 -0
- package/dist/runs/shared/child-attempt.js +693 -0
- package/dist/runs/shared/child-execution-cwd.js +96 -0
- package/dist/runs/shared/child-observations.js +114 -0
- package/dist/runs/shared/claude-code.js +199 -0
- package/dist/runs/shared/completion-guard.js +31 -0
- package/dist/runs/shared/dynamic-fanout.js +274 -0
- package/dist/runs/shared/history-index.js +72 -0
- package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
- package/dist/runs/shared/model-fallback.js +185 -0
- package/dist/runs/shared/mutating-tool-guard.js +170 -0
- package/dist/runs/shared/native-finalization.js +101 -0
- package/dist/runs/shared/native-usage.js +153 -0
- package/dist/runs/shared/nested-events.js +700 -0
- package/dist/runs/shared/nested-path.js +48 -0
- package/dist/runs/shared/nested-render.js +108 -0
- package/dist/runs/shared/parallel-utils.js +61 -0
- package/dist/runs/shared/parent-receipts.js +68 -0
- package/dist/runs/shared/parent-usage.js +121 -0
- package/dist/runs/shared/pi-args.js +310 -0
- package/dist/runs/shared/pi-spawn.js +52 -0
- package/dist/runs/shared/run-history-queries.js +45 -0
- package/dist/runs/shared/run-history.js +110 -0
- package/dist/runs/shared/run-id-ambiguity.js +4 -0
- package/dist/runs/shared/run-records.js +679 -0
- package/dist/runs/shared/session-cwd-preload.js +33 -0
- package/dist/runs/shared/single-output.js +222 -0
- package/dist/runs/shared/streaming-text.js +15 -0
- package/dist/runs/shared/structured-output.js +55 -0
- package/dist/runs/shared/subagent-control.js +179 -0
- package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
- package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
- package/dist/runs/shared/supervisor-questions.js +350 -0
- package/dist/runs/shared/workflow-graph.js +214 -0
- package/dist/runs/shared/workflow-policy.js +84 -0
- package/dist/runs/shared/worktree.js +525 -0
- package/dist/shared/agent-context-policy.js +126 -0
- package/dist/shared/artifacts.js +96 -0
- package/dist/shared/atomic-json.js +13 -0
- package/dist/shared/cooperative.js +20 -0
- package/dist/shared/execution-cwd.js +14 -0
- package/dist/shared/file-coalescer.js +21 -0
- package/dist/shared/fork-context.js +49 -0
- package/dist/shared/formatters.js +85 -0
- package/dist/shared/journal-reader.js +407 -0
- package/dist/shared/lazy-tools.js +32 -0
- package/dist/shared/model-info.js +68 -0
- package/dist/shared/native-session.js +15 -0
- package/dist/shared/native-tui.js +18 -0
- package/dist/shared/native-typebox.js +20 -0
- package/dist/shared/post-exit-stdio-guard.js +197 -0
- package/dist/shared/prompt-sections.js +8 -0
- package/dist/shared/session-entries.js +52 -0
- package/dist/shared/session-identity.js +14 -0
- package/dist/shared/settings.js +254 -0
- package/dist/shared/status-format.js +111 -0
- package/dist/shared/temp-root.js +114 -0
- package/dist/shared/types.js +207 -0
- package/dist/shared/ui-mode.js +3 -0
- package/dist/shared/utils.js +334 -0
- package/dist/slash/prompt-template-bridge.js +298 -0
- package/dist/slash/slash-bridge.js +116 -0
- package/dist/slash/slash-commands.js +607 -0
- package/dist/slash/slash-live-state.js +246 -0
- package/dist/tui/action-hints.js +75 -0
- package/dist/tui/agent-history.js +197 -0
- package/dist/tui/agent-view.js +1656 -0
- package/dist/tui/render.js +1515 -0
- package/docs/intercom-bridge.md +212 -0
- package/docs/intercom.md +581 -0
- package/docs/performance.md +121 -0
- package/package.json +102 -4
- package/prompts/gather-context-and-clarify.md +14 -0
- package/prompts/parallel-cleanup.md +62 -0
- package/prompts/parallel-context-build.md +56 -0
- package/prompts/parallel-handoff-plan.md +62 -0
- package/prompts/parallel-research.md +51 -0
- package/prompts/parallel-review.md +59 -0
- package/prompts/review-loop.md +42 -0
- package/scripts/build.mjs +134 -0
- package/scripts/check-compat.mjs +48 -0
- package/scripts/compat-host.mjs +19 -0
- package/scripts/intercom-bridge-credentials.mjs +143 -0
- package/scripts/intercom-remote.mjs +103 -0
- package/scripts/linux-smoke.sh +51 -0
- package/scripts/local-install-smoke.mjs +66 -0
- package/scripts/native-package-smoke.mjs +82 -0
- package/scripts/package-smoke.mjs +209 -0
- package/scripts/prepare.mjs +67 -0
- package/scripts/real-pi-smoke.mjs +415 -0
- package/scripts/run-tests.mjs +149 -0
- package/skills/pi-intercom/SKILL.md +113 -0
- package/skills/pi-intercom/evals/evals.json +29 -0
- package/skills/pi-intercom/evals/trigger-evals.json +47 -0
- package/skills/pi-intercom/references/peer-sessions.md +69 -0
- package/skills/pi-subagents/SKILL.md +76 -0
- package/skills/pi-subagents/evals/evals.json +29 -0
- package/skills/pi-subagents/evals/trigger-evals.json +42 -0
- package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
- package/src/agents/agent-management.ts +813 -0
- package/src/agents/agent-scope.ts +7 -0
- package/src/agents/agent-selection.ts +23 -0
- package/src/agents/agent-serializer.ts +99 -0
- package/src/agents/agents.ts +916 -0
- package/src/agents/chain-serializer.ts +329 -0
- package/src/agents/frontmatter.ts +21 -0
- package/src/agents/identity.ts +30 -0
- package/src/agents/skills.ts +637 -0
- package/src/extension/build-info.ts +2 -0
- package/src/extension/compact-tools.ts +114 -0
- package/src/extension/config.ts +23 -0
- package/src/extension/control-notices.ts +47 -0
- package/src/extension/doctor.ts +171 -0
- package/src/extension/fanout-child.ts +312 -0
- package/src/extension/index.ts +649 -0
- package/src/extension/schemas.ts +444 -0
- package/src/extension/tool-input.ts +19 -0
- package/src/extension/tool-result.ts +16 -0
- package/src/history/canonical-result.ts +62 -0
- package/src/history/database.ts +59 -0
- package/src/history/index.ts +156 -0
- package/src/history/ingest.ts +349 -0
- package/src/history/queries.ts +241 -0
- package/src/history/store.ts +203 -0
- package/src/history/types.ts +166 -0
- package/src/history/worker.ts +321 -0
- package/src/intercom/intercom-bridge.ts +123 -0
- package/src/intercom/live-intercom.ts +104 -0
- package/src/intercom/result-intercom.ts +429 -0
- package/src/pi-intercom/agent-dir.ts +9 -0
- package/src/pi-intercom/bridge.ts +668 -0
- package/src/pi-intercom/broker/broker.ts +463 -0
- package/src/pi-intercom/broker/client.ts +558 -0
- package/src/pi-intercom/broker/framing.ts +77 -0
- package/src/pi-intercom/broker/paths.ts +47 -0
- package/src/pi-intercom/broker/pid.ts +59 -0
- package/src/pi-intercom/broker/spawn.ts +248 -0
- package/src/pi-intercom/config.ts +142 -0
- package/src/pi-intercom/index.ts +2929 -0
- package/src/pi-intercom/reply-tracker.ts +211 -0
- package/src/pi-intercom/session-targets.ts +172 -0
- package/src/pi-intercom/subagent-live-events.ts +133 -0
- package/src/pi-intercom/topics.ts +125 -0
- package/src/pi-intercom/types.ts +271 -0
- package/src/pi-intercom/ui/compose.ts +289 -0
- package/src/pi-intercom/ui/inline-message.ts +92 -0
- package/src/pi-intercom/ui/session-list.ts +139 -0
- package/src/runs/background/async-control.ts +89 -0
- package/src/runs/background/async-execution.ts +850 -0
- package/src/runs/background/async-job-tracker.ts +385 -0
- package/src/runs/background/async-result-file.ts +62 -0
- package/src/runs/background/async-resume.ts +333 -0
- package/src/runs/background/async-status.ts +481 -0
- package/src/runs/background/completion-dedupe.ts +79 -0
- package/src/runs/background/completion-delivery.ts +195 -0
- package/src/runs/background/notify.ts +123 -0
- package/src/runs/background/parallel-groups.ts +53 -0
- package/src/runs/background/result-watcher.ts +423 -0
- package/src/runs/background/run-id-resolver.ts +69 -0
- package/src/runs/background/run-status.ts +384 -0
- package/src/runs/background/stale-run-reconciler.ts +369 -0
- package/src/runs/background/subagent-runner-launcher.ts +56 -0
- package/src/runs/background/subagent-runner.ts +2053 -0
- package/src/runs/background/top-level-async.ts +13 -0
- package/src/runs/foreground/chain-clarify.ts +1340 -0
- package/src/runs/foreground/clarify-invocation.ts +77 -0
- package/src/runs/foreground/execution-input.ts +467 -0
- package/src/runs/foreground/foreground-control.ts +821 -0
- package/src/runs/foreground/question-control.ts +82 -0
- package/src/runs/foreground/run-async-path.ts +243 -0
- package/src/runs/foreground/subagent-executor.ts +501 -0
- package/src/runs/foreground/subagent-params.ts +320 -0
- package/src/runs/foreground/wait-run.ts +124 -0
- package/src/runs/shared/acceptance-contract.ts +257 -0
- package/src/runs/shared/acceptance-evaluation.ts +275 -0
- package/src/runs/shared/acceptance-finalization.ts +353 -0
- package/src/runs/shared/acceptance-reports.ts +152 -0
- package/src/runs/shared/acceptance.ts +33 -0
- package/src/runs/shared/chain-outputs.ts +114 -0
- package/src/runs/shared/child-attempt.ts +623 -0
- package/src/runs/shared/child-execution-cwd.ts +91 -0
- package/src/runs/shared/child-observations.ts +99 -0
- package/src/runs/shared/claude-code.ts +243 -0
- package/src/runs/shared/completion-guard.ts +35 -0
- package/src/runs/shared/dynamic-fanout.ts +295 -0
- package/src/runs/shared/history-index.ts +69 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
- package/src/runs/shared/model-fallback.ts +219 -0
- package/src/runs/shared/mutating-tool-guard.ts +194 -0
- package/src/runs/shared/native-finalization.ts +128 -0
- package/src/runs/shared/native-usage.ts +113 -0
- package/src/runs/shared/nested-events.ts +752 -0
- package/src/runs/shared/nested-path.ts +52 -0
- package/src/runs/shared/nested-render.ts +119 -0
- package/src/runs/shared/parallel-utils.ts +153 -0
- package/src/runs/shared/parent-receipts.ts +59 -0
- package/src/runs/shared/parent-usage.ts +121 -0
- package/src/runs/shared/pi-args.ts +350 -0
- package/src/runs/shared/pi-spawn.ts +52 -0
- package/src/runs/shared/run-history-queries.ts +44 -0
- package/src/runs/shared/run-history.ts +100 -0
- package/src/runs/shared/run-id-ambiguity.ts +4 -0
- package/src/runs/shared/run-records.ts +607 -0
- package/src/runs/shared/session-cwd-preload.ts +27 -0
- package/src/runs/shared/single-output.ts +267 -0
- package/src/runs/shared/streaming-text.ts +18 -0
- package/src/runs/shared/structured-output.ts +68 -0
- package/src/runs/shared/subagent-control.ts +219 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
- package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
- package/src/runs/shared/supervisor-questions.ts +415 -0
- package/src/runs/shared/workflow-graph.ts +220 -0
- package/src/runs/shared/workflow-policy.ts +125 -0
- package/src/runs/shared/worktree.ts +635 -0
- package/src/shared/agent-context-policy.ts +184 -0
- package/src/shared/artifacts.ts +95 -0
- package/src/shared/atomic-json.ts +16 -0
- package/src/shared/cooperative.ts +21 -0
- package/src/shared/execution-cwd.ts +14 -0
- package/src/shared/file-coalescer.ts +29 -0
- package/src/shared/fork-context.ts +68 -0
- package/src/shared/formatters.ts +84 -0
- package/src/shared/journal-reader.ts +333 -0
- package/src/shared/lazy-tools.ts +36 -0
- package/src/shared/model-info.ts +81 -0
- package/src/shared/native-session.ts +8 -0
- package/src/shared/native-tui.ts +10 -0
- package/src/shared/native-typebox.ts +14 -0
- package/src/shared/post-exit-stdio-guard.ts +204 -0
- package/src/shared/prompt-sections.ts +10 -0
- package/src/shared/session-entries.ts +49 -0
- package/src/shared/session-identity.ts +18 -0
- package/src/shared/settings.ts +384 -0
- package/src/shared/status-format.ts +123 -0
- package/src/shared/temp-root.ts +107 -0
- package/src/shared/types.ts +1260 -0
- package/src/shared/ui-mode.ts +5 -0
- package/src/shared/utils.ts +342 -0
- package/src/slash/prompt-template-bridge.ts +408 -0
- package/src/slash/slash-bridge.ts +178 -0
- package/src/slash/slash-commands.ts +617 -0
- package/src/slash/slash-live-state.ts +292 -0
- package/src/tui/action-hints.ts +69 -0
- package/src/tui/agent-history.ts +210 -0
- package/src/tui/agent-view.ts +1323 -0
- package/src/tui/render.ts +1550 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.js";
|
|
2
|
+
import { parseFrontmatter } from "./frontmatter.js";
|
|
3
|
+
import { ChainOutputValidationError, validateChainOutputBindings } from "../runs/shared/chain-outputs.js";
|
|
4
|
+
import { validateAcceptanceInput } from "../runs/shared/acceptance.js";
|
|
5
|
+
import { Errors, Check } from "../shared/native-typebox.js";
|
|
6
|
+
import { ChainItemSchema } from "../extension/schemas.js";
|
|
7
|
+
function parseStepBody(agent, sectionBody) {
|
|
8
|
+
const lines = sectionBody.split("\n");
|
|
9
|
+
const blankIndex = lines.findIndex((line) => line.trim() === "");
|
|
10
|
+
const configLines = blankIndex === -1 ? lines : lines.slice(0, blankIndex);
|
|
11
|
+
const leadingTaskLines = [];
|
|
12
|
+
const taskLines = blankIndex === -1 ? [] : lines.slice(blankIndex + 1);
|
|
13
|
+
const step = { agent, task: "" };
|
|
14
|
+
let encodedTask;
|
|
15
|
+
for (const line of configLines) {
|
|
16
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
17
|
+
if (!match) {
|
|
18
|
+
leadingTaskLines.push(line);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const key = match[1].trim().toLowerCase();
|
|
22
|
+
const rawValue = match[2].trim();
|
|
23
|
+
if (key === "task-json") {
|
|
24
|
+
if (encodedTask !== undefined)
|
|
25
|
+
throw new Error(`Duplicate task-json for step '${agent}'.`);
|
|
26
|
+
const value = JSON.parse(rawValue);
|
|
27
|
+
if (typeof value !== "string")
|
|
28
|
+
throw new Error(`task-json for step '${agent}' must be a JSON string.`);
|
|
29
|
+
encodedTask = value;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (key === "output") {
|
|
33
|
+
if (rawValue === "false")
|
|
34
|
+
step.output = false;
|
|
35
|
+
else if (rawValue)
|
|
36
|
+
step.output = rawValue;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (key === "phase") {
|
|
40
|
+
if (rawValue)
|
|
41
|
+
step.phase = rawValue;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (key === "label") {
|
|
45
|
+
if (rawValue)
|
|
46
|
+
step.label = rawValue;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (key === "as") {
|
|
50
|
+
if (rawValue)
|
|
51
|
+
step.as = rawValue;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (key === "outputschema") {
|
|
55
|
+
if (rawValue.startsWith("{") || rawValue.startsWith("[")) {
|
|
56
|
+
throw new Error("Inline outputSchema values are not supported in .chain.md files; use a schema file path.");
|
|
57
|
+
}
|
|
58
|
+
if (rawValue)
|
|
59
|
+
step.outputSchema = rawValue;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (key === "outputmode") {
|
|
63
|
+
if (rawValue !== "inline" && rawValue !== "file-only")
|
|
64
|
+
throw new Error(`Invalid outputMode '${rawValue}' for step '${agent}'.`);
|
|
65
|
+
step.outputMode = rawValue;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (key === "reads") {
|
|
69
|
+
if (rawValue === "false") {
|
|
70
|
+
step.reads = false;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const reads = rawValue
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((v) => v.trim())
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
step.reads = reads.length > 0 ? reads : false;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (key === "model") {
|
|
82
|
+
if (rawValue)
|
|
83
|
+
step.model = rawValue;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (key === "skills") {
|
|
87
|
+
if (rawValue === "false") {
|
|
88
|
+
step.skills = false;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const skills = rawValue
|
|
92
|
+
.split(",")
|
|
93
|
+
.map((v) => v.trim())
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
step.skills = skills.length > 0 ? skills : false;
|
|
96
|
+
}
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (key === "progress") {
|
|
100
|
+
if (rawValue !== "true" && rawValue !== "false")
|
|
101
|
+
throw new Error(`Invalid progress '${rawValue}' for step '${agent}'; use true or false.`);
|
|
102
|
+
step.progress = rawValue === "true";
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
leadingTaskLines.push(line);
|
|
106
|
+
}
|
|
107
|
+
const proseTask = [...leadingTaskLines, ...taskLines].join("\n").trim();
|
|
108
|
+
if (encodedTask !== undefined && proseTask)
|
|
109
|
+
throw new Error(`Step '${agent}' cannot combine task-json with task prose.`);
|
|
110
|
+
step.task = encodedTask ?? proseTask;
|
|
111
|
+
return step;
|
|
112
|
+
}
|
|
113
|
+
export function parseChain(content, source, filePath) {
|
|
114
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
115
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
116
|
+
throw new Error("Chain frontmatter must include name and description");
|
|
117
|
+
}
|
|
118
|
+
const matches = [...body.matchAll(/^##\s+(.+)[^\S\n]*$/gm)];
|
|
119
|
+
const steps = [];
|
|
120
|
+
for (let i = 0; i < matches.length; i++) {
|
|
121
|
+
const match = matches[i];
|
|
122
|
+
const agent = match[1].trim();
|
|
123
|
+
const lineEndOffset = body[match.index + match[0].length] === "\n" ? 1 : 0;
|
|
124
|
+
const sectionStart = match.index + match[0].length + lineEndOffset;
|
|
125
|
+
const sectionEnd = i + 1 < matches.length ? matches[i + 1].index : body.length;
|
|
126
|
+
const sectionBody = body.slice(sectionStart, sectionEnd).trimEnd();
|
|
127
|
+
steps.push(parseStepBody(agent, sectionBody));
|
|
128
|
+
}
|
|
129
|
+
if (steps.length === 0)
|
|
130
|
+
throw new Error("Chain must include at least one ## agent step.");
|
|
131
|
+
const localName = frontmatter.name;
|
|
132
|
+
const parsedPackage = parsePackageName(frontmatter.package, `Chain '${localName}' package`);
|
|
133
|
+
if (parsedPackage.error)
|
|
134
|
+
throw new Error(parsedPackage.error);
|
|
135
|
+
const packageName = parsedPackage.packageName;
|
|
136
|
+
const extraFields = {};
|
|
137
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
138
|
+
if (key === "name" || key === "package" || key === "description")
|
|
139
|
+
continue;
|
|
140
|
+
extraFields[key] = value;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
name: buildRuntimeName(localName, packageName),
|
|
144
|
+
localName,
|
|
145
|
+
packageName,
|
|
146
|
+
description: frontmatter.description,
|
|
147
|
+
source,
|
|
148
|
+
filePath,
|
|
149
|
+
steps,
|
|
150
|
+
extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const JSON_CHAIN_STEP_KEYS = new Set(["agent", "task", "phase", "label", "as", "outputSchema", "cwd", "output", "outputMode", "reads", "progress", "skill", "model", "acceptance", "parallel", "expand", "collect", "concurrency", "failFast", "worktree"]);
|
|
154
|
+
const JSON_PARALLEL_TASK_KEYS = new Set(["agent", "task", "phase", "label", "as", "outputSchema", "cwd", "count", "output", "outputMode", "reads", "progress", "skill", "model", "acceptance"]);
|
|
155
|
+
export function parseJsonChain(content, source, filePath) {
|
|
156
|
+
let parsed;
|
|
157
|
+
try {
|
|
158
|
+
parsed = JSON.parse(content);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
+
throw new Error(`Invalid JSON chain '${filePath}': ${message}`);
|
|
163
|
+
}
|
|
164
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
165
|
+
throw new Error(`JSON chain '${filePath}' must contain an object root.`);
|
|
166
|
+
}
|
|
167
|
+
const input = parsed;
|
|
168
|
+
if (typeof input.name !== "string" || !input.name.trim()) {
|
|
169
|
+
throw new Error(`JSON chain '${filePath}' must include string name.`);
|
|
170
|
+
}
|
|
171
|
+
if (typeof input.description !== "string" || !input.description.trim()) {
|
|
172
|
+
throw new Error(`JSON chain '${filePath}' must include string description.`);
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(input.chain) || input.chain.length === 0) {
|
|
175
|
+
throw new Error(`JSON chain '${filePath}' must include a non-empty array chain.`);
|
|
176
|
+
}
|
|
177
|
+
for (let i = 0; i < input.chain.length; i++) {
|
|
178
|
+
const step = input.chain[i];
|
|
179
|
+
if (!step || typeof step !== "object" || Array.isArray(step)) {
|
|
180
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} must be an object.`);
|
|
181
|
+
}
|
|
182
|
+
const stepRecord = step;
|
|
183
|
+
const unknownKeys = Object.keys(stepRecord).filter((key) => !JSON_CHAIN_STEP_KEYS.has(key));
|
|
184
|
+
if (unknownKeys.length)
|
|
185
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} has unknown field${unknownKeys.length === 1 ? "" : "s"}: ${unknownKeys.join(", ")}.`);
|
|
186
|
+
const parallel = stepRecord.parallel;
|
|
187
|
+
if (parallel !== undefined && stepRecord.agent !== undefined)
|
|
188
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} cannot set both agent and parallel.`);
|
|
189
|
+
if (parallel === undefined) {
|
|
190
|
+
if (typeof stepRecord.agent !== "string" || !stepRecord.agent.trim())
|
|
191
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} must include a non-empty agent.`);
|
|
192
|
+
}
|
|
193
|
+
else if (Array.isArray(parallel)) {
|
|
194
|
+
if (parallel.length === 0)
|
|
195
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} parallel must not be empty.`);
|
|
196
|
+
for (let taskIndex = 0; taskIndex < parallel.length; taskIndex++) {
|
|
197
|
+
const task = parallel[taskIndex];
|
|
198
|
+
if (!task || typeof task !== "object" || Array.isArray(task) || typeof task.agent !== "string" || !task.agent.trim()) {
|
|
199
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} parallel task ${taskIndex + 1} must include a non-empty agent.`);
|
|
200
|
+
}
|
|
201
|
+
const taskUnknownKeys = Object.keys(task).filter((key) => !JSON_PARALLEL_TASK_KEYS.has(key));
|
|
202
|
+
if (taskUnknownKeys.length)
|
|
203
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} parallel task ${taskIndex + 1} has unknown field${taskUnknownKeys.length === 1 ? "" : "s"}: ${taskUnknownKeys.join(", ")}.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (typeof parallel === "object" && parallel !== null) {
|
|
207
|
+
if (typeof parallel.agent !== "string" || !parallel.agent.trim())
|
|
208
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} dynamic template must include a non-empty agent.`);
|
|
209
|
+
if (!stepRecord.expand || typeof stepRecord.expand !== "object" || !stepRecord.collect || typeof stepRecord.collect !== "object")
|
|
210
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} dynamic fanout requires expand and collect objects.`);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} parallel must be an array or object.`);
|
|
214
|
+
}
|
|
215
|
+
if (stepRecord.concurrency !== undefined && (typeof stepRecord.concurrency !== "number" || !Number.isInteger(stepRecord.concurrency) || stepRecord.concurrency < 1))
|
|
216
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} concurrency must be an integer >= 1.`);
|
|
217
|
+
if (Array.isArray(parallel) && Object.hasOwn(stepRecord, "acceptance")) {
|
|
218
|
+
throw new Error(`Invalid JSON chain '${filePath}': step ${i + 1} acceptance is not supported on static parallel groups; set acceptance on each parallel task.`);
|
|
219
|
+
}
|
|
220
|
+
if (parallel && typeof parallel === "object" && !Array.isArray(parallel) && Object.hasOwn(stepRecord, "acceptance")) {
|
|
221
|
+
throw new Error(`Invalid JSON chain '${filePath}': step ${i + 1} acceptance is not supported on dynamic fanout groups; set acceptance on the dynamic template.`);
|
|
222
|
+
}
|
|
223
|
+
const acceptanceErrors = validateAcceptanceInput(stepRecord.acceptance, `step ${i + 1} acceptance`);
|
|
224
|
+
if (acceptanceErrors.length > 0) {
|
|
225
|
+
throw new Error(`Invalid JSON chain '${filePath}': ${acceptanceErrors.join(" ")}`);
|
|
226
|
+
}
|
|
227
|
+
if (Array.isArray(parallel)) {
|
|
228
|
+
for (let taskIndex = 0; taskIndex < parallel.length; taskIndex++) {
|
|
229
|
+
const task = parallel[taskIndex];
|
|
230
|
+
if (!task || typeof task !== "object" || Array.isArray(task))
|
|
231
|
+
continue;
|
|
232
|
+
const taskErrors = validateAcceptanceInput(task.acceptance, `step ${i + 1} parallel task ${taskIndex + 1} acceptance`);
|
|
233
|
+
if (taskErrors.length > 0) {
|
|
234
|
+
throw new Error(`Invalid JSON chain '${filePath}': ${taskErrors.join(" ")}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else if (parallel && typeof parallel === "object") {
|
|
239
|
+
const templateErrors = validateAcceptanceInput(parallel.acceptance, `step ${i + 1} dynamic template acceptance`);
|
|
240
|
+
if (templateErrors.length > 0) {
|
|
241
|
+
throw new Error(`Invalid JSON chain '${filePath}': ${templateErrors.join(" ")}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
validateChainOutputBindings(input.chain, { maxItems: Number.MAX_SAFE_INTEGER });
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (error instanceof ChainOutputValidationError)
|
|
250
|
+
throw new Error(`Invalid JSON chain '${filePath}': ${error.message}`);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
for (let i = 0; i < input.chain.length; i++) {
|
|
254
|
+
const step = input.chain[i];
|
|
255
|
+
if (!Check(ChainItemSchema, step)) {
|
|
256
|
+
const issue = [...Errors(ChainItemSchema, step)][0];
|
|
257
|
+
throw new Error(`JSON chain '${filePath}' step ${i + 1} is invalid${issue ? `: ${issue.message}` : ""}.`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const parsedPackage = parsePackageName(typeof input.package === "string" ? input.package : undefined, `Chain '${input.name}' package`);
|
|
261
|
+
if (parsedPackage.error)
|
|
262
|
+
throw new Error(parsedPackage.error);
|
|
263
|
+
const extraFields = {};
|
|
264
|
+
for (const [key, value] of Object.entries(input)) {
|
|
265
|
+
if (key === "name" || key === "package" || key === "description" || key === "chain")
|
|
266
|
+
continue;
|
|
267
|
+
if (typeof value === "string")
|
|
268
|
+
extraFields[key] = value;
|
|
269
|
+
}
|
|
270
|
+
const steps = input.chain.map((step) => {
|
|
271
|
+
if (!Object.hasOwn(step, "skill"))
|
|
272
|
+
return step;
|
|
273
|
+
const { skill, ...normalized } = step;
|
|
274
|
+
return {
|
|
275
|
+
...normalized,
|
|
276
|
+
skills: typeof skill === "string" ? [skill] : skill,
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
name: buildRuntimeName(input.name.trim(), parsedPackage.packageName),
|
|
281
|
+
localName: input.name.trim(),
|
|
282
|
+
packageName: parsedPackage.packageName,
|
|
283
|
+
description: input.description.trim(),
|
|
284
|
+
source,
|
|
285
|
+
filePath,
|
|
286
|
+
steps,
|
|
287
|
+
extraFields: Object.keys(extraFields).length > 0 ? extraFields : undefined,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
export function serializeJsonChain(config) {
|
|
291
|
+
const chain = config.steps.map((step) => {
|
|
292
|
+
const { skills, ...serialized } = step;
|
|
293
|
+
return skills === undefined ? serialized : { ...serialized, skill: skills };
|
|
294
|
+
});
|
|
295
|
+
const root = {
|
|
296
|
+
name: frontmatterNameForConfig(config),
|
|
297
|
+
description: config.description,
|
|
298
|
+
chain,
|
|
299
|
+
};
|
|
300
|
+
if (config.packageName)
|
|
301
|
+
root.package = config.packageName;
|
|
302
|
+
if (config.extraFields) {
|
|
303
|
+
for (const [key, value] of Object.entries(config.extraFields)) {
|
|
304
|
+
if (key !== "name" && key !== "description" && key !== "package" && key !== "chain")
|
|
305
|
+
root[key] = value;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return `${JSON.stringify(root, null, 2)}\n`;
|
|
309
|
+
}
|
|
310
|
+
export function serializeChain(config) {
|
|
311
|
+
const lines = [];
|
|
312
|
+
lines.push("---");
|
|
313
|
+
lines.push(`name: ${frontmatterNameForConfig(config)}`);
|
|
314
|
+
if (config.packageName)
|
|
315
|
+
lines.push(`package: ${config.packageName}`);
|
|
316
|
+
lines.push(`description: ${config.description}`);
|
|
317
|
+
if (config.extraFields) {
|
|
318
|
+
for (const [key, value] of Object.entries(config.extraFields)) {
|
|
319
|
+
lines.push(`${key}: ${value}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
lines.push("---");
|
|
323
|
+
lines.push("");
|
|
324
|
+
for (let i = 0; i < config.steps.length; i++) {
|
|
325
|
+
const step = config.steps[i];
|
|
326
|
+
lines.push(`## ${step.agent}`);
|
|
327
|
+
if (step.output === false)
|
|
328
|
+
lines.push("output: false");
|
|
329
|
+
else if (step.output)
|
|
330
|
+
lines.push(`output: ${step.output}`);
|
|
331
|
+
if (step.phase)
|
|
332
|
+
lines.push(`phase: ${step.phase}`);
|
|
333
|
+
if (step.label)
|
|
334
|
+
lines.push(`label: ${step.label}`);
|
|
335
|
+
if (step.as)
|
|
336
|
+
lines.push(`as: ${step.as}`);
|
|
337
|
+
if (step.outputSchema)
|
|
338
|
+
lines.push(`outputSchema: ${step.outputSchema}`);
|
|
339
|
+
if (step.outputMode)
|
|
340
|
+
lines.push(`outputMode: ${step.outputMode}`);
|
|
341
|
+
if (step.reads === false)
|
|
342
|
+
lines.push("reads: false");
|
|
343
|
+
else if (Array.isArray(step.reads) && step.reads.length > 0)
|
|
344
|
+
lines.push(`reads: ${step.reads.join(", ")}`);
|
|
345
|
+
if (step.model)
|
|
346
|
+
lines.push(`model: ${step.model}`);
|
|
347
|
+
if (step.skills === false)
|
|
348
|
+
lines.push("skills: false");
|
|
349
|
+
else if (Array.isArray(step.skills) && step.skills.length > 0)
|
|
350
|
+
lines.push(`skills: ${step.skills.join(", ")}`);
|
|
351
|
+
if (step.progress !== undefined)
|
|
352
|
+
lines.push(`progress: ${step.progress ? "true" : "false"}`);
|
|
353
|
+
const task = step.task ?? "";
|
|
354
|
+
if (/^##\s+/m.test(task)) {
|
|
355
|
+
// JSON leaves these separators raw, but the Markdown header regex treats them as line breaks.
|
|
356
|
+
const encoded = JSON.stringify(task).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
357
|
+
lines.push(`task-json: ${encoded}`);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
lines.push("");
|
|
361
|
+
lines.push(task);
|
|
362
|
+
}
|
|
363
|
+
if (i < config.steps.length - 1)
|
|
364
|
+
lines.push("");
|
|
365
|
+
}
|
|
366
|
+
return `${lines.join("\n")}\n`;
|
|
367
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function parseFrontmatter(content) {
|
|
2
|
+
const frontmatter = {};
|
|
3
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
4
|
+
const lines = normalized.split("\n");
|
|
5
|
+
if (!/^---\s*$/.test(lines[0] ?? ""))
|
|
6
|
+
return { frontmatter, body: normalized };
|
|
7
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && /^---\s*$/.test(line));
|
|
8
|
+
if (endIndex === -1)
|
|
9
|
+
return { frontmatter, body: normalized };
|
|
10
|
+
for (const line of lines.slice(1, endIndex)) {
|
|
11
|
+
const match = line.match(/^([\w-]+):\s*(.*)$/);
|
|
12
|
+
if (!match)
|
|
13
|
+
continue;
|
|
14
|
+
let value = match[2].trim();
|
|
15
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
16
|
+
value = value.slice(1, -1);
|
|
17
|
+
}
|
|
18
|
+
frontmatter[match[1]] = value;
|
|
19
|
+
}
|
|
20
|
+
return { frontmatter, body: lines.slice(endIndex + 1).join("\n").trim() };
|
|
21
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/;
|
|
2
|
+
function normalizePackageName(value) {
|
|
3
|
+
const trimmed = value?.trim();
|
|
4
|
+
if (!trimmed)
|
|
5
|
+
return undefined;
|
|
6
|
+
return trimmed.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9.-]/g, "").replace(/-+/g, "-").replace(/\.+/g, ".").replace(/(?:^[-.]+|[-.]+$)/g, "");
|
|
7
|
+
}
|
|
8
|
+
export function parsePackageName(value, label = "package") {
|
|
9
|
+
if (value === undefined || value === false || value === "")
|
|
10
|
+
return { packageName: undefined };
|
|
11
|
+
if (typeof value !== "string")
|
|
12
|
+
return { error: `${label} must be a string or false when provided.` };
|
|
13
|
+
const packageName = normalizePackageName(value);
|
|
14
|
+
if (!packageName || !IDENTIFIER_PATTERN.test(packageName))
|
|
15
|
+
return { error: `${label} is invalid after sanitization.` };
|
|
16
|
+
return { packageName };
|
|
17
|
+
}
|
|
18
|
+
export function buildRuntimeName(localName, packageName) {
|
|
19
|
+
const trimmedPackage = packageName?.trim();
|
|
20
|
+
return trimmedPackage ? `${trimmedPackage}.${localName}` : localName;
|
|
21
|
+
}
|
|
22
|
+
export function frontmatterNameForConfig(config) {
|
|
23
|
+
if (config.localName)
|
|
24
|
+
return config.localName;
|
|
25
|
+
if (config.packageName && config.name.startsWith(`${config.packageName}.`)) {
|
|
26
|
+
return config.name.slice(config.packageName.length + 1);
|
|
27
|
+
}
|
|
28
|
+
return config.name;
|
|
29
|
+
}
|