@adhdev/daemon-standalone 1.0.56-rc.6 → 1.0.56-rc.7
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/index.js
CHANGED
|
@@ -36620,10 +36620,10 @@ var require_dist3 = __commonJS({
|
|
|
36620
36620
|
}
|
|
36621
36621
|
function getDaemonBuildInfo() {
|
|
36622
36622
|
if (cached2) return cached2;
|
|
36623
|
-
const commit = readInjected(true ? "
|
|
36624
|
-
const commitShort = readInjected(true ? "
|
|
36625
|
-
const version2 = readInjected(true ? "1.0.56-rc.
|
|
36626
|
-
const builtAt = readInjected(true ? "2026-08-
|
|
36623
|
+
const commit = readInjected(true ? "13ea4fe8748ca3d97f0e4f9cc10a4c5969855fff" : void 0) ?? "unknown";
|
|
36624
|
+
const commitShort = readInjected(true ? "13ea4fe8" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36625
|
+
const version2 = readInjected(true ? "1.0.56-rc.7" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36626
|
+
const builtAt = readInjected(true ? "2026-08-19T14:43:40.002Z" : void 0);
|
|
36627
36627
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36628
36628
|
return cached2;
|
|
36629
36629
|
}
|
|
@@ -44261,6 +44261,13 @@ Next step: ${nextStep}`;
|
|
|
44261
44261
|
import_meta = {};
|
|
44262
44262
|
}
|
|
44263
44263
|
});
|
|
44264
|
+
function normalizeDeliveryMode(raw) {
|
|
44265
|
+
if (raw === void 0 || raw === null || raw === "") return { mode: DEFAULT_DELIVERY_MODE };
|
|
44266
|
+
const v = String(raw).trim().toLowerCase();
|
|
44267
|
+
if (v === "when_idle" || v === "whenidle") return { mode: "when_idle" };
|
|
44268
|
+
if (v === "interrupt") return { mode: "interrupt" };
|
|
44269
|
+
return { mode: DEFAULT_DELIVERY_MODE, unrecognized: String(raw) };
|
|
44270
|
+
}
|
|
44264
44271
|
function resolveDeliveryDecision(sessionStatus, opts) {
|
|
44265
44272
|
const status = (sessionStatus || "").trim().toLowerCase();
|
|
44266
44273
|
if (!status) {
|
|
@@ -44292,6 +44299,20 @@ Next step: ${nextStep}`;
|
|
|
44292
44299
|
message: "Session is waiting for approval \u2014 approval message delivered immediately."
|
|
44293
44300
|
};
|
|
44294
44301
|
}
|
|
44302
|
+
if (opts?.deliveryMode === "interrupt") {
|
|
44303
|
+
if (!opts.interruptSupported) {
|
|
44304
|
+
return {
|
|
44305
|
+
decision: "rejected",
|
|
44306
|
+
reason: "interrupt_unsupported_for_provider",
|
|
44307
|
+
message: opts.interruptUnsupportedMessage ?? `Session is ${status} and delivery mode 'interrupt' was requested, but this provider cannot interrupt a running turn. Refusing rather than silently queueing: queueing would let the current turn finish on the old instructions. Re-dispatch with delivery mode 'when_idle' if delivery-after-completion is acceptable.`
|
|
44308
|
+
};
|
|
44309
|
+
}
|
|
44310
|
+
return {
|
|
44311
|
+
decision: "interrupt",
|
|
44312
|
+
reason: `session_${status}_interrupt_requested`,
|
|
44313
|
+
message: `Session is ${status}. Aborting the in-flight turn via the provider's stop control, then delivering the task once it settles to idle. The work the session had not yet finished is discarded.`
|
|
44314
|
+
};
|
|
44315
|
+
}
|
|
44295
44316
|
return {
|
|
44296
44317
|
decision: "queued",
|
|
44297
44318
|
reason: `session_${status}_busy`,
|
|
@@ -44380,6 +44401,7 @@ Next step: ${nextStep}`;
|
|
|
44380
44401
|
}
|
|
44381
44402
|
}
|
|
44382
44403
|
var import_crypto4;
|
|
44404
|
+
var DEFAULT_DELIVERY_MODE;
|
|
44383
44405
|
var IMMEDIATE_DELIVERY_STATUSES;
|
|
44384
44406
|
var BUSY_DELIVERY_STATUSES;
|
|
44385
44407
|
var TERMINAL_DELIVERY_STATUSES;
|
|
@@ -44388,6 +44410,7 @@ Next step: ${nextStep}`;
|
|
|
44388
44410
|
"use strict";
|
|
44389
44411
|
import_crypto4 = require("crypto");
|
|
44390
44412
|
init_mesh_runtime_store();
|
|
44413
|
+
DEFAULT_DELIVERY_MODE = "when_idle";
|
|
44391
44414
|
IMMEDIATE_DELIVERY_STATUSES = /* @__PURE__ */ new Set([
|
|
44392
44415
|
"idle",
|
|
44393
44416
|
"waiting_input",
|
|
@@ -45688,6 +45711,25 @@ Next step: ${nextStep}`;
|
|
|
45688
45711
|
while (end > 0 && (buf[end] & 192) === 128) end -= 1;
|
|
45689
45712
|
return `${buf.subarray(0, end).toString("utf8")}${marker}`;
|
|
45690
45713
|
}
|
|
45714
|
+
function describeAvailablePointers(upstream) {
|
|
45715
|
+
if (!upstream) return " (no output is recorded for that ref yet)";
|
|
45716
|
+
const envelope = upstream.envelope;
|
|
45717
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
45718
|
+
return " (that ref has an output, but its envelope is not an object)";
|
|
45719
|
+
}
|
|
45720
|
+
const pointers = [];
|
|
45721
|
+
for (const key2 of Object.keys(envelope)) {
|
|
45722
|
+
pointers.push(`/${key2}`);
|
|
45723
|
+
if (key2 !== "worker_result") continue;
|
|
45724
|
+
const workerResult = envelope.worker_result;
|
|
45725
|
+
if (!workerResult || typeof workerResult !== "object" || Array.isArray(workerResult)) continue;
|
|
45726
|
+
for (const sub of Object.keys(workerResult)) {
|
|
45727
|
+
pointers.push(`/worker_result/${sub}`);
|
|
45728
|
+
}
|
|
45729
|
+
}
|
|
45730
|
+
if (pointers.length === 0) return " (that ref's envelope is empty)";
|
|
45731
|
+
return `. Available pointers on ref '${upstream.ref}': ${pointers.join(", ")}`;
|
|
45732
|
+
}
|
|
45691
45733
|
function resolveInputBindings(bindings, outputsByRef) {
|
|
45692
45734
|
const resolved = [];
|
|
45693
45735
|
let totalBytes = 0;
|
|
@@ -45698,7 +45740,7 @@ Next step: ${nextStep}`;
|
|
|
45698
45740
|
if (binding.required) {
|
|
45699
45741
|
throw new MeshMaterializationError(
|
|
45700
45742
|
"required_input_missing",
|
|
45701
|
-
`required binding '${binding.as}' found nothing at '${binding.select}' of ref '${binding.from}'
|
|
45743
|
+
`required binding '${binding.as}' found nothing at '${binding.select}' of ref '${binding.from}'` + describeAvailablePointers(upstream),
|
|
45702
45744
|
binding.as
|
|
45703
45745
|
);
|
|
45704
45746
|
}
|
|
@@ -47122,108 +47164,6 @@ ${blocks.join("\n\n")}`;
|
|
|
47122
47164
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
47123
47165
|
}
|
|
47124
47166
|
});
|
|
47125
|
-
function readPayloadTaskId(entry) {
|
|
47126
|
-
const value = entry.payload?.taskId;
|
|
47127
|
-
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
47128
|
-
}
|
|
47129
|
-
function parseTime(value) {
|
|
47130
|
-
if (!value) return null;
|
|
47131
|
-
const parsed = new Date(value).getTime();
|
|
47132
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
47133
|
-
}
|
|
47134
|
-
function computeMeshTaskStats(meshId, opts) {
|
|
47135
|
-
const queue = getQueue(meshId);
|
|
47136
|
-
const queueById = new Map(queue.map((task) => [task.id, task]));
|
|
47137
|
-
let targetIds;
|
|
47138
|
-
if (opts?.taskIds?.length) {
|
|
47139
|
-
targetIds = [...new Set(opts.taskIds)];
|
|
47140
|
-
} else if (opts?.missionId) {
|
|
47141
|
-
targetIds = queue.filter((task) => task.missionId === opts.missionId).map((task) => task.id);
|
|
47142
|
-
} else {
|
|
47143
|
-
targetIds = queue.map((task) => task.id);
|
|
47144
|
-
}
|
|
47145
|
-
if (targetIds.length === 0) return [];
|
|
47146
|
-
const targetSet = new Set(targetIds);
|
|
47147
|
-
const entries = readLedgerEntries(meshId, { tail: opts?.tail ?? 1e3 });
|
|
47148
|
-
const dispatches = /* @__PURE__ */ new Map();
|
|
47149
|
-
const terminals = /* @__PURE__ */ new Map();
|
|
47150
|
-
for (const entry of entries) {
|
|
47151
|
-
const taskId = readPayloadTaskId(entry);
|
|
47152
|
-
if (!taskId || !targetSet.has(taskId)) continue;
|
|
47153
|
-
if (entry.kind === "task_dispatched") {
|
|
47154
|
-
const existing = dispatches.get(taskId);
|
|
47155
|
-
if (existing) existing.count += 1;
|
|
47156
|
-
else dispatches.set(taskId, { first: entry.timestamp, count: 1 });
|
|
47157
|
-
} else if (entry.kind === "task_completed" || entry.kind === "task_failed") {
|
|
47158
|
-
terminals.set(taskId, { at: entry.timestamp, kind: entry.kind });
|
|
47159
|
-
}
|
|
47160
|
-
}
|
|
47161
|
-
return targetIds.map((taskId) => {
|
|
47162
|
-
const queueEntry = queueById.get(taskId);
|
|
47163
|
-
const dispatch = dispatches.get(taskId);
|
|
47164
|
-
const terminal = terminals.get(taskId);
|
|
47165
|
-
const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
|
|
47166
|
-
const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
|
|
47167
|
-
const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
|
|
47168
|
-
const terminalTime = parseTime(terminal?.at);
|
|
47169
|
-
const stats = {
|
|
47170
|
-
taskId,
|
|
47171
|
-
status,
|
|
47172
|
-
dispatchedAt: dispatch?.first ?? queueEntry?.dispatchTimestamp ?? null,
|
|
47173
|
-
terminalAt: terminal?.at ?? null,
|
|
47174
|
-
terminalKind: terminal?.kind ?? null,
|
|
47175
|
-
durationMs: null,
|
|
47176
|
-
dispatchCount: dispatch?.count ?? 0,
|
|
47177
|
-
requeueCount: queueEntry?.requeueCount ?? 0
|
|
47178
|
-
};
|
|
47179
|
-
if (dispatchTime !== null && terminalTime !== null && terminalTime >= dispatchTime) {
|
|
47180
|
-
stats.durationMs = terminalTime - dispatchTime;
|
|
47181
|
-
} else if (isTerminalStatus) {
|
|
47182
|
-
stats.incompleteEvidence = true;
|
|
47183
|
-
}
|
|
47184
|
-
return stats;
|
|
47185
|
-
});
|
|
47186
|
-
}
|
|
47187
|
-
function computeMeshMissionStats(meshId, missionId) {
|
|
47188
|
-
const tasks = computeMeshTaskStats(meshId, { missionId });
|
|
47189
|
-
const stats = {
|
|
47190
|
-
missionId,
|
|
47191
|
-
taskCount: tasks.length,
|
|
47192
|
-
completed: 0,
|
|
47193
|
-
failed: 0,
|
|
47194
|
-
totalDurationMs: 0,
|
|
47195
|
-
wallClockMs: null,
|
|
47196
|
-
retries: 0,
|
|
47197
|
-
incompleteTaskIds: []
|
|
47198
|
-
};
|
|
47199
|
-
let firstDispatch = null;
|
|
47200
|
-
let lastTerminal = null;
|
|
47201
|
-
for (const task of tasks) {
|
|
47202
|
-
if (task.status === "completed") stats.completed += 1;
|
|
47203
|
-
else if (task.status === "failed") stats.failed += 1;
|
|
47204
|
-
stats.retries += task.requeueCount;
|
|
47205
|
-
if (task.incompleteEvidence) {
|
|
47206
|
-
stats.incompleteTaskIds.push(task.taskId);
|
|
47207
|
-
continue;
|
|
47208
|
-
}
|
|
47209
|
-
if (task.durationMs !== null) stats.totalDurationMs += task.durationMs;
|
|
47210
|
-
const dispatchTime = parseTime(task.dispatchedAt);
|
|
47211
|
-
const terminalTime = parseTime(task.terminalAt);
|
|
47212
|
-
if (dispatchTime !== null && (firstDispatch === null || dispatchTime < firstDispatch)) firstDispatch = dispatchTime;
|
|
47213
|
-
if (terminalTime !== null && (lastTerminal === null || terminalTime > lastTerminal)) lastTerminal = terminalTime;
|
|
47214
|
-
}
|
|
47215
|
-
if (firstDispatch !== null && lastTerminal !== null && lastTerminal >= firstDispatch) {
|
|
47216
|
-
stats.wallClockMs = lastTerminal - firstDispatch;
|
|
47217
|
-
}
|
|
47218
|
-
return stats;
|
|
47219
|
-
}
|
|
47220
|
-
var init_mesh_task_stats = __esm2({
|
|
47221
|
-
"src/mesh/mesh-task-stats.ts"() {
|
|
47222
|
-
"use strict";
|
|
47223
|
-
init_mesh_ledger();
|
|
47224
|
-
init_mesh_work_queue();
|
|
47225
|
-
}
|
|
47226
|
-
});
|
|
47227
47167
|
function truncateString(value, maxChars) {
|
|
47228
47168
|
if (value.length <= maxChars) return value;
|
|
47229
47169
|
if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));
|
|
@@ -47943,6 +47883,7 @@ ${blocks.join("\n\n")}`;
|
|
|
47943
47883
|
const taskId = readNonEmptyString(metadata.taskId) || readNonEmptyString(readRecord2(metadata.payload)?.taskId);
|
|
47944
47884
|
const jobId = readRefineJobId2(event);
|
|
47945
47885
|
const timestamp2 = metadata.timestamp !== void 0 && metadata.timestamp !== null ? String(metadata.timestamp) : "";
|
|
47886
|
+
const alertReason = COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT.has(event.event) ? readNonEmptyString(metadata.reason) : void 0;
|
|
47946
47887
|
return [
|
|
47947
47888
|
event.meshId,
|
|
47948
47889
|
event.event,
|
|
@@ -47951,7 +47892,8 @@ ${blocks.join("\n\n")}`;
|
|
|
47951
47892
|
providerSessionId || "",
|
|
47952
47893
|
taskId || "",
|
|
47953
47894
|
jobId || "",
|
|
47954
|
-
timestamp2 || ""
|
|
47895
|
+
timestamp2 || "",
|
|
47896
|
+
...alertReason ? [alertReason] : []
|
|
47955
47897
|
].join("::");
|
|
47956
47898
|
}
|
|
47957
47899
|
function hasPendingCoordinatorEventDuplicate(event) {
|
|
@@ -48400,6 +48342,7 @@ ${blocks.join("\n\n")}`;
|
|
|
48400
48342
|
var warnedV2Violations;
|
|
48401
48343
|
var loggedSqlitePendingDrainFailure;
|
|
48402
48344
|
var TERMINAL_COMPLETION_EVENTS;
|
|
48345
|
+
var COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT;
|
|
48403
48346
|
var PENDING_EVENTS_DRAINED_RETENTION_MS;
|
|
48404
48347
|
var PENDING_EVENTS_UNDRAINED_RETENTION_MS;
|
|
48405
48348
|
var PENDING_RETENTION_EXPIRED_HOLD_REASON;
|
|
@@ -48450,6 +48393,9 @@ ${blocks.join("\n\n")}`;
|
|
|
48450
48393
|
warnedV2Violations = /* @__PURE__ */ new Set();
|
|
48451
48394
|
loggedSqlitePendingDrainFailure = false;
|
|
48452
48395
|
TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
|
|
48396
|
+
COORDINATOR_ALERT_EVENTS_WITH_REASON_FINGERPRINT = /* @__PURE__ */ new Set([
|
|
48397
|
+
"mesh:dispatch_blocked"
|
|
48398
|
+
]);
|
|
48453
48399
|
PENDING_EVENTS_DRAINED_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
48454
48400
|
PENDING_EVENTS_UNDRAINED_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
48455
48401
|
PENDING_RETENTION_EXPIRED_HOLD_REASON = "pending_retention_expired";
|
|
@@ -48470,6 +48416,245 @@ ${blocks.join("\n\n")}`;
|
|
|
48470
48416
|
};
|
|
48471
48417
|
}
|
|
48472
48418
|
});
|
|
48419
|
+
function s(v) {
|
|
48420
|
+
return typeof v === "string" && v.trim() ? v.trim() : "";
|
|
48421
|
+
}
|
|
48422
|
+
function meshEventTraceKey(ctx) {
|
|
48423
|
+
const segs = [`task=${s(ctx.taskId) || "-"}`];
|
|
48424
|
+
const eventId = s(ctx.eventId);
|
|
48425
|
+
if (eventId) segs.push(`evt=${eventId}`);
|
|
48426
|
+
segs.push(`sess=${s(ctx.sessionId) || "-"}`);
|
|
48427
|
+
const nodeId = s(ctx.nodeId);
|
|
48428
|
+
if (nodeId) segs.push(`node=${nodeId}`);
|
|
48429
|
+
const meshId = s(ctx.meshId);
|
|
48430
|
+
if (meshId) segs.push(`mesh=${meshId}`);
|
|
48431
|
+
const event = s(ctx.event);
|
|
48432
|
+
if (event) segs.push(`event=${event}`);
|
|
48433
|
+
return segs.join(" ");
|
|
48434
|
+
}
|
|
48435
|
+
function traceMeshEventStage(stage, ctx, detail) {
|
|
48436
|
+
LOG2.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
48437
|
+
}
|
|
48438
|
+
function traceMeshEventDrop(reason, ctx, detail) {
|
|
48439
|
+
LOG2.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
48440
|
+
}
|
|
48441
|
+
var CAT;
|
|
48442
|
+
var init_mesh_event_trace = __esm2({
|
|
48443
|
+
"src/mesh/mesh-event-trace.ts"() {
|
|
48444
|
+
"use strict";
|
|
48445
|
+
init_logger();
|
|
48446
|
+
CAT = "EvtTrace";
|
|
48447
|
+
}
|
|
48448
|
+
});
|
|
48449
|
+
function taskIsParked(task) {
|
|
48450
|
+
return !!readNonEmptyString(task?.parked?.reason);
|
|
48451
|
+
}
|
|
48452
|
+
function buildParkingRecord(task, reason, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
48453
|
+
return {
|
|
48454
|
+
reason,
|
|
48455
|
+
parkedAt: nowIso,
|
|
48456
|
+
// Mirror the ORIGINAL address. `targetSessionId` also stays on the row
|
|
48457
|
+
// (that is what keeps the row unclaimable), but a later re-target
|
|
48458
|
+
// overwrites it — this copy is what still answers "who was this delta
|
|
48459
|
+
// written for?" after the coordinator has moved it.
|
|
48460
|
+
...readNonEmptyString(task.targetSessionId) ? { targetSessionId: task.targetSessionId } : {},
|
|
48461
|
+
...readNonEmptyString(task.targetNodeId) ? { targetNodeId: task.targetNodeId } : {}
|
|
48462
|
+
};
|
|
48463
|
+
}
|
|
48464
|
+
function parkedAgeMs(task, nowMs = Date.now()) {
|
|
48465
|
+
const parkedAt = readNonEmptyString(task.parked?.parkedAt);
|
|
48466
|
+
if (!parkedAt) return null;
|
|
48467
|
+
const parsed = Date.parse(parkedAt);
|
|
48468
|
+
return Number.isFinite(parsed) ? nowMs - parsed : null;
|
|
48469
|
+
}
|
|
48470
|
+
function parkedTaskRetentionExpired(task, nowMs = Date.now(), retentionMs = PARKED_TASK_RETENTION_MS) {
|
|
48471
|
+
if (!taskIsParked(task)) return false;
|
|
48472
|
+
const age = parkedAgeMs(task, nowMs);
|
|
48473
|
+
return age !== null && age >= retentionMs;
|
|
48474
|
+
}
|
|
48475
|
+
function buildParkedTaskNotice(taskId, targetSessionId, reason) {
|
|
48476
|
+
const addressee = readNonEmptyString(targetSessionId);
|
|
48477
|
+
return `The delta was addressed to session '${addressee || "(unknown)"}' and that pin went stale (${reason || PARK_REASON_PIN_EXPIRED}), so the task is now PARKED \u2014 held for you, deliberately NOT re-homed onto another session. A message written for one session's context becomes a context-free instruction anywhere else, so the daemon will not choose a new addressee for you. The task is claimable by nobody until you act on it, and it is dropped (as a terminal failure, with another notification) if left parked for ${Math.round(PARKED_TASK_RETENTION_MS / 36e5)}h. Four ways out \u2014 inspect it with mesh_view_queue (parked rows are listed under parkedTasks with their original addressee), then either re-target it with mesh_queue_requeue(task_id='${taskId}', target_session_id='<live session>') \u2014 or clear_target_session to let any compatible session take it; REWRITE it first with mesh_queue_requeue(task_id='${taskId}', message='<updated instruction>') if the situation moved on while it waited (the common case: the worker already did the part your delta was about); or cancel it with mesh_queue_cancel(task_id='${taskId}') if it is no longer wanted. Any requeue unparks the task.`;
|
|
48478
|
+
}
|
|
48479
|
+
function notifyCoordinatorOfParkedTaskDropped(meshId, task) {
|
|
48480
|
+
const taskId = task.id;
|
|
48481
|
+
const addressee = readNonEmptyString(task.parked?.targetSessionId);
|
|
48482
|
+
const hours = Math.round(PARKED_TASK_RETENTION_MS / 36e5);
|
|
48483
|
+
const coordinatorMessage = `[System] A PARKED mesh task was dropped after ${hours}h with no coordinator decision.
|
|
48484
|
+
Task ${taskId} was parked because its delta was addressed to session '${addressee || "(unknown)"}' and that pin went stale. It was held \u2014 claimable by nobody \u2014 waiting for you to re-target, rewrite, or cancel it. That never happened, so it is now marked FAILED (${PARK_RETENTION_EXPIRED_REASON}) and any dependent tasks have been unblocked.
|
|
48485
|
+
The instruction it carried was never delivered to anyone. If it still matters, re-enqueue it (mesh_enqueue_task) against a live session; the failed row remains in the queue as the audit record. To avoid this next time, check parkedTasks in mesh_view_queue \u2014 parked rows are surfaced there from the moment they park.`;
|
|
48486
|
+
const nodeLabel = readNonEmptyString(task.targetNodeId) || readNonEmptyString(task.parked?.targetNodeId) || meshId;
|
|
48487
|
+
try {
|
|
48488
|
+
const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
|
|
48489
|
+
const targetCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId);
|
|
48490
|
+
queuePendingMeshCoordinatorEvent({
|
|
48491
|
+
event: "mesh:dispatch_blocked",
|
|
48492
|
+
meshId,
|
|
48493
|
+
nodeLabel,
|
|
48494
|
+
metadataEvent: {
|
|
48495
|
+
source: "mesh_parked_task_retention_expired",
|
|
48496
|
+
taskId,
|
|
48497
|
+
reason: PARK_RETENTION_EXPIRED_REASON,
|
|
48498
|
+
...addressee ? { parkedTargetSessionId: addressee } : {},
|
|
48499
|
+
coordinatorMessage
|
|
48500
|
+
},
|
|
48501
|
+
coordinatorMessage,
|
|
48502
|
+
queuedAt: Date.now(),
|
|
48503
|
+
...targetCoordinatorDaemonId ? { targetCoordinatorDaemonId } : {},
|
|
48504
|
+
...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
|
|
48505
|
+
});
|
|
48506
|
+
} catch (e) {
|
|
48507
|
+
LOG2.warn("MeshQueue", `Failed to surface parked-task retention drop for task ${taskId} (mesh ${meshId}): ${e?.message || e}`);
|
|
48508
|
+
}
|
|
48509
|
+
}
|
|
48510
|
+
function parkExpiredTargetPin(meshId, task, verdict, ttlMs, park) {
|
|
48511
|
+
if (!verdict.expired) return false;
|
|
48512
|
+
const parked = park(meshId, task.id, PARK_REASON_PIN_EXPIRED);
|
|
48513
|
+
if (parked) {
|
|
48514
|
+
noteTargetPinCleared(PARK_REASON_PIN_EXPIRED);
|
|
48515
|
+
traceMeshEventDrop("target_session_pin_expired", {
|
|
48516
|
+
taskId: task.id,
|
|
48517
|
+
sessionId: readNonEmptyString(task.targetSessionId),
|
|
48518
|
+
nodeId: readNonEmptyString(task.targetNodeId),
|
|
48519
|
+
meshId,
|
|
48520
|
+
event: "agent:ready"
|
|
48521
|
+
}, `unproductive ${Math.round((verdict.ageMs ?? 0) / 1e3)}s \u2265 ttl ${Math.round(ttlMs / 1e3)}s \u2192 task PARKED (held for the coordinator, NOT re-homed)`);
|
|
48522
|
+
}
|
|
48523
|
+
return true;
|
|
48524
|
+
}
|
|
48525
|
+
function settleParkedQueueTask(meshId, task, markSkip, sweep) {
|
|
48526
|
+
const swept = sweep(meshId, task.id);
|
|
48527
|
+
if (swept) {
|
|
48528
|
+
notifyCoordinatorOfParkedTaskDropped(meshId, task);
|
|
48529
|
+
return "swept";
|
|
48530
|
+
}
|
|
48531
|
+
markSkip(PARKED_SKIP_REASON);
|
|
48532
|
+
return "held";
|
|
48533
|
+
}
|
|
48534
|
+
function logTaskParked(meshId, taskId, reason, targetSessionId) {
|
|
48535
|
+
LOG2.warn("MeshQueue", `PIN-PARKING: task ${taskId} (mesh ${meshId}) PARKED (${reason})${targetSessionId ? ` \u2014 original addressee session ${targetSessionId}` : ""}; it is claimable by nobody until the coordinator re-targets, edits, or cancels it.`);
|
|
48536
|
+
}
|
|
48537
|
+
var PARKED_TASK_RETENTION_MS;
|
|
48538
|
+
var PARK_REASON_PIN_EXPIRED;
|
|
48539
|
+
var PARKED_SKIP_REASON;
|
|
48540
|
+
var PARK_RETENTION_EXPIRED_REASON;
|
|
48541
|
+
var init_mesh_task_parking = __esm2({
|
|
48542
|
+
"src/mesh/mesh-task-parking.ts"() {
|
|
48543
|
+
"use strict";
|
|
48544
|
+
init_logger();
|
|
48545
|
+
init_mesh_events_utils();
|
|
48546
|
+
init_mesh_events_pending();
|
|
48547
|
+
init_mesh_turn_ledger();
|
|
48548
|
+
init_mesh_event_trace();
|
|
48549
|
+
init_config();
|
|
48550
|
+
PARKED_TASK_RETENTION_MS = 24 * 60 * 6e4;
|
|
48551
|
+
PARK_REASON_PIN_EXPIRED = "target_session_pin_expired_parked";
|
|
48552
|
+
PARKED_SKIP_REASON = "target_session_pin_parked";
|
|
48553
|
+
PARK_RETENTION_EXPIRED_REASON = "parked_task_retention_expired";
|
|
48554
|
+
}
|
|
48555
|
+
});
|
|
48556
|
+
function readPayloadTaskId(entry) {
|
|
48557
|
+
const value = entry.payload?.taskId;
|
|
48558
|
+
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
48559
|
+
}
|
|
48560
|
+
function parseTime(value) {
|
|
48561
|
+
if (!value) return null;
|
|
48562
|
+
const parsed = new Date(value).getTime();
|
|
48563
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
48564
|
+
}
|
|
48565
|
+
function computeMeshTaskStats(meshId, opts) {
|
|
48566
|
+
const queue = getQueue(meshId);
|
|
48567
|
+
const queueById = new Map(queue.map((task) => [task.id, task]));
|
|
48568
|
+
let targetIds;
|
|
48569
|
+
if (opts?.taskIds?.length) {
|
|
48570
|
+
targetIds = [...new Set(opts.taskIds)];
|
|
48571
|
+
} else if (opts?.missionId) {
|
|
48572
|
+
targetIds = queue.filter((task) => task.missionId === opts.missionId).map((task) => task.id);
|
|
48573
|
+
} else {
|
|
48574
|
+
targetIds = queue.map((task) => task.id);
|
|
48575
|
+
}
|
|
48576
|
+
if (targetIds.length === 0) return [];
|
|
48577
|
+
const targetSet = new Set(targetIds);
|
|
48578
|
+
const entries = readLedgerEntries(meshId, { tail: opts?.tail ?? 1e3 });
|
|
48579
|
+
const dispatches = /* @__PURE__ */ new Map();
|
|
48580
|
+
const terminals = /* @__PURE__ */ new Map();
|
|
48581
|
+
for (const entry of entries) {
|
|
48582
|
+
const taskId = readPayloadTaskId(entry);
|
|
48583
|
+
if (!taskId || !targetSet.has(taskId)) continue;
|
|
48584
|
+
if (entry.kind === "task_dispatched") {
|
|
48585
|
+
const existing = dispatches.get(taskId);
|
|
48586
|
+
if (existing) existing.count += 1;
|
|
48587
|
+
else dispatches.set(taskId, { first: entry.timestamp, count: 1 });
|
|
48588
|
+
} else if (entry.kind === "task_completed" || entry.kind === "task_failed") {
|
|
48589
|
+
terminals.set(taskId, { at: entry.timestamp, kind: entry.kind });
|
|
48590
|
+
}
|
|
48591
|
+
}
|
|
48592
|
+
return targetIds.map((taskId) => {
|
|
48593
|
+
const queueEntry = queueById.get(taskId);
|
|
48594
|
+
const dispatch = dispatches.get(taskId);
|
|
48595
|
+
const terminal = terminals.get(taskId);
|
|
48596
|
+
const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
|
|
48597
|
+
const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
|
|
48598
|
+
const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
|
|
48599
|
+
const terminalTime = parseTime(terminal?.at);
|
|
48600
|
+
const stats = {
|
|
48601
|
+
taskId,
|
|
48602
|
+
status,
|
|
48603
|
+
dispatchedAt: dispatch?.first ?? queueEntry?.dispatchTimestamp ?? null,
|
|
48604
|
+
terminalAt: terminal?.at ?? null,
|
|
48605
|
+
terminalKind: terminal?.kind ?? null,
|
|
48606
|
+
durationMs: null,
|
|
48607
|
+
dispatchCount: dispatch?.count ?? 0,
|
|
48608
|
+
requeueCount: queueEntry?.requeueCount ?? 0
|
|
48609
|
+
};
|
|
48610
|
+
if (dispatchTime !== null && terminalTime !== null && terminalTime >= dispatchTime) {
|
|
48611
|
+
stats.durationMs = terminalTime - dispatchTime;
|
|
48612
|
+
} else if (isTerminalStatus) {
|
|
48613
|
+
stats.incompleteEvidence = true;
|
|
48614
|
+
}
|
|
48615
|
+
return stats;
|
|
48616
|
+
});
|
|
48617
|
+
}
|
|
48618
|
+
function computeMeshMissionStats(meshId, missionId) {
|
|
48619
|
+
const tasks = computeMeshTaskStats(meshId, { missionId });
|
|
48620
|
+
const stats = {
|
|
48621
|
+
missionId,
|
|
48622
|
+
taskCount: tasks.length,
|
|
48623
|
+
completed: 0,
|
|
48624
|
+
failed: 0,
|
|
48625
|
+
totalDurationMs: 0,
|
|
48626
|
+
wallClockMs: null,
|
|
48627
|
+
retries: 0,
|
|
48628
|
+
incompleteTaskIds: []
|
|
48629
|
+
};
|
|
48630
|
+
let firstDispatch = null;
|
|
48631
|
+
let lastTerminal = null;
|
|
48632
|
+
for (const task of tasks) {
|
|
48633
|
+
if (task.status === "completed") stats.completed += 1;
|
|
48634
|
+
else if (task.status === "failed") stats.failed += 1;
|
|
48635
|
+
stats.retries += task.requeueCount;
|
|
48636
|
+
if (task.incompleteEvidence) {
|
|
48637
|
+
stats.incompleteTaskIds.push(task.taskId);
|
|
48638
|
+
continue;
|
|
48639
|
+
}
|
|
48640
|
+
if (task.durationMs !== null) stats.totalDurationMs += task.durationMs;
|
|
48641
|
+
const dispatchTime = parseTime(task.dispatchedAt);
|
|
48642
|
+
const terminalTime = parseTime(task.terminalAt);
|
|
48643
|
+
if (dispatchTime !== null && (firstDispatch === null || dispatchTime < firstDispatch)) firstDispatch = dispatchTime;
|
|
48644
|
+
if (terminalTime !== null && (lastTerminal === null || terminalTime > lastTerminal)) lastTerminal = terminalTime;
|
|
48645
|
+
}
|
|
48646
|
+
if (firstDispatch !== null && lastTerminal !== null && lastTerminal >= firstDispatch) {
|
|
48647
|
+
stats.wallClockMs = lastTerminal - firstDispatch;
|
|
48648
|
+
}
|
|
48649
|
+
return stats;
|
|
48650
|
+
}
|
|
48651
|
+
var init_mesh_task_stats = __esm2({
|
|
48652
|
+
"src/mesh/mesh-task-stats.ts"() {
|
|
48653
|
+
"use strict";
|
|
48654
|
+
init_mesh_ledger();
|
|
48655
|
+
init_mesh_work_queue();
|
|
48656
|
+
}
|
|
48657
|
+
});
|
|
48473
48658
|
var mesh_missions_exports = {};
|
|
48474
48659
|
__export2(mesh_missions_exports, {
|
|
48475
48660
|
COMPACT_STATUS_GOAL_PREVIEW_MAX: () => COMPACT_STATUS_GOAL_PREVIEW_MAX,
|
|
@@ -48855,11 +49040,12 @@ ${blocks.join("\n\n")}`;
|
|
|
48855
49040
|
describeTaskDependencyState: () => describeTaskDependencyState,
|
|
48856
49041
|
enqueueTask: () => enqueueTask,
|
|
48857
49042
|
enqueueTaskGraph: () => enqueueTaskGraph,
|
|
48858
|
-
|
|
49043
|
+
failRetentionExpiredParkedTask: () => failRetentionExpiredParkedTask,
|
|
48859
49044
|
formatMeshTaskModeViolations: () => formatMeshTaskModeViolations,
|
|
48860
49045
|
getActiveDirectDispatches: () => getActiveDirectDispatches,
|
|
48861
49046
|
getMeshQueueRevision: () => getMeshQueueRevision,
|
|
48862
49047
|
getMeshQueueStats: () => getMeshQueueStats,
|
|
49048
|
+
getParkedTasks: () => getParkedTasks,
|
|
48863
49049
|
getQueue: () => getQueue,
|
|
48864
49050
|
hasPendingDependents: () => hasPendingDependents,
|
|
48865
49051
|
insertDirectDispatch: () => insertDirectDispatch,
|
|
@@ -48871,6 +49057,7 @@ ${blocks.join("\n\n")}`;
|
|
|
48871
49057
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
48872
49058
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
48873
49059
|
normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
|
|
49060
|
+
parkTaskTargetPin: () => parkTaskTargetPin,
|
|
48874
49061
|
reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
|
|
48875
49062
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
48876
49063
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
@@ -49352,6 +49539,7 @@ ${blocks.join("\n\n")}`;
|
|
|
49352
49539
|
entry.status = "cancelled";
|
|
49353
49540
|
entry.cancelledAt = now;
|
|
49354
49541
|
if (opts?.reason) entry.cancelReason = opts.reason;
|
|
49542
|
+
delete entry.parked;
|
|
49355
49543
|
delete entry.assignedNodeId;
|
|
49356
49544
|
delete entry.assignedSessionId;
|
|
49357
49545
|
delete entry.assignedProviderType;
|
|
@@ -49384,6 +49572,12 @@ ${blocks.join("\n\n")}`;
|
|
|
49384
49572
|
if (value) lastCancelledTaskAssignment.delete(key2);
|
|
49385
49573
|
return value;
|
|
49386
49574
|
}
|
|
49575
|
+
function applyRequeueMessageEdit(entry, message) {
|
|
49576
|
+
if (typeof message !== "string") return;
|
|
49577
|
+
const next = message.trim();
|
|
49578
|
+
if (!next || next === entry.message) return;
|
|
49579
|
+
entry.message = next;
|
|
49580
|
+
}
|
|
49387
49581
|
function requeueTask(meshId, taskId, opts) {
|
|
49388
49582
|
requireMeshHostQueueOwner(opts);
|
|
49389
49583
|
const result = withQueueLock(meshId, () => {
|
|
@@ -49418,6 +49612,8 @@ ${blocks.join("\n\n")}`;
|
|
|
49418
49612
|
entry.requeuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49419
49613
|
entry.dispatchFailureCount = dispatchFailures;
|
|
49420
49614
|
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
49615
|
+
applyRequeueMessageEdit(entry, opts?.message);
|
|
49616
|
+
delete entry.parked;
|
|
49421
49617
|
const backoffMs = DISPATCH_RETRY_BACKOFF_BASE_MS * Math.pow(2, dispatchFailures - 1);
|
|
49422
49618
|
entry.notBefore = resolveNotBefore(Math.min(backoffMs, DISPATCH_RETRY_BACKOFF_MAX_MS));
|
|
49423
49619
|
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
@@ -49446,6 +49642,8 @@ ${blocks.join("\n\n")}`;
|
|
|
49446
49642
|
entry.requeuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49447
49643
|
entry.requeueCount = currentCount + 1;
|
|
49448
49644
|
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
49645
|
+
applyRequeueMessageEdit(entry, opts?.message);
|
|
49646
|
+
delete entry.parked;
|
|
49449
49647
|
const notBefore = resolveNotBefore(opts?.notBefore);
|
|
49450
49648
|
if (notBefore) entry.notBefore = notBefore;
|
|
49451
49649
|
else delete entry.notBefore;
|
|
@@ -49455,24 +49653,46 @@ ${blocks.join("\n\n")}`;
|
|
|
49455
49653
|
if (result?.missionAffected) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
|
|
49456
49654
|
return result ? result.entry : null;
|
|
49457
49655
|
}
|
|
49458
|
-
function
|
|
49656
|
+
function parkTaskTargetPin(meshId, taskId, opts) {
|
|
49459
49657
|
requireMeshHostQueueOwner(opts);
|
|
49460
49658
|
return withQueueLock(meshId, () => {
|
|
49461
49659
|
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
49462
49660
|
if (!entry) return null;
|
|
49463
49661
|
if (entry.status !== "pending") return null;
|
|
49464
49662
|
if (!entry.targetSessionId && !entry.targetNodeId) return null;
|
|
49465
|
-
|
|
49466
|
-
const
|
|
49467
|
-
|
|
49468
|
-
|
|
49469
|
-
entry.updatedAt =
|
|
49470
|
-
|
|
49663
|
+
if (taskIsParked(entry)) return null;
|
|
49664
|
+
const reason = opts?.reason || PARK_REASON_PIN_EXPIRED;
|
|
49665
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
49666
|
+
entry.parked = buildParkingRecord(entry, reason, now);
|
|
49667
|
+
entry.updatedAt = now;
|
|
49668
|
+
entry.requeueReason = reason;
|
|
49471
49669
|
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
49472
|
-
|
|
49670
|
+
logTaskParked(meshId, taskId, reason, entry.targetSessionId);
|
|
49473
49671
|
return entry;
|
|
49474
49672
|
});
|
|
49475
49673
|
}
|
|
49674
|
+
function failRetentionExpiredParkedTask(meshId, taskId, opts) {
|
|
49675
|
+
requireMeshHostQueueOwner(opts);
|
|
49676
|
+
const result = withQueueLock(meshId, () => {
|
|
49677
|
+
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
49678
|
+
if (!entry) return null;
|
|
49679
|
+
if (entry.status !== "pending" || !taskIsParked(entry)) return null;
|
|
49680
|
+
if (!parkedTaskRetentionExpired(entry, Date.now(), opts?.retentionMs)) return null;
|
|
49681
|
+
const hours = Math.round((opts?.retentionMs ?? PARKED_TASK_RETENTION_MS) / 36e5);
|
|
49682
|
+
entry.status = "failed";
|
|
49683
|
+
entry.cancelReason = `${PARK_RETENTION_EXPIRED_REASON}: parked for over ${hours}h (addressed to session ${entry.parked?.targetSessionId || "unknown"}) with no coordinator decision`;
|
|
49684
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49685
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
49686
|
+
const cascaded = propagateDependencyFailure(meshId, taskId);
|
|
49687
|
+
LOG2.warn("MeshQueue", `PIN-PARKING retention: task ${taskId} (mesh ${meshId}) stayed parked past ${hours}h with no coordinator decision; failed it (dependents unblocked). This is reported to the coordinator, never a silent drop.`);
|
|
49688
|
+
return { entry, cascaded, missionAffected: true };
|
|
49689
|
+
});
|
|
49690
|
+
if (result?.missionAffected) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
|
|
49691
|
+
return result ? result.entry : null;
|
|
49692
|
+
}
|
|
49693
|
+
function getParkedTasks(meshId) {
|
|
49694
|
+
return getQueue(meshId, { status: ["pending"] }).filter(taskIsParked);
|
|
49695
|
+
}
|
|
49476
49696
|
function reclaimStrandedAssignedTask(meshId, taskId, opts) {
|
|
49477
49697
|
requireMeshHostQueueOwner(opts);
|
|
49478
49698
|
const result = withQueueLock(meshId, () => {
|
|
@@ -49700,6 +49920,7 @@ ${blocks.join("\n\n")}`;
|
|
|
49700
49920
|
init_mesh_graph_derived_failure();
|
|
49701
49921
|
init_dist();
|
|
49702
49922
|
init_mesh_task_mode_guardrail();
|
|
49923
|
+
init_mesh_task_parking();
|
|
49703
49924
|
init_mesh_task_mode_guardrail();
|
|
49704
49925
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
49705
49926
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
@@ -52571,6 +52792,7 @@ ${blocks.join("\n\n")}`;
|
|
|
52571
52792
|
init_config();
|
|
52572
52793
|
init_mesh_ledger();
|
|
52573
52794
|
init_mesh_work_queue();
|
|
52795
|
+
init_mesh_task_parking();
|
|
52574
52796
|
init_mesh_graph_schema();
|
|
52575
52797
|
init_mesh_graph_store();
|
|
52576
52798
|
init_slot_model_enforcement();
|
|
@@ -53539,7 +53761,8 @@ ${blocks.join("\n\n")}`;
|
|
|
53539
53761
|
};
|
|
53540
53762
|
const allowedTaskDifficulties = opts?.allowedTaskDifficulties;
|
|
53541
53763
|
const difficultyAllows = (candidate) => !allowedTaskDifficulties || candidate.difficulty === "freeform" || !candidate.difficulty || allowedTaskDifficulties.includes(candidate.difficulty);
|
|
53542
|
-
const
|
|
53764
|
+
const notParked = (candidate) => !taskIsParked(candidate);
|
|
53765
|
+
const entry = candidates.find((candidate) => nodeSatisfiesRequiredTags(candidate.requiredTags, capabilityTags) && dependenciesSatisfied(candidate) && notBeforeReady(candidate) && notParked(candidate) && convergenceAllows(candidate) && targetMatches(candidate) && difficultyAllows(candidate) && parallelCapsAllow(candidate) && nodeConflictAllows(candidate));
|
|
53543
53766
|
if (!entry) return null;
|
|
53544
53767
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
53545
53768
|
entry.status = "assigned";
|
|
@@ -55255,6 +55478,7 @@ ${blocks.join("\n\n")}`;
|
|
|
55255
55478
|
compactLedger: () => compactLedger,
|
|
55256
55479
|
enforceAllLedgerRotationCaps: () => enforceAllLedgerRotationCaps,
|
|
55257
55480
|
enforceLedgerRotationCap: () => enforceLedgerRotationCap,
|
|
55481
|
+
extractJsonObjectFromSummary: () => extractJsonObjectFromSummary,
|
|
55258
55482
|
getLedgerDir: () => getLedgerDir2,
|
|
55259
55483
|
getLedgerSummary: () => getLedgerSummary,
|
|
55260
55484
|
getSessionRecoveryContext: () => getSessionRecoveryContext,
|
|
@@ -59520,6 +59744,7 @@ ${rendered}`, "utf-8");
|
|
|
59520
59744
|
void opts.registerNode;
|
|
59521
59745
|
return {
|
|
59522
59746
|
nowMs: () => Date.now(),
|
|
59747
|
+
resolveBaseRevision: (req) => defaultResolveBaseRevision(req),
|
|
59523
59748
|
createWorktree: (req) => defaultCreateWorktree(req),
|
|
59524
59749
|
findOwnedWorktree: (req) => defaultFindOwnedWorktree(req),
|
|
59525
59750
|
inspectWorktree: (req) => defaultInspectWorktree(req),
|
|
@@ -59565,6 +59790,40 @@ ${rendered}`, "utf-8");
|
|
|
59565
59790
|
env: gitChildEnv()
|
|
59566
59791
|
});
|
|
59567
59792
|
}
|
|
59793
|
+
async function defaultResolveBaseRevision(req) {
|
|
59794
|
+
let repoRoot;
|
|
59795
|
+
try {
|
|
59796
|
+
({ repoRoot } = resolveSourceRepo(req.meshId, req.sourceNodeId));
|
|
59797
|
+
} catch {
|
|
59798
|
+
return void 0;
|
|
59799
|
+
}
|
|
59800
|
+
try {
|
|
59801
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
59802
|
+
cwd: repoRoot,
|
|
59803
|
+
encoding: "utf8",
|
|
59804
|
+
timeout: GIT_TIMEOUT_MS3,
|
|
59805
|
+
maxBuffer: GIT_MAX_BUFFER2,
|
|
59806
|
+
windowsHide: true,
|
|
59807
|
+
env: gitChildEnv()
|
|
59808
|
+
});
|
|
59809
|
+
const branch = stdout.trim();
|
|
59810
|
+
if (branch && branch !== "HEAD") return branch;
|
|
59811
|
+
} catch {
|
|
59812
|
+
}
|
|
59813
|
+
try {
|
|
59814
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], {
|
|
59815
|
+
cwd: repoRoot,
|
|
59816
|
+
encoding: "utf8",
|
|
59817
|
+
timeout: GIT_TIMEOUT_MS3,
|
|
59818
|
+
maxBuffer: GIT_MAX_BUFFER2,
|
|
59819
|
+
windowsHide: true,
|
|
59820
|
+
env: gitChildEnv()
|
|
59821
|
+
});
|
|
59822
|
+
return stdout.trim() || void 0;
|
|
59823
|
+
} catch {
|
|
59824
|
+
return void 0;
|
|
59825
|
+
}
|
|
59826
|
+
}
|
|
59568
59827
|
async function defaultCreateWorktree(req) {
|
|
59569
59828
|
const existing = await defaultFindOwnedWorktree({
|
|
59570
59829
|
meshId: req.meshId,
|
|
@@ -59910,8 +60169,26 @@ ${rendered}`, "utf-8");
|
|
|
59910
60169
|
}
|
|
59911
60170
|
return compensateClaimedIntent({ ...intent, ownerTag }, ports, generation);
|
|
59912
60171
|
}
|
|
59913
|
-
|
|
59914
|
-
|
|
60172
|
+
let baseRevision = intent.baseRevision;
|
|
60173
|
+
if (intent.sagaState === "declared" && !baseRevision) {
|
|
60174
|
+
const derived = await ports.resolveBaseRevision({
|
|
60175
|
+
meshId: intent.meshId,
|
|
60176
|
+
graphId,
|
|
60177
|
+
workspaceRef,
|
|
60178
|
+
sourceNodeId: intent.sourceNodeId
|
|
60179
|
+
}).catch(() => void 0);
|
|
60180
|
+
if (derived) {
|
|
60181
|
+
store.transaction(() => {
|
|
60182
|
+
store.graphStore().patchWorkspaceIntent(graphId, workspaceRef, {
|
|
60183
|
+
baseRevision: derived
|
|
60184
|
+
}, nowIso, { leaseGeneration: generation });
|
|
60185
|
+
});
|
|
60186
|
+
baseRevision = derived;
|
|
60187
|
+
LOG2.info("MeshGraphWorkspace", `derived base revision '${derived}' for ${graphId}/${workspaceRef} from source node ${intent.sourceNodeId ?? "base"}`);
|
|
60188
|
+
} else {
|
|
60189
|
+
LOG2.debug("MeshGraphWorkspace", `${graphId}/${workspaceRef} stays declared: no base_revision and none derivable from source node ${intent.sourceNodeId ?? "(none declared)"}`);
|
|
60190
|
+
return { graphId, workspaceRef, sagaState: "declared", action: "skipped_no_base" };
|
|
60191
|
+
}
|
|
59915
60192
|
}
|
|
59916
60193
|
store.transaction(() => {
|
|
59917
60194
|
store.graphStore().patchWorkspaceIntent(graphId, workspaceRef, {
|
|
@@ -59919,7 +60196,7 @@ ${rendered}`, "utf-8");
|
|
|
59919
60196
|
ownerTag
|
|
59920
60197
|
}, nowIso, { leaseGeneration: generation });
|
|
59921
60198
|
});
|
|
59922
|
-
return prepareClaimedIntent({ ...intent, sagaState: "preparing", ownerTag }, ports, generation, faults);
|
|
60199
|
+
return prepareClaimedIntent({ ...intent, baseRevision, sagaState: "preparing", ownerTag }, ports, generation, faults);
|
|
59923
60200
|
}
|
|
59924
60201
|
async function prepareClaimedIntent(intent, ports, generation, faults) {
|
|
59925
60202
|
const store = MeshRuntimeStore.getInstance();
|
|
@@ -65073,36 +65350,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65073
65350
|
init_chat_history();
|
|
65074
65351
|
}
|
|
65075
65352
|
});
|
|
65076
|
-
function s(v) {
|
|
65077
|
-
return typeof v === "string" && v.trim() ? v.trim() : "";
|
|
65078
|
-
}
|
|
65079
|
-
function meshEventTraceKey(ctx) {
|
|
65080
|
-
const segs = [`task=${s(ctx.taskId) || "-"}`];
|
|
65081
|
-
const eventId = s(ctx.eventId);
|
|
65082
|
-
if (eventId) segs.push(`evt=${eventId}`);
|
|
65083
|
-
segs.push(`sess=${s(ctx.sessionId) || "-"}`);
|
|
65084
|
-
const nodeId = s(ctx.nodeId);
|
|
65085
|
-
if (nodeId) segs.push(`node=${nodeId}`);
|
|
65086
|
-
const meshId = s(ctx.meshId);
|
|
65087
|
-
if (meshId) segs.push(`mesh=${meshId}`);
|
|
65088
|
-
const event = s(ctx.event);
|
|
65089
|
-
if (event) segs.push(`event=${event}`);
|
|
65090
|
-
return segs.join(" ");
|
|
65091
|
-
}
|
|
65092
|
-
function traceMeshEventStage(stage, ctx, detail) {
|
|
65093
|
-
LOG2.info(CAT, `[stage:${stage}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
65094
|
-
}
|
|
65095
|
-
function traceMeshEventDrop(reason, ctx, detail) {
|
|
65096
|
-
LOG2.warn(CAT, `[drop:${reason}] ${meshEventTraceKey(ctx)}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
65097
|
-
}
|
|
65098
|
-
var CAT;
|
|
65099
|
-
var init_mesh_event_trace = __esm2({
|
|
65100
|
-
"src/mesh/mesh-event-trace.ts"() {
|
|
65101
|
-
"use strict";
|
|
65102
|
-
init_logger();
|
|
65103
|
-
CAT = "EvtTrace";
|
|
65104
|
-
}
|
|
65105
|
-
});
|
|
65106
65353
|
function isAnthropicProvider(providerType) {
|
|
65107
65354
|
const p = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
|
|
65108
65355
|
return p.length > 0 && ANTHROPIC_PROVIDER_TYPES.has(p);
|
|
@@ -65577,6 +65824,43 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65577
65824
|
const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
|
|
65578
65825
|
return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
|
|
65579
65826
|
}
|
|
65827
|
+
function resolveTargetPinTtlVerdict(components, task, nowMs = Date.now()) {
|
|
65828
|
+
const wallAgeMs = targetPinAgeMs(task, nowMs);
|
|
65829
|
+
if (wallAgeMs === null) return { expired: false, ageMs: null, suspended: false };
|
|
65830
|
+
const targetSessionId = readNonEmptyString(task.targetSessionId);
|
|
65831
|
+
const key2 = `${task.meshId}::${task.id}`;
|
|
65832
|
+
const prior = targetPinGeneratingCreditMs.get(key2);
|
|
65833
|
+
const generating = !!targetSessionId && resolveSessionBusyVerdict(components, targetSessionId) === "GENERATING";
|
|
65834
|
+
let creditMs = prior?.creditMs ?? 0;
|
|
65835
|
+
if (generating && prior) {
|
|
65836
|
+
creditMs += Math.max(0, nowMs - prior.lastSeenMs);
|
|
65837
|
+
}
|
|
65838
|
+
targetPinGeneratingCreditMs.set(key2, { creditMs, lastSeenMs: nowMs });
|
|
65839
|
+
const unproductiveAgeMs = Math.max(0, wallAgeMs - creditMs);
|
|
65840
|
+
return {
|
|
65841
|
+
// A tick on which the addressee is OBSERVABLY GENERATING never expires the pin,
|
|
65842
|
+
// independently of the accumulated credit.
|
|
65843
|
+
//
|
|
65844
|
+
// The credit ledger alone is not sufficient here, and the difference is the
|
|
65845
|
+
// whole live defect. Credit only accrues from the SECOND observation onward
|
|
65846
|
+
// (the first has no prior interval to bank), and it is in-memory — so a pin
|
|
65847
|
+
// that crossed the wall-clock TTL while the daemon was not watching, or before
|
|
65848
|
+
// a restart, would arrive at its very first post-restart observation with zero
|
|
65849
|
+
// credit and expire on the spot, while the session it is addressed to is
|
|
65850
|
+
// visibly mid-turn. That is exactly the observed failure (`unclaimed 902s`
|
|
65851
|
+
// logged on an `agent:ready`, i.e. at the end of real work) reproduced by a
|
|
65852
|
+
// different route.
|
|
65853
|
+
//
|
|
65854
|
+
// Gating on the live verdict makes "is the addressee working right now?" the
|
|
65855
|
+
// decisive question and leaves the credit ledger as what it should be: an
|
|
65856
|
+
// optimisation that stops intermittent work from silently burning the budget.
|
|
65857
|
+
// It cannot make a pin immortal — the verdict is re-evaluated every tick from
|
|
65858
|
+
// live state and only a LOCAL, observably-generating session can produce it.
|
|
65859
|
+
expired: !generating && unproductiveAgeMs >= TARGET_SESSION_PIN_TTL_MS,
|
|
65860
|
+
ageMs: unproductiveAgeMs,
|
|
65861
|
+
suspended: generating
|
|
65862
|
+
};
|
|
65863
|
+
}
|
|
65580
65864
|
function resolveDeadTargetVerdict(components, meshId, mesh, task) {
|
|
65581
65865
|
const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
|
|
65582
65866
|
const targetSessionId = readNonEmptyString(task.targetSessionId);
|
|
@@ -65652,6 +65936,21 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65652
65936
|
summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
|
|
65653
65937
|
nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
|
|
65654
65938
|
};
|
|
65939
|
+
if (reason === PARKED_SKIP_REASON) {
|
|
65940
|
+
const base = "it was pinned to a specific session, that pin went stale, and the task is now PARKED \u2014 deliberately held for you rather than re-homed onto another session, because a delta written for one session's context becomes a context-free instruction anywhere else";
|
|
65941
|
+
if (evidence === "consumed") return {
|
|
65942
|
+
summary: `${base}. The delivery record shows the addressed session DID receive and start acting on this message, so the parked row is a bookkeeping remnant, not a lost delta`,
|
|
65943
|
+
nextAction: "Do NOT re-send it. Confirm with mesh_read_chat / mesh_read_terminal that the work is under way, then clear the park with mesh_queue_cancel (or requeue it only if you genuinely want it run again)."
|
|
65944
|
+
};
|
|
65945
|
+
if (evidence === "delivered") return {
|
|
65946
|
+
summary: `${base}. The message WAS handed to that session's transport but no turn start was echoed, so whether it acted on it is unconfirmed`,
|
|
65947
|
+
nextAction: "Check the session (mesh_read_chat / mesh_read_terminal) before acting. If it is already handling it, cancel the parked row; if not, mesh_queue_requeue with target_session_id=<live session> \u2014 and pass message=<rewritten instruction> if the situation moved on while it waited."
|
|
65948
|
+
};
|
|
65949
|
+
return {
|
|
65950
|
+
summary: `${base}. No delivery to that session was ever recorded, so the message did not reach it and the worker is still acting on its previous instructions`,
|
|
65951
|
+
nextAction: "Re-target it with mesh_queue_requeue(target_session_id=<live session>), or drop the pin with clear_target_session to let any compatible session take it. Re-read the work produced meanwhile and pass message=<rewritten instruction> if the delta is now partly stale; mesh_queue_cancel if it is moot."
|
|
65952
|
+
};
|
|
65953
|
+
}
|
|
65655
65954
|
if (reason === "target_session_pin_expired") {
|
|
65656
65955
|
if (evidence === "consumed") return {
|
|
65657
65956
|
summary: "it was pinned to a specific session and the pin TTL expired before the queue row was claimed \u2014 but the delivery record shows this session DID receive and start acting on the message (a turn was started for it), so the queue row lagging is a bookkeeping gap, not a lost delta",
|
|
@@ -65698,11 +65997,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65698
65997
|
const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
|
|
65699
65998
|
const targetCoordinatorSessionId = readNonEmptyString(task?.sourceCoordinatorSessionId);
|
|
65700
65999
|
const nodeLabel = readNonEmptyString(nodeId) || readNonEmptyString(task?.targetNodeId);
|
|
65701
|
-
const evidence = reason === "target_session_pin_expired" ? resolveTaskDeliveryEvidence(meshId, taskId) : void 0;
|
|
66000
|
+
const evidence = reason === "target_session_pin_expired" || reason === PARKED_SKIP_REASON ? resolveTaskDeliveryEvidence(meshId, taskId) : void 0;
|
|
65702
66001
|
const { summary, nextAction } = actionableSkipGuidance(reason, evidence);
|
|
65703
66002
|
const providerAvailabilityResult = reason.startsWith("provider") || reason === "missing_provider_priority";
|
|
65704
66003
|
const reachabilityResult = reason.startsWith("remote_auto_launch");
|
|
65705
|
-
const closing = reason === "target_session_pin_expired" ? "The stale pin has already been cleared, so the task is now claimable by any compatible session \u2014 the action above is about the session it was originally addressed to." : providerAvailabilityResult ? "This result needs action if it persists: a later provider-status refresh or an already-starting usable session can clear it, but a genuinely missing, disabled, or misconfigured provider will keep the task pending until you fix that configuration." : reachabilityResult ? "This result needs action if it persists: the node reconnecting (or re-registering its daemon id) clears it on its own, but a node that stays unreachable will keep the task pending until you bring it back or re-target the task." : "This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.";
|
|
66004
|
+
const closing = reason === PARKED_SKIP_REASON ? `This task is claimable by NOBODY until you act on it \u2014 no session will pick it up and no timer will re-home it. It is held for ${Math.round(PARKED_TASK_RETENTION_MS / 36e5)}h and then failed (with another notification), so it is never silently discarded. Parked rows are listed under parkedTasks in mesh_view_queue, and any mesh_queue_requeue unparks it \u2014 including one that only rewrites its message.` : reason === "target_session_pin_expired" ? "The stale pin has already been cleared, so the task is now claimable by any compatible session \u2014 the action above is about the session it was originally addressed to." : providerAvailabilityResult ? "This result needs action if it persists: a later provider-status refresh or an already-starting usable session can clear it, but a genuinely missing, disabled, or misconfigured provider will keep the task pending until you fix that configuration." : reachabilityResult ? "This result needs action if it persists: the node reconnecting (or re-registering its daemon id) clears it on its own, but a node that stays unreachable will keep the task pending until you bring it back or re-target the task." : "This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.";
|
|
65706
66005
|
const coordinatorMessage = `[System] A queued mesh task${nodeLabel ? ` for node ${nodeLabel}` : ""} is not being dispatched because ${summary}. ${nextAction} ${closing}`;
|
|
65707
66006
|
try {
|
|
65708
66007
|
queuePendingMeshCoordinatorEvent({
|
|
@@ -65731,6 +66030,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65731
66030
|
var lastActionableSkipNotified;
|
|
65732
66031
|
var DEAD_TARGET_GRACE_MS;
|
|
65733
66032
|
var TARGET_SESSION_PIN_TTL_MS;
|
|
66033
|
+
var targetPinGeneratingCreditMs;
|
|
65734
66034
|
var init_mesh_skip_notify = __esm2({
|
|
65735
66035
|
"src/mesh/mesh-skip-notify.ts"() {
|
|
65736
66036
|
"use strict";
|
|
@@ -65746,6 +66046,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65746
66046
|
init_slot_model_enforcement();
|
|
65747
66047
|
init_mesh_queue_assignment();
|
|
65748
66048
|
init_mesh_queue_observability();
|
|
66049
|
+
init_mesh_task_parking();
|
|
65749
66050
|
ACTIONABLE_SKIP_REASON_PREFIXES = [
|
|
65750
66051
|
"target_node_id_unmatched",
|
|
65751
66052
|
"no_node_satisfies_required_tags",
|
|
@@ -65768,6 +66069,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65768
66069
|
// premise the delta was meant to correct. The coordinator must know its
|
|
65769
66070
|
// addressed message lost its address.
|
|
65770
66071
|
"target_session_pin_expired",
|
|
66072
|
+
// PIN-PARKING: the reason above's successor. The pin no longer CLEARS on expiry
|
|
66073
|
+
// (that silently re-homed a context-bound delta onto an arbitrary session); the
|
|
66074
|
+
// task PARKS instead — held, still addressed, claimable by nobody. That state is
|
|
66075
|
+
// by construction actionable and terminal-until-touched: nothing in the daemon
|
|
66076
|
+
// will ever move a parked task, so if the coordinator is not told, the work is
|
|
66077
|
+
// lost exactly as surely as if it had been dropped. Both reasons stay listed —
|
|
66078
|
+
// the old one so a version-skewed daemon's rows still page.
|
|
66079
|
+
PARKED_SKIP_REASON,
|
|
65771
66080
|
// SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
|
|
65772
66081
|
// Permanent — no amount of waiting produces a slot, so the coordinator must
|
|
65773
66082
|
// re-drive (adjust difficulty, target another node, ask the owner). Its
|
|
@@ -65786,6 +66095,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
65786
66095
|
lastActionableSkipNotified = /* @__PURE__ */ new Map();
|
|
65787
66096
|
DEAD_TARGET_GRACE_MS = 6e4;
|
|
65788
66097
|
TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
|
|
66098
|
+
targetPinGeneratingCreditMs = /* @__PURE__ */ new Map();
|
|
65789
66099
|
}
|
|
65790
66100
|
});
|
|
65791
66101
|
function nodesForMesh(meshId) {
|
|
@@ -66194,6 +66504,173 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66194
66504
|
INTRA_NODE_LOSERS_MAX = 2;
|
|
66195
66505
|
}
|
|
66196
66506
|
});
|
|
66507
|
+
function localCoordinatorDaemonId() {
|
|
66508
|
+
return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
|
|
66509
|
+
}
|
|
66510
|
+
function isIdleSessionState2(state) {
|
|
66511
|
+
const status = readNonEmptyString(state?.status).toLowerCase();
|
|
66512
|
+
if (TERMINAL_SESSION_STATUSES.includes(status)) return false;
|
|
66513
|
+
return status === "idle" || state?.activeChat?.status === "waiting_input";
|
|
66514
|
+
}
|
|
66515
|
+
function sweepAutoLaunchOrphanSessions(components, meshId) {
|
|
66516
|
+
let sessions;
|
|
66517
|
+
try {
|
|
66518
|
+
sessions = components.instanceManager?.getByCategory?.("cli") || [];
|
|
66519
|
+
} catch {
|
|
66520
|
+
return;
|
|
66521
|
+
}
|
|
66522
|
+
if (!sessions.length) return;
|
|
66523
|
+
let queue;
|
|
66524
|
+
try {
|
|
66525
|
+
queue = getQueue(meshId);
|
|
66526
|
+
} catch {
|
|
66527
|
+
return;
|
|
66528
|
+
}
|
|
66529
|
+
const byId = new Map(queue.map((t) => [t.id, t]));
|
|
66530
|
+
for (const inst of sessions) {
|
|
66531
|
+
let state;
|
|
66532
|
+
try {
|
|
66533
|
+
state = inst.getState();
|
|
66534
|
+
} catch {
|
|
66535
|
+
continue;
|
|
66536
|
+
}
|
|
66537
|
+
const settings = state?.settings || {};
|
|
66538
|
+
if (readNonEmptyString(settings.meshNodeFor) !== meshId) continue;
|
|
66539
|
+
const originTaskId = readNonEmptyString(settings.autoLaunchedForQueueTaskId);
|
|
66540
|
+
if (!originTaskId) continue;
|
|
66541
|
+
const sessionId = readNonEmptyString(state?.instanceId);
|
|
66542
|
+
if (!sessionId) continue;
|
|
66543
|
+
if (!isIdleSessionState2(state)) continue;
|
|
66544
|
+
if (sessionHasActiveAssignment(meshId, sessionId)) continue;
|
|
66545
|
+
const originTask = byId.get(originTaskId);
|
|
66546
|
+
const assignedElsewhere = !!originTask && originTask.status === "assigned" && !!readNonEmptyString(originTask.assignedSessionId) && !sessionIdsEquivalent(readNonEmptyString(originTask.assignedSessionId), sessionId);
|
|
66547
|
+
const originGone = !originTask || originTask.status !== "pending" && originTask.status !== "assigned";
|
|
66548
|
+
const dedupKey = `${meshId}::${sessionId}`;
|
|
66549
|
+
if (!assignedElsewhere && !originGone) {
|
|
66550
|
+
autoLaunchOrphanFirstSeenAtMs.delete(dedupKey);
|
|
66551
|
+
continue;
|
|
66552
|
+
}
|
|
66553
|
+
const nowMs = Date.now();
|
|
66554
|
+
const firstSeenAtMs = autoLaunchOrphanFirstSeenAtMs.get(dedupKey);
|
|
66555
|
+
if (firstSeenAtMs === void 0) {
|
|
66556
|
+
autoLaunchOrphanFirstSeenAtMs.set(dedupKey, nowMs);
|
|
66557
|
+
if (autoLaunchOrphanFirstSeenAtMs.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
|
|
66558
|
+
const oldest = autoLaunchOrphanFirstSeenAtMs.keys().next().value;
|
|
66559
|
+
if (oldest !== void 0) autoLaunchOrphanFirstSeenAtMs.delete(oldest);
|
|
66560
|
+
}
|
|
66561
|
+
continue;
|
|
66562
|
+
}
|
|
66563
|
+
if (nowMs - firstSeenAtMs < AUTO_LAUNCH_ORPHAN_GRACE_MS) continue;
|
|
66564
|
+
if (autoLaunchOrphanNotified.has(dedupKey)) continue;
|
|
66565
|
+
autoLaunchOrphanNotified.add(dedupKey);
|
|
66566
|
+
if (autoLaunchOrphanNotified.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
|
|
66567
|
+
const oldest = autoLaunchOrphanNotified.values().next().value;
|
|
66568
|
+
if (oldest !== void 0) autoLaunchOrphanNotified.delete(oldest);
|
|
66569
|
+
}
|
|
66570
|
+
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
66571
|
+
const detail = assignedElsewhere ? `task ${originTaskId} is assigned to a different session (${readNonEmptyString(originTask.assignedSessionId)})` : `task ${originTaskId} is no longer active (${originTask ? originTask.status : "absent from the queue"})`;
|
|
66572
|
+
recordAutoLaunchEvent(meshId, {
|
|
66573
|
+
phase: "skipped",
|
|
66574
|
+
taskId: originTaskId,
|
|
66575
|
+
reason: "auto_launch_orphan_session_detected",
|
|
66576
|
+
nodeId,
|
|
66577
|
+
sessionId
|
|
66578
|
+
});
|
|
66579
|
+
LOG2.warn("MeshQueue", `AUTOLAUNCH-ORPHAN-SWEEP: session ${sessionId}${nodeId ? ` on node ${nodeId}` : ""} (mesh ${meshId}) was auto-launched for task ${originTaskId} but ${detail}; it is idle with no work and will not self-recover.`);
|
|
66580
|
+
try {
|
|
66581
|
+
queuePendingMeshCoordinatorEvent({
|
|
66582
|
+
event: "mesh:dispatch_blocked",
|
|
66583
|
+
meshId,
|
|
66584
|
+
nodeLabel: nodeId || meshId,
|
|
66585
|
+
...nodeId ? { nodeId } : {},
|
|
66586
|
+
metadataEvent: {
|
|
66587
|
+
source: "mesh_auto_launch_orphan_session",
|
|
66588
|
+
taskId: originTaskId,
|
|
66589
|
+
sessionId,
|
|
66590
|
+
reason: "auto_launch_orphan_session_detected"
|
|
66591
|
+
},
|
|
66592
|
+
coordinatorMessage: `[System] Mesh session ${sessionId}${nodeId ? ` on node ${nodeId}` : ""} was auto-launched for task ${originTaskId}, but ${detail}. The session is idle with no work assigned and nothing reclaims it on its own. Reuse it for other queued work or stop it.`,
|
|
66593
|
+
queuedAt: Date.now(),
|
|
66594
|
+
...readNonEmptyString(originTask?.sourceCoordinatorSessionId) ? { targetCoordinatorSessionId: readNonEmptyString(originTask.sourceCoordinatorSessionId) } : {},
|
|
66595
|
+
...localCoordinatorDaemonId() ? { targetCoordinatorDaemonId: localCoordinatorDaemonId() } : {}
|
|
66596
|
+
});
|
|
66597
|
+
} catch (e) {
|
|
66598
|
+
LOG2.warn("MeshQueue", `AUTOLAUNCH-ORPHAN-SWEEP: failed to queue the coordinator notification for session ${sessionId}: ${e?.message || e}`);
|
|
66599
|
+
}
|
|
66600
|
+
}
|
|
66601
|
+
}
|
|
66602
|
+
function autoLaunchWriteWouldClobberWinner(meshId, taskId, args, awaitClaimWindowMs2) {
|
|
66603
|
+
if (args.status === "completed" && readNonEmptyString(args.sessionId)) return false;
|
|
66604
|
+
let existing;
|
|
66605
|
+
try {
|
|
66606
|
+
existing = getQueue(meshId).find((t) => t.id === taskId)?.autoLaunch;
|
|
66607
|
+
} catch {
|
|
66608
|
+
return false;
|
|
66609
|
+
}
|
|
66610
|
+
const heldSessionId = existing ? readNonEmptyString(existing.sessionId) : "";
|
|
66611
|
+
if (!existing || existing.status !== "completed" || !heldSessionId) return false;
|
|
66612
|
+
const heldAtMs = Date.parse(existing.updatedAt);
|
|
66613
|
+
if (!Number.isFinite(heldAtMs) || Date.now() - heldAtMs >= awaitClaimWindowMs2) return false;
|
|
66614
|
+
if (sessionIdsEquivalent(readNonEmptyString(args.sessionId), heldSessionId)) return false;
|
|
66615
|
+
LOG2.info("MeshQueue", `AUTOLAUNCH-WINNER-CLOBBER: suppressed a '${args.status}' autoLaunch write for task ${taskId} (mesh ${meshId}) that would have overwritten the in-window launch record for session ${heldSessionId}; the field keeps pointing at the actually-launched session.`);
|
|
66616
|
+
return true;
|
|
66617
|
+
}
|
|
66618
|
+
function awaitClaimWindowMs(cycles) {
|
|
66619
|
+
return AUTO_LAUNCH_AWAIT_CLAIM_MS * Math.pow(2, Math.min(cycles, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES));
|
|
66620
|
+
}
|
|
66621
|
+
function remoteSessionAppearsLive(meshId, sessionId) {
|
|
66622
|
+
if (!sessionId) return false;
|
|
66623
|
+
try {
|
|
66624
|
+
return MeshRuntimeStore.getInstance().getRemoteIdleSessions(meshId).some((s2) => sessionIdsEquivalent(s2.sessionId, sessionId));
|
|
66625
|
+
} catch {
|
|
66626
|
+
return false;
|
|
66627
|
+
}
|
|
66628
|
+
}
|
|
66629
|
+
function inWindowAutoLaunchSessionIdsForNode(meshId, nodeId) {
|
|
66630
|
+
const nowMs = Date.now();
|
|
66631
|
+
const out = [];
|
|
66632
|
+
for (const task of getQueue(meshId, { status: ["pending"] })) {
|
|
66633
|
+
const al = task.autoLaunch;
|
|
66634
|
+
const sid = al ? readNonEmptyString(al.sessionId) : "";
|
|
66635
|
+
if (!al || al.status !== "started" && al.status !== "completed" || !sid) continue;
|
|
66636
|
+
if (!daemonIdsEquivalent(al.nodeId, nodeId)) continue;
|
|
66637
|
+
const launchedAtMs = Date.parse(al.updatedAt);
|
|
66638
|
+
const inBaseWindow = Number.isFinite(launchedAtMs) && nowMs - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS;
|
|
66639
|
+
const inBackoff = autoLaunchAwaitClaimBackoff.has(`${meshId}::${task.id}`);
|
|
66640
|
+
if (inBaseWindow || inBackoff) out.push(sid);
|
|
66641
|
+
}
|
|
66642
|
+
return out;
|
|
66643
|
+
}
|
|
66644
|
+
var AUTO_LAUNCH_AWAIT_CLAIM_MS;
|
|
66645
|
+
var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
|
|
66646
|
+
var AUTO_LAUNCH_REMOTE_IDLE_TTL_MS;
|
|
66647
|
+
var TERMINAL_SESSION_STATUSES;
|
|
66648
|
+
var autoLaunchOrphanNotified;
|
|
66649
|
+
var autoLaunchOrphanFirstSeenAtMs;
|
|
66650
|
+
var AUTO_LAUNCH_ORPHAN_GRACE_MS;
|
|
66651
|
+
var autoLaunchAwaitClaimBackoff;
|
|
66652
|
+
var init_mesh_autolaunch_integrity = __esm2({
|
|
66653
|
+
"src/mesh/mesh-autolaunch-integrity.ts"() {
|
|
66654
|
+
"use strict";
|
|
66655
|
+
init_config();
|
|
66656
|
+
init_logger();
|
|
66657
|
+
init_dist();
|
|
66658
|
+
init_mesh_work_queue();
|
|
66659
|
+
init_mesh_events_utils();
|
|
66660
|
+
init_mesh_events_pending();
|
|
66661
|
+
init_mesh_queue_observability();
|
|
66662
|
+
init_mesh_scheduling_fitness();
|
|
66663
|
+
init_mesh_runtime_store();
|
|
66664
|
+
AUTO_LAUNCH_AWAIT_CLAIM_MS = 9e4;
|
|
66665
|
+
AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES = 2;
|
|
66666
|
+
AUTO_LAUNCH_REMOTE_IDLE_TTL_MS = 5 * 60 * 1e3;
|
|
66667
|
+
TERMINAL_SESSION_STATUSES = ["stopped", "failed", "terminated", "exited", "closed"];
|
|
66668
|
+
autoLaunchOrphanNotified = /* @__PURE__ */ new Set();
|
|
66669
|
+
autoLaunchOrphanFirstSeenAtMs = /* @__PURE__ */ new Map();
|
|
66670
|
+
AUTO_LAUNCH_ORPHAN_GRACE_MS = 3e4;
|
|
66671
|
+
autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
|
|
66672
|
+
}
|
|
66673
|
+
});
|
|
66197
66674
|
function classifiedDifficultiesForSlot(slot) {
|
|
66198
66675
|
const highest = slot.difficulty?.reduce((rank, value) => {
|
|
66199
66676
|
const candidate = CLASSIFIED_DIFFICULTIES.indexOf(value);
|
|
@@ -66298,7 +66775,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66298
66775
|
CLASSIFIED_DIFFICULTIES = ["easy", "medium", "difficult"];
|
|
66299
66776
|
}
|
|
66300
66777
|
});
|
|
66301
|
-
function
|
|
66778
|
+
function localCoordinatorDaemonId2() {
|
|
66302
66779
|
return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
|
|
66303
66780
|
}
|
|
66304
66781
|
function loadRepoConfigForNode(node) {
|
|
@@ -66407,7 +66884,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66407
66884
|
const routingDecision = {
|
|
66408
66885
|
source: routing?.source ?? "queue",
|
|
66409
66886
|
selectedNodeId: ctx.nodeId,
|
|
66410
|
-
...
|
|
66887
|
+
...localCoordinatorDaemonId2() ? { daemonId: localCoordinatorDaemonId2() } : {},
|
|
66411
66888
|
transport: ctx.transport,
|
|
66412
66889
|
// D: resolved execution profile — prefer the caller's resolved values, fall back
|
|
66413
66890
|
// to what the claimed task row carries (queue/idle drains carry it on the task).
|
|
@@ -66727,7 +67204,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66727
67204
|
nodeId,
|
|
66728
67205
|
sessionId,
|
|
66729
67206
|
providerType,
|
|
66730
|
-
coordinatorDaemonId:
|
|
67207
|
+
coordinatorDaemonId: localCoordinatorDaemonId2(),
|
|
66731
67208
|
coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) || void 0
|
|
66732
67209
|
});
|
|
66733
67210
|
dispatchAttemptId = attempt.attemptId;
|
|
@@ -66771,7 +67248,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66771
67248
|
if (remoteDaemonId && components.dispatchMeshCommand) {
|
|
66772
67249
|
const isLocalNode = isLocalAutoLaunchNode(node) || components.cliManager.adapters.has(sessionId);
|
|
66773
67250
|
if (!isLocalNode) {
|
|
66774
|
-
const localDaemonIdForDispatch =
|
|
67251
|
+
const localDaemonIdForDispatch = localCoordinatorDaemonId2();
|
|
66775
67252
|
const sourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId) || void 0;
|
|
66776
67253
|
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
66777
67254
|
deliverTaskToSession(
|
|
@@ -66821,7 +67298,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66821
67298
|
try {
|
|
66822
67299
|
const inst = components.instanceManager.getInstance(sessionId);
|
|
66823
67300
|
if (inst && typeof inst.updateSettings === "function") {
|
|
66824
|
-
const localDaemonId =
|
|
67301
|
+
const localDaemonId = localCoordinatorDaemonId2();
|
|
66825
67302
|
const localSourceCoordinatorSessionId = readNonEmptyString(task.sourceCoordinatorSessionId);
|
|
66826
67303
|
inst.updateSettings({
|
|
66827
67304
|
meshNodeFor: meshId,
|
|
@@ -66865,7 +67342,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66865
67342
|
...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
|
|
66866
67343
|
// TURN-LEDGER (Stage 5): the opaque attempt identity (see remote branch above).
|
|
66867
67344
|
...dispatchAttemptId ? { attemptId: dispatchAttemptId } : {},
|
|
66868
|
-
...
|
|
67345
|
+
...localCoordinatorDaemonId2() ? { coordinatorDaemonId: localCoordinatorDaemonId2() } : {},
|
|
66869
67346
|
...readNonEmptyString(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
|
|
66870
67347
|
...silentIdlePushOnDispatch ? { silentIdlePush: true } : {}
|
|
66871
67348
|
}
|
|
@@ -66878,38 +67355,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66878
67355
|
task,
|
|
66879
67356
|
transport: "local",
|
|
66880
67357
|
...readNonEmptyString(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString(task.sourceCoordinatorSessionId) } : {},
|
|
66881
|
-
...
|
|
67358
|
+
...localCoordinatorDaemonId2() ? { sourceCoordinatorDaemonId: localCoordinatorDaemonId2() } : {},
|
|
66882
67359
|
...routingDecision ? { routingDecision } : {}
|
|
66883
67360
|
}
|
|
66884
67361
|
);
|
|
66885
67362
|
return true;
|
|
66886
67363
|
}
|
|
66887
|
-
function awaitClaimWindowMs(cycles) {
|
|
66888
|
-
return AUTO_LAUNCH_AWAIT_CLAIM_MS * Math.pow(2, Math.min(cycles, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES));
|
|
66889
|
-
}
|
|
66890
|
-
function remoteSessionAppearsLive(meshId, sessionId) {
|
|
66891
|
-
if (!sessionId) return false;
|
|
66892
|
-
try {
|
|
66893
|
-
return MeshRuntimeStore.getInstance().getRemoteIdleSessions(meshId).some((s2) => sessionIdsEquivalent(s2.sessionId, sessionId));
|
|
66894
|
-
} catch {
|
|
66895
|
-
return false;
|
|
66896
|
-
}
|
|
66897
|
-
}
|
|
66898
|
-
function inWindowAutoLaunchSessionIdsForNode(meshId, nodeId) {
|
|
66899
|
-
const nowMs = Date.now();
|
|
66900
|
-
const out = [];
|
|
66901
|
-
for (const task of getQueue(meshId, { status: ["pending"] })) {
|
|
66902
|
-
const al = task.autoLaunch;
|
|
66903
|
-
const sid = al ? readNonEmptyString(al.sessionId) : "";
|
|
66904
|
-
if (!al || al.status !== "started" && al.status !== "completed" || !sid) continue;
|
|
66905
|
-
if (!daemonIdsEquivalent(al.nodeId, nodeId)) continue;
|
|
66906
|
-
const launchedAtMs = Date.parse(al.updatedAt);
|
|
66907
|
-
const inBaseWindow = Number.isFinite(launchedAtMs) && nowMs - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS;
|
|
66908
|
-
const inBackoff = autoLaunchAwaitClaimBackoff.has(`${meshId}::${task.id}`);
|
|
66909
|
-
if (inBaseWindow || inBackoff) out.push(sid);
|
|
66910
|
-
}
|
|
66911
|
-
return out;
|
|
66912
|
-
}
|
|
66913
67364
|
function sweepExpiredCooldowns() {
|
|
66914
67365
|
const now = Date.now();
|
|
66915
67366
|
for (const [key2, until] of autoLaunchCooldownUntil) {
|
|
@@ -66929,7 +67380,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66929
67380
|
function isTerminalSessionStatus(status) {
|
|
66930
67381
|
return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
|
|
66931
67382
|
}
|
|
66932
|
-
function
|
|
67383
|
+
function isIdleSessionState3(state) {
|
|
66933
67384
|
const status = readNonEmptyString(state?.status).toLowerCase();
|
|
66934
67385
|
if (isTerminalSessionStatus(status)) return false;
|
|
66935
67386
|
return status === "idle" || state?.activeChat?.status === "waiting_input";
|
|
@@ -66949,7 +67400,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66949
67400
|
const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
66950
67401
|
if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
|
|
66951
67402
|
const sessionId = readNonEmptyString(state.instanceId);
|
|
66952
|
-
if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) &&
|
|
67403
|
+
if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
|
|
66953
67404
|
return sessionStateLooksActive(state);
|
|
66954
67405
|
});
|
|
66955
67406
|
}
|
|
@@ -66980,7 +67431,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
66980
67431
|
const daemonId = readMeshNodeDaemonId(node ?? {});
|
|
66981
67432
|
if (!daemonId) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
|
|
66982
67433
|
if (!components.dispatchMeshCommand) return { mode: "skip", reason: "remote_auto_launch_unsupported" };
|
|
66983
|
-
const coordinatorDaemonId =
|
|
67434
|
+
const coordinatorDaemonId = localCoordinatorDaemonId2();
|
|
66984
67435
|
if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
|
|
66985
67436
|
return { mode: "remote", daemonId, coordinatorDaemonId };
|
|
66986
67437
|
}
|
|
@@ -67054,8 +67505,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67054
67505
|
const reason = args.reason || args.error;
|
|
67055
67506
|
const difficultyFloorSkip = args.status === "skipped" && isDifficultyFloorWaitReason(reason);
|
|
67056
67507
|
if (difficultyFloorSkip) {
|
|
67057
|
-
handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId:
|
|
67058
|
-
} else {
|
|
67508
|
+
handleDifficultyFloorSkip({ meshId, taskId, reason, nodeId: args.nodeId, coordinatorDaemonId: localCoordinatorDaemonId2() });
|
|
67509
|
+
} else if (!autoLaunchWriteWouldClobberWinner(meshId, taskId, args, AUTO_LAUNCH_AWAIT_CLAIM_MS)) {
|
|
67059
67510
|
recordTaskAutoLaunch(meshId, taskId, {
|
|
67060
67511
|
status: args.status,
|
|
67061
67512
|
reason,
|
|
@@ -67207,7 +67658,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67207
67658
|
const nowMs = Date.now();
|
|
67208
67659
|
const state = autoLaunchAwaitClaimBackoff.get(backoffKey) || { cycles: 0, nextAttemptAtMs: 0 };
|
|
67209
67660
|
if (state.nextAttemptAtMs && nowMs < state.nextAttemptAtMs) return "backoff";
|
|
67210
|
-
const atCap = state.cycles >=
|
|
67661
|
+
const atCap = state.cycles >= AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
|
|
67211
67662
|
const live = remoteSessionAppearsLive(meshId, sessionId);
|
|
67212
67663
|
if ((live || atCap) && nodeId && providerType) {
|
|
67213
67664
|
try {
|
|
@@ -67233,7 +67684,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67233
67684
|
return "respawn";
|
|
67234
67685
|
}
|
|
67235
67686
|
}
|
|
67236
|
-
const cycles = Math.min(state.cycles + 1,
|
|
67687
|
+
const cycles = Math.min(state.cycles + 1, AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2);
|
|
67237
67688
|
autoLaunchAwaitClaimBackoff.set(backoffKey, { cycles, nextAttemptAtMs: nowMs + awaitClaimWindowMs(cycles) });
|
|
67238
67689
|
recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim_backoff", nodeId, sessionId });
|
|
67239
67690
|
return "backoff";
|
|
@@ -67254,333 +67705,339 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67254
67705
|
const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
|
|
67255
67706
|
const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
|
|
67256
67707
|
for (const task of pending) {
|
|
67257
|
-
|
|
67258
|
-
|
|
67708
|
+
const taskLaunchKey = `${meshId}::${task.id}`;
|
|
67709
|
+
if (autoLaunchTaskInProgress.has(taskLaunchKey)) {
|
|
67710
|
+
recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "auto_launch_task_in_progress" });
|
|
67259
67711
|
continue;
|
|
67260
67712
|
}
|
|
67261
|
-
|
|
67262
|
-
|
|
67263
|
-
|
|
67264
|
-
|
|
67265
|
-
const isReadonly = isTaskReadonly(task);
|
|
67266
|
-
if (isReadonly) {
|
|
67267
|
-
if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
|
|
67268
|
-
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_readonly_parallel_tasks_reached" });
|
|
67713
|
+
autoLaunchTaskInProgress.add(taskLaunchKey);
|
|
67714
|
+
try {
|
|
67715
|
+
if (!taskDependenciesSatisfied(task, statusById)) {
|
|
67716
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dependencies_unsatisfied" });
|
|
67269
67717
|
continue;
|
|
67270
67718
|
}
|
|
67271
|
-
|
|
67272
|
-
|
|
67273
|
-
continue;
|
|
67274
|
-
}
|
|
67275
|
-
if (task.targetSessionId) {
|
|
67276
|
-
const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
|
|
67277
|
-
if (deadTarget.dead) {
|
|
67278
|
-
const requeued = requeueTask(meshId, task.id, {
|
|
67279
|
-
reason: deadTarget.reason,
|
|
67280
|
-
clearTargetSession: true,
|
|
67281
|
-
// Keep the node pin if only the SESSION died on a still-live node; clear it
|
|
67282
|
-
// when the NODE itself is absent (nothing to pin to).
|
|
67283
|
-
clearTargetNode: deadTarget.nodeDead
|
|
67284
|
-
});
|
|
67285
|
-
if (requeued) {
|
|
67286
|
-
noteTargetPinCleared(deadTarget.reason);
|
|
67287
|
-
LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
|
|
67288
|
-
}
|
|
67289
|
-
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
|
|
67719
|
+
if (!meshTaskNotBeforeReady(task)) {
|
|
67720
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "not_before_delayed" });
|
|
67290
67721
|
continue;
|
|
67291
67722
|
}
|
|
67292
|
-
const
|
|
67293
|
-
if (
|
|
67294
|
-
|
|
67295
|
-
|
|
67296
|
-
|
|
67297
|
-
traceMeshEventDrop("target_session_pin_expired", {
|
|
67298
|
-
taskId: task.id,
|
|
67299
|
-
sessionId: readNonEmptyString(task.targetSessionId),
|
|
67300
|
-
nodeId: readNonEmptyString(task.targetNodeId),
|
|
67301
|
-
meshId,
|
|
67302
|
-
event: "agent:ready"
|
|
67303
|
-
}, `unclaimed ${Math.round(pinAgeMs / 1e3)}s \u2265 ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s \u2192 pin cleared, claimable`);
|
|
67304
|
-
LOG2.warn("MeshQueue", `TARGET-PIN-TTL: task ${task.id} (mesh ${meshId}) stayed pinned-but-unclaimed for ${Math.round(pinAgeMs / 1e3)}s (ttl ${Math.round(TARGET_SESSION_PIN_TTL_MS / 1e3)}s); expired the stale target pin so a compatible session can claim it.`);
|
|
67723
|
+
const isReadonly = isTaskReadonly(task);
|
|
67724
|
+
if (isReadonly) {
|
|
67725
|
+
if (activeReadonlyAssignedCount(meshId) >= maxReadonlyParallelTasks) {
|
|
67726
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_readonly_parallel_tasks_reached" });
|
|
67727
|
+
continue;
|
|
67305
67728
|
}
|
|
67306
|
-
|
|
67729
|
+
} else if (activeWriteAssignedCount(meshId) >= maxParallelTasks) {
|
|
67730
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
|
|
67307
67731
|
continue;
|
|
67308
67732
|
}
|
|
67309
|
-
|
|
67310
|
-
|
|
67311
|
-
|
|
67312
|
-
|
|
67313
|
-
|
|
67314
|
-
|
|
67315
|
-
const alNodeId = readNonEmptyString(task.autoLaunch.nodeId);
|
|
67316
|
-
const alProvider = readNonEmptyString(task.autoLaunch.providerType);
|
|
67317
|
-
if (Number.isFinite(launchedAtMs) && Date.now() - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS) {
|
|
67318
|
-
recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim", nodeId: alNodeId, sessionId: alSessionId });
|
|
67319
|
-
continue;
|
|
67320
|
-
}
|
|
67321
|
-
if (Number.isFinite(launchedAtMs) && alSessionId && alNodeId) {
|
|
67322
|
-
const outcome = driveExpiredAwaitClaim(components, meshId, task, { sessionId: alSessionId, nodeId: alNodeId, providerType: alProvider });
|
|
67323
|
-
if (outcome === "claimed" || outcome === "fallback") return true;
|
|
67324
|
-
if (outcome === "backoff") continue;
|
|
67325
|
-
}
|
|
67326
|
-
}
|
|
67327
|
-
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
67328
|
-
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
67329
|
-
if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
|
|
67330
|
-
if (task.requiredTags?.length) {
|
|
67331
|
-
const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
|
|
67332
|
-
const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
|
|
67333
|
-
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
67334
|
-
return providerCandidates.some(
|
|
67335
|
-
(p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
|
|
67733
|
+
if (taskIsParked(task)) {
|
|
67734
|
+
settleParkedQueueTask(
|
|
67735
|
+
meshId,
|
|
67736
|
+
task,
|
|
67737
|
+
(reason) => markAutoLaunch(meshId, task.id, { status: "skipped", reason }),
|
|
67738
|
+
failRetentionExpiredParkedTask
|
|
67336
67739
|
);
|
|
67337
|
-
}
|
|
67338
|
-
return true;
|
|
67339
|
-
}) : [];
|
|
67340
|
-
if (!candidateNodes.length) {
|
|
67341
|
-
const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
|
|
67342
|
-
const convergenceOntoWorktree = task.taskMode === "convergence" && Array.isArray(mesh?.nodes) && (() => {
|
|
67343
|
-
const matched = mesh.nodes.filter((n) => !task.targetNodeId || meshNodeIdMatches(n, task.targetNodeId));
|
|
67344
|
-
return matched.length > 0 && matched.every((n) => n?.isLocalWorktree === true);
|
|
67345
|
-
})();
|
|
67346
|
-
const targetTransientlyUnresolved = targetPinUnmatched && isTargetNodeTransientlyUnresolved(mesh, task);
|
|
67347
|
-
markAutoLaunch(meshId, task.id, {
|
|
67348
|
-
status: "skipped",
|
|
67349
|
-
reason: convergenceOntoWorktree ? "mesh_convergence_target_is_worktree" : targetTransientlyUnresolved ? TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON : targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
|
|
67350
|
-
nodeId: task.targetNodeId
|
|
67351
|
-
});
|
|
67352
|
-
continue;
|
|
67353
|
-
}
|
|
67354
|
-
const strategy = resolveSchedulingStrategy(mesh);
|
|
67355
|
-
const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
|
|
67356
|
-
meshId,
|
|
67357
|
-
strategy,
|
|
67358
|
-
candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
|
|
67359
|
-
// Auto-launch drains one task at a time, so the task IS in scope here —
|
|
67360
|
-
// pass it through for the 'fitness' strategy's task→slot ranking. The
|
|
67361
|
-
// mesh's quotaRouting thresholds ride along so the fitness score can
|
|
67362
|
-
// include the quota-headroom spread bonus (fail-open when unset).
|
|
67363
|
-
{ bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
|
|
67364
|
-
).map((c) => c.node);
|
|
67365
|
-
const skippedCandidates = [];
|
|
67366
|
-
const SKIPPED_CANDIDATES_MAX = 5;
|
|
67367
|
-
const markSkip = (nodeIdForSkip, reason, extra) => {
|
|
67368
|
-
markAutoLaunch(meshId, task.id, { status: "skipped", reason, nodeId: nodeIdForSkip, ...extra || {} });
|
|
67369
|
-
if (nodeIdForSkip && skippedCandidates.length < SKIPPED_CANDIDATES_MAX) {
|
|
67370
|
-
skippedCandidates.push({ nodeId: nodeIdForSkip, reason });
|
|
67371
|
-
}
|
|
67372
|
-
};
|
|
67373
|
-
for (const node of orderedCandidateNodes) {
|
|
67374
|
-
const nodeId = readMeshNodeId(node);
|
|
67375
|
-
if (!nodeId) continue;
|
|
67376
|
-
const launchKey = `${meshId}:${nodeId}`;
|
|
67377
|
-
const now = Date.now();
|
|
67378
|
-
const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
|
|
67379
|
-
if (cooldownUntil > 0 && now >= cooldownUntil) autoLaunchCooldownUntil.delete(launchKey);
|
|
67380
|
-
if (autoLaunchInProgress.has(launchKey)) {
|
|
67381
|
-
markSkip(nodeId, "auto_launch_in_progress");
|
|
67382
|
-
continue;
|
|
67383
|
-
}
|
|
67384
|
-
if (now < cooldownUntil) {
|
|
67385
|
-
markSkip(nodeId, "auto_launch_cooldown");
|
|
67386
|
-
continue;
|
|
67387
|
-
}
|
|
67388
|
-
if (isDirtyNode(node)) {
|
|
67389
|
-
markSkip(nodeId, "dirty_workspace");
|
|
67390
|
-
continue;
|
|
67391
|
-
}
|
|
67392
|
-
if (!isLaunchableNode(node)) {
|
|
67393
|
-
markSkip(nodeId, "node_health_not_launchable");
|
|
67394
|
-
continue;
|
|
67395
|
-
}
|
|
67396
|
-
if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
|
|
67397
|
-
markSkip(nodeId, "node_stale_behind_upstream");
|
|
67398
67740
|
continue;
|
|
67399
67741
|
}
|
|
67400
|
-
|
|
67401
|
-
|
|
67402
|
-
|
|
67403
|
-
|
|
67404
|
-
|
|
67405
|
-
|
|
67406
|
-
|
|
67407
|
-
|
|
67408
|
-
|
|
67409
|
-
|
|
67410
|
-
|
|
67411
|
-
|
|
67412
|
-
|
|
67742
|
+
if (task.targetSessionId) {
|
|
67743
|
+
const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
|
|
67744
|
+
if (deadTarget.dead) {
|
|
67745
|
+
const requeued = requeueTask(meshId, task.id, {
|
|
67746
|
+
reason: deadTarget.reason,
|
|
67747
|
+
clearTargetSession: true,
|
|
67748
|
+
// Keep the node pin if only the SESSION died on a still-live node; clear it
|
|
67749
|
+
// when the NODE itself is absent (nothing to pin to).
|
|
67750
|
+
clearTargetNode: deadTarget.nodeDead
|
|
67751
|
+
});
|
|
67752
|
+
if (requeued) {
|
|
67753
|
+
noteTargetPinCleared(deadTarget.reason);
|
|
67754
|
+
LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
|
|
67755
|
+
}
|
|
67756
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
|
|
67757
|
+
continue;
|
|
67758
|
+
}
|
|
67759
|
+
if (parkExpiredTargetPin(meshId, task, resolveTargetPinTtlVerdict(components, task), TARGET_SESSION_PIN_TTL_MS, (m, t, r) => parkTaskTargetPin(m, t, { reason: r }))) {
|
|
67760
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: PARKED_SKIP_REASON });
|
|
67761
|
+
continue;
|
|
67762
|
+
}
|
|
67763
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
|
|
67413
67764
|
continue;
|
|
67414
67765
|
}
|
|
67415
|
-
|
|
67416
|
-
|
|
67417
|
-
|
|
67766
|
+
if (task.autoLaunch?.status === "completed" && task.autoLaunch.sessionId) {
|
|
67767
|
+
const launchedAtMs = Date.parse(task.autoLaunch.updatedAt);
|
|
67768
|
+
const alSessionId = readNonEmptyString(task.autoLaunch.sessionId);
|
|
67769
|
+
const alNodeId = readNonEmptyString(task.autoLaunch.nodeId);
|
|
67770
|
+
const alProvider = readNonEmptyString(task.autoLaunch.providerType);
|
|
67771
|
+
if (Number.isFinite(launchedAtMs) && Date.now() - launchedAtMs < AUTO_LAUNCH_AWAIT_CLAIM_MS) {
|
|
67772
|
+
recordAutoLaunchEvent(meshId, { phase: "skipped", taskId: task.id, reason: "awaiting_launched_session_claim", nodeId: alNodeId, sessionId: alSessionId });
|
|
67773
|
+
continue;
|
|
67774
|
+
}
|
|
67775
|
+
if (Number.isFinite(launchedAtMs) && alSessionId && alNodeId) {
|
|
67776
|
+
const outcome = driveExpiredAwaitClaim(components, meshId, task, { sessionId: alSessionId, nodeId: alNodeId, providerType: alProvider });
|
|
67777
|
+
if (outcome === "claimed" || outcome === "fallback") return true;
|
|
67778
|
+
if (outcome === "backoff") continue;
|
|
67779
|
+
}
|
|
67780
|
+
}
|
|
67781
|
+
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
67782
|
+
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
67783
|
+
if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
|
|
67784
|
+
if (task.requiredTags?.length) {
|
|
67785
|
+
const slotProviders = resolveNodeCapabilitySlots(node, meshId).map((s2) => s2.provider).filter(Boolean);
|
|
67786
|
+
const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
|
|
67787
|
+
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
67788
|
+
return providerCandidates.some(
|
|
67789
|
+
(p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
|
|
67790
|
+
);
|
|
67791
|
+
}
|
|
67792
|
+
return true;
|
|
67793
|
+
}) : [];
|
|
67794
|
+
if (!candidateNodes.length) {
|
|
67795
|
+
const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
|
|
67796
|
+
const convergenceOntoWorktree = task.taskMode === "convergence" && Array.isArray(mesh?.nodes) && (() => {
|
|
67797
|
+
const matched = mesh.nodes.filter((n) => !task.targetNodeId || meshNodeIdMatches(n, task.targetNodeId));
|
|
67798
|
+
return matched.length > 0 && matched.every((n) => n?.isLocalWorktree === true);
|
|
67799
|
+
})();
|
|
67800
|
+
const targetTransientlyUnresolved = targetPinUnmatched && isTargetNodeTransientlyUnresolved(mesh, task);
|
|
67801
|
+
markAutoLaunch(meshId, task.id, {
|
|
67802
|
+
status: "skipped",
|
|
67803
|
+
reason: convergenceOntoWorktree ? "mesh_convergence_target_is_worktree" : targetTransientlyUnresolved ? TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON : targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
|
|
67804
|
+
nodeId: task.targetNodeId
|
|
67805
|
+
});
|
|
67418
67806
|
continue;
|
|
67419
67807
|
}
|
|
67420
|
-
|
|
67421
|
-
|
|
67422
|
-
|
|
67423
|
-
|
|
67424
|
-
|
|
67808
|
+
const strategy = resolveSchedulingStrategy(mesh);
|
|
67809
|
+
const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
|
|
67810
|
+
meshId,
|
|
67811
|
+
strategy,
|
|
67812
|
+
candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
|
|
67813
|
+
// Auto-launch drains one task at a time, so the task IS in scope here —
|
|
67814
|
+
// pass it through for the 'fitness' strategy's task→slot ranking. The
|
|
67815
|
+
// mesh's quotaRouting thresholds ride along so the fitness score can
|
|
67816
|
+
// include the quota-headroom spread bonus (fail-open when unset).
|
|
67817
|
+
{ bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
|
|
67818
|
+
).map((c) => c.node);
|
|
67819
|
+
const skippedCandidates = [];
|
|
67820
|
+
const SKIPPED_CANDIDATES_MAX = 5;
|
|
67821
|
+
const markSkip = (nodeIdForSkip, reason, extra) => {
|
|
67822
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason, nodeId: nodeIdForSkip, ...extra || {} });
|
|
67823
|
+
if (nodeIdForSkip && skippedCandidates.length < SKIPPED_CANDIDATES_MAX) {
|
|
67824
|
+
skippedCandidates.push({ nodeId: nodeIdForSkip, reason });
|
|
67825
|
+
}
|
|
67826
|
+
};
|
|
67827
|
+
for (const node of orderedCandidateNodes) {
|
|
67828
|
+
const nodeId = readMeshNodeId(node);
|
|
67829
|
+
if (!nodeId) continue;
|
|
67830
|
+
const launchKey = `${meshId}:${nodeId}`;
|
|
67831
|
+
const now = Date.now();
|
|
67832
|
+
const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
|
|
67833
|
+
if (cooldownUntil > 0 && now >= cooldownUntil) autoLaunchCooldownUntil.delete(launchKey);
|
|
67834
|
+
if (autoLaunchInProgress.has(launchKey)) {
|
|
67835
|
+
markSkip(nodeId, "auto_launch_in_progress");
|
|
67425
67836
|
continue;
|
|
67426
67837
|
}
|
|
67427
|
-
|
|
67428
|
-
|
|
67429
|
-
const effectiveThinkingLevel = resolveLaunchAxis(task.thinkingLevel, task.thinkingLevelSource, resolved.thinkingLevel, slotCoversDifficulty);
|
|
67430
|
-
const slotDecision = decideSlotForModel({
|
|
67431
|
-
requestedModel,
|
|
67432
|
-
slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
|
|
67433
|
-
slot,
|
|
67434
|
-
available: slotHasCapacity(meshId, nodeId, node, slot, mesh?.nodes, isReadonly)
|
|
67435
|
-
}))
|
|
67436
|
-
});
|
|
67437
|
-
if (slotDecision.outcome === "wait") {
|
|
67438
|
-
LOG2.info("MeshQueue", `SLOT MODEL GUARD: model '${requestedModel}' is declared on node ${nodeId} but every matching slot is at its maxParallel cap (task ${task.id}); leaving the task queued until a slot goes idle`);
|
|
67439
|
-
markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
|
|
67838
|
+
if (now < cooldownUntil) {
|
|
67839
|
+
markSkip(nodeId, "auto_launch_cooldown");
|
|
67440
67840
|
continue;
|
|
67441
67841
|
}
|
|
67442
|
-
if (
|
|
67443
|
-
|
|
67444
|
-
markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
|
|
67842
|
+
if (isDirtyNode(node)) {
|
|
67843
|
+
markSkip(nodeId, "dirty_workspace");
|
|
67445
67844
|
continue;
|
|
67446
67845
|
}
|
|
67447
|
-
|
|
67448
|
-
|
|
67449
|
-
const finalizedModel = slotDecision.slot.model?.trim() || void 0;
|
|
67450
|
-
const slotWasDemoted = resolved.slot?.provider !== slotDecision.slot.provider || winningModel !== finalizedModel;
|
|
67451
|
-
const demotionReason = slotWasDemoted ? slotHasCapacity(meshId, nodeId, node, resolved.slot, mesh?.nodes, isReadonly) ? "slot_reselected_during_launch" : "winning_slot_capacity_exhausted" : void 0;
|
|
67452
|
-
const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
|
|
67453
|
-
if (rawEffectiveModel && effectiveModel === void 0) {
|
|
67454
|
-
LOG2.info("MeshQueue", `CODEX-400 GUARD: dropped incompatible launch model '${rawEffectiveModel}' for non-Anthropic provider '${resolved.providerType}' on node ${nodeId} (task ${task.id}); provider will use its own default model`);
|
|
67455
|
-
}
|
|
67456
|
-
const providerCap = effectiveSlotCap(
|
|
67457
|
-
resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType),
|
|
67458
|
-
isReadonly
|
|
67459
|
-
);
|
|
67460
|
-
if (providerCap !== void 0 && activeProviderAssignedCount(
|
|
67461
|
-
meshId,
|
|
67462
|
-
nodeId,
|
|
67463
|
-
resolved.providerType,
|
|
67464
|
-
resolveDaemonSiblingNodeIds(nodeId, mesh?.nodes)
|
|
67465
|
-
) >= providerCap) {
|
|
67466
|
-
markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
|
|
67846
|
+
if (!isLaunchableNode(node)) {
|
|
67847
|
+
markSkip(nodeId, "node_health_not_launchable");
|
|
67467
67848
|
continue;
|
|
67468
67849
|
}
|
|
67469
|
-
|
|
67470
|
-
|
|
67471
|
-
|
|
67472
|
-
|
|
67473
|
-
|
|
67474
|
-
|
|
67475
|
-
|
|
67476
|
-
|
|
67477
|
-
|
|
67478
|
-
|
|
67479
|
-
|
|
67480
|
-
|
|
67481
|
-
|
|
67482
|
-
|
|
67483
|
-
|
|
67484
|
-
|
|
67485
|
-
|
|
67486
|
-
|
|
67487
|
-
|
|
67488
|
-
|
|
67489
|
-
|
|
67490
|
-
|
|
67491
|
-
|
|
67850
|
+
if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
|
|
67851
|
+
markSkip(nodeId, "node_stale_behind_upstream");
|
|
67852
|
+
continue;
|
|
67853
|
+
}
|
|
67854
|
+
const launchTarget = resolveAutoLaunchTarget(components, node);
|
|
67855
|
+
if (launchTarget.mode === "skip") {
|
|
67856
|
+
markSkip(nodeId, launchTarget.reason || "auto_launch_unavailable");
|
|
67857
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67858
|
+
sweepExpiredCooldowns();
|
|
67859
|
+
continue;
|
|
67860
|
+
}
|
|
67861
|
+
if (nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node)) {
|
|
67862
|
+
markSkip(nodeId, "node_has_live_session_pending_claim");
|
|
67863
|
+
continue;
|
|
67864
|
+
}
|
|
67865
|
+
if (!isTaskReadonly(task) && nodeHasActiveAssignment(meshId, nodeId)) {
|
|
67866
|
+
markSkip(nodeId, "node_has_active_assignment");
|
|
67867
|
+
continue;
|
|
67868
|
+
}
|
|
67869
|
+
const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
|
|
67870
|
+
if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
|
|
67871
|
+
markSkip(nodeId, "max_concurrent_sessions_reached");
|
|
67872
|
+
continue;
|
|
67873
|
+
}
|
|
67874
|
+
autoLaunchInProgress.add(launchKey);
|
|
67875
|
+
try {
|
|
67876
|
+
const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader), task.id);
|
|
67877
|
+
if (!resolved.providerType) {
|
|
67878
|
+
markSkip(nodeId, resolved.reason || "provider_unusable");
|
|
67879
|
+
continue;
|
|
67880
|
+
}
|
|
67881
|
+
const slotCoversDifficulty = slotCoversTaskDifficulty(resolved.slot, task.difficulty);
|
|
67882
|
+
const requestedModel = resolveLaunchAxis(task.model, task.modelSource, resolved.model, slotCoversDifficulty);
|
|
67883
|
+
const effectiveThinkingLevel = resolveLaunchAxis(task.thinkingLevel, task.thinkingLevelSource, resolved.thinkingLevel, slotCoversDifficulty);
|
|
67884
|
+
const slotDecision = decideSlotForModel({
|
|
67885
|
+
requestedModel,
|
|
67886
|
+
slots: resolveNodeCapabilitySlots(node, meshId).map((slot) => ({
|
|
67887
|
+
slot,
|
|
67888
|
+
available: slotHasCapacity(meshId, nodeId, node, slot, mesh?.nodes, isReadonly)
|
|
67889
|
+
}))
|
|
67890
|
+
});
|
|
67891
|
+
if (slotDecision.outcome === "wait") {
|
|
67892
|
+
LOG2.info("MeshQueue", `SLOT MODEL GUARD: model '${requestedModel}' is declared on node ${nodeId} but every matching slot is at its maxParallel cap (task ${task.id}); leaving the task queued until a slot goes idle`);
|
|
67893
|
+
markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
|
|
67894
|
+
continue;
|
|
67895
|
+
}
|
|
67896
|
+
if (slotDecision.outcome === "notify") {
|
|
67897
|
+
LOG2.warn("MeshQueue", `SLOT MODEL GUARD: no slot on node ${nodeId} declares model '${requestedModel}' (declared: ${slotDecision.declaredModels.join(", ") || "none"}) for task ${task.id}; not launching \u2014 surfacing to the coordinator to re-drive`);
|
|
67898
|
+
markSkip(nodeId, slotDecision.reason, { providerType: resolved.providerType });
|
|
67899
|
+
continue;
|
|
67900
|
+
}
|
|
67901
|
+
const rawEffectiveModel = slotDecision.model;
|
|
67902
|
+
const winningModel = resolved.slot?.model?.trim() || void 0;
|
|
67903
|
+
const finalizedModel = slotDecision.slot.model?.trim() || void 0;
|
|
67904
|
+
const slotWasDemoted = resolved.slot?.provider !== slotDecision.slot.provider || winningModel !== finalizedModel;
|
|
67905
|
+
const demotionReason = slotWasDemoted ? slotHasCapacity(meshId, nodeId, node, resolved.slot, mesh?.nodes, isReadonly) ? "slot_reselected_during_launch" : "winning_slot_capacity_exhausted" : void 0;
|
|
67906
|
+
const effectiveModel = isModelCompatibleWithProvider(rawEffectiveModel, resolved.providerType) ? rawEffectiveModel : void 0;
|
|
67907
|
+
if (rawEffectiveModel && effectiveModel === void 0) {
|
|
67908
|
+
LOG2.info("MeshQueue", `CODEX-400 GUARD: dropped incompatible launch model '${rawEffectiveModel}' for non-Anthropic provider '${resolved.providerType}' on node ${nodeId} (task ${task.id}); provider will use its own default model`);
|
|
67909
|
+
}
|
|
67910
|
+
const providerCap = effectiveSlotCap(
|
|
67911
|
+
resolveProviderMaxParallel(resolveNodeCapabilitySlots(node, meshId), resolved.providerType),
|
|
67912
|
+
isReadonly
|
|
67913
|
+
);
|
|
67914
|
+
if (providerCap !== void 0 && activeProviderAssignedCount(
|
|
67915
|
+
meshId,
|
|
67916
|
+
nodeId,
|
|
67917
|
+
resolved.providerType,
|
|
67918
|
+
resolveDaemonSiblingNodeIds(nodeId, mesh?.nodes)
|
|
67919
|
+
) >= providerCap) {
|
|
67920
|
+
markSkip(nodeId, "max_provider_parallel_reached", { providerType: resolved.providerType });
|
|
67921
|
+
continue;
|
|
67922
|
+
}
|
|
67923
|
+
const launchSettings = {
|
|
67924
|
+
// Worker launch envelope: role + mesh context so worker can route completion events.
|
|
67925
|
+
role: "worker",
|
|
67926
|
+
meshNodeFor: meshId,
|
|
67927
|
+
meshNodeId: nodeId,
|
|
67928
|
+
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
|
|
67929
|
+
// Coordinator-dispatched worker: auto-approve unless mesh/node policy
|
|
67930
|
+
// opts out (default true). Lands in settingsOverride and beats the
|
|
67931
|
+
// global per-provider-type boolean/mode through explicit opposite-key clearing.
|
|
67932
|
+
...delegatedWorkerAutoApproveSettingsForNode(
|
|
67933
|
+
mesh,
|
|
67934
|
+
node,
|
|
67935
|
+
components.providerLoader?.getMeta(resolved.providerType),
|
|
67936
|
+
resolved.providerType
|
|
67937
|
+
),
|
|
67938
|
+
launchedByCoordinator: true,
|
|
67939
|
+
autoLaunchedForQueueTaskId: task.id
|
|
67492
67940
|
};
|
|
67941
|
+
if (launchTarget.mode === "remote") {
|
|
67942
|
+
const remoteSettings = {
|
|
67943
|
+
...launchSettings,
|
|
67944
|
+
meshCoordinatorDaemonId: launchTarget.coordinatorDaemonId,
|
|
67945
|
+
meshCoordinatorNodeId: nodeId
|
|
67946
|
+
};
|
|
67947
|
+
markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
|
|
67948
|
+
let launchResult2;
|
|
67949
|
+
try {
|
|
67950
|
+
launchResult2 = await components.dispatchMeshCommand(launchTarget.daemonId, "launch_cli", withStatusProbeMarker({
|
|
67951
|
+
cliType: resolved.providerType,
|
|
67952
|
+
dir: node.workspace,
|
|
67953
|
+
settings: remoteSettings,
|
|
67954
|
+
// MAGI-KIND-PANEL model axis: forward the task's model override so the
|
|
67955
|
+
// remote worker session launches with it (initialModel). Best-effort.
|
|
67956
|
+
// Slot-aware: task override wins, else the matched slot's model.
|
|
67957
|
+
...effectiveModel ? { initialModel: effectiveModel } : {},
|
|
67958
|
+
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
67959
|
+
...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
|
|
67960
|
+
}));
|
|
67961
|
+
} catch (e) {
|
|
67962
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
|
|
67963
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67964
|
+
sweepExpiredCooldowns();
|
|
67965
|
+
return false;
|
|
67966
|
+
}
|
|
67967
|
+
const payload = launchResult2 && typeof launchResult2 === "object" && "payload" in launchResult2 && launchResult2.payload && typeof launchResult2.payload === "object" ? launchResult2.payload : launchResult2;
|
|
67968
|
+
if (!payload?.success) {
|
|
67969
|
+
const reason = readNonEmptyString(payload?.error) || "remote_launch_cli_failed";
|
|
67970
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
67971
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67972
|
+
sweepExpiredCooldowns();
|
|
67973
|
+
return false;
|
|
67974
|
+
}
|
|
67975
|
+
const remoteSessionId = readNonEmptyString(payload.sessionId) || readNonEmptyString(payload.id) || readNonEmptyString(payload.runtimeSessionId);
|
|
67976
|
+
markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId: remoteSessionId || void 0, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
|
|
67977
|
+
logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, remoteSessionId || void 0);
|
|
67978
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67979
|
+
sweepExpiredCooldowns();
|
|
67980
|
+
return true;
|
|
67981
|
+
}
|
|
67493
67982
|
markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
|
|
67494
|
-
|
|
67495
|
-
|
|
67496
|
-
|
|
67497
|
-
|
|
67498
|
-
|
|
67499
|
-
|
|
67500
|
-
|
|
67501
|
-
|
|
67502
|
-
|
|
67503
|
-
|
|
67504
|
-
|
|
67505
|
-
|
|
67506
|
-
})
|
|
67507
|
-
} catch (e) {
|
|
67508
|
-
markAutoLaunch(meshId, task.id, { status: "failed", reason: `remote_launch_dispatch_failed: ${e?.message || String(e)}`, nodeId, providerType: resolved.providerType });
|
|
67983
|
+
const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
|
|
67984
|
+
cliType: resolved.providerType,
|
|
67985
|
+
dir: node.workspace,
|
|
67986
|
+
settings: launchSettings,
|
|
67987
|
+
// MAGI-KIND-PANEL model axis: local launch forwards the effective model
|
|
67988
|
+
// (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
|
|
67989
|
+
...effectiveModel ? { initialModel: effectiveModel } : {},
|
|
67990
|
+
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
67991
|
+
...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
|
|
67992
|
+
});
|
|
67993
|
+
if (!launchResult?.success) {
|
|
67994
|
+
const reason = launchResult?.error || "launch_cli_failed";
|
|
67995
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
67509
67996
|
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67510
67997
|
sweepExpiredCooldowns();
|
|
67511
67998
|
return false;
|
|
67512
67999
|
}
|
|
67513
|
-
const
|
|
67514
|
-
if (!
|
|
67515
|
-
|
|
67516
|
-
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
68000
|
+
const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
|
|
68001
|
+
if (!sessionId) {
|
|
68002
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
|
|
67517
68003
|
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67518
68004
|
sweepExpiredCooldowns();
|
|
67519
68005
|
return false;
|
|
67520
68006
|
}
|
|
67521
|
-
|
|
67522
|
-
|
|
67523
|
-
|
|
67524
|
-
|
|
67525
|
-
|
|
68007
|
+
markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
|
|
68008
|
+
logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, sessionId);
|
|
68009
|
+
await waitForLocalSessionReady(components, sessionId);
|
|
68010
|
+
const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
|
|
68011
|
+
const routingDecision = buildAutoLaunchRoutingDecision({
|
|
68012
|
+
node,
|
|
68013
|
+
meshId,
|
|
68014
|
+
task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
|
|
68015
|
+
resolved,
|
|
68016
|
+
quotaRouting: mesh?.policy?.quotaRouting ?? null,
|
|
68017
|
+
quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
|
|
68018
|
+
skippedCandidates,
|
|
68019
|
+
requiredTagsResult: {
|
|
68020
|
+
required: requiredTags,
|
|
68021
|
+
satisfied: !requiredTags.length || nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, resolved.providerType)),
|
|
68022
|
+
missing: requiredTags.filter((t) => !buildMeshNodeCapabilityTags(node, resolved.providerType).includes(t))
|
|
68023
|
+
},
|
|
68024
|
+
effectiveModel,
|
|
68025
|
+
effectiveThinkingLevel,
|
|
68026
|
+
executedSlot: slotDecision.slot,
|
|
68027
|
+
demotionReason
|
|
68028
|
+
});
|
|
68029
|
+
tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType, routingDecision);
|
|
67526
68030
|
return true;
|
|
67527
|
-
}
|
|
67528
|
-
|
|
67529
|
-
const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
|
|
67530
|
-
cliType: resolved.providerType,
|
|
67531
|
-
dir: node.workspace,
|
|
67532
|
-
settings: launchSettings,
|
|
67533
|
-
// MAGI-KIND-PANEL model axis: local launch forwards the effective model
|
|
67534
|
-
// (task override, else matched slot) as initialModel (CLI → modelLaunchArgs; ACP → setConfigOption).
|
|
67535
|
-
...effectiveModel ? { initialModel: effectiveModel } : {},
|
|
67536
|
-
// BRAIN-ROUTING thinking axis: forward the effective thinking level (initialThinkingLevel).
|
|
67537
|
-
...effectiveThinkingLevel ? { initialThinkingLevel: effectiveThinkingLevel } : {}
|
|
67538
|
-
});
|
|
67539
|
-
if (!launchResult?.success) {
|
|
67540
|
-
const reason = launchResult?.error || "launch_cli_failed";
|
|
67541
|
-
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
67542
|
-
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67543
|
-
sweepExpiredCooldowns();
|
|
67544
|
-
return false;
|
|
67545
|
-
}
|
|
67546
|
-
const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
|
|
67547
|
-
if (!sessionId) {
|
|
67548
|
-
markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
|
|
68031
|
+
} catch (e) {
|
|
68032
|
+
markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
|
|
67549
68033
|
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67550
|
-
sweepExpiredCooldowns();
|
|
67551
68034
|
return false;
|
|
68035
|
+
} finally {
|
|
68036
|
+
autoLaunchInProgress.delete(launchKey);
|
|
67552
68037
|
}
|
|
67553
|
-
markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId, ...effectiveModel ? { model: effectiveModel } : {}, ...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {} });
|
|
67554
|
-
logAutoLaunchQuotaFallbackSuccess(resolved, task.id, nodeId, sessionId);
|
|
67555
|
-
await waitForLocalSessionReady(components, sessionId);
|
|
67556
|
-
const requiredTags = Array.isArray(task.requiredTags) ? task.requiredTags.filter((t) => !!t) : [];
|
|
67557
|
-
const routingDecision = buildAutoLaunchRoutingDecision({
|
|
67558
|
-
node,
|
|
67559
|
-
meshId,
|
|
67560
|
-
task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
|
|
67561
|
-
resolved,
|
|
67562
|
-
quotaRouting: mesh?.policy?.quotaRouting ?? null,
|
|
67563
|
-
quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
|
|
67564
|
-
skippedCandidates,
|
|
67565
|
-
requiredTagsResult: {
|
|
67566
|
-
required: requiredTags,
|
|
67567
|
-
satisfied: !requiredTags.length || nodeSatisfiesRequiredTags(requiredTags, buildMeshNodeCapabilityTags(node, resolved.providerType)),
|
|
67568
|
-
missing: requiredTags.filter((t) => !buildMeshNodeCapabilityTags(node, resolved.providerType).includes(t))
|
|
67569
|
-
},
|
|
67570
|
-
effectiveModel,
|
|
67571
|
-
effectiveThinkingLevel,
|
|
67572
|
-
executedSlot: slotDecision.slot,
|
|
67573
|
-
demotionReason
|
|
67574
|
-
});
|
|
67575
|
-
tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType, routingDecision);
|
|
67576
|
-
return true;
|
|
67577
|
-
} catch (e) {
|
|
67578
|
-
markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
|
|
67579
|
-
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
67580
|
-
return false;
|
|
67581
|
-
} finally {
|
|
67582
|
-
autoLaunchInProgress.delete(launchKey);
|
|
67583
68038
|
}
|
|
68039
|
+
} finally {
|
|
68040
|
+
autoLaunchTaskInProgress.delete(taskLaunchKey);
|
|
67584
68041
|
}
|
|
67585
68042
|
}
|
|
67586
68043
|
return false;
|
|
@@ -67627,7 +68084,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67627
68084
|
if (instMeshId !== meshId) continue;
|
|
67628
68085
|
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
67629
68086
|
if (!nodeId) continue;
|
|
67630
|
-
if (!
|
|
68087
|
+
if (!isIdleSessionState3(state)) {
|
|
67631
68088
|
const status = readNonEmptyString(state.status).toLowerCase();
|
|
67632
68089
|
skippedSessions.push({
|
|
67633
68090
|
nodeId,
|
|
@@ -67700,6 +68157,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67700
68157
|
}
|
|
67701
68158
|
}
|
|
67702
68159
|
autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
|
|
68160
|
+
sweepAutoLaunchOrphanSessions(components, meshId);
|
|
67703
68161
|
const afterQueue = getQueue(meshId);
|
|
67704
68162
|
const pendingAfter = afterQueue.filter((task) => task.status === "pending").length;
|
|
67705
68163
|
const assignedAfter = afterQueue.filter((task) => task.status === "assigned").length;
|
|
@@ -67758,12 +68216,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67758
68216
|
var LOCAL_LAUNCH_READY_TIMEOUT_MS;
|
|
67759
68217
|
var LOCAL_LAUNCH_READY_POLL_MS;
|
|
67760
68218
|
var autoLaunchInProgress;
|
|
68219
|
+
var autoLaunchTaskInProgress;
|
|
67761
68220
|
var autoLaunchCooldownUntil;
|
|
67762
68221
|
var AUTO_LAUNCH_COOLDOWN_MS;
|
|
67763
|
-
var
|
|
67764
|
-
var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES;
|
|
67765
|
-
var AUTO_LAUNCH_REMOTE_IDLE_TTL_MS;
|
|
67766
|
-
var autoLaunchAwaitClaimBackoff;
|
|
68222
|
+
var AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2;
|
|
67767
68223
|
var init_mesh_queue_assignment = __esm2({
|
|
67768
68224
|
"src/mesh/mesh-queue-assignment.ts"() {
|
|
67769
68225
|
"use strict";
|
|
@@ -67796,14 +68252,17 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67796
68252
|
init_mesh_duplicate_dispatch();
|
|
67797
68253
|
init_mesh_auto_fast_forward();
|
|
67798
68254
|
init_mesh_skip_notify();
|
|
68255
|
+
init_mesh_task_parking();
|
|
67799
68256
|
init_mesh_scheduling_fitness();
|
|
67800
68257
|
init_mesh_queue_observability();
|
|
67801
68258
|
init_mesh_routing_decision();
|
|
68259
|
+
init_mesh_autolaunch_integrity();
|
|
67802
68260
|
init_mesh_difficulty_floor();
|
|
67803
68261
|
init_mesh_difficulty_floor();
|
|
67804
68262
|
init_mesh_auto_fast_forward();
|
|
67805
68263
|
init_mesh_skip_notify();
|
|
67806
68264
|
init_mesh_scheduling_fitness();
|
|
68265
|
+
init_mesh_autolaunch_integrity();
|
|
67807
68266
|
BOOTSTRAP_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["complete", "failed"]);
|
|
67808
68267
|
DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
|
|
67809
68268
|
DISPATCH_CONNECT_TIMEOUT_MS = MESH_CONNECT_TIMEOUT_MS2;
|
|
@@ -67811,12 +68270,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
67811
68270
|
LOCAL_LAUNCH_READY_TIMEOUT_MS = 15e3;
|
|
67812
68271
|
LOCAL_LAUNCH_READY_POLL_MS = 100;
|
|
67813
68272
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
68273
|
+
autoLaunchTaskInProgress = /* @__PURE__ */ new Set();
|
|
67814
68274
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
67815
68275
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
67816
|
-
|
|
67817
|
-
AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES = 2;
|
|
67818
|
-
AUTO_LAUNCH_REMOTE_IDLE_TTL_MS = 5 * 60 * 1e3;
|
|
67819
|
-
autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
|
|
68276
|
+
AUTO_LAUNCH_AWAIT_CLAIM_BACKOFF_CAP_CYCLES2 = 2;
|
|
67820
68277
|
}
|
|
67821
68278
|
});
|
|
67822
68279
|
function readSettings(state) {
|
|
@@ -70550,6 +71007,11 @@ ${cleanBody}`;
|
|
|
70550
71007
|
if (isFalseIdleCompletion(metadataEvent)) return false;
|
|
70551
71008
|
return !!readWorkerResultMetadata(metadataEvent) || !!readNonEmptyString(metadataEvent.finalSummary);
|
|
70552
71009
|
}
|
|
71010
|
+
function resolveGraphEnvelopeWorkerResult(metadataEvent) {
|
|
71011
|
+
const explicit = readWorkerResultMetadata(metadataEvent);
|
|
71012
|
+
if (explicit) return explicit;
|
|
71013
|
+
return extractJsonObjectFromSummary(readNonEmptyString(metadataEvent.finalSummary) || void 0);
|
|
71014
|
+
}
|
|
70553
71015
|
function supersedesTruncatedTerminalSummary(args) {
|
|
70554
71016
|
if (!args.terminalTaskId || !args.eventTaskId || args.terminalTaskId !== args.eventTaskId) return false;
|
|
70555
71017
|
if (!isGenuineCompletionEvidence(args.metadataEvent)) return false;
|
|
@@ -71187,7 +71649,7 @@ ${cleanBody}`;
|
|
|
71187
71649
|
// as the terminal flip (design :317-327 step 2).
|
|
71188
71650
|
envelope: {
|
|
71189
71651
|
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0,
|
|
71190
|
-
workerResult:
|
|
71652
|
+
workerResult: resolveGraphEnvelopeWorkerResult(args.metadataEvent),
|
|
71191
71653
|
nodeId: readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0,
|
|
71192
71654
|
providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0
|
|
71193
71655
|
}
|
|
@@ -82466,6 +82928,7 @@ ${e?.stderr || ""}`;
|
|
|
82466
82928
|
CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES: () => CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES,
|
|
82467
82929
|
CHAT_MESSAGE_VISIBILITIES: () => CHAT_MESSAGE_VISIBILITIES,
|
|
82468
82930
|
COMPACT_STATUS_GOAL_PREVIEW_MAX: () => COMPACT_STATUS_GOAL_PREVIEW_MAX,
|
|
82931
|
+
CTRL_C: () => CTRL_C,
|
|
82469
82932
|
CdpDomHandlers: () => CdpDomHandlers,
|
|
82470
82933
|
CliProviderInstance: () => CliProviderInstance,
|
|
82471
82934
|
DAEMON_WS_PATH: () => DAEMON_WS_PATH2,
|
|
@@ -82475,6 +82938,7 @@ ${e?.stderr || ""}`;
|
|
|
82475
82938
|
DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY: () => DEFAULT_CHAT_TAIL_MISSING_SESSION_POLICY,
|
|
82476
82939
|
DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS: () => DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS2,
|
|
82477
82940
|
DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT2,
|
|
82941
|
+
DEFAULT_DELIVERY_MODE: () => DEFAULT_DELIVERY_MODE,
|
|
82478
82942
|
DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
82479
82943
|
DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
|
|
82480
82944
|
DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
|
|
@@ -82500,6 +82964,7 @@ ${e?.stderr || ""}`;
|
|
|
82500
82964
|
DaemonStatusReporter: () => DaemonStatusReporter,
|
|
82501
82965
|
DevServer: () => DevServer,
|
|
82502
82966
|
DuplicateMeshDispatchError: () => DuplicateMeshDispatchError,
|
|
82967
|
+
ESC: () => ESC,
|
|
82503
82968
|
FsmDriver: () => FsmDriver,
|
|
82504
82969
|
GOAL_PREVIEW_MAX: () => GOAL_PREVIEW_MAX,
|
|
82505
82970
|
GitCommandError: () => GitCommandError,
|
|
@@ -82554,6 +83019,10 @@ ${e?.stderr || ""}`;
|
|
|
82554
83019
|
OPERATING_NOTE_KIND: () => OPERATING_NOTE_KIND,
|
|
82555
83020
|
OPERATING_NOTE_TOMBSTONE_KIND: () => OPERATING_NOTE_TOMBSTONE_KIND,
|
|
82556
83021
|
P2pRelayFailureError: () => P2pRelayFailureError,
|
|
83022
|
+
PARKED_SKIP_REASON: () => PARKED_SKIP_REASON,
|
|
83023
|
+
PARKED_TASK_RETENTION_MS: () => PARKED_TASK_RETENTION_MS,
|
|
83024
|
+
PARK_REASON_PIN_EXPIRED: () => PARK_REASON_PIN_EXPIRED,
|
|
83025
|
+
PARK_RETENTION_EXPIRED_REASON: () => PARK_RETENTION_EXPIRED_REASON,
|
|
82557
83026
|
PROVIDER_CHANNEL_ENV_VAR: () => PROVIDER_CHANNEL_ENV_VAR,
|
|
82558
83027
|
PRUNABLE_ORPHAN_STALE_REASONS: () => PRUNABLE_ORPHAN_STALE_REASONS,
|
|
82559
83028
|
ProviderChannelError: () => ProviderChannelError,
|
|
@@ -82567,6 +83036,7 @@ ${e?.stderr || ""}`;
|
|
|
82567
83036
|
STALE_MAGI_WINDOW_MS: () => STALE_MAGI_WINDOW_MS,
|
|
82568
83037
|
STALE_TERMINAL_REFINE_WINDOW_MS: () => STALE_TERMINAL_REFINE_WINDOW_MS,
|
|
82569
83038
|
STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
|
|
83039
|
+
STOP_CONTROL_ID: () => STOP_CONTROL_ID,
|
|
82570
83040
|
SYM: () => SYM,
|
|
82571
83041
|
SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
|
|
82572
83042
|
StatuslineInstallError: () => StatuslineInstallError2,
|
|
@@ -82617,6 +83087,7 @@ ${e?.stderr || ""}`;
|
|
|
82617
83087
|
buildMissionPromptSection: () => buildMissionPromptSection,
|
|
82618
83088
|
buildOrphanedPinNotice: () => buildOrphanedPinNotice,
|
|
82619
83089
|
buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
|
|
83090
|
+
buildParkedTaskNotice: () => buildParkedTaskNotice,
|
|
82620
83091
|
buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
|
|
82621
83092
|
buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
|
|
82622
83093
|
buildSessionEntries: () => buildSessionEntries,
|
|
@@ -82696,6 +83167,7 @@ ${e?.stderr || ""}`;
|
|
|
82696
83167
|
evaluateProviderQuotaGate: () => evaluateProviderQuotaGate,
|
|
82697
83168
|
execNpmCommandSync: () => execNpmCommandSync,
|
|
82698
83169
|
expandDaemonIdForms: () => expandDaemonIdForms,
|
|
83170
|
+
failRetentionExpiredParkedTask: () => failRetentionExpiredParkedTask,
|
|
82699
83171
|
fastForwardMeshNode: () => fastForwardMeshNode,
|
|
82700
83172
|
fetchAntigravityQuota: () => fetchAntigravityQuota2,
|
|
82701
83173
|
fetchClaudeQuota: () => fetchClaudeQuota2,
|
|
@@ -82748,6 +83220,7 @@ ${e?.stderr || ""}`;
|
|
|
82748
83220
|
getMeshStatusMissionSummaries: () => getMeshStatusMissionSummaries,
|
|
82749
83221
|
getMeshStatusMissionsCompact: () => getMeshStatusMissionsCompact,
|
|
82750
83222
|
getNpmExecOptions: () => getNpmExecOptions,
|
|
83223
|
+
getParkedTasks: () => getParkedTasks,
|
|
82751
83224
|
getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
|
|
82752
83225
|
getProcessInstanceContext: () => getProcessInstanceContext,
|
|
82753
83226
|
getQueue: () => getQueue,
|
|
@@ -82847,6 +83320,7 @@ ${e?.stderr || ""}`;
|
|
|
82847
83320
|
normalizeChatMessageKind: () => normalizeChatMessageKind,
|
|
82848
83321
|
normalizeChatMessages: () => normalizeChatMessages,
|
|
82849
83322
|
normalizeChatTailActiveModal: () => normalizeChatTailActiveModal,
|
|
83323
|
+
normalizeDeliveryMode: () => normalizeDeliveryMode,
|
|
82850
83324
|
normalizeGitOutput: () => normalizeGitOutput,
|
|
82851
83325
|
normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams2,
|
|
82852
83326
|
normalizeInputEnvelope: () => normalizeInputEnvelope,
|
|
@@ -82867,7 +83341,11 @@ ${e?.stderr || ""}`;
|
|
|
82867
83341
|
normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
|
|
82868
83342
|
normalizeSessionModalFields: () => normalizeSessionModalFields,
|
|
82869
83343
|
notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
|
|
83344
|
+
notifyCoordinatorOfParkedTaskDropped: () => notifyCoordinatorOfParkedTaskDropped,
|
|
82870
83345
|
otherTrackConfigDir: () => otherTrackConfigDir,
|
|
83346
|
+
parkTaskTargetPin: () => parkTaskTargetPin,
|
|
83347
|
+
parkedAgeMs: () => parkedAgeMs,
|
|
83348
|
+
parkedTaskRetentionExpired: () => parkedTaskRetentionExpired,
|
|
82871
83349
|
parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
|
|
82872
83350
|
parsePorcelainV2Status: () => parsePorcelainV2Status,
|
|
82873
83351
|
parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
|
|
@@ -82951,6 +83429,7 @@ ${e?.stderr || ""}`;
|
|
|
82951
83429
|
resolveGitRepository: () => resolveGitRepository,
|
|
82952
83430
|
resolveInstanceContext: () => resolveInstanceContext,
|
|
82953
83431
|
resolveInstanceDir: () => resolveInstanceDir,
|
|
83432
|
+
resolveInterruptCapability: () => resolveInterruptCapability,
|
|
82954
83433
|
resolveMagiSessionCleanupMode: () => resolveMagiSessionCleanupMode,
|
|
82955
83434
|
resolveMaxParallelTasks: () => resolveMaxParallelTasks,
|
|
82956
83435
|
resolveMeshHostStatus: () => resolveMeshHostStatus,
|
|
@@ -83008,6 +83487,7 @@ ${e?.stderr || ""}`;
|
|
|
83008
83487
|
supportsUnicodeSymbols: () => supportsUnicodeSymbols,
|
|
83009
83488
|
sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
|
|
83010
83489
|
taskDependenciesSatisfied: () => taskDependenciesSatisfied,
|
|
83490
|
+
taskIsParked: () => taskIsParked,
|
|
83011
83491
|
tombstoneOperatingNote: () => tombstoneOperatingNote,
|
|
83012
83492
|
totalTokens: () => totalTokens,
|
|
83013
83493
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
@@ -85505,6 +85985,8 @@ ${e?.stderr || ""}`;
|
|
|
85505
85985
|
};
|
|
85506
85986
|
}
|
|
85507
85987
|
init_mesh_work_queue();
|
|
85988
|
+
init_mesh_work_queue();
|
|
85989
|
+
init_mesh_task_parking();
|
|
85508
85990
|
init_mesh_graph_derived_failure();
|
|
85509
85991
|
init_mesh_graph_workspace_saga();
|
|
85510
85992
|
init_mesh_graph_workspace_safety();
|
|
@@ -86104,6 +86586,12 @@ ${e?.stderr || ""}`;
|
|
|
86104
86586
|
workspaceRef: ws.workspaceRef,
|
|
86105
86587
|
detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
|
|
86106
86588
|
});
|
|
86589
|
+
} else if (ws.sagaState === "declared" && !ws.baseRevision) {
|
|
86590
|
+
actions.push({
|
|
86591
|
+
kind: "workspace_declared_no_base",
|
|
86592
|
+
workspaceRef: ws.workspaceRef,
|
|
86593
|
+
detail: `Workspace '${ws.workspaceRef}' is stuck in 'declared': no base_revision was given and none could be derived from ${ws.sourceNodeId ? `source node '${ws.sourceNodeId}' (its repo is unreachable on this daemon, or git could not resolve its HEAD)` : "a source node, because the declaration named none"}. The workspace will never clone and every task bound to it stays blocked. Re-declare the workspace with an explicit base_revision (e.g. 'main')${ws.sourceNodeId ? "" : " and a source_node_id"}.`
|
|
86594
|
+
});
|
|
86107
86595
|
}
|
|
86108
86596
|
}
|
|
86109
86597
|
return {
|
|
@@ -86270,6 +86758,47 @@ The pin is NOT cleared automatically: a pin often encodes required context conti
|
|
|
86270
86758
|
init_contracts();
|
|
86271
86759
|
init_mesh_events_utils();
|
|
86272
86760
|
init_mesh_delivery_policy();
|
|
86761
|
+
var CTRL_C = "";
|
|
86762
|
+
var ESC = "\x1B";
|
|
86763
|
+
var STOP_CONTROL_ID = "stop";
|
|
86764
|
+
var LIVE_VERIFIED_PROVIDERS = /* @__PURE__ */ new Set(["claude-cli"]);
|
|
86765
|
+
function nameForKeys(keys) {
|
|
86766
|
+
if (keys === CTRL_C) return "Ctrl-C";
|
|
86767
|
+
if (keys === ESC) return "ESC";
|
|
86768
|
+
return "custom";
|
|
86769
|
+
}
|
|
86770
|
+
function resolveInterruptCapability(providerType, controlBar) {
|
|
86771
|
+
const stop = (controlBar ?? []).find((c) => c.id === STOP_CONTROL_ID);
|
|
86772
|
+
if (!stop) {
|
|
86773
|
+
return {
|
|
86774
|
+
supported: false,
|
|
86775
|
+
reason: "no_stop_control",
|
|
86776
|
+
message: `Provider '${providerType}' declares no '${STOP_CONTROL_ID}' control, so a running turn cannot be interrupted. Dispatch with delivery mode when_idle and the task will be delivered once the session finishes on its own.`
|
|
86777
|
+
};
|
|
86778
|
+
}
|
|
86779
|
+
if (stop.action?.type !== "send_keys") {
|
|
86780
|
+
return {
|
|
86781
|
+
supported: false,
|
|
86782
|
+
reason: "stop_control_not_send_keys",
|
|
86783
|
+
message: `Provider '${providerType}' declares a '${STOP_CONTROL_ID}' control of type '${stop.action?.type}', not a key write, so it cannot be used to abort a turn. Dispatch with delivery mode when_idle instead.`
|
|
86784
|
+
};
|
|
86785
|
+
}
|
|
86786
|
+
const keys = stop.action.keys;
|
|
86787
|
+
if (typeof keys !== "string" || keys.length === 0) {
|
|
86788
|
+
return {
|
|
86789
|
+
supported: false,
|
|
86790
|
+
reason: "stop_keys_empty",
|
|
86791
|
+
message: `Provider '${providerType}' declares a '${STOP_CONTROL_ID}' control with an EMPTY key sequence, so pressing it would write nothing to the terminal while still reporting success. Treating this as unsupported rather than silently doing nothing. Dispatch with delivery mode when_idle, or fix the provider spec to declare a real stop key.`
|
|
86792
|
+
};
|
|
86793
|
+
}
|
|
86794
|
+
return {
|
|
86795
|
+
supported: true,
|
|
86796
|
+
keys,
|
|
86797
|
+
keyName: nameForKeys(keys),
|
|
86798
|
+
confidence: LIVE_VERIFIED_PROVIDERS.has(providerType) ? "proven" : "declared",
|
|
86799
|
+
...stop.visible_when_state ? { visibleWhenState: stop.visible_when_state } : {}
|
|
86800
|
+
};
|
|
86801
|
+
}
|
|
86273
86802
|
var NO_FALLBACK_REASON = "This mesh operation needs a live peer-to-peer connection to the node, which is not open right now. This is often transient \u2014 the peer may be connecting, slow via TURN relay, or briefly offline. Wait a moment and retry, or re-establish the node's connection. Mesh commands use P2P only by design \u2014 there is no cloud/WS relay fallback.";
|
|
86274
86803
|
var P2P_NEXT_ACTION = "The peer connection is recoverable. Wait a moment for the connection to establish (especially over TURN relay), then retry. If the node remains unreachable, check that the target daemon is running and online, then re-establish its connection.";
|
|
86275
86804
|
var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
|
|
@@ -102077,6 +102606,52 @@ ${marker}`,
|
|
|
102077
102606
|
hash: hash2
|
|
102078
102607
|
};
|
|
102079
102608
|
}
|
|
102609
|
+
/**
|
|
102610
|
+
* Report whether this provider can have its in-flight turn interrupted,
|
|
102611
|
+
* resolved from the spec THIS session actually loaded (never a hardcoded
|
|
102612
|
+
* per-provider table — the stop key varies by spec version; hermes-cli
|
|
102613
|
+
* ships Ctrl-C in specs/0.14.json and an EMPTY key in specs/4.0.json).
|
|
102614
|
+
*/
|
|
102615
|
+
getInterruptCapability() {
|
|
102616
|
+
return resolveInterruptCapability(this.cliType, this.spec.control_bar);
|
|
102617
|
+
}
|
|
102618
|
+
/**
|
|
102619
|
+
* Abort the turn currently in flight by writing the provider's own stop
|
|
102620
|
+
* key to the PTY. Used by delivery mode 'interrupt': the caller then waits
|
|
102621
|
+
* for the FSM to report idle and lets the ordinary queued-send drain
|
|
102622
|
+
* deliver the new prompt as a genuine new turn.
|
|
102623
|
+
*
|
|
102624
|
+
* ★ Deliberately NOT routed through invokeScript('stop'). That path calls
|
|
102625
|
+
* FsmDriver.handleClickControl, which silently returns when the control's
|
|
102626
|
+
* `visible_when_state` does not include the current state, and calls
|
|
102627
|
+
* send_keys("") for a provider whose stop key is empty — while
|
|
102628
|
+
* invokeScript unconditionally returns `{ ok: true, effects:[sent_keys] }`
|
|
102629
|
+
* either way. Reporting a successful interrupt that wrote nothing is
|
|
102630
|
+
* exactly the failure this feature exists to remove, so capability is
|
|
102631
|
+
* validated HERE, before any write, and the outcome is reported honestly.
|
|
102632
|
+
*/
|
|
102633
|
+
async interruptTurn() {
|
|
102634
|
+
if (!this.spawned || this.exited) {
|
|
102635
|
+
return { ok: false, reason: "not_running", message: `${this.cliName} is not running.` };
|
|
102636
|
+
}
|
|
102637
|
+
const cap = this.getInterruptCapability();
|
|
102638
|
+
if (!cap.supported) {
|
|
102639
|
+
LOG2.warn("SpecAdapter", `[${this.cliType}] interrupt refused: ${cap.reason}`);
|
|
102640
|
+
return { ok: false, reason: cap.reason, message: cap.message };
|
|
102641
|
+
}
|
|
102642
|
+
const status = this.latestState?.status;
|
|
102643
|
+
if (status !== "generating") {
|
|
102644
|
+
return {
|
|
102645
|
+
ok: false,
|
|
102646
|
+
reason: "not_busy",
|
|
102647
|
+
message: `Session is '${status ?? "unknown"}', not generating \u2014 nothing to interrupt.`
|
|
102648
|
+
};
|
|
102649
|
+
}
|
|
102650
|
+
this.driver.dispatch({ kind: "pty_write", data: cap.keys });
|
|
102651
|
+
const bytes = Buffer.byteLength(cap.keys, "utf8");
|
|
102652
|
+
LOG2.info("SpecAdapter", `[${this.cliType}] turn interrupted via ${cap.keyName} (bytes=${bytes}, confidence=${cap.confidence})`);
|
|
102653
|
+
return { ok: true, keyName: cap.keyName, bytes, confidence: cap.confidence };
|
|
102654
|
+
}
|
|
102080
102655
|
/**
|
|
102081
102656
|
* MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key
|
|
102082
102657
|
* sequence into the spec-driven PTY for mesh_send_keys. Mirrors
|
|
@@ -110692,6 +111267,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
110692
111267
|
} else if (action === "stop") {
|
|
110693
111268
|
await this.stopSession(key2);
|
|
110694
111269
|
return { success: true, stopped: true };
|
|
111270
|
+
} else if (action === "interrupt_capability") {
|
|
111271
|
+
const probe = adapter;
|
|
111272
|
+
if (typeof probe.getInterruptCapability !== "function") {
|
|
111273
|
+
return {
|
|
111274
|
+
success: true,
|
|
111275
|
+
supported: false,
|
|
111276
|
+
reason: "interrupt_not_implemented",
|
|
111277
|
+
message: `Provider '${agentType}' runs on an adapter with no interrupt support.`
|
|
111278
|
+
};
|
|
111279
|
+
}
|
|
111280
|
+
const cap = probe.getInterruptCapability();
|
|
111281
|
+
return { success: true, ...cap };
|
|
111282
|
+
} else if (action === "interrupt_turn") {
|
|
111283
|
+
const interruptible = adapter;
|
|
111284
|
+
if (typeof interruptible.interruptTurn !== "function") {
|
|
111285
|
+
return {
|
|
111286
|
+
success: false,
|
|
111287
|
+
interrupted: false,
|
|
111288
|
+
reason: "interrupt_not_implemented",
|
|
111289
|
+
error: `Provider '${agentType}' runs on an adapter that cannot interrupt a turn.`
|
|
111290
|
+
};
|
|
111291
|
+
}
|
|
111292
|
+
const outcome = await interruptible.interruptTurn();
|
|
111293
|
+
if (!outcome.ok) {
|
|
111294
|
+
return { success: false, interrupted: false, reason: outcome.reason, error: outcome.message };
|
|
111295
|
+
}
|
|
111296
|
+
return {
|
|
111297
|
+
success: true,
|
|
111298
|
+
interrupted: true,
|
|
111299
|
+
keyName: outcome.keyName,
|
|
111300
|
+
bytes: outcome.bytes,
|
|
111301
|
+
confidence: outcome.confidence
|
|
111302
|
+
};
|
|
110695
111303
|
}
|
|
110696
111304
|
throw new Error(`Unknown action: ${action}`);
|
|
110697
111305
|
}
|
|
@@ -110854,7 +111462,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
110854
111462
|
const { shouldDeferDispatchForBootstrap: shouldDeferDispatchForBootstrap2 } = await Promise.resolve().then(() => (init_worktree_bootstrap_config(), worktree_bootstrap_config_exports));
|
|
110855
111463
|
if (shouldDeferDispatchForBootstrap2(nodeObj)) {
|
|
110856
111464
|
const dispatchSessionState = dispatchSessionId ? ctx.deps.instanceManager?.getInstance?.(dispatchSessionId)?.getState?.() : void 0;
|
|
110857
|
-
const sessionConfirmedReady = !!dispatchSessionState &&
|
|
111465
|
+
const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState3(dispatchSessionState);
|
|
110858
111466
|
if (!sessionConfirmedReady) {
|
|
110859
111467
|
return {
|
|
110860
111468
|
success: false,
|
|
@@ -118475,7 +119083,12 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
118475
119083
|
// CANON-IDENTITY: an in-flight (actively-generating) task is refused by
|
|
118476
119084
|
// default to avoid a duplicate second dispatch; an explicit operator
|
|
118477
119085
|
// force overrides that guard (and the retry cap).
|
|
118478
|
-
force: args?.force === true
|
|
119086
|
+
force: args?.force === true,
|
|
119087
|
+
// PIN-PARKING (edit): optional instruction rewrite, so a coordinator in
|
|
119088
|
+
// IPC/cloud mode has the same exit from parking as the in-process path.
|
|
119089
|
+
// Without this the field would be silently dropped exactly where the
|
|
119090
|
+
// multi-coordinator topology needs it most.
|
|
119091
|
+
message: typeof args?.message === "string" ? args.message : void 0
|
|
118479
119092
|
});
|
|
118480
119093
|
if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
|
|
118481
119094
|
if (task.status === "assigned" && args?.force !== true) {
|