@developerz.ai/aitm 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-config/agent-config-detector.js +0 -3
- package/dist/cli/args.js +0 -10
- package/dist/cli/cli.js +0 -8
- package/dist/cli/commands.js +0 -46
- package/dist/compaction/compactor.js +0 -24
- package/dist/config/config-loader.js +0 -29
- package/dist/config/config-writer.js +0 -5
- package/dist/config/schema.js +0 -12
- package/dist/credentials/credentials.js +0 -14
- package/dist/credentials/defaults.js +0 -15
- package/dist/fs/atomic-write.js +0 -7
- package/dist/github/errors.js +0 -2
- package/dist/github/github-client.js +0 -21
- package/dist/github/schema.js +0 -2
- package/dist/index.js +0 -2
- package/dist/logger/logger.js +0 -6
- package/dist/loop/run-loop-adapter.js +0 -45
- package/dist/loop/take-over-flow.js +0 -37
- package/dist/loop/work-loop.js +0 -36
- package/dist/mcp/mcp-client.js +0 -13
- package/dist/mcp/schema.js +0 -15
- package/dist/openrouter/client.js +0 -4
- package/dist/openrouter/model-limits.js +0 -4
- package/dist/openrouter/server-tools.js +0 -16
- package/dist/orchestrator/orchestrator.js +0 -26
- package/dist/orchestrator/subagent-tools.js +0 -22
- package/dist/orchestrator/system-prompts.js +0 -3
- package/dist/plan/plan-graph.js +0 -9
- package/dist/plan/schema.js +0 -8
- package/dist/state/schema.js +0 -5
- package/dist/state/state-store.js +0 -8
- package/dist/subagents/factory.js +0 -9
- package/dist/subagents/planner.js +0 -9
- package/dist/subagents/reviewer.js +0 -25
- package/dist/subagents/worker.js +0 -27
- package/dist/testing/temp-repo.js +0 -4
- package/dist/tools/datetime.js +0 -9
- package/dist/tools/fetch-html.js +0 -24
- package/dist/tools/github-thread-tool.js +0 -15
- package/dist/tools/web-fetch.js +0 -32
- package/dist/workspace/worktree-pool.js +0 -21
- package/package.json +2 -2
- package/dist/agent-config/agent-config-detector.js.map +0 -1
- package/dist/cli/args.js.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/commands.js.map +0 -1
- package/dist/compaction/compactor.js.map +0 -1
- package/dist/config/config-loader.js.map +0 -1
- package/dist/config/config-writer.js.map +0 -1
- package/dist/config/schema.js.map +0 -1
- package/dist/credentials/credentials.js.map +0 -1
- package/dist/credentials/defaults.js.map +0 -1
- package/dist/fs/atomic-write.js.map +0 -1
- package/dist/github/errors.js.map +0 -1
- package/dist/github/github-client.js.map +0 -1
- package/dist/github/schema.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/logger.js.map +0 -1
- package/dist/loop/run-loop-adapter.js.map +0 -1
- package/dist/loop/take-over-flow.js.map +0 -1
- package/dist/loop/work-loop.js.map +0 -1
- package/dist/mcp/mcp-client.js.map +0 -1
- package/dist/mcp/schema.js.map +0 -1
- package/dist/openrouter/client.js.map +0 -1
- package/dist/openrouter/model-limits.js.map +0 -1
- package/dist/openrouter/server-tools.js.map +0 -1
- package/dist/orchestrator/orchestrator.js.map +0 -1
- package/dist/orchestrator/subagent-tools.js.map +0 -1
- package/dist/orchestrator/system-prompts.js.map +0 -1
- package/dist/plan/plan-graph.js.map +0 -1
- package/dist/plan/schema.js.map +0 -1
- package/dist/state/schema.js.map +0 -1
- package/dist/state/state-store.js.map +0 -1
- package/dist/subagents/factory.js.map +0 -1
- package/dist/subagents/planner.js.map +0 -1
- package/dist/subagents/reviewer.js.map +0 -1
- package/dist/subagents/worker.js.map +0 -1
- package/dist/testing/temp-repo.js.map +0 -1
- package/dist/tools/datetime.js.map +0 -1
- package/dist/tools/fetch-html.js.map +0 -1
- package/dist/tools/github-thread-tool.js.map +0 -1
- package/dist/tools/web-fetch.js.map +0 -1
- package/dist/workspace/worktree-pool.js.map +0 -1
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
// Production wiring for `aitm start`. Composes the WorkLoop's structural ports out of the
|
|
2
|
-
// real Planner, Orchestrator, WorktreePool, PlanGraph, MCP tools and GitHubClient.
|
|
3
|
-
//
|
|
4
|
-
// Symmetric counterpart to the merge-pr flow: `runStart` injects this as its `runLoop` seam
|
|
5
|
-
// (see src/cli/commands.ts `defaultRunLoop`). Every external dependency is reachable through
|
|
6
|
-
// a seam so the four WorkLoopResult branches are unit-testable without spawning subagents,
|
|
7
|
-
// git, or `gh` — the integration suite (test/integration/) covers the real stack.
|
|
8
|
-
//
|
|
9
|
-
// Flow:
|
|
10
|
-
// 1. Resume detection — if state already holds prGroups, reuse them; else run the Planner.
|
|
11
|
-
// 2. Persist the plan into RunState (status → working).
|
|
12
|
-
// 3. Build a *live* PlanGraph that re-reads the mirrored prGroups on every ready()/isComplete()
|
|
13
|
-
// so status transitions written by the loop are visible (PlanGraph snapshots at construction).
|
|
14
|
-
// 4. Bridge the Orchestrator/subagents into the WorkLoopOrchestrator port and run the loop.
|
|
15
1
|
import { resolve as resolvePath } from 'node:path';
|
|
16
2
|
import { bashTool, composeSystemPrompt, editFileTool, globTool, grepTool, multiEditTool, readFileTool, writeFileTool, } from '@developerz.ai/ai-claude-compat';
|
|
17
3
|
import { tool } from 'ai';
|
|
@@ -25,10 +11,6 @@ import { createReviewerAgent, REVIEWER_SYSTEM_PREFIX, runReviewer as runReviewer
|
|
|
25
11
|
import { createWorkerAgent, runWorker as runWorkerSubagent, WORKER_SYSTEM_PREFIX, } from "../subagents/worker.js";
|
|
26
12
|
import { WorktreePool } from "../workspace/worktree-pool.js";
|
|
27
13
|
import { WorkLoop, } from "./work-loop.js";
|
|
28
|
-
// Worktree-scoped Claude-Code-style tools the Worker/Reviewer fall back to when no MCP server
|
|
29
|
-
// supplies them. aitm is MCP-first, but a bare `aitm start` (no `mcpServers` configured) must
|
|
30
|
-
// still be able to read, search, edit, commit and open a PR — so it uses the compat lib's
|
|
31
|
-
// tools, scoped to the active worktree.
|
|
32
14
|
export function localEditTools(cwd) {
|
|
33
15
|
return {
|
|
34
16
|
readFile: readFileTool({ cwd }),
|
|
@@ -40,7 +22,6 @@ export function localEditTools(cwd) {
|
|
|
40
22
|
bash: bashTool({ cwd }),
|
|
41
23
|
};
|
|
42
24
|
}
|
|
43
|
-
// Read-only subset for the Planner — survey the repo without write/edit/bash.
|
|
44
25
|
export function localReadTools(cwd) {
|
|
45
26
|
return {
|
|
46
27
|
readFile: readFileTool({ cwd }),
|
|
@@ -50,8 +31,6 @@ export function localReadTools(cwd) {
|
|
|
50
31
|
}
|
|
51
32
|
export async function runLoopAdapter(input, seams = {}) {
|
|
52
33
|
const state = seams.state ?? input.state;
|
|
53
|
-
// MCP is only needed when a real Planner / Orchestrator default runs. When both are stubbed
|
|
54
|
-
// (unit tests), we never connect, so no transport is spawned.
|
|
55
34
|
const usesMcp = !seams.planGroups || !seams.makeOrchestrator;
|
|
56
35
|
const mcp = seams.makeMcp
|
|
57
36
|
? seams.makeMcp(input)
|
|
@@ -64,7 +43,6 @@ export async function runLoopAdapter(input, seams = {}) {
|
|
|
64
43
|
}
|
|
65
44
|
const current = await state.read();
|
|
66
45
|
const rollingContext = (await state.readContext?.()) ?? '';
|
|
67
|
-
// ---- Plan (fresh) or resume (prior prGroups present) -------------------
|
|
68
46
|
let groups;
|
|
69
47
|
if (current.prGroups.length > 0) {
|
|
70
48
|
groups = current.prGroups;
|
|
@@ -84,9 +62,6 @@ export async function runLoopAdapter(input, seams = {}) {
|
|
|
84
62
|
groups = outcome.groups;
|
|
85
63
|
await state.update((s) => ({ ...s, status: 'working', prGroups: groups }));
|
|
86
64
|
}
|
|
87
|
-
// ---- Live graph + state proxy ------------------------------------------
|
|
88
|
-
// PlanGraph captures its groups at construction, so rebuild it per call against the
|
|
89
|
-
// mirror that workLoopState keeps in sync after every persisted update.
|
|
90
65
|
let liveGroups = groups;
|
|
91
66
|
const graph = {
|
|
92
67
|
ready: () => new PlanGraph([...liveGroups]).ready(),
|
|
@@ -99,7 +74,6 @@ export async function runLoopAdapter(input, seams = {}) {
|
|
|
99
74
|
return next;
|
|
100
75
|
},
|
|
101
76
|
};
|
|
102
|
-
// ---- Remaining deps ----------------------------------------------------
|
|
103
77
|
const pool = seams.makePool?.(input) ??
|
|
104
78
|
new WorktreePool(input.cwd, resolvePath(input.cwd, '.ai-task-master'), input.resolved.concurrency);
|
|
105
79
|
const github = seams.makeGithub?.(input) ?? input.github;
|
|
@@ -128,7 +102,6 @@ export async function runLoopAdapter(input, seams = {}) {
|
|
|
128
102
|
}
|
|
129
103
|
}
|
|
130
104
|
}
|
|
131
|
-
// ---- Plan ------------------------------------------------------------------
|
|
132
105
|
export function planToPrGroups(plan) {
|
|
133
106
|
return plan.groups.map((g) => ({
|
|
134
107
|
id: g.id,
|
|
@@ -159,7 +132,6 @@ async function defaultPlanGroups(input, mcp) {
|
|
|
159
132
|
return { kind: 'blocked', reason: result.reason };
|
|
160
133
|
return { kind: 'error', error: result.error };
|
|
161
134
|
}
|
|
162
|
-
// ---- Orchestrator bridge ---------------------------------------------------
|
|
163
135
|
function defaultMakeOrchestrator(ctx) {
|
|
164
136
|
const { input, mcp, rollingContext } = ctx;
|
|
165
137
|
const style = input.agentConfig.contents;
|
|
@@ -172,8 +144,6 @@ function defaultMakeOrchestrator(ctx) {
|
|
|
172
144
|
});
|
|
173
145
|
return {
|
|
174
146
|
runWorker: async ({ group, worktree, baseBranch }) => {
|
|
175
|
-
// Prefer MCP-supplied tools; partial-fill any the server omits from the local set so a
|
|
176
|
-
// bare `aitm start` (no mcpServers configured) can still edit, commit and open a PR.
|
|
177
147
|
const tools = resolveWorkerTools(mcp.toolsForRole('worker'), worktree.path);
|
|
178
148
|
const agent = createWorkerAgent({
|
|
179
149
|
model: input.credentials.modelFor('worker'),
|
|
@@ -190,16 +160,12 @@ function defaultMakeOrchestrator(ctx) {
|
|
|
190
160
|
},
|
|
191
161
|
finalizeCommit: (group, delivery, worktreePath) => orch.finalizeCommit(group, delivery, worktreePath),
|
|
192
162
|
openPr: async (group, delivery, baseBranch) => {
|
|
193
|
-
// The Worker's commits live on the group branch in a linked worktree (shared object
|
|
194
|
-
// store). Push it to origin first — `gh pr create` won't open a PR for a branch that
|
|
195
|
-
// isn't on the remote ("No commits between … / Head ref must be a branch").
|
|
196
163
|
const head = group.branch ?? `aitm/${group.id}`;
|
|
197
164
|
await execa('git', ['push', '-u', 'origin', head], { cwd: input.cwd });
|
|
198
165
|
return orch.openPr(group, delivery, baseBranch);
|
|
199
166
|
},
|
|
200
167
|
runReviewer: async ({ pr, threads, worktree }) => {
|
|
201
168
|
const github = githubThreadTool(input.github);
|
|
202
|
-
// Same partial-fill as the Worker, plus the local `github` thread tool.
|
|
203
169
|
const tools = resolveReviewerTools(mcp.toolsForRole('reviewer'), worktree.path, github);
|
|
204
170
|
const agent = createReviewerAgent({
|
|
205
171
|
model: input.credentials.modelFor('reviewer'),
|
|
@@ -215,10 +181,6 @@ function defaultMakeOrchestrator(ctx) {
|
|
|
215
181
|
},
|
|
216
182
|
};
|
|
217
183
|
}
|
|
218
|
-
// MCP exposes a dynamically-typed ToolSet. The subagents need a statically-shaped tool record.
|
|
219
|
-
// Rather than fail closed when a server only exports the legacy readFile/writeFile/bash, we
|
|
220
|
-
// partial-fill: prefer the MCP tool for each name, falling back to the local worktree-scoped
|
|
221
|
-
// tool for any the server omits. The shape is asserted once at this boundary.
|
|
222
184
|
function resolveWorkerTools(set, cwd) {
|
|
223
185
|
const local = localEditTools(cwd);
|
|
224
186
|
return {
|
|
@@ -234,10 +196,6 @@ function resolveWorkerTools(set, cwd) {
|
|
|
234
196
|
function resolveReviewerTools(set, cwd, github) {
|
|
235
197
|
return { ...resolveWorkerTools(set, cwd), github };
|
|
236
198
|
}
|
|
237
|
-
// The Planner gets only the read-only subset, partial-filled the same way. This is also the fix
|
|
238
|
-
// for the latent no-MCP bug: previously the Planner was handed the raw MCP ToolSet with no local
|
|
239
|
-
// fallback, so a bare `aitm start` left it with zero tools despite its prompt promising
|
|
240
|
-
// readFile/grep/glob.
|
|
241
199
|
function resolvePlannerTools(set, cwd) {
|
|
242
200
|
const local = localReadTools(cwd);
|
|
243
201
|
return {
|
|
@@ -246,8 +204,6 @@ function resolvePlannerTools(set, cwd) {
|
|
|
246
204
|
glob: set.glob ?? local.glob,
|
|
247
205
|
};
|
|
248
206
|
}
|
|
249
|
-
// Flat object, not a discriminatedUnion → no `oneOf` in the tool params (rejected by some
|
|
250
|
-
// OpenRouter-routed providers). `body` applies to replyToThread only.
|
|
251
207
|
const githubToolInputSchema = z.object({
|
|
252
208
|
action: z.enum(['replyToThread', 'resolveThread']),
|
|
253
209
|
threadId: z.string().min(1),
|
|
@@ -267,4 +223,3 @@ export function githubThreadTool(github) {
|
|
|
267
223
|
},
|
|
268
224
|
});
|
|
269
225
|
}
|
|
270
|
-
//# sourceMappingURL=run-loop-adapter.js.map
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
// Take-over merge flow. Drives an externally-built PR (Claude Code, gh pr create, etc.)
|
|
2
|
-
// to merge: waits for CI, runs Reviewer to address unresolved review threads (CodeRabbit
|
|
3
|
-
// + human reviewers), pushes fixes, and merges. Mirrors the claude-task-master
|
|
4
|
-
// `merge_pr()` shape from src/claude_task_master/cli_commands/fix_pr.py:
|
|
5
|
-
//
|
|
6
|
-
// for iteration in 0..maxIterations:
|
|
7
|
-
// status = waitForChecks(pr)
|
|
8
|
-
// threads = listUnresolvedThreads(pr)
|
|
9
|
-
// if status == success and threads.empty: break
|
|
10
|
-
// if status == failure: runWorker (CI-fix path, optional)
|
|
11
|
-
// if threads.any: runReviewer per thread, push commits
|
|
12
|
-
// sleep(cooldown) # let CI restart
|
|
13
|
-
// mergePr(pr)
|
|
14
|
-
//
|
|
15
|
-
// Unlike WorkLoop.autoMergeFlow, this does NOT acquire a `git worktree`. The user is
|
|
16
|
-
// expected to be on the PR branch in their cwd; everything happens in-place. That's
|
|
17
|
-
// the simpler model and matches how a human reviewer would handle it.
|
|
18
|
-
//
|
|
19
|
-
// docs/vendor/ai-sdk/chunk-09.md §"Subagents" — Reviewer/Worker are built ad-hoc per loop
|
|
20
|
-
// iteration because their tool bindings (worktree, threads) change each iteration.
|
|
21
1
|
import { composeSystemPrompt } from '@developerz.ai/ai-claude-compat';
|
|
22
2
|
import { CiFailed } from "../github/errors.js";
|
|
23
3
|
import { createReviewerAgent, REVIEWER_SYSTEM_PREFIX, runReviewer, } from "../subagents/reviewer.js";
|
|
@@ -29,21 +9,14 @@ export async function runTakeOverFlow(input) {
|
|
|
29
9
|
const cooldownMs = input.cooldownMs ?? DEFAULT_COOLDOWN_MS;
|
|
30
10
|
const sleep = input.sleep ?? defaultSleep;
|
|
31
11
|
const log = input.logger;
|
|
32
|
-
// Hoisted so the post-loop merge can report how many iterations actually ran: on an
|
|
33
|
-
// early `break` it holds the break index, on natural exhaustion it equals maxIterations.
|
|
34
12
|
let iteration = 0;
|
|
35
13
|
for (; iteration < maxIterations; iteration++) {
|
|
36
14
|
log?.info('take-over: iteration start', { pr: input.pr, iteration });
|
|
37
|
-
// 1. Wait for CI to settle. waitForChecks throws CiFailed on hard failure; we treat
|
|
38
|
-
// that as "Worker should try to fix" rather than a fatal error.
|
|
39
15
|
const ciStatus = await observeCheckStatus(input.github, input.pr);
|
|
40
16
|
log?.info('take-over: ci status', { pr: input.pr, ciStatus });
|
|
41
|
-
// 2. Pull review threads. Always runs — even on CI failure, threads may exist and
|
|
42
|
-
// fixing them might happen to fix CI too.
|
|
43
17
|
const threads = await input.github.listUnresolvedThreads(input.pr);
|
|
44
18
|
log?.info('take-over: threads', { pr: input.pr, count: threads.length });
|
|
45
19
|
if (ciStatus === 'success' && threads.length === 0) {
|
|
46
|
-
// Happy path: nothing left to do. Merge.
|
|
47
20
|
break;
|
|
48
21
|
}
|
|
49
22
|
let pushedSomething = false;
|
|
@@ -69,7 +42,6 @@ export async function runTakeOverFlow(input) {
|
|
|
69
42
|
iterations: iteration,
|
|
70
43
|
};
|
|
71
44
|
}
|
|
72
|
-
// Reviewer commits per-thread fixes via the bash tool; we still need to push them.
|
|
73
45
|
if (reviewed.resolutions.some((r) => r.kind === 'fixed')) {
|
|
74
46
|
pushedSomething = true;
|
|
75
47
|
}
|
|
@@ -78,12 +50,9 @@ export async function runTakeOverFlow(input) {
|
|
|
78
50
|
await input.push(input.worktreePath);
|
|
79
51
|
log?.info('take-over: pushed fixes', { pr: input.pr });
|
|
80
52
|
}
|
|
81
|
-
// Sleep so the next iteration's waitForChecks sees fresh CI state, not the stale
|
|
82
|
-
// success/failure from before our push triggered a new run.
|
|
83
53
|
if (cooldownMs > 0)
|
|
84
54
|
await sleep(cooldownMs);
|
|
85
55
|
}
|
|
86
|
-
// Final state check — make sure we didn't fall through the loop with a hung iteration.
|
|
87
56
|
const finalStatus = await observeCheckStatus(input.github, input.pr);
|
|
88
57
|
const finalThreads = await input.github.listUnresolvedThreads(input.pr);
|
|
89
58
|
if (finalStatus !== 'success') {
|
|
@@ -104,8 +73,6 @@ export async function runTakeOverFlow(input) {
|
|
|
104
73
|
log?.info('take-over: merged', { pr: input.pr });
|
|
105
74
|
return { kind: 'merged', pr: input.pr, iterations: iteration };
|
|
106
75
|
}
|
|
107
|
-
// Convert waitForChecks' throw-on-failure semantics into a status return so the loop can
|
|
108
|
-
// treat CI failure as a recoverable state.
|
|
109
76
|
async function observeCheckStatus(github, pr) {
|
|
110
77
|
try {
|
|
111
78
|
return await github.waitForChecks(pr);
|
|
@@ -137,9 +104,6 @@ async function runReviewerThreads(input, threads) {
|
|
|
137
104
|
styleContents: input.subagents.styleContents,
|
|
138
105
|
});
|
|
139
106
|
}
|
|
140
|
-
// Worker CI-fix path. Build a synthetic PR group whose only task is "fix CI on this PR",
|
|
141
|
-
// then run the regular Worker. Worker emits a FileManifest and runs per-file editors —
|
|
142
|
-
// suitable for "test failed, fix it" if Worker has enough context from the worktree.
|
|
143
107
|
async function runWorkerCiFix(input) {
|
|
144
108
|
const group = {
|
|
145
109
|
id: `takeover-ci-${input.pr}`,
|
|
@@ -180,4 +144,3 @@ function defaultSleep(ms) {
|
|
|
180
144
|
setTimeout(resolve, ms);
|
|
181
145
|
});
|
|
182
146
|
}
|
|
183
|
-
//# sourceMappingURL=take-over-flow.js.map
|
package/dist/loop/work-loop.js
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
// docs/architecture.md (WorkLoop row), docs/commands/start.md §Flow
|
|
2
|
-
// Drives the Orchestrator group-by-group. Extended for concurrent execution:
|
|
3
|
-
//
|
|
4
|
-
// while !plan.isComplete():
|
|
5
|
-
// ready = planGraph.ready()
|
|
6
|
-
// batch = ready.slice(0, free worker slots)
|
|
7
|
-
// await Promise.all(batch.map(g => runGroup(g)))
|
|
8
|
-
//
|
|
9
|
-
// Each runGroup acquires a WorktreePool slot, runs Worker, and on PR open hands off to
|
|
10
|
-
// the merge-pr flow (CI wait + Reviewer + GitHubClient.mergePr).
|
|
11
|
-
//
|
|
12
|
-
// Deps are structural ports — concrete classes (Orchestrator, GitHubClient, StateStore,
|
|
13
|
-
// WorktreePool, PlanGraph) satisfy them at runtime; tests pass literal stubs.
|
|
14
1
|
import { CiFailed } from "../github/errors.js";
|
|
15
2
|
const DEFAULT_MERGE_METHOD = 'squash';
|
|
16
|
-
// Thrown when a state-write fails *after* an external side effect (openPr/mergePr) already
|
|
17
|
-
// succeeded. Carries the real outcome so runGroup doesn't roll the group back to 'blocked'
|
|
18
|
-
// and cause a retry to reopen/re-merge work that already landed.
|
|
19
3
|
class StateWriteAfterSuccess extends Error {
|
|
20
4
|
outcome;
|
|
21
5
|
cause;
|
|
@@ -52,7 +36,6 @@ export class WorkLoop {
|
|
|
52
36
|
}
|
|
53
37
|
return this.finalResult();
|
|
54
38
|
}
|
|
55
|
-
// Run a single group end-to-end: worktree → Worker → (optionally) merge-pr inline.
|
|
56
39
|
async runGroup(group) {
|
|
57
40
|
const branch = group.branch ?? `aitm/${group.id}`;
|
|
58
41
|
let acquired = false;
|
|
@@ -71,18 +54,13 @@ export class WorkLoop {
|
|
|
71
54
|
}
|
|
72
55
|
catch (err) {
|
|
73
56
|
if (acquired) {
|
|
74
|
-
// best-effort release if processGroup itself threw before the inner finally ran;
|
|
75
|
-
// the inner finally would have run already in normal flow, so this is defensive.
|
|
76
57
|
try {
|
|
77
58
|
await this.deps.pool.release(group.id);
|
|
78
59
|
}
|
|
79
60
|
catch {
|
|
80
|
-
/* swallow */
|
|
81
61
|
}
|
|
82
62
|
}
|
|
83
63
|
if (err instanceof StateWriteAfterSuccess) {
|
|
84
|
-
// External side effect (openPr/mergePr) already succeeded; persist failed.
|
|
85
|
-
// Keep the real outcome so a retry doesn't reopen or re-merge.
|
|
86
64
|
this.outcomes.push(err.outcome);
|
|
87
65
|
return;
|
|
88
66
|
}
|
|
@@ -91,7 +69,6 @@ export class WorkLoop {
|
|
|
91
69
|
await this.markStatus(group.id, 'blocked');
|
|
92
70
|
}
|
|
93
71
|
catch {
|
|
94
|
-
/* swallow secondary failures */
|
|
95
72
|
}
|
|
96
73
|
this.outcomes.push({ groupId: group.id, status: 'blocked', reason });
|
|
97
74
|
}
|
|
@@ -108,20 +85,15 @@ export class WorkLoop {
|
|
|
108
85
|
const delivery = workerResult.delivery;
|
|
109
86
|
await orchestrator.finalizeCommit(group, delivery, worktree.path);
|
|
110
87
|
const pr = await orchestrator.openPr(group, delivery, baseBranch);
|
|
111
|
-
// openPr already landed externally; if persistence fails here, surface the real
|
|
112
|
-
// outcome via StateWriteAfterSuccess so the outer catch doesn't flip us to 'blocked'.
|
|
113
88
|
await this.persistAfterSideEffect({ groupId: group.id, status: 'awaiting-pr', pr: pr.number }, () => this.markStatus(group.id, 'awaiting-pr', { pr: pr.number }));
|
|
114
89
|
if (!this.deps.autoMerge) {
|
|
115
90
|
this.outcomes.push({ groupId: group.id, status: 'awaiting-pr', pr: pr.number });
|
|
116
91
|
return;
|
|
117
92
|
}
|
|
118
93
|
await this.autoMergeFlow(group, pr, worktree, baseBranch);
|
|
119
|
-
// mergePr already landed externally; same guard as above.
|
|
120
94
|
await this.persistAfterSideEffect({ groupId: group.id, status: 'merged', pr: pr.number }, () => this.markStatus(group.id, 'merged'));
|
|
121
95
|
this.outcomes.push({ groupId: group.id, status: 'merged', pr: pr.number });
|
|
122
96
|
}
|
|
123
|
-
// Run a state write that follows a successful external side effect. If the write throws,
|
|
124
|
-
// wrap the error in StateWriteAfterSuccess so callers don't roll the outcome back.
|
|
125
97
|
async persistAfterSideEffect(outcome, write) {
|
|
126
98
|
try {
|
|
127
99
|
await write();
|
|
@@ -132,7 +104,6 @@ export class WorkLoop {
|
|
|
132
104
|
}
|
|
133
105
|
async autoMergeFlow(group, pr, worktree, baseBranch) {
|
|
134
106
|
const { orchestrator, github } = this.deps;
|
|
135
|
-
// CI: wait for checks. On failure, ask Worker to fix and re-check.
|
|
136
107
|
try {
|
|
137
108
|
await github.waitForChecks(pr.number);
|
|
138
109
|
}
|
|
@@ -147,7 +118,6 @@ export class WorkLoop {
|
|
|
147
118
|
await orchestrator.finalizeCommit(group, fix.delivery, worktree.path);
|
|
148
119
|
await github.waitForChecks(pr.number);
|
|
149
120
|
}
|
|
150
|
-
// Review: resolve any unresolved threads via Reviewer.
|
|
151
121
|
const threads = await github.listUnresolvedThreads(pr.number);
|
|
152
122
|
if (threads.length > 0) {
|
|
153
123
|
const review = await orchestrator.runReviewer({ pr: pr.number, threads, worktree });
|
|
@@ -166,16 +136,11 @@ export class WorkLoop {
|
|
|
166
136
|
return Math.min(concurrency, readyCount, remaining);
|
|
167
137
|
}
|
|
168
138
|
async markStatus(id, status, patch = {}) {
|
|
169
|
-
// Status transitions do not bump sessionCount — that's owned by incrementSessionCount,
|
|
170
|
-
// which fires once per batch dispatch so the in-memory and persisted counters agree.
|
|
171
139
|
await this.deps.state.update((s) => ({
|
|
172
140
|
...s,
|
|
173
141
|
prGroups: s.prGroups.map((g) => (g.id === id ? { ...g, ...patch, status } : g)),
|
|
174
142
|
}));
|
|
175
143
|
}
|
|
176
|
-
// Single source of truth for session counting: bump both the in-memory counter (used by
|
|
177
|
-
// run() to enforce maxSessions) and the persisted counter (used by reporting/resume) in
|
|
178
|
-
// one call. Drops in-memory if persistence fails so the two stay aligned.
|
|
179
144
|
async incrementSessionCount(by) {
|
|
180
145
|
if (by <= 0)
|
|
181
146
|
return;
|
|
@@ -208,4 +173,3 @@ export class WorkLoop {
|
|
|
208
173
|
return { kind: 'success', outcomes: this.outcomes.slice() };
|
|
209
174
|
}
|
|
210
175
|
}
|
|
211
|
-
//# sourceMappingURL=work-loop.js.map
|
package/dist/mcp/mcp-client.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
// Connects to every MCP server declared in `mcpServers` (config) and exposes the
|
|
2
|
-
// union of their tools to subagents. The Vercel AI SDK's @ai-sdk/mcp client gives
|
|
3
|
-
// us tool-conversion; we wire transport per entry and merge the tool maps.
|
|
4
|
-
//
|
|
5
|
-
// docs/vendor/ai-sdk/chunk-15.md §"Initializing an MCP Client"
|
|
6
|
-
// docs/mcp.md
|
|
7
|
-
//
|
|
8
|
-
// Lifecycle: connectAll() at run start, toolsForRole() during agent build, close() on exit
|
|
9
|
-
// (success / blocked / SIGINT). Failures on individual servers are logged + skipped — a
|
|
10
|
-
// broken MCP server should not block the whole run.
|
|
11
1
|
import { experimental_createMCPClient } from '@ai-sdk/mcp';
|
|
12
2
|
import { Experimental_StdioMCPTransport } from '@ai-sdk/mcp/mcp-stdio';
|
|
13
3
|
export class McpClientManager {
|
|
@@ -20,8 +10,6 @@ export class McpClientManager {
|
|
|
20
10
|
}
|
|
21
11
|
async connectAll() {
|
|
22
12
|
for (const [name, server] of Object.entries(this.init.servers)) {
|
|
23
|
-
// Track the client outside the try so a failure during tools() can still
|
|
24
|
-
// close the spawned process / socket instead of leaking it.
|
|
25
13
|
let client;
|
|
26
14
|
try {
|
|
27
15
|
const transport = transportKind(server);
|
|
@@ -120,4 +108,3 @@ function clientNameFor(name) {
|
|
|
120
108
|
function errorMessage(err) {
|
|
121
109
|
return err instanceof Error ? err.message : String(err);
|
|
122
110
|
}
|
|
123
|
-
//# sourceMappingURL=mcp-client.js.map
|
package/dist/mcp/schema.js
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
// MCP *client* config — aitm consumes external MCP servers as additional tool surfaces
|
|
2
|
-
// for subagents. aitm is NOT exposed as an MCP server (banned by CLAUDE.md §"Out of scope").
|
|
3
|
-
//
|
|
4
|
-
// Shape mirrors Claude Code's `mcpServers` config — three transports, same keys.
|
|
5
|
-
// Refs:
|
|
6
|
-
// https://code.claude.com/docs/en/mcp (Claude Code reference)
|
|
7
|
-
// https://modelcontextprotocol.io/ (spec)
|
|
8
|
-
// docs/vendor/ai-sdk/chunk-15.md §"Model Context Protocol" (Vercel AI SDK client)
|
|
9
|
-
//
|
|
10
|
-
// Transports:
|
|
11
|
-
// stdio — local binary launched as a child process. Default when `type` omitted.
|
|
12
|
-
// sse — HTTP server-sent events. URL + optional headers.
|
|
13
|
-
// http — Streamable HTTP transport. URL + optional headers. Preferred for production.
|
|
14
1
|
import { z } from 'zod';
|
|
15
2
|
export const McpStdioServerSchema = z.object({
|
|
16
3
|
type: z.literal('stdio').optional(),
|
|
17
4
|
command: z.string(),
|
|
18
5
|
args: z.array(z.string()).optional(),
|
|
19
6
|
env: z.record(z.string(), z.string()).optional(),
|
|
20
|
-
// Working directory for the spawned process. Defaults to repo root.
|
|
21
7
|
cwd: z.string().optional(),
|
|
22
8
|
});
|
|
23
9
|
export const McpSseServerSchema = z.object({
|
|
@@ -36,4 +22,3 @@ export const McpServerSchema = z.union([
|
|
|
36
22
|
McpHttpServerSchema,
|
|
37
23
|
]);
|
|
38
24
|
export const McpServersSchema = z.record(z.string(), McpServerSchema);
|
|
39
|
-
//# sourceMappingURL=schema.js.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Thin OpenRouter API client. Only the endpoints we actually need:
|
|
2
|
-
// GET /api/v1/models — model catalog including context length / pricing.
|
|
3
|
-
// docs/auth.md §"LLM provider", docs/runtime.md (web fetch, not Bun.fetch).
|
|
4
1
|
import { z } from 'zod';
|
|
5
2
|
export const OpenRouterModelSchema = z
|
|
6
3
|
.object({
|
|
@@ -37,4 +34,3 @@ export class OpenRouterClient {
|
|
|
37
34
|
return OpenRouterModelsResponseSchema.parse(json).data;
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Exposes per-model limits (context window, pricing hints) used by the Compactor.
|
|
2
|
-
// Cached per-run after first fetch — model catalog changes slowly.
|
|
3
|
-
// docs/auth.md, src/compaction/compactor.ts
|
|
4
1
|
export class ModelNotFound extends Error {
|
|
5
2
|
modelId;
|
|
6
3
|
name = 'ModelNotFound';
|
|
@@ -36,4 +33,3 @@ export class ModelLimitsRegistry {
|
|
|
36
33
|
this.cache = next;
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
|
-
//# sourceMappingURL=model-limits.js.map
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
// OpenRouter server tools — model-decides tools executed on OpenRouter's side.
|
|
2
|
-
// These are NOT Vercel AI SDK function tools; they ride in `providerOptions.openrouter`
|
|
3
|
-
// (or equivalent extraBody) so OpenRouter sees them in the request `tools[]` array verbatim.
|
|
4
|
-
//
|
|
5
|
-
// Refs:
|
|
6
|
-
// https://openrouter.ai/docs/guides/features/server-tools/web-search
|
|
7
|
-
// https://openrouter.ai/docs/guides/features/server-tools/web-fetch
|
|
8
|
-
//
|
|
9
|
-
// Web search → citations land as annotations[].url_citation on the assistant message,
|
|
10
|
-
// plus usage.server_tool_use.web_search_requests for cost accounting. No round-trip.
|
|
11
|
-
// Web fetch → standard tool_call round-trip with { url, title, content, status, retrieved_at }.
|
|
12
1
|
export function webSearchTool(options = {}) {
|
|
13
2
|
return { type: 'openrouter:web_search', parameters: options };
|
|
14
3
|
}
|
|
15
|
-
// Renamed from webFetchTool — the canonical webFetchTool is now the local variant in
|
|
16
|
-
// src/tools/web-fetch.ts. This one is the explicit "delegate to OpenRouter" opt-in.
|
|
17
4
|
export function webFetchServerTool(options = {}) {
|
|
18
5
|
return { type: 'openrouter:web_fetch', parameters: options };
|
|
19
6
|
}
|
|
20
|
-
// Build the providerOptions.openrouter fragment to be merged into an AI SDK call.
|
|
21
|
-
// Usage at the model handle layer (src/credentials/credentials.ts) — see Credentials.modelFor.
|
|
22
7
|
export function providerOptionsWithServerTools(tools) {
|
|
23
8
|
return { openrouter: { tools: [...tools] } };
|
|
24
9
|
}
|
|
25
|
-
//# sourceMappingURL=server-tools.js.map
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
// docs/architecture.md §Flow, docs/subagents.md §Composition
|
|
2
|
-
// Top-level agent. Owns:
|
|
3
|
-
// - prompt composition (style payload + role prefix + rolling context)
|
|
4
|
-
// - routing between Planner / Worker / Reviewer (each exposed as a tool)
|
|
5
|
-
// - **PR creation** — title, body, commit message: docs say Worker opens the PR,
|
|
6
|
-
// but the Worker can be inconsistent on global-context narration. Orchestrator
|
|
7
|
-
// re-composes the commit message and opens the PR via GitHubClient, taking the
|
|
8
|
-
// Worker's draft as input. This is the reliability win: one place that knows
|
|
9
|
-
// the whole plan writes the PR-level prose.
|
|
10
|
-
//
|
|
11
|
-
// SDK references:
|
|
12
|
-
// docs/vendor/ai-sdk/chunk-04.md §"ToolLoopAgent"
|
|
13
|
-
// docs/vendor/ai-sdk/chunk-09.md §"Subagents" §"Controlling What the Model Sees"
|
|
14
|
-
// docs/vendor/ai-sdk/chunk-09.md §"Loop Control" — stopWhen: [stepCountIs(N), hasToolCall('done')]
|
|
15
1
|
import { generateText, hasToolCall, Output, stepCountIs, ToolLoopAgent } from 'ai';
|
|
16
2
|
import { ExecaError, execa } from 'execa';
|
|
17
3
|
import { z } from 'zod';
|
|
@@ -36,8 +22,6 @@ export const defaultRunCmd = async (file, args, options) => {
|
|
|
36
22
|
throw err;
|
|
37
23
|
}
|
|
38
24
|
};
|
|
39
|
-
// Inlined per CLAUDE.md "no premature abstraction". Full system prompt is
|
|
40
|
-
// `agentConfig.contents + ORCHESTRATOR_ROLE_PREFIX + rollingContext`.
|
|
41
25
|
export const ORCHESTRATOR_ROLE_PREFIX = [
|
|
42
26
|
'',
|
|
43
27
|
'## Role: Orchestrator',
|
|
@@ -56,16 +40,11 @@ export const ORCHESTRATOR_ROLE_PREFIX = [
|
|
|
56
40
|
' - Be specific and terse. No marketing prose.',
|
|
57
41
|
' - Conventional commit subjects, ≤72 chars.',
|
|
58
42
|
].join('\n');
|
|
59
|
-
// Structured-output schema for PR composition. Title cap reinforces conventional-commit
|
|
60
|
-
// brevity; body is free-form markdown.
|
|
61
43
|
const PrCompositionSchema = z.object({
|
|
62
44
|
title: z.string().min(1).max(72),
|
|
63
45
|
body: z.string().min(1),
|
|
64
46
|
});
|
|
65
|
-
// Fallback session cap when caller passes null / 0 / negative `maxSessions`.
|
|
66
47
|
export const DEFAULT_MAX_STEPS = 50;
|
|
67
|
-
// Resolve the agent step cap from caller-provided `maxSessions`. Falls back to the
|
|
68
|
-
// default when the value is null, zero, or negative. Exported for unit testing.
|
|
69
48
|
export function resolveMaxSteps(maxSessions) {
|
|
70
49
|
return typeof maxSessions === 'number' && maxSessions > 0 ? maxSessions : DEFAULT_MAX_STEPS;
|
|
71
50
|
}
|
|
@@ -111,8 +90,6 @@ export class Orchestrator {
|
|
|
111
90
|
this.init.rollingContext,
|
|
112
91
|
].join('\n');
|
|
113
92
|
}
|
|
114
|
-
// Re-write the Worker's draft commit message via the orchestrator model, then
|
|
115
|
-
// `git commit --amend` on the active worktree. Returns the new HEAD SHA.
|
|
116
93
|
async finalizeCommit(group, delivery, worktreePath) {
|
|
117
94
|
const refined = await this.refineCommitMessage(group, delivery);
|
|
118
95
|
const runCmd = this.init.runCmd ?? defaultRunCmd;
|
|
@@ -126,8 +103,6 @@ export class Orchestrator {
|
|
|
126
103
|
}
|
|
127
104
|
return sha.stdout.trim();
|
|
128
105
|
}
|
|
129
|
-
// Compose PR title + body via the orchestrator model, then open the PR through the github
|
|
130
|
-
// client. Falls back to `aitm/<group.id>` when `group.branch` is unset.
|
|
131
106
|
async openPr(group, delivery, baseBranch) {
|
|
132
107
|
const { title, body } = await this.composePr(group, delivery);
|
|
133
108
|
const head = group.branch ?? `aitm/${group.id}`;
|
|
@@ -177,4 +152,3 @@ export class Orchestrator {
|
|
|
177
152
|
].join('\n');
|
|
178
153
|
}
|
|
179
154
|
}
|
|
180
|
-
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
// Wrap each subagent as a tool consumable by the Orchestrator agent.
|
|
2
|
-
// Pattern verbatim from docs/vendor/ai-sdk/chunk-09.md §"Subagents" and §"Controlling What the
|
|
3
|
-
// Model Sees" — `toModelOutput` keeps the Orchestrator context lean by collapsing the full
|
|
4
|
-
// subagent result to a one-line `<role> [<status>]: <summary>`.
|
|
5
|
-
//
|
|
6
|
-
// Note: AI SDK 6 names the field `toModelOutput` (the SDK-5 name was
|
|
7
|
-
// `experimental_toToolResultContent`; the task brief uses the older spelling).
|
|
8
|
-
//
|
|
9
|
-
// Each wrapper:
|
|
10
|
-
// - inputSchema — Zod schema; the Orchestrator's model fills it in per call
|
|
11
|
-
// - execute — builds the subagent via its factory using `credentials.modelFor(role)` and
|
|
12
|
-
// runs the matching runner; returns the full structured result
|
|
13
|
-
// - toModelOutput — collapses that result to a single status line for the Orchestrator
|
|
14
1
|
import { tool } from 'ai';
|
|
15
2
|
import { z } from 'zod';
|
|
16
3
|
import { createPlannerAgent, PLANNER_SYSTEM_PREFIX, runPlanner, } from "../subagents/planner.js";
|
|
@@ -21,8 +8,6 @@ const plannerInputSchema = z.object({
|
|
|
21
8
|
criteria: z.string().optional().describe('Optional acceptance criteria'),
|
|
22
9
|
maxPrs: z.number().int().positive().describe('Cap on PR-group count'),
|
|
23
10
|
});
|
|
24
|
-
// Worker + Reviewer have all state bound in deps — the Orchestrator's model invokes them with
|
|
25
|
-
// no arguments, signalling "process the currently-active group / review threads".
|
|
26
11
|
const emptyInputSchema = z.object({});
|
|
27
12
|
export function makePlannerTool(deps) {
|
|
28
13
|
return tool({
|
|
@@ -85,13 +70,7 @@ export function makeReviewerTool(deps) {
|
|
|
85
70
|
toModelOutput: ({ output }) => ({ type: 'text', value: summarizeReviewerResult(output) }),
|
|
86
71
|
});
|
|
87
72
|
}
|
|
88
|
-
// Cap per-line summary length so a verbose subagent reply (long IDs, multiline draft
|
|
89
|
-
// commit messages, error payloads) can't bloat the orchestrator's context. 220 chars
|
|
90
|
-
// is enough for a status line + a few tens of chars of free text.
|
|
91
73
|
const MAX_SUMMARY_CHARS = 220;
|
|
92
|
-
// Show only the first N planner group IDs, then `+M more` — the count alone is what
|
|
93
|
-
// matters to the orchestrator; the orchestrator can re-read the plan if it needs the
|
|
94
|
-
// full ID list.
|
|
95
74
|
const MAX_PREVIEW_IDS = 8;
|
|
96
75
|
function compactOneLine(text, max = MAX_SUMMARY_CHARS) {
|
|
97
76
|
return text.replace(/\s+/g, ' ').trim().slice(0, max);
|
|
@@ -130,4 +109,3 @@ function summarizeReviewerResult(r) {
|
|
|
130
109
|
return compactOneLine(`reviewer [blocked]: ${r.reason}`);
|
|
131
110
|
return compactOneLine(`reviewer [error]: ${r.error}`);
|
|
132
111
|
}
|
|
133
|
-
//# sourceMappingURL=subagent-tools.js.map
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Role system-prompt prefixes for each agent. Inlined per CLAUDE.md "no premature abstraction".
|
|
2
|
-
// Appended after `agentConfig.contents` (house style) to form the full instructions string.
|
|
3
1
|
export const ORCHESTRATOR_PREFIX = [
|
|
4
2
|
'',
|
|
5
3
|
'## Role: Orchestrator',
|
|
@@ -75,4 +73,3 @@ export const REVIEWER_PREFIX = [
|
|
|
75
73
|
' - Resolve the thread for "fixed" and "wontfix"; leave it open for "replied".',
|
|
76
74
|
' - Return JSON that matches the ThreadResolutionOutput schema exactly.',
|
|
77
75
|
].join('\n');
|
|
78
|
-
//# sourceMappingURL=system-prompts.js.map
|
package/dist/plan/plan-graph.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Dependency DAG over PR groups. Drives concurrent execution in src/loop/work-loop.ts.
|
|
2
|
-
// Why a graph and not a list — docs/task-groups.md (extended): large goals split into
|
|
3
|
-
// independent PRs that can run in parallel. A linear list serializes work needlessly.
|
|
4
1
|
export class PlanGraph {
|
|
5
2
|
groups;
|
|
6
3
|
index;
|
|
@@ -9,12 +6,10 @@ export class PlanGraph {
|
|
|
9
6
|
PlanGraph.validate(groups);
|
|
10
7
|
this.index = new Map(groups.map((g) => [g.id, g]));
|
|
11
8
|
}
|
|
12
|
-
// Groups currently ready to run: status === 'pending' AND all deps merged.
|
|
13
9
|
ready() {
|
|
14
10
|
return this.groups.filter((g) => g.status === 'pending' &&
|
|
15
11
|
g.dependsOn.every((dep) => this.index.get(dep)?.status === 'merged'));
|
|
16
12
|
}
|
|
17
|
-
// Groups blocked on at least one unmerged dep.
|
|
18
13
|
blocked() {
|
|
19
14
|
return this.groups.filter((g) => g.status === 'pending' &&
|
|
20
15
|
g.dependsOn.some((dep) => this.index.get(dep)?.status !== 'merged'));
|
|
@@ -22,12 +17,9 @@ export class PlanGraph {
|
|
|
22
17
|
byId(id) {
|
|
23
18
|
return this.index.get(id);
|
|
24
19
|
}
|
|
25
|
-
// True when every group is in a terminal state (merged or blocked).
|
|
26
20
|
isComplete() {
|
|
27
21
|
return this.groups.every((g) => g.status === 'merged' || g.status === 'blocked');
|
|
28
22
|
}
|
|
29
|
-
// Static: detect cycles + dangling deps at plan-acceptance time.
|
|
30
|
-
// DFS coloring — white=unvisited, gray=on stack, black=fully explored.
|
|
31
23
|
static validate(groups) {
|
|
32
24
|
const ids = new Set();
|
|
33
25
|
for (const g of groups) {
|
|
@@ -66,4 +58,3 @@ export class PlanGraph {
|
|
|
66
58
|
visit(g.id, []);
|
|
67
59
|
}
|
|
68
60
|
}
|
|
69
|
-
//# sourceMappingURL=plan-graph.js.map
|
package/dist/plan/schema.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
// Plan-time output of Planner subagent — see docs/subagents.md §Roster (Planner row).
|
|
2
|
-
// PrGroup is persisted via src/state/schema.ts; the plan schema below is what
|
|
3
|
-
// Planner returns to the Orchestrator before that persistence step.
|
|
4
1
|
import { z } from 'zod';
|
|
5
2
|
export const PlannedTaskSchema = z.object({
|
|
6
3
|
description: z.string(),
|
|
7
|
-
// Optional file hint helps Worker plan its parallel file-edit fanout.
|
|
8
|
-
// See docs/vendor/ai-sdk/chunk-09.md §"Orchestrator-Worker" — Worker uses this
|
|
9
|
-
// to emit a file manifest, then Promise.all over per-file editor subagents.
|
|
10
4
|
filesHint: z.array(z.string()).optional(),
|
|
11
5
|
});
|
|
12
6
|
export const PlannedGroupSchema = z.object({
|
|
13
7
|
id: z.string(),
|
|
14
8
|
title: z.string(),
|
|
15
9
|
tasks: z.array(PlannedTaskSchema),
|
|
16
|
-
// Group ids that must merge before this group runs. Empty = root of DAG.
|
|
17
10
|
dependsOn: z.array(z.string()).default([]),
|
|
18
11
|
});
|
|
19
12
|
export const PlanSchema = z.object({
|
|
@@ -21,4 +14,3 @@ export const PlanSchema = z.object({
|
|
|
21
14
|
criteria: z.string().optional(),
|
|
22
15
|
groups: z.array(PlannedGroupSchema),
|
|
23
16
|
});
|
|
24
|
-
//# sourceMappingURL=schema.js.map
|