@tangle-network/agent-runtime 0.23.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -498
- package/dist/agent.d.ts +5 -206
- package/dist/chunk-GLR25NG7.js +92 -0
- package/dist/chunk-GLR25NG7.js.map +1 -0
- package/dist/{chunk-IQHYOJU3.js → chunk-ZJACJZF7.js} +289 -1
- package/dist/chunk-ZJACJZF7.js.map +1 -0
- package/dist/improvement-adapter-CaZxFxTd.d.ts +207 -0
- package/dist/improvement.d.ts +120 -0
- package/dist/improvement.js +161 -0
- package/dist/improvement.js.map +1 -0
- package/dist/local-harness-KrdFTY5R.d.ts +82 -0
- package/dist/mcp/bin.js +2 -1
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +190 -2
- package/dist/mcp/index.js +20 -8
- package/dist/mcp/index.js.map +1 -1
- package/package.json +7 -2
- package/dist/chunk-IQHYOJU3.js.map +0 -1
package/dist/agent.d.ts
CHANGED
|
@@ -1,103 +1,9 @@
|
|
|
1
1
|
import * as _tangle_network_agent_eval from '@tangle-network/agent-eval';
|
|
2
|
-
import {
|
|
2
|
+
import { TraceAnalystKindSpec, AnalystFinding } from '@tangle-network/agent-eval';
|
|
3
3
|
import { R as RuntimeStreamEvent } from './types-BFgFD_sl.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* `AgentSurfaces` — declarative map of the mutable file/directory paths
|
|
8
|
-
* the self-improvement loop can edit on behalf of an agent.
|
|
9
|
-
*
|
|
10
|
-
* The substrate uses this map to resolve every parsed `FindingSubject`
|
|
11
|
-
* (from agent-eval) to a real on-disk path. No per-vertical glue;
|
|
12
|
-
* no fabricated paths; no silent `existsSync(...)` skips that hide
|
|
13
|
-
* misconfiguration from the operator.
|
|
14
|
-
*
|
|
15
|
-
* Surfaces are validated at `defineAgent` time — missing paths fail
|
|
16
|
-
* loud with a list of every offender. A surface that's not needed
|
|
17
|
-
* (e.g. an agent with no RAG corpora) is simply omitted; the loop
|
|
18
|
-
* refuses to route those subjects rather than fabricating a target.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Surface declarations. Every path is repo-relative (or absolute) at
|
|
23
|
-
* `defineAgent` time. At resolution time, paths are joined against the
|
|
24
|
-
* agent's `repoRoot`.
|
|
25
|
-
*
|
|
26
|
-
* `systemPrompt`, `tools`, `personas` are DIRECTORIES; the loop appends
|
|
27
|
-
* `<section>.md`, `<tool>/README.md`, `<persona-id>.yaml` etc.
|
|
28
|
-
* `rubric`, `outputSchema` are SINGLE FILES; the loop edits them in
|
|
29
|
-
* place.
|
|
30
|
-
*
|
|
31
|
-
* `knowledge` is the agent-knowledge root (typically `.agent-knowledge`);
|
|
32
|
-
* `applyKnowledgeWriteBlocks` writes pages relative to it.
|
|
33
|
-
*
|
|
34
|
-
* Optional surfaces (`scaffolding`, `memory`, `rag`, `outputSchema`)
|
|
35
|
-
* can be omitted — the loop will reject findings targeting them with a
|
|
36
|
-
* clear log message instead of fabricating a path.
|
|
37
|
-
*/
|
|
38
|
-
interface AgentSurfaces {
|
|
39
|
-
/** Directory containing one markdown file per system-prompt section. */
|
|
40
|
-
systemPrompt: string;
|
|
41
|
-
/** Directory containing one subdir per tool (`<tool>/README.md`). */
|
|
42
|
-
tools: string;
|
|
43
|
-
/** Single file (TypeScript module) defining the rubric weights + dimensions. */
|
|
44
|
-
rubric: string;
|
|
45
|
-
/** Knowledge-base root; typically `.agent-knowledge`. */
|
|
46
|
-
knowledge: string;
|
|
47
|
-
/** Directory containing one YAML/JSON file per persona. */
|
|
48
|
-
personas: string;
|
|
49
|
-
/** Optional: directory containing scaffolding rules (precondition checks, retry policies). */
|
|
50
|
-
scaffolding?: string;
|
|
51
|
-
/** Optional: memory store path (JSONL / SQLite / DB). */
|
|
52
|
-
memory?: string;
|
|
53
|
-
/** Optional: directory containing RAG corpora (`<corpus>/<doc-id>.md`). */
|
|
54
|
-
rag?: string;
|
|
55
|
-
/** Optional: single file defining the output schema (Zod / JSON Schema). */
|
|
56
|
-
outputSchema?: string;
|
|
57
|
-
}
|
|
58
|
-
interface ResolvedSurface {
|
|
59
|
-
/** Absolute filesystem path the operator can `cat` / `vim`. */
|
|
60
|
-
absolutePath: string;
|
|
61
|
-
/** Repo-relative path for PR descriptions, diffs, audit logs. */
|
|
62
|
-
repoRelativePath: string;
|
|
63
|
-
/** Whether the path currently exists on disk. */
|
|
64
|
-
exists: boolean;
|
|
65
|
-
/** The substrate's intent: edit an existing file or create a new one. */
|
|
66
|
-
intent: 'edit-existing' | 'create-new';
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Resolve a parsed `FindingSubject` to the file path the substrate
|
|
70
|
-
* should edit (or create) on disk.
|
|
71
|
-
*
|
|
72
|
-
* Returns `null` when:
|
|
73
|
-
* - the subject targets a surface the agent didn't declare
|
|
74
|
-
* (e.g. `rag:*` when `surfaces.rag` is undefined), OR
|
|
75
|
-
* - the subject is a `cluster` (failure-mode emits these as evidence,
|
|
76
|
-
* not actionable mutations — they don't route to a file).
|
|
77
|
-
*
|
|
78
|
-
* Returns a `ResolvedSurface` with `intent: 'create-new'` when the
|
|
79
|
-
* subject names a path that doesn't yet exist (e.g. a new wiki page).
|
|
80
|
-
* The caller chooses whether to honour the create — for tightly-managed
|
|
81
|
-
* surfaces like `systemPrompt` it's usually a contract violation
|
|
82
|
-
* (the analyst named a section that doesn't exist); for `knowledge`
|
|
83
|
-
* it's the whole point.
|
|
84
|
-
*/
|
|
85
|
-
declare function resolveSubjectPath(subject: FindingSubject, surfaces: AgentSurfaces, repoRoot: string): ResolvedSurface | null;
|
|
86
|
-
/**
|
|
87
|
-
* Validate that every declared surface exists on disk under `repoRoot`.
|
|
88
|
-
*
|
|
89
|
-
* Returns an array of `SurfaceValidationIssue` — empty when all required
|
|
90
|
-
* surfaces resolve. `defineAgent` throws with the issues rendered, so
|
|
91
|
-
* a misconfigured manifest fails at startup (not at the first finding
|
|
92
|
-
* the loop produces 20 minutes later).
|
|
93
|
-
*/
|
|
94
|
-
interface SurfaceValidationIssue {
|
|
95
|
-
surface: keyof AgentSurfaces;
|
|
96
|
-
path: string;
|
|
97
|
-
reason: 'missing' | 'not-directory' | 'not-file';
|
|
98
|
-
}
|
|
99
|
-
declare function validateSurfaces(surfaces: AgentSurfaces, repoRoot: string): ReadonlyArray<SurfaceValidationIssue>;
|
|
100
|
-
declare function renderSurfaceIssues(issues: ReadonlyArray<SurfaceValidationIssue>, repoRoot: string): string;
|
|
4
|
+
import { A as AgentSurfaces } from './improvement-adapter-CaZxFxTd.js';
|
|
5
|
+
export { C as CreateSurfaceImprovementAdapterOpts, D as DraftPatchInput, a as DraftPatchOutput, R as ResolvedSurface, S as SurfaceImprovementEdit, b as SurfaceValidationIssue, c as createSurfaceImprovementAdapter, r as renderSurfaceIssues, d as resolveSubjectPath, v as validateSurfaces } from './improvement-adapter-CaZxFxTd.js';
|
|
6
|
+
import { K as KnowledgeAdapter, a as RunAnalystLoopResult } from './types-D_MXrmJP.js';
|
|
101
7
|
|
|
102
8
|
/**
|
|
103
9
|
* The full agent manifest. Each agent ships ONE of these.
|
|
@@ -342,113 +248,6 @@ declare class AgentManifestError extends Error {
|
|
|
342
248
|
*/
|
|
343
249
|
declare function defineAgent<TPersona = unknown, TRunOutput = unknown>(manifest: AgentManifest<TPersona, TRunOutput>): AgentManifest<TPersona, TRunOutput>;
|
|
344
250
|
|
|
345
|
-
/**
|
|
346
|
-
* Substrate-default `ImprovementAdapter` — surfaces-driven, LLM-drafted
|
|
347
|
-
* patches, optional auto-apply or PR-open.
|
|
348
|
-
*
|
|
349
|
-
* This is the one ImprovementAdapter every vertical agent uses. The
|
|
350
|
-
* substrate parses each finding's `subject` via
|
|
351
|
-
* `parseFindingSubject` (agent-eval), resolves it to a real file path
|
|
352
|
-
* via the agent's `AgentSurfaces`, reads the current content, and asks
|
|
353
|
-
* an LLM to draft a unified-diff patch given the finding + current
|
|
354
|
-
* content + per-kind editing-discipline rules.
|
|
355
|
-
*
|
|
356
|
-
* Auto-apply gates on the source-finding's confidence and the
|
|
357
|
-
* autoApply.improvement policy. Two modes:
|
|
358
|
-
* `write` — apply the patch in-place via `git apply -p0`. Operator
|
|
359
|
-
* reviews via `git diff`.
|
|
360
|
-
* `open-pr` — write to a branch, commit, push, open a PR via `gh`.
|
|
361
|
-
* Operator reviews via the PR UI.
|
|
362
|
-
*
|
|
363
|
-
* Fail-loud rules:
|
|
364
|
-
* - Findings whose subject doesn't parse → counted in `errors`.
|
|
365
|
-
* - Findings whose subject targets an undeclared surface → counted in
|
|
366
|
-
* `errors` with the offending kind in the message.
|
|
367
|
-
* - Findings whose target path doesn't exist AND the kind isn't a
|
|
368
|
-
* create-new variant (`new-tool`, `knowledge.wiki`) → counted in
|
|
369
|
-
* `errors` with the resolved path in the message.
|
|
370
|
-
* - LLM drafts that fail JSON-schema validation → counted in
|
|
371
|
-
* `errors` with the schema issue.
|
|
372
|
-
*
|
|
373
|
-
* No silent skips. Every dropped finding has a recorded reason the
|
|
374
|
-
* loop's report surfaces.
|
|
375
|
-
*/
|
|
376
|
-
|
|
377
|
-
interface SurfaceImprovementEdit {
|
|
378
|
-
/** Stable id derived from the source finding so re-proposals are idempotent. */
|
|
379
|
-
id: string;
|
|
380
|
-
/** The finding that produced this edit — for revert + audit trail. */
|
|
381
|
-
sourceFindingId: string;
|
|
382
|
-
/** Parsed subject; included so the apply step doesn't re-parse. */
|
|
383
|
-
subject: FindingSubject;
|
|
384
|
-
/** Resolved on-disk target. */
|
|
385
|
-
target: ResolvedSurface;
|
|
386
|
-
/** SHA-256 of the current file content the patch was drafted against. */
|
|
387
|
-
baseSha256: string;
|
|
388
|
-
/** Unified-diff patch the LLM drafted (relative to `target.absolutePath`). */
|
|
389
|
-
patch: string;
|
|
390
|
-
/** One-line summary the operator sees in the report / PR title. */
|
|
391
|
-
summary: string;
|
|
392
|
-
/** Multi-line rationale for the PR body — finding context + LLM reasoning. */
|
|
393
|
-
rationale: string;
|
|
394
|
-
/** Carry-forward from the finding so the apply gate can check the threshold. */
|
|
395
|
-
confidence: number;
|
|
396
|
-
/** Carry-forward severity for prioritization. */
|
|
397
|
-
severity: AnalystFinding['severity'];
|
|
398
|
-
}
|
|
399
|
-
interface CreateSurfaceImprovementAdapterOpts {
|
|
400
|
-
surfaces: AgentSurfaces;
|
|
401
|
-
repoRoot: string;
|
|
402
|
-
/**
|
|
403
|
-
* LLM-draft callback. Given a finding + current file content + the
|
|
404
|
-
* resolved target, returns a unified-diff patch + summary + rationale.
|
|
405
|
-
*
|
|
406
|
-
* Required — the substrate doesn't ship a hardcoded prompt; the agent
|
|
407
|
-
* author picks the model (Haiku for cheap routine drafts, Sonnet for
|
|
408
|
-
* substantive prompt rewrites, etc.) via this callback.
|
|
409
|
-
*/
|
|
410
|
-
draftPatch: (input: DraftPatchInput) => Promise<DraftPatchOutput>;
|
|
411
|
-
/**
|
|
412
|
-
* Apply mode:
|
|
413
|
-
* `write` — `git apply` in-place; operator reviews via `git diff`
|
|
414
|
-
* `open-pr` — branch + commit + push + `gh pr create`
|
|
415
|
-
* `none` — never apply; collect proposals for the report only
|
|
416
|
-
*
|
|
417
|
-
* The `apply` method honours this even when the loop calls it; the
|
|
418
|
-
* effective behaviour is also gated on the per-finding confidence
|
|
419
|
-
* threshold via `runAnalystLoop`'s `autoApply` policy.
|
|
420
|
-
*/
|
|
421
|
-
mode?: 'write' | 'open-pr' | 'none';
|
|
422
|
-
/** When `mode === 'open-pr'`, the base branch new PRs target. Default: `main`. */
|
|
423
|
-
baseBranch?: string;
|
|
424
|
-
/** Required for `mode === 'open-pr'` — the GH owner/repo (`tangle-network/tax-agent`). */
|
|
425
|
-
ghRepo?: string;
|
|
426
|
-
/**
|
|
427
|
-
* When the resolved target doesn't exist, allow the substrate to
|
|
428
|
-
* CREATE the file (for `knowledge.wiki`, `new-tool` subjects). Default
|
|
429
|
-
* true for those kinds, false for `system-prompt` / `rubric` / etc.
|
|
430
|
-
* (named sections that don't exist are a contract violation, not a
|
|
431
|
-
* scaffolding opportunity).
|
|
432
|
-
*/
|
|
433
|
-
allowCreateForKinds?: ReadonlyArray<FindingSubject['kind']>;
|
|
434
|
-
}
|
|
435
|
-
interface DraftPatchInput {
|
|
436
|
-
finding: AnalystFinding;
|
|
437
|
-
subject: FindingSubject;
|
|
438
|
-
target: ResolvedSurface;
|
|
439
|
-
/** Current file content (empty string when `intent === 'create-new'`). */
|
|
440
|
-
currentContent: string;
|
|
441
|
-
}
|
|
442
|
-
interface DraftPatchOutput {
|
|
443
|
-
/** Unified diff against the current file content. Empty string skips this finding. */
|
|
444
|
-
patch: string;
|
|
445
|
-
/** One-line summary for the operator. */
|
|
446
|
-
summary: string;
|
|
447
|
-
/** Multi-line rationale for the PR body. */
|
|
448
|
-
rationale: string;
|
|
449
|
-
}
|
|
450
|
-
declare function createSurfaceImprovementAdapter(opts: CreateSurfaceImprovementAdapterOpts): ImprovementAdapter<SurfaceImprovementEdit>;
|
|
451
|
-
|
|
452
251
|
/**
|
|
453
252
|
* Substrate-default `KnowledgeAdapter` — wraps agent-knowledge's
|
|
454
253
|
* `proposeFromFindings` + `applyKnowledgeWriteBlocks` with substrate
|
|
@@ -580,4 +379,4 @@ declare function measureOutcome<TProposal, TEdit>(result: RunAnalystLoopResult<T
|
|
|
580
379
|
outcome: OutcomeMeasurement;
|
|
581
380
|
}>;
|
|
582
381
|
|
|
583
|
-
export { type AgentManifest, AgentManifestError, type AgentRubric, type AgentRunContext, type AgentRunInvocation, type AgentRuntime,
|
|
382
|
+
export { type AgentManifest, AgentManifestError, type AgentRubric, type AgentRunContext, type AgentRunInvocation, type AgentRuntime, AgentSurfaces, type AnalystConfig, type AutoApplyPolicy, type CreateSurfaceKnowledgeAdapterOpts, type JudgeConfig, type KnowledgeAdapterDeps, type OutcomeMeasurement, type OutcomeMeasurementOpts, type RubricDimension, collectAgentRun, createSurfaceKnowledgeAdapter, defineAgent, measureOutcome, unimplementedAgentRun };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/mcp/local-harness.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
var HARNESS_INVOCATIONS = {
|
|
4
|
+
claude: {
|
|
5
|
+
command: "claude",
|
|
6
|
+
buildArgs: (taskPrompt) => ["--headless", "-p", taskPrompt]
|
|
7
|
+
},
|
|
8
|
+
codex: {
|
|
9
|
+
command: "codex",
|
|
10
|
+
buildArgs: (taskPrompt) => ["run", taskPrompt]
|
|
11
|
+
},
|
|
12
|
+
opencode: {
|
|
13
|
+
command: "opencode",
|
|
14
|
+
buildArgs: (taskPrompt) => ["run", taskPrompt]
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
18
|
+
function runLocalHarness(options) {
|
|
19
|
+
const { harness, cwd, taskPrompt } = options;
|
|
20
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
21
|
+
const env = options.env ?? process.env;
|
|
22
|
+
const spawnImpl = options.spawn ?? spawn;
|
|
23
|
+
const invocation = HARNESS_INVOCATIONS[harness];
|
|
24
|
+
if (!invocation) {
|
|
25
|
+
return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`));
|
|
26
|
+
}
|
|
27
|
+
const startedAt = Date.now();
|
|
28
|
+
const args = invocation.buildArgs(taskPrompt);
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
let child;
|
|
31
|
+
try {
|
|
32
|
+
child = spawnImpl(invocation.command, args, { cwd, env, stdio: "pipe" });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let stdout = "";
|
|
38
|
+
let stderr = "";
|
|
39
|
+
let timedOut = false;
|
|
40
|
+
let settled = false;
|
|
41
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
42
|
+
timedOut = true;
|
|
43
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
44
|
+
}, timeoutMs) : null;
|
|
45
|
+
if (timer && typeof timer.unref === "function") {
|
|
46
|
+
;
|
|
47
|
+
timer.unref();
|
|
48
|
+
}
|
|
49
|
+
const onAbort = () => {
|
|
50
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
51
|
+
};
|
|
52
|
+
if (options.signal) {
|
|
53
|
+
if (options.signal.aborted) onAbort();
|
|
54
|
+
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
55
|
+
}
|
|
56
|
+
child.stdout?.on("data", (chunk) => {
|
|
57
|
+
stdout += String(chunk);
|
|
58
|
+
});
|
|
59
|
+
child.stderr?.on("data", (chunk) => {
|
|
60
|
+
stderr += String(chunk);
|
|
61
|
+
});
|
|
62
|
+
const finalize = (result) => {
|
|
63
|
+
if (settled) return;
|
|
64
|
+
settled = true;
|
|
65
|
+
if (timer) clearTimeout(timer);
|
|
66
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
67
|
+
resolve(result);
|
|
68
|
+
};
|
|
69
|
+
child.on("error", (err) => {
|
|
70
|
+
if (settled) return;
|
|
71
|
+
settled = true;
|
|
72
|
+
if (timer) clearTimeout(timer);
|
|
73
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
74
|
+
reject(err);
|
|
75
|
+
});
|
|
76
|
+
child.on("close", (code, signal) => {
|
|
77
|
+
finalize({
|
|
78
|
+
exitCode: code,
|
|
79
|
+
stdout,
|
|
80
|
+
stderr,
|
|
81
|
+
killedBySignal: signal,
|
|
82
|
+
durationMs: Date.now() - startedAt,
|
|
83
|
+
timedOut
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
runLocalHarness
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=chunk-GLR25NG7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/local-harness.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Subprocess wrappers for the local coding-harness CLIs installed in the\n * sandbox image (claude-code, codex, opencode). Used by the in-process\n * delegation executor (`createInProcessExecutor`) so a `delegate_code` call\n * spawns a real harness on a real git worktree instead of provisioning a\n * sibling sandbox.\n *\n * All harness invocations:\n * - run with `cwd` set to the worktree\n * - inherit env from the parent (the MCP server inside the sandbox has\n * the harness's auth already)\n * - capture stdout/stderr\n * - support cancellation via AbortSignal\n * - enforce a wall-clock timeout\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process'\n\n/** Local coding harness available inside the sandbox. */\nexport type LocalHarness = 'claude' | 'codex' | 'opencode'\n\n/** Default per-harness command + arg shape. */\nconst HARNESS_INVOCATIONS: Record<\n LocalHarness,\n { command: string; buildArgs: (taskPrompt: string) => string[] }\n> = {\n claude: {\n command: 'claude',\n buildArgs: (taskPrompt) => ['--headless', '-p', taskPrompt],\n },\n codex: {\n command: 'codex',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n },\n opencode: {\n command: 'opencode',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n },\n}\n\n/** @experimental */\nexport interface RunLocalHarnessOptions {\n harness: LocalHarness\n /** Working directory for the subprocess (typically a worktree path). */\n cwd: string\n /** Prompt forwarded as the harness CLI's task argument. */\n taskPrompt: string\n /** Wall-clock kill deadline (ms). Default 5 min. Subprocess SIGTERMed on expiry. */\n timeoutMs?: number\n /** Caller cancellation. SIGTERM is sent on abort. */\n signal?: AbortSignal\n /** Override env (defaults to inheriting from the parent). */\n env?: NodeJS.ProcessEnv\n /**\n * Test seam — inject a custom spawner so unit tests can mock the\n * subprocess without touching the OS. Defaults to node's `child_process.spawn`.\n */\n spawn?: (\n command: string,\n args: ReadonlyArray<string>,\n opts: {\n cwd: string\n env: NodeJS.ProcessEnv\n stdio: 'pipe'\n },\n ) => ChildProcess\n}\n\n/** @experimental */\nexport interface LocalHarnessResult {\n /** OS exit code. `null` when killed before exit. */\n exitCode: number | null\n /** Concatenated stdout. */\n stdout: string\n /** Concatenated stderr. */\n stderr: string\n /** Set when the process exited via signal (timeout / abort). */\n killedBySignal: NodeJS.Signals | null\n /** Wall-clock duration ms (spawn → exit). */\n durationMs: number\n /** Set when timeoutMs elapsed before exit. */\n timedOut: boolean\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000\n\n/**\n * Spawn a local coding harness CLI as a subprocess + collect its output.\n *\n * NOT responsible for parsing the harness's output or extracting a diff —\n * the in-process executor's `streamPrompt` orchestrates `git diff` against\n * the worktree after this resolves. This function is intentionally narrow:\n * spawn, wait, capture, return.\n *\n * Fails loud — throws when:\n * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)\n * - the harness binary is not on PATH (ENOENT)\n *\n * Does NOT throw when:\n * - the subprocess exits non-zero (`result.exitCode` carries the code)\n * - the subprocess is aborted / timed out (`result.killedBySignal` /\n * `result.timedOut` carries the reason)\n *\n * @experimental\n */\nexport function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult> {\n const { harness, cwd, taskPrompt } = options\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const env = options.env ?? process.env\n const spawnImpl = options.spawn ?? spawn\n\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`))\n }\n\n const startedAt = Date.now()\n const args = invocation.buildArgs(taskPrompt)\n\n return new Promise<LocalHarnessResult>((resolve, reject) => {\n let child: ChildProcess\n try {\n child = spawnImpl(invocation.command, args, { cwd, env, stdio: 'pipe' })\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n return\n }\n\n let stdout = ''\n let stderr = ''\n let timedOut = false\n let settled = false\n\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n if (!child.killed) child.kill('SIGTERM')\n }, timeoutMs)\n : null\n if (timer && typeof (timer as { unref?: () => void }).unref === 'function') {\n ;(timer as { unref: () => void }).unref()\n }\n\n const onAbort = () => {\n if (!child.killed) child.kill('SIGTERM')\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, { once: true })\n }\n\n child.stdout?.on('data', (chunk) => {\n stdout += String(chunk)\n })\n child.stderr?.on('data', (chunk) => {\n stderr += String(chunk)\n })\n\n const finalize = (result: LocalHarnessResult) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n resolve(result)\n }\n\n child.on('error', (err) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n reject(err)\n })\n\n child.on('close', (code, signal) => {\n finalize({\n exitCode: code,\n stdout,\n stderr,\n killedBySignal: signal,\n durationMs: Date.now() - startedAt,\n timedOut,\n })\n })\n })\n}\n"],"mappings":";AAkBA,SAA4B,aAAa;AAMzC,IAAM,sBAGF;AAAA,EACF,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,cAAc,MAAM,UAAU;AAAA,EAC5D;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,OAAO,UAAU;AAAA,EAC/C;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,WAAW,CAAC,eAAe,CAAC,OAAO,UAAU;AAAA,EAC/C;AACF;AA8CA,IAAM,qBAAqB,IAAI,KAAK;AAqB7B,SAAS,gBAAgB,SAA8D;AAC5F,QAAM,EAAE,SAAS,KAAK,WAAW,IAAI;AACrC,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,YAAY,QAAQ,SAAS;AAEnC,QAAM,aAAa,oBAAoB,OAAO;AAC9C,MAAI,CAAC,YAAY;AACf,WAAO,QAAQ,OAAO,IAAI,MAAM,oCAAoC,OAAO,OAAO,CAAC,EAAE,CAAC;AAAA,EACxF;AAEA,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,OAAO,WAAW,UAAU,UAAU;AAE5C,SAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAC1D,QAAI;AACJ,QAAI;AACF,cAAQ,UAAU,WAAW,SAAS,MAAM,EAAE,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,IACzE,SAAS,KAAK;AACZ,aAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAC1D;AAAA,IACF;AAEA,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,UAAU;AAEd,UAAM,QACJ,YAAY,IACR,WAAW,MAAM;AACf,iBAAW;AACX,UAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,SAAS;AAAA,IACzC,GAAG,SAAS,IACZ;AACN,QAAI,SAAS,OAAQ,MAAiC,UAAU,YAAY;AAC1E;AAAC,MAAC,MAAgC,MAAM;AAAA,IAC1C;AAEA,UAAM,UAAU,MAAM;AACpB,UAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,SAAS;AAAA,IACzC;AACA,QAAI,QAAQ,QAAQ;AAClB,UAAI,QAAQ,OAAO,QAAS,SAAQ;AAAA,UAC/B,SAAQ,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,IACvE;AAEA,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AACD,UAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU;AAClC,gBAAU,OAAO,KAAK;AAAA,IACxB,CAAC;AAED,UAAM,WAAW,CAAC,WAA+B;AAC/C,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ,QAAQ,oBAAoB,SAAS,OAAO;AACpD,aAAO,GAAG;AAAA,IACZ,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,MAAM,WAAW;AAClC,eAAS;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,YAAY,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
|
@@ -22,6 +22,9 @@ import {
|
|
|
22
22
|
createDelegationHistoryHandler,
|
|
23
23
|
createDelegationStatusHandler
|
|
24
24
|
} from "./chunk-UNQM6XQO.js";
|
|
25
|
+
import {
|
|
26
|
+
runLocalHarness
|
|
27
|
+
} from "./chunk-GLR25NG7.js";
|
|
25
28
|
import {
|
|
26
29
|
runLoop
|
|
27
30
|
} from "./chunk-TZ53F7M7.js";
|
|
@@ -96,9 +99,276 @@ function readId(box) {
|
|
|
96
99
|
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
// src/mcp/worktree.ts
|
|
103
|
+
import { spawn } from "child_process";
|
|
104
|
+
async function runGitAsync(args, cwd, runner) {
|
|
105
|
+
if (runner) return runner(args, { cwd });
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const proc = spawn("git", args, { cwd, stdio: "pipe" });
|
|
108
|
+
let stdout = "";
|
|
109
|
+
let stderr = "";
|
|
110
|
+
proc.stdout?.on("data", (c) => {
|
|
111
|
+
stdout += String(c);
|
|
112
|
+
});
|
|
113
|
+
proc.stderr?.on("data", (c) => {
|
|
114
|
+
stderr += String(c);
|
|
115
|
+
});
|
|
116
|
+
proc.on("error", reject);
|
|
117
|
+
proc.on("close", (code) => resolve({ stdout, stderr, exitCode: code ?? -1 }));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function ensureGitOk(step, result) {
|
|
121
|
+
if (result.exitCode !== 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`worktree: git ${step} failed (exit ${result.exitCode}): ${result.stderr.slice(0, 400)}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function createWorktree(options) {
|
|
128
|
+
const variants = options.variantsDir ?? ".coder-variants";
|
|
129
|
+
const baseRef = options.baseRef ?? "HEAD";
|
|
130
|
+
const branch = `delegate/${options.runId}`;
|
|
131
|
+
const path = `${options.repoRoot.replace(/\/+$/, "")}/${variants}/${options.runId}`;
|
|
132
|
+
const headSha = await runGitAsync(["rev-parse", baseRef], options.repoRoot, options.runGit);
|
|
133
|
+
ensureGitOk(`rev-parse ${baseRef}`, headSha);
|
|
134
|
+
const add = await runGitAsync(
|
|
135
|
+
["worktree", "add", "-b", branch, path, baseRef],
|
|
136
|
+
options.repoRoot,
|
|
137
|
+
options.runGit
|
|
138
|
+
);
|
|
139
|
+
ensureGitOk(`worktree add ${path}`, add);
|
|
140
|
+
return { path, baseSha: headSha.stdout.trim(), branch };
|
|
141
|
+
}
|
|
142
|
+
async function captureWorktreeDiff(options) {
|
|
143
|
+
const baseRef = options.baseRef ?? options.worktree.baseSha;
|
|
144
|
+
const patch = await runGitAsync(["diff", baseRef], options.worktree.path, options.runGit);
|
|
145
|
+
const shortstat = await runGitAsync(
|
|
146
|
+
["diff", "--shortstat", baseRef],
|
|
147
|
+
options.worktree.path,
|
|
148
|
+
options.runGit
|
|
149
|
+
);
|
|
150
|
+
const stats = parseShortstat(shortstat.stdout);
|
|
151
|
+
return { patch: patch.stdout, stats };
|
|
152
|
+
}
|
|
153
|
+
function parseShortstat(text) {
|
|
154
|
+
const out = { filesChanged: 0, insertions: 0, deletions: 0 };
|
|
155
|
+
const filesMatch = text.match(/(\d+)\s+files?\s+changed/);
|
|
156
|
+
if (filesMatch?.[1]) out.filesChanged = Number(filesMatch[1]);
|
|
157
|
+
const insertMatch = text.match(/(\d+)\s+insertions?/);
|
|
158
|
+
if (insertMatch?.[1]) out.insertions = Number(insertMatch[1]);
|
|
159
|
+
const deleteMatch = text.match(/(\d+)\s+deletions?/);
|
|
160
|
+
if (deleteMatch?.[1]) out.deletions = Number(deleteMatch[1]);
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
async function removeWorktree(options) {
|
|
164
|
+
const force = options.force ?? true;
|
|
165
|
+
const args = ["worktree", "remove"];
|
|
166
|
+
if (force) args.push("--force");
|
|
167
|
+
args.push(options.worktree.path);
|
|
168
|
+
const result = await runGitAsync(args, options.repoRoot, options.runGit);
|
|
169
|
+
if (result.exitCode !== 0 && !/not a working tree/.test(result.stderr)) {
|
|
170
|
+
await runGitAsync(
|
|
171
|
+
["branch", "-D", options.worktree.branch],
|
|
172
|
+
options.repoRoot,
|
|
173
|
+
options.runGit
|
|
174
|
+
).catch(() => void 0);
|
|
175
|
+
}
|
|
176
|
+
await runGitAsync(
|
|
177
|
+
["branch", "-D", options.worktree.branch],
|
|
178
|
+
options.repoRoot,
|
|
179
|
+
options.runGit
|
|
180
|
+
).catch(() => void 0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/mcp/in-process-executor.ts
|
|
184
|
+
import { randomUUID } from "crypto";
|
|
185
|
+
var DEFAULT_HARNESS_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
186
|
+
var DEFAULT_POSTCHECK_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
187
|
+
function createInProcessExecutor(options) {
|
|
188
|
+
const harnesses = options.harnesses && options.harnesses.length > 0 ? [...options.harnesses] : ["claude"];
|
|
189
|
+
const runHarness = options.runHarness ?? runLocalHarness;
|
|
190
|
+
const runPostCheck = options.runPostCheck ?? defaultRunPostCheck;
|
|
191
|
+
let callIndex = 0;
|
|
192
|
+
const client = {
|
|
193
|
+
async create(_opts) {
|
|
194
|
+
const runId = randomUUID();
|
|
195
|
+
const harness = harnesses[callIndex % harnesses.length];
|
|
196
|
+
callIndex += 1;
|
|
197
|
+
const virtual = {
|
|
198
|
+
// Synthesize the minimum SandboxInstance surface the kernel touches.
|
|
199
|
+
// We CAST through unknown because SandboxInstance is a `declare class`
|
|
200
|
+
// with private fields; we're producing a structural subtype that
|
|
201
|
+
// satisfies the kernel's narrow usage (`box.id`, `box.streamPrompt`).
|
|
202
|
+
id: `in-process-${runId}`,
|
|
203
|
+
__inProcess: { runId, harness },
|
|
204
|
+
// eslint-disable-next-line require-yield
|
|
205
|
+
async *streamPrompt(message, promptOpts) {
|
|
206
|
+
const taskPrompt = typeof message === "string" ? message : message.map(
|
|
207
|
+
(p) => typeof p === "object" && p && "text" in p ? String(p.text) : ""
|
|
208
|
+
).join("\n");
|
|
209
|
+
let worktree;
|
|
210
|
+
try {
|
|
211
|
+
worktree = await createWorktree({
|
|
212
|
+
repoRoot: options.repoRoot,
|
|
213
|
+
runId,
|
|
214
|
+
runGit: options.runGit
|
|
215
|
+
});
|
|
216
|
+
this.__inProcess.worktree = worktree;
|
|
217
|
+
yield {
|
|
218
|
+
type: "in_process.harness.started",
|
|
219
|
+
data: {
|
|
220
|
+
runId,
|
|
221
|
+
harness,
|
|
222
|
+
worktreePath: worktree.path,
|
|
223
|
+
command: harness
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const harnessResult = await runHarness({
|
|
227
|
+
harness,
|
|
228
|
+
cwd: worktree.path,
|
|
229
|
+
taskPrompt,
|
|
230
|
+
timeoutMs: options.harnessTimeoutMs ?? DEFAULT_HARNESS_TIMEOUT_MS,
|
|
231
|
+
signal: promptOpts?.signal
|
|
232
|
+
});
|
|
233
|
+
yield {
|
|
234
|
+
type: "in_process.harness.ended",
|
|
235
|
+
data: {
|
|
236
|
+
runId,
|
|
237
|
+
exitCode: harnessResult.exitCode,
|
|
238
|
+
durationMs: harnessResult.durationMs,
|
|
239
|
+
killedBySignal: harnessResult.killedBySignal,
|
|
240
|
+
timedOut: harnessResult.timedOut,
|
|
241
|
+
stdoutBytes: harnessResult.stdout.length,
|
|
242
|
+
stderrBytes: harnessResult.stderr.length
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const diff = await captureWorktreeDiff({ worktree, runGit: options.runGit });
|
|
246
|
+
const testCheck = options.testCmd ? await runPostCheck(options.testCmd, worktree.path, promptOpts?.signal).catch(
|
|
247
|
+
(err) => ({
|
|
248
|
+
exitCode: -1,
|
|
249
|
+
stdout: "",
|
|
250
|
+
stderr: err instanceof Error ? err.message : String(err)
|
|
251
|
+
})
|
|
252
|
+
) : { exitCode: 0, stdout: "", stderr: "" };
|
|
253
|
+
const typecheckCheck = options.typecheckCmd ? await runPostCheck(options.typecheckCmd, worktree.path, promptOpts?.signal).catch(
|
|
254
|
+
(err) => ({
|
|
255
|
+
exitCode: -1,
|
|
256
|
+
stdout: "",
|
|
257
|
+
stderr: err instanceof Error ? err.message : String(err)
|
|
258
|
+
})
|
|
259
|
+
) : { exitCode: 0, stdout: "", stderr: "" };
|
|
260
|
+
const coderOutput = {
|
|
261
|
+
branch: worktree.branch,
|
|
262
|
+
patch: diff.patch,
|
|
263
|
+
testResult: {
|
|
264
|
+
passed: !options.testCmd || testCheck.exitCode === 0,
|
|
265
|
+
output: tail(testCheck.stderr || testCheck.stdout, 4e3)
|
|
266
|
+
},
|
|
267
|
+
typecheckResult: {
|
|
268
|
+
passed: !options.typecheckCmd || typecheckCheck.exitCode === 0,
|
|
269
|
+
output: tail(typecheckCheck.stderr || typecheckCheck.stdout, 4e3)
|
|
270
|
+
},
|
|
271
|
+
diffStats: diff.stats,
|
|
272
|
+
reviewerNotes: harnessResult.exitCode === 0 ? void 0 : `harness ${harness} exited ${harnessResult.exitCode}${harnessResult.timedOut ? " (timed out)" : ""}`
|
|
273
|
+
};
|
|
274
|
+
yield {
|
|
275
|
+
type: "result",
|
|
276
|
+
data: {
|
|
277
|
+
result: coderOutput,
|
|
278
|
+
source: "in-process-executor",
|
|
279
|
+
harness,
|
|
280
|
+
runId
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
} finally {
|
|
284
|
+
if (worktree) {
|
|
285
|
+
await removeWorktree({
|
|
286
|
+
worktree,
|
|
287
|
+
repoRoot: options.repoRoot,
|
|
288
|
+
runGit: options.runGit
|
|
289
|
+
}).catch(() => void 0);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
return virtual;
|
|
295
|
+
},
|
|
296
|
+
describePlacement(box) {
|
|
297
|
+
const sandboxId = box.id;
|
|
298
|
+
const meta = box.__inProcess;
|
|
299
|
+
return {
|
|
300
|
+
kind: "sibling",
|
|
301
|
+
sandboxId,
|
|
302
|
+
worktreePath: meta?.worktree?.path,
|
|
303
|
+
harness: meta?.harness
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
return {
|
|
308
|
+
client,
|
|
309
|
+
describe() {
|
|
310
|
+
return `in-process (repoRoot=${options.repoRoot}, harnesses=[${harnesses.join(",")}]${options.testCmd ? `, testCmd="${options.testCmd}"` : ""}${options.typecheckCmd ? `, typecheckCmd="${options.typecheckCmd}"` : ""})`;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async function defaultRunPostCheck(cmd, cwd, signal) {
|
|
315
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
316
|
+
return new Promise((resolve, reject) => {
|
|
317
|
+
const child = spawn2("sh", ["-c", cmd], { cwd, stdio: "pipe" });
|
|
318
|
+
let stdout = "";
|
|
319
|
+
let stderr = "";
|
|
320
|
+
child.stdout?.on("data", (c) => {
|
|
321
|
+
stdout += String(c);
|
|
322
|
+
});
|
|
323
|
+
child.stderr?.on("data", (c) => {
|
|
324
|
+
stderr += String(c);
|
|
325
|
+
});
|
|
326
|
+
if (signal) {
|
|
327
|
+
const onAbort = () => {
|
|
328
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
329
|
+
};
|
|
330
|
+
if (signal.aborted) onAbort();
|
|
331
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
332
|
+
}
|
|
333
|
+
const killTimer = setTimeout(() => {
|
|
334
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
335
|
+
}, DEFAULT_POSTCHECK_TIMEOUT_MS);
|
|
336
|
+
if (typeof killTimer.unref === "function") {
|
|
337
|
+
;
|
|
338
|
+
killTimer.unref();
|
|
339
|
+
}
|
|
340
|
+
child.on("error", (err) => {
|
|
341
|
+
clearTimeout(killTimer);
|
|
342
|
+
reject(err);
|
|
343
|
+
});
|
|
344
|
+
child.on("close", (code) => {
|
|
345
|
+
clearTimeout(killTimer);
|
|
346
|
+
resolve({ exitCode: code ?? -1, stdout, stderr });
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function tail(text, max) {
|
|
351
|
+
if (text.length <= max) return text;
|
|
352
|
+
return text.slice(text.length - max);
|
|
353
|
+
}
|
|
354
|
+
|
|
99
355
|
// src/mcp/bin-helpers.ts
|
|
100
356
|
async function detectExecutor(args) {
|
|
101
357
|
const env = args.env ?? process.env;
|
|
358
|
+
if (env.AGENT_RUNTIME_IN_SANDBOX === "1") {
|
|
359
|
+
const repoRoot = env.AGENT_RUNTIME_REPO_ROOT?.trim();
|
|
360
|
+
if (!repoRoot) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
"agent-runtime-mcp: AGENT_RUNTIME_IN_SANDBOX=1 requires AGENT_RUNTIME_REPO_ROOT to point at the workspace root"
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
return createInProcessExecutor({
|
|
366
|
+
repoRoot,
|
|
367
|
+
harnesses: parseHarnesses(env.AGENT_RUNTIME_LOCAL_HARNESSES),
|
|
368
|
+
testCmd: env.AGENT_RUNTIME_TEST_CMD?.trim() || void 0,
|
|
369
|
+
typecheckCmd: env.AGENT_RUNTIME_TYPECHECK_CMD?.trim() || void 0
|
|
370
|
+
});
|
|
371
|
+
}
|
|
102
372
|
const fleetId = parseFleetId(env.TANGLE_FLEET_ID);
|
|
103
373
|
if (!fleetId) {
|
|
104
374
|
return createSiblingSandboxExecutor({ client: args.sandboxClient });
|
|
@@ -111,6 +381,20 @@ async function detectExecutor(args) {
|
|
|
111
381
|
excludeMachineIds
|
|
112
382
|
});
|
|
113
383
|
}
|
|
384
|
+
var KNOWN_HARNESSES = ["claude", "codex", "opencode"];
|
|
385
|
+
function parseHarnesses(raw) {
|
|
386
|
+
if (!raw) return void 0;
|
|
387
|
+
const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
388
|
+
if (parts.length === 0) return void 0;
|
|
389
|
+
for (const part of parts) {
|
|
390
|
+
if (!KNOWN_HARNESSES.includes(part)) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
`agent-runtime-mcp: AGENT_RUNTIME_LOCAL_HARNESSES contains unknown harness "${part}". Expected: ${KNOWN_HARNESSES.join(", ")}.`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return parts;
|
|
397
|
+
}
|
|
114
398
|
async function defaultResolveFleet(sandboxClient, fleetId) {
|
|
115
399
|
const fleets = sandboxClient.fleets;
|
|
116
400
|
if (!fleets || typeof fleets.get !== "function") {
|
|
@@ -419,9 +703,13 @@ function createInProcessTransport() {
|
|
|
419
703
|
export {
|
|
420
704
|
createSiblingSandboxExecutor,
|
|
421
705
|
createFleetWorkspaceExecutor,
|
|
706
|
+
createWorktree,
|
|
707
|
+
captureWorktreeDiff,
|
|
708
|
+
removeWorktree,
|
|
709
|
+
createInProcessExecutor,
|
|
422
710
|
detectExecutor,
|
|
423
711
|
createDefaultCoderDelegate,
|
|
424
712
|
createMcpServer,
|
|
425
713
|
createInProcessTransport
|
|
426
714
|
};
|
|
427
|
-
//# sourceMappingURL=chunk-
|
|
715
|
+
//# sourceMappingURL=chunk-ZJACJZF7.js.map
|