@coreplane/switchboard 1.220.1 → 1.221.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/dist/assets/deploy/cloudflare-resident/worker.ts +87 -22
- package/dist/assets/package-lock.json +3 -3
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/execution/residentReuse.ts +98 -0
- package/dist/assets/src/execution/residentSteps.ts +1 -0
- package/dist/assets/web/dist/.vite/manifest.json +18 -18
- package/dist/assets/web/dist/assets/{ResidentDetailPage-DvCt96Dk.js → ResidentDetailPage-iqyj3Kpt.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-BZaYja1-.js → ResidentsIndexPage-BiECMcX5.js} +1 -1
- package/dist/assets/web/dist/assets/{RunRoutePage-DI4vzCUu.js → RunRoutePage-YCWYYZkj.js} +4 -4
- package/dist/assets/web/dist/assets/{RunsIndexPage-Ch9uP4p4.js → RunsIndexPage-BX6231bA.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-DAkAHMCH.js → ScheduledPage-ibRdZaDS.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-CfBonGcV.js → StatusDot-W2x3d782.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-CBD36E3W.js → Tooltip-BLRVgG37.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-DQ1U73LF.js → dist-C3C34ZMu.js} +1 -1
- package/dist/assets/web/dist/assets/{main-CdLnG-yl.js → main-B9dN-Wec.js} +2 -2
- package/dist/cli.js +863 -308
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4139,6 +4139,7 @@ var init_residentSteps = __esm({
|
|
|
4139
4139
|
"ops-dir": "preparing a directory",
|
|
4140
4140
|
"stage-dir": "preparing a directory",
|
|
4141
4141
|
stat: "checking a file",
|
|
4142
|
+
"boot-id": "reading the container's identity",
|
|
4142
4143
|
touch: "touching a marker",
|
|
4143
4144
|
nproc: "counting CPUs",
|
|
4144
4145
|
mutex_wait: "waiting for the workspace",
|
|
@@ -9419,7 +9420,7 @@ async function parseResidentBody(res) {
|
|
|
9419
9420
|
}
|
|
9420
9421
|
return sanitizeResidentBody(data);
|
|
9421
9422
|
}
|
|
9422
|
-
var DETACH_TIMEOUT_MS, ResidentNeedsRefError, ResidentOperations, ResidentExecutor;
|
|
9423
|
+
var DETACH_TIMEOUT_MS, ResidentNeedsRefError, ResidentReuseRefusedError, ResidentOperations, ResidentExecutor;
|
|
9423
9424
|
var init_resident = __esm({
|
|
9424
9425
|
"../../src/execution/resident.ts"() {
|
|
9425
9426
|
"use strict";
|
|
@@ -9451,6 +9452,16 @@ var init_resident = __esm({
|
|
|
9451
9452
|
defaultRef;
|
|
9452
9453
|
needs = "ref";
|
|
9453
9454
|
};
|
|
9455
|
+
ResidentReuseRefusedError = class extends Error {
|
|
9456
|
+
constructor(resource, why) {
|
|
9457
|
+
super(`resident attach: ${resource} cannot reuse this thread's worktree (${why})`);
|
|
9458
|
+
this.resource = resource;
|
|
9459
|
+
this.why = why;
|
|
9460
|
+
this.name = "ResidentReuseRefusedError";
|
|
9461
|
+
}
|
|
9462
|
+
resource;
|
|
9463
|
+
why;
|
|
9464
|
+
};
|
|
9454
9465
|
ResidentOperations = class {
|
|
9455
9466
|
constructor(opts) {
|
|
9456
9467
|
this.opts = opts;
|
|
@@ -9608,6 +9619,7 @@ var init_resident = __esm({
|
|
|
9608
9619
|
if (this.opts.refHint) body.refHint = this.opts.refHint;
|
|
9609
9620
|
if (this.opts.readonly) body.readonly = true;
|
|
9610
9621
|
if (this.opts.sha) body.sha = this.opts.sha;
|
|
9622
|
+
if (this.opts.reuse) body.reuse = true;
|
|
9611
9623
|
const answered = await this.call("/attach", body, timeoutMs, void 0, span);
|
|
9612
9624
|
const data = answered.data;
|
|
9613
9625
|
const status3 = answered.status === 200 && typeof data.error === "string" && typeof data.status === "number" ? data.status : answered.status;
|
|
@@ -9621,6 +9633,7 @@ var init_resident = __esm({
|
|
|
9621
9633
|
sha: data.sha,
|
|
9622
9634
|
...typeof data.workspace === "string" && data.workspace ? { workspace: data.workspace } : {},
|
|
9623
9635
|
...typeof data.user === "string" && data.user ? { user: data.user } : {},
|
|
9636
|
+
...typeof data.container === "string" && data.container ? { container: data.container } : {},
|
|
9624
9637
|
...trace.length > 0 ? { trace } : {},
|
|
9625
9638
|
...typeof data.attachMs === "number" && Number.isFinite(data.attachMs) ? { attachMs: data.attachMs } : {}
|
|
9626
9639
|
};
|
|
@@ -9640,6 +9653,8 @@ var init_resident = __esm({
|
|
|
9640
9653
|
const defaultRef = typeof data.defaultRef === "string" && data.defaultRef ? data.defaultRef : void 0;
|
|
9641
9654
|
return traced(new ResidentNeedsRefError(this.opts.resource, defaultRef));
|
|
9642
9655
|
}
|
|
9656
|
+
if (status3 === 409 && data.needs === "recreate")
|
|
9657
|
+
return traced(new ResidentReuseRefusedError(this.opts.resource, err2));
|
|
9643
9658
|
if (status3 === 404) return traced(new Error(`resident attach: ${this.opts.resource} is not onboarded (${err2})`));
|
|
9644
9659
|
return traced(new Error(`resident attach failed for ${this.opts.resource}: ${err2}`));
|
|
9645
9660
|
}
|
|
@@ -9994,11 +10009,33 @@ var init_githubApp = __esm({
|
|
|
9994
10009
|
// ../../src/execution/factory.ts
|
|
9995
10010
|
import { resolve as resolve7 } from "node:path";
|
|
9996
10011
|
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
10012
|
+
function workspaceBindingOf(value) {
|
|
10013
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
10014
|
+
const v = value;
|
|
10015
|
+
if (typeof v.backend !== "string" || !BACKENDS.includes(v.backend)) return void 0;
|
|
10016
|
+
return {
|
|
10017
|
+
backend: v.backend,
|
|
10018
|
+
...typeof v.workspace === "string" && v.workspace ? { workspace: v.workspace } : {},
|
|
10019
|
+
...typeof v.user === "string" && v.user ? { user: v.user } : {},
|
|
10020
|
+
...typeof v.container === "string" && v.container ? { container: v.container } : {}
|
|
10021
|
+
};
|
|
10022
|
+
}
|
|
10023
|
+
function workspaceBindingFor(selection, machine = "repo-resident") {
|
|
10024
|
+
if (machine === "none" || selection.backend === void 0) return void 0;
|
|
10025
|
+
const b = selection.binding;
|
|
10026
|
+
return {
|
|
10027
|
+
backend: selection.backend,
|
|
10028
|
+
...b?.workspace !== void 0 ? { workspace: b.workspace } : {},
|
|
10029
|
+
...b?.user !== void 0 ? { user: b.user } : {},
|
|
10030
|
+
...b?.container !== void 0 ? { container: b.container } : {}
|
|
10031
|
+
};
|
|
10032
|
+
}
|
|
9997
10033
|
async function makeExecutor(opts, ctx, span) {
|
|
9998
10034
|
const machine = ctx.profile.machine;
|
|
9999
10035
|
if (machine === "none") {
|
|
10000
10036
|
return { executor: new NullExecutor(ctx.agent.name), backend: "local" };
|
|
10001
10037
|
}
|
|
10038
|
+
if (ctx.reattach !== void 0) return reattachWorkspace(opts, ctx, ctx.reattach, span);
|
|
10002
10039
|
if (machine === "blank") {
|
|
10003
10040
|
return {
|
|
10004
10041
|
executor: await makePerThreadExecutor(opts, { threadKey: ctx.threadKey, resolveEnvs: async () => ({}) }),
|
|
@@ -10056,6 +10093,58 @@ async function makeExecutor(opts, ctx, span) {
|
|
|
10056
10093
|
...failedAttach ? { trace: failedAttach.steps } : {}
|
|
10057
10094
|
};
|
|
10058
10095
|
}
|
|
10096
|
+
async function reattachWorkspace(opts, ctx, recorded, span) {
|
|
10097
|
+
const refuse3 = (why) => new WorkspaceReattachRefusedError(recorded, oneLine(why));
|
|
10098
|
+
if (recorded.backend !== "resident") {
|
|
10099
|
+
const input = ctx.profile.machine === "blank" ? { threadKey: ctx.threadKey, resolveEnvs: async () => ({}) } : perThreadCheckout(ctx);
|
|
10100
|
+
return { executor: await makePerThreadExecutor(opts, input), backend: perThreadBackend(opts) };
|
|
10101
|
+
}
|
|
10102
|
+
const resident = opts.execution?.resident;
|
|
10103
|
+
if (!resident) throw refuse3("no resident backend is configured in this process");
|
|
10104
|
+
if (!ctx.repo) throw refuse3("the run's row names no repository to re-attach on");
|
|
10105
|
+
const tokenEnv = resident.tokenEnv ?? "RESIDENT_OPERATOR_TOKEN";
|
|
10106
|
+
const token2 = processSecrets.named(tokenEnv);
|
|
10107
|
+
if (!token2) throw new Error(`execution.resident is configured but ${tokenEnv} is not set`);
|
|
10108
|
+
const resource = repoResourceId(ctx.repo);
|
|
10109
|
+
const probe = await probeResident(resident, token2, resource, span);
|
|
10110
|
+
if (probe.kind === "unreachable") throw refuse3(`resident unreachable (${probe.error})`);
|
|
10111
|
+
if (!isServiceable(probe.state, probe.reason))
|
|
10112
|
+
throw refuse3(`resident ${probe.state}${probe.reason ? ` (${probe.reason})` : ""}`);
|
|
10113
|
+
const nonWarm = probe.state === "warm" ? void 0 : oneLine(`${probe.state}${probe.reason ? ` (${probe.reason})` : ""}`);
|
|
10114
|
+
const executor = new ResidentExecutor({
|
|
10115
|
+
baseUrl: resident.baseUrl,
|
|
10116
|
+
token: token2.reveal(),
|
|
10117
|
+
resource,
|
|
10118
|
+
threadKey: ctx.threadKey,
|
|
10119
|
+
refHint: ctx.ref,
|
|
10120
|
+
readonly: ctx.profile.identity === "read" ? true : void 0,
|
|
10121
|
+
sha: ctx.headSha,
|
|
10122
|
+
reuse: true
|
|
10123
|
+
});
|
|
10124
|
+
let binding;
|
|
10125
|
+
try {
|
|
10126
|
+
binding = await executor.attach(span);
|
|
10127
|
+
} catch (err2) {
|
|
10128
|
+
throw refuse3(err2 instanceof Error ? err2.message : String(err2));
|
|
10129
|
+
}
|
|
10130
|
+
const same = (recordedValue, answered) => recordedValue === void 0 || answered === void 0 || recordedValue === answered;
|
|
10131
|
+
if (!same(recorded.workspace, binding.workspace) || !same(recorded.user, binding.user)) {
|
|
10132
|
+
const name = (workspace, user) => `${workspace ?? "(unnamed)"} as ${user ?? "(unnamed)"}`;
|
|
10133
|
+
throw refuse3(
|
|
10134
|
+
`the resident's worktree for this thread is ${name(binding.workspace, binding.user)}, not the run's recorded ${name(recorded.workspace, recorded.user)}`
|
|
10135
|
+
);
|
|
10136
|
+
}
|
|
10137
|
+
const where = `${resource.replace(/^repo:/, "")} \xB7 ${binding.ref}@${binding.sha.slice(0, 7)}`;
|
|
10138
|
+
return {
|
|
10139
|
+
executor,
|
|
10140
|
+
resident: true,
|
|
10141
|
+
backend: "resident",
|
|
10142
|
+
...binding.trace ? { trace: binding.trace } : {},
|
|
10143
|
+
...binding.attachMs !== void 0 ? { attachMs: binding.attachMs } : {},
|
|
10144
|
+
binding,
|
|
10145
|
+
note: nonWarm ? `resident ${nonWarm} \xB7 ${where} \xB7 re-attached to the run's worktree` : `resident \xB7 ${where} \xB7 re-attached to the run's worktree`
|
|
10146
|
+
};
|
|
10147
|
+
}
|
|
10059
10148
|
async function openResident(opts, nonWarm, span) {
|
|
10060
10149
|
let executor = new ResidentExecutor(opts);
|
|
10061
10150
|
let binding;
|
|
@@ -10206,7 +10295,7 @@ function perThreadBackend(opts) {
|
|
|
10206
10295
|
const type = opts.execution?.type ?? "local";
|
|
10207
10296
|
return type === "e2b" ? "e2b" : type === "cloudflare" ? "sandbox" : "local";
|
|
10208
10297
|
}
|
|
10209
|
-
var PROBE_OUTAGE_WINDOW_MS, probeOutage, NullExecutor;
|
|
10298
|
+
var BACKENDS, WorkspaceReattachRefusedError, PROBE_OUTAGE_WINDOW_MS, probeOutage, NullExecutor;
|
|
10210
10299
|
var init_factory = __esm({
|
|
10211
10300
|
"../../src/execution/factory.ts"() {
|
|
10212
10301
|
"use strict";
|
|
@@ -10221,6 +10310,17 @@ var init_factory = __esm({
|
|
|
10221
10310
|
init_residentState();
|
|
10222
10311
|
init_clock();
|
|
10223
10312
|
init_secrets2();
|
|
10313
|
+
BACKENDS = ["local", "resident", "sandbox", "e2b"];
|
|
10314
|
+
WorkspaceReattachRefusedError = class extends Error {
|
|
10315
|
+
constructor(recorded, why) {
|
|
10316
|
+
super(`the run's workspace on the ${recorded.backend} backend could not be re-attached: ${why}`);
|
|
10317
|
+
this.recorded = recorded;
|
|
10318
|
+
this.why = why;
|
|
10319
|
+
this.name = "WorkspaceReattachRefusedError";
|
|
10320
|
+
}
|
|
10321
|
+
recorded;
|
|
10322
|
+
why;
|
|
10323
|
+
};
|
|
10224
10324
|
PROBE_OUTAGE_WINDOW_MS = 3e4;
|
|
10225
10325
|
NullExecutor = class {
|
|
10226
10326
|
constructor(agentName) {
|
|
@@ -20508,7 +20608,7 @@ function createRunsService(deps) {
|
|
|
20508
20608
|
const analyze = deps.analyze ?? ((events, opts) => analyzeRunFriction(events, opts));
|
|
20509
20609
|
const clock3 = deps.clock ?? systemClock;
|
|
20510
20610
|
const warn = deps.warn ?? ((m) => console.warn(m));
|
|
20511
|
-
const
|
|
20611
|
+
const describe12 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
20512
20612
|
let listing;
|
|
20513
20613
|
const liveRows = () => {
|
|
20514
20614
|
if (!ledger) return null;
|
|
@@ -20528,7 +20628,7 @@ function createRunsService(deps) {
|
|
|
20528
20628
|
try {
|
|
20529
20629
|
rows = await pending2;
|
|
20530
20630
|
} catch (err2) {
|
|
20531
|
-
warn(`[runs] run ledger list failed \u2014 showing this process's runs only: ${
|
|
20631
|
+
warn(`[runs] run ledger list failed \u2014 showing this process's runs only: ${describe12(err2)}`);
|
|
20532
20632
|
return [];
|
|
20533
20633
|
}
|
|
20534
20634
|
const foreign = rows.filter((row) => !registry.getById(row.runId));
|
|
@@ -20538,7 +20638,7 @@ function createRunsService(deps) {
|
|
|
20538
20638
|
try {
|
|
20539
20639
|
events = await ledger.readEvents(row.runId);
|
|
20540
20640
|
} catch (err2) {
|
|
20541
|
-
warn(`[runs] run ledger events read failed for ${row.runId}: ${
|
|
20641
|
+
warn(`[runs] run ledger events read failed for ${row.runId}: ${describe12(err2)}`);
|
|
20542
20642
|
}
|
|
20543
20643
|
return ledgerView(row, events);
|
|
20544
20644
|
})
|
|
@@ -20552,7 +20652,7 @@ function createRunsService(deps) {
|
|
|
20552
20652
|
if (!row) return null;
|
|
20553
20653
|
return { row, events: await ledger.readEvents(id) };
|
|
20554
20654
|
} catch (err2) {
|
|
20555
|
-
warn(`[runs] run ledger read failed for ${id}: ${
|
|
20655
|
+
warn(`[runs] run ledger read failed for ${id}: ${describe12(err2)}`);
|
|
20556
20656
|
return null;
|
|
20557
20657
|
}
|
|
20558
20658
|
};
|
|
@@ -20570,7 +20670,7 @@ function createRunsService(deps) {
|
|
|
20570
20670
|
row = await storeSummary(id);
|
|
20571
20671
|
} catch (err2) {
|
|
20572
20672
|
warn(
|
|
20573
|
-
`[runs] history store read failed for ${id} \u2014 serving the registry row without its record: ${
|
|
20673
|
+
`[runs] history store read failed for ${id} \u2014 serving the registry row without its record: ${describe12(err2)}`
|
|
20574
20674
|
);
|
|
20575
20675
|
return {};
|
|
20576
20676
|
}
|
|
@@ -20741,7 +20841,7 @@ function createRunsService(deps) {
|
|
|
20741
20841
|
const r = await ledger.requestStop(id, mode);
|
|
20742
20842
|
if (r.ok) return { ok: true, value: { id, mode, state: "stopping" } };
|
|
20743
20843
|
} catch (err2) {
|
|
20744
|
-
warn(`[runs] run ledger stop failed for ${id}: ${
|
|
20844
|
+
warn(`[runs] run ledger stop failed for ${id}: ${describe12(err2)}`);
|
|
20745
20845
|
}
|
|
20746
20846
|
}
|
|
20747
20847
|
return await storeSummary(id) ? conflict : notFound;
|
|
@@ -26605,9 +26705,10 @@ var init_runs2 = __esm({
|
|
|
26605
26705
|
request.budget = budget;
|
|
26606
26706
|
}
|
|
26607
26707
|
const spawn7 = ctx.spawn ?? nullSpawnCapability;
|
|
26708
|
+
const conversation = ctx.conversation ? await ctx.conversation() : void 0;
|
|
26608
26709
|
const out = await spawn7.spawn(request, {
|
|
26609
26710
|
remainingMs: ctx.remainingMs?.() ?? Number.POSITIVE_INFINITY,
|
|
26610
|
-
...
|
|
26711
|
+
...conversation ? { conversation } : {}
|
|
26611
26712
|
});
|
|
26612
26713
|
if (out.kind === "refused") return `spawn refused (${out.reason}): ${out.message}`;
|
|
26613
26714
|
return `spawned a ${preset} run: ${out.runId} in thread ${out.threadKey}${out.url ? ` (${out.url})` : ""} \u2014 it runs on its own; await_runs waits for it and returns its reply; get_run_status ${out.runId} answers its progress meanwhile.`;
|
|
@@ -26786,6 +26887,252 @@ var init_runs2 = __esm({
|
|
|
26786
26887
|
}
|
|
26787
26888
|
});
|
|
26788
26889
|
|
|
26890
|
+
// ../../src/core/dispatch/staging.ts
|
|
26891
|
+
function stagedBasename(index, name) {
|
|
26892
|
+
return `${index}-${safeBasename(name)}`;
|
|
26893
|
+
}
|
|
26894
|
+
function pullCommandFor(url, basename4) {
|
|
26895
|
+
return `mkdir -p ${ATTACHMENTS_DIR} && curl -fsS -o ${shellQuote(`${ATTACHMENTS_DIR}/${basename4}`)} ${shellQuote(url)}`;
|
|
26896
|
+
}
|
|
26897
|
+
function excludeCommand() {
|
|
26898
|
+
const entry = shellQuote(`${ATTACHMENTS_DIR}/`);
|
|
26899
|
+
return `mkdir -p .git/info && { grep -qxF ${entry} .git/info/exclude 2>/dev/null || echo ${entry} >> .git/info/exclude; }`;
|
|
26900
|
+
}
|
|
26901
|
+
function formatSize(bytes) {
|
|
26902
|
+
if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)} GB`;
|
|
26903
|
+
if (bytes >= 1e6) return `${Math.round(bytes / 1e6)} MB`;
|
|
26904
|
+
if (bytes >= 1e3) return `${Math.round(bytes / 1e3)} KB`;
|
|
26905
|
+
return `${bytes} B`;
|
|
26906
|
+
}
|
|
26907
|
+
function attachmentsLine(outcomes) {
|
|
26908
|
+
const landed = outcomes.filter((o) => o.error === void 0);
|
|
26909
|
+
const failed = outcomes.filter((o) => o.error !== void 0);
|
|
26910
|
+
const parts = [];
|
|
26911
|
+
if (landed.length > 0) {
|
|
26912
|
+
parts.push(
|
|
26913
|
+
`Attached files are in ./${ATTACHMENTS_DIR}/: ${landed.map(
|
|
26914
|
+
(o) => `${o.basename} (${formatSize(o.file.size)}, ${o.file.type}${o.earlier ? ", from earlier in the thread" : ""})`
|
|
26915
|
+
).join(", ")}`
|
|
26916
|
+
);
|
|
26917
|
+
}
|
|
26918
|
+
for (const o of failed) parts.push(`${o.file.name} could not be staged: ${o.error}`);
|
|
26919
|
+
return parts.join(". ");
|
|
26920
|
+
}
|
|
26921
|
+
function noWorkspaceLine(files) {
|
|
26922
|
+
return files.map(
|
|
26923
|
+
(f) => `this agent has no workspace for ${f.name} (${formatSize(f.size)}, ${f.type}); ask \`agent:coding\` to work with it`
|
|
26924
|
+
).join(". ");
|
|
26925
|
+
}
|
|
26926
|
+
function stagingIndex() {
|
|
26927
|
+
let n2 = 0;
|
|
26928
|
+
return () => ++n2;
|
|
26929
|
+
}
|
|
26930
|
+
async function copyStaged(files, deps) {
|
|
26931
|
+
return Promise.all(
|
|
26932
|
+
files.map(async (file) => {
|
|
26933
|
+
const index = deps.nextIndex();
|
|
26934
|
+
const basename4 = stagedBasename(index, file.name);
|
|
26935
|
+
const key = inboundKey(deps.threadKey, file.messageId, index, file.name);
|
|
26936
|
+
try {
|
|
26937
|
+
await deps.store.copyFromUrl({ url: file.url, size: file.size, key });
|
|
26938
|
+
deps.publish?.({
|
|
26939
|
+
type: "artifact",
|
|
26940
|
+
direction: "in",
|
|
26941
|
+
key,
|
|
26942
|
+
name: file.name,
|
|
26943
|
+
size: file.size,
|
|
26944
|
+
contentType: file.type
|
|
26945
|
+
});
|
|
26946
|
+
return { file, basename: basename4, key };
|
|
26947
|
+
} catch (err2) {
|
|
26948
|
+
return { file, basename: basename4, key, error: `the copy into the store failed: ${describe4(err2)}` };
|
|
26949
|
+
}
|
|
26950
|
+
})
|
|
26951
|
+
);
|
|
26952
|
+
}
|
|
26953
|
+
async function pullStaged(outcomes, deps) {
|
|
26954
|
+
const landed = outcomes.filter((o) => o.error === void 0);
|
|
26955
|
+
if (landed.length === 0) return [...outcomes];
|
|
26956
|
+
const timeoutMs = deps.timeoutMs ?? BASH_TIMEOUT_MAX_MS;
|
|
26957
|
+
const opts = { timeoutMs, ...deps.signal ? { signal: deps.signal } : {} };
|
|
26958
|
+
if (deps.resident) await deps.executor.exec(excludeCommand(), opts);
|
|
26959
|
+
const pulled = /* @__PURE__ */ new Map();
|
|
26960
|
+
for (const o of landed) {
|
|
26961
|
+
const url = await deps.store.presignGet(o.key);
|
|
26962
|
+
const out = await deps.executor.exec(pullCommandFor(url, o.basename), opts);
|
|
26963
|
+
pulled.set(o.key, parseExitPrefix(out).failed ? `the pull into the workspace failed: ${out.trim()}` : void 0);
|
|
26964
|
+
}
|
|
26965
|
+
return outcomes.map((o) => {
|
|
26966
|
+
const error = o.error ?? pulled.get(o.key);
|
|
26967
|
+
if (error !== void 0) return { ...o, error };
|
|
26968
|
+
const { error: _dropped, ...landedOutcome } = o;
|
|
26969
|
+
return landedOutcome;
|
|
26970
|
+
});
|
|
26971
|
+
}
|
|
26972
|
+
function stageThreadArtifacts(artifacts, deps) {
|
|
26973
|
+
const outcomes = [];
|
|
26974
|
+
for (const a of artifacts) {
|
|
26975
|
+
const file = { name: a.name, size: a.size, type: a.contentType };
|
|
26976
|
+
if (a.held === void 0) {
|
|
26977
|
+
outcomes.push({
|
|
26978
|
+
file,
|
|
26979
|
+
basename: safeBasename(a.name),
|
|
26980
|
+
key: a.key,
|
|
26981
|
+
earlier: true,
|
|
26982
|
+
error: "the store could not be asked whether it still holds it"
|
|
26983
|
+
});
|
|
26984
|
+
continue;
|
|
26985
|
+
}
|
|
26986
|
+
if (!a.held) {
|
|
26987
|
+
outcomes.push({
|
|
26988
|
+
file,
|
|
26989
|
+
basename: safeBasename(a.name),
|
|
26990
|
+
key: a.key,
|
|
26991
|
+
earlier: true,
|
|
26992
|
+
error: "the store no longer holds it (its retention passed)"
|
|
26993
|
+
});
|
|
26994
|
+
continue;
|
|
26995
|
+
}
|
|
26996
|
+
const basename4 = stagedBasename(deps.nextIndex(), a.name);
|
|
26997
|
+
deps.publish?.({
|
|
26998
|
+
type: "artifact",
|
|
26999
|
+
direction: "in",
|
|
27000
|
+
key: a.key,
|
|
27001
|
+
name: a.name,
|
|
27002
|
+
size: a.size,
|
|
27003
|
+
contentType: a.contentType
|
|
27004
|
+
});
|
|
27005
|
+
outcomes.push({ file, basename: basename4, key: a.key, earlier: true });
|
|
27006
|
+
}
|
|
27007
|
+
return outcomes;
|
|
27008
|
+
}
|
|
27009
|
+
async function stageIntoWorkspace(files, deps) {
|
|
27010
|
+
const outcomes = await pullStaged(await copyStaged(files, deps), deps);
|
|
27011
|
+
return { line: attachmentsLine(outcomes), outcomes };
|
|
27012
|
+
}
|
|
27013
|
+
var ATTACHMENTS_DIR, WorkspaceFiles, describe4;
|
|
27014
|
+
var init_staging = __esm({
|
|
27015
|
+
"../../src/core/dispatch/staging.ts"() {
|
|
27016
|
+
"use strict";
|
|
27017
|
+
init_keys();
|
|
27018
|
+
init_bashTimeout();
|
|
27019
|
+
init_shellQuote();
|
|
27020
|
+
init_runEvents();
|
|
27021
|
+
ATTACHMENTS_DIR = "attachments";
|
|
27022
|
+
WorkspaceFiles = class {
|
|
27023
|
+
paths = /* @__PURE__ */ new Map();
|
|
27024
|
+
/** Every outcome that landed: its key now names a workspace path. */
|
|
27025
|
+
record(outcomes) {
|
|
27026
|
+
for (const o of outcomes) if (o.error === void 0) this.paths.set(o.key, `${ATTACHMENTS_DIR}/${o.basename}`);
|
|
27027
|
+
}
|
|
27028
|
+
pathOf(key) {
|
|
27029
|
+
return this.paths.get(key);
|
|
27030
|
+
}
|
|
27031
|
+
};
|
|
27032
|
+
describe4 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
27033
|
+
}
|
|
27034
|
+
});
|
|
27035
|
+
|
|
27036
|
+
// ../../src/core/dispatch/threadAssets.ts
|
|
27037
|
+
function threadAssetsOf(runsOldestFirst) {
|
|
27038
|
+
const seen = /* @__PURE__ */ new Set();
|
|
27039
|
+
const out = [];
|
|
27040
|
+
for (const run2 of runsOldestFirst) {
|
|
27041
|
+
for (const e of run2.events) {
|
|
27042
|
+
if (e.type !== "artifact" || seen.has(e.key)) continue;
|
|
27043
|
+
seen.add(e.key);
|
|
27044
|
+
out.push({
|
|
27045
|
+
key: e.key,
|
|
27046
|
+
name: e.name,
|
|
27047
|
+
size: e.size,
|
|
27048
|
+
contentType: e.contentType,
|
|
27049
|
+
direction: e.direction,
|
|
27050
|
+
runId: run2.id,
|
|
27051
|
+
...e.seq !== void 0 ? { seq: e.seq } : {}
|
|
27052
|
+
});
|
|
27053
|
+
}
|
|
27054
|
+
}
|
|
27055
|
+
return out;
|
|
27056
|
+
}
|
|
27057
|
+
async function readThreadAssets(deps, threadKey, warn = (line3) => console.warn(line3)) {
|
|
27058
|
+
const runs = [];
|
|
27059
|
+
let before;
|
|
27060
|
+
try {
|
|
27061
|
+
for (; ; ) {
|
|
27062
|
+
const page2 = await deps.runs.listRuns({
|
|
27063
|
+
status: "all",
|
|
27064
|
+
visibleTo: { kind: "all" },
|
|
27065
|
+
threadKey,
|
|
27066
|
+
limit: THREAD_ASSETS_PAGE,
|
|
27067
|
+
...before !== void 0 ? { before: before.finishedAt, beforeId: before.id } : {}
|
|
27068
|
+
});
|
|
27069
|
+
runs.push(...page2.runs);
|
|
27070
|
+
if (page2.storeUnavailable) {
|
|
27071
|
+
warn(
|
|
27072
|
+
`[thread] ${threadKey}: listing its runs for their files answered live rows only after ${runs.length} run(s) \u2014 the run store was unavailable`
|
|
27073
|
+
);
|
|
27074
|
+
break;
|
|
27075
|
+
}
|
|
27076
|
+
if (page2.nextBefore === void 0) break;
|
|
27077
|
+
before = page2.nextBefore;
|
|
27078
|
+
}
|
|
27079
|
+
} catch (err2) {
|
|
27080
|
+
warn(
|
|
27081
|
+
`[thread] ${threadKey}: listing its runs for their files stopped after ${runs.length} run(s) \u2014 ${describe5(err2)}`
|
|
27082
|
+
);
|
|
27083
|
+
}
|
|
27084
|
+
const perRun = [];
|
|
27085
|
+
for (const run2 of runs.reverse()) {
|
|
27086
|
+
const events = [];
|
|
27087
|
+
let afterSeq;
|
|
27088
|
+
try {
|
|
27089
|
+
for (; ; ) {
|
|
27090
|
+
const r = await deps.runs.getRunEvents(run2.id, afterSeq === void 0 ? {} : { afterSeq });
|
|
27091
|
+
if (!r.ok) {
|
|
27092
|
+
warn(`[thread] ${run2.id}: reading its files stopped after ${events.length} event(s) \u2014 ${r.error}`);
|
|
27093
|
+
break;
|
|
27094
|
+
}
|
|
27095
|
+
events.push(...r.value.events);
|
|
27096
|
+
if (r.value.nextAfterSeq === void 0) break;
|
|
27097
|
+
afterSeq = r.value.nextAfterSeq;
|
|
27098
|
+
}
|
|
27099
|
+
} catch (err2) {
|
|
27100
|
+
warn(`[thread] ${run2.id}: reading its files failed after ${events.length} event(s) \u2014 ${describe5(err2)}`);
|
|
27101
|
+
}
|
|
27102
|
+
perRun.push({ id: run2.id, events });
|
|
27103
|
+
}
|
|
27104
|
+
return Promise.all(
|
|
27105
|
+
threadAssetsOf(perRun).map(async (asset) => {
|
|
27106
|
+
try {
|
|
27107
|
+
return { ...asset, held: await deps.store.head(asset.key) !== null };
|
|
27108
|
+
} catch (err2) {
|
|
27109
|
+
warn(`[thread] ${asset.key}: the store could not be asked whether it holds it \u2014 ${describe5(err2)}`);
|
|
27110
|
+
return asset;
|
|
27111
|
+
}
|
|
27112
|
+
})
|
|
27113
|
+
);
|
|
27114
|
+
}
|
|
27115
|
+
function describeAsset(asset, path) {
|
|
27116
|
+
const what = `${asset.name} (${formatSize(asset.size)}, ${asset.contentType})`;
|
|
27117
|
+
const origin = asset.direction === "in" ? "received on this thread" : `produced by run ${asset.runId}`;
|
|
27118
|
+
return `${what} \u2014 ${origin}; ${whereIs(asset, path)}`;
|
|
27119
|
+
}
|
|
27120
|
+
function whereIs(asset, path) {
|
|
27121
|
+
if (path !== void 0) return `in this workspace at ./${path}`;
|
|
27122
|
+
if (asset.held === false) return "no longer in the store (its retention passed)";
|
|
27123
|
+
if (asset.held === void 0) return "the store could not be asked whether it still holds it";
|
|
27124
|
+
return asset.direction === "out" ? "on that run's page, not in this workspace" : "in the store, not in this workspace";
|
|
27125
|
+
}
|
|
27126
|
+
var THREAD_ASSETS_PAGE, describe5;
|
|
27127
|
+
var init_threadAssets = __esm({
|
|
27128
|
+
"../../src/core/dispatch/threadAssets.ts"() {
|
|
27129
|
+
"use strict";
|
|
27130
|
+
init_staging();
|
|
27131
|
+
THREAD_ASSETS_PAGE = 50;
|
|
27132
|
+
describe5 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
27133
|
+
}
|
|
27134
|
+
});
|
|
27135
|
+
|
|
26789
27136
|
// ../../src/core/runLedger/sessionLog.ts
|
|
26790
27137
|
function sessionKey(threadKey, agent) {
|
|
26791
27138
|
return `${threadKey}:${agent ?? "-"}`;
|
|
@@ -26807,31 +27154,50 @@ var init_sessionLog = __esm({
|
|
|
26807
27154
|
});
|
|
26808
27155
|
|
|
26809
27156
|
// ../../src/tools/session.ts
|
|
26810
|
-
function sessionCapabilityFor(run2, ledger) {
|
|
27157
|
+
function sessionCapabilityFor(run2, ledger, assets) {
|
|
26811
27158
|
const session = run2?.session;
|
|
26812
27159
|
if (!session) return void 0;
|
|
26813
27160
|
return {
|
|
26814
27161
|
session,
|
|
26815
27162
|
search: (query, limit) => ledger.searchSession(session.key, query, limit),
|
|
26816
27163
|
readTurn: async (idx) => (await ledger.readSession(session.key, idx, idx)).messages[0],
|
|
27164
|
+
readConversation: async () => (await ledger.readSession(session.key, session.seedFrom)).messages,
|
|
26817
27165
|
readNotepad: () => ledger.readNotepad(session.key),
|
|
26818
|
-
writeNotepad: (text) => ledger.writeNotepad(session.key, text)
|
|
27166
|
+
writeNotepad: (text) => ledger.writeNotepad(session.key, text),
|
|
27167
|
+
...assets ? { assets: assets.read, workspacePathOf: assets.pathOf } : {}
|
|
26819
27168
|
};
|
|
26820
27169
|
}
|
|
27170
|
+
function assetView(asset, path) {
|
|
27171
|
+
return {
|
|
27172
|
+
name: asset.name,
|
|
27173
|
+
size: asset.size,
|
|
27174
|
+
type: asset.contentType,
|
|
27175
|
+
[asset.direction === "in" ? "received" : "produced"]: `run ${asset.runId}`,
|
|
27176
|
+
key: asset.key,
|
|
27177
|
+
where: whereIs(asset, path)
|
|
27178
|
+
};
|
|
27179
|
+
}
|
|
27180
|
+
function filesNamedIn(text, catalogue) {
|
|
27181
|
+
return catalogue.filter((a) => [a.name, safeBasename(a.name)].some((n2) => nameAt2(n2).test(text)));
|
|
27182
|
+
}
|
|
26821
27183
|
async function mayRead(ctx) {
|
|
26822
27184
|
if (!ctx.runs?.runId) return false;
|
|
26823
27185
|
const res = await ctx.runs.service.getRun(ctx.runs.runId);
|
|
26824
27186
|
return res.ok && authorize(ctx.runs.actor, "runs:read", runResource(res.value)).allow;
|
|
26825
27187
|
}
|
|
26826
|
-
var UNAVAILABLE3, DEFAULT_HITS, MAX_HITS, SNIPPET_CHARS, snippetOf, recallTool, notesTool, SESSION_TOOLS;
|
|
27188
|
+
var FILE_BEARING, nameAt2, UNAVAILABLE3, DEFAULT_HITS, MAX_HITS, SNIPPET_CHARS, snippetOf, recallTool, notesTool, SESSION_TOOLS;
|
|
26827
27189
|
var init_session = __esm({
|
|
26828
27190
|
"../../src/tools/session.ts"() {
|
|
26829
27191
|
"use strict";
|
|
27192
|
+
init_keys();
|
|
26830
27193
|
init_authorize();
|
|
27194
|
+
init_threadAssets();
|
|
26831
27195
|
init_runEvents();
|
|
26832
27196
|
init_sessionLog();
|
|
26833
27197
|
init_runRecord();
|
|
26834
27198
|
init_runsService();
|
|
27199
|
+
FILE_BEARING = /attachments\/|attach_file|attached files/i;
|
|
27200
|
+
nameAt2 = (name) => new RegExp(`(?<![A-Za-z0-9._])${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9._-])`);
|
|
26835
27201
|
UNAVAILABLE3 = "the session tools are not available in this context: this run has no session log.";
|
|
26836
27202
|
DEFAULT_HITS = 5;
|
|
26837
27203
|
MAX_HITS = 50;
|
|
@@ -26842,7 +27208,7 @@ var init_session = __esm({
|
|
|
26842
27208
|
};
|
|
26843
27209
|
recallTool = {
|
|
26844
27210
|
name: "recall",
|
|
26845
|
-
description: "Search this conversation's whole log \u2014 every turn of this thread on this agent, earlier runs' included and everything a compaction removed from your window \u2014 for words, and get the matching turns in relevance order with their turn numbers and a snippet; or pass `turn` to read one turn whole (a tool result, a command, a message). Use it when you need something said or seen earlier that is not in front of you: a failing test's name, a command's output, a decision. `limit` defaults to 5 (at most 50).",
|
|
27211
|
+
description: "Search this conversation's whole log \u2014 every turn of this thread on this agent, earlier runs' included and everything a compaction removed from your window \u2014 for words, and get the matching turns in relevance order with their turn numbers and a snippet; or pass `turn` to read one turn whole (a tool result, a command, a message). Use it when you need something said or seen earlier that is not in front of you: a failing test's name, a command's output, a decision. `limit` defaults to 5 (at most 50). A hit that names a file of this thread carries the file's key and where it is for this run; `assets: true` lists every file the thread received or its runs produced instead of searching.",
|
|
26846
27212
|
inputSchema: {
|
|
26847
27213
|
type: "object",
|
|
26848
27214
|
properties: {
|
|
@@ -26851,6 +27217,10 @@ var init_session = __esm({
|
|
|
26851
27217
|
turn: {
|
|
26852
27218
|
type: "integer",
|
|
26853
27219
|
description: "A turn number from an earlier recall: returns that turn whole instead of searching"
|
|
27220
|
+
},
|
|
27221
|
+
assets: {
|
|
27222
|
+
type: "boolean",
|
|
27223
|
+
description: "List the thread's files \u2014 received on its messages or produced by its runs \u2014 with their keys and where each is for this run, instead of searching"
|
|
26854
27224
|
}
|
|
26855
27225
|
}
|
|
26856
27226
|
},
|
|
@@ -26859,13 +27229,30 @@ var init_session = __esm({
|
|
|
26859
27229
|
if (!ctx.session) return UNAVAILABLE3;
|
|
26860
27230
|
const turn = typeof input.turn === "number" ? Math.trunc(input.turn) : void 0;
|
|
26861
27231
|
const query = typeof input.query === "string" ? input.query.trim() : "";
|
|
26862
|
-
|
|
26863
|
-
if (
|
|
27232
|
+
const listAssets = input.assets === true;
|
|
27233
|
+
if (turn === void 0 && query.length === 0 && !listAssets)
|
|
27234
|
+
return "recall needs a query, a turn number or assets: true.";
|
|
27235
|
+
if (!await mayRead(ctx)) {
|
|
27236
|
+
return JSON.stringify(turn !== void 0 ? { turn, content: null } : listAssets ? { assets: [] } : { hits: [] });
|
|
27237
|
+
}
|
|
26864
27238
|
if (turn !== void 0) {
|
|
26865
27239
|
const message3 = turn >= 0 ? await ctx.session.readTurn(turn) : void 0;
|
|
26866
27240
|
if (!message3) return `no turn ${turn} in this session's log.`;
|
|
26867
27241
|
return JSON.stringify({ turn, role: message3.role, content: message3.content });
|
|
26868
27242
|
}
|
|
27243
|
+
if (listAssets) {
|
|
27244
|
+
if (!ctx.session.assets) {
|
|
27245
|
+
return JSON.stringify({
|
|
27246
|
+
assets: [],
|
|
27247
|
+
note: "this deployment has no artifact store: the thread's files are not catalogued"
|
|
27248
|
+
});
|
|
27249
|
+
}
|
|
27250
|
+
const assets = await ctx.session.assets();
|
|
27251
|
+
return JSON.stringify({
|
|
27252
|
+
assets: assets.map((a) => assetView(a, ctx.session.workspacePathOf?.(a.key))),
|
|
27253
|
+
...assets.length === 0 ? { note: "no run of this thread has received or produced a file" } : {}
|
|
27254
|
+
});
|
|
27255
|
+
}
|
|
26869
27256
|
const limit = Math.min(
|
|
26870
27257
|
MAX_HITS,
|
|
26871
27258
|
Math.max(1, typeof input.limit === "number" ? Math.trunc(input.limit) : DEFAULT_HITS)
|
|
@@ -26875,12 +27262,17 @@ var init_session = __esm({
|
|
|
26875
27262
|
if (hits.length === 0)
|
|
26876
27263
|
notes.push("no turn of this session's log matches; your notes (notes {}) may hold what you look for");
|
|
26877
27264
|
if (ctx.session.session.seedFrom === 0) notes.push("this is the first run of its session: the log begins with it");
|
|
27265
|
+
const catalogue = ctx.session.assets && hits.some((h) => FILE_BEARING.test(h.text)) ? await ctx.session.assets() : [];
|
|
26878
27266
|
return JSON.stringify({
|
|
26879
|
-
hits: hits.map((h) =>
|
|
26880
|
-
|
|
26881
|
-
|
|
26882
|
-
|
|
26883
|
-
|
|
27267
|
+
hits: hits.map((h) => {
|
|
27268
|
+
const files = filesNamedIn(h.text, catalogue);
|
|
27269
|
+
return {
|
|
27270
|
+
turn: h.idx,
|
|
27271
|
+
...h.role !== void 0 ? { role: h.role } : {},
|
|
27272
|
+
snippet: snippetOf(h.text),
|
|
27273
|
+
...files.length > 0 ? { files: files.map((a) => assetView(a, ctx.session.workspacePathOf?.(a.key))) } : {}
|
|
27274
|
+
};
|
|
27275
|
+
}),
|
|
26884
27276
|
...gaps.length ? {
|
|
26885
27277
|
gaps: `the log has a gap at ${gaps.map((g) => `turn ${g}`).join(", ")}: a run before it detached from the ledger, so turns it saw after that point are not here`
|
|
26886
27278
|
} : {},
|
|
@@ -27545,7 +27937,11 @@ function imperativeRule(writers) {
|
|
|
27545
27937
|
}
|
|
27546
27938
|
function attachRule(attachers) {
|
|
27547
27939
|
const names = attachers.map((a) => `\`${a}\``).join(" or ");
|
|
27548
|
-
return
|
|
27940
|
+
return [
|
|
27941
|
+
`Only ${names} can attach or post a file into the thread (the \`attach_file\` tool).`,
|
|
27942
|
+
`A request that names attach_file routes to ${names}, whatever it asks the tool to do \u2014 a probe, a test or a diagnostic of the tool is still a call to it.`,
|
|
27943
|
+
"A request that asks for a file, a screenshot, a recording or an attachment to be posted, attached or sent back routes there too, however read-only the rest of it sounds; a read-only preset can describe a file but never post one."
|
|
27944
|
+
].join(" ");
|
|
27549
27945
|
}
|
|
27550
27946
|
function tidyReason(reason) {
|
|
27551
27947
|
const line3 = oneLine(redactAndCap(reason, ROUTE_REASON_CAP));
|
|
@@ -28613,7 +29009,8 @@ async function attachRoundWorkspace(input) {
|
|
|
28613
29009
|
profile,
|
|
28614
29010
|
repo: input.round.repo,
|
|
28615
29011
|
ref: input.round.ref,
|
|
28616
|
-
headSha: input.round.headSha
|
|
29012
|
+
headSha: input.round.headSha,
|
|
29013
|
+
...input.round.reattach !== void 0 ? { reattach: input.round.reattach } : {}
|
|
28617
29014
|
},
|
|
28618
29015
|
input.span
|
|
28619
29016
|
);
|
|
@@ -29240,157 +29637,6 @@ var init_messages = __esm({
|
|
|
29240
29637
|
}
|
|
29241
29638
|
});
|
|
29242
29639
|
|
|
29243
|
-
// ../../src/core/dispatch/staging.ts
|
|
29244
|
-
function stagedBasename(index, name) {
|
|
29245
|
-
return `${index}-${safeBasename(name)}`;
|
|
29246
|
-
}
|
|
29247
|
-
function pullCommandFor(url, basename4) {
|
|
29248
|
-
return `mkdir -p ${ATTACHMENTS_DIR} && curl -fsS -o ${shellQuote(`${ATTACHMENTS_DIR}/${basename4}`)} ${shellQuote(url)}`;
|
|
29249
|
-
}
|
|
29250
|
-
function excludeCommand() {
|
|
29251
|
-
const entry = shellQuote(`${ATTACHMENTS_DIR}/`);
|
|
29252
|
-
return `mkdir -p .git/info && { grep -qxF ${entry} .git/info/exclude 2>/dev/null || echo ${entry} >> .git/info/exclude; }`;
|
|
29253
|
-
}
|
|
29254
|
-
function formatSize(bytes) {
|
|
29255
|
-
if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)} GB`;
|
|
29256
|
-
if (bytes >= 1e6) return `${Math.round(bytes / 1e6)} MB`;
|
|
29257
|
-
if (bytes >= 1e3) return `${Math.round(bytes / 1e3)} KB`;
|
|
29258
|
-
return `${bytes} B`;
|
|
29259
|
-
}
|
|
29260
|
-
function attachmentsLine(outcomes) {
|
|
29261
|
-
const landed = outcomes.filter((o) => o.error === void 0);
|
|
29262
|
-
const failed = outcomes.filter((o) => o.error !== void 0);
|
|
29263
|
-
const parts = [];
|
|
29264
|
-
if (landed.length > 0) {
|
|
29265
|
-
parts.push(
|
|
29266
|
-
`Attached files are in ./${ATTACHMENTS_DIR}/: ${landed.map(
|
|
29267
|
-
(o) => `${o.basename} (${formatSize(o.file.size)}, ${o.file.type}${o.earlier ? ", from earlier in the thread" : ""})`
|
|
29268
|
-
).join(", ")}`
|
|
29269
|
-
);
|
|
29270
|
-
}
|
|
29271
|
-
for (const o of failed) parts.push(`${o.file.name} could not be staged: ${o.error}`);
|
|
29272
|
-
return parts.join(". ");
|
|
29273
|
-
}
|
|
29274
|
-
function noWorkspaceLine(files) {
|
|
29275
|
-
return files.map(
|
|
29276
|
-
(f) => `this agent has no workspace for ${f.name} (${formatSize(f.size)}, ${f.type}); ask \`agent:coding\` to work with it`
|
|
29277
|
-
).join(". ");
|
|
29278
|
-
}
|
|
29279
|
-
function stagingIndex() {
|
|
29280
|
-
let n2 = 0;
|
|
29281
|
-
return () => ++n2;
|
|
29282
|
-
}
|
|
29283
|
-
async function copyStaged(files, deps) {
|
|
29284
|
-
return Promise.all(
|
|
29285
|
-
files.map(async (file) => {
|
|
29286
|
-
const index = deps.nextIndex();
|
|
29287
|
-
const basename4 = stagedBasename(index, file.name);
|
|
29288
|
-
const key = inboundKey(deps.threadKey, file.messageId, index, file.name);
|
|
29289
|
-
try {
|
|
29290
|
-
await deps.store.copyFromUrl({ url: file.url, size: file.size, key });
|
|
29291
|
-
deps.publish?.({
|
|
29292
|
-
type: "artifact",
|
|
29293
|
-
direction: "in",
|
|
29294
|
-
key,
|
|
29295
|
-
name: file.name,
|
|
29296
|
-
size: file.size,
|
|
29297
|
-
contentType: file.type
|
|
29298
|
-
});
|
|
29299
|
-
return { file, basename: basename4, key };
|
|
29300
|
-
} catch (err2) {
|
|
29301
|
-
return { file, basename: basename4, key, error: `the copy into the store failed: ${describe4(err2)}` };
|
|
29302
|
-
}
|
|
29303
|
-
})
|
|
29304
|
-
);
|
|
29305
|
-
}
|
|
29306
|
-
async function pullStaged(outcomes, deps) {
|
|
29307
|
-
const landed = outcomes.filter((o) => o.error === void 0);
|
|
29308
|
-
if (landed.length === 0) return [...outcomes];
|
|
29309
|
-
const timeoutMs = deps.timeoutMs ?? BASH_TIMEOUT_MAX_MS;
|
|
29310
|
-
const opts = { timeoutMs, ...deps.signal ? { signal: deps.signal } : {} };
|
|
29311
|
-
if (deps.resident) await deps.executor.exec(excludeCommand(), opts);
|
|
29312
|
-
const pulled = /* @__PURE__ */ new Map();
|
|
29313
|
-
for (const o of landed) {
|
|
29314
|
-
const url = await deps.store.presignGet(o.key);
|
|
29315
|
-
const out = await deps.executor.exec(pullCommandFor(url, o.basename), opts);
|
|
29316
|
-
pulled.set(o.key, parseExitPrefix(out).failed ? `the pull into the workspace failed: ${out.trim()}` : void 0);
|
|
29317
|
-
}
|
|
29318
|
-
return outcomes.map((o) => {
|
|
29319
|
-
const error = o.error ?? pulled.get(o.key);
|
|
29320
|
-
if (error !== void 0) return { ...o, error };
|
|
29321
|
-
const { error: _dropped, ...landedOutcome } = o;
|
|
29322
|
-
return landedOutcome;
|
|
29323
|
-
});
|
|
29324
|
-
}
|
|
29325
|
-
function threadInboundArtifacts(runsOldestFirst) {
|
|
29326
|
-
const seen = /* @__PURE__ */ new Set();
|
|
29327
|
-
const out = [];
|
|
29328
|
-
for (const events of runsOldestFirst) {
|
|
29329
|
-
for (const e of events) {
|
|
29330
|
-
if (e.type !== "artifact" || e.direction !== "in" || seen.has(e.key)) continue;
|
|
29331
|
-
seen.add(e.key);
|
|
29332
|
-
out.push({ key: e.key, name: e.name, size: e.size, contentType: e.contentType });
|
|
29333
|
-
}
|
|
29334
|
-
}
|
|
29335
|
-
return out;
|
|
29336
|
-
}
|
|
29337
|
-
async function stageThreadArtifacts(artifacts, deps) {
|
|
29338
|
-
const outcomes = [];
|
|
29339
|
-
for (const a of artifacts) {
|
|
29340
|
-
const file = { name: a.name, size: a.size, type: a.contentType };
|
|
29341
|
-
let held;
|
|
29342
|
-
try {
|
|
29343
|
-
held = await deps.store.head(a.key) !== null;
|
|
29344
|
-
} catch (err2) {
|
|
29345
|
-
outcomes.push({
|
|
29346
|
-
file,
|
|
29347
|
-
basename: safeBasename(a.name),
|
|
29348
|
-
key: a.key,
|
|
29349
|
-
earlier: true,
|
|
29350
|
-
error: `the store could not be asked: ${describe4(err2)}`
|
|
29351
|
-
});
|
|
29352
|
-
continue;
|
|
29353
|
-
}
|
|
29354
|
-
if (!held) {
|
|
29355
|
-
outcomes.push({
|
|
29356
|
-
file,
|
|
29357
|
-
basename: safeBasename(a.name),
|
|
29358
|
-
key: a.key,
|
|
29359
|
-
earlier: true,
|
|
29360
|
-
error: "the store no longer holds it (its retention passed)"
|
|
29361
|
-
});
|
|
29362
|
-
continue;
|
|
29363
|
-
}
|
|
29364
|
-
const basename4 = stagedBasename(deps.nextIndex(), a.name);
|
|
29365
|
-
deps.publish?.({
|
|
29366
|
-
type: "artifact",
|
|
29367
|
-
direction: "in",
|
|
29368
|
-
key: a.key,
|
|
29369
|
-
name: a.name,
|
|
29370
|
-
size: a.size,
|
|
29371
|
-
contentType: a.contentType
|
|
29372
|
-
});
|
|
29373
|
-
outcomes.push({ file, basename: basename4, key: a.key, earlier: true });
|
|
29374
|
-
}
|
|
29375
|
-
return outcomes;
|
|
29376
|
-
}
|
|
29377
|
-
async function stageIntoWorkspace(files, deps) {
|
|
29378
|
-
const outcomes = await pullStaged(await copyStaged(files, deps), deps);
|
|
29379
|
-
return { line: attachmentsLine(outcomes), outcomes };
|
|
29380
|
-
}
|
|
29381
|
-
var ATTACHMENTS_DIR, describe4;
|
|
29382
|
-
var init_staging = __esm({
|
|
29383
|
-
"../../src/core/dispatch/staging.ts"() {
|
|
29384
|
-
"use strict";
|
|
29385
|
-
init_keys();
|
|
29386
|
-
init_bashTimeout();
|
|
29387
|
-
init_shellQuote();
|
|
29388
|
-
init_runEvents();
|
|
29389
|
-
ATTACHMENTS_DIR = "attachments";
|
|
29390
|
-
describe4 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
29391
|
-
}
|
|
29392
|
-
});
|
|
29393
|
-
|
|
29394
29640
|
// ../../src/core/modelProxy/runBearers.ts
|
|
29395
29641
|
import { createHash as createHash4, randomBytes as randomBytes4, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
29396
29642
|
function token(runId3, secret2) {
|
|
@@ -30093,7 +30339,7 @@ async function reserveRun(deps, ctx) {
|
|
|
30093
30339
|
return void 0;
|
|
30094
30340
|
}
|
|
30095
30341
|
async function attachWorkspace(deps, ctx) {
|
|
30096
|
-
const { msg, io, refuse: refuse3, card, shell, closeLines, clock: clock3, agent, profile, repoCtx, root } = ctx;
|
|
30342
|
+
const { msg, io, refuse: refuse3, card, shell, closeLines, clock: clock3, agent, profile, repoCtx, root, reattach } = ctx;
|
|
30097
30343
|
let round2;
|
|
30098
30344
|
try {
|
|
30099
30345
|
round2 = await root.span("dispatch.workspace.attach", async (span) => {
|
|
@@ -30118,7 +30364,8 @@ async function attachWorkspace(deps, ctx) {
|
|
|
30118
30364
|
profile,
|
|
30119
30365
|
repo: repoCtx.repo,
|
|
30120
30366
|
ref: repoCtx.ref,
|
|
30121
|
-
headSha: repoCtx.headSha
|
|
30367
|
+
headSha: repoCtx.headSha,
|
|
30368
|
+
...reattach !== void 0 ? { reattach } : {}
|
|
30122
30369
|
},
|
|
30123
30370
|
logKey: msg.threadKey,
|
|
30124
30371
|
span
|
|
@@ -30145,6 +30392,7 @@ async function attachWorkspace(deps, ctx) {
|
|
|
30145
30392
|
});
|
|
30146
30393
|
return { kind: "refused", reason: "which_branch" };
|
|
30147
30394
|
}
|
|
30395
|
+
if (err2 instanceof WorkspaceReattachRefusedError) return { kind: "reattach_refused", why: err2.why };
|
|
30148
30396
|
throw err2;
|
|
30149
30397
|
}
|
|
30150
30398
|
return { kind: "attached", round: round2 };
|
|
@@ -30181,6 +30429,10 @@ function sessionNotesBlock(session) {
|
|
|
30181
30429
|
parts.push(
|
|
30182
30430
|
"SUMMARY OF THE EARLIER CONVERSATION (written when the context was compacted; every earlier turn is still reachable with `recall`):\n" + summary
|
|
30183
30431
|
);
|
|
30432
|
+
if (session.files && session.files.length > 0)
|
|
30433
|
+
parts.push(
|
|
30434
|
+
"FILES OF THIS THREAD (received on its messages or produced by its runs, as their records name them; `recall {assets: true}` lists them with their keys):\n" + session.files.map((line3) => `- ${line3}`).join("\n")
|
|
30435
|
+
);
|
|
30184
30436
|
return parts.length ? parts.join("\n\n") : void 0;
|
|
30185
30437
|
}
|
|
30186
30438
|
async function composePrompt(deps, ctx) {
|
|
@@ -30280,6 +30532,7 @@ var init_provision = __esm({
|
|
|
30280
30532
|
init_contract3();
|
|
30281
30533
|
init_registry();
|
|
30282
30534
|
init_profile();
|
|
30535
|
+
init_factory();
|
|
30283
30536
|
init_residentTrace();
|
|
30284
30537
|
init_resident();
|
|
30285
30538
|
init_memory();
|
|
@@ -30333,6 +30586,7 @@ async function claimRun(deps, ctx) {
|
|
|
30333
30586
|
} = ctx;
|
|
30334
30587
|
const { resident, binding } = selection;
|
|
30335
30588
|
let ledgerRun = ctx.ledgerRun;
|
|
30589
|
+
const workspaceBinding = workspaceBindingFor(selection, profile.machine);
|
|
30336
30590
|
if (!resume && reserved) {
|
|
30337
30591
|
const ledger = deps.runLedger;
|
|
30338
30592
|
const opened = await root.span(
|
|
@@ -30366,6 +30620,7 @@ async function claimRun(deps, ctx) {
|
|
|
30366
30620
|
...requestRow !== void 0 ? { request: requestRow } : {}
|
|
30367
30621
|
},
|
|
30368
30622
|
card: card.handle ?? null,
|
|
30623
|
+
...workspaceBinding !== void 0 ? { state: { binding: workspaceBinding } } : {},
|
|
30369
30624
|
// The row reserved before the attach (item 42), promoted in place;
|
|
30370
30625
|
// its hooks (a stop, a fence) were wired at the reservation and stay.
|
|
30371
30626
|
reservation: reserved,
|
|
@@ -30422,6 +30677,7 @@ var init_run2 = __esm({
|
|
|
30422
30677
|
init_workspace();
|
|
30423
30678
|
init_web();
|
|
30424
30679
|
init_githubApi();
|
|
30680
|
+
init_factory();
|
|
30425
30681
|
init_runRegistry();
|
|
30426
30682
|
init_secrets2();
|
|
30427
30683
|
webCapability = () => sharedWeb ??= makeWebCapability(processSecrets);
|
|
@@ -30454,6 +30710,17 @@ const URL_ENV = "SWITCHBOARD_HARNESS_URL";
|
|
|
30454
30710
|
/** How long the tool_call hook keeps asking an unreachable bot before it blocks. */
|
|
30455
30711
|
const AUTHORIZE_WAIT_MS = 90_000;
|
|
30456
30712
|
const AUTHORIZE_RETRY_MS = 2_000;
|
|
30713
|
+
/** How long one request for a relayed tool's result may take before it is asked
|
|
30714
|
+
* again: over the bot's window (30 s, after which it answers that the call is
|
|
30715
|
+
* still running), under this runtime's own five-minute header timeout on a
|
|
30716
|
+
* fetch, so a hung connection costs a minute of the wait, not five. */
|
|
30717
|
+
const TOOL_REQUEST_TIMEOUT_MS = 60_000;
|
|
30718
|
+
/** How long a relayed call keeps asking a bot it cannot reach before it fails; the cadence is the hook's. */
|
|
30719
|
+
const TOOL_WAIT_MS = 90_000;
|
|
30720
|
+
/** A bot that says a call is still running is asked again after the window it
|
|
30721
|
+
* held the request for; one that answered sooner is asked again no sooner
|
|
30722
|
+
* than this, so a misbehaving bot is never asked in a hot loop. */
|
|
30723
|
+
const TOOL_REASK_FLOOR_MS = 1_000;
|
|
30457
30724
|
|
|
30458
30725
|
function settings() {
|
|
30459
30726
|
const url = process.env[URL_ENV];
|
|
@@ -30527,6 +30794,49 @@ async function authorize(event) {
|
|
|
30527
30794
|
}
|
|
30528
30795
|
}
|
|
30529
30796
|
|
|
30797
|
+
/** pi's abort signal for the call, with this request's own timeout beside it
|
|
30798
|
+
* where the runtime can combine the two; pi's alone where it cannot. */
|
|
30799
|
+
function requestSignal(signal) {
|
|
30800
|
+
const timeout = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(TOOL_REQUEST_TIMEOUT_MS) : undefined;
|
|
30801
|
+
if (!timeout) return signal;
|
|
30802
|
+
if (!signal) return timeout;
|
|
30803
|
+
return typeof AbortSignal.any === "function" ? AbortSignal.any([signal, timeout]) : signal;
|
|
30804
|
+
}
|
|
30805
|
+
|
|
30806
|
+
/** A relayed tool's result. The call is posted with its id; a bot that answers
|
|
30807
|
+
* that the call is still running (a wait on other runs can take minutes) is
|
|
30808
|
+
* asked again with the same id until it answers: the bot runs the call once
|
|
30809
|
+
* and every later ask joins it. A bot that cannot be reached or is failing (a
|
|
30810
|
+
* network error, a timed-out request, a 5xx) is asked again every two seconds
|
|
30811
|
+
* until the wait is up, then the call fails naming why; a refusal at the door
|
|
30812
|
+
* (a 4xx) fails it at once; an abort from pi ends the asking. */
|
|
30813
|
+
async function relay(toolCallId, tool, input, signal) {
|
|
30814
|
+
let lastAnswered = Date.now();
|
|
30815
|
+
let lastError = "";
|
|
30816
|
+
for (;;) {
|
|
30817
|
+
if (signal && signal.aborted) throw new Error("switchboard harness: " + tool + " was aborted before the bot answered");
|
|
30818
|
+
const asked = Date.now();
|
|
30819
|
+
let answer;
|
|
30820
|
+
try {
|
|
30821
|
+
answer = await call("POST", "/harness/tool", { toolCallId, tool, input }, requestSignal(signal));
|
|
30822
|
+
} catch (err) {
|
|
30823
|
+
if (signal && signal.aborted) throw err;
|
|
30824
|
+
const status = err instanceof HarnessAnswerError ? err.status : undefined;
|
|
30825
|
+
if (status !== undefined && status >= 400 && status < 500) throw err;
|
|
30826
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
30827
|
+
if (Date.now() - lastAnswered >= TOOL_WAIT_MS) {
|
|
30828
|
+
throw new Error("switchboard harness: the bot did not answer " + tool + " for 90 s (" + lastError + ")");
|
|
30829
|
+
}
|
|
30830
|
+
await sleep(AUTHORIZE_RETRY_MS);
|
|
30831
|
+
continue;
|
|
30832
|
+
}
|
|
30833
|
+
lastAnswered = Date.now();
|
|
30834
|
+
if (!answer.pending) return answer;
|
|
30835
|
+
const held = Date.now() - asked;
|
|
30836
|
+
if (held < TOOL_REASK_FLOOR_MS) await sleep(TOOL_REASK_FLOOR_MS - held);
|
|
30837
|
+
}
|
|
30838
|
+
}
|
|
30839
|
+
|
|
30530
30840
|
function relayTool(def) {
|
|
30531
30841
|
return {
|
|
30532
30842
|
name: def.name,
|
|
@@ -30534,7 +30844,7 @@ function relayTool(def) {
|
|
|
30534
30844
|
description: def.description,
|
|
30535
30845
|
parameters: def.inputSchema,
|
|
30536
30846
|
async execute(toolCallId, params, signal) {
|
|
30537
|
-
const answer = await
|
|
30847
|
+
const answer = await relay(toolCallId, def.name, params, signal);
|
|
30538
30848
|
if (answer.isError) throw new Error(answer.content.map((c) => (c.type === "text" ? c.text : "")).join("\\n"));
|
|
30539
30849
|
return { content: answer.content, details: {} };
|
|
30540
30850
|
},
|
|
@@ -30733,6 +31043,9 @@ function readLogScript(path, offset, maxBytes) {
|
|
|
30733
31043
|
function aliveScript(pid) {
|
|
30734
31044
|
return `kill -0 ${pid} 2>/dev/null && echo alive || echo dead`;
|
|
30735
31045
|
}
|
|
31046
|
+
function identityScript() {
|
|
31047
|
+
return "cat /proc/sys/kernel/random/boot_id 2>/dev/null || true";
|
|
31048
|
+
}
|
|
30736
31049
|
function killScript(pid) {
|
|
30737
31050
|
return `kill -TERM -- -${pid} 2>/dev/null; kill -TERM ${pid} 2>/dev/null; sleep 1; kill -KILL -- -${pid} 2>/dev/null; kill -KILL ${pid} 2>/dev/null; true`;
|
|
30738
31051
|
}
|
|
@@ -30749,7 +31062,7 @@ function stdoutOf(operation, out) {
|
|
|
30749
31062
|
const stdout = cut >= 0 ? out.slice(0, cut) : out;
|
|
30750
31063
|
return stdout === "(no output)" ? "" : stdout;
|
|
30751
31064
|
}
|
|
30752
|
-
var PiContainerError, WRITE_CHUNK_CHARS, INLINE_LINE_CHARS, LOG_READ_BYTES, MESSAGE_UPDATE_MARK, LOG_FILTER, STDERR_MARK, OP_TIMEOUT_MS, ExecPiContainer;
|
|
31065
|
+
var PiContainerError, WRITE_CHUNK_CHARS, INLINE_LINE_CHARS, LOG_READ_BYTES, MESSAGE_UPDATE_MARK, LOG_FILTER, IDENTITY_WORD, STDERR_MARK, OP_TIMEOUT_MS, ExecPiContainer;
|
|
30753
31066
|
var init_container = __esm({
|
|
30754
31067
|
"../../src/core/harness/pi/container.ts"() {
|
|
30755
31068
|
"use strict";
|
|
@@ -30769,6 +31082,7 @@ var init_container = __esm({
|
|
|
30769
31082
|
LOG_READ_BYTES = 48 * 1024;
|
|
30770
31083
|
MESSAGE_UPDATE_MARK = '"type":"message_update"';
|
|
30771
31084
|
LOG_FILTER = `grep --line-buffered -v ${shellQuote(MESSAGE_UPDATE_MARK)}`;
|
|
31085
|
+
IDENTITY_WORD = /^[A-Za-z0-9-]{1,64}$/;
|
|
30772
31086
|
STDERR_MARK = "\n--- stderr ---\n";
|
|
30773
31087
|
OP_TIMEOUT_MS = 6e4;
|
|
30774
31088
|
ExecPiContainer = class {
|
|
@@ -30782,6 +31096,12 @@ var init_container = __esm({
|
|
|
30782
31096
|
async makeRoot(runId3) {
|
|
30783
31097
|
return piRunPaths(runId3);
|
|
30784
31098
|
}
|
|
31099
|
+
/** The start script never changes directory, so pi runs where the executor
|
|
31100
|
+
* runs every command: the thread's checkout, which the harness names (a
|
|
31101
|
+
* resident's `/exec` runs in the thread's worktree). No command runs here. */
|
|
31102
|
+
cwd(_paths, checkout) {
|
|
31103
|
+
return checkout;
|
|
31104
|
+
}
|
|
30785
31105
|
async writeFile(path, content) {
|
|
30786
31106
|
for (const script of writeFileScripts(path, content)) stdoutOf("write", await this.exec(script));
|
|
30787
31107
|
}
|
|
@@ -30807,6 +31127,16 @@ var init_container = __esm({
|
|
|
30807
31127
|
async alive(pid) {
|
|
30808
31128
|
return stdoutOf("alive", await this.exec(aliveScript(pid))).trim() === "alive";
|
|
30809
31129
|
}
|
|
31130
|
+
/** One word or nothing: an empty answer, a malformed one or a command the
|
|
31131
|
+
* executor could not run is no identity: a judgement never rests on a guess. */
|
|
31132
|
+
async identity() {
|
|
31133
|
+
try {
|
|
31134
|
+
const word = stdoutOf("identity", await this.exec(identityScript())).trim();
|
|
31135
|
+
return IDENTITY_WORD.test(word) ? word : void 0;
|
|
31136
|
+
} catch {
|
|
31137
|
+
return void 0;
|
|
31138
|
+
}
|
|
31139
|
+
}
|
|
30810
31140
|
async kill(pid) {
|
|
30811
31141
|
stdoutOf("kill", await this.exec(killScript(pid)));
|
|
30812
31142
|
}
|
|
@@ -30938,6 +31268,14 @@ var init_botHostContainer = __esm({
|
|
|
30938
31268
|
throw new PiContainerError("root", message2(err2));
|
|
30939
31269
|
}
|
|
30940
31270
|
}
|
|
31271
|
+
/** The run's own root: `start` spawns pi in it, so that is the directory a
|
|
31272
|
+
* session written for this root names, present on this host in this
|
|
31273
|
+
* generation. The checkout the harness names is a container path this host
|
|
31274
|
+
* does not have (a `none` run has no workspace), and the previous
|
|
31275
|
+
* generation's root is gone with its pi: neither is where pi runs. */
|
|
31276
|
+
cwd(paths, _checkout) {
|
|
31277
|
+
return paths.dir;
|
|
31278
|
+
}
|
|
30941
31279
|
/** Parents at 700, the file at 600, exact bytes, created and never
|
|
30942
31280
|
* replaced: under a root this container made nothing is there before the
|
|
30943
31281
|
* harness writes it, so a path already present is a named failure, never
|
|
@@ -31058,6 +31396,12 @@ var init_botHostContainer = __esm({
|
|
|
31058
31396
|
const child = this.byPid.get(pid);
|
|
31059
31397
|
return child !== void 0 && !child.exited;
|
|
31060
31398
|
}
|
|
31399
|
+
/** The bot host names no container: its pi is a child of one bot generation
|
|
31400
|
+
* and never outlives it, so a resume judges it by its pid alone (`alive`
|
|
31401
|
+
* answers no for a pid this container did not start). */
|
|
31402
|
+
async identity() {
|
|
31403
|
+
return void 0;
|
|
31404
|
+
}
|
|
31061
31405
|
/** The group first (pi leads it), then pi itself: TERM, the grace, KILL;
|
|
31062
31406
|
* a pid this container never started, or one already ended, is nothing to end. */
|
|
31063
31407
|
async kill(pid) {
|
|
@@ -31383,6 +31727,11 @@ var init_bridge2 = __esm({
|
|
|
31383
31727
|
openSpan(callId) {
|
|
31384
31728
|
return this.openTools.get(callId)?.span;
|
|
31385
31729
|
}
|
|
31730
|
+
/** Whether the bridge has read the call's start off the log and not yet its
|
|
31731
|
+
* end: what a relayed request that arrived ahead of the poll waits for. */
|
|
31732
|
+
callOpen(callId) {
|
|
31733
|
+
return this.openTools.has(callId);
|
|
31734
|
+
}
|
|
31386
31735
|
/** End whatever tool spans a stopped pi left open, so no span outlives the run. */
|
|
31387
31736
|
closeOpenSpans(reason) {
|
|
31388
31737
|
for (const [callId, open2] of this.openTools) {
|
|
@@ -31704,7 +32053,8 @@ function piHarnessFactsOf(value) {
|
|
|
31704
32053
|
logOffset: v.logOffset,
|
|
31705
32054
|
...typeof v.sessionFile === "string" ? { sessionFile: v.sessionFile } : {},
|
|
31706
32055
|
...typeof v.bearerHash === "string" ? { bearerHash: v.bearerHash } : {},
|
|
31707
|
-
...typeof v.root === "string" ? { root: v.root } : {}
|
|
32056
|
+
...typeof v.root === "string" ? { root: v.root } : {},
|
|
32057
|
+
...typeof v.container === "string" ? { container: v.container } : {}
|
|
31708
32058
|
};
|
|
31709
32059
|
}
|
|
31710
32060
|
function relayedTools(tools) {
|
|
@@ -31773,6 +32123,24 @@ async function runPiHarness(deps, run2) {
|
|
|
31773
32123
|
const deadline = now() + remainingMs;
|
|
31774
32124
|
const warnAt = deadline - Math.min(3 * 6e4, run2.agent.maxMinutes * 15e3);
|
|
31775
32125
|
run2.toolContext.remainingMs = () => deadline - now();
|
|
32126
|
+
if (run2.conversation) {
|
|
32127
|
+
const readConversation = run2.conversation;
|
|
32128
|
+
run2.toolContext.conversation = async () => {
|
|
32129
|
+
try {
|
|
32130
|
+
return await readConversation();
|
|
32131
|
+
} catch (err2) {
|
|
32132
|
+
emit({
|
|
32133
|
+
type: "run_note",
|
|
32134
|
+
kind: "seed",
|
|
32135
|
+
summary: redactAndCap(
|
|
32136
|
+
`the conversation could not be read from the session log for a child's seed (${err2 instanceof Error ? err2.message : String(err2)}); a child spawned now starts from its own thread`,
|
|
32137
|
+
300
|
|
32138
|
+
)
|
|
32139
|
+
});
|
|
32140
|
+
return void 0;
|
|
32141
|
+
}
|
|
32142
|
+
};
|
|
32143
|
+
}
|
|
31776
32144
|
bridge.turns = run2.resume?.turn ?? 0;
|
|
31777
32145
|
const mirror = new PiMirror({
|
|
31778
32146
|
onStep: run2.onStep,
|
|
@@ -31802,7 +32170,11 @@ async function runPiHarness(deps, run2) {
|
|
|
31802
32170
|
emit,
|
|
31803
32171
|
toolSpan: (callId) => bridge.openSpan(callId),
|
|
31804
32172
|
gateSaw: (callId) => bridge.gateSaw(callId),
|
|
31805
|
-
toolsBlocked
|
|
32173
|
+
toolsBlocked,
|
|
32174
|
+
callSeen: async (callId) => {
|
|
32175
|
+
for (let waited = 0; !bridge.callOpen(callId) && waited < CALL_SEEN_WAIT_MS; waited += CALL_SEEN_TICK_MS)
|
|
32176
|
+
await deps.sleep(CALL_SEEN_TICK_MS);
|
|
32177
|
+
}
|
|
31806
32178
|
};
|
|
31807
32179
|
const forget = deps.registry.register(live);
|
|
31808
32180
|
let pid;
|
|
@@ -31819,21 +32191,27 @@ async function runPiHarness(deps, run2) {
|
|
|
31819
32191
|
const recorded = run2.resume?.facts;
|
|
31820
32192
|
let reattached = false;
|
|
31821
32193
|
let ended;
|
|
32194
|
+
let elsewhere;
|
|
32195
|
+
const here = await container.identity();
|
|
31822
32196
|
const honoured = (hash) => hash !== void 0 && (deps.bearers === void 0 || deps.bearers.adopt(run2.runId, hash));
|
|
31823
32197
|
if (recorded !== void 0) {
|
|
31824
|
-
|
|
31825
|
-
|
|
31826
|
-
|
|
31827
|
-
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31831
|
-
})
|
|
32198
|
+
if (recorded.container !== void 0 && here !== void 0 && recorded.container !== here) {
|
|
32199
|
+
elsewhere = `pi is elsewhere: the row's pi (pid ${recorded.pid}) ran in container ${recorded.container}, not the one this run was handed (${here}), so it was neither probed nor ended here`;
|
|
32200
|
+
} else {
|
|
32201
|
+
const alive = await container.alive(recorded.pid);
|
|
32202
|
+
if (alive && recorded.root !== void 0 && honoured(recorded.bearerHash)) {
|
|
32203
|
+
reattached = true;
|
|
32204
|
+
paths = piRunPathsAt(recorded.root);
|
|
32205
|
+
} else if (alive) {
|
|
32206
|
+
ended = recorded.root === void 0 ? "named no directory for its pi" : "carried no bearer this generation could honour for its pi";
|
|
32207
|
+
await container.kill(recorded.pid).catch(() => {
|
|
32208
|
+
});
|
|
32209
|
+
}
|
|
31832
32210
|
}
|
|
31833
32211
|
}
|
|
31834
32212
|
if (reattached && recorded && paths !== void 0) {
|
|
31835
32213
|
pid = recorded.pid;
|
|
31836
|
-
facts2 = { ...recorded };
|
|
32214
|
+
facts2 = { ...recorded, ...recorded.container === void 0 && here !== void 0 ? { container: here } : {} };
|
|
31837
32215
|
transport = new PiRpcTransport({
|
|
31838
32216
|
container,
|
|
31839
32217
|
paths,
|
|
@@ -31849,7 +32227,7 @@ async function runPiHarness(deps, run2) {
|
|
|
31849
32227
|
);
|
|
31850
32228
|
} else {
|
|
31851
32229
|
paths = await container.makeRoot(run2.runId);
|
|
31852
|
-
if (recorded?.root !== void 0 && recorded.root !== paths.dir)
|
|
32230
|
+
if (recorded?.root !== void 0 && recorded.root !== paths.dir && elsewhere === void 0)
|
|
31853
32231
|
await container.remove(piRunPathsAt(recorded.root)).catch(() => {
|
|
31854
32232
|
});
|
|
31855
32233
|
const spec = {
|
|
@@ -31882,7 +32260,7 @@ async function runPiHarness(deps, run2) {
|
|
|
31882
32260
|
piSessionFile(
|
|
31883
32261
|
session.messages,
|
|
31884
32262
|
{
|
|
31885
|
-
cwd: run2.rules.checkout,
|
|
32263
|
+
cwd: container.cwd(paths, run2.rules.checkout),
|
|
31886
32264
|
model: {
|
|
31887
32265
|
provider: run2.model.provider,
|
|
31888
32266
|
id: run2.model.id,
|
|
@@ -31896,7 +32274,7 @@ async function runPiHarness(deps, run2) {
|
|
|
31896
32274
|
}
|
|
31897
32275
|
if (run2.resume) {
|
|
31898
32276
|
const lost = run2.resume.settlements.length;
|
|
31899
|
-
const how = ended !== void 0 && recorded !== void 0 ? `the row ${ended} (pid ${recorded.pid}), so it was ended and pi restarted` : "pi restarted";
|
|
32277
|
+
const how = elsewhere !== void 0 ? `${elsewhere}, and pi restarted` : ended !== void 0 && recorded !== void 0 ? `the row ${ended} (pid ${recorded.pid}), so it was ended and pi restarted` : "pi restarted";
|
|
31900
32278
|
note(
|
|
31901
32279
|
"resumed",
|
|
31902
32280
|
`resumed after a restart: ${how} on the mirrored transcript \u2014 ${lost} call(s) were in flight, each answered with a restart note; ${Math.round(remainingMs / 6e4)} min of budget left`
|
|
@@ -31906,7 +32284,13 @@ async function runPiHarness(deps, run2) {
|
|
|
31906
32284
|
for (const file of piLaunchFiles(launch)) await container.writeFile(file.path, file.content);
|
|
31907
32285
|
({ pid } = await container.start({ paths, args: piLaunchArgs(launch), env: piLaunchEnv(launch, deps.bearer) }));
|
|
31908
32286
|
const bearerHash = bearerHashOf(deps.bearer);
|
|
31909
|
-
facts2 = {
|
|
32287
|
+
facts2 = {
|
|
32288
|
+
pid,
|
|
32289
|
+
logOffset: 0,
|
|
32290
|
+
root: paths.dir,
|
|
32291
|
+
...bearerHash !== void 0 ? { bearerHash } : {},
|
|
32292
|
+
...here !== void 0 ? { container: here } : {}
|
|
32293
|
+
};
|
|
31910
32294
|
save();
|
|
31911
32295
|
transport = new PiRpcTransport({ container, paths, pid, pollMs: deps.pollMs ?? 750, sleep: deps.sleep });
|
|
31912
32296
|
}
|
|
@@ -32101,7 +32485,7 @@ ${stagedLine}` : prompt,
|
|
|
32101
32485
|
agentSpan?.end(hardStopped || bypass ? "error" : "ok");
|
|
32102
32486
|
}
|
|
32103
32487
|
}
|
|
32104
|
-
var NATIVE_WORKSPACE_TOOLS, FINALE_TIMEOUT_MS2, CONTINUE_PROMPT, PromptRefused, GateBypassed;
|
|
32488
|
+
var NATIVE_WORKSPACE_TOOLS, FINALE_TIMEOUT_MS2, CALL_SEEN_WAIT_MS, CALL_SEEN_TICK_MS, CONTINUE_PROMPT, PromptRefused, GateBypassed;
|
|
32105
32489
|
var init_harness = __esm({
|
|
32106
32490
|
"../../src/core/harness/pi/harness.ts"() {
|
|
32107
32491
|
"use strict";
|
|
@@ -32116,6 +32500,8 @@ var init_harness = __esm({
|
|
|
32116
32500
|
init_transport();
|
|
32117
32501
|
NATIVE_WORKSPACE_TOOLS = /* @__PURE__ */ new Set(["bash", "read_file", "write_file"]);
|
|
32118
32502
|
FINALE_TIMEOUT_MS2 = 3 * 6e4;
|
|
32503
|
+
CALL_SEEN_WAIT_MS = 3e3;
|
|
32504
|
+
CALL_SEEN_TICK_MS = 50;
|
|
32119
32505
|
CONTINUE_PROMPT = "Continue where you left off: the bot restarted mid-run, so re-check the effects of your last command before relying on them.";
|
|
32120
32506
|
PromptRefused = class extends Error {
|
|
32121
32507
|
constructor(reason) {
|
|
@@ -32970,6 +33356,7 @@ async function runLoop2(deps, ctx) {
|
|
|
32970
33356
|
executor,
|
|
32971
33357
|
resident: round2.selection.resident !== void 0
|
|
32972
33358
|
});
|
|
33359
|
+
ctx.workspaceFiles?.record(staged.outcomes);
|
|
32973
33360
|
return staged.line;
|
|
32974
33361
|
} : void 0;
|
|
32975
33362
|
let artifactSeq = 0;
|
|
@@ -33018,10 +33405,19 @@ async function runLoop2(deps, ctx) {
|
|
|
33018
33405
|
});
|
|
33019
33406
|
if (harness === "pi" && piFacts) {
|
|
33020
33407
|
const container = deps.harness?.containerFor?.(executor, profile.machine) ?? piContainerFor(executor, profile.machine);
|
|
33021
|
-
await container.
|
|
33022
|
-
|
|
33023
|
-
|
|
33024
|
-
|
|
33408
|
+
const here = await container.identity();
|
|
33409
|
+
if (piFacts.container === void 0 || here === void 0 || piFacts.container === here) {
|
|
33410
|
+
await container.kill(piFacts.pid).catch(() => {
|
|
33411
|
+
});
|
|
33412
|
+
if (piFacts.root !== void 0) await container.remove(piRunPathsAt(piFacts.root)).catch(() => {
|
|
33413
|
+
});
|
|
33414
|
+
} else {
|
|
33415
|
+
onEvent({
|
|
33416
|
+
type: "run_note",
|
|
33417
|
+
kind: "resumed",
|
|
33418
|
+
summary: `the run's pi (pid ${piFacts.pid}) ran in container ${piFacts.container}, not the one this run was handed (${here}): it was not ended here`
|
|
33419
|
+
});
|
|
33420
|
+
}
|
|
33025
33421
|
}
|
|
33026
33422
|
answer = answerUnderEnding(finish2.answer, loopEnding, agent.maxMinutes);
|
|
33027
33423
|
} else if (harness === "pi") {
|
|
@@ -33066,7 +33462,7 @@ async function runLoop2(deps, ctx) {
|
|
|
33066
33462
|
messages,
|
|
33067
33463
|
tools: relayedTools(mergeTools(TOOLSETS[agent.toolset] ?? [], mcpForRun?.tools)),
|
|
33068
33464
|
toolContext,
|
|
33069
|
-
...session ? { notepad: () => session.readNotepad() } : {},
|
|
33465
|
+
...session ? { notepad: () => session.readNotepad(), conversation: () => session.readConversation() } : {},
|
|
33070
33466
|
rules: {
|
|
33071
33467
|
checkout: binding?.workspace ?? "/workspace",
|
|
33072
33468
|
...ownBranch !== void 0 ? { branch: ownBranch } : {},
|
|
@@ -33884,7 +34280,7 @@ async function plan(deps, input) {
|
|
|
33884
34280
|
try {
|
|
33885
34281
|
planId = planIdOf(request.planPath);
|
|
33886
34282
|
} catch (err2) {
|
|
33887
|
-
return { ok: false, reply: `\u{1F6AB} ${
|
|
34283
|
+
return { ok: false, reply: `\u{1F6AB} ${describe6(err2)}` };
|
|
33888
34284
|
}
|
|
33889
34285
|
let text;
|
|
33890
34286
|
try {
|
|
@@ -33892,7 +34288,7 @@ async function plan(deps, input) {
|
|
|
33892
34288
|
} catch (err2) {
|
|
33893
34289
|
return {
|
|
33894
34290
|
ok: false,
|
|
33895
|
-
reply: `\u{1F6AB} The plan \`${request.planPath}\` could not be read at \`${base}\` in ${entry.repo}: ${
|
|
34291
|
+
reply: `\u{1F6AB} The plan \`${request.planPath}\` could not be read at \`${base}\` in ${entry.repo}: ${describe6(err2)}`
|
|
33896
34292
|
};
|
|
33897
34293
|
}
|
|
33898
34294
|
const graph = parsePlanGraph(text, planId);
|
|
@@ -33905,7 +34301,7 @@ async function plan(deps, input) {
|
|
|
33905
34301
|
try {
|
|
33906
34302
|
selected = openPlanCursor(graph, request.units).order;
|
|
33907
34303
|
} catch (err2) {
|
|
33908
|
-
return { ok: false, reply: `\u{1F6AB} ${
|
|
34304
|
+
return { ok: false, reply: `\u{1F6AB} ${describe6(err2)}` };
|
|
33909
34305
|
}
|
|
33910
34306
|
return { ok: true, planned: { kind: "plan", planId, path: request.planPath, base, graph, selected, identity: identity2 } };
|
|
33911
34307
|
}
|
|
@@ -34021,7 +34417,7 @@ async function start(deps, input, instance, units, where, write) {
|
|
|
34021
34417
|
try {
|
|
34022
34418
|
answer = await deps.create(instance.id);
|
|
34023
34419
|
} catch (err2) {
|
|
34024
|
-
answer = { kind: "unanswered", reason:
|
|
34420
|
+
answer = { kind: "unanswered", reason: describe6(err2) };
|
|
34025
34421
|
}
|
|
34026
34422
|
switch (answer.kind) {
|
|
34027
34423
|
case "created": {
|
|
@@ -34043,7 +34439,7 @@ async function start(deps, input, instance, units, where, write) {
|
|
|
34043
34439
|
);
|
|
34044
34440
|
}
|
|
34045
34441
|
}
|
|
34046
|
-
var refused3,
|
|
34442
|
+
var refused3, describe6, RUNNING, ENDED;
|
|
34047
34443
|
var init_handOff = __esm({
|
|
34048
34444
|
"../../src/core/coordinator/handOff.ts"() {
|
|
34049
34445
|
"use strict";
|
|
@@ -34051,7 +34447,7 @@ var init_handOff = __esm({
|
|
|
34051
34447
|
init_coordinator();
|
|
34052
34448
|
init_briefs();
|
|
34053
34449
|
refused3 = (reply) => ({ status: "aborted", reply });
|
|
34054
|
-
|
|
34450
|
+
describe6 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
34055
34451
|
RUNNING = /* @__PURE__ */ new Set(["queued", "running", "paused", "waiting", "waitingForPause"]);
|
|
34056
34452
|
ENDED = /* @__PURE__ */ new Set(["complete", "errored", "terminated"]);
|
|
34057
34453
|
}
|
|
@@ -34744,6 +35140,77 @@ var init_settle = __esm({
|
|
|
34744
35140
|
}
|
|
34745
35141
|
});
|
|
34746
35142
|
|
|
35143
|
+
// ../../src/core/dispatch/reattach.ts
|
|
35144
|
+
function carriedWorkspaceBinding(row) {
|
|
35145
|
+
const recorded = workspaceBindingOf(row.state.binding);
|
|
35146
|
+
if (recorded) return recorded;
|
|
35147
|
+
if (row.meta.selection === "resident")
|
|
35148
|
+
return { backend: "resident", ...row.meta.workspace ? { workspace: row.meta.workspace } : {} };
|
|
35149
|
+
if (row.meta.selection === "sandbox" || row.meta.selection === "local") return { backend: row.meta.selection };
|
|
35150
|
+
return void 0;
|
|
35151
|
+
}
|
|
35152
|
+
function lostWorkspaceNote(why, restarts) {
|
|
35153
|
+
const lost = `resumed after a restart: the run's workspace could not be re-attached (${why})`;
|
|
35154
|
+
return restarts ? `${lost}; the run restarts from its request as a new run in this thread` : `${lost}, and the row's request cannot be read, so the run ends here; re-send it to run it again`;
|
|
35155
|
+
}
|
|
35156
|
+
async function abandonLostWorkspace(ctx) {
|
|
35157
|
+
const { refuse: refuse3, card, shell, closeLines, clock: clock3, run: run2, registry, resume, ledgerRun, why } = ctx;
|
|
35158
|
+
const restored = messageFromInbox(resume.row.meta.request ?? {}, resume.row.startedAt);
|
|
35159
|
+
const summary = lostWorkspaceNote(why, restored !== void 0);
|
|
35160
|
+
const note = { type: "run_note", kind: "resumed", summary, at: clock3() };
|
|
35161
|
+
await refuse3("workspace_lost", async () => {
|
|
35162
|
+
registry.publish(run2.id, note);
|
|
35163
|
+
await card.done(
|
|
35164
|
+
shell.close({
|
|
35165
|
+
kind: "not_started",
|
|
35166
|
+
icon: "\u{1F501}",
|
|
35167
|
+
reason: restored ? "workspace lost across the restart; restarting from the request" : "workspace lost across the restart; re-send to run again",
|
|
35168
|
+
...closeLines(clock3(), false)
|
|
35169
|
+
})
|
|
35170
|
+
);
|
|
35171
|
+
if (ledgerRun)
|
|
35172
|
+
await closeResumedRow(
|
|
35173
|
+
ledgerRun,
|
|
35174
|
+
{ ...resume, events: [...resume.events, { ...note, seq: resume.lastSeq + 1 }] },
|
|
35175
|
+
"the run's workspace could not be re-attached"
|
|
35176
|
+
);
|
|
35177
|
+
});
|
|
35178
|
+
console.log(
|
|
35179
|
+
`[resume] ${resume.row.threadKey} run ${resume.row.runId} ${restored ? "restarts from its request" : "ends"}: ${why}`
|
|
35180
|
+
);
|
|
35181
|
+
return restored?.msg;
|
|
35182
|
+
}
|
|
35183
|
+
function prepareRestartTurn(deps, ctx) {
|
|
35184
|
+
const { request, pending: pending2, clock: clock3 } = ctx;
|
|
35185
|
+
const merged = mergeFollowUps(pending2);
|
|
35186
|
+
const receivedAt = clock3();
|
|
35187
|
+
const msg = {
|
|
35188
|
+
...request,
|
|
35189
|
+
...merged ? {
|
|
35190
|
+
text: `${request.text}
|
|
35191
|
+
|
|
35192
|
+
${merged.text}`,
|
|
35193
|
+
...merged.images || request.images ? { images: [...request.images ?? [], ...merged.images ?? []] } : {},
|
|
35194
|
+
...merged.documents || request.documents ? { documents: [...request.documents ?? [], ...merged.documents ?? []] } : {},
|
|
35195
|
+
...merged.staged || request.staged ? { staged: [...request.staged ?? [], ...merged.staged ?? []] } : {}
|
|
35196
|
+
} : {},
|
|
35197
|
+
receivedAt,
|
|
35198
|
+
originAt: void 0
|
|
35199
|
+
};
|
|
35200
|
+
const trace = startRequestRoot(deps, { channel: channelOf(msg.channelId), receivedAt });
|
|
35201
|
+
return { msg, opts: { trace } };
|
|
35202
|
+
}
|
|
35203
|
+
var init_reattach = __esm({
|
|
35204
|
+
"../../src/core/dispatch/reattach.ts"() {
|
|
35205
|
+
"use strict";
|
|
35206
|
+
init_factory();
|
|
35207
|
+
init_inboxMessage();
|
|
35208
|
+
init_requestTrace();
|
|
35209
|
+
init_threadAdmission();
|
|
35210
|
+
init_admission();
|
|
35211
|
+
}
|
|
35212
|
+
});
|
|
35213
|
+
|
|
34747
35214
|
// ../../src/core/dispatch/lineage.ts
|
|
34748
35215
|
function lineageOf(newest) {
|
|
34749
35216
|
if (newest === void 0 || newest.parentRunId === void 0) return void 0;
|
|
@@ -34803,31 +35270,6 @@ var init_lineage = __esm({
|
|
|
34803
35270
|
});
|
|
34804
35271
|
|
|
34805
35272
|
// ../../src/core/dispatch/thread.ts
|
|
34806
|
-
async function readThreadArtifacts(service, thread, warn = (line3) => console.warn(line3)) {
|
|
34807
|
-
const perRun = [];
|
|
34808
|
-
for (const run2 of [...thread].reverse()) {
|
|
34809
|
-
const events = [];
|
|
34810
|
-
let afterSeq;
|
|
34811
|
-
try {
|
|
34812
|
-
for (; ; ) {
|
|
34813
|
-
const r = await service.getRunEvents(run2.id, afterSeq === void 0 ? {} : { afterSeq });
|
|
34814
|
-
if (!r.ok) {
|
|
34815
|
-
warn(`[thread] ${run2.id}: reading its received files stopped after ${events.length} event(s) \u2014 ${r.error}`);
|
|
34816
|
-
break;
|
|
34817
|
-
}
|
|
34818
|
-
events.push(...r.value.events);
|
|
34819
|
-
if (r.value.nextAfterSeq === void 0) break;
|
|
34820
|
-
afterSeq = r.value.nextAfterSeq;
|
|
34821
|
-
}
|
|
34822
|
-
} catch (err2) {
|
|
34823
|
-
warn(
|
|
34824
|
-
`[thread] ${run2.id}: reading its received files failed after ${events.length} event(s) \u2014 ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
34825
|
-
);
|
|
34826
|
-
}
|
|
34827
|
-
perRun.push(events);
|
|
34828
|
-
}
|
|
34829
|
-
return threadInboundArtifacts(perRun);
|
|
34830
|
-
}
|
|
34831
35273
|
async function readThread(service, threadKey) {
|
|
34832
35274
|
try {
|
|
34833
35275
|
const page2 = await service.listRuns({
|
|
@@ -34869,7 +35311,6 @@ var THREAD_READ_LIMIT;
|
|
|
34869
35311
|
var init_thread = __esm({
|
|
34870
35312
|
"../../src/core/dispatch/thread.ts"() {
|
|
34871
35313
|
"use strict";
|
|
34872
|
-
init_staging();
|
|
34873
35314
|
THREAD_READ_LIMIT = 8;
|
|
34874
35315
|
}
|
|
34875
35316
|
});
|
|
@@ -34989,22 +35430,22 @@ function createRunEnding(deps) {
|
|
|
34989
35430
|
try {
|
|
34990
35431
|
deps.registry.seal(id, replyOk === void 0 ? {} : { replyOk });
|
|
34991
35432
|
} catch (err2) {
|
|
34992
|
-
log(`[ending] seal ${id}: ${
|
|
35433
|
+
log(`[ending] seal ${id}: ${describe7(err2)}`);
|
|
34993
35434
|
}
|
|
34994
35435
|
try {
|
|
34995
35436
|
afterSeal?.();
|
|
34996
35437
|
} catch (err2) {
|
|
34997
|
-
log(`[ending] after seal ${id}: ${
|
|
35438
|
+
log(`[ending] after seal ${id}: ${describe7(err2)}`);
|
|
34998
35439
|
}
|
|
34999
35440
|
}
|
|
35000
35441
|
for (const entry of pending2.splice(0)) {
|
|
35001
35442
|
try {
|
|
35002
35443
|
const result = deps.registry.seal(entry.runId);
|
|
35003
35444
|
void Promise.resolve(entry.write(result, entry.failedAfterFinish)).catch(
|
|
35004
|
-
(err2) => log(`[ending] record ${entry.runId}: ${
|
|
35445
|
+
(err2) => log(`[ending] record ${entry.runId}: ${describe7(err2)}`)
|
|
35005
35446
|
);
|
|
35006
35447
|
} catch (err2) {
|
|
35007
|
-
log(`[ending] record ${entry.runId}: ${
|
|
35448
|
+
log(`[ending] record ${entry.runId}: ${describe7(err2)}`);
|
|
35008
35449
|
}
|
|
35009
35450
|
}
|
|
35010
35451
|
};
|
|
@@ -35014,7 +35455,7 @@ function createRunEnding(deps) {
|
|
|
35014
35455
|
try {
|
|
35015
35456
|
deps.onFinished?.(runId3);
|
|
35016
35457
|
} catch (err2) {
|
|
35017
|
-
log(`[ending] finished ${runId3}: ${
|
|
35458
|
+
log(`[ending] finished ${runId3}: ${describe7(err2)}`);
|
|
35018
35459
|
}
|
|
35019
35460
|
},
|
|
35020
35461
|
register(entry) {
|
|
@@ -35042,7 +35483,7 @@ function createRunEnding(deps) {
|
|
|
35042
35483
|
}
|
|
35043
35484
|
};
|
|
35044
35485
|
}
|
|
35045
|
-
function
|
|
35486
|
+
function describe7(err2) {
|
|
35046
35487
|
return err2 instanceof Error ? err2.message : String(err2);
|
|
35047
35488
|
}
|
|
35048
35489
|
var init_runEnding = __esm({
|
|
@@ -35104,6 +35545,8 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35104
35545
|
let reserved;
|
|
35105
35546
|
let requestRow;
|
|
35106
35547
|
let fencedWhileAttaching = false;
|
|
35548
|
+
let resumeRowClosed = false;
|
|
35549
|
+
let restartRequest;
|
|
35107
35550
|
const reservationHooks = {
|
|
35108
35551
|
onStop: (mode) => void registered?.control.requestStop(mode),
|
|
35109
35552
|
onFenced: () => {
|
|
@@ -35344,18 +35787,21 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35344
35787
|
await tellLineage({ kind: "started", runId: runId3 });
|
|
35345
35788
|
const staged = !resume && deps.artifacts && msg.staged && msg.staged.length > 0 ? msg.staged : [];
|
|
35346
35789
|
const nextStagedIndex = stagingIndex();
|
|
35790
|
+
const workspaceFiles = new WorkspaceFiles();
|
|
35347
35791
|
const stagedCopies = staged.length > 0 && agent.machine !== "none" ? copyStaged(staged, {
|
|
35348
35792
|
store: deps.artifacts,
|
|
35349
35793
|
threadKey: msg.threadKey,
|
|
35350
35794
|
nextIndex: nextStagedIndex,
|
|
35351
35795
|
publish: (e) => registry.publish(runId3, e)
|
|
35352
35796
|
}) : void 0;
|
|
35353
|
-
const
|
|
35354
|
-
(
|
|
35355
|
-
|
|
35356
|
-
|
|
35357
|
-
|
|
35358
|
-
|
|
35797
|
+
const threadAssets = !resume && deps.artifacts && thread && thread.length > 0 ? readThreadAssets({ runs: runsService, store: deps.artifacts }, msg.threadKey).then(
|
|
35798
|
+
(assets) => assets.filter((a) => a.runId !== runId3)
|
|
35799
|
+
) : void 0;
|
|
35800
|
+
const earlierStaged = threadAssets && agent.machine !== "none" ? threadAssets.then(
|
|
35801
|
+
(assets) => stageThreadArtifacts(
|
|
35802
|
+
assets.filter((a) => a.direction === "in"),
|
|
35803
|
+
{ nextIndex: nextStagedIndex, publish: (e) => registry.publish(runId3, e) }
|
|
35804
|
+
)
|
|
35359
35805
|
) : void 0;
|
|
35360
35806
|
const reservation = await reserveRun(deps, {
|
|
35361
35807
|
msg,
|
|
@@ -35382,6 +35828,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35382
35828
|
reserved = reservation.reserved;
|
|
35383
35829
|
requestRow = reservation.requestRow;
|
|
35384
35830
|
}
|
|
35831
|
+
const reattach = resume ? carriedWorkspaceBinding(resume.row) : void 0;
|
|
35385
35832
|
const attach = await attachWorkspace(deps, {
|
|
35386
35833
|
msg,
|
|
35387
35834
|
io,
|
|
@@ -35393,10 +35840,35 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35393
35840
|
agent,
|
|
35394
35841
|
profile,
|
|
35395
35842
|
repoCtx,
|
|
35396
|
-
root
|
|
35843
|
+
root,
|
|
35844
|
+
...reattach !== void 0 ? { reattach } : {}
|
|
35397
35845
|
});
|
|
35398
35846
|
if (attach.kind === "refused") return ended;
|
|
35847
|
+
if (attach.kind === "reattach_refused") {
|
|
35848
|
+
if (resume) {
|
|
35849
|
+
restartRequest = await abandonLostWorkspace({
|
|
35850
|
+
msg,
|
|
35851
|
+
io,
|
|
35852
|
+
refuse: refuse3,
|
|
35853
|
+
card,
|
|
35854
|
+
shell,
|
|
35855
|
+
closeLines,
|
|
35856
|
+
clock: clock3,
|
|
35857
|
+
run: run2,
|
|
35858
|
+
registry,
|
|
35859
|
+
resume,
|
|
35860
|
+
ledgerRun,
|
|
35861
|
+
why: attach.why
|
|
35862
|
+
});
|
|
35863
|
+
resumeRowClosed = true;
|
|
35864
|
+
}
|
|
35865
|
+
return ended;
|
|
35866
|
+
}
|
|
35399
35867
|
const { round: round2 } = attach;
|
|
35868
|
+
if (resume && ledgerRun) {
|
|
35869
|
+
const rebound = workspaceBindingFor(round2.selection, profile.machine);
|
|
35870
|
+
if (rebound !== void 0) ledgerRun.setState({ binding: rebound });
|
|
35871
|
+
}
|
|
35400
35872
|
const { executor, note, resident } = round2.selection;
|
|
35401
35873
|
if (fencedWhileAttaching) {
|
|
35402
35874
|
console.log(
|
|
@@ -35408,13 +35880,16 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35408
35880
|
}
|
|
35409
35881
|
const earlier2 = earlierStaged ? await earlierStaged : [];
|
|
35410
35882
|
if (staged.length > 0 || earlier2.length > 0) {
|
|
35411
|
-
|
|
35412
|
-
|
|
35883
|
+
let line3;
|
|
35884
|
+
if (stagedCopies || earlier2.length > 0) {
|
|
35885
|
+
const pulled = await pullStaged([...stagedCopies ? await stagedCopies : [], ...earlier2], {
|
|
35413
35886
|
store: deps.artifacts,
|
|
35414
35887
|
executor,
|
|
35415
35888
|
resident: resident !== void 0
|
|
35416
|
-
})
|
|
35417
|
-
|
|
35889
|
+
});
|
|
35890
|
+
workspaceFiles.record(pulled);
|
|
35891
|
+
line3 = attachmentsLine(pulled);
|
|
35892
|
+
} else line3 = noWorkspaceLine(staged);
|
|
35418
35893
|
const request = messages[messages.length - 1];
|
|
35419
35894
|
if (request && request.role === "user" && line3) {
|
|
35420
35895
|
request.content = Array.isArray(request.content) ? [...request.content, { type: "text", text: line3 }] : [
|
|
@@ -35444,6 +35919,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35444
35919
|
if (headGate.headAdopted) publishMeta(repoCtx);
|
|
35445
35920
|
const isPrReview = agent.name === "review" && repoCtx.repo !== void 0 && repoCtx.pr !== void 0;
|
|
35446
35921
|
const isCodingPrRun = agent.toolset === "full";
|
|
35922
|
+
const threadFiles = threadAssets ? (await threadAssets).map((a) => describeAsset(a, workspaceFiles.pathOf(a.key))) : [];
|
|
35447
35923
|
const prompt = await composePrompt(deps, {
|
|
35448
35924
|
msg,
|
|
35449
35925
|
agent,
|
|
@@ -35460,11 +35936,13 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35460
35936
|
root,
|
|
35461
35937
|
...contractBlock !== void 0 && agent.name === "review" ? { contract: contractBlock } : {},
|
|
35462
35938
|
// What the session already knows (session-log item 10): the notepad and
|
|
35463
|
-
// the newest compaction's summary the seed brought back,
|
|
35464
|
-
|
|
35939
|
+
// the newest compaction's summary the seed brought back, and the thread's
|
|
35940
|
+
// files (record 0033) with where each is for this run, for the prompt.
|
|
35941
|
+
...session?.notepad !== void 0 || session?.summary !== void 0 || threadFiles.length > 0 ? {
|
|
35465
35942
|
session: {
|
|
35466
|
-
...session
|
|
35467
|
-
...session
|
|
35943
|
+
...session?.notepad !== void 0 ? { notepad: session.notepad } : {},
|
|
35944
|
+
...session?.summary !== void 0 ? { summary: session.summary } : {},
|
|
35945
|
+
...threadFiles.length > 0 ? { files: threadFiles } : {}
|
|
35468
35946
|
}
|
|
35469
35947
|
} : {}
|
|
35470
35948
|
});
|
|
@@ -35524,7 +36002,14 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35524
36002
|
seed,
|
|
35525
36003
|
...session ? { seedLog: session.log } : {}
|
|
35526
36004
|
});
|
|
35527
|
-
const sessionTools = sessionCapabilityFor(
|
|
36005
|
+
const sessionTools = sessionCapabilityFor(
|
|
36006
|
+
ledgerRun,
|
|
36007
|
+
deps.runLedger,
|
|
36008
|
+
deps.artifacts ? {
|
|
36009
|
+
read: () => readThreadAssets({ runs: runsService, store: deps.artifacts }, msg.threadKey),
|
|
36010
|
+
pathOf: (key) => workspaceFiles.pathOf(key)
|
|
36011
|
+
} : void 0
|
|
36012
|
+
);
|
|
35528
36013
|
const { spawn: spawn7, runs, steer, wait } = runToolCapabilities(
|
|
35529
36014
|
{ core: deps, dispatch, registry, clock: clock3 },
|
|
35530
36015
|
{
|
|
@@ -35544,6 +36029,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35544
36029
|
profile,
|
|
35545
36030
|
resolved,
|
|
35546
36031
|
stagingIndex: nextStagedIndex,
|
|
36032
|
+
workspaceFiles,
|
|
35547
36033
|
provider,
|
|
35548
36034
|
model,
|
|
35549
36035
|
messages,
|
|
@@ -35646,7 +36132,7 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35646
36132
|
clearInterval(setupHeartbeat);
|
|
35647
36133
|
ending.drain(void 0);
|
|
35648
36134
|
if (registered) deps.runBearers?.revoke(registered.id);
|
|
35649
|
-
if (resume && ledgerRun && !runLoopStarted) {
|
|
36135
|
+
if (resume && ledgerRun && !runLoopStarted && !resumeRowClosed) {
|
|
35650
36136
|
const adopted = ledgerRun;
|
|
35651
36137
|
await root.span(
|
|
35652
36138
|
"post.history_write",
|
|
@@ -35661,7 +36147,15 @@ async function dispatch(deps, msg, io, opts = {}) {
|
|
|
35661
36147
|
const stopMode = settled.kind === "handed-on" ? void 0 : settled.stopMode;
|
|
35662
36148
|
ended.status = caught ? "failed" : refused5 ? "refused" : stopMode ? "stopped" : "completed";
|
|
35663
36149
|
root.end(caught ? "error" : "ok", { status: ended.status });
|
|
35664
|
-
if (
|
|
36150
|
+
if (restartRequest) {
|
|
36151
|
+
const pending2 = settled.kind === "handed-on" ? settled.pending : [];
|
|
36152
|
+
const restart2 = prepareRestartTurn(deps, { request: restartRequest, pending: pending2, clock: clock3 });
|
|
36153
|
+
await dispatch(deps, restart2.msg, io, restart2.opts).catch(
|
|
36154
|
+
(err2) => console.error(
|
|
36155
|
+
`[dispatch] ${msg.threadKey} restart after a lost workspace failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
36156
|
+
)
|
|
36157
|
+
);
|
|
36158
|
+
} else if (settled.kind === "handed-on") {
|
|
35665
36159
|
const fresh = prepareFreshTurn(deps, { agent: settled.agent, pending: settled.pending, clock: clock3 });
|
|
35666
36160
|
await dispatch(deps, fresh.msg, fresh.io, fresh.opts).catch(
|
|
35667
36161
|
(err2) => console.error(
|
|
@@ -35704,10 +36198,13 @@ var init_dispatcher = __esm({
|
|
|
35704
36198
|
init_contract();
|
|
35705
36199
|
init_codingChild();
|
|
35706
36200
|
init_settle();
|
|
36201
|
+
init_reattach();
|
|
36202
|
+
init_factory();
|
|
35707
36203
|
init_lineage();
|
|
35708
36204
|
init_seed();
|
|
35709
36205
|
init_session();
|
|
35710
36206
|
init_thread();
|
|
36207
|
+
init_threadAssets();
|
|
35711
36208
|
init_select();
|
|
35712
36209
|
init_spawn();
|
|
35713
36210
|
init_runsService();
|
|
@@ -35718,7 +36215,7 @@ var init_dispatcher = __esm({
|
|
|
35718
36215
|
});
|
|
35719
36216
|
|
|
35720
36217
|
// ../../src/core/runHistoryWriter.ts
|
|
35721
|
-
function
|
|
36218
|
+
function describe8(err2) {
|
|
35722
36219
|
return err2 instanceof Error ? err2.message : String(err2);
|
|
35723
36220
|
}
|
|
35724
36221
|
function createRunHistoryWriter(opts) {
|
|
@@ -35735,7 +36232,7 @@ function createRunHistoryWriter(opts) {
|
|
|
35735
36232
|
try {
|
|
35736
36233
|
opts.onPersisted?.(id);
|
|
35737
36234
|
} catch (err2) {
|
|
35738
|
-
opts.warn(`[run-history] onPersisted hook failed for ${id}: ${
|
|
36235
|
+
opts.warn(`[run-history] onPersisted hook failed for ${id}: ${describe8(err2)}`);
|
|
35739
36236
|
}
|
|
35740
36237
|
};
|
|
35741
36238
|
const attemptAll = async (record2, flag2, sink, span) => {
|
|
@@ -35758,12 +36255,12 @@ function createRunHistoryWriter(opts) {
|
|
|
35758
36255
|
}
|
|
35759
36256
|
if (err2 instanceof PermanentStoreError) {
|
|
35760
36257
|
failures++;
|
|
35761
|
-
opts.warn(`[run-history] ${record2.id} not persisted (permanent, not retried): ${
|
|
36258
|
+
opts.warn(`[run-history] ${record2.id} not persisted (permanent, not retried): ${describe8(err2)}`);
|
|
35762
36259
|
return;
|
|
35763
36260
|
}
|
|
35764
36261
|
if (attempt >= attempts) {
|
|
35765
36262
|
failures++;
|
|
35766
|
-
opts.warn(`[run-history] ${record2.id} not persisted after ${attempts} attempts: ${
|
|
36263
|
+
opts.warn(`[run-history] ${record2.id} not persisted after ${attempts} attempts: ${describe8(err2)}`);
|
|
35767
36264
|
return;
|
|
35768
36265
|
}
|
|
35769
36266
|
await sleep2(jittered(RUN_HISTORY_RETRY_DELAYS_MS[attempt - 1]));
|
|
@@ -35783,7 +36280,7 @@ function createRunHistoryWriter(opts) {
|
|
|
35783
36280
|
}
|
|
35784
36281
|
const p = attemptAll(record2, flag2, writeOpts?.via ?? opts.store, writeOpts?.span).catch((err2) => {
|
|
35785
36282
|
failures++;
|
|
35786
|
-
opts.warn(`[run-history] ${record2.id} writer failed unexpectedly: ${
|
|
36283
|
+
opts.warn(`[run-history] ${record2.id} writer failed unexpectedly: ${describe8(err2)}`);
|
|
35787
36284
|
}).finally(() => {
|
|
35788
36285
|
if (flag2) provisionalFlags.delete(flag2);
|
|
35789
36286
|
void inFlight.delete(p);
|
|
@@ -36200,7 +36697,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36200
36697
|
return { outcome: "untracked" };
|
|
36201
36698
|
}
|
|
36202
36699
|
if (err2 instanceof PermanentStoreError || attempt >= claimAttempts) {
|
|
36203
|
-
warn(`[ledger] ${req.threadKey} not tracked: claim failed after ${attempt} attempt(s): ${
|
|
36700
|
+
warn(`[ledger] ${req.threadKey} not tracked: claim failed after ${attempt} attempt(s): ${describe9(err2)}`);
|
|
36204
36701
|
return { outcome: "untracked" };
|
|
36205
36702
|
}
|
|
36206
36703
|
await sleep2(RETRY_MS[Math.min(attempt, RETRY_MS.length) - 1]);
|
|
@@ -36362,7 +36859,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36362
36859
|
if (!recorded.ok) this.detach(`seed record refused (${recorded.reason})`);
|
|
36363
36860
|
else this.seeded = true;
|
|
36364
36861
|
} catch (err2) {
|
|
36365
|
-
this.detach(`seed failed: ${
|
|
36862
|
+
this.detach(`seed failed: ${describe9(err2)}`);
|
|
36366
36863
|
}
|
|
36367
36864
|
}
|
|
36368
36865
|
async step(report2) {
|
|
@@ -36389,7 +36886,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36389
36886
|
return;
|
|
36390
36887
|
} catch (err2) {
|
|
36391
36888
|
if (err2 instanceof RouteMissingError || err2 instanceof PermanentStoreError || attempt >= 2) {
|
|
36392
|
-
this.detach(`step ${record2.step} failed: ${
|
|
36889
|
+
this.detach(`step ${record2.step} failed: ${describe9(err2)}`);
|
|
36393
36890
|
return;
|
|
36394
36891
|
}
|
|
36395
36892
|
await sleep2(RETRY_MS[0]);
|
|
@@ -36422,7 +36919,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36422
36919
|
} catch (err2) {
|
|
36423
36920
|
if (err2 instanceof RouteMissingError || err2 instanceof PermanentStoreError || attempt >= 2) {
|
|
36424
36921
|
this.stateDirty = true;
|
|
36425
|
-
warn(`[ledger] ${this.threadKey} state not written: ${
|
|
36922
|
+
warn(`[ledger] ${this.threadKey} state not written: ${describe9(err2)}`);
|
|
36426
36923
|
return;
|
|
36427
36924
|
}
|
|
36428
36925
|
await sleep2(RETRY_MS[0]);
|
|
@@ -36440,7 +36937,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36440
36937
|
this.detach(`finishing refused (fenced)`);
|
|
36441
36938
|
return "fenced";
|
|
36442
36939
|
} catch (err2) {
|
|
36443
|
-
warn(`[ledger] ${this.threadKey} finishing failed: ${
|
|
36940
|
+
warn(`[ledger] ${this.threadKey} finishing failed: ${describe9(err2)}`);
|
|
36444
36941
|
return "unavailable";
|
|
36445
36942
|
}
|
|
36446
36943
|
}
|
|
@@ -36455,7 +36952,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36455
36952
|
warn(`[ledger] ${this.threadKey} abandon refused (fenced) \u2014 the row is another generation's`);
|
|
36456
36953
|
} catch (err2) {
|
|
36457
36954
|
warn(
|
|
36458
|
-
`[ledger] ${this.threadKey} abandon failed: ${
|
|
36955
|
+
`[ledger] ${this.threadKey} abandon failed: ${describe9(err2)} \u2014 the sweep will take the row once its lease lapses`
|
|
36459
36956
|
);
|
|
36460
36957
|
}
|
|
36461
36958
|
}
|
|
@@ -36476,7 +36973,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36476
36973
|
this.onStop(result.stop);
|
|
36477
36974
|
}
|
|
36478
36975
|
} catch (err2) {
|
|
36479
|
-
warn(`[ledger] ${this.threadKey} heartbeat failed: ${
|
|
36976
|
+
warn(`[ledger] ${this.threadKey} heartbeat failed: ${describe9(err2)}`);
|
|
36480
36977
|
}
|
|
36481
36978
|
}
|
|
36482
36979
|
stopHeartbeat() {
|
|
@@ -36554,7 +37051,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36554
37051
|
warn(`[ledger] inbox push refused for run ${runId3}: no live row \u2014 the follow-up rides in memory only`);
|
|
36555
37052
|
return void 0;
|
|
36556
37053
|
} catch (err2) {
|
|
36557
|
-
warn(`[ledger] inbox push failed for run ${runId3}: ${
|
|
37054
|
+
warn(`[ledger] inbox push failed for run ${runId3}: ${describe9(err2)} \u2014 the follow-up rides in memory only`);
|
|
36558
37055
|
return void 0;
|
|
36559
37056
|
}
|
|
36560
37057
|
},
|
|
@@ -36563,7 +37060,7 @@ function createLedgerWriteThrough(opts) {
|
|
|
36563
37060
|
return await ledger.readInbox(runId3, afterSeq);
|
|
36564
37061
|
} catch (err2) {
|
|
36565
37062
|
warn(
|
|
36566
|
-
err2 instanceof RouteMissingError ? `[ledger] state Worker has no inbox read route \u2014 run ${runId3} resumes with the reclaim's inbox snapshot only` : `[ledger] inbox read failed for run ${runId3}: ${
|
|
37063
|
+
err2 instanceof RouteMissingError ? `[ledger] state Worker has no inbox read route \u2014 run ${runId3} resumes with the reclaim's inbox snapshot only` : `[ledger] inbox read failed for run ${runId3}: ${describe9(err2)} \u2014 resuming with the reclaim's snapshot only`
|
|
36567
37064
|
);
|
|
36568
37065
|
return [];
|
|
36569
37066
|
}
|
|
@@ -36585,12 +37082,12 @@ function createLedgerWriteThrough(opts) {
|
|
|
36585
37082
|
for (const r of candidates) if (markedSet.has(r.runId)) r.markHandedOff();
|
|
36586
37083
|
return { marked };
|
|
36587
37084
|
} catch (err2) {
|
|
36588
|
-
return { marked: [], failed:
|
|
37085
|
+
return { marked: [], failed: describe9(err2) };
|
|
36589
37086
|
}
|
|
36590
37087
|
}
|
|
36591
37088
|
};
|
|
36592
37089
|
}
|
|
36593
|
-
var NullLedgerWriteThrough, NullLedgerRun, RETRY_MS,
|
|
37090
|
+
var NullLedgerWriteThrough, NullLedgerRun, RETRY_MS, describe9;
|
|
36594
37091
|
var init_writeThrough = __esm({
|
|
36595
37092
|
"../../src/core/runLedger/writeThrough.ts"() {
|
|
36596
37093
|
"use strict";
|
|
@@ -36670,7 +37167,7 @@ var init_writeThrough = __esm({
|
|
|
36670
37167
|
}
|
|
36671
37168
|
};
|
|
36672
37169
|
RETRY_MS = [200, 800];
|
|
36673
|
-
|
|
37170
|
+
describe9 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
36674
37171
|
}
|
|
36675
37172
|
});
|
|
36676
37173
|
|
|
@@ -41316,7 +41813,7 @@ async function reclaimRuns(opts) {
|
|
|
41316
41813
|
reclaimed = await ledger.reclaim(gen, now(), LEASE_MS);
|
|
41317
41814
|
} catch (err2) {
|
|
41318
41815
|
warn(
|
|
41319
|
-
err2 instanceof RouteMissingError ? "[reclaim] state Worker has no run-ledger routes \u2014 nothing to take over" : `[reclaim] reclaim failed: ${
|
|
41816
|
+
err2 instanceof RouteMissingError ? "[reclaim] state Worker has no run-ledger routes \u2014 nothing to take over" : `[reclaim] reclaim failed: ${describe10(err2)} \u2014 runs the last generation left stay on the ledger until the next boot`
|
|
41320
41817
|
);
|
|
41321
41818
|
return outcome;
|
|
41322
41819
|
}
|
|
@@ -41384,8 +41881,8 @@ async function reclaimRuns(opts) {
|
|
|
41384
41881
|
`[reclaim] ${row.runId} ${row.threadKey} closed ${status3} (from ${run2.reclaimedFrom}; ${why}; ${events.length} event(s))`
|
|
41385
41882
|
);
|
|
41386
41883
|
} catch (err2) {
|
|
41387
|
-
outcome.failed.push({ runId: row.runId, error:
|
|
41388
|
-
warn(`[reclaim] ${row.runId} ${row.threadKey}: ${
|
|
41884
|
+
outcome.failed.push({ runId: row.runId, error: describe10(err2) });
|
|
41885
|
+
warn(`[reclaim] ${row.runId} ${row.threadKey}: ${describe10(err2)} \u2014 left on the ledger for the next boot`);
|
|
41389
41886
|
}
|
|
41390
41887
|
}
|
|
41391
41888
|
try {
|
|
@@ -41401,7 +41898,7 @@ async function reclaimRuns(opts) {
|
|
|
41401
41898
|
});
|
|
41402
41899
|
}
|
|
41403
41900
|
} catch (err2) {
|
|
41404
|
-
warn(`[reclaim] listing live runs failed: ${
|
|
41901
|
+
warn(`[reclaim] listing live runs failed: ${describe10(err2)} \u2014 the card sweep runs without the ledger's guard`);
|
|
41405
41902
|
}
|
|
41406
41903
|
if (reclaimed.length > 0 || outcome.liveElsewhere.length > 0) {
|
|
41407
41904
|
log(
|
|
@@ -41423,7 +41920,7 @@ function startReclaimSweep(opts) {
|
|
|
41423
41920
|
await opts.onOutcome(outcome);
|
|
41424
41921
|
}
|
|
41425
41922
|
} catch (err2) {
|
|
41426
|
-
warn(`[reclaim] sweep failed: ${
|
|
41923
|
+
warn(`[reclaim] sweep failed: ${describe10(err2)}`);
|
|
41427
41924
|
} finally {
|
|
41428
41925
|
running = false;
|
|
41429
41926
|
}
|
|
@@ -41449,7 +41946,7 @@ async function completenessVerdict(ledger, row, lastStep) {
|
|
|
41449
41946
|
const source2 = transcriptSource(row.meta);
|
|
41450
41947
|
transcript = source2.kind === "session" ? await ledger.readSession(source2.key, source2.from) : await ledger.readTranscript(row.runId);
|
|
41451
41948
|
} catch (err2) {
|
|
41452
|
-
return { resumable: false, why: `transcript unreadable (${
|
|
41949
|
+
return { resumable: false, why: `transcript unreadable (${describe10(err2)})` };
|
|
41453
41950
|
}
|
|
41454
41951
|
if (!transcript.complete) return { resumable: false, why: `transcript incomplete: ${transcript.gap}` };
|
|
41455
41952
|
const verdict = transcriptCompleteness({
|
|
@@ -41470,7 +41967,7 @@ async function completenessVerdict(ledger, row, lastStep) {
|
|
|
41470
41967
|
return { resumable: false, why: verdict.why };
|
|
41471
41968
|
}
|
|
41472
41969
|
}
|
|
41473
|
-
var TERMINAL,
|
|
41970
|
+
var TERMINAL, describe10;
|
|
41474
41971
|
var init_boot = __esm({
|
|
41475
41972
|
"../../src/core/boot.ts"() {
|
|
41476
41973
|
"use strict";
|
|
@@ -41487,7 +41984,7 @@ var init_boot = __esm({
|
|
|
41487
41984
|
"failed",
|
|
41488
41985
|
"interrupted"
|
|
41489
41986
|
]);
|
|
41490
|
-
|
|
41987
|
+
describe10 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
41491
41988
|
}
|
|
41492
41989
|
});
|
|
41493
41990
|
|
|
@@ -42336,6 +42833,17 @@ var init_toolRules = __esm({
|
|
|
42336
42833
|
});
|
|
42337
42834
|
|
|
42338
42835
|
// ../../src/core/harness/pi/relay.ts
|
|
42836
|
+
async function relayToolCall(harness, calls, ask, opts = {}) {
|
|
42837
|
+
const answer = calls.join(ask.toolCallId, () => runRelayedTool(harness, ask, { signal: calls.signal }));
|
|
42838
|
+
const window = new AbortController();
|
|
42839
|
+
const sleep2 = opts.sleep ?? sleepUnlessAborted;
|
|
42840
|
+
const progress = await Promise.race([
|
|
42841
|
+
answer.then((a) => ({ done: true, answer: a })),
|
|
42842
|
+
sleep2(opts.windowMs ?? RELAY_POLL_WINDOW_MS, window.signal).then(() => ({ done: false }))
|
|
42843
|
+
]);
|
|
42844
|
+
window.abort();
|
|
42845
|
+
return progress;
|
|
42846
|
+
}
|
|
42339
42847
|
function relayedToolDefinitions(harness) {
|
|
42340
42848
|
return harness.tools.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
|
|
42341
42849
|
}
|
|
@@ -42356,18 +42864,23 @@ function authorizeToolCall(harness, ask) {
|
|
|
42356
42864
|
if (verdict.verdict === "allowed") return { allow: true };
|
|
42357
42865
|
return refuse3(verdict.reason);
|
|
42358
42866
|
}
|
|
42359
|
-
async function runRelayedTool(harness, ask) {
|
|
42867
|
+
async function runRelayedTool(harness, ask, opts = {}) {
|
|
42360
42868
|
const tool = harness.tools.find((t) => t.name === ask.tool);
|
|
42361
42869
|
if (!tool) return { content: [{ type: "text", text: `Unknown tool: ${ask.tool}` }], isError: true };
|
|
42362
42870
|
const span = harness.toolSpan(ask.toolCallId);
|
|
42363
42871
|
const base = harness.toolContext;
|
|
42364
|
-
const
|
|
42365
|
-
|
|
42366
|
-
span
|
|
42367
|
-
|
|
42368
|
-
|
|
42369
|
-
|
|
42370
|
-
|
|
42872
|
+
const readConversation = base.conversation;
|
|
42873
|
+
const ctx = {
|
|
42874
|
+
...span ? {
|
|
42875
|
+
...base,
|
|
42876
|
+
span,
|
|
42877
|
+
executor: new TracingExecutor(base.executor, span, harness.backend),
|
|
42878
|
+
publish: (e) => harness.emit(withSpanId2(e, span.id)),
|
|
42879
|
+
...base.github?.api.withSpan ? { github: { ...base.github, api: base.github.api.withSpan(span) } } : {}
|
|
42880
|
+
} : { ...base, publish: (e) => harness.emit(e) },
|
|
42881
|
+
...opts.signal && !base.signal ? { signal: opts.signal } : {},
|
|
42882
|
+
...readConversation && harness.callSeen ? { conversation: () => harness.callSeen(ask.toolCallId).then(() => readConversation()) } : {}
|
|
42883
|
+
};
|
|
42371
42884
|
try {
|
|
42372
42885
|
const input = typeof ask.input === "object" && ask.input !== null ? ask.input : {};
|
|
42373
42886
|
const output = await tool.run(input, ctx);
|
|
@@ -42395,30 +42908,64 @@ function withSpanId2(e, spanId) {
|
|
|
42395
42908
|
return e;
|
|
42396
42909
|
}
|
|
42397
42910
|
}
|
|
42398
|
-
var HarnessRegistry;
|
|
42911
|
+
var HarnessRegistry, RELAY_POLL_WINDOW_MS, RelayedCalls;
|
|
42399
42912
|
var init_relay = __esm({
|
|
42400
42913
|
"../../src/core/harness/pi/relay.ts"() {
|
|
42401
42914
|
"use strict";
|
|
42402
42915
|
init_tracingExecutor();
|
|
42403
42916
|
init_types3();
|
|
42917
|
+
init_awaitChildren();
|
|
42404
42918
|
init_runEvents();
|
|
42405
42919
|
init_toolRules();
|
|
42406
42920
|
HarnessRegistry = class {
|
|
42407
42921
|
live = /* @__PURE__ */ new Map();
|
|
42408
|
-
/** The run drives a pi from here
|
|
42922
|
+
/** The run drives a pi from here, its relayed calls kept beside it; the
|
|
42923
|
+
* returned function forgets it and ends whatever call still runs. */
|
|
42409
42924
|
register(harness) {
|
|
42410
|
-
|
|
42925
|
+
const entry = { harness, calls: new RelayedCalls() };
|
|
42926
|
+
this.live.set(harness.runId, entry);
|
|
42411
42927
|
return () => {
|
|
42412
|
-
if (this.live.get(harness.runId)
|
|
42928
|
+
if (this.live.get(harness.runId) !== entry) return;
|
|
42929
|
+
this.live.delete(harness.runId);
|
|
42930
|
+
entry.calls.end();
|
|
42413
42931
|
};
|
|
42414
42932
|
}
|
|
42415
42933
|
get(runId3) {
|
|
42416
|
-
return this.live.get(runId3);
|
|
42934
|
+
return this.live.get(runId3)?.harness;
|
|
42935
|
+
}
|
|
42936
|
+
/** The run's relayed calls, for as long as it is registered. */
|
|
42937
|
+
calls(runId3) {
|
|
42938
|
+
return this.live.get(runId3)?.calls;
|
|
42417
42939
|
}
|
|
42418
42940
|
size() {
|
|
42419
42941
|
return this.live.size;
|
|
42420
42942
|
}
|
|
42421
42943
|
};
|
|
42944
|
+
RELAY_POLL_WINDOW_MS = 3e4;
|
|
42945
|
+
RelayedCalls = class {
|
|
42946
|
+
calls = /* @__PURE__ */ new Map();
|
|
42947
|
+
ending = new AbortController();
|
|
42948
|
+
get size() {
|
|
42949
|
+
return this.calls.size;
|
|
42950
|
+
}
|
|
42951
|
+
/** Aborted when the run ends: the signal every call here runs under. */
|
|
42952
|
+
get signal() {
|
|
42953
|
+
return this.ending.signal;
|
|
42954
|
+
}
|
|
42955
|
+
/** The call's answer as a promise: started by `start` on the first ask, the same promise after. */
|
|
42956
|
+
join(callId, start3) {
|
|
42957
|
+
let answer = this.calls.get(callId);
|
|
42958
|
+
if (!answer) {
|
|
42959
|
+
answer = start3();
|
|
42960
|
+
this.calls.set(callId, answer);
|
|
42961
|
+
}
|
|
42962
|
+
return answer;
|
|
42963
|
+
}
|
|
42964
|
+
end() {
|
|
42965
|
+
this.ending.abort();
|
|
42966
|
+
this.calls.clear();
|
|
42967
|
+
}
|
|
42968
|
+
};
|
|
42422
42969
|
}
|
|
42423
42970
|
});
|
|
42424
42971
|
|
|
@@ -42467,7 +43014,15 @@ async function handleHarnessRequest(deps, req) {
|
|
|
42467
43014
|
deps.log?.(`[harness] run=${door.runId} authorize ${ask.tool} \u2192 ${answer2.allow ? "allow" : "refuse"}`);
|
|
42468
43015
|
return { status: 200, body: { ...answer2 } };
|
|
42469
43016
|
}
|
|
42470
|
-
const
|
|
43017
|
+
const progress = await relayToolCall(harness, deps.harnesses.calls(door.runId), ask, {
|
|
43018
|
+
...deps.relayWindowMs !== void 0 ? { windowMs: deps.relayWindowMs } : {},
|
|
43019
|
+
...deps.sleep ? { sleep: deps.sleep } : {}
|
|
43020
|
+
});
|
|
43021
|
+
if (!progress.done) {
|
|
43022
|
+
deps.log?.(`[harness] run=${door.runId} tool ${ask.tool} \u2192 pending`);
|
|
43023
|
+
return { status: 202, body: { pending: true, toolCallId: ask.toolCallId } };
|
|
43024
|
+
}
|
|
43025
|
+
const { answer } = progress;
|
|
42471
43026
|
deps.log?.(`[harness] run=${door.runId} tool ${ask.tool} \u2192 ${answer.isError ? "error" : "ok"}`);
|
|
42472
43027
|
return { status: 200, body: { ...answer } };
|
|
42473
43028
|
}
|
|
@@ -42803,7 +43358,7 @@ async function openThreadFromRequester(deps, instance, lead, at) {
|
|
|
42803
43358
|
}
|
|
42804
43359
|
};
|
|
42805
43360
|
} catch (err2) {
|
|
42806
|
-
return { ok: false, response: json(502, { ok: false, error: "thread_failed", message:
|
|
43361
|
+
return { ok: false, response: json(502, { ok: false, error: "thread_failed", message: describe11(err2), at }) };
|
|
42807
43362
|
}
|
|
42808
43363
|
}
|
|
42809
43364
|
async function ensureReviewThread(deps, instance, row, at) {
|
|
@@ -42929,8 +43484,8 @@ async function spawn6(body, deps) {
|
|
|
42929
43484
|
try {
|
|
42930
43485
|
turn = await composeChild(req.brief, instance, row, briefReaders(deps, instance, io));
|
|
42931
43486
|
} catch (err2) {
|
|
42932
|
-
log(`[coordinator] ${instance.id} ${req.step}: the brief could not be composed: ${
|
|
42933
|
-
return json(502, { ok: false, error: "brief_failed", message:
|
|
43487
|
+
log(`[coordinator] ${instance.id} ${req.step}: the brief could not be composed: ${describe11(err2)}`);
|
|
43488
|
+
return json(502, { ok: false, error: "brief_failed", message: describe11(err2), at });
|
|
42934
43489
|
}
|
|
42935
43490
|
} else {
|
|
42936
43491
|
turn = { prompt: req.prompt };
|
|
@@ -42980,7 +43535,7 @@ async function spawn6(body, deps) {
|
|
|
42980
43535
|
);
|
|
42981
43536
|
void settled.then((end) => {
|
|
42982
43537
|
if (end.kind === "threw")
|
|
42983
|
-
log(`[coordinator] ${instance.id} ${req.step}: the child's dispatch threw: ${
|
|
43538
|
+
log(`[coordinator] ${instance.id} ${req.step}: the child's dispatch threw: ${describe11(end.err)}`);
|
|
42984
43539
|
});
|
|
42985
43540
|
const first = await Promise.race([started.then((id) => ({ kind: "started", id })), settled]);
|
|
42986
43541
|
if (first.kind === "started" || startedId !== void 0) {
|
|
@@ -42988,7 +43543,7 @@ async function spawn6(body, deps) {
|
|
|
42988
43543
|
log(`[coordinator] ${instance.id} ${req.step}: spawned ${req.preset} run ${runId3} in ${threadKey}`);
|
|
42989
43544
|
return json(200, { ok: true, runId: runId3, threadKey, at });
|
|
42990
43545
|
}
|
|
42991
|
-
if (first.kind === "threw") return json(502, { ok: false, error: "spawn_failed", message:
|
|
43546
|
+
if (first.kind === "threw") return json(502, { ok: false, error: "spawn_failed", message: describe11(first.err), at });
|
|
42992
43547
|
const refusal = first.outcome.refusal;
|
|
42993
43548
|
if (refusal === "coordinator_thread_live") {
|
|
42994
43549
|
const now = await liveOnThread(deps.runs, instance, threadKey);
|
|
@@ -43141,7 +43696,7 @@ async function prCheck(body, deps) {
|
|
|
43141
43696
|
at
|
|
43142
43697
|
});
|
|
43143
43698
|
} catch (err2) {
|
|
43144
|
-
return json(502, { ok: false, error: "github_unavailable", message:
|
|
43699
|
+
return json(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
|
|
43145
43700
|
}
|
|
43146
43701
|
}
|
|
43147
43702
|
async function plan2(body, deps) {
|
|
@@ -43250,7 +43805,7 @@ async function branch(body, deps) {
|
|
|
43250
43805
|
await deps.createBranchRef(instance.repo, name, base);
|
|
43251
43806
|
return json(200, { ok: true, branch: name, base, at });
|
|
43252
43807
|
} catch (err2) {
|
|
43253
|
-
return json(200, { ok: false, reason:
|
|
43808
|
+
return json(200, { ok: false, reason: describe11(err2), at });
|
|
43254
43809
|
}
|
|
43255
43810
|
}
|
|
43256
43811
|
function unitLines(units) {
|
|
@@ -43302,7 +43857,7 @@ async function round(body, deps) {
|
|
|
43302
43857
|
instance,
|
|
43303
43858
|
units.map((u) => u.unit === updated.unit ? updated : u)
|
|
43304
43859
|
).catch(
|
|
43305
|
-
(err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${
|
|
43860
|
+
(err2) => (deps.log ?? console.warn)(`[coordinator] ${instance.id}: the card could not be redrawn: ${describe11(err2)}`)
|
|
43306
43861
|
);
|
|
43307
43862
|
return json(200, { ok: true, at });
|
|
43308
43863
|
}
|
|
@@ -43336,7 +43891,7 @@ async function unitEnd(body, deps) {
|
|
|
43336
43891
|
told = true;
|
|
43337
43892
|
} catch (err2) {
|
|
43338
43893
|
(deps.log ?? console.warn)(
|
|
43339
|
-
`[coordinator] ${instance.id} ${row.unit}: the report could not be posted: ${
|
|
43894
|
+
`[coordinator] ${instance.id} ${row.unit}: the report could not be posted: ${describe11(err2)}`
|
|
43340
43895
|
);
|
|
43341
43896
|
}
|
|
43342
43897
|
}
|
|
@@ -43350,7 +43905,7 @@ ${ending.report}` + (rendered !== void 0 ? `
|
|
|
43350
43905
|
${rendered}` : "");
|
|
43351
43906
|
await deps.github.commentIssue(instance.repo, row.issue, comment).catch(
|
|
43352
43907
|
(err2) => (deps.log ?? console.warn)(
|
|
43353
|
-
`[coordinator] ${instance.id} ${row.unit}: the board comment could not be posted: ${
|
|
43908
|
+
`[coordinator] ${instance.id} ${row.unit}: the board comment could not be posted: ${describe11(err2)}`
|
|
43354
43909
|
)
|
|
43355
43910
|
);
|
|
43356
43911
|
}
|
|
@@ -43405,7 +43960,7 @@ async function merge(body, deps, subject2) {
|
|
|
43405
43960
|
try {
|
|
43406
43961
|
facts2 = await deps.fetchPrFacts(pr);
|
|
43407
43962
|
} catch (err2) {
|
|
43408
|
-
return json(502, { ok: false, error: "github_unavailable", message:
|
|
43963
|
+
return json(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
|
|
43409
43964
|
}
|
|
43410
43965
|
if (facts2 === void 0)
|
|
43411
43966
|
return json(502, { ok: false, error: "github_unavailable", message: `${where} could not be read`, at });
|
|
@@ -43457,7 +44012,7 @@ async function merge(body, deps, subject2) {
|
|
|
43457
44012
|
title: facts2.title ?? `Merge pull request #${pr.number}`
|
|
43458
44013
|
});
|
|
43459
44014
|
} catch (err2) {
|
|
43460
|
-
return json(502, { ok: false, error: "github_unavailable", message:
|
|
44015
|
+
return json(502, { ok: false, error: "github_unavailable", message: describe11(err2), at });
|
|
43461
44016
|
}
|
|
43462
44017
|
if (!merged.ok) {
|
|
43463
44018
|
log(
|
|
@@ -43632,13 +44187,13 @@ function createAdminCoordinatorHandler(deps) {
|
|
|
43632
44187
|
}
|
|
43633
44188
|
write(res, await answerCoordinatorStep(door, read.body, deps));
|
|
43634
44189
|
} catch (err2) {
|
|
43635
|
-
(deps.log ?? console.error)(`[coordinator] ${
|
|
44190
|
+
(deps.log ?? console.error)(`[coordinator] ${describe11(err2)}`);
|
|
43636
44191
|
write(res, json(500, { ok: false, error: "internal error" }));
|
|
43637
44192
|
}
|
|
43638
44193
|
})();
|
|
43639
44194
|
};
|
|
43640
44195
|
}
|
|
43641
|
-
var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json,
|
|
44196
|
+
var COORDINATOR_ADMIN_PREFIX, MAX_SPAWN_PROMPT_CHARS, FINISHED_LOOKBACK_PAGES, MAX_ADMIN_BODY_BYTES, UNIT_ID, PRESETS_OF_KIND, invalid, json, describe11, EVERY_RUN, REVIEW_POSTED_CHECKS, REVIEW_POSTED_RECHECK_MS, ROUND_OUTCOMES, ENDING_ICON, STEPS;
|
|
43642
44197
|
var init_adminCoordinator = __esm({
|
|
43643
44198
|
"../../src/channels/adminCoordinator.ts"() {
|
|
43644
44199
|
"use strict";
|
|
@@ -43671,7 +44226,7 @@ var init_adminCoordinator = __esm({
|
|
|
43671
44226
|
};
|
|
43672
44227
|
invalid = (error) => ({ ok: false, error });
|
|
43673
44228
|
json = (status3, body) => ({ status: status3, body });
|
|
43674
|
-
|
|
44229
|
+
describe11 = (err2) => err2 instanceof Error ? err2.message : String(err2);
|
|
43675
44230
|
EVERY_RUN = { kind: "all" };
|
|
43676
44231
|
REVIEW_POSTED_CHECKS = 3;
|
|
43677
44232
|
REVIEW_POSTED_RECHECK_MS = 2e3;
|