@adhdev/daemon-core 0.9.82-rc.350 → 0.9.82-rc.352
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/commands/upgrade-helper.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +528 -216
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +526 -216
- package/dist/index.mjs.map +1 -1
- package/dist/logging/logger.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-runtime-store.d.ts +10 -0
- package/dist/mesh/mesh-work-queue.d.ts +28 -0
- package/dist/providers/approval-utils.d.ts +7 -0
- package/dist/providers/cli-provider-instance.d.ts +15 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +1 -0
- package/package.json +2 -2
- package/src/commands/upgrade-helper.ts +57 -14
- package/src/index.ts +5 -0
- package/src/logging/command-log.ts +7 -5
- package/src/logging/logger.ts +12 -6
- package/src/mesh/mesh-active-work.ts +64 -20
- package/src/mesh/mesh-events-coordinator.ts +249 -89
- package/src/mesh/mesh-events-pending.ts +27 -19
- package/src/mesh/mesh-events-stale.ts +17 -0
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-reconcile-loop.ts +67 -7
- package/src/mesh/mesh-runtime-store.ts +22 -1
- package/src/mesh/mesh-task-stats.ts +8 -1
- package/src/mesh/mesh-work-queue.ts +89 -0
- package/src/providers/approval-utils.d.ts +1 -0
- package/src/providers/approval-utils.ts +10 -0
- package/src/providers/cli-provider-instance.ts +73 -19
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +51 -0
package/dist/index.mjs
CHANGED
|
@@ -311,10 +311,10 @@ function readInjected(value) {
|
|
|
311
311
|
}
|
|
312
312
|
function getDaemonBuildInfo() {
|
|
313
313
|
if (cached) return cached;
|
|
314
|
-
const commit = readInjected(true ? "
|
|
315
|
-
const commitShort = readInjected(true ? "
|
|
316
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
317
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
314
|
+
const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
|
|
315
|
+
const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
316
|
+
const version = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
317
|
+
const builtAt = readInjected(true ? "2026-06-22T07:17:05.715Z" : void 0);
|
|
318
318
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
319
319
|
return cached;
|
|
320
320
|
}
|
|
@@ -3193,7 +3193,7 @@ function installGlobalInterceptor() {
|
|
|
3193
3193
|
function getLogPath() {
|
|
3194
3194
|
return currentLogFile;
|
|
3195
3195
|
}
|
|
3196
|
-
var LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
3196
|
+
var LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
3197
3197
|
var init_logger = __esm({
|
|
3198
3198
|
"src/logging/logger.ts"() {
|
|
3199
3199
|
"use strict";
|
|
@@ -3201,7 +3201,8 @@ var init_logger = __esm({
|
|
|
3201
3201
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
3202
3202
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
3203
3203
|
currentLevel = "info";
|
|
3204
|
-
|
|
3204
|
+
ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
|
|
3205
|
+
LOG_DIR = path9.join(ADHDEV_HOME, "logs");
|
|
3205
3206
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
3206
3207
|
MAX_LOG_DAYS = 7;
|
|
3207
3208
|
try {
|
|
@@ -4001,6 +4002,7 @@ __export(mesh_work_queue_exports, {
|
|
|
4001
4002
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
4002
4003
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
4003
4004
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
4005
|
+
reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
|
|
4004
4006
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
4005
4007
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
4006
4008
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
@@ -4437,6 +4439,52 @@ function requeueTask(meshId, taskId, opts) {
|
|
|
4437
4439
|
return entry;
|
|
4438
4440
|
});
|
|
4439
4441
|
}
|
|
4442
|
+
function reclaimStrandedAssignedTask(meshId, taskId, opts) {
|
|
4443
|
+
requireMeshHostQueueOwner(opts);
|
|
4444
|
+
return withQueueLock(meshId, () => {
|
|
4445
|
+
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
4446
|
+
if (!entry) return null;
|
|
4447
|
+
if (entry.status !== "assigned") return null;
|
|
4448
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4449
|
+
const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
|
|
4450
|
+
const reclaims = (entry.strandedReclaimCount || 0) + 1;
|
|
4451
|
+
const prevNode = entry.assignedNodeId;
|
|
4452
|
+
const prevSession = entry.assignedSessionId;
|
|
4453
|
+
delete entry.assignedNodeId;
|
|
4454
|
+
delete entry.assignedSessionId;
|
|
4455
|
+
delete entry.assignedProviderType;
|
|
4456
|
+
delete entry.dispatchTimestamp;
|
|
4457
|
+
entry.strandedReclaimCount = reclaims;
|
|
4458
|
+
entry.updatedAt = now;
|
|
4459
|
+
if (reclaims > MAX_STRANDED_RECLAIMS) {
|
|
4460
|
+
entry.status = "failed";
|
|
4461
|
+
entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
|
|
4462
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4463
|
+
propagateDependencyFailure(meshId, taskId);
|
|
4464
|
+
} else {
|
|
4465
|
+
entry.status = "pending";
|
|
4466
|
+
entry.requeuedAt = now;
|
|
4467
|
+
entry.requeueReason = reason;
|
|
4468
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4469
|
+
}
|
|
4470
|
+
try {
|
|
4471
|
+
appendLedgerEntry(meshId, {
|
|
4472
|
+
kind: "task_reclaimed",
|
|
4473
|
+
nodeId: prevNode,
|
|
4474
|
+
sessionId: prevSession,
|
|
4475
|
+
payload: {
|
|
4476
|
+
taskId,
|
|
4477
|
+
reason,
|
|
4478
|
+
...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
|
|
4479
|
+
reclaimCount: reclaims,
|
|
4480
|
+
outcome: entry.status
|
|
4481
|
+
}
|
|
4482
|
+
});
|
|
4483
|
+
} catch {
|
|
4484
|
+
}
|
|
4485
|
+
return entry;
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4440
4488
|
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
4441
4489
|
return withQueueLock(meshId, () => {
|
|
4442
4490
|
const store = MeshRuntimeStore.getInstance();
|
|
@@ -4550,7 +4598,7 @@ function recordMeshToolCall(opts) {
|
|
|
4550
4598
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
4551
4599
|
}
|
|
4552
4600
|
}
|
|
4553
|
-
var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS;
|
|
4601
|
+
var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS;
|
|
4554
4602
|
var init_mesh_work_queue = __esm({
|
|
4555
4603
|
"src/mesh/mesh-work-queue.ts"() {
|
|
4556
4604
|
"use strict";
|
|
@@ -4559,6 +4607,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4559
4607
|
init_mesh_runtime_store();
|
|
4560
4608
|
init_mesh_config();
|
|
4561
4609
|
init_logger();
|
|
4610
|
+
init_mesh_ledger();
|
|
4562
4611
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
4563
4612
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
4564
4613
|
MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
|
|
@@ -4611,6 +4660,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4611
4660
|
]);
|
|
4612
4661
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
4613
4662
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
4663
|
+
MAX_STRANDED_RECLAIMS = 3;
|
|
4614
4664
|
}
|
|
4615
4665
|
});
|
|
4616
4666
|
|
|
@@ -4670,6 +4720,10 @@ var init_mesh_runtime_store = __esm({
|
|
|
4670
4720
|
migratedMeshIds = /* @__PURE__ */ new Set();
|
|
4671
4721
|
fingerprintSweepCounter = 0;
|
|
4672
4722
|
walWriteCounter = 0;
|
|
4723
|
+
// Independent cadence for the tool-call-log sweep. Must NOT share walWriteCounter:
|
|
4724
|
+
// sharing makes each store's threshold drift by the other's write volume (WAL
|
|
4725
|
+
// checkpoint at 500 vs tool-log sweep at 200 would interfere arbitrarily).
|
|
4726
|
+
toolCallLogCounter = 0;
|
|
4673
4727
|
static WAL_CHECK_INTERVAL = 500;
|
|
4674
4728
|
static WAL_MAX_BYTES = 50 * 1024 * 1024;
|
|
4675
4729
|
// 50 MB
|
|
@@ -5492,6 +5546,22 @@ var init_mesh_runtime_store = __esm({
|
|
|
5492
5546
|
updatedAt: r.updated_at
|
|
5493
5547
|
}));
|
|
5494
5548
|
}
|
|
5549
|
+
/**
|
|
5550
|
+
* Bug B watchdog support: true when at least one delivery record for the task has
|
|
5551
|
+
* reached a confirmed-handed-off status (delivered / acked / completed). The
|
|
5552
|
+
* assigned-stranded watchdog uses this to distinguish a dispatch that was never
|
|
5553
|
+
* confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
|
|
5554
|
+
* in-flight or completion-lost task, which is PHASE 4's responsibility, not this
|
|
5555
|
+
* watchdog's). Indexed by (mesh_id, task_id).
|
|
5556
|
+
*/
|
|
5557
|
+
taskHasConfirmedDelivery(meshId, taskId) {
|
|
5558
|
+
const row = this.db.prepare(`
|
|
5559
|
+
SELECT 1 FROM mesh_session_delivery
|
|
5560
|
+
WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
|
|
5561
|
+
LIMIT 1
|
|
5562
|
+
`).get(meshId, taskId);
|
|
5563
|
+
return !!row;
|
|
5564
|
+
}
|
|
5495
5565
|
expireStaleSessionDeliveries(meshId) {
|
|
5496
5566
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5497
5567
|
this.db.prepare(`
|
|
@@ -5563,7 +5633,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
5563
5633
|
"SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
|
|
5564
5634
|
).get(meshId, tool, windowStart);
|
|
5565
5635
|
const callsInWindow = row?.cnt ?? 0;
|
|
5566
|
-
if (++this.
|
|
5636
|
+
if (++this.toolCallLogCounter % 200 === 0) {
|
|
5567
5637
|
this.db.prepare(
|
|
5568
5638
|
"DELETE FROM mesh_tool_call_log WHERE called_at < ?"
|
|
5569
5639
|
).run(now - Math.max(windowMs * 10, 6e4));
|
|
@@ -6018,9 +6088,9 @@ function computeMeshTaskStats(meshId, opts) {
|
|
|
6018
6088
|
}
|
|
6019
6089
|
return targetIds.map((taskId) => {
|
|
6020
6090
|
const queueEntry = queueById.get(taskId);
|
|
6021
|
-
const status = queueEntry?.status ?? "unknown";
|
|
6022
6091
|
const dispatch = dispatches.get(taskId);
|
|
6023
6092
|
const terminal = terminals.get(taskId);
|
|
6093
|
+
const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
|
|
6024
6094
|
const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
|
|
6025
6095
|
const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
|
|
6026
6096
|
const terminalTime = parseTime(terminal?.at);
|
|
@@ -7915,6 +7985,35 @@ function meshNodeIdMatches(node, candidateId) {
|
|
|
7915
7985
|
if (!trimmed) return false;
|
|
7916
7986
|
return normalizeMeshNodeId(node) === trimmed;
|
|
7917
7987
|
}
|
|
7988
|
+
function machineCoreFromDaemonId(id) {
|
|
7989
|
+
const trimmed = readString5(id);
|
|
7990
|
+
if (!trimmed) return void 0;
|
|
7991
|
+
for (const prefix of DAEMON_ID_PREFIXES) {
|
|
7992
|
+
if (trimmed.startsWith(prefix)) {
|
|
7993
|
+
const core = trimmed.slice(prefix.length).trim();
|
|
7994
|
+
return core || void 0;
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
return trimmed;
|
|
7998
|
+
}
|
|
7999
|
+
function expandDaemonIdForms(ids) {
|
|
8000
|
+
const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
|
|
8001
|
+
const out = [];
|
|
8002
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8003
|
+
const add = (value) => {
|
|
8004
|
+
if (!value || seen.has(value)) return;
|
|
8005
|
+
seen.add(value);
|
|
8006
|
+
out.push(value);
|
|
8007
|
+
};
|
|
8008
|
+
for (const raw of list) add(readString5(raw));
|
|
8009
|
+
for (const raw of list) {
|
|
8010
|
+
const core = machineCoreFromDaemonId(readString5(raw));
|
|
8011
|
+
if (!core || !core.startsWith("mach_")) continue;
|
|
8012
|
+
add(core);
|
|
8013
|
+
for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
|
|
8014
|
+
}
|
|
8015
|
+
return out;
|
|
8016
|
+
}
|
|
7918
8017
|
function summarizeGitShape(status) {
|
|
7919
8018
|
const record = readRecord3(status);
|
|
7920
8019
|
if (!Object.keys(record).length) return null;
|
|
@@ -7949,9 +8048,11 @@ function summarizeGitShape(status) {
|
|
|
7949
8048
|
submodules
|
|
7950
8049
|
};
|
|
7951
8050
|
}
|
|
8051
|
+
var DAEMON_ID_PREFIXES;
|
|
7952
8052
|
var init_dist = __esm({
|
|
7953
8053
|
"../mesh-shared/dist/index.mjs"() {
|
|
7954
8054
|
"use strict";
|
|
8055
|
+
DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
|
|
7955
8056
|
}
|
|
7956
8057
|
});
|
|
7957
8058
|
|
|
@@ -8029,6 +8130,14 @@ function statusFromTerminal(entry) {
|
|
|
8029
8130
|
if (entry.kind === "task_completed") return "idle";
|
|
8030
8131
|
return "failed";
|
|
8031
8132
|
}
|
|
8133
|
+
function classifyDirectDispatch(params) {
|
|
8134
|
+
const { status, isTerminalRow, hasTerminalStatus, liveStatus, liveStaleReason, dispatchedToIdleSession } = params;
|
|
8135
|
+
const isNoTransition = !hasTerminalStatus && !liveStatus;
|
|
8136
|
+
const isIdleUnacknowledged = status === "idle";
|
|
8137
|
+
const ledgerOnlyStaleReason = !isTerminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? LEDGER_ONLY_STALE_REASON : void 0;
|
|
8138
|
+
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !liveStaleReason);
|
|
8139
|
+
return { ledgerOnlyStaleReason, isFreshUnacknowledged };
|
|
8140
|
+
}
|
|
8032
8141
|
function buildMeshActiveWorkSummary(activeWork) {
|
|
8033
8142
|
const statusCounts = {
|
|
8034
8143
|
pending: 0,
|
|
@@ -8091,10 +8200,14 @@ function buildMeshActiveWork(opts) {
|
|
|
8091
8200
|
const dbStatus = dispatch.status;
|
|
8092
8201
|
const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
|
|
8093
8202
|
const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
|
|
8094
|
-
const
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8203
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8204
|
+
status,
|
|
8205
|
+
isTerminalRow: isTerminal,
|
|
8206
|
+
hasTerminalStatus: isTerminal,
|
|
8207
|
+
liveStatus: live.status,
|
|
8208
|
+
liveStaleReason: live.staleReason,
|
|
8209
|
+
dispatchedToIdleSession: dispatch.dispatchedToIdleSession === true
|
|
8210
|
+
});
|
|
8098
8211
|
const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
|
|
8099
8212
|
const record = {
|
|
8100
8213
|
taskId: dispatch.taskId,
|
|
@@ -8137,13 +8250,16 @@ function buildMeshActiveWork(opts) {
|
|
|
8137
8250
|
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
8138
8251
|
const status = terminalStatus || live.status || "assigned";
|
|
8139
8252
|
const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
|
|
8140
|
-
const
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8253
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8254
|
+
status,
|
|
8255
|
+
isTerminalRow: terminalRow,
|
|
8256
|
+
hasTerminalStatus: Boolean(terminalStatus),
|
|
8257
|
+
liveStatus: live.status,
|
|
8258
|
+
liveStaleReason: live.staleReason,
|
|
8259
|
+
dispatchedToIdleSession: dispatch.payload?.dispatchedToIdleSession === true
|
|
8260
|
+
});
|
|
8144
8261
|
const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
|
|
8145
8262
|
const { title, summary: summary2 } = summarizeMessage(message);
|
|
8146
|
-
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
|
|
8147
8263
|
const record = {
|
|
8148
8264
|
taskId,
|
|
8149
8265
|
source: "direct",
|
|
@@ -8185,13 +8301,16 @@ function buildMeshActiveWork(opts) {
|
|
|
8185
8301
|
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
8186
8302
|
const status = terminalStatus || live.status || "assigned";
|
|
8187
8303
|
const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
|
|
8188
|
-
const
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8304
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8305
|
+
status,
|
|
8306
|
+
isTerminalRow: terminalRow,
|
|
8307
|
+
hasTerminalStatus: Boolean(terminalStatus),
|
|
8308
|
+
liveStatus: live.status,
|
|
8309
|
+
liveStaleReason: live.staleReason,
|
|
8310
|
+
dispatchedToIdleSession: dispatch.payload?.dispatchedToIdleSession === true
|
|
8311
|
+
});
|
|
8192
8312
|
const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
|
|
8193
8313
|
const { title, summary: summary2 } = summarizeMessage(message);
|
|
8194
|
-
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
|
|
8195
8314
|
const record = {
|
|
8196
8315
|
taskId,
|
|
8197
8316
|
source: "direct",
|
|
@@ -8342,7 +8461,7 @@ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
|
|
|
8342
8461
|
...opts.note ? { note: opts.note } : {}
|
|
8343
8462
|
};
|
|
8344
8463
|
}
|
|
8345
|
-
var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, PRUNABLE_ORPHAN_STALE_REASONS;
|
|
8464
|
+
var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, LEDGER_ONLY_STALE_REASON, PRUNABLE_ORPHAN_STALE_REASONS;
|
|
8346
8465
|
var init_mesh_active_work = __esm({
|
|
8347
8466
|
"src/mesh/mesh-active-work.ts"() {
|
|
8348
8467
|
"use strict";
|
|
@@ -8351,6 +8470,7 @@ var init_mesh_active_work = __esm({
|
|
|
8351
8470
|
init_dist();
|
|
8352
8471
|
DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
|
|
8353
8472
|
TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
|
|
8473
|
+
LEDGER_ONLY_STALE_REASON = "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition";
|
|
8354
8474
|
PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
|
|
8355
8475
|
"direct task node is no longer in the live mesh",
|
|
8356
8476
|
"direct task session is not present in live session records",
|
|
@@ -8592,17 +8712,7 @@ import { appendFileSync as appendFileSync2, existsSync as existsSync14, readFile
|
|
|
8592
8712
|
import { join as join15 } from "path";
|
|
8593
8713
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8594
8714
|
function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
8595
|
-
|
|
8596
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8597
|
-
const out = [];
|
|
8598
|
-
for (const id of raw) {
|
|
8599
|
-
if (typeof id !== "string") continue;
|
|
8600
|
-
const trimmed = id.trim();
|
|
8601
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
8602
|
-
seen.add(trimmed);
|
|
8603
|
-
out.push(trimmed);
|
|
8604
|
-
}
|
|
8605
|
-
return out;
|
|
8715
|
+
return expandDaemonIdForms(coordinatorDaemonId);
|
|
8606
8716
|
}
|
|
8607
8717
|
function readRefineJobId2(event) {
|
|
8608
8718
|
const metadata = readRecord4(event.metadataEvent) || event;
|
|
@@ -8790,6 +8900,7 @@ function queuePendingMeshCoordinatorEvent(event) {
|
|
|
8790
8900
|
return true;
|
|
8791
8901
|
}
|
|
8792
8902
|
const fingerprint = buildPendingEventFingerprint(event);
|
|
8903
|
+
let sqliteOk = false;
|
|
8793
8904
|
try {
|
|
8794
8905
|
MeshRuntimeStore.getInstance().insertPendingEvent({
|
|
8795
8906
|
id: randomUUID7(),
|
|
@@ -8800,11 +8911,17 @@ function queuePendingMeshCoordinatorEvent(event) {
|
|
|
8800
8911
|
fingerprint: fingerprint || null,
|
|
8801
8912
|
queuedAt: event.queuedAt
|
|
8802
8913
|
});
|
|
8914
|
+
sqliteOk = true;
|
|
8803
8915
|
} catch {
|
|
8804
8916
|
}
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8917
|
+
try {
|
|
8918
|
+
const path42 = getPendingEventsPath(event.meshId, event.targetCoordinatorDaemonId);
|
|
8919
|
+
trimPendingEventsIfNeeded(path42);
|
|
8920
|
+
appendFileSync2(path42, JSON.stringify(event) + "\n", "utf-8");
|
|
8921
|
+
} catch (e) {
|
|
8922
|
+
if (!sqliteOk) throw e;
|
|
8923
|
+
LOG.warn("MeshEvents", `JSONL append failed for mesh ${event.meshId}; SQLite holds the event: ${e?.message || e}`);
|
|
8924
|
+
}
|
|
8808
8925
|
return true;
|
|
8809
8926
|
} catch (e) {
|
|
8810
8927
|
LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
@@ -8982,6 +9099,7 @@ var init_mesh_events_pending = __esm({
|
|
|
8982
9099
|
init_mesh_ledger();
|
|
8983
9100
|
init_mesh_runtime_store();
|
|
8984
9101
|
init_mesh_events_utils();
|
|
9102
|
+
init_dist();
|
|
8985
9103
|
REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
|
|
8986
9104
|
MAX_PENDING_EVENTS_BYTES = 100 * 1024;
|
|
8987
9105
|
MAX_PENDING_EVENTS_KEEP = 50;
|
|
@@ -9200,6 +9318,12 @@ function hasUnterminalDirectDispatchLedgerEntry(meshId, sessionId) {
|
|
|
9200
9318
|
}
|
|
9201
9319
|
return false;
|
|
9202
9320
|
}
|
|
9321
|
+
function isWeakCompletionLedgerPayload(payload) {
|
|
9322
|
+
if (!payload) return false;
|
|
9323
|
+
if (payload.evidenceLevel === "insufficient" || payload.reviewRecommended === true) return true;
|
|
9324
|
+
const diag = readRecord4(payload.completionDiagnostic);
|
|
9325
|
+
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
9326
|
+
}
|
|
9203
9327
|
function findDirectDispatchLedgerEntry(args) {
|
|
9204
9328
|
const entries = readLedgerEntries(args.meshId, { tail: 500 });
|
|
9205
9329
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
@@ -9236,6 +9360,7 @@ function hasTerminalLedgerAfterDispatch(args) {
|
|
|
9236
9360
|
if (!afterDispatch) continue;
|
|
9237
9361
|
}
|
|
9238
9362
|
if (entry.kind !== "task_completed" && entry.kind !== "task_failed" && entry.kind !== "task_stalled") continue;
|
|
9363
|
+
if (entry.kind === "task_completed" && isWeakCompletionLedgerPayload(entry.payload)) continue;
|
|
9239
9364
|
const terminalTaskId = readNonEmptyString2(entry.payload?.taskId);
|
|
9240
9365
|
if (terminalTaskId && terminalTaskId === args.taskId) return true;
|
|
9241
9366
|
if (terminalTaskId && terminalTaskId !== args.taskId) continue;
|
|
@@ -12429,12 +12554,9 @@ var init_snapshot = __esm({
|
|
|
12429
12554
|
// src/mesh/mesh-events-coordinator.ts
|
|
12430
12555
|
import { existsSync as existsSync17 } from "fs";
|
|
12431
12556
|
function resolveCoordinatorDrainDaemonIds(components) {
|
|
12432
|
-
const ids = /* @__PURE__ */ new Set();
|
|
12433
12557
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
12434
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
12435
12558
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
12436
|
-
|
|
12437
|
-
return [...ids];
|
|
12559
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
12438
12560
|
}
|
|
12439
12561
|
function getCachedMeshByWorkspace(workspace) {
|
|
12440
12562
|
const now = Date.now();
|
|
@@ -12561,6 +12683,79 @@ function isDuplicateRefineTerminalEvent(meshId, eventName, metadataEvent) {
|
|
|
12561
12683
|
recordFingerprintSeen(fingerprint);
|
|
12562
12684
|
return false;
|
|
12563
12685
|
}
|
|
12686
|
+
function isFalseIdleCompletion(metadataEvent) {
|
|
12687
|
+
const diag = readRecord4(metadataEvent.completionDiagnostic);
|
|
12688
|
+
if (!diag) return false;
|
|
12689
|
+
return diag.finalAssistantPresent === false || diag.blockReason === "missing_final_assistant";
|
|
12690
|
+
}
|
|
12691
|
+
function isGenuineCompletionEvidence(metadataEvent) {
|
|
12692
|
+
if (isFalseIdleCompletion(metadataEvent)) return false;
|
|
12693
|
+
return !!readWorkerResultMetadata(metadataEvent) || !!readNonEmptyString2(metadataEvent.finalSummary);
|
|
12694
|
+
}
|
|
12695
|
+
function isWeakTerminalLedgerPayload(payload) {
|
|
12696
|
+
if (!payload) return false;
|
|
12697
|
+
if (payload.evidenceLevel === "insufficient" || payload.reviewRecommended === true) return true;
|
|
12698
|
+
const diag = readRecord4(payload.completionDiagnostic);
|
|
12699
|
+
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
12700
|
+
}
|
|
12701
|
+
function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
12702
|
+
try {
|
|
12703
|
+
const matches = getActiveDirectDispatches(meshId).filter((d) => d.sessionId === sessionId);
|
|
12704
|
+
if (!matches.length) return void 0;
|
|
12705
|
+
return readNonEmptyString2(matches[matches.length - 1].taskId) || void 0;
|
|
12706
|
+
} catch {
|
|
12707
|
+
return void 0;
|
|
12708
|
+
}
|
|
12709
|
+
}
|
|
12710
|
+
function deliverTaskToSession(dispatchThunk, ctx) {
|
|
12711
|
+
const delivery = createSessionDelivery({
|
|
12712
|
+
meshId: ctx.meshId,
|
|
12713
|
+
nodeId: ctx.nodeId,
|
|
12714
|
+
sessionId: ctx.sessionId,
|
|
12715
|
+
providerType: ctx.providerType,
|
|
12716
|
+
taskId: ctx.task.id,
|
|
12717
|
+
kind: "task",
|
|
12718
|
+
message: ctx.task.message,
|
|
12719
|
+
status: "delivering",
|
|
12720
|
+
...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
|
|
12721
|
+
...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
|
|
12722
|
+
});
|
|
12723
|
+
let dispatchPromise;
|
|
12724
|
+
try {
|
|
12725
|
+
dispatchPromise = Promise.resolve(dispatchThunk());
|
|
12726
|
+
} catch (e) {
|
|
12727
|
+
dispatchPromise = Promise.reject(e);
|
|
12728
|
+
}
|
|
12729
|
+
let timer;
|
|
12730
|
+
const guarded = Promise.race([
|
|
12731
|
+
dispatchPromise,
|
|
12732
|
+
new Promise((_, reject) => {
|
|
12733
|
+
timer = setTimeout(
|
|
12734
|
+
() => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
|
|
12735
|
+
DISPATCH_CONFIRM_TIMEOUT_MS
|
|
12736
|
+
);
|
|
12737
|
+
if (typeof timer?.unref === "function") timer.unref();
|
|
12738
|
+
})
|
|
12739
|
+
]);
|
|
12740
|
+
guarded.then(() => {
|
|
12741
|
+
if (timer) clearTimeout(timer);
|
|
12742
|
+
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12743
|
+
}).catch((e) => {
|
|
12744
|
+
if (timer) clearTimeout(timer);
|
|
12745
|
+
LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
|
|
12746
|
+
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12747
|
+
updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
|
|
12748
|
+
try {
|
|
12749
|
+
appendLedgerEntry(ctx.meshId, {
|
|
12750
|
+
kind: "dispatch_failed",
|
|
12751
|
+
nodeId: ctx.nodeId,
|
|
12752
|
+
sessionId: ctx.sessionId,
|
|
12753
|
+
payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
|
|
12754
|
+
});
|
|
12755
|
+
} catch {
|
|
12756
|
+
}
|
|
12757
|
+
});
|
|
12758
|
+
}
|
|
12564
12759
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12565
12760
|
const mesh = getMeshWithCache(components, meshId);
|
|
12566
12761
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
@@ -12579,46 +12774,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12579
12774
|
if (!isLocalNode) {
|
|
12580
12775
|
const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
12581
12776
|
const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
|
|
12582
|
-
const
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
meshContext: {
|
|
12777
|
+
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
12778
|
+
const remoteDaemonId = node.daemonId;
|
|
12779
|
+
deliverTaskToSession(
|
|
12780
|
+
() => dispatchMeshCommand(remoteDaemonId, "agent_command", {
|
|
12781
|
+
targetSessionId: sessionId,
|
|
12782
|
+
cliType: providerType,
|
|
12783
|
+
action: "send_chat",
|
|
12784
|
+
message: task.message,
|
|
12785
|
+
meshContext: {
|
|
12786
|
+
meshId,
|
|
12787
|
+
nodeId,
|
|
12788
|
+
taskId: task.id,
|
|
12789
|
+
...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
|
|
12790
|
+
...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
|
|
12791
|
+
}
|
|
12792
|
+
}),
|
|
12793
|
+
{
|
|
12600
12794
|
meshId,
|
|
12601
12795
|
nodeId,
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
}).catch((e) => {
|
|
12609
|
-
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
12610
|
-
updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12611
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12612
|
-
try {
|
|
12613
|
-
appendLedgerEntry(meshId, {
|
|
12614
|
-
kind: "dispatch_failed",
|
|
12615
|
-
nodeId,
|
|
12616
|
-
sessionId,
|
|
12617
|
-
payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
|
|
12618
|
-
});
|
|
12619
|
-
} catch {
|
|
12796
|
+
sessionId,
|
|
12797
|
+
providerType,
|
|
12798
|
+
task,
|
|
12799
|
+
transport: "remote",
|
|
12800
|
+
...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
|
|
12801
|
+
...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
|
|
12620
12802
|
}
|
|
12621
|
-
|
|
12803
|
+
);
|
|
12622
12804
|
return true;
|
|
12623
12805
|
}
|
|
12624
12806
|
}
|
|
@@ -12640,39 +12822,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12640
12822
|
}
|
|
12641
12823
|
} catch {
|
|
12642
12824
|
}
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
message: task.message
|
|
12660
|
-
}).then(() => {
|
|
12661
|
-
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12662
|
-
}).catch((e) => {
|
|
12663
|
-
LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
|
|
12664
|
-
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12665
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12666
|
-
try {
|
|
12667
|
-
appendLedgerEntry(meshId, {
|
|
12668
|
-
kind: "dispatch_failed",
|
|
12669
|
-
nodeId,
|
|
12670
|
-
sessionId,
|
|
12671
|
-
payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
|
|
12672
|
-
});
|
|
12673
|
-
} catch {
|
|
12825
|
+
deliverTaskToSession(
|
|
12826
|
+
() => components.cliManager.handleCliCommand("agent_command", {
|
|
12827
|
+
targetSessionId: sessionId,
|
|
12828
|
+
cliType: providerType,
|
|
12829
|
+
action: "send_chat",
|
|
12830
|
+
message: task.message
|
|
12831
|
+
}),
|
|
12832
|
+
{
|
|
12833
|
+
meshId,
|
|
12834
|
+
nodeId,
|
|
12835
|
+
sessionId,
|
|
12836
|
+
providerType,
|
|
12837
|
+
task,
|
|
12838
|
+
transport: "local",
|
|
12839
|
+
...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
|
|
12840
|
+
...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
|
|
12674
12841
|
}
|
|
12675
|
-
|
|
12842
|
+
);
|
|
12676
12843
|
return true;
|
|
12677
12844
|
}
|
|
12678
12845
|
function sweepExpiredCooldowns() {
|
|
@@ -12937,7 +13104,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
12937
13104
|
}
|
|
12938
13105
|
}
|
|
12939
13106
|
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
12940
|
-
if (task.targetNodeId &&
|
|
13107
|
+
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
12941
13108
|
if (task.requiredTags?.length) {
|
|
12942
13109
|
const priorities = normalizeProviderPriority(node?.policy);
|
|
12943
13110
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
@@ -12948,7 +13115,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
12948
13115
|
return true;
|
|
12949
13116
|
}) : [];
|
|
12950
13117
|
if (!candidateNodes.length) {
|
|
12951
|
-
|
|
13118
|
+
const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
|
|
13119
|
+
markAutoLaunch(meshId, task.id, {
|
|
13120
|
+
status: "skipped",
|
|
13121
|
+
reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
|
|
13122
|
+
nodeId: task.targetNodeId
|
|
13123
|
+
});
|
|
12952
13124
|
continue;
|
|
12953
13125
|
}
|
|
12954
13126
|
const strategy = resolveSchedulingStrategy(mesh);
|
|
@@ -13405,7 +13577,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13405
13577
|
});
|
|
13406
13578
|
if (terminal?.kind === "task_completed" && !sessionHasActiveAssignment(args.meshId, eventSessionId)) {
|
|
13407
13579
|
const newDispatchAfterTerminal = hasDispatchAfterTerminal(args.meshId, eventSessionId, terminal.id);
|
|
13408
|
-
|
|
13580
|
+
const supersedesWeakTerminal = isWeakTerminalLedgerPayload(terminal.payload) && isGenuineCompletionEvidence(args.metadataEvent);
|
|
13581
|
+
if (!newDispatchAfterTerminal && !supersedesWeakTerminal) {
|
|
13409
13582
|
const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
|
|
13410
13583
|
const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
|
|
13411
13584
|
const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
|
|
@@ -13451,24 +13624,30 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13451
13624
|
return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
|
|
13452
13625
|
}
|
|
13453
13626
|
}
|
|
13454
|
-
function markSessionTerminal(sessionId, outcome, occurredAtMs) {
|
|
13627
|
+
function markSessionTerminal(sessionId, outcome, occurredAtMs, opts) {
|
|
13455
13628
|
const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
|
|
13456
13629
|
const task = updateSessionTaskStatus(args.meshId, sessionId, outcome, {
|
|
13457
13630
|
occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
|
|
13458
13631
|
taskId: eventTaskId
|
|
13459
13632
|
});
|
|
13460
|
-
|
|
13633
|
+
const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
|
|
13634
|
+
if (!leaveDirectDispatchActive) {
|
|
13635
|
+
updateDirectDispatchStatus(args.meshId, sessionId, outcome);
|
|
13636
|
+
}
|
|
13461
13637
|
markSessionDeliveriesTerminal(args.meshId, sessionId, outcome);
|
|
13462
13638
|
setImmediate(() => cleanupTerminalDirectDispatches());
|
|
13463
13639
|
return task ? { id: task.id } : null;
|
|
13464
13640
|
}
|
|
13465
13641
|
let completedTaskForLedger = null;
|
|
13642
|
+
let directDispatchTaskIdForLedger;
|
|
13466
13643
|
if (args.event === "agent:generating_completed") {
|
|
13467
13644
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
13468
13645
|
const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
|
|
13469
13646
|
const providerType = readNonEmptyString2(args.metadataEvent.providerType);
|
|
13470
13647
|
if (sessionId) {
|
|
13471
|
-
|
|
13648
|
+
directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
13649
|
+
const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
|
|
13650
|
+
completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
|
|
13472
13651
|
if (nodeId && providerType) {
|
|
13473
13652
|
runIdleMaintenanceThenAssignQueue(components, { meshId: args.meshId, nodeId, sessionId, providerType });
|
|
13474
13653
|
}
|
|
@@ -13571,6 +13750,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13571
13750
|
}
|
|
13572
13751
|
}
|
|
13573
13752
|
if (sessionId) {
|
|
13753
|
+
directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
13574
13754
|
completedTaskForLedger = markSessionTerminal(sessionId, "failed");
|
|
13575
13755
|
}
|
|
13576
13756
|
}
|
|
@@ -13600,7 +13780,10 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13600
13780
|
payload: {
|
|
13601
13781
|
event: args.event,
|
|
13602
13782
|
nodeLabel: args.nodeLabel,
|
|
13603
|
-
|
|
13783
|
+
// Fix B: fall back to the direct-dispatch taskId when no work-queue row
|
|
13784
|
+
// matched, so the terminal entry is attributable in mesh task-stats
|
|
13785
|
+
// (otherwise the direct task shows status='unknown' / terminalKind=null).
|
|
13786
|
+
taskId: completedTaskForLedger?.id || directDispatchTaskIdForLedger || void 0,
|
|
13604
13787
|
providerSessionId,
|
|
13605
13788
|
finalSummary,
|
|
13606
13789
|
workerResult,
|
|
@@ -13888,7 +14071,7 @@ function setupMeshEventForwarding(components) {
|
|
|
13888
14071
|
});
|
|
13889
14072
|
});
|
|
13890
14073
|
}
|
|
13891
|
-
var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
|
|
14074
|
+
var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
|
|
13892
14075
|
var init_mesh_events_coordinator = __esm({
|
|
13893
14076
|
"src/mesh/mesh-events-coordinator.ts"() {
|
|
13894
14077
|
"use strict";
|
|
@@ -13916,6 +14099,7 @@ var init_mesh_events_coordinator = __esm({
|
|
|
13916
14099
|
idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
|
|
13917
14100
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
13918
14101
|
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
14102
|
+
DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
|
|
13919
14103
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
13920
14104
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
13921
14105
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -13971,12 +14155,9 @@ function resolveReconcileIntervalMs() {
|
|
|
13971
14155
|
return DEFAULT_RECONCILE_INTERVAL_MS;
|
|
13972
14156
|
}
|
|
13973
14157
|
function resolveCoordinatorDaemonIds(components) {
|
|
13974
|
-
const ids = /* @__PURE__ */ new Set();
|
|
13975
14158
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
13976
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
13977
14159
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
13978
|
-
|
|
13979
|
-
return [...ids];
|
|
14160
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
13980
14161
|
}
|
|
13981
14162
|
function daemonHostsMesh(mesh, daemonIds) {
|
|
13982
14163
|
const host = mesh.meshHost;
|
|
@@ -14060,6 +14241,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
|
|
|
14060
14241
|
}
|
|
14061
14242
|
}
|
|
14062
14243
|
}
|
|
14244
|
+
function recoverStrandedAssignedDispatches(meshId, store) {
|
|
14245
|
+
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
14246
|
+
if (!assigned.length) return;
|
|
14247
|
+
const nowMs = Date.now();
|
|
14248
|
+
for (const row of assigned) {
|
|
14249
|
+
const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
|
|
14250
|
+
if (!Number.isFinite(dispatchedAtMs)) continue;
|
|
14251
|
+
if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
|
|
14252
|
+
if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
|
|
14253
|
+
const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
|
|
14254
|
+
reason: "assigned_stranded_dispatch_unconfirmed",
|
|
14255
|
+
ageMs: nowMs - dispatchedAtMs
|
|
14256
|
+
});
|
|
14257
|
+
if (reclaimed) {
|
|
14258
|
+
LOG.warn("MeshReconcile", `Reclaimed stranded assigned task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}, dispatched ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s ago, never confirmed delivered \u2192 ${reclaimed.status})`);
|
|
14259
|
+
}
|
|
14260
|
+
}
|
|
14261
|
+
}
|
|
14063
14262
|
async function runMeshReconcileTick(components) {
|
|
14064
14263
|
const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
14065
14264
|
const drainDaemonIds = resolveCoordinatorDaemonIds(components);
|
|
@@ -14089,6 +14288,17 @@ async function runMeshReconcileTick(components) {
|
|
|
14089
14288
|
}
|
|
14090
14289
|
}
|
|
14091
14290
|
}
|
|
14291
|
+
if (store) {
|
|
14292
|
+
for (const mesh of listMeshes()) {
|
|
14293
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14294
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
14295
|
+
try {
|
|
14296
|
+
recoverStrandedAssignedDispatches(mesh.id, store);
|
|
14297
|
+
} catch (e) {
|
|
14298
|
+
LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
14299
|
+
}
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14092
14302
|
for (const mesh of listMeshes()) {
|
|
14093
14303
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14094
14304
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
@@ -14476,7 +14686,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
14476
14686
|
}
|
|
14477
14687
|
};
|
|
14478
14688
|
}
|
|
14479
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
|
|
14689
|
+
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
|
|
14480
14690
|
var init_mesh_reconcile_loop = __esm({
|
|
14481
14691
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
14482
14692
|
"use strict";
|
|
@@ -14489,6 +14699,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14489
14699
|
init_mesh_events_coordinator();
|
|
14490
14700
|
init_mesh_unresolved_forward_outbox();
|
|
14491
14701
|
init_mesh_events_utils();
|
|
14702
|
+
init_dist();
|
|
14492
14703
|
init_mesh_work_queue();
|
|
14493
14704
|
init_mesh_ledger();
|
|
14494
14705
|
init_mesh_active_work();
|
|
@@ -14497,6 +14708,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14497
14708
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
14498
14709
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
14499
14710
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
14711
|
+
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
14500
14712
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
14501
14713
|
}
|
|
14502
14714
|
});
|
|
@@ -14529,6 +14741,84 @@ var init_mesh_events = __esm({
|
|
|
14529
14741
|
}
|
|
14530
14742
|
});
|
|
14531
14743
|
|
|
14744
|
+
// src/providers/approval-utils.ts
|
|
14745
|
+
function normalizeApprovalLabel(value) {
|
|
14746
|
+
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
14747
|
+
}
|
|
14748
|
+
function isNegativeApprovalLabel(value) {
|
|
14749
|
+
const label = normalizeApprovalLabel(value);
|
|
14750
|
+
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
14751
|
+
}
|
|
14752
|
+
function hasNegativeApprovalOption(buttons) {
|
|
14753
|
+
return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
|
|
14754
|
+
}
|
|
14755
|
+
function getApprovalPositiveHints(provider) {
|
|
14756
|
+
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
14757
|
+
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14758
|
+
}
|
|
14759
|
+
function pickApprovalButton(buttons, provider) {
|
|
14760
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
14761
|
+
if (labels.length === 0) {
|
|
14762
|
+
return { index: -1, label: "" };
|
|
14763
|
+
}
|
|
14764
|
+
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
14765
|
+
const hints = getApprovalPositiveHints(provider);
|
|
14766
|
+
for (const hint of hints) {
|
|
14767
|
+
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
14768
|
+
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
14769
|
+
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14770
|
+
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
14771
|
+
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14772
|
+
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
14773
|
+
}
|
|
14774
|
+
return { index: -1, label: "" };
|
|
14775
|
+
}
|
|
14776
|
+
function pickAutoApprovalButton(buttons) {
|
|
14777
|
+
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
14778
|
+
const index = labels.findIndex(Boolean);
|
|
14779
|
+
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
14780
|
+
}
|
|
14781
|
+
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
14782
|
+
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
14783
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
14784
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
14785
|
+
return lines.join("\n");
|
|
14786
|
+
}
|
|
14787
|
+
function looksLikeActiveApprovalPromptText(content) {
|
|
14788
|
+
const text = content.trim();
|
|
14789
|
+
if (!text || text.length > 2e3) return false;
|
|
14790
|
+
const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
|
|
14791
|
+
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
14792
|
+
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
14793
|
+
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
14794
|
+
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
14795
|
+
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
14796
|
+
if (hasDontAskAgain && hasNoOption) return true;
|
|
14797
|
+
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
14798
|
+
return false;
|
|
14799
|
+
}
|
|
14800
|
+
var DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14801
|
+
var init_approval_utils = __esm({
|
|
14802
|
+
"src/providers/approval-utils.ts"() {
|
|
14803
|
+
"use strict";
|
|
14804
|
+
DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
14805
|
+
"yes",
|
|
14806
|
+
"allow once",
|
|
14807
|
+
"approve",
|
|
14808
|
+
"accept",
|
|
14809
|
+
"continue",
|
|
14810
|
+
"run",
|
|
14811
|
+
"proceed",
|
|
14812
|
+
"confirm",
|
|
14813
|
+
"save",
|
|
14814
|
+
"ok",
|
|
14815
|
+
"trust",
|
|
14816
|
+
"allow",
|
|
14817
|
+
"always allow"
|
|
14818
|
+
];
|
|
14819
|
+
}
|
|
14820
|
+
});
|
|
14821
|
+
|
|
14532
14822
|
// src/logging/debug-config.ts
|
|
14533
14823
|
function normalizeCategories(categories) {
|
|
14534
14824
|
if (!Array.isArray(categories)) return [];
|
|
@@ -15515,6 +15805,27 @@ function compileSettledPromptMatchers(spec) {
|
|
|
15515
15805
|
});
|
|
15516
15806
|
return { prompt, footers };
|
|
15517
15807
|
}
|
|
15808
|
+
function extractButtonLabels(spec, text) {
|
|
15809
|
+
if (!text) return [];
|
|
15810
|
+
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
15811
|
+
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
15812
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
15813
|
+
const out = [];
|
|
15814
|
+
for (const line of text.split("\n")) {
|
|
15815
|
+
buttonRe.lastIndex = 0;
|
|
15816
|
+
const m = buttonRe.exec(line);
|
|
15817
|
+
if (!m) continue;
|
|
15818
|
+
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
15819
|
+
if (captured && captured.trim()) out.push(captured.trim());
|
|
15820
|
+
}
|
|
15821
|
+
return out;
|
|
15822
|
+
}
|
|
15823
|
+
function buttonBlockApprovalCue(spec, text) {
|
|
15824
|
+
const labels = extractButtonLabels(spec, text);
|
|
15825
|
+
if (labels.length < 2) return false;
|
|
15826
|
+
if (pickApprovalButton(labels).index < 0) return false;
|
|
15827
|
+
return hasNegativeApprovalOption(labels);
|
|
15828
|
+
}
|
|
15518
15829
|
function modalMatches(spec, input) {
|
|
15519
15830
|
const text = input.screenText ?? "";
|
|
15520
15831
|
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
@@ -15523,6 +15834,7 @@ function modalMatches(spec, input) {
|
|
|
15523
15834
|
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
15524
15835
|
if (re.test(text)) return true;
|
|
15525
15836
|
}
|
|
15837
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
15526
15838
|
return false;
|
|
15527
15839
|
}
|
|
15528
15840
|
function evaluateGroup(group, spec, input, compiled) {
|
|
@@ -15577,6 +15889,7 @@ var init_detect_status = __esm({
|
|
|
15577
15889
|
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
15578
15890
|
"use strict";
|
|
15579
15891
|
init_visible_region();
|
|
15892
|
+
init_approval_utils();
|
|
15580
15893
|
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15581
15894
|
}
|
|
15582
15895
|
});
|
|
@@ -21754,6 +22067,7 @@ init_mesh_active_work();
|
|
|
21754
22067
|
init_mesh_refine_status();
|
|
21755
22068
|
init_mesh_host_ownership();
|
|
21756
22069
|
init_mesh_events();
|
|
22070
|
+
init_mesh_events_utils();
|
|
21757
22071
|
init_mesh_delivery_policy();
|
|
21758
22072
|
|
|
21759
22073
|
// src/mesh/p2p-relay-failure.ts
|
|
@@ -25673,76 +25987,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
25673
25987
|
return normalized;
|
|
25674
25988
|
}
|
|
25675
25989
|
|
|
25676
|
-
// src/providers/approval-utils.ts
|
|
25677
|
-
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
25678
|
-
"yes",
|
|
25679
|
-
"allow once",
|
|
25680
|
-
"approve",
|
|
25681
|
-
"accept",
|
|
25682
|
-
"continue",
|
|
25683
|
-
"run",
|
|
25684
|
-
"proceed",
|
|
25685
|
-
"confirm",
|
|
25686
|
-
"save",
|
|
25687
|
-
"ok",
|
|
25688
|
-
"trust",
|
|
25689
|
-
"allow",
|
|
25690
|
-
"always allow"
|
|
25691
|
-
];
|
|
25692
|
-
function normalizeApprovalLabel(value) {
|
|
25693
|
-
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
25694
|
-
}
|
|
25695
|
-
function isNegativeApprovalLabel(value) {
|
|
25696
|
-
const label = normalizeApprovalLabel(value);
|
|
25697
|
-
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
25698
|
-
}
|
|
25699
|
-
function getApprovalPositiveHints(provider) {
|
|
25700
|
-
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
25701
|
-
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
25702
|
-
}
|
|
25703
|
-
function pickApprovalButton(buttons, provider) {
|
|
25704
|
-
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
25705
|
-
if (labels.length === 0) {
|
|
25706
|
-
return { index: -1, label: "" };
|
|
25707
|
-
}
|
|
25708
|
-
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
25709
|
-
const hints = getApprovalPositiveHints(provider);
|
|
25710
|
-
for (const hint of hints) {
|
|
25711
|
-
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
25712
|
-
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
25713
|
-
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
25714
|
-
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
25715
|
-
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
25716
|
-
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
25717
|
-
}
|
|
25718
|
-
return { index: -1, label: "" };
|
|
25719
|
-
}
|
|
25720
|
-
function pickAutoApprovalButton(buttons) {
|
|
25721
|
-
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
25722
|
-
const index = labels.findIndex(Boolean);
|
|
25723
|
-
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
25724
|
-
}
|
|
25725
|
-
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
25726
|
-
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
25727
|
-
const cleanMessage = String(modalMessage || "").trim();
|
|
25728
|
-
if (cleanMessage) lines.push(cleanMessage);
|
|
25729
|
-
return lines.join("\n");
|
|
25730
|
-
}
|
|
25731
|
-
function looksLikeActiveApprovalPromptText(content) {
|
|
25732
|
-
const text = content.trim();
|
|
25733
|
-
if (!text || text.length > 2e3) return false;
|
|
25734
|
-
const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
|
|
25735
|
-
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
25736
|
-
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
25737
|
-
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
25738
|
-
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
25739
|
-
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
25740
|
-
if (hasDontAskAgain && hasNoOption) return true;
|
|
25741
|
-
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
25742
|
-
return false;
|
|
25743
|
-
}
|
|
25744
|
-
|
|
25745
25990
|
// src/providers/ide-provider-instance.ts
|
|
25991
|
+
init_approval_utils();
|
|
25746
25992
|
init_provider_patch_state();
|
|
25747
25993
|
init_chat_message_normalization();
|
|
25748
25994
|
init_open_panel_support();
|
|
@@ -26826,6 +27072,7 @@ import * as fs7 from "fs";
|
|
|
26826
27072
|
import * as os10 from "os";
|
|
26827
27073
|
import * as path16 from "path";
|
|
26828
27074
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
27075
|
+
init_approval_utils();
|
|
26829
27076
|
init_coordinator_registry();
|
|
26830
27077
|
init_logger();
|
|
26831
27078
|
|
|
@@ -34814,6 +35061,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
34814
35061
|
// src/providers/cli-provider-instance.ts
|
|
34815
35062
|
init_logger();
|
|
34816
35063
|
init_control_effects();
|
|
35064
|
+
init_approval_utils();
|
|
34817
35065
|
init_provider_patch_state();
|
|
34818
35066
|
|
|
34819
35067
|
// src/providers/provider-session-id.ts
|
|
@@ -35075,6 +35323,20 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35075
35323
|
* keystroke until the modal *content* has settled.
|
|
35076
35324
|
*/
|
|
35077
35325
|
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
35326
|
+
/**
|
|
35327
|
+
* Busy-side hysteresis for the settle gate. A momentary `generating` flip
|
|
35328
|
+
* while the SAME approval modal's button block is still on screen (its
|
|
35329
|
+
* question line scrolled out of the captured frame, only the buttons + a
|
|
35330
|
+
* residual `esc to interrupt` spinner remain) briefly reports
|
|
35331
|
+
* status!=waiting_approval. Without hysteresis that flip wipes the settle
|
|
35332
|
+
* clock, and the modal→generating→modal flap restarts the 600ms window
|
|
35333
|
+
* every time so auto-approve never fires. We keep the in-progress settle
|
|
35334
|
+
* gate warm across an inactive blip up to this bound; only once the modal
|
|
35335
|
+
* has genuinely stayed gone this long (a real resolution → idle) is the
|
|
35336
|
+
* gate cleared. Bounded so a genuinely new, later approval still re-settles
|
|
35337
|
+
* from scratch rather than firing on a stale timestamp.
|
|
35338
|
+
*/
|
|
35339
|
+
static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
|
|
35078
35340
|
adapter;
|
|
35079
35341
|
context = null;
|
|
35080
35342
|
events = [];
|
|
@@ -35096,6 +35358,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35096
35358
|
pendingAutoApprovalSignature = "";
|
|
35097
35359
|
pendingAutoApprovalSince = 0;
|
|
35098
35360
|
autoApproveSettleTimer = null;
|
|
35361
|
+
// Wall-clock when auto-approve first observed status!=waiting_approval while
|
|
35362
|
+
// a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
|
|
35363
|
+
// brief generating flip does not immediately wipe the settle clock.
|
|
35364
|
+
autoApproveInactiveSince = 0;
|
|
35099
35365
|
controlValues = {};
|
|
35100
35366
|
summaryMetadata = void 0;
|
|
35101
35367
|
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
@@ -35916,14 +36182,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35916
36182
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
35917
36183
|
if (!autoApproveActive) {
|
|
35918
36184
|
this.lastAutoApprovalSignature = "";
|
|
36185
|
+
if (this.pendingAutoApprovalSince) {
|
|
36186
|
+
if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
|
|
36187
|
+
const goneForMs = now - this.autoApproveInactiveSince;
|
|
36188
|
+
if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
|
|
36189
|
+
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
36190
|
+
this.autoApproveSettleTimer = setTimeout(() => {
|
|
36191
|
+
this.autoApproveSettleTimer = null;
|
|
36192
|
+
this.recheckAutoApproveSettled();
|
|
36193
|
+
}, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
|
|
36194
|
+
return autoApproveActive;
|
|
36195
|
+
}
|
|
36196
|
+
}
|
|
35919
36197
|
this.pendingAutoApprovalSignature = "";
|
|
35920
36198
|
this.pendingAutoApprovalSince = 0;
|
|
36199
|
+
this.autoApproveInactiveSince = 0;
|
|
35921
36200
|
if (this.autoApproveSettleTimer) {
|
|
35922
36201
|
clearTimeout(this.autoApproveSettleTimer);
|
|
35923
36202
|
this.autoApproveSettleTimer = null;
|
|
35924
36203
|
}
|
|
35925
36204
|
return autoApproveActive;
|
|
35926
36205
|
}
|
|
36206
|
+
this.autoApproveInactiveSince = 0;
|
|
35927
36207
|
const modal = adapterStatus.activeModal;
|
|
35928
36208
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
35929
36209
|
if (!modal || buttons.length === 0) {
|
|
@@ -35933,18 +36213,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35933
36213
|
if (buttonIndex < 0) {
|
|
35934
36214
|
return autoApproveActive;
|
|
35935
36215
|
}
|
|
35936
|
-
const
|
|
35937
|
-
const signature = [
|
|
35938
|
-
approvalEntrySeq,
|
|
36216
|
+
const modalSignature = [
|
|
35939
36217
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
35940
36218
|
buttons.join("|"),
|
|
35941
36219
|
buttonIndex
|
|
35942
36220
|
].join("::");
|
|
35943
|
-
|
|
36221
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
36222
|
+
const busySignature = `${approvalEntrySeq}::${modalSignature}`;
|
|
36223
|
+
if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
|
|
35944
36224
|
return autoApproveActive;
|
|
35945
36225
|
}
|
|
35946
|
-
if (
|
|
35947
|
-
this.pendingAutoApprovalSignature =
|
|
36226
|
+
if (modalSignature !== this.pendingAutoApprovalSignature) {
|
|
36227
|
+
this.pendingAutoApprovalSignature = modalSignature;
|
|
35948
36228
|
this.pendingAutoApprovalSince = now;
|
|
35949
36229
|
}
|
|
35950
36230
|
const settledForMs = now - this.pendingAutoApprovalSince;
|
|
@@ -35961,9 +36241,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35961
36241
|
this.autoApproveSettleTimer = null;
|
|
35962
36242
|
}
|
|
35963
36243
|
this.autoApproveBusy = true;
|
|
35964
|
-
this.lastAutoApprovalSignature =
|
|
36244
|
+
this.lastAutoApprovalSignature = busySignature;
|
|
35965
36245
|
this.pendingAutoApprovalSignature = "";
|
|
35966
36246
|
this.pendingAutoApprovalSince = 0;
|
|
36247
|
+
this.autoApproveInactiveSince = 0;
|
|
35967
36248
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
35968
36249
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
35969
36250
|
this.autoApproveBusy = false;
|
|
@@ -42855,7 +43136,8 @@ init_logger();
|
|
|
42855
43136
|
import * as fs23 from "fs";
|
|
42856
43137
|
import * as path35 from "path";
|
|
42857
43138
|
import * as os26 from "os";
|
|
42858
|
-
var
|
|
43139
|
+
var ADHDEV_HOME2 = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path35.join(os26.homedir(), ".adhdev");
|
|
43140
|
+
var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
|
|
42859
43141
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
42860
43142
|
var MAX_DAYS = 7;
|
|
42861
43143
|
try {
|
|
@@ -43315,7 +43597,7 @@ function runGit2(repoRoot, args) {
|
|
|
43315
43597
|
return "";
|
|
43316
43598
|
}
|
|
43317
43599
|
}
|
|
43318
|
-
function
|
|
43600
|
+
function readRecord5(repoRoot) {
|
|
43319
43601
|
const path42 = resolve19(repoRoot, PREVIEW_DEPLOY_RECORD);
|
|
43320
43602
|
if (!existsSync36(path42)) return null;
|
|
43321
43603
|
try {
|
|
@@ -43355,7 +43637,7 @@ function readCurrentMainCommit(repoRoot) {
|
|
|
43355
43637
|
}
|
|
43356
43638
|
function buildPreviewFreshness(repoRoot) {
|
|
43357
43639
|
const current = readCurrentMainCommit(repoRoot);
|
|
43358
|
-
const record =
|
|
43640
|
+
const record = readRecord5(repoRoot);
|
|
43359
43641
|
const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
|
|
43360
43642
|
const targets = readTargetFreshness(record, current.currentMainCommit);
|
|
43361
43643
|
let status = "unknown";
|
|
@@ -43710,13 +43992,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
43710
43992
|
}
|
|
43711
43993
|
}
|
|
43712
43994
|
}
|
|
43713
|
-
function stopSessionHostProcesses(appName) {
|
|
43995
|
+
async function stopSessionHostProcesses(appName) {
|
|
43714
43996
|
const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
43997
|
+
let killedPid = null;
|
|
43715
43998
|
try {
|
|
43716
43999
|
if (fs25.existsSync(pidFile)) {
|
|
43717
44000
|
const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
|
|
43718
44001
|
if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
|
|
43719
|
-
killPid(pid);
|
|
44002
|
+
if (killPid(pid)) killedPid = pid;
|
|
43720
44003
|
}
|
|
43721
44004
|
}
|
|
43722
44005
|
} catch {
|
|
@@ -43726,6 +44009,15 @@ function stopSessionHostProcesses(appName) {
|
|
|
43726
44009
|
} catch {
|
|
43727
44010
|
}
|
|
43728
44011
|
}
|
|
44012
|
+
if (killedPid !== null) {
|
|
44013
|
+
await waitForPidExit(killedPid, 15e3);
|
|
44014
|
+
}
|
|
44015
|
+
}
|
|
44016
|
+
function isRetriableInstallLockError(error) {
|
|
44017
|
+
const code = error?.code;
|
|
44018
|
+
if (code === "EBUSY" || code === "EPERM") return true;
|
|
44019
|
+
const text = `${error?.message || ""} ${error?.stderr || ""}`;
|
|
44020
|
+
return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
|
|
43729
44021
|
}
|
|
43730
44022
|
function removeDaemonPidFile() {
|
|
43731
44023
|
const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
|
|
@@ -43805,22 +44097,37 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
43805
44097
|
appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
|
|
43806
44098
|
await waitForPidExit(payload.parentPid, 15e3);
|
|
43807
44099
|
}
|
|
43808
|
-
stopSessionHostProcesses(sessionHostAppName);
|
|
44100
|
+
await stopSessionHostProcesses(sessionHostAppName);
|
|
43809
44101
|
removeDaemonPidFile();
|
|
43810
44102
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
43811
44103
|
const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
|
|
43812
44104
|
appendUpgradeLog(`Installing ${spec}`);
|
|
43813
|
-
const
|
|
43814
|
-
|
|
43815
|
-
|
|
43816
|
-
{
|
|
43817
|
-
|
|
43818
|
-
|
|
43819
|
-
|
|
43820
|
-
|
|
43821
|
-
|
|
44105
|
+
const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
|
|
44106
|
+
let installOutput = "";
|
|
44107
|
+
for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
|
|
44108
|
+
try {
|
|
44109
|
+
installOutput = String(execFileSync5(
|
|
44110
|
+
installCommand.command,
|
|
44111
|
+
installCommand.args,
|
|
44112
|
+
{
|
|
44113
|
+
encoding: "utf8",
|
|
44114
|
+
stdio: "pipe",
|
|
44115
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
44116
|
+
env: buildInstallEnvWithNodeOnPath(),
|
|
44117
|
+
...installCommand.execOptions
|
|
44118
|
+
}
|
|
44119
|
+
));
|
|
44120
|
+
break;
|
|
44121
|
+
} catch (error) {
|
|
44122
|
+
if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
|
|
44123
|
+
appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
|
|
44124
|
+
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
44125
|
+
await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
|
|
44126
|
+
continue;
|
|
44127
|
+
}
|
|
44128
|
+
throw error;
|
|
43822
44129
|
}
|
|
43823
|
-
|
|
44130
|
+
}
|
|
43824
44131
|
if (installOutput.trim()) {
|
|
43825
44132
|
appendUpgradeLog(installOutput.trim());
|
|
43826
44133
|
}
|
|
@@ -52582,6 +52889,7 @@ var DaemonAgentStreamManager = class {
|
|
|
52582
52889
|
|
|
52583
52890
|
// src/agent-stream/poller.ts
|
|
52584
52891
|
init_logger();
|
|
52892
|
+
init_approval_utils();
|
|
52585
52893
|
init_chat_message_normalization();
|
|
52586
52894
|
var AgentStreamPoller = class {
|
|
52587
52895
|
deps;
|
|
@@ -60283,6 +60591,7 @@ export {
|
|
|
60283
60591
|
readLedgerEntries,
|
|
60284
60592
|
readLedgerSlice,
|
|
60285
60593
|
readLedgerSliceFromStore,
|
|
60594
|
+
readMeshCompletionSummary,
|
|
60286
60595
|
reconcileDirectDispatchCompletionFromTranscript,
|
|
60287
60596
|
recordCompletionConflict,
|
|
60288
60597
|
recordDebugTrace,
|
|
@@ -60308,6 +60617,7 @@ export {
|
|
|
60308
60617
|
resolveMeshHostStatus,
|
|
60309
60618
|
resolveMeshNodeAttribution,
|
|
60310
60619
|
resolveMeshRefineValidationPlan,
|
|
60620
|
+
resolveMeshSurfacedSessionPreview,
|
|
60311
60621
|
resolveNodeSchedulingPriority,
|
|
60312
60622
|
resolveSessionHostAppName,
|
|
60313
60623
|
resolveSessionHostAppNameResolution,
|