github-router 0.3.202 → 0.3.206
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/browser-ext/manifest.json +1 -1
- package/dist/{engine-CIt6z9K3.js → engine-DZjN7BuD.js} +1 -1
- package/dist/main.js +166 -60
- package/dist/main.js.map +1 -1
- package/dist/paths-BO22pMUb.js.map +1 -1
- package/dist/{peer-mcp-personas-rik0SIrg.js → peer-mcp-personas-C3ii7ZqP.js} +241 -56
- package/dist/peer-mcp-personas-C3ii7ZqP.js.map +1 -0
- package/package.json +1 -1
- package/dist/peer-mcp-personas-rik0SIrg.js.map +0 -1
|
@@ -1367,7 +1367,7 @@ var ArtifactClient = class {
|
|
|
1367
1367
|
attempt += 1;
|
|
1368
1368
|
if (!(err instanceof ArtifactError && err.retryable && retryableCodes.has(err.code)) || attempt > retries || signal?.aborted) throw err;
|
|
1369
1369
|
const base = this.retryBaseMs;
|
|
1370
|
-
await sleep$
|
|
1370
|
+
await sleep$3(base <= 0 ? 0 : Math.round(base * 2 ** (attempt - 1) * (.5 + Math.random() * .5)), signal);
|
|
1371
1371
|
}
|
|
1372
1372
|
}
|
|
1373
1373
|
async requestOnce(o) {
|
|
@@ -1431,7 +1431,7 @@ var ArtifactClient = class {
|
|
|
1431
1431
|
}
|
|
1432
1432
|
}
|
|
1433
1433
|
};
|
|
1434
|
-
function sleep$
|
|
1434
|
+
function sleep$3(ms, signal) {
|
|
1435
1435
|
if (ms <= 0) return Promise.resolve();
|
|
1436
1436
|
return new Promise((resolve, reject) => {
|
|
1437
1437
|
const onAbort = () => {
|
|
@@ -3133,7 +3133,7 @@ function isHardNotReady(reason) {
|
|
|
3133
3133
|
*/
|
|
3134
3134
|
async function waitForMessageReady(client, localId, options = {}) {
|
|
3135
3135
|
const now = options.now ?? Date.now;
|
|
3136
|
-
const sleep$
|
|
3136
|
+
const sleep$4 = options.sleep ?? realSleep;
|
|
3137
3137
|
const waitMs = Math.max(0, options.waitMs ?? 0);
|
|
3138
3138
|
const pollMs = Math.max(1, options.pollMs ?? DEFAULT_READY_POLL_MS);
|
|
3139
3139
|
const deadline = now() + waitMs;
|
|
@@ -3165,7 +3165,7 @@ async function waitForMessageReady(client, localId, options = {}) {
|
|
|
3165
3165
|
ready: false,
|
|
3166
3166
|
readiness: last
|
|
3167
3167
|
};
|
|
3168
|
-
await sleep$
|
|
3168
|
+
await sleep$4(Math.min(pollMs, remaining));
|
|
3169
3169
|
}
|
|
3170
3170
|
}
|
|
3171
3171
|
/**
|
|
@@ -3243,7 +3243,7 @@ async function primeTurnCursor(client, localId, timeoutMs, signal) {
|
|
|
3243
3243
|
*/
|
|
3244
3244
|
async function waitForTurnSettled(client, localId, options) {
|
|
3245
3245
|
const now = options.now ?? Date.now;
|
|
3246
|
-
const sleep$
|
|
3246
|
+
const sleep$4 = options.sleep ?? realSleep;
|
|
3247
3247
|
const pollTimeoutMs = Math.max(1, options.pollTimeoutMs ?? DEFAULT_TURN_POLL_MS);
|
|
3248
3248
|
const budget = Number.isFinite(options.timeoutMs) ? Math.max(0, options.timeoutMs) : 0;
|
|
3249
3249
|
const deadline = now() + budget;
|
|
@@ -3275,7 +3275,7 @@ async function waitForTurnSettled(client, localId, options) {
|
|
|
3275
3275
|
reason: "timeout",
|
|
3276
3276
|
cursor
|
|
3277
3277
|
};
|
|
3278
|
-
await sleep$
|
|
3278
|
+
await sleep$4(Math.min(TURN_POLL_ERROR_BACKOFF_MS, Math.max(0, deadline - now())));
|
|
3279
3279
|
continue;
|
|
3280
3280
|
}
|
|
3281
3281
|
cursor = response.cursor;
|
|
@@ -3392,13 +3392,13 @@ async function readTail(client, localId, lines, signal) {
|
|
|
3392
3392
|
async function driveTask(deps) {
|
|
3393
3393
|
const { client, localId, prompt, timeoutMs, expectReport, idempotencyKey, interruptKey, reportId, signal } = deps;
|
|
3394
3394
|
const now = deps.now ?? Date.now;
|
|
3395
|
-
const sleep$
|
|
3395
|
+
const sleep$4 = deps.sleep ?? realSleep;
|
|
3396
3396
|
const tailLines = deps.tailLines ?? DEFAULT_TAIL_LINES;
|
|
3397
3397
|
const pollTimeoutMs = deps.pollTimeoutMs;
|
|
3398
3398
|
const readyResult = await waitForMessageReady(client, localId, {
|
|
3399
3399
|
waitMs: deps.idleWaitMs ?? DEFAULT_IDLE_WAIT_MS,
|
|
3400
3400
|
now,
|
|
3401
|
-
sleep: sleep$
|
|
3401
|
+
sleep: sleep$4,
|
|
3402
3402
|
signal
|
|
3403
3403
|
});
|
|
3404
3404
|
if (!readyResult.ready && isHardNotReady(readyResult.readiness.reason)) return {
|
|
@@ -3441,7 +3441,7 @@ async function driveTask(deps) {
|
|
|
3441
3441
|
pollTimeoutMs,
|
|
3442
3442
|
cursor,
|
|
3443
3443
|
now,
|
|
3444
|
-
sleep: sleep$
|
|
3444
|
+
sleep: sleep$4,
|
|
3445
3445
|
signal
|
|
3446
3446
|
});
|
|
3447
3447
|
cursor = settle.cursor;
|
|
@@ -3463,7 +3463,7 @@ async function driveTask(deps) {
|
|
|
3463
3463
|
pollTimeoutMs,
|
|
3464
3464
|
cursor,
|
|
3465
3465
|
now,
|
|
3466
|
-
sleep: sleep$
|
|
3466
|
+
sleep: sleep$4,
|
|
3467
3467
|
signal
|
|
3468
3468
|
});
|
|
3469
3469
|
recovered = recovery.settled;
|
|
@@ -5732,7 +5732,7 @@ function runFenced(token, fn) {
|
|
|
5732
5732
|
function currentFenceToken() {
|
|
5733
5733
|
return fenceStore.getStore();
|
|
5734
5734
|
}
|
|
5735
|
-
function sleep$
|
|
5735
|
+
function sleep$2(ms) {
|
|
5736
5736
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5737
5737
|
}
|
|
5738
5738
|
async function writeJsonSecure(target, value) {
|
|
@@ -5779,7 +5779,7 @@ async function withFileLock(target, fn) {
|
|
|
5779
5779
|
}
|
|
5780
5780
|
} catch {}
|
|
5781
5781
|
if (Date.now() - start > LOCK_MAX_WAIT_MS) throw new Error(`first-mate durable-store lock timeout for ${target}`);
|
|
5782
|
-
await sleep$
|
|
5782
|
+
await sleep$2(LOCK_RETRY_MS);
|
|
5783
5783
|
}
|
|
5784
5784
|
const verifyOwner = async () => {
|
|
5785
5785
|
try {
|
|
@@ -5858,7 +5858,7 @@ async function commitJsonCas(opts) {
|
|
|
5858
5858
|
};
|
|
5859
5859
|
if (explicit) throw new DurableConflictError(`durable store rev changed under CAS for ${opts.path} (expected ${base})`);
|
|
5860
5860
|
lastConflict = new DurableConflictError(`durable store contention for ${opts.path} (attempt ${attempt + 1}/${OCC_MAX_ATTEMPTS})`);
|
|
5861
|
-
await sleep$
|
|
5861
|
+
await sleep$2(Math.floor(OCC_BACKOFF_MS * (attempt + 1) * (.5 + Math.random())));
|
|
5862
5862
|
}
|
|
5863
5863
|
throw lastConflict ?? new DurableConflictError(`durable store failed to converge for ${opts.path}`);
|
|
5864
5864
|
}
|
|
@@ -9956,6 +9956,40 @@ function segment(value) {
|
|
|
9956
9956
|
|
|
9957
9957
|
//#endregion
|
|
9958
9958
|
//#region src/lib/first-mate/scheduler/answer-inbox.ts
|
|
9959
|
+
const sleep$1 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9960
|
+
/**
|
|
9961
|
+
* Windows sharing-violation codes. When two processes rename the SAME source
|
|
9962
|
+
* concurrently, POSIX gives the loser a clean `ENOENT` (source already gone),
|
|
9963
|
+
* but Windows can transiently surface `EPERM`/`EACCES`/`EBUSY` (the source is
|
|
9964
|
+
* momentarily locked by the peer's in-flight rename) before it resolves to
|
|
9965
|
+
* ENOENT. Treat these as retryable, not fatal.
|
|
9966
|
+
*/
|
|
9967
|
+
const WIN_RENAME_TRANSIENT = new Set([
|
|
9968
|
+
"EPERM",
|
|
9969
|
+
"EACCES",
|
|
9970
|
+
"EBUSY"
|
|
9971
|
+
]);
|
|
9972
|
+
/**
|
|
9973
|
+
* Atomically claim `from` by renaming it to the process-unique `to`.
|
|
9974
|
+
* Returns `true` if THIS caller won the claim, `false` if a peer already took it
|
|
9975
|
+
* (source gone). Retries transient Windows sharing violations so two concurrent
|
|
9976
|
+
* drainers converge to exactly one winner instead of one throwing. Non-transient
|
|
9977
|
+
* errors propagate.
|
|
9978
|
+
*/
|
|
9979
|
+
async function claimByRename(from, to) {
|
|
9980
|
+
for (let attempt = 0;; attempt++) try {
|
|
9981
|
+
await fs.rename(from, to);
|
|
9982
|
+
return true;
|
|
9983
|
+
} catch (err) {
|
|
9984
|
+
const code = err.code;
|
|
9985
|
+
if (code === "ENOENT") return false;
|
|
9986
|
+
if (WIN_RENAME_TRANSIENT.has(code ?? "") && attempt < 25) {
|
|
9987
|
+
await sleep$1(4 + attempt);
|
|
9988
|
+
continue;
|
|
9989
|
+
}
|
|
9990
|
+
throw err;
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9959
9993
|
var AnswerInbox = class {
|
|
9960
9994
|
file;
|
|
9961
9995
|
chain = Promise.resolve();
|
|
@@ -10032,12 +10066,7 @@ var AnswerInbox = class {
|
|
|
10032
10066
|
const orphan = nodePath.join(dir, name);
|
|
10033
10067
|
if (this.inflight.has(orphan)) continue;
|
|
10034
10068
|
const claim = `${orphan}.claim.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
10035
|
-
|
|
10036
|
-
await fs.rename(orphan, claim);
|
|
10037
|
-
} catch (err) {
|
|
10038
|
-
if (err.code === "ENOENT") continue;
|
|
10039
|
-
throw err;
|
|
10040
|
-
}
|
|
10069
|
+
if (!await claimByRename(orphan, claim)) continue;
|
|
10041
10070
|
try {
|
|
10042
10071
|
this.mergeLines(await fs.readFile(claim, "utf8"), out);
|
|
10043
10072
|
claimed.push(claim);
|
|
@@ -10046,12 +10075,11 @@ var AnswerInbox = class {
|
|
|
10046
10075
|
}
|
|
10047
10076
|
}
|
|
10048
10077
|
const target = `${this.file}.draining.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
10049
|
-
try {
|
|
10050
|
-
await fs.rename(this.file, target);
|
|
10078
|
+
if (await claimByRename(this.file, target)) try {
|
|
10051
10079
|
this.mergeLines(await fs.readFile(target, "utf8"), out);
|
|
10052
10080
|
claimed.push(target);
|
|
10053
10081
|
} catch (err) {
|
|
10054
|
-
if (err.code !== "ENOENT")
|
|
10082
|
+
if (err.code !== "ENOENT") consola.warn(`first-mate: deferring unreadable inbox claim ${target} for retry:`, err);
|
|
10055
10083
|
}
|
|
10056
10084
|
for (const p of claimed) this.inflight.add(p);
|
|
10057
10085
|
const ack = async () => {
|
|
@@ -27302,11 +27330,152 @@ function buildWorkerTools(opts) {
|
|
|
27302
27330
|
];
|
|
27303
27331
|
}
|
|
27304
27332
|
|
|
27333
|
+
//#endregion
|
|
27334
|
+
//#region src/lib/worker-agent/relay-cap.ts
|
|
27335
|
+
/** Default relay-safe byte budget for a worker result. */
|
|
27336
|
+
const DEFAULT_MAX_RESULT_BYTES = 16 * 1024;
|
|
27337
|
+
/** Lower clamp — below this a preview is barely useful. */
|
|
27338
|
+
const MIN_MAX_RESULT_BYTES = 8 * 1024;
|
|
27339
|
+
/**
|
|
27340
|
+
* Upper clamp. 20480 B ≈ 20k tokens even at the ~1 byte/token dense
|
|
27341
|
+
* worst case, comfortably under Claude Code's 25k-token result cap. The
|
|
27342
|
+
* env override is clamped to this, so it can never reintroduce the
|
|
27343
|
+
* overflow the cap exists to prevent.
|
|
27344
|
+
*/
|
|
27345
|
+
const MAX_MAX_RESULT_BYTES = 20 * 1024;
|
|
27346
|
+
/**
|
|
27347
|
+
* Age at which a spilled `.patch`/`.txt` is swept. Matches the worktree
|
|
27348
|
+
* dir age sweep in `worktree.ts`.
|
|
27349
|
+
*/
|
|
27350
|
+
const AGE_SWEEP_MS$1 = 10080 * 60 * 1e3;
|
|
27351
|
+
/** Min interval between throttled hot-path sweeps (see `sweepAgedWorkerDiffs`). */
|
|
27352
|
+
const SWEEP_THROTTLE_MS = 60 * 1e3;
|
|
27353
|
+
let lastThrottledSweepAt = 0;
|
|
27354
|
+
/**
|
|
27355
|
+
* Strict name pattern for router-written overflow files:
|
|
27356
|
+
* `<pid>-<8hex>.(patch|txt)`. Shared by the sweep so it NEVER removes a
|
|
27357
|
+
* file it didn't write (a user could drop something else under the dir).
|
|
27358
|
+
*/
|
|
27359
|
+
const WORKER_DIFF_NAME_RE = /^\d+-[0-9a-f]{8}\.(?:patch|txt)$/;
|
|
27360
|
+
/** Resolve the relay-safe byte cap from env, clamped to the safe range. */
|
|
27361
|
+
function resolveMaxResultBytes() {
|
|
27362
|
+
const raw = process$1.env.GH_ROUTER_WORKER_MAX_RESULT_BYTES;
|
|
27363
|
+
if (raw === void 0) return DEFAULT_MAX_RESULT_BYTES;
|
|
27364
|
+
const n = Number(raw);
|
|
27365
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return DEFAULT_MAX_RESULT_BYTES;
|
|
27366
|
+
return Math.min(MAX_MAX_RESULT_BYTES, Math.max(MIN_MAX_RESULT_BYTES, n));
|
|
27367
|
+
}
|
|
27368
|
+
/**
|
|
27369
|
+
* Return the longest prefix of `text` whose UTF-8 encoding is at most
|
|
27370
|
+
* `maxBytes` long, cut on a codepoint boundary (never mid-multibyte).
|
|
27371
|
+
*/
|
|
27372
|
+
function utf8HeadPreview(text, maxBytes) {
|
|
27373
|
+
if (maxBytes <= 0) return "";
|
|
27374
|
+
const buf = Buffer.from(text, "utf8");
|
|
27375
|
+
if (buf.length <= maxBytes) return text;
|
|
27376
|
+
let end = maxBytes;
|
|
27377
|
+
while (end > 0 && (buf[end] & 192) === 128) end--;
|
|
27378
|
+
return buf.subarray(0, end).toString("utf8");
|
|
27379
|
+
}
|
|
27380
|
+
/**
|
|
27381
|
+
* Best-effort age sweep of router-written overflow files under
|
|
27382
|
+
* `WORKER_DIFFS_DIR`. Only removes entries whose NAME matches
|
|
27383
|
+
* `WORKER_DIFF_NAME_RE` and whose mtime is older than 7 days — a fresh
|
|
27384
|
+
* file a concurrent worker just wrote (new mtime) is never touched, and
|
|
27385
|
+
* a locked file just skips. Errors are swallowed so a sweep never blocks
|
|
27386
|
+
* the write it precedes.
|
|
27387
|
+
*
|
|
27388
|
+
* `opts.throttle` (used by the write hot path) skips the readdir/stat scan
|
|
27389
|
+
* when a throttled sweep ran within the last minute, so concurrent overflow
|
|
27390
|
+
* writes don't each re-scan the directory. Direct callers (and tests) sweep
|
|
27391
|
+
* unconditionally.
|
|
27392
|
+
*/
|
|
27393
|
+
async function sweepAgedWorkerDiffs(opts = {}) {
|
|
27394
|
+
if (opts.throttle) {
|
|
27395
|
+
const now$1 = Date.now();
|
|
27396
|
+
if (now$1 - lastThrottledSweepAt < SWEEP_THROTTLE_MS) return;
|
|
27397
|
+
lastThrottledSweepAt = now$1;
|
|
27398
|
+
}
|
|
27399
|
+
let entries;
|
|
27400
|
+
try {
|
|
27401
|
+
entries = await fs.readdir(PATHS.WORKER_DIFFS_DIR);
|
|
27402
|
+
} catch {
|
|
27403
|
+
return;
|
|
27404
|
+
}
|
|
27405
|
+
const now = Date.now();
|
|
27406
|
+
for (const name of entries) {
|
|
27407
|
+
if (!WORKER_DIFF_NAME_RE.test(name)) continue;
|
|
27408
|
+
const full = nodePath.join(PATHS.WORKER_DIFFS_DIR, name);
|
|
27409
|
+
try {
|
|
27410
|
+
if (now - (await fs.stat(full)).mtimeMs < AGE_SWEEP_MS$1) continue;
|
|
27411
|
+
await fs.rm(full, { force: true }).catch(() => {});
|
|
27412
|
+
} catch {}
|
|
27413
|
+
}
|
|
27414
|
+
}
|
|
27415
|
+
/**
|
|
27416
|
+
* Write `text` in full to a durable `.txt` under `WORKER_DIFFS_DIR` and
|
|
27417
|
+
* return its absolute path. Unique `<pid>-<8hex>.txt` name, `0o600`,
|
|
27418
|
+
* exclusive-create (`wx`) so it never clobbers a pre-existing file /
|
|
27419
|
+
* symlink. Retries on the (astronomically rare) name collision with a
|
|
27420
|
+
* fresh suffix. Sweeps aged files first (throttled, best-effort).
|
|
27421
|
+
*/
|
|
27422
|
+
async function saveOverflowResult(text) {
|
|
27423
|
+
await sweepAgedWorkerDiffs({ throttle: true });
|
|
27424
|
+
await fs.mkdir(PATHS.WORKER_DIFFS_DIR, { recursive: true });
|
|
27425
|
+
let lastErr;
|
|
27426
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
27427
|
+
const name = `${process$1.pid}-${randomBytes(4).toString("hex")}.txt`;
|
|
27428
|
+
const outPath = nodePath.join(PATHS.WORKER_DIFFS_DIR, name);
|
|
27429
|
+
try {
|
|
27430
|
+
await fs.writeFile(outPath, text, {
|
|
27431
|
+
mode: 384,
|
|
27432
|
+
flag: "wx"
|
|
27433
|
+
});
|
|
27434
|
+
return outPath;
|
|
27435
|
+
} catch (err) {
|
|
27436
|
+
if (err.code !== "EEXIST") throw err;
|
|
27437
|
+
lastErr = err;
|
|
27438
|
+
}
|
|
27439
|
+
}
|
|
27440
|
+
throw lastErr instanceof Error ? lastErr : /* @__PURE__ */ new Error("saveOverflowResult: exhausted unique-name retries");
|
|
27441
|
+
}
|
|
27442
|
+
/**
|
|
27443
|
+
* Cap a worker result to a relay-safe size. `reservedBytes` is the UTF-8
|
|
27444
|
+
* byte length of any must-survive envelope the caller will add OUTSIDE this
|
|
27445
|
+
* result (a `clampNote`/`worktreeNote` prefix, or the browse
|
|
27446
|
+
* `[browse session: id]` suffix), so the caller's final `envelope + result`
|
|
27447
|
+
* (or `result + envelope`) is guaranteed `<=` the configured cap.
|
|
27448
|
+
*
|
|
27449
|
+
* If the text fits under the effective cap it is returned unchanged.
|
|
27450
|
+
* Otherwise the FULL text is spilled to a durable file and a bounded
|
|
27451
|
+
* UTF-8-safe head preview + the file path is returned. On write failure the
|
|
27452
|
+
* preview is still returned with an explicit failure note — NEVER the
|
|
27453
|
+
* oversized original (that would re-trigger the overflow this guard prevents).
|
|
27454
|
+
*/
|
|
27455
|
+
async function relaySafeText(text, reservedBytes = 0) {
|
|
27456
|
+
const cap = Math.max(0, resolveMaxResultBytes() - Math.max(0, reservedBytes));
|
|
27457
|
+
if (Buffer.byteLength(text, "utf8") <= cap) return text;
|
|
27458
|
+
let savedPath = null;
|
|
27459
|
+
let saveError = null;
|
|
27460
|
+
try {
|
|
27461
|
+
savedPath = await saveOverflowResult(text);
|
|
27462
|
+
} catch (err) {
|
|
27463
|
+
saveError = err instanceof Error ? err.message : String(err);
|
|
27464
|
+
}
|
|
27465
|
+
const trailer = savedPath !== null ? `\n\n[result truncated to fit the relay; full result saved to: ${savedPath}]` : `\n\n[result truncated to fit the relay; saving the full result failed: ${saveError ?? "unknown"}]`;
|
|
27466
|
+
const out = utf8HeadPreview(text, Math.max(0, cap - Buffer.byteLength(trailer, "utf8"))) + trailer;
|
|
27467
|
+
return Buffer.byteLength(out, "utf8") <= cap ? out : utf8HeadPreview(out, cap);
|
|
27468
|
+
}
|
|
27469
|
+
|
|
27305
27470
|
//#endregion
|
|
27306
27471
|
//#region src/lib/worker-agent/worktree.ts
|
|
27307
|
-
/**
|
|
27308
|
-
*
|
|
27309
|
-
|
|
27472
|
+
/**
|
|
27473
|
+
* A diff at or below this size is inlined in full. Above it, `finalize()`
|
|
27474
|
+
* saves the complete patch to a file and returns a `git diff --stat`
|
|
27475
|
+
* summary + a bounded UTF-8-safe preview + the file path, so the full
|
|
27476
|
+
* change never rides (and overflows) Claude Code's 25k-token result relay.
|
|
27477
|
+
*/
|
|
27478
|
+
const PREVIEW_CAP = 8 * 1024;
|
|
27310
27479
|
/** Max entries allowed under `<repoRoot>/.git/worker-worktrees/`. */
|
|
27311
27480
|
const QUOTA_PER_REPO = 20;
|
|
27312
27481
|
/** Per-call age sweep: remove worktree dirs older than this. */
|
|
@@ -27393,7 +27562,7 @@ async function findRepoRoot(workspaceAbs) {
|
|
|
27393
27562
|
} catch (err) {
|
|
27394
27563
|
const e = err;
|
|
27395
27564
|
const detail = e.stderr ? e.stderr.trim() : e.message;
|
|
27396
|
-
throw new Error(`worker-agent worktree: git unavailable or workspace is not a repository: ${detail}
|
|
27565
|
+
throw new Error(`worker-agent worktree: git unavailable or workspace is not a repository: ${detail}. worker_implement/worker_test always run in an isolated git worktree and require a git repo; for in-place edits (or a non-git workspace), use the native \`implementer\` subagent instead.`);
|
|
27397
27566
|
}
|
|
27398
27567
|
const lines = result.stdout.split(/\r?\n/).filter((s) => s.length > 0);
|
|
27399
27568
|
if (lines.length < 2) throw new Error(`worker-agent worktree: unexpected git rev-parse output: ${JSON.stringify(result.stdout)}`);
|
|
@@ -27570,7 +27739,7 @@ async function createWorktree(workspaceAbs, opts) {
|
|
|
27570
27739
|
"diff",
|
|
27571
27740
|
"HEAD"
|
|
27572
27741
|
], { maxBuffer: 256 * 1024 * 1024 });
|
|
27573
|
-
if (diff.stdout
|
|
27742
|
+
if (Buffer.byteLength(diff.stdout, "utf8") <= PREVIEW_CAP) return diff.stdout;
|
|
27574
27743
|
let stat$1 = "";
|
|
27575
27744
|
try {
|
|
27576
27745
|
stat$1 = (await execFileP("git", [
|
|
@@ -27581,8 +27750,8 @@ async function createWorktree(workspaceAbs, opts) {
|
|
|
27581
27750
|
"HEAD"
|
|
27582
27751
|
])).stdout;
|
|
27583
27752
|
} catch {}
|
|
27584
|
-
const
|
|
27585
|
-
const
|
|
27753
|
+
const preview = utf8HeadPreview(diff.stdout, PREVIEW_CAP);
|
|
27754
|
+
const previewBlock = `\n\n--- diff preview (first ${PREVIEW_CAP >> 10} KiB of ${Buffer.byteLength(diff.stdout, "utf8")} bytes) ---\n` + preview;
|
|
27586
27755
|
let savedPath = null;
|
|
27587
27756
|
let saveError = null;
|
|
27588
27757
|
try {
|
|
@@ -27590,8 +27759,8 @@ async function createWorktree(workspaceAbs, opts) {
|
|
|
27590
27759
|
} catch (err) {
|
|
27591
27760
|
saveError = err.message;
|
|
27592
27761
|
}
|
|
27593
|
-
if (savedPath !== null) return
|
|
27594
|
-
return
|
|
27762
|
+
if (savedPath !== null) return `[large diff — full patch saved to a file]\n${stat$1}${previewBlock}\n\nFull patch (git apply-able; includes binary blobs) saved to: ${savedPath}`;
|
|
27763
|
+
return `[large diff — full patch save FAILED: ${saveError ?? "unknown"}; only the summary + preview below survive]\n${stat$1}${previewBlock}`;
|
|
27595
27764
|
};
|
|
27596
27765
|
return {
|
|
27597
27766
|
dir,
|
|
@@ -27605,11 +27774,11 @@ async function createWorktree(workspaceAbs, opts) {
|
|
|
27605
27774
|
* durable, router-owned file under `PATHS.WORKER_DIFFS_DIR` and return its
|
|
27606
27775
|
* absolute path.
|
|
27607
27776
|
*
|
|
27608
|
-
* Called by `finalize()`
|
|
27609
|
-
*
|
|
27610
|
-
*
|
|
27611
|
-
*
|
|
27612
|
-
*
|
|
27777
|
+
* Called by `finalize()` whenever the inline diff exceeds `PREVIEW_CAP`:
|
|
27778
|
+
* the worktree is removed immediately after finalize, so the full patch
|
|
27779
|
+
* must be persisted or the actual change is lost forever. The durable dir
|
|
27780
|
+
* lives under the app dir — never inside the worktree (deleted) nor the
|
|
27781
|
+
* user's repo (don't pollute it).
|
|
27613
27782
|
*
|
|
27614
27783
|
* `--binary` keeps binary blobs recoverable (git base85-encodes them into
|
|
27615
27784
|
* the patch) and `--full-index` writes exact 40-char object indexes, so the
|
|
@@ -27628,14 +27797,24 @@ async function saveOverflowPatch(dir) {
|
|
|
27628
27797
|
"--full-index",
|
|
27629
27798
|
"HEAD"
|
|
27630
27799
|
], { maxBuffer: 256 * 1024 * 1024 });
|
|
27800
|
+
await sweepAgedWorkerDiffs({ throttle: true });
|
|
27631
27801
|
await fs.mkdir(PATHS.WORKER_DIFFS_DIR, { recursive: true });
|
|
27632
|
-
|
|
27633
|
-
|
|
27634
|
-
|
|
27635
|
-
|
|
27636
|
-
|
|
27637
|
-
|
|
27638
|
-
|
|
27802
|
+
let lastErr;
|
|
27803
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
27804
|
+
const name = `${process$1.pid}-${randomBytes(4).toString("hex")}.patch`;
|
|
27805
|
+
const patchPath = nodePath.join(PATHS.WORKER_DIFFS_DIR, name);
|
|
27806
|
+
try {
|
|
27807
|
+
await fs.writeFile(patchPath, patch.stdout, {
|
|
27808
|
+
mode: 384,
|
|
27809
|
+
flag: "wx"
|
|
27810
|
+
});
|
|
27811
|
+
return patchPath;
|
|
27812
|
+
} catch (err) {
|
|
27813
|
+
if (err.code !== "EEXIST") throw err;
|
|
27814
|
+
lastErr = err;
|
|
27815
|
+
}
|
|
27816
|
+
}
|
|
27817
|
+
throw lastErr instanceof Error ? lastErr : /* @__PURE__ */ new Error("saveOverflowPatch: exhausted unique-name retries");
|
|
27639
27818
|
}
|
|
27640
27819
|
|
|
27641
27820
|
//#endregion
|
|
@@ -30669,7 +30848,7 @@ function buildPeerAwarenessSnippet(opts) {
|
|
|
30669
30848
|
criticList.push("`opus_critic` (Opus 4.6)");
|
|
30670
30849
|
const codexCliClause = opts.codexCli ? " `mcp__codex-cli__codex` dispatches to `codex-implementer` (gpt-5.3-codex with workspace-write) for end-to-end coding tasks." : "";
|
|
30671
30850
|
const para2Parts = [`\`mcp__${searchKey}__code\` is the one-stop code search (no extra model call). Its DEFAULT mode (or \`mode:"semantic"\`) ranks by MEANING via ColBERT over a per-workspace index, the first thing to reach for on intent/concept questions ("where is retry/backoff handled", "how does auth work"); when that index isn't ready it transparently falls back to lexical (the response \`source\` says which engine ran). Forced modes cover the rest: \`lexical\` (BM25F-ranked + tree-sitter, best for exact symbols), \`exact\`, \`regex\`, \`complete\` (exhaustive set), \`ast_pattern\`+\`ast_lang\` for multi-line AST shapes, \`scan\` for a whole-workspace symbol outline, \`multiline\` for cross-line regex. Multiple queries can run in a single turn. The index covers code-shaped files; for unstructured files (logs, \`.csv\`, \`.env*\`, config-only wiring), \`grep\`/\`glob\` still apply.`];
|
|
30672
|
-
if (opts.workerToolsAvailable) para2Parts.push(`\`worker-*\` are background Agent subagents (subagent_type) that run the matching worker in its own context and deliver the result as a completion notification, so a long run never blocks the turn: \`worker-explore\` (read-only research), \`worker-review\` (reads the code to verify a change or claim), \`worker-plan\` (ordered implementation plan), \`worker-implement\` (edit/write/bash;
|
|
30851
|
+
if (opts.workerToolsAvailable) para2Parts.push(`\`worker-*\` are background Agent subagents (subagent_type) that run the matching worker in its own context and deliver the result as a completion notification, so a long run never blocks the turn: \`worker-explore\` (read-only research), \`worker-review\` (reads the code to verify a change or claim), \`worker-plan\` (ordered implementation plan), \`worker-implement\` (edit/write/bash; ALWAYS runs in an isolated git worktree and returns the diff via a saved patch file; for in-place edits use the \`implementer\` subagent), \`worker-test\` (independent test author; also always worktree-isolated). The raw \`mcp__${workersKey}__*\` tools they call are guarded (a direct main-thread call is redirected to the matching agent); Workers themselves have \`code_search\`.`);
|
|
30673
30852
|
para2Parts.push(`Three native subagents are always available (Task): \`implementer\` (bounded implementation), \`debugger\` (reproduce + isolate a failure's root cause), and \`qa-engineer\` (review + author/run tests), each in its own context so the lead's context stays free; on gpt-5.6-sol when in the catalog, else the lead's model.`);
|
|
30674
30853
|
if (opts.workerToolsAvailable) para2Parts.push(`For a bounded, well-scoped implementation, prefer the \`implementer\` subagent over \`worker-implement\`; reach for \`worker-implement\` only when you specifically need git-worktree isolation, parallel variants, or a throwaway experiment.`);
|
|
30675
30854
|
if (opts.workerToolsAvailable) para2Parts.push(`\`mcp__${orchestrateKey}__decompose\` composes an open-ended ask into a typed, VERIFIED workflow IR (a strong driver decorrelated by a cross-lab critic, so the decompose step isn't a single point of failure), and \`mcp__${orchestrateKey}__run_workflow\` executes that IR through a frozen kernel delivering max(orchestrated, baseline) over a sealed executable gate, so it never ships worse than a plain single-model run. \`mcp__${orchestrateKey}__verify_workflow\` checks an IR's floor invariants before you run it, and \`mcp__${orchestrateKey}__attest_step\` audits that a finished run's producers were each checked by a different lab. They suit non-trivial, role-separated asks; a trivial ask does not need them.`);
|
|
@@ -30982,7 +31161,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
30982
31161
|
toolNameHttp: "implement",
|
|
30983
31162
|
group: "workers",
|
|
30984
31163
|
capability: "worker",
|
|
30985
|
-
description: "Runs as the background `worker-implement` agent. Dispatch via the Agent tool (subagent_type: worker-implement) so the turn is never blocked; the result arrives as a completion notification. Delegates a scoped coding task to an autonomous worker (Pi runtime; default model `gpt-5.6-sol` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the explore read-only tools plus edit, write, bash, and codex_review, and it returns its final text with any changed files or worktree diff. Use for bounded implementation work that may take a while or benefits from isolated worker context. Not for pure research, planning, review, or independent test authoring; use explore, plan, review, or test for those scopes.
|
|
31164
|
+
description: "Runs as the background `worker-implement` agent. Dispatch via the Agent tool (subagent_type: worker-implement) so the turn is never blocked; the result arrives as a completion notification. Delegates a scoped coding task to an autonomous worker (Pi runtime; default model `gpt-5.6-sol` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the explore read-only tools plus edit, write, bash, and codex_review, and it returns its final text with any changed files or worktree diff. Use for bounded implementation work that may take a while or benefits from isolated worker context. Not for pure research, planning, review, or independent test authoring; use explore, plan, review, or test for those scopes. ALWAYS runs in an isolated git worktree and returns the diff via a saved patch file (a `--stat` summary + a bounded preview + the patch path; a small diff is inlined in full) — it never edits your working tree, and it HARD-ERRORS if the workspace is not a git repository. For in-place edits, use the native `implementer` subagent.",
|
|
30986
31165
|
inputSchema: {
|
|
30987
31166
|
type: "object",
|
|
30988
31167
|
required: ["prompt"],
|
|
@@ -30994,7 +31173,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
30994
31173
|
},
|
|
30995
31174
|
worktree: {
|
|
30996
31175
|
type: "boolean",
|
|
30997
|
-
description: "
|
|
31176
|
+
description: "Ignored — worker_implement ALWAYS runs in an isolated git worktree and returns the diff (retained for compatibility; worktree:false is overridden with a note). For in-place edits, use the `implementer` subagent."
|
|
30998
31177
|
},
|
|
30999
31178
|
model: {
|
|
31000
31179
|
type: "string",
|
|
@@ -31014,7 +31193,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31014
31193
|
},
|
|
31015
31194
|
workspace: {
|
|
31016
31195
|
type: "string",
|
|
31017
|
-
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected).
|
|
31196
|
+
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected). Must be inside a git repo (implement always runs in a worktree)."
|
|
31018
31197
|
},
|
|
31019
31198
|
maxWallClockMs: {
|
|
31020
31199
|
type: "integer",
|
|
@@ -31130,7 +31309,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31130
31309
|
toolNameHttp: "test",
|
|
31131
31310
|
group: "workers",
|
|
31132
31311
|
capability: "worker",
|
|
31133
|
-
description: "Runs as the background `worker-test` agent. Dispatch via the Agent tool (subagent_type: worker-test) so the turn is never blocked; the result arrives as a completion notification. Independent adversarial test authoring by an autonomous worker (Pi runtime; default model `gpt-5.6-sol` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the same read/write toolset as implement and writes tests that try to break the implementation through edge cases, error paths, and acceptance criteria, then runs them and reports pass/fail. Use when a separate test author should challenge an implementation without modifying the production code to make tests pass. Not for implementing fixes, broad research, or code review; use implement, explore, or review for those scopes.
|
|
31312
|
+
description: "Runs as the background `worker-test` agent. Dispatch via the Agent tool (subagent_type: worker-test) so the turn is never blocked; the result arrives as a completion notification. Independent adversarial test authoring by an autonomous worker (Pi runtime; default model `gpt-5.6-sol` at xhigh reasoning, override via `model` with any Copilot-catalog model that advertises `tool_calls`). It has the same read/write toolset as implement and writes tests that try to break the implementation through edge cases, error paths, and acceptance criteria, then runs them and reports pass/fail. Use when a separate test author should challenge an implementation without modifying the production code to make tests pass. Not for implementing fixes, broad research, or code review; use implement, explore, or review for those scopes. ALWAYS runs in an isolated git worktree and returns the test diff via a saved patch file (a `--stat` summary + a bounded preview + the patch path; a small diff is inlined in full) — it never edits your working tree, and it HARD-ERRORS if the workspace is not a git repository. For in-place test authoring, use the native `implementer` subagent.",
|
|
31134
31313
|
inputSchema: {
|
|
31135
31314
|
type: "object",
|
|
31136
31315
|
required: ["prompt"],
|
|
@@ -31142,7 +31321,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31142
31321
|
},
|
|
31143
31322
|
worktree: {
|
|
31144
31323
|
type: "boolean",
|
|
31145
|
-
description: "
|
|
31324
|
+
description: "Ignored — worker_test ALWAYS runs in an isolated git worktree and returns the diff (retained for compatibility; worktree:false is overridden with a note). For in-place test authoring, use the `implementer` subagent."
|
|
31146
31325
|
},
|
|
31147
31326
|
model: {
|
|
31148
31327
|
type: "string",
|
|
@@ -31162,7 +31341,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31162
31341
|
},
|
|
31163
31342
|
workspace: {
|
|
31164
31343
|
type: "string",
|
|
31165
|
-
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected).
|
|
31344
|
+
description: "Optional absolute path to the workspace the worker operates in. Defaults to the proxy's launch cwd. Use this when the parent agent has multiple workspaces open and the worker must operate in a specific one. Must be absolute (relative paths rejected). Must be inside a git repo (test always runs in a worktree)."
|
|
31166
31345
|
},
|
|
31167
31346
|
maxWallClockMs: {
|
|
31168
31347
|
type: "integer",
|
|
@@ -31551,15 +31730,19 @@ async function runWorkerToolCall(call) {
|
|
|
31551
31730
|
thinking = thinkingRaw;
|
|
31552
31731
|
}
|
|
31553
31732
|
let worktree;
|
|
31554
|
-
|
|
31555
|
-
|
|
31733
|
+
let worktreeNote = "";
|
|
31734
|
+
if (mode === "implement" || mode === "test") {
|
|
31735
|
+
if (args.worktree !== void 0 && typeof args.worktree !== "boolean") return {
|
|
31556
31736
|
content: [{
|
|
31557
31737
|
type: "text",
|
|
31558
31738
|
text: `worker_${mode}: arguments.worktree must be a boolean when provided`
|
|
31559
31739
|
}],
|
|
31560
31740
|
isError: true
|
|
31561
31741
|
};
|
|
31562
|
-
worktree =
|
|
31742
|
+
worktree = true;
|
|
31743
|
+
if (args.worktree === false) worktreeNote = `[note: worker_${mode} always runs in an isolated git worktree; the requested worktree:false was overridden. For in-place edits, use the \`implementer\` subagent.]
|
|
31744
|
+
|
|
31745
|
+
`;
|
|
31563
31746
|
}
|
|
31564
31747
|
let workspace;
|
|
31565
31748
|
if (args.workspace !== void 0) {
|
|
@@ -31613,10 +31796,11 @@ async function runWorkerToolCall(call) {
|
|
|
31613
31796
|
maxWallClockMs,
|
|
31614
31797
|
signal
|
|
31615
31798
|
});
|
|
31799
|
+
const notePrefix = `${clampNote}${worktreeNote}`;
|
|
31616
31800
|
return {
|
|
31617
31801
|
content: [{
|
|
31618
31802
|
type: "text",
|
|
31619
|
-
text: `${
|
|
31803
|
+
text: `${notePrefix}${await relaySafeText(result.text, Buffer.byteLength(notePrefix, "utf8"))}`
|
|
31620
31804
|
}],
|
|
31621
31805
|
isError: result.isError
|
|
31622
31806
|
};
|
|
@@ -31705,10 +31889,11 @@ async function runBrowseToolCall(args, signal) {
|
|
|
31705
31889
|
} finally {
|
|
31706
31890
|
releaseBrowseSession(sessionId);
|
|
31707
31891
|
}
|
|
31892
|
+
const sessionSuffix = `\n\n[browse session: ${sessionId}]`;
|
|
31708
31893
|
return {
|
|
31709
31894
|
content: [{
|
|
31710
31895
|
type: "text",
|
|
31711
|
-
text: `${result.text
|
|
31896
|
+
text: `${await relaySafeText(result.text, Buffer.byteLength(sessionSuffix, "utf8"))}${sessionSuffix}`
|
|
31712
31897
|
}],
|
|
31713
31898
|
isError: result.isError
|
|
31714
31899
|
};
|
|
@@ -31846,4 +32031,4 @@ function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
|
|
|
31846
32031
|
|
|
31847
32032
|
//#endregion
|
|
31848
32033
|
export { buildAdvisorStream as $, setupCopilotToken as $t, trustRepo as A, readResponseBodyCapped as At, runWorkerAgent as B, buildWorkspaceHeaderJson as Bt, fileLastPromptStore as C, getTokenCount as Ct, repoRoot as D, createResponses as Dt, repoFingerprint as E, pickEndpoint as Et, EXPLORE_DEFAULT_MODEL as F, extractTarGzMember as Ft, toolbeltEnabled as G, DEFAULT_CODEX_MODEL_FALLBACKS as Gt, buildEnv as H, toolbeltPathOverride as Ht, IMPLEMENT_DEFAULT_MODEL as I, extractZipMember as It, TOOLBELT_TOOLS$1 as J, UPSTREAM_INACTIVITY_TIMEOUT_MS as Jt, toolbeltSkipSet as K, DEFAULT_PORT as Kt, PLAN_DEFAULT_MODEL as L, shouldUseInsecureTls as Lt, resolveSealedGate as M, provisionBrowserAssets as Mt, BROWSE_DEFAULT_MODEL as N, hasSupportedBrowserInstalled as Nt, stopGateEnabledForRepo as O, createChatCompletions as Ot, DEFAULT_MODEL as P, provisionAndIndexColbert as Pt, ADVISOR_TOOL_INSTRUCTIONS as Q, withInstallLock as Qt, REVIEW_DEFAULT_MODEL as R, ArtifactClient as Rt, fileFindingsStore as S, createMessages as St, isSubagentContext as T, resolveMcpToolTimeoutMs as Tt, availableToolCommands as U, DEFAULT_CLAUDE_MODEL_FALLBACKS as Ut, withNoOutputRetry as V, collapsePathKeys as Vt, buildToolbeltAwareness as W, DEFAULT_CODEX_MODEL as Wt, searchWeb as X, pickClaudeDefault as Xt, assetFor as Y, generateRandomPort as Yt, ADVISOR_INTERNAL_TOOL_NAME as Z, getPackageVersion as Zt, stopGateDisabled as _, copilotBaseUrl as _n, nativeSubagentModel as _t, buildPeerAwarenessSnippet as a, cacheVSCodeVersion as an, logStreamError as at, stopReviewEnabled as b, state as bn, shimDefaultsToXhigh as bt, personasFor as c, resolveCodexModel as cn, handleMcpDelete as ct, buildStopHookCommand as d, getModels as dn, artifactToolsEnabled as dt, setupGitHubAgentToken as en, injectAdvisorTool as et, captureLaunchBaseline as f, getGitHubUser as fn, browseAgentEnabled as ft, launchBaselineKey as g, GITHUB_API_BASE_URL as gn, geminiAvailable as gt, injectStopHookIntoSettingsFile as h, forwardError as hn, fleetToolsEnabled as ht, buildAgentPrompt as i, cacheModels as in, isControllerClosedError as it, liveExec as j, parseJsonOrDiagnose as jt, stopReviewStateDir as k, MAX_RESPONSE_BODY_BYTES as kt, buildArtifactOpenHookCommand as l, resolveModel as ln, handleMcpPost as lt, fileBlockBudget as m, HTTPError as mn, browserToolsEnabled as mt, MCP_GROUPS as n, tryRefreshAndRetry as nn, buildAnthropicErrorEvent as nt, buildPeerAwarenessSummary as o, filterBetaHeader as on, readIteratorWithTimeout as ot, decideStopHook as p, fetchWithTransientRetry as pn, browserCompoundToolsEnabled as pt, vscodeRipgrepPath as q, UPSTREAM_FETCH_TIMEOUT_MS as qt, assertMcpToolSurfaceConsistent as r, cacheCopilotVersion as rn, buildOpenAIErrorEvent as rt, enumerateInjectedMcpToolNames as s, isNullish as sn, relayAnthropicStream as st, GROUP_META as t, setupGitHubToken as tn, isAdvisorRequested as tt, buildSessionBindHookCommand as u, sleep as un, agentToolsEnabled as ut, stopGateId as v, copilotHeaders as vn, standInToolEnabled as vt, fileReviewDebounce as w, assembleResponsesPayload as wt, fileBaselineStore as x, countTokens as xt, stopGatePlanMode as y, githubHeaders as yn, workerToolsEnabled as yt, appendPlanReminder as z, buildWorkspaceHeaderHelperCommand as zt };
|
|
31849
|
-
//# sourceMappingURL=peer-mcp-personas-
|
|
32034
|
+
//# sourceMappingURL=peer-mcp-personas-C3ii7ZqP.js.map
|