@developerz.ai/aitm 0.0.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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/agent-config/agent-config-detector.d.ts +15 -0
- package/dist/agent-config/agent-config-detector.js +56 -0
- package/dist/agent-config/agent-config-detector.js.map +1 -0
- package/dist/cli/args.d.ts +37 -0
- package/dist/cli/args.js +238 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/cli.d.ts +15 -0
- package/dist/cli/cli.js +113 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/commands.d.ts +83 -0
- package/dist/cli/commands.js +521 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/compaction/compactor.d.ts +20 -0
- package/dist/compaction/compactor.js +75 -0
- package/dist/compaction/compactor.js.map +1 -0
- package/dist/config/config-loader.d.ts +25 -0
- package/dist/config/config-loader.js +275 -0
- package/dist/config/config-loader.js.map +1 -0
- package/dist/config/config-writer.d.ts +14 -0
- package/dist/config/config-writer.js +178 -0
- package/dist/config/config-writer.js.map +1 -0
- package/dist/config/schema.d.ts +85 -0
- package/dist/config/schema.js +38 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/credentials/credentials.d.ts +15 -0
- package/dist/credentials/credentials.js +58 -0
- package/dist/credentials/credentials.js.map +1 -0
- package/dist/credentials/defaults.d.ts +2 -0
- package/dist/credentials/defaults.js +21 -0
- package/dist/credentials/defaults.js.map +1 -0
- package/dist/fs/atomic-write.d.ts +1 -0
- package/dist/fs/atomic-write.js +27 -0
- package/dist/fs/atomic-write.js.map +1 -0
- package/dist/github/errors.d.ts +18 -0
- package/dist/github/errors.js +20 -0
- package/dist/github/errors.js.map +1 -0
- package/dist/github/github-client.d.ts +47 -0
- package/dist/github/github-client.js +417 -0
- package/dist/github/github-client.js.map +1 -0
- package/dist/github/schema.d.ts +44 -0
- package/dist/github/schema.js +23 -0
- package/dist/github/schema.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/logger.d.ts +36 -0
- package/dist/logger/logger.js +123 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/loop/run-loop-adapter.d.ts +46 -0
- package/dist/loop/run-loop-adapter.js +270 -0
- package/dist/loop/run-loop-adapter.js.map +1 -0
- package/dist/loop/take-over-flow.d.ts +57 -0
- package/dist/loop/take-over-flow.js +183 -0
- package/dist/loop/take-over-flow.js.map +1 -0
- package/dist/loop/work-loop.d.ts +95 -0
- package/dist/loop/work-loop.js +211 -0
- package/dist/loop/work-loop.js.map +1 -0
- package/dist/mcp/mcp-client.d.ts +27 -0
- package/dist/mcp/mcp-client.js +123 -0
- package/dist/mcp/mcp-client.js.map +1 -0
- package/dist/mcp/schema.d.ts +53 -0
- package/dist/mcp/schema.js +39 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/openrouter/client.d.ts +28 -0
- package/dist/openrouter/client.js +40 -0
- package/dist/openrouter/client.js.map +1 -0
- package/dist/openrouter/model-limits.d.ts +21 -0
- package/dist/openrouter/model-limits.js +39 -0
- package/dist/openrouter/model-limits.js.map +1 -0
- package/dist/openrouter/server-tools.d.ts +35 -0
- package/dist/openrouter/server-tools.js +25 -0
- package/dist/openrouter/server-tools.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +60 -0
- package/dist/orchestrator/orchestrator.js +180 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/subagent-tools.d.ts +44 -0
- package/dist/orchestrator/subagent-tools.js +133 -0
- package/dist/orchestrator/subagent-tools.js.map +1 -0
- package/dist/orchestrator/system-prompts.d.ts +4 -0
- package/dist/orchestrator/system-prompts.js +78 -0
- package/dist/orchestrator/system-prompts.js.map +1 -0
- package/dist/plan/plan-graph.d.ts +11 -0
- package/dist/plan/plan-graph.js +69 -0
- package/dist/plan/plan-graph.js.map +1 -0
- package/dist/plan/schema.d.ts +30 -0
- package/dist/plan/schema.js +24 -0
- package/dist/plan/schema.js.map +1 -0
- package/dist/state/schema.d.ts +88 -0
- package/dist/state/schema.js +53 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/state/state-store.d.ts +16 -0
- package/dist/state/state-store.js +129 -0
- package/dist/state/state-store.js.map +1 -0
- package/dist/subagents/factory.d.ts +8 -0
- package/dist/subagents/factory.js +10 -0
- package/dist/subagents/factory.js.map +1 -0
- package/dist/subagents/planner.d.ts +31 -0
- package/dist/subagents/planner.js +83 -0
- package/dist/subagents/planner.js.map +1 -0
- package/dist/subagents/reviewer.d.ts +60 -0
- package/dist/subagents/reviewer.js +159 -0
- package/dist/subagents/reviewer.js.map +1 -0
- package/dist/subagents/worker.d.ts +71 -0
- package/dist/subagents/worker.js +180 -0
- package/dist/subagents/worker.js.map +1 -0
- package/dist/testing/temp-repo.d.ts +7 -0
- package/dist/testing/temp-repo.js +21 -0
- package/dist/testing/temp-repo.js.map +1 -0
- package/dist/tools/datetime.d.ts +12 -0
- package/dist/tools/datetime.js +42 -0
- package/dist/tools/datetime.js.map +1 -0
- package/dist/tools/fetch-html.d.ts +32 -0
- package/dist/tools/fetch-html.js +139 -0
- package/dist/tools/fetch-html.js.map +1 -0
- package/dist/tools/github-thread-tool.d.ts +10 -0
- package/dist/tools/github-thread-tool.js +36 -0
- package/dist/tools/github-thread-tool.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +31 -0
- package/dist/tools/web-fetch.js +223 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/workspace/worktree-pool.d.ts +21 -0
- package/dist/workspace/worktree-pool.js +104 -0
- package/dist/workspace/worktree-pool.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// docs/subagents.md (Reviewer row), docs/commands/merge-pr.md
|
|
2
|
+
// Input: unresolved review threads. Output: per-thread resolution (reply / push fix / mark stale).
|
|
3
|
+
// Pushes go through Worker tools (FS write, bash); thread ops via a `github` tool that wraps a
|
|
4
|
+
// subset of GitHubClient (replyToThread / resolveThread).
|
|
5
|
+
//
|
|
6
|
+
// Strategy:
|
|
7
|
+
// - One agent.generate call per thread, scoped to that thread's conversation.
|
|
8
|
+
// - Agent emits a structured ThreadResolutionOutput JSON describing the chosen outcome.
|
|
9
|
+
// - For "fixed", the runner — not the agent — performs the git commit so commit shas are
|
|
10
|
+
// deterministic and observable (worker pattern).
|
|
11
|
+
//
|
|
12
|
+
// SDK references:
|
|
13
|
+
// chunk-04.md §"ToolLoopAgent"
|
|
14
|
+
// chunk-05.md §"Generating Structured Data"
|
|
15
|
+
// chunk-09.md §"Subagents"
|
|
16
|
+
import { createSubagent } from '@developerz.ai/ai-claude-compat';
|
|
17
|
+
import { Output } from 'ai';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
// Per-thread structured output emitted by the model. A FLAT object (not a discriminatedUnion):
|
|
20
|
+
// a discriminated union compiles to JSON-Schema `oneOf`, which several OpenRouter-routed
|
|
21
|
+
// providers reject for structured output (`output_config.format.schema: Schema type 'oneOf' is
|
|
22
|
+
// not supported` — seen on Anthropic, which is also aitm's default tier). `kind` selects the
|
|
23
|
+
// outcome; `commitMessage` is expected for "fixed" and `reason` for "wontfix".
|
|
24
|
+
export const ThreadResolutionOutputSchema = z.object({
|
|
25
|
+
kind: z.enum(['fixed', 'replied', 'wontfix']),
|
|
26
|
+
commitMessage: z.string().optional(),
|
|
27
|
+
reason: z.string().optional(),
|
|
28
|
+
});
|
|
29
|
+
export const REVIEWER_SYSTEM_PREFIX = [
|
|
30
|
+
'',
|
|
31
|
+
'You are the Reviewer subagent. You receive ONE unresolved PR review thread at a time and',
|
|
32
|
+
'decide between three outcomes, emitting a ThreadResolutionOutput JSON that names the choice.',
|
|
33
|
+
'',
|
|
34
|
+
'- "fixed": the reviewer is right and a code change is needed. Use your tools (grep/glob/',
|
|
35
|
+
' readFile to locate, editFile/multiEdit to change, writeFile to rewrite, bash for the rest)',
|
|
36
|
+
' to make the fix inside the worktree. DO NOT run `git commit` yourself — the runner commits',
|
|
37
|
+
' every staged change after you finish. Reply on the thread via the github tool explaining',
|
|
38
|
+
' the fix and resolve the thread, then emit { kind: "fixed", commitMessage } where',
|
|
39
|
+
' commitMessage is the subject line the runner will pass to `git commit`.',
|
|
40
|
+
'- "replied": the comment is a question or clarification request and no code change is needed.',
|
|
41
|
+
' Answer it via github.replyToThread. Do not edit code. Emit { kind: "replied" }.',
|
|
42
|
+
'- "wontfix": the suggestion is stale, out of scope, or you disagree. Reply with the reason',
|
|
43
|
+
' via github.replyToThread, resolve the thread via github.resolveThread, and emit',
|
|
44
|
+
' { kind: "wontfix", reason }.',
|
|
45
|
+
'',
|
|
46
|
+
'Rules:',
|
|
47
|
+
'- Stay inside the worktree. No work outside the repo.',
|
|
48
|
+
'- Resolve the thread for "fixed" and "wontfix" outcomes; "replied" leaves it open.',
|
|
49
|
+
'- Return JSON that matches the ThreadResolutionOutput schema exactly.',
|
|
50
|
+
].join('\n');
|
|
51
|
+
// Module-private link from agent to its init so runReviewer can drive bash commits with the
|
|
52
|
+
// same tools without exposing them on the public agent surface (worker uses the same pattern).
|
|
53
|
+
const reviewerInitRegistry = new WeakMap();
|
|
54
|
+
export function createReviewerAgent(init) {
|
|
55
|
+
const agent = createSubagent({
|
|
56
|
+
model: init.model,
|
|
57
|
+
tools: init.tools,
|
|
58
|
+
systemPrompt: init.systemPrompt,
|
|
59
|
+
output: Output.object({
|
|
60
|
+
schema: ThreadResolutionOutputSchema,
|
|
61
|
+
name: 'ThreadResolution',
|
|
62
|
+
}),
|
|
63
|
+
...(init.maxSteps !== undefined ? { maxSteps: init.maxSteps } : {}),
|
|
64
|
+
}, 20);
|
|
65
|
+
reviewerInitRegistry.set(agent, init);
|
|
66
|
+
return agent;
|
|
67
|
+
}
|
|
68
|
+
export async function runReviewer(agent, input) {
|
|
69
|
+
const init = reviewerInitRegistry.get(agent);
|
|
70
|
+
if (!init) {
|
|
71
|
+
return {
|
|
72
|
+
kind: 'error',
|
|
73
|
+
error: 'runReviewer called with an agent not built by createReviewerAgent',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (input.threads.length === 0) {
|
|
77
|
+
return { kind: 'ok', resolutions: [] };
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const resolutions = [];
|
|
81
|
+
for (const thread of input.threads) {
|
|
82
|
+
resolutions.push(await resolveOneThread(agent, init, input, thread));
|
|
83
|
+
}
|
|
84
|
+
return { kind: 'ok', resolutions };
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
return { kind: 'error', error: err instanceof Error ? err.message : String(err) };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function resolveOneThread(agent, init, input, thread) {
|
|
91
|
+
const result = await agent.generate({ prompt: buildThreadPrompt(input, thread) });
|
|
92
|
+
const out = result.experimental_output;
|
|
93
|
+
switch (out.kind) {
|
|
94
|
+
case 'fixed': {
|
|
95
|
+
// commitMessage is optional on the flat schema; fall back to a generic subject.
|
|
96
|
+
const message = out.commitMessage?.trim() || `fix: address review thread ${thread.id}`;
|
|
97
|
+
const commitSha = await commitFix(init.tools.bash, input.worktreePath, message);
|
|
98
|
+
return { threadId: thread.id, kind: 'fixed', commitSha };
|
|
99
|
+
}
|
|
100
|
+
case 'replied':
|
|
101
|
+
return { threadId: thread.id, kind: 'replied' };
|
|
102
|
+
default:
|
|
103
|
+
return {
|
|
104
|
+
threadId: thread.id,
|
|
105
|
+
kind: 'wontfix',
|
|
106
|
+
reason: out.reason?.trim() || 'no reason provided',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function buildThreadPrompt(input, thread) {
|
|
111
|
+
const lines = [`PR: #${input.pr}`, `Worktree: ${input.worktreePath}`, `Thread id: ${thread.id}`];
|
|
112
|
+
if (thread.path)
|
|
113
|
+
lines.push(`File: ${thread.path}`);
|
|
114
|
+
lines.push('', 'Conversation:');
|
|
115
|
+
for (const c of thread.comments) {
|
|
116
|
+
lines.push(` @${c.author}: ${c.body}`);
|
|
117
|
+
}
|
|
118
|
+
lines.push('', 'Decide the outcome, take the action, then emit the ThreadResolutionOutput JSON.');
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
}
|
|
121
|
+
async function commitFix(bash, worktreePath, message) {
|
|
122
|
+
const exec = bash.execute;
|
|
123
|
+
if (typeof exec !== 'function') {
|
|
124
|
+
throw new Error('bash tool is missing an execute function');
|
|
125
|
+
}
|
|
126
|
+
const wt = shQuote(worktreePath);
|
|
127
|
+
await runBash(exec, `git -C ${wt} add -A`);
|
|
128
|
+
await runBash(exec, `git -C ${wt} commit -m ${shQuote(message)}`);
|
|
129
|
+
const sha = await captureBash(exec, `git -C ${wt} rev-parse HEAD`);
|
|
130
|
+
return sha.trim();
|
|
131
|
+
}
|
|
132
|
+
async function runBash(exec, command) {
|
|
133
|
+
const out = await execBash(exec, command);
|
|
134
|
+
if (out.exitCode !== 0) {
|
|
135
|
+
throw new Error(`bash failed (${out.exitCode}): ${command}\n${out.stderr}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function captureBash(exec, command) {
|
|
139
|
+
const out = await execBash(exec, command);
|
|
140
|
+
if (out.exitCode !== 0) {
|
|
141
|
+
throw new Error(`bash failed (${out.exitCode}): ${command}\n${out.stderr}`);
|
|
142
|
+
}
|
|
143
|
+
return out.stdout;
|
|
144
|
+
}
|
|
145
|
+
async function execBash(exec, command) {
|
|
146
|
+
const out = await exec({ command }, { toolCallId: `reviewer-bash-${Date.now()}`, messages: [] });
|
|
147
|
+
if (isAsyncIterable(out)) {
|
|
148
|
+
throw new Error('bash tool returned an async iterable; expected a single result');
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
function isAsyncIterable(v) {
|
|
153
|
+
return v !== null && typeof v === 'object' && Symbol.asyncIterator in v;
|
|
154
|
+
}
|
|
155
|
+
// POSIX shell-quote: wrap in single quotes, escape embedded single quotes.
|
|
156
|
+
function shQuote(s) {
|
|
157
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=reviewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewer.js","sourceRoot":"","sources":["../../src/subagents/reviewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { BashInput, BashOutput, EditFileInput, EditFileOutput, GlobInput, GlobOutput, GrepInput, GrepOutput, MultiEditInput, MultiEditOutput, ReadFileInput, ReadFileOutput, WriteFileInput, WriteFileOutput } from '@developerz.ai/ai-claude-compat';
|
|
2
|
+
import { type DeepPartial, Output, type Tool, type ToolLoopAgent } from 'ai';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import type { PrGroup } from '../state/schema.ts';
|
|
5
|
+
import type { SubagentInit } from './factory.ts';
|
|
6
|
+
export type WorkerTools = {
|
|
7
|
+
readFile: Tool<ReadFileInput, ReadFileOutput>;
|
|
8
|
+
writeFile: Tool<WriteFileInput, WriteFileOutput>;
|
|
9
|
+
editFile: Tool<EditFileInput, EditFileOutput>;
|
|
10
|
+
multiEdit: Tool<MultiEditInput, MultiEditOutput>;
|
|
11
|
+
grep: Tool<GrepInput, GrepOutput>;
|
|
12
|
+
glob: Tool<GlobInput, GlobOutput>;
|
|
13
|
+
bash: Tool<BashInput, BashOutput>;
|
|
14
|
+
};
|
|
15
|
+
export declare const FileManifestEntrySchema: z.ZodObject<{
|
|
16
|
+
path: z.ZodString;
|
|
17
|
+
kind: z.ZodEnum<{
|
|
18
|
+
create: "create";
|
|
19
|
+
modify: "modify";
|
|
20
|
+
delete: "delete";
|
|
21
|
+
}>;
|
|
22
|
+
purpose: z.ZodString;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
export type FileManifestEntry = z.infer<typeof FileManifestEntrySchema>;
|
|
25
|
+
export declare const FileManifestSchema: z.ZodObject<{
|
|
26
|
+
files: z.ZodArray<z.ZodObject<{
|
|
27
|
+
path: z.ZodString;
|
|
28
|
+
kind: z.ZodEnum<{
|
|
29
|
+
create: "create";
|
|
30
|
+
modify: "modify";
|
|
31
|
+
delete: "delete";
|
|
32
|
+
}>;
|
|
33
|
+
purpose: z.ZodString;
|
|
34
|
+
}, z.core.$strip>>;
|
|
35
|
+
draftCommitMessage: z.ZodString;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
export type FileManifest = z.infer<typeof FileManifestSchema>;
|
|
38
|
+
type WorkerOutput = Output.Output<FileManifest, DeepPartial<FileManifest>, never>;
|
|
39
|
+
export type WorkerAgent = ToolLoopAgent<never, WorkerTools, WorkerOutput>;
|
|
40
|
+
export type WorkerInput = {
|
|
41
|
+
group: PrGroup;
|
|
42
|
+
worktreePath: string;
|
|
43
|
+
baseBranch: string;
|
|
44
|
+
styleContents: string;
|
|
45
|
+
rollingContext: string;
|
|
46
|
+
};
|
|
47
|
+
export type FileChange = {
|
|
48
|
+
path: string;
|
|
49
|
+
kind: 'create' | 'modify' | 'delete';
|
|
50
|
+
summary: string;
|
|
51
|
+
};
|
|
52
|
+
export type WorkerDelivery = {
|
|
53
|
+
branch: string;
|
|
54
|
+
draftCommitMessage: string;
|
|
55
|
+
changes: FileChange[];
|
|
56
|
+
progressEntries: string[];
|
|
57
|
+
};
|
|
58
|
+
export type WorkerResult = {
|
|
59
|
+
kind: 'ok';
|
|
60
|
+
delivery: WorkerDelivery;
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'blocked';
|
|
63
|
+
reason: string;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'error';
|
|
66
|
+
error: string;
|
|
67
|
+
};
|
|
68
|
+
export declare const WORKER_SYSTEM_PREFIX: string;
|
|
69
|
+
export declare function createWorkerAgent(init: SubagentInit<WorkerTools>): WorkerAgent;
|
|
70
|
+
export declare function runWorker(agent: WorkerAgent, input: WorkerInput): Promise<WorkerResult>;
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// docs/subagents.md (Worker row), docs/task-groups.md, docs/commands/start.md
|
|
2
|
+
// One PR group: produce file changes + commits on a dedicated branch. Does NOT open the PR
|
|
3
|
+
// and does NOT finalize the commit message — those belong to the Orchestrator (more reliable
|
|
4
|
+
// at composing global-context narration: PR title, body, squash commit message).
|
|
5
|
+
//
|
|
6
|
+
// Strategy for *really big PRs* (the explicit design goal) — two layers of parallelism:
|
|
7
|
+
//
|
|
8
|
+
// Layer A (outer, across files): plan a file manifest via Output.object() that lists every
|
|
9
|
+
// file to create/modify/delete (docs/vendor/ai-sdk/chunk-09.md §"Orchestrator-Worker"),
|
|
10
|
+
// then Promise.all over per-file editor sub-subagents.
|
|
11
|
+
//
|
|
12
|
+
// Layer B (inner, within one step): each editor enables `parallelToolCalls: true` (default
|
|
13
|
+
// in the SDK — chunk-02.md §"parallelToolCalls") so the model can issue multiple readFile /
|
|
14
|
+
// writeFile tool calls in a single step and the runtime executes them concurrently.
|
|
15
|
+
//
|
|
16
|
+
// SDK references:
|
|
17
|
+
// chunk-09.md §"Orchestrator-Worker" (manifest + per-file workers)
|
|
18
|
+
// chunk-09.md §"Subagents" §"Controlling What the Model Sees" (toModelOutput one-line summary)
|
|
19
|
+
// chunk-04.md §"ToolLoopAgent" (agent class)
|
|
20
|
+
// chunk-02.md §"Tool Calling" (parallelToolCalls)
|
|
21
|
+
import { composeSystemPrompt, createSubagent } from '@developerz.ai/ai-claude-compat';
|
|
22
|
+
import { generateText, Output, stepCountIs, } from 'ai';
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
// File manifest — Phase 1 structured output. Each entry drives one editor in Phase 2.
|
|
25
|
+
export const FileManifestEntrySchema = z.object({
|
|
26
|
+
path: z.string().min(1),
|
|
27
|
+
kind: z.enum(['create', 'modify', 'delete']),
|
|
28
|
+
purpose: z.string().min(1),
|
|
29
|
+
});
|
|
30
|
+
export const FileManifestSchema = z.object({
|
|
31
|
+
files: z.array(FileManifestEntrySchema),
|
|
32
|
+
draftCommitMessage: z.string().min(1),
|
|
33
|
+
});
|
|
34
|
+
export const WORKER_SYSTEM_PREFIX = [
|
|
35
|
+
'',
|
|
36
|
+
'You are the Worker subagent. You receive one PR group: a coherent batch of tasks that',
|
|
37
|
+
'land in a single pull request on a dedicated branch. Work in two phases.',
|
|
38
|
+
'',
|
|
39
|
+
'Phase 1 — manifest. Use your read-only tools (readFile with optional offset/limit, grep,',
|
|
40
|
+
'glob) to ground yourself in the existing code, then emit a FileManifest JSON listing every',
|
|
41
|
+
'file to create/modify/delete plus a one-line draft commit message. Do not edit yet.',
|
|
42
|
+
'',
|
|
43
|
+
'Phase 2 — edits. Each manifest entry is handed to a dedicated editor subagent in',
|
|
44
|
+
'parallel by the runtime; you do not execute Phase 2 yourself.',
|
|
45
|
+
'',
|
|
46
|
+
'Rules:',
|
|
47
|
+
'- Stay inside the worktree provided. No work outside the repo.',
|
|
48
|
+
'- One responsibility per file. If a file has multiple unrelated edits, split it.',
|
|
49
|
+
'- draftCommitMessage is a hint to the Orchestrator; keep the subject under 72 chars.',
|
|
50
|
+
'- Return the FileManifest JSON exactly matching the schema.',
|
|
51
|
+
].join('\n');
|
|
52
|
+
// Editor subagent prompt — applied to every per-file fanout. Kept here so the Worker
|
|
53
|
+
// owns the contract its editors run under.
|
|
54
|
+
const EDITOR_SYSTEM_PREFIX = [
|
|
55
|
+
'',
|
|
56
|
+
'You are a per-file editor subagent. You receive one file path and a purpose.',
|
|
57
|
+
'- To CREATE a file, emit its full contents via `writeFile`.',
|
|
58
|
+
'- To MODIFY an existing file, `readFile` it first, then prefer `editFile` (one exact string',
|
|
59
|
+
' replacement) or `multiEdit` (several replacements applied atomically). Use `writeFile` only',
|
|
60
|
+
' for a full rewrite.',
|
|
61
|
+
'- To DELETE a file, use `bash` with `rm -f <path>`.',
|
|
62
|
+
'You may issue multiple tool calls in parallel.',
|
|
63
|
+
'',
|
|
64
|
+
"IMPORTANT: your final assistant message is returned to the outer Worker as this file's",
|
|
65
|
+
'summary. Keep it to one line, present-tense, and specific.',
|
|
66
|
+
].join('\n');
|
|
67
|
+
// Module-private link from a Worker agent back to its init, so runWorker can spawn editor
|
|
68
|
+
// sub-agents with the same model + tool handles without exposing them on the public surface.
|
|
69
|
+
const workerInitRegistry = new WeakMap();
|
|
70
|
+
export function createWorkerAgent(init) {
|
|
71
|
+
const agent = createSubagent({
|
|
72
|
+
model: init.model,
|
|
73
|
+
tools: init.tools,
|
|
74
|
+
systemPrompt: init.systemPrompt,
|
|
75
|
+
output: Output.object({ schema: FileManifestSchema, name: 'FileManifest' }),
|
|
76
|
+
...(init.maxSteps !== undefined ? { maxSteps: init.maxSteps } : {}),
|
|
77
|
+
}, 30);
|
|
78
|
+
workerInitRegistry.set(agent, init);
|
|
79
|
+
return agent;
|
|
80
|
+
}
|
|
81
|
+
export async function runWorker(agent, input) {
|
|
82
|
+
const init = workerInitRegistry.get(agent);
|
|
83
|
+
if (!init) {
|
|
84
|
+
return {
|
|
85
|
+
kind: 'error',
|
|
86
|
+
error: 'runWorker called with an agent not built by createWorkerAgent',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const branch = input.group.branch ?? `aitm/${input.group.id}`;
|
|
90
|
+
try {
|
|
91
|
+
const manifest = await planManifest(agent, input);
|
|
92
|
+
if (manifest.files.length === 0) {
|
|
93
|
+
return { kind: 'blocked', reason: 'worker produced an empty file manifest' };
|
|
94
|
+
}
|
|
95
|
+
const changes = await Promise.all(manifest.files.map((file) => runEditor(init, file, input)));
|
|
96
|
+
await commitOnBranch(init.tools.bash, input, branch, manifest.draftCommitMessage);
|
|
97
|
+
return {
|
|
98
|
+
kind: 'ok',
|
|
99
|
+
delivery: {
|
|
100
|
+
branch,
|
|
101
|
+
draftCommitMessage: manifest.draftCommitMessage,
|
|
102
|
+
changes,
|
|
103
|
+
progressEntries: input.group.tasks.map((task) => `- ${task}`),
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return { kind: 'error', error: err instanceof Error ? err.message : String(err) };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function planManifest(agent, input) {
|
|
112
|
+
const result = await agent.generate({ prompt: buildManifestPrompt(input) });
|
|
113
|
+
return result.experimental_output;
|
|
114
|
+
}
|
|
115
|
+
function buildManifestPrompt(input) {
|
|
116
|
+
const lines = [
|
|
117
|
+
`PR group: ${input.group.id} — ${input.group.title}`,
|
|
118
|
+
`Branch: ${input.group.branch ?? `aitm/${input.group.id}`}`,
|
|
119
|
+
`Base branch: ${input.baseBranch}`,
|
|
120
|
+
`Worktree: ${input.worktreePath}`,
|
|
121
|
+
'',
|
|
122
|
+
'Tasks in this PR group:',
|
|
123
|
+
...input.group.tasks.map((task, i) => ` ${i + 1}. ${task}`),
|
|
124
|
+
];
|
|
125
|
+
if (input.rollingContext.trim()) {
|
|
126
|
+
lines.push('', 'Rolling context from prior PRs:', input.rollingContext);
|
|
127
|
+
}
|
|
128
|
+
lines.push('', 'Survey the repo, then emit the FileManifest JSON.');
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
async function runEditor(init, file, input) {
|
|
132
|
+
const { text } = await generateText({
|
|
133
|
+
model: init.model,
|
|
134
|
+
tools: init.tools,
|
|
135
|
+
system: composeSystemPrompt(input.styleContents, EDITOR_SYSTEM_PREFIX, input.worktreePath),
|
|
136
|
+
prompt: buildEditorPrompt(file, input),
|
|
137
|
+
stopWhen: stepCountIs(12),
|
|
138
|
+
providerOptions: { openai: { parallelToolCalls: true } },
|
|
139
|
+
});
|
|
140
|
+
const firstLine = text.trim().split('\n')[0];
|
|
141
|
+
const summary = firstLine && firstLine.length > 0 ? firstLine : `${file.kind} ${file.path}`;
|
|
142
|
+
return { path: file.path, kind: file.kind, summary };
|
|
143
|
+
}
|
|
144
|
+
function buildEditorPrompt(file, input) {
|
|
145
|
+
return [
|
|
146
|
+
`Worktree: ${input.worktreePath}`,
|
|
147
|
+
`File: ${file.path}`,
|
|
148
|
+
`Change kind: ${file.kind}`,
|
|
149
|
+
`Purpose: ${file.purpose}`,
|
|
150
|
+
'',
|
|
151
|
+
'Make the change. Reply with a one-line summary.',
|
|
152
|
+
].join('\n');
|
|
153
|
+
}
|
|
154
|
+
async function commitOnBranch(bash, input, branch, message) {
|
|
155
|
+
const exec = bash.execute;
|
|
156
|
+
if (typeof exec !== 'function') {
|
|
157
|
+
throw new Error('bash tool is missing an execute function');
|
|
158
|
+
}
|
|
159
|
+
const wt = shQuote(input.worktreePath);
|
|
160
|
+
await runBash(exec, `git -C ${wt} checkout -B ${shQuote(branch)}`);
|
|
161
|
+
await runBash(exec, `git -C ${wt} add -A`);
|
|
162
|
+
await runBash(exec, `git -C ${wt} commit -m ${shQuote(message)}`);
|
|
163
|
+
}
|
|
164
|
+
async function runBash(exec, command) {
|
|
165
|
+
const out = await exec({ command }, { toolCallId: `worker-bash-${Date.now()}`, messages: [] });
|
|
166
|
+
if (isAsyncIterable(out)) {
|
|
167
|
+
throw new Error('bash tool returned an async iterable; expected a single result');
|
|
168
|
+
}
|
|
169
|
+
if (out.exitCode !== 0) {
|
|
170
|
+
throw new Error(`bash failed (${out.exitCode}): ${command}\n${out.stderr}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function isAsyncIterable(v) {
|
|
174
|
+
return v !== null && typeof v === 'object' && Symbol.asyncIterator in v;
|
|
175
|
+
}
|
|
176
|
+
// POSIX shell-quote: wrap in single quotes, escape embedded single quotes.
|
|
177
|
+
function shQuote(s) {
|
|
178
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/subagents/worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Shared test helper: spin up a throwaway git repo in a tempdir.
|
|
2
|
+
// Used by *.test.ts (unit) for filesystem-touching cases AND by test/integration/*.test.ts.
|
|
3
|
+
// docs/runtime.md §Testing — integration tests run against real temp git repos.
|
|
4
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { execa } from 'execa';
|
|
8
|
+
export async function makeTempRepo(opts) {
|
|
9
|
+
const path = await mkdtemp(join(tmpdir(), 'aitm-test-'));
|
|
10
|
+
await execa('git', ['init'], { cwd: path });
|
|
11
|
+
await execa('git', ['config', 'user.email', 'test@aitm.local'], { cwd: path });
|
|
12
|
+
await execa('git', ['config', 'user.name', 'aitm-test'], { cwd: path });
|
|
13
|
+
if (opts?.withClaudeMd) {
|
|
14
|
+
await writeFile(join(path, 'CLAUDE.md'), '# CLAUDE.md\n');
|
|
15
|
+
}
|
|
16
|
+
const cleanup = async () => {
|
|
17
|
+
await rm(path, { recursive: true, force: true });
|
|
18
|
+
};
|
|
19
|
+
return { path, cleanup };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=temp-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-repo.js","sourceRoot":"","sources":["../../src/testing/temp-repo.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,4FAA4F;AAC5F,gFAAgF;AAEhF,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAO9B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAiC;IAClE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IAEzD,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/E,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAExE,IAAI,IAAI,EAAE,YAAY,EAAE,CAAC;QACvB,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,eAAe,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACxC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const datetimeInputSchema: z.ZodObject<{
|
|
4
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export type DatetimeInput = z.infer<typeof datetimeInputSchema>;
|
|
7
|
+
export type DatetimeOutput = {
|
|
8
|
+
datetime: string;
|
|
9
|
+
timezone: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function datetimeTool(): Tool;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Local datetime tool — implemented as a Vercel AI SDK function tool, NOT as
|
|
2
|
+
// `openrouter:datetime` (server tool). Local is faster (no round-trip), free, and
|
|
3
|
+
// not subject to the server-tool beta API. The server-tool variant offers zero
|
|
4
|
+
// capability beyond `new Date()` + `Intl.DateTimeFormat`.
|
|
5
|
+
//
|
|
6
|
+
// SDK ref: docs/vendor/ai-sdk/chunk-02.md §"Tool Calling" — tool({ description, inputSchema, execute }).
|
|
7
|
+
import { tool } from 'ai';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
// Reject empty string and invalid IANA tz strings; allow `undefined` from `.optional()`.
|
|
10
|
+
// `toLocaleString` throws on both empty and unknown timezones, so we validate upfront.
|
|
11
|
+
function isValidTimezone(tz) {
|
|
12
|
+
if (tz === undefined)
|
|
13
|
+
return true;
|
|
14
|
+
if (tz === '')
|
|
15
|
+
return false;
|
|
16
|
+
try {
|
|
17
|
+
new Intl.DateTimeFormat('en-US', { timeZone: tz });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const datetimeInputSchema = z.object({
|
|
25
|
+
timezone: z.string().optional().refine(isValidTimezone, { message: 'Invalid IANA timezone' }),
|
|
26
|
+
});
|
|
27
|
+
export function datetimeTool() {
|
|
28
|
+
return tool({
|
|
29
|
+
description: 'Get the current date and time, optionally formatted for a specific timezone',
|
|
30
|
+
inputSchema: datetimeInputSchema,
|
|
31
|
+
execute: async (input) => {
|
|
32
|
+
const datetime = new Date().toLocaleString('en-US', {
|
|
33
|
+
timeZone: input.timezone,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
datetime,
|
|
37
|
+
timezone: input.timezone ?? '',
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=datetime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datetime.js","sourceRoot":"","sources":["../../src/tools/datetime.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,kFAAkF;AAClF,+EAA+E;AAC/E,0DAA0D;AAC1D,EAAE;AACF,yGAAyG;AAGzG,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,yFAAyF;AACzF,uFAAuF;AACvF,SAAS,eAAe,CAAC,EAAsB;IAC7C,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;CAC9F,CAAC,CAAC;AASH,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC;QACV,WAAW,EAAE,6EAA6E;QAC1F,WAAW,EAAE,mBAAmB;QAChC,OAAO,EAAE,KAAK,EAAE,KAAoB,EAA2B,EAAE;YAC/D,MAAM,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE;gBAClD,QAAQ,EAAE,KAAK,CAAC,QAAQ;aACzB,CAAC,CAAC;YACH,OAAO;gBACL,QAAQ;gBACR,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;aAC/B,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { type LookupFn, type WebFetchOutput } from './web-fetch.ts';
|
|
4
|
+
declare const fetchHtmlInputSchema: z.ZodObject<{
|
|
5
|
+
url: z.ZodString;
|
|
6
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
impersonate: z.ZodOptional<z.ZodEnum<{
|
|
9
|
+
chrome: "chrome";
|
|
10
|
+
firefox: "firefox";
|
|
11
|
+
safari: "safari";
|
|
12
|
+
}>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export type FetchHtmlInput = z.infer<typeof fetchHtmlInputSchema>;
|
|
15
|
+
export type ImpersonateTarget = z.infer<typeof fetchHtmlInputSchema>['impersonate'];
|
|
16
|
+
export declare const DEFAULT_IMPERSONATE_TARGETS: Readonly<Record<'chrome' | 'firefox' | 'safari', string>>;
|
|
17
|
+
export type ExecLike = (file: string, args: readonly string[], options?: {
|
|
18
|
+
timeout?: number;
|
|
19
|
+
maxBuffer?: number;
|
|
20
|
+
}) => Promise<{
|
|
21
|
+
stdout: unknown;
|
|
22
|
+
stderr: unknown;
|
|
23
|
+
}>;
|
|
24
|
+
export type FetchHtmlInit = {
|
|
25
|
+
binary?: string;
|
|
26
|
+
targets?: Record<'chrome' | 'firefox' | 'safari', string>;
|
|
27
|
+
lookup?: LookupFn;
|
|
28
|
+
exec?: ExecLike;
|
|
29
|
+
};
|
|
30
|
+
export declare function isFetchHtmlAvailable(init?: FetchHtmlInit): Promise<boolean>;
|
|
31
|
+
export declare function fetchHtmlTool(init?: FetchHtmlInit): Tool<FetchHtmlInput, WebFetchOutput>;
|
|
32
|
+
export {};
|