@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.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-22T07:17:05.715Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -3195,7 +3195,7 @@ function installGlobalInterceptor() {
|
|
|
3195
3195
|
function getLogPath() {
|
|
3196
3196
|
return currentLogFile;
|
|
3197
3197
|
}
|
|
3198
|
-
var fs4, path9, os3, 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;
|
|
3198
|
+
var fs4, path9, os3, 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;
|
|
3199
3199
|
var init_logger = __esm({
|
|
3200
3200
|
"src/logging/logger.ts"() {
|
|
3201
3201
|
"use strict";
|
|
@@ -3206,7 +3206,8 @@ var init_logger = __esm({
|
|
|
3206
3206
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
3207
3207
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
3208
3208
|
currentLevel = "info";
|
|
3209
|
-
|
|
3209
|
+
ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
|
|
3210
|
+
LOG_DIR = path9.join(ADHDEV_HOME, "logs");
|
|
3210
3211
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
3211
3212
|
MAX_LOG_DAYS = 7;
|
|
3212
3213
|
try {
|
|
@@ -4007,6 +4008,7 @@ __export(mesh_work_queue_exports, {
|
|
|
4007
4008
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
4008
4009
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
4009
4010
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
4011
|
+
reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
|
|
4010
4012
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
4011
4013
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
4012
4014
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
@@ -4442,6 +4444,52 @@ function requeueTask(meshId, taskId, opts) {
|
|
|
4442
4444
|
return entry;
|
|
4443
4445
|
});
|
|
4444
4446
|
}
|
|
4447
|
+
function reclaimStrandedAssignedTask(meshId, taskId, opts) {
|
|
4448
|
+
requireMeshHostQueueOwner(opts);
|
|
4449
|
+
return withQueueLock(meshId, () => {
|
|
4450
|
+
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
4451
|
+
if (!entry) return null;
|
|
4452
|
+
if (entry.status !== "assigned") return null;
|
|
4453
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4454
|
+
const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
|
|
4455
|
+
const reclaims = (entry.strandedReclaimCount || 0) + 1;
|
|
4456
|
+
const prevNode = entry.assignedNodeId;
|
|
4457
|
+
const prevSession = entry.assignedSessionId;
|
|
4458
|
+
delete entry.assignedNodeId;
|
|
4459
|
+
delete entry.assignedSessionId;
|
|
4460
|
+
delete entry.assignedProviderType;
|
|
4461
|
+
delete entry.dispatchTimestamp;
|
|
4462
|
+
entry.strandedReclaimCount = reclaims;
|
|
4463
|
+
entry.updatedAt = now;
|
|
4464
|
+
if (reclaims > MAX_STRANDED_RECLAIMS) {
|
|
4465
|
+
entry.status = "failed";
|
|
4466
|
+
entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
|
|
4467
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4468
|
+
propagateDependencyFailure(meshId, taskId);
|
|
4469
|
+
} else {
|
|
4470
|
+
entry.status = "pending";
|
|
4471
|
+
entry.requeuedAt = now;
|
|
4472
|
+
entry.requeueReason = reason;
|
|
4473
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4474
|
+
}
|
|
4475
|
+
try {
|
|
4476
|
+
appendLedgerEntry(meshId, {
|
|
4477
|
+
kind: "task_reclaimed",
|
|
4478
|
+
nodeId: prevNode,
|
|
4479
|
+
sessionId: prevSession,
|
|
4480
|
+
payload: {
|
|
4481
|
+
taskId,
|
|
4482
|
+
reason,
|
|
4483
|
+
...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
|
|
4484
|
+
reclaimCount: reclaims,
|
|
4485
|
+
outcome: entry.status
|
|
4486
|
+
}
|
|
4487
|
+
});
|
|
4488
|
+
} catch {
|
|
4489
|
+
}
|
|
4490
|
+
return entry;
|
|
4491
|
+
});
|
|
4492
|
+
}
|
|
4445
4493
|
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
4446
4494
|
return withQueueLock(meshId, () => {
|
|
4447
4495
|
const store = MeshRuntimeStore.getInstance();
|
|
@@ -4555,7 +4603,7 @@ function recordMeshToolCall(opts) {
|
|
|
4555
4603
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
4556
4604
|
}
|
|
4557
4605
|
}
|
|
4558
|
-
var import_crypto5, 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;
|
|
4606
|
+
var import_crypto5, 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;
|
|
4559
4607
|
var init_mesh_work_queue = __esm({
|
|
4560
4608
|
"src/mesh/mesh-work-queue.ts"() {
|
|
4561
4609
|
"use strict";
|
|
@@ -4565,6 +4613,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4565
4613
|
init_mesh_runtime_store();
|
|
4566
4614
|
init_mesh_config();
|
|
4567
4615
|
init_logger();
|
|
4616
|
+
init_mesh_ledger();
|
|
4568
4617
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
4569
4618
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
4570
4619
|
MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
|
|
@@ -4617,6 +4666,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4617
4666
|
]);
|
|
4618
4667
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
4619
4668
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
4669
|
+
MAX_STRANDED_RECLAIMS = 3;
|
|
4620
4670
|
}
|
|
4621
4671
|
});
|
|
4622
4672
|
|
|
@@ -4676,6 +4726,10 @@ var init_mesh_runtime_store = __esm({
|
|
|
4676
4726
|
migratedMeshIds = /* @__PURE__ */ new Set();
|
|
4677
4727
|
fingerprintSweepCounter = 0;
|
|
4678
4728
|
walWriteCounter = 0;
|
|
4729
|
+
// Independent cadence for the tool-call-log sweep. Must NOT share walWriteCounter:
|
|
4730
|
+
// sharing makes each store's threshold drift by the other's write volume (WAL
|
|
4731
|
+
// checkpoint at 500 vs tool-log sweep at 200 would interfere arbitrarily).
|
|
4732
|
+
toolCallLogCounter = 0;
|
|
4679
4733
|
static WAL_CHECK_INTERVAL = 500;
|
|
4680
4734
|
static WAL_MAX_BYTES = 50 * 1024 * 1024;
|
|
4681
4735
|
// 50 MB
|
|
@@ -5498,6 +5552,22 @@ var init_mesh_runtime_store = __esm({
|
|
|
5498
5552
|
updatedAt: r.updated_at
|
|
5499
5553
|
}));
|
|
5500
5554
|
}
|
|
5555
|
+
/**
|
|
5556
|
+
* Bug B watchdog support: true when at least one delivery record for the task has
|
|
5557
|
+
* reached a confirmed-handed-off status (delivered / acked / completed). The
|
|
5558
|
+
* assigned-stranded watchdog uses this to distinguish a dispatch that was never
|
|
5559
|
+
* confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
|
|
5560
|
+
* in-flight or completion-lost task, which is PHASE 4's responsibility, not this
|
|
5561
|
+
* watchdog's). Indexed by (mesh_id, task_id).
|
|
5562
|
+
*/
|
|
5563
|
+
taskHasConfirmedDelivery(meshId, taskId) {
|
|
5564
|
+
const row = this.db.prepare(`
|
|
5565
|
+
SELECT 1 FROM mesh_session_delivery
|
|
5566
|
+
WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
|
|
5567
|
+
LIMIT 1
|
|
5568
|
+
`).get(meshId, taskId);
|
|
5569
|
+
return !!row;
|
|
5570
|
+
}
|
|
5501
5571
|
expireStaleSessionDeliveries(meshId) {
|
|
5502
5572
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5503
5573
|
this.db.prepare(`
|
|
@@ -5569,7 +5639,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
5569
5639
|
"SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
|
|
5570
5640
|
).get(meshId, tool, windowStart);
|
|
5571
5641
|
const callsInWindow = row?.cnt ?? 0;
|
|
5572
|
-
if (++this.
|
|
5642
|
+
if (++this.toolCallLogCounter % 200 === 0) {
|
|
5573
5643
|
this.db.prepare(
|
|
5574
5644
|
"DELETE FROM mesh_tool_call_log WHERE called_at < ?"
|
|
5575
5645
|
).run(now - Math.max(windowMs * 10, 6e4));
|
|
@@ -6024,9 +6094,9 @@ function computeMeshTaskStats(meshId, opts) {
|
|
|
6024
6094
|
}
|
|
6025
6095
|
return targetIds.map((taskId) => {
|
|
6026
6096
|
const queueEntry = queueById.get(taskId);
|
|
6027
|
-
const status = queueEntry?.status ?? "unknown";
|
|
6028
6097
|
const dispatch = dispatches.get(taskId);
|
|
6029
6098
|
const terminal = terminals.get(taskId);
|
|
6099
|
+
const status = queueEntry?.status ?? (terminal ? terminal.kind === "task_completed" ? "completed" : "failed" : "unknown");
|
|
6030
6100
|
const isTerminalStatus = status === "completed" || status === "failed" || status === "cancelled";
|
|
6031
6101
|
const dispatchTime = parseTime(dispatch?.first ?? queueEntry?.dispatchTimestamp);
|
|
6032
6102
|
const terminalTime = parseTime(terminal?.at);
|
|
@@ -7921,6 +7991,35 @@ function meshNodeIdMatches(node, candidateId) {
|
|
|
7921
7991
|
if (!trimmed) return false;
|
|
7922
7992
|
return normalizeMeshNodeId(node) === trimmed;
|
|
7923
7993
|
}
|
|
7994
|
+
function machineCoreFromDaemonId(id) {
|
|
7995
|
+
const trimmed = readString5(id);
|
|
7996
|
+
if (!trimmed) return void 0;
|
|
7997
|
+
for (const prefix of DAEMON_ID_PREFIXES) {
|
|
7998
|
+
if (trimmed.startsWith(prefix)) {
|
|
7999
|
+
const core = trimmed.slice(prefix.length).trim();
|
|
8000
|
+
return core || void 0;
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
return trimmed;
|
|
8004
|
+
}
|
|
8005
|
+
function expandDaemonIdForms(ids) {
|
|
8006
|
+
const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
|
|
8007
|
+
const out = [];
|
|
8008
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8009
|
+
const add = (value) => {
|
|
8010
|
+
if (!value || seen.has(value)) return;
|
|
8011
|
+
seen.add(value);
|
|
8012
|
+
out.push(value);
|
|
8013
|
+
};
|
|
8014
|
+
for (const raw of list) add(readString5(raw));
|
|
8015
|
+
for (const raw of list) {
|
|
8016
|
+
const core = machineCoreFromDaemonId(readString5(raw));
|
|
8017
|
+
if (!core || !core.startsWith("mach_")) continue;
|
|
8018
|
+
add(core);
|
|
8019
|
+
for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
|
|
8020
|
+
}
|
|
8021
|
+
return out;
|
|
8022
|
+
}
|
|
7924
8023
|
function summarizeGitShape(status) {
|
|
7925
8024
|
const record = readRecord3(status);
|
|
7926
8025
|
if (!Object.keys(record).length) return null;
|
|
@@ -7955,9 +8054,11 @@ function summarizeGitShape(status) {
|
|
|
7955
8054
|
submodules
|
|
7956
8055
|
};
|
|
7957
8056
|
}
|
|
8057
|
+
var DAEMON_ID_PREFIXES;
|
|
7958
8058
|
var init_dist = __esm({
|
|
7959
8059
|
"../mesh-shared/dist/index.mjs"() {
|
|
7960
8060
|
"use strict";
|
|
8061
|
+
DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
|
|
7961
8062
|
}
|
|
7962
8063
|
});
|
|
7963
8064
|
|
|
@@ -8035,6 +8136,14 @@ function statusFromTerminal(entry) {
|
|
|
8035
8136
|
if (entry.kind === "task_completed") return "idle";
|
|
8036
8137
|
return "failed";
|
|
8037
8138
|
}
|
|
8139
|
+
function classifyDirectDispatch(params) {
|
|
8140
|
+
const { status, isTerminalRow, hasTerminalStatus, liveStatus, liveStaleReason, dispatchedToIdleSession } = params;
|
|
8141
|
+
const isNoTransition = !hasTerminalStatus && !liveStatus;
|
|
8142
|
+
const isIdleUnacknowledged = status === "idle";
|
|
8143
|
+
const ledgerOnlyStaleReason = !isTerminalRow && (isIdleUnacknowledged || isNoTransition || dispatchedToIdleSession && isIdleUnacknowledged) ? LEDGER_ONLY_STALE_REASON : void 0;
|
|
8144
|
+
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !liveStaleReason);
|
|
8145
|
+
return { ledgerOnlyStaleReason, isFreshUnacknowledged };
|
|
8146
|
+
}
|
|
8038
8147
|
function buildMeshActiveWorkSummary(activeWork) {
|
|
8039
8148
|
const statusCounts = {
|
|
8040
8149
|
pending: 0,
|
|
@@ -8097,10 +8206,14 @@ function buildMeshActiveWork(opts) {
|
|
|
8097
8206
|
const dbStatus = dispatch.status;
|
|
8098
8207
|
const isTerminal = dbStatus === "completed" || dbStatus === "failed" || dbStatus === "stale";
|
|
8099
8208
|
const status = isTerminal ? dbStatus === "completed" ? "idle" : "failed" : live.status || (dbStatus === "acked" ? "generating" : "assigned");
|
|
8100
|
-
const
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8209
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8210
|
+
status,
|
|
8211
|
+
isTerminalRow: isTerminal,
|
|
8212
|
+
hasTerminalStatus: isTerminal,
|
|
8213
|
+
liveStatus: live.status,
|
|
8214
|
+
liveStaleReason: live.staleReason,
|
|
8215
|
+
dispatchedToIdleSession: dispatch.dispatchedToIdleSession === true
|
|
8216
|
+
});
|
|
8104
8217
|
const { title, summary: summary2 } = summarizeMessage(dispatch.message || "");
|
|
8105
8218
|
const record = {
|
|
8106
8219
|
taskId: dispatch.taskId,
|
|
@@ -8143,13 +8256,16 @@ function buildMeshActiveWork(opts) {
|
|
|
8143
8256
|
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
8144
8257
|
const status = terminalStatus || live.status || "assigned";
|
|
8145
8258
|
const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
|
|
8146
|
-
const
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8259
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8260
|
+
status,
|
|
8261
|
+
isTerminalRow: terminalRow,
|
|
8262
|
+
hasTerminalStatus: Boolean(terminalStatus),
|
|
8263
|
+
liveStatus: live.status,
|
|
8264
|
+
liveStaleReason: live.staleReason,
|
|
8265
|
+
dispatchedToIdleSession: dispatch.payload?.dispatchedToIdleSession === true
|
|
8266
|
+
});
|
|
8150
8267
|
const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
|
|
8151
8268
|
const { title, summary: summary2 } = summarizeMessage(message);
|
|
8152
|
-
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
|
|
8153
8269
|
const record = {
|
|
8154
8270
|
taskId,
|
|
8155
8271
|
source: "direct",
|
|
@@ -8191,13 +8307,16 @@ function buildMeshActiveWork(opts) {
|
|
|
8191
8307
|
const live = sessionStatusFromNodes(opts.nodes, dispatch.nodeId, dispatch.sessionId);
|
|
8192
8308
|
const status = terminalStatus || live.status || "assigned";
|
|
8193
8309
|
const terminalRow = Boolean(terminal && terminal.kind !== "task_approval_needed");
|
|
8194
|
-
const
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8310
|
+
const { ledgerOnlyStaleReason, isFreshUnacknowledged } = classifyDirectDispatch({
|
|
8311
|
+
status,
|
|
8312
|
+
isTerminalRow: terminalRow,
|
|
8313
|
+
hasTerminalStatus: Boolean(terminalStatus),
|
|
8314
|
+
liveStatus: live.status,
|
|
8315
|
+
liveStaleReason: live.staleReason,
|
|
8316
|
+
dispatchedToIdleSession: dispatch.payload?.dispatchedToIdleSession === true
|
|
8317
|
+
});
|
|
8198
8318
|
const message = readString6(dispatch.payload?.message) || readString6(dispatch.payload?.summary) || "";
|
|
8199
8319
|
const { title, summary: summary2 } = summarizeMessage(message);
|
|
8200
|
-
const isFreshUnacknowledged = Boolean(ledgerOnlyStaleReason && !live.staleReason);
|
|
8201
8320
|
const record = {
|
|
8202
8321
|
taskId,
|
|
8203
8322
|
source: "direct",
|
|
@@ -8348,7 +8467,7 @@ function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
|
|
|
8348
8467
|
...opts.note ? { note: opts.note } : {}
|
|
8349
8468
|
};
|
|
8350
8469
|
}
|
|
8351
|
-
var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, PRUNABLE_ORPHAN_STALE_REASONS;
|
|
8470
|
+
var DIRECT_DISPATCH_VIA, TERMINAL_LEDGER_KINDS, LEDGER_ONLY_STALE_REASON, PRUNABLE_ORPHAN_STALE_REASONS;
|
|
8352
8471
|
var init_mesh_active_work = __esm({
|
|
8353
8472
|
"src/mesh/mesh-active-work.ts"() {
|
|
8354
8473
|
"use strict";
|
|
@@ -8357,6 +8476,7 @@ var init_mesh_active_work = __esm({
|
|
|
8357
8476
|
init_dist();
|
|
8358
8477
|
DIRECT_DISPATCH_VIA = /* @__PURE__ */ new Set(["p2p_direct", "local_direct", "mesh_send_task"]);
|
|
8359
8478
|
TERMINAL_LEDGER_KINDS = /* @__PURE__ */ new Set(["task_completed", "task_failed", "task_stalled"]);
|
|
8479
|
+
LEDGER_ONLY_STALE_REASON = "direct task dispatch has no provider acknowledgement, transcript append, or active runtime transition";
|
|
8360
8480
|
PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
|
|
8361
8481
|
"direct task node is no longer in the live mesh",
|
|
8362
8482
|
"direct task session is not present in live session records",
|
|
@@ -8595,17 +8715,7 @@ var init_mesh_events_utils = __esm({
|
|
|
8595
8715
|
|
|
8596
8716
|
// src/mesh/mesh-events-pending.ts
|
|
8597
8717
|
function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
8598
|
-
|
|
8599
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8600
|
-
const out = [];
|
|
8601
|
-
for (const id of raw) {
|
|
8602
|
-
if (typeof id !== "string") continue;
|
|
8603
|
-
const trimmed = id.trim();
|
|
8604
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
8605
|
-
seen.add(trimmed);
|
|
8606
|
-
out.push(trimmed);
|
|
8607
|
-
}
|
|
8608
|
-
return out;
|
|
8718
|
+
return expandDaemonIdForms(coordinatorDaemonId);
|
|
8609
8719
|
}
|
|
8610
8720
|
function readRefineJobId2(event) {
|
|
8611
8721
|
const metadata = readRecord4(event.metadataEvent) || event;
|
|
@@ -8793,6 +8903,7 @@ function queuePendingMeshCoordinatorEvent(event) {
|
|
|
8793
8903
|
return true;
|
|
8794
8904
|
}
|
|
8795
8905
|
const fingerprint = buildPendingEventFingerprint(event);
|
|
8906
|
+
let sqliteOk = false;
|
|
8796
8907
|
try {
|
|
8797
8908
|
MeshRuntimeStore.getInstance().insertPendingEvent({
|
|
8798
8909
|
id: (0, import_crypto7.randomUUID)(),
|
|
@@ -8803,11 +8914,17 @@ function queuePendingMeshCoordinatorEvent(event) {
|
|
|
8803
8914
|
fingerprint: fingerprint || null,
|
|
8804
8915
|
queuedAt: event.queuedAt
|
|
8805
8916
|
});
|
|
8917
|
+
sqliteOk = true;
|
|
8806
8918
|
} catch {
|
|
8807
8919
|
}
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8920
|
+
try {
|
|
8921
|
+
const path42 = getPendingEventsPath(event.meshId, event.targetCoordinatorDaemonId);
|
|
8922
|
+
trimPendingEventsIfNeeded(path42);
|
|
8923
|
+
(0, import_fs10.appendFileSync)(path42, JSON.stringify(event) + "\n", "utf-8");
|
|
8924
|
+
} catch (e) {
|
|
8925
|
+
if (!sqliteOk) throw e;
|
|
8926
|
+
LOG.warn("MeshEvents", `JSONL append failed for mesh ${event.meshId}; SQLite holds the event: ${e?.message || e}`);
|
|
8927
|
+
}
|
|
8811
8928
|
return true;
|
|
8812
8929
|
} catch (e) {
|
|
8813
8930
|
LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
@@ -8988,6 +9105,7 @@ var init_mesh_events_pending = __esm({
|
|
|
8988
9105
|
init_mesh_ledger();
|
|
8989
9106
|
init_mesh_runtime_store();
|
|
8990
9107
|
init_mesh_events_utils();
|
|
9108
|
+
init_dist();
|
|
8991
9109
|
REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
|
|
8992
9110
|
MAX_PENDING_EVENTS_BYTES = 100 * 1024;
|
|
8993
9111
|
MAX_PENDING_EVENTS_KEEP = 50;
|
|
@@ -9206,6 +9324,12 @@ function hasUnterminalDirectDispatchLedgerEntry(meshId, sessionId) {
|
|
|
9206
9324
|
}
|
|
9207
9325
|
return false;
|
|
9208
9326
|
}
|
|
9327
|
+
function isWeakCompletionLedgerPayload(payload) {
|
|
9328
|
+
if (!payload) return false;
|
|
9329
|
+
if (payload.evidenceLevel === "insufficient" || payload.reviewRecommended === true) return true;
|
|
9330
|
+
const diag = readRecord4(payload.completionDiagnostic);
|
|
9331
|
+
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
9332
|
+
}
|
|
9209
9333
|
function findDirectDispatchLedgerEntry(args) {
|
|
9210
9334
|
const entries = readLedgerEntries(args.meshId, { tail: 500 });
|
|
9211
9335
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
@@ -9242,6 +9366,7 @@ function hasTerminalLedgerAfterDispatch(args) {
|
|
|
9242
9366
|
if (!afterDispatch) continue;
|
|
9243
9367
|
}
|
|
9244
9368
|
if (entry.kind !== "task_completed" && entry.kind !== "task_failed" && entry.kind !== "task_stalled") continue;
|
|
9369
|
+
if (entry.kind === "task_completed" && isWeakCompletionLedgerPayload(entry.payload)) continue;
|
|
9245
9370
|
const terminalTaskId = readNonEmptyString2(entry.payload?.taskId);
|
|
9246
9371
|
if (terminalTaskId && terminalTaskId === args.taskId) return true;
|
|
9247
9372
|
if (terminalTaskId && terminalTaskId !== args.taskId) continue;
|
|
@@ -12432,12 +12557,9 @@ var init_snapshot = __esm({
|
|
|
12432
12557
|
|
|
12433
12558
|
// src/mesh/mesh-events-coordinator.ts
|
|
12434
12559
|
function resolveCoordinatorDrainDaemonIds(components) {
|
|
12435
|
-
const ids = /* @__PURE__ */ new Set();
|
|
12436
12560
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
12437
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
12438
12561
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
12439
|
-
|
|
12440
|
-
return [...ids];
|
|
12562
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
12441
12563
|
}
|
|
12442
12564
|
function getCachedMeshByWorkspace(workspace) {
|
|
12443
12565
|
const now = Date.now();
|
|
@@ -12564,6 +12686,79 @@ function isDuplicateRefineTerminalEvent(meshId, eventName, metadataEvent) {
|
|
|
12564
12686
|
recordFingerprintSeen(fingerprint);
|
|
12565
12687
|
return false;
|
|
12566
12688
|
}
|
|
12689
|
+
function isFalseIdleCompletion(metadataEvent) {
|
|
12690
|
+
const diag = readRecord4(metadataEvent.completionDiagnostic);
|
|
12691
|
+
if (!diag) return false;
|
|
12692
|
+
return diag.finalAssistantPresent === false || diag.blockReason === "missing_final_assistant";
|
|
12693
|
+
}
|
|
12694
|
+
function isGenuineCompletionEvidence(metadataEvent) {
|
|
12695
|
+
if (isFalseIdleCompletion(metadataEvent)) return false;
|
|
12696
|
+
return !!readWorkerResultMetadata(metadataEvent) || !!readNonEmptyString2(metadataEvent.finalSummary);
|
|
12697
|
+
}
|
|
12698
|
+
function isWeakTerminalLedgerPayload(payload) {
|
|
12699
|
+
if (!payload) return false;
|
|
12700
|
+
if (payload.evidenceLevel === "insufficient" || payload.reviewRecommended === true) return true;
|
|
12701
|
+
const diag = readRecord4(payload.completionDiagnostic);
|
|
12702
|
+
return diag?.finalAssistantPresent === false || diag?.blockReason === "missing_final_assistant";
|
|
12703
|
+
}
|
|
12704
|
+
function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
12705
|
+
try {
|
|
12706
|
+
const matches = getActiveDirectDispatches(meshId).filter((d) => d.sessionId === sessionId);
|
|
12707
|
+
if (!matches.length) return void 0;
|
|
12708
|
+
return readNonEmptyString2(matches[matches.length - 1].taskId) || void 0;
|
|
12709
|
+
} catch {
|
|
12710
|
+
return void 0;
|
|
12711
|
+
}
|
|
12712
|
+
}
|
|
12713
|
+
function deliverTaskToSession(dispatchThunk, ctx) {
|
|
12714
|
+
const delivery = createSessionDelivery({
|
|
12715
|
+
meshId: ctx.meshId,
|
|
12716
|
+
nodeId: ctx.nodeId,
|
|
12717
|
+
sessionId: ctx.sessionId,
|
|
12718
|
+
providerType: ctx.providerType,
|
|
12719
|
+
taskId: ctx.task.id,
|
|
12720
|
+
kind: "task",
|
|
12721
|
+
message: ctx.task.message,
|
|
12722
|
+
status: "delivering",
|
|
12723
|
+
...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
|
|
12724
|
+
...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
|
|
12725
|
+
});
|
|
12726
|
+
let dispatchPromise;
|
|
12727
|
+
try {
|
|
12728
|
+
dispatchPromise = Promise.resolve(dispatchThunk());
|
|
12729
|
+
} catch (e) {
|
|
12730
|
+
dispatchPromise = Promise.reject(e);
|
|
12731
|
+
}
|
|
12732
|
+
let timer;
|
|
12733
|
+
const guarded = Promise.race([
|
|
12734
|
+
dispatchPromise,
|
|
12735
|
+
new Promise((_, reject) => {
|
|
12736
|
+
timer = setTimeout(
|
|
12737
|
+
() => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
|
|
12738
|
+
DISPATCH_CONFIRM_TIMEOUT_MS
|
|
12739
|
+
);
|
|
12740
|
+
if (typeof timer?.unref === "function") timer.unref();
|
|
12741
|
+
})
|
|
12742
|
+
]);
|
|
12743
|
+
guarded.then(() => {
|
|
12744
|
+
if (timer) clearTimeout(timer);
|
|
12745
|
+
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12746
|
+
}).catch((e) => {
|
|
12747
|
+
if (timer) clearTimeout(timer);
|
|
12748
|
+
LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
|
|
12749
|
+
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12750
|
+
updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
|
|
12751
|
+
try {
|
|
12752
|
+
appendLedgerEntry(ctx.meshId, {
|
|
12753
|
+
kind: "dispatch_failed",
|
|
12754
|
+
nodeId: ctx.nodeId,
|
|
12755
|
+
sessionId: ctx.sessionId,
|
|
12756
|
+
payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
|
|
12757
|
+
});
|
|
12758
|
+
} catch {
|
|
12759
|
+
}
|
|
12760
|
+
});
|
|
12761
|
+
}
|
|
12567
12762
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12568
12763
|
const mesh = getMeshWithCache(components, meshId);
|
|
12569
12764
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
@@ -12582,46 +12777,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12582
12777
|
if (!isLocalNode) {
|
|
12583
12778
|
const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
12584
12779
|
const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
|
|
12585
|
-
const
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
meshContext: {
|
|
12780
|
+
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
12781
|
+
const remoteDaemonId = node.daemonId;
|
|
12782
|
+
deliverTaskToSession(
|
|
12783
|
+
() => dispatchMeshCommand(remoteDaemonId, "agent_command", {
|
|
12784
|
+
targetSessionId: sessionId,
|
|
12785
|
+
cliType: providerType,
|
|
12786
|
+
action: "send_chat",
|
|
12787
|
+
message: task.message,
|
|
12788
|
+
meshContext: {
|
|
12789
|
+
meshId,
|
|
12790
|
+
nodeId,
|
|
12791
|
+
taskId: task.id,
|
|
12792
|
+
...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
|
|
12793
|
+
...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
|
|
12794
|
+
}
|
|
12795
|
+
}),
|
|
12796
|
+
{
|
|
12603
12797
|
meshId,
|
|
12604
12798
|
nodeId,
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
}).catch((e) => {
|
|
12612
|
-
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
12613
|
-
updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12614
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12615
|
-
try {
|
|
12616
|
-
appendLedgerEntry(meshId, {
|
|
12617
|
-
kind: "dispatch_failed",
|
|
12618
|
-
nodeId,
|
|
12619
|
-
sessionId,
|
|
12620
|
-
payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
|
|
12621
|
-
});
|
|
12622
|
-
} catch {
|
|
12799
|
+
sessionId,
|
|
12800
|
+
providerType,
|
|
12801
|
+
task,
|
|
12802
|
+
transport: "remote",
|
|
12803
|
+
...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
|
|
12804
|
+
...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
|
|
12623
12805
|
}
|
|
12624
|
-
|
|
12806
|
+
);
|
|
12625
12807
|
return true;
|
|
12626
12808
|
}
|
|
12627
12809
|
}
|
|
@@ -12643,39 +12825,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12643
12825
|
}
|
|
12644
12826
|
} catch {
|
|
12645
12827
|
}
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
message: task.message
|
|
12663
|
-
}).then(() => {
|
|
12664
|
-
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12665
|
-
}).catch((e) => {
|
|
12666
|
-
LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
|
|
12667
|
-
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12668
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12669
|
-
try {
|
|
12670
|
-
appendLedgerEntry(meshId, {
|
|
12671
|
-
kind: "dispatch_failed",
|
|
12672
|
-
nodeId,
|
|
12673
|
-
sessionId,
|
|
12674
|
-
payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
|
|
12675
|
-
});
|
|
12676
|
-
} catch {
|
|
12828
|
+
deliverTaskToSession(
|
|
12829
|
+
() => components.cliManager.handleCliCommand("agent_command", {
|
|
12830
|
+
targetSessionId: sessionId,
|
|
12831
|
+
cliType: providerType,
|
|
12832
|
+
action: "send_chat",
|
|
12833
|
+
message: task.message
|
|
12834
|
+
}),
|
|
12835
|
+
{
|
|
12836
|
+
meshId,
|
|
12837
|
+
nodeId,
|
|
12838
|
+
sessionId,
|
|
12839
|
+
providerType,
|
|
12840
|
+
task,
|
|
12841
|
+
transport: "local",
|
|
12842
|
+
...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
|
|
12843
|
+
...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
|
|
12677
12844
|
}
|
|
12678
|
-
|
|
12845
|
+
);
|
|
12679
12846
|
return true;
|
|
12680
12847
|
}
|
|
12681
12848
|
function sweepExpiredCooldowns() {
|
|
@@ -12940,7 +13107,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
12940
13107
|
}
|
|
12941
13108
|
}
|
|
12942
13109
|
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
12943
|
-
if (task.targetNodeId &&
|
|
13110
|
+
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
12944
13111
|
if (task.requiredTags?.length) {
|
|
12945
13112
|
const priorities = normalizeProviderPriority(node?.policy);
|
|
12946
13113
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
@@ -12951,7 +13118,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
12951
13118
|
return true;
|
|
12952
13119
|
}) : [];
|
|
12953
13120
|
if (!candidateNodes.length) {
|
|
12954
|
-
|
|
13121
|
+
const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
|
|
13122
|
+
markAutoLaunch(meshId, task.id, {
|
|
13123
|
+
status: "skipped",
|
|
13124
|
+
reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
|
|
13125
|
+
nodeId: task.targetNodeId
|
|
13126
|
+
});
|
|
12955
13127
|
continue;
|
|
12956
13128
|
}
|
|
12957
13129
|
const strategy = resolveSchedulingStrategy(mesh);
|
|
@@ -13408,7 +13580,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13408
13580
|
});
|
|
13409
13581
|
if (terminal?.kind === "task_completed" && !sessionHasActiveAssignment(args.meshId, eventSessionId)) {
|
|
13410
13582
|
const newDispatchAfterTerminal = hasDispatchAfterTerminal(args.meshId, eventSessionId, terminal.id);
|
|
13411
|
-
|
|
13583
|
+
const supersedesWeakTerminal = isWeakTerminalLedgerPayload(terminal.payload) && isGenuineCompletionEvidence(args.metadataEvent);
|
|
13584
|
+
if (!newDispatchAfterTerminal && !supersedesWeakTerminal) {
|
|
13412
13585
|
const terminalProviderSessionId = readNonEmptyString2(terminal.payload.providerSessionId);
|
|
13413
13586
|
const terminalFinalSummary = readNonEmptyString2(terminal.payload.finalSummary);
|
|
13414
13587
|
const eventProviderSessionId = readNonEmptyString2(args.metadataEvent.providerSessionId);
|
|
@@ -13454,24 +13627,30 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13454
13627
|
return { success: true, forwarded: 0, suppressed: true, duplicateStopped: true };
|
|
13455
13628
|
}
|
|
13456
13629
|
}
|
|
13457
|
-
function markSessionTerminal(sessionId, outcome, occurredAtMs) {
|
|
13630
|
+
function markSessionTerminal(sessionId, outcome, occurredAtMs, opts) {
|
|
13458
13631
|
const eventTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
|
|
13459
13632
|
const task = updateSessionTaskStatus(args.meshId, sessionId, outcome, {
|
|
13460
13633
|
occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
|
|
13461
13634
|
taskId: eventTaskId
|
|
13462
13635
|
});
|
|
13463
|
-
|
|
13636
|
+
const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true;
|
|
13637
|
+
if (!leaveDirectDispatchActive) {
|
|
13638
|
+
updateDirectDispatchStatus(args.meshId, sessionId, outcome);
|
|
13639
|
+
}
|
|
13464
13640
|
markSessionDeliveriesTerminal(args.meshId, sessionId, outcome);
|
|
13465
13641
|
setImmediate(() => cleanupTerminalDirectDispatches());
|
|
13466
13642
|
return task ? { id: task.id } : null;
|
|
13467
13643
|
}
|
|
13468
13644
|
let completedTaskForLedger = null;
|
|
13645
|
+
let directDispatchTaskIdForLedger;
|
|
13469
13646
|
if (args.event === "agent:generating_completed") {
|
|
13470
13647
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
13471
13648
|
const nodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
|
|
13472
13649
|
const providerType = readNonEmptyString2(args.metadataEvent.providerType);
|
|
13473
13650
|
if (sessionId) {
|
|
13474
|
-
|
|
13651
|
+
directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
13652
|
+
const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
|
|
13653
|
+
completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
|
|
13475
13654
|
if (nodeId && providerType) {
|
|
13476
13655
|
runIdleMaintenanceThenAssignQueue(components, { meshId: args.meshId, nodeId, sessionId, providerType });
|
|
13477
13656
|
}
|
|
@@ -13574,6 +13753,7 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13574
13753
|
}
|
|
13575
13754
|
}
|
|
13576
13755
|
if (sessionId) {
|
|
13756
|
+
directDispatchTaskIdForLedger = resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
|
|
13577
13757
|
completedTaskForLedger = markSessionTerminal(sessionId, "failed");
|
|
13578
13758
|
}
|
|
13579
13759
|
}
|
|
@@ -13603,7 +13783,10 @@ function injectMeshSystemMessage(components, args) {
|
|
|
13603
13783
|
payload: {
|
|
13604
13784
|
event: args.event,
|
|
13605
13785
|
nodeLabel: args.nodeLabel,
|
|
13606
|
-
|
|
13786
|
+
// Fix B: fall back to the direct-dispatch taskId when no work-queue row
|
|
13787
|
+
// matched, so the terminal entry is attributable in mesh task-stats
|
|
13788
|
+
// (otherwise the direct task shows status='unknown' / terminalKind=null).
|
|
13789
|
+
taskId: completedTaskForLedger?.id || directDispatchTaskIdForLedger || void 0,
|
|
13607
13790
|
providerSessionId,
|
|
13608
13791
|
finalSummary,
|
|
13609
13792
|
workerResult,
|
|
@@ -13891,7 +14074,7 @@ function setupMeshEventForwarding(components) {
|
|
|
13891
14074
|
});
|
|
13892
14075
|
});
|
|
13893
14076
|
}
|
|
13894
|
-
var import_fs13, 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;
|
|
14077
|
+
var import_fs13, 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;
|
|
13895
14078
|
var init_mesh_events_coordinator = __esm({
|
|
13896
14079
|
"src/mesh/mesh-events-coordinator.ts"() {
|
|
13897
14080
|
"use strict";
|
|
@@ -13920,6 +14103,7 @@ var init_mesh_events_coordinator = __esm({
|
|
|
13920
14103
|
idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
|
|
13921
14104
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
13922
14105
|
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
14106
|
+
DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
|
|
13923
14107
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
13924
14108
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
13925
14109
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -13975,12 +14159,9 @@ function resolveReconcileIntervalMs() {
|
|
|
13975
14159
|
return DEFAULT_RECONCILE_INTERVAL_MS;
|
|
13976
14160
|
}
|
|
13977
14161
|
function resolveCoordinatorDaemonIds(components) {
|
|
13978
|
-
const ids = /* @__PURE__ */ new Set();
|
|
13979
14162
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
13980
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
13981
14163
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
13982
|
-
|
|
13983
|
-
return [...ids];
|
|
14164
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
13984
14165
|
}
|
|
13985
14166
|
function daemonHostsMesh(mesh, daemonIds) {
|
|
13986
14167
|
const host = mesh.meshHost;
|
|
@@ -14064,6 +14245,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
|
|
|
14064
14245
|
}
|
|
14065
14246
|
}
|
|
14066
14247
|
}
|
|
14248
|
+
function recoverStrandedAssignedDispatches(meshId, store) {
|
|
14249
|
+
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
14250
|
+
if (!assigned.length) return;
|
|
14251
|
+
const nowMs = Date.now();
|
|
14252
|
+
for (const row of assigned) {
|
|
14253
|
+
const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
|
|
14254
|
+
if (!Number.isFinite(dispatchedAtMs)) continue;
|
|
14255
|
+
if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
|
|
14256
|
+
if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
|
|
14257
|
+
const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
|
|
14258
|
+
reason: "assigned_stranded_dispatch_unconfirmed",
|
|
14259
|
+
ageMs: nowMs - dispatchedAtMs
|
|
14260
|
+
});
|
|
14261
|
+
if (reclaimed) {
|
|
14262
|
+
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})`);
|
|
14263
|
+
}
|
|
14264
|
+
}
|
|
14265
|
+
}
|
|
14067
14266
|
async function runMeshReconcileTick(components) {
|
|
14068
14267
|
const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
14069
14268
|
const drainDaemonIds = resolveCoordinatorDaemonIds(components);
|
|
@@ -14093,6 +14292,17 @@ async function runMeshReconcileTick(components) {
|
|
|
14093
14292
|
}
|
|
14094
14293
|
}
|
|
14095
14294
|
}
|
|
14295
|
+
if (store) {
|
|
14296
|
+
for (const mesh of listMeshes()) {
|
|
14297
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14298
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
14299
|
+
try {
|
|
14300
|
+
recoverStrandedAssignedDispatches(mesh.id, store);
|
|
14301
|
+
} catch (e) {
|
|
14302
|
+
LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
14303
|
+
}
|
|
14304
|
+
}
|
|
14305
|
+
}
|
|
14096
14306
|
for (const mesh of listMeshes()) {
|
|
14097
14307
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14098
14308
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
@@ -14480,7 +14690,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
14480
14690
|
}
|
|
14481
14691
|
};
|
|
14482
14692
|
}
|
|
14483
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
|
|
14693
|
+
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
|
|
14484
14694
|
var init_mesh_reconcile_loop = __esm({
|
|
14485
14695
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
14486
14696
|
"use strict";
|
|
@@ -14493,6 +14703,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14493
14703
|
init_mesh_events_coordinator();
|
|
14494
14704
|
init_mesh_unresolved_forward_outbox();
|
|
14495
14705
|
init_mesh_events_utils();
|
|
14706
|
+
init_dist();
|
|
14496
14707
|
init_mesh_work_queue();
|
|
14497
14708
|
init_mesh_ledger();
|
|
14498
14709
|
init_mesh_active_work();
|
|
@@ -14501,6 +14712,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14501
14712
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
14502
14713
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
14503
14714
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
14715
|
+
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
14504
14716
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
14505
14717
|
}
|
|
14506
14718
|
});
|
|
@@ -14533,6 +14745,84 @@ var init_mesh_events = __esm({
|
|
|
14533
14745
|
}
|
|
14534
14746
|
});
|
|
14535
14747
|
|
|
14748
|
+
// src/providers/approval-utils.ts
|
|
14749
|
+
function normalizeApprovalLabel(value) {
|
|
14750
|
+
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
14751
|
+
}
|
|
14752
|
+
function isNegativeApprovalLabel(value) {
|
|
14753
|
+
const label = normalizeApprovalLabel(value);
|
|
14754
|
+
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
14755
|
+
}
|
|
14756
|
+
function hasNegativeApprovalOption(buttons) {
|
|
14757
|
+
return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
|
|
14758
|
+
}
|
|
14759
|
+
function getApprovalPositiveHints(provider) {
|
|
14760
|
+
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
14761
|
+
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14762
|
+
}
|
|
14763
|
+
function pickApprovalButton(buttons, provider) {
|
|
14764
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
14765
|
+
if (labels.length === 0) {
|
|
14766
|
+
return { index: -1, label: "" };
|
|
14767
|
+
}
|
|
14768
|
+
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
14769
|
+
const hints = getApprovalPositiveHints(provider);
|
|
14770
|
+
for (const hint of hints) {
|
|
14771
|
+
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
14772
|
+
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
14773
|
+
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14774
|
+
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
14775
|
+
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14776
|
+
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
14777
|
+
}
|
|
14778
|
+
return { index: -1, label: "" };
|
|
14779
|
+
}
|
|
14780
|
+
function pickAutoApprovalButton(buttons) {
|
|
14781
|
+
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
14782
|
+
const index = labels.findIndex(Boolean);
|
|
14783
|
+
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
14784
|
+
}
|
|
14785
|
+
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
14786
|
+
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
14787
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
14788
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
14789
|
+
return lines.join("\n");
|
|
14790
|
+
}
|
|
14791
|
+
function looksLikeActiveApprovalPromptText(content) {
|
|
14792
|
+
const text = content.trim();
|
|
14793
|
+
if (!text || text.length > 2e3) return false;
|
|
14794
|
+
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);
|
|
14795
|
+
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
14796
|
+
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
14797
|
+
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
14798
|
+
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
14799
|
+
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
14800
|
+
if (hasDontAskAgain && hasNoOption) return true;
|
|
14801
|
+
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
14802
|
+
return false;
|
|
14803
|
+
}
|
|
14804
|
+
var DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14805
|
+
var init_approval_utils = __esm({
|
|
14806
|
+
"src/providers/approval-utils.ts"() {
|
|
14807
|
+
"use strict";
|
|
14808
|
+
DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
14809
|
+
"yes",
|
|
14810
|
+
"allow once",
|
|
14811
|
+
"approve",
|
|
14812
|
+
"accept",
|
|
14813
|
+
"continue",
|
|
14814
|
+
"run",
|
|
14815
|
+
"proceed",
|
|
14816
|
+
"confirm",
|
|
14817
|
+
"save",
|
|
14818
|
+
"ok",
|
|
14819
|
+
"trust",
|
|
14820
|
+
"allow",
|
|
14821
|
+
"always allow"
|
|
14822
|
+
];
|
|
14823
|
+
}
|
|
14824
|
+
});
|
|
14825
|
+
|
|
14536
14826
|
// src/logging/debug-config.ts
|
|
14537
14827
|
function normalizeCategories(categories) {
|
|
14538
14828
|
if (!Array.isArray(categories)) return [];
|
|
@@ -15519,6 +15809,27 @@ function compileSettledPromptMatchers(spec) {
|
|
|
15519
15809
|
});
|
|
15520
15810
|
return { prompt, footers };
|
|
15521
15811
|
}
|
|
15812
|
+
function extractButtonLabels(spec, text) {
|
|
15813
|
+
if (!text) return [];
|
|
15814
|
+
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
15815
|
+
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
15816
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
15817
|
+
const out = [];
|
|
15818
|
+
for (const line of text.split("\n")) {
|
|
15819
|
+
buttonRe.lastIndex = 0;
|
|
15820
|
+
const m = buttonRe.exec(line);
|
|
15821
|
+
if (!m) continue;
|
|
15822
|
+
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
15823
|
+
if (captured && captured.trim()) out.push(captured.trim());
|
|
15824
|
+
}
|
|
15825
|
+
return out;
|
|
15826
|
+
}
|
|
15827
|
+
function buttonBlockApprovalCue(spec, text) {
|
|
15828
|
+
const labels = extractButtonLabels(spec, text);
|
|
15829
|
+
if (labels.length < 2) return false;
|
|
15830
|
+
if (pickApprovalButton(labels).index < 0) return false;
|
|
15831
|
+
return hasNegativeApprovalOption(labels);
|
|
15832
|
+
}
|
|
15522
15833
|
function modalMatches(spec, input) {
|
|
15523
15834
|
const text = input.screenText ?? "";
|
|
15524
15835
|
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
@@ -15527,6 +15838,7 @@ function modalMatches(spec, input) {
|
|
|
15527
15838
|
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
15528
15839
|
if (re.test(text)) return true;
|
|
15529
15840
|
}
|
|
15841
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
15530
15842
|
return false;
|
|
15531
15843
|
}
|
|
15532
15844
|
function evaluateGroup(group, spec, input, compiled) {
|
|
@@ -15581,6 +15893,7 @@ var init_detect_status = __esm({
|
|
|
15581
15893
|
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
15582
15894
|
"use strict";
|
|
15583
15895
|
init_visible_region();
|
|
15896
|
+
init_approval_utils();
|
|
15584
15897
|
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15585
15898
|
}
|
|
15586
15899
|
});
|
|
@@ -20174,6 +20487,7 @@ __export(index_exports, {
|
|
|
20174
20487
|
readLedgerEntries: () => readLedgerEntries,
|
|
20175
20488
|
readLedgerSlice: () => readLedgerSlice,
|
|
20176
20489
|
readLedgerSliceFromStore: () => readLedgerSliceFromStore,
|
|
20490
|
+
readMeshCompletionSummary: () => readMeshCompletionSummary,
|
|
20177
20491
|
reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
|
|
20178
20492
|
recordCompletionConflict: () => recordCompletionConflict,
|
|
20179
20493
|
recordDebugTrace: () => recordDebugTrace,
|
|
@@ -20199,6 +20513,7 @@ __export(index_exports, {
|
|
|
20199
20513
|
resolveMeshHostStatus: () => resolveMeshHostStatus,
|
|
20200
20514
|
resolveMeshNodeAttribution: () => resolveMeshNodeAttribution,
|
|
20201
20515
|
resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
|
|
20516
|
+
resolveMeshSurfacedSessionPreview: () => resolveMeshSurfacedSessionPreview,
|
|
20202
20517
|
resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
|
|
20203
20518
|
resolveSessionHostAppName: () => resolveSessionHostAppName,
|
|
20204
20519
|
resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution,
|
|
@@ -22117,6 +22432,7 @@ init_mesh_active_work();
|
|
|
22117
22432
|
init_mesh_refine_status();
|
|
22118
22433
|
init_mesh_host_ownership();
|
|
22119
22434
|
init_mesh_events();
|
|
22435
|
+
init_mesh_events_utils();
|
|
22120
22436
|
init_mesh_delivery_policy();
|
|
22121
22437
|
|
|
22122
22438
|
// src/mesh/p2p-relay-failure.ts
|
|
@@ -26036,76 +26352,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
26036
26352
|
return normalized;
|
|
26037
26353
|
}
|
|
26038
26354
|
|
|
26039
|
-
// src/providers/approval-utils.ts
|
|
26040
|
-
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
26041
|
-
"yes",
|
|
26042
|
-
"allow once",
|
|
26043
|
-
"approve",
|
|
26044
|
-
"accept",
|
|
26045
|
-
"continue",
|
|
26046
|
-
"run",
|
|
26047
|
-
"proceed",
|
|
26048
|
-
"confirm",
|
|
26049
|
-
"save",
|
|
26050
|
-
"ok",
|
|
26051
|
-
"trust",
|
|
26052
|
-
"allow",
|
|
26053
|
-
"always allow"
|
|
26054
|
-
];
|
|
26055
|
-
function normalizeApprovalLabel(value) {
|
|
26056
|
-
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
26057
|
-
}
|
|
26058
|
-
function isNegativeApprovalLabel(value) {
|
|
26059
|
-
const label = normalizeApprovalLabel(value);
|
|
26060
|
-
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
26061
|
-
}
|
|
26062
|
-
function getApprovalPositiveHints(provider) {
|
|
26063
|
-
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
26064
|
-
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
26065
|
-
}
|
|
26066
|
-
function pickApprovalButton(buttons, provider) {
|
|
26067
|
-
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
26068
|
-
if (labels.length === 0) {
|
|
26069
|
-
return { index: -1, label: "" };
|
|
26070
|
-
}
|
|
26071
|
-
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
26072
|
-
const hints = getApprovalPositiveHints(provider);
|
|
26073
|
-
for (const hint of hints) {
|
|
26074
|
-
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
26075
|
-
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
26076
|
-
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
26077
|
-
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
26078
|
-
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
26079
|
-
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
26080
|
-
}
|
|
26081
|
-
return { index: -1, label: "" };
|
|
26082
|
-
}
|
|
26083
|
-
function pickAutoApprovalButton(buttons) {
|
|
26084
|
-
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
26085
|
-
const index = labels.findIndex(Boolean);
|
|
26086
|
-
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
26087
|
-
}
|
|
26088
|
-
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
26089
|
-
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
26090
|
-
const cleanMessage = String(modalMessage || "").trim();
|
|
26091
|
-
if (cleanMessage) lines.push(cleanMessage);
|
|
26092
|
-
return lines.join("\n");
|
|
26093
|
-
}
|
|
26094
|
-
function looksLikeActiveApprovalPromptText(content) {
|
|
26095
|
-
const text = content.trim();
|
|
26096
|
-
if (!text || text.length > 2e3) return false;
|
|
26097
|
-
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);
|
|
26098
|
-
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
26099
|
-
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
26100
|
-
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
26101
|
-
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
26102
|
-
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
26103
|
-
if (hasDontAskAgain && hasNoOption) return true;
|
|
26104
|
-
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
26105
|
-
return false;
|
|
26106
|
-
}
|
|
26107
|
-
|
|
26108
26355
|
// src/providers/ide-provider-instance.ts
|
|
26356
|
+
init_approval_utils();
|
|
26109
26357
|
init_provider_patch_state();
|
|
26110
26358
|
init_chat_message_normalization();
|
|
26111
26359
|
init_open_panel_support();
|
|
@@ -27189,6 +27437,7 @@ var path16 = __toESM(require("path"));
|
|
|
27189
27437
|
var import_node_crypto3 = require("crypto");
|
|
27190
27438
|
init_contracts();
|
|
27191
27439
|
init_provider_input_support();
|
|
27440
|
+
init_approval_utils();
|
|
27192
27441
|
init_coordinator_registry();
|
|
27193
27442
|
init_logger();
|
|
27194
27443
|
|
|
@@ -35177,6 +35426,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
35177
35426
|
// src/providers/cli-provider-instance.ts
|
|
35178
35427
|
init_logger();
|
|
35179
35428
|
init_control_effects();
|
|
35429
|
+
init_approval_utils();
|
|
35180
35430
|
init_provider_patch_state();
|
|
35181
35431
|
|
|
35182
35432
|
// src/providers/provider-session-id.ts
|
|
@@ -35438,6 +35688,20 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35438
35688
|
* keystroke until the modal *content* has settled.
|
|
35439
35689
|
*/
|
|
35440
35690
|
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
35691
|
+
/**
|
|
35692
|
+
* Busy-side hysteresis for the settle gate. A momentary `generating` flip
|
|
35693
|
+
* while the SAME approval modal's button block is still on screen (its
|
|
35694
|
+
* question line scrolled out of the captured frame, only the buttons + a
|
|
35695
|
+
* residual `esc to interrupt` spinner remain) briefly reports
|
|
35696
|
+
* status!=waiting_approval. Without hysteresis that flip wipes the settle
|
|
35697
|
+
* clock, and the modal→generating→modal flap restarts the 600ms window
|
|
35698
|
+
* every time so auto-approve never fires. We keep the in-progress settle
|
|
35699
|
+
* gate warm across an inactive blip up to this bound; only once the modal
|
|
35700
|
+
* has genuinely stayed gone this long (a real resolution → idle) is the
|
|
35701
|
+
* gate cleared. Bounded so a genuinely new, later approval still re-settles
|
|
35702
|
+
* from scratch rather than firing on a stale timestamp.
|
|
35703
|
+
*/
|
|
35704
|
+
static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
|
|
35441
35705
|
adapter;
|
|
35442
35706
|
context = null;
|
|
35443
35707
|
events = [];
|
|
@@ -35459,6 +35723,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35459
35723
|
pendingAutoApprovalSignature = "";
|
|
35460
35724
|
pendingAutoApprovalSince = 0;
|
|
35461
35725
|
autoApproveSettleTimer = null;
|
|
35726
|
+
// Wall-clock when auto-approve first observed status!=waiting_approval while
|
|
35727
|
+
// a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
|
|
35728
|
+
// brief generating flip does not immediately wipe the settle clock.
|
|
35729
|
+
autoApproveInactiveSince = 0;
|
|
35462
35730
|
controlValues = {};
|
|
35463
35731
|
summaryMetadata = void 0;
|
|
35464
35732
|
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
@@ -36279,14 +36547,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36279
36547
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
36280
36548
|
if (!autoApproveActive) {
|
|
36281
36549
|
this.lastAutoApprovalSignature = "";
|
|
36550
|
+
if (this.pendingAutoApprovalSince) {
|
|
36551
|
+
if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
|
|
36552
|
+
const goneForMs = now - this.autoApproveInactiveSince;
|
|
36553
|
+
if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
|
|
36554
|
+
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
36555
|
+
this.autoApproveSettleTimer = setTimeout(() => {
|
|
36556
|
+
this.autoApproveSettleTimer = null;
|
|
36557
|
+
this.recheckAutoApproveSettled();
|
|
36558
|
+
}, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
|
|
36559
|
+
return autoApproveActive;
|
|
36560
|
+
}
|
|
36561
|
+
}
|
|
36282
36562
|
this.pendingAutoApprovalSignature = "";
|
|
36283
36563
|
this.pendingAutoApprovalSince = 0;
|
|
36564
|
+
this.autoApproveInactiveSince = 0;
|
|
36284
36565
|
if (this.autoApproveSettleTimer) {
|
|
36285
36566
|
clearTimeout(this.autoApproveSettleTimer);
|
|
36286
36567
|
this.autoApproveSettleTimer = null;
|
|
36287
36568
|
}
|
|
36288
36569
|
return autoApproveActive;
|
|
36289
36570
|
}
|
|
36571
|
+
this.autoApproveInactiveSince = 0;
|
|
36290
36572
|
const modal = adapterStatus.activeModal;
|
|
36291
36573
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
36292
36574
|
if (!modal || buttons.length === 0) {
|
|
@@ -36296,18 +36578,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36296
36578
|
if (buttonIndex < 0) {
|
|
36297
36579
|
return autoApproveActive;
|
|
36298
36580
|
}
|
|
36299
|
-
const
|
|
36300
|
-
const signature = [
|
|
36301
|
-
approvalEntrySeq,
|
|
36581
|
+
const modalSignature = [
|
|
36302
36582
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
36303
36583
|
buttons.join("|"),
|
|
36304
36584
|
buttonIndex
|
|
36305
36585
|
].join("::");
|
|
36306
|
-
|
|
36586
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
36587
|
+
const busySignature = `${approvalEntrySeq}::${modalSignature}`;
|
|
36588
|
+
if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
|
|
36307
36589
|
return autoApproveActive;
|
|
36308
36590
|
}
|
|
36309
|
-
if (
|
|
36310
|
-
this.pendingAutoApprovalSignature =
|
|
36591
|
+
if (modalSignature !== this.pendingAutoApprovalSignature) {
|
|
36592
|
+
this.pendingAutoApprovalSignature = modalSignature;
|
|
36311
36593
|
this.pendingAutoApprovalSince = now;
|
|
36312
36594
|
}
|
|
36313
36595
|
const settledForMs = now - this.pendingAutoApprovalSince;
|
|
@@ -36324,9 +36606,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36324
36606
|
this.autoApproveSettleTimer = null;
|
|
36325
36607
|
}
|
|
36326
36608
|
this.autoApproveBusy = true;
|
|
36327
|
-
this.lastAutoApprovalSignature =
|
|
36609
|
+
this.lastAutoApprovalSignature = busySignature;
|
|
36328
36610
|
this.pendingAutoApprovalSignature = "";
|
|
36329
36611
|
this.pendingAutoApprovalSince = 0;
|
|
36612
|
+
this.autoApproveInactiveSince = 0;
|
|
36330
36613
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
36331
36614
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
36332
36615
|
this.autoApproveBusy = false;
|
|
@@ -43213,7 +43496,8 @@ init_logger();
|
|
|
43213
43496
|
var fs23 = __toESM(require("fs"));
|
|
43214
43497
|
var path35 = __toESM(require("path"));
|
|
43215
43498
|
var os26 = __toESM(require("os"));
|
|
43216
|
-
var
|
|
43499
|
+
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");
|
|
43500
|
+
var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
|
|
43217
43501
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
43218
43502
|
var MAX_DAYS = 7;
|
|
43219
43503
|
try {
|
|
@@ -43673,7 +43957,7 @@ function runGit2(repoRoot, args) {
|
|
|
43673
43957
|
return "";
|
|
43674
43958
|
}
|
|
43675
43959
|
}
|
|
43676
|
-
function
|
|
43960
|
+
function readRecord5(repoRoot) {
|
|
43677
43961
|
const path42 = (0, import_node_path2.resolve)(repoRoot, PREVIEW_DEPLOY_RECORD);
|
|
43678
43962
|
if (!(0, import_node_fs4.existsSync)(path42)) return null;
|
|
43679
43963
|
try {
|
|
@@ -43713,7 +43997,7 @@ function readCurrentMainCommit(repoRoot) {
|
|
|
43713
43997
|
}
|
|
43714
43998
|
function buildPreviewFreshness(repoRoot) {
|
|
43715
43999
|
const current = readCurrentMainCommit(repoRoot);
|
|
43716
|
-
const record =
|
|
44000
|
+
const record = readRecord5(repoRoot);
|
|
43717
44001
|
const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
|
|
43718
44002
|
const targets = readTargetFreshness(record, current.currentMainCommit);
|
|
43719
44003
|
let status = "unknown";
|
|
@@ -44068,13 +44352,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
44068
44352
|
}
|
|
44069
44353
|
}
|
|
44070
44354
|
}
|
|
44071
|
-
function stopSessionHostProcesses(appName) {
|
|
44355
|
+
async function stopSessionHostProcesses(appName) {
|
|
44072
44356
|
const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
44357
|
+
let killedPid = null;
|
|
44073
44358
|
try {
|
|
44074
44359
|
if (fs25.existsSync(pidFile)) {
|
|
44075
44360
|
const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
|
|
44076
44361
|
if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
|
|
44077
|
-
killPid(pid);
|
|
44362
|
+
if (killPid(pid)) killedPid = pid;
|
|
44078
44363
|
}
|
|
44079
44364
|
}
|
|
44080
44365
|
} catch {
|
|
@@ -44084,6 +44369,15 @@ function stopSessionHostProcesses(appName) {
|
|
|
44084
44369
|
} catch {
|
|
44085
44370
|
}
|
|
44086
44371
|
}
|
|
44372
|
+
if (killedPid !== null) {
|
|
44373
|
+
await waitForPidExit(killedPid, 15e3);
|
|
44374
|
+
}
|
|
44375
|
+
}
|
|
44376
|
+
function isRetriableInstallLockError(error) {
|
|
44377
|
+
const code = error?.code;
|
|
44378
|
+
if (code === "EBUSY" || code === "EPERM") return true;
|
|
44379
|
+
const text = `${error?.message || ""} ${error?.stderr || ""}`;
|
|
44380
|
+
return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
|
|
44087
44381
|
}
|
|
44088
44382
|
function removeDaemonPidFile() {
|
|
44089
44383
|
const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
|
|
@@ -44163,22 +44457,37 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
44163
44457
|
appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
|
|
44164
44458
|
await waitForPidExit(payload.parentPid, 15e3);
|
|
44165
44459
|
}
|
|
44166
|
-
stopSessionHostProcesses(sessionHostAppName);
|
|
44460
|
+
await stopSessionHostProcesses(sessionHostAppName);
|
|
44167
44461
|
removeDaemonPidFile();
|
|
44168
44462
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
44169
44463
|
const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
|
|
44170
44464
|
appendUpgradeLog(`Installing ${spec}`);
|
|
44171
|
-
const
|
|
44172
|
-
|
|
44173
|
-
|
|
44174
|
-
{
|
|
44175
|
-
|
|
44176
|
-
|
|
44177
|
-
|
|
44178
|
-
|
|
44179
|
-
|
|
44465
|
+
const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
|
|
44466
|
+
let installOutput = "";
|
|
44467
|
+
for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
|
|
44468
|
+
try {
|
|
44469
|
+
installOutput = String((0, import_child_process8.execFileSync)(
|
|
44470
|
+
installCommand.command,
|
|
44471
|
+
installCommand.args,
|
|
44472
|
+
{
|
|
44473
|
+
encoding: "utf8",
|
|
44474
|
+
stdio: "pipe",
|
|
44475
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
44476
|
+
env: buildInstallEnvWithNodeOnPath(),
|
|
44477
|
+
...installCommand.execOptions
|
|
44478
|
+
}
|
|
44479
|
+
));
|
|
44480
|
+
break;
|
|
44481
|
+
} catch (error) {
|
|
44482
|
+
if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
|
|
44483
|
+
appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
|
|
44484
|
+
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
44485
|
+
await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
|
|
44486
|
+
continue;
|
|
44487
|
+
}
|
|
44488
|
+
throw error;
|
|
44180
44489
|
}
|
|
44181
|
-
|
|
44490
|
+
}
|
|
44182
44491
|
if (installOutput.trim()) {
|
|
44183
44492
|
appendUpgradeLog(installOutput.trim());
|
|
44184
44493
|
}
|
|
@@ -52940,6 +53249,7 @@ var DaemonAgentStreamManager = class {
|
|
|
52940
53249
|
|
|
52941
53250
|
// src/agent-stream/poller.ts
|
|
52942
53251
|
init_logger();
|
|
53252
|
+
init_approval_utils();
|
|
52943
53253
|
init_chat_message_normalization();
|
|
52944
53254
|
var AgentStreamPoller = class {
|
|
52945
53255
|
deps;
|
|
@@ -60635,6 +60945,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
60635
60945
|
readLedgerEntries,
|
|
60636
60946
|
readLedgerSlice,
|
|
60637
60947
|
readLedgerSliceFromStore,
|
|
60948
|
+
readMeshCompletionSummary,
|
|
60638
60949
|
reconcileDirectDispatchCompletionFromTranscript,
|
|
60639
60950
|
recordCompletionConflict,
|
|
60640
60951
|
recordDebugTrace,
|
|
@@ -60660,6 +60971,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
60660
60971
|
resolveMeshHostStatus,
|
|
60661
60972
|
resolveMeshNodeAttribution,
|
|
60662
60973
|
resolveMeshRefineValidationPlan,
|
|
60974
|
+
resolveMeshSurfacedSessionPreview,
|
|
60663
60975
|
resolveNodeSchedulingPriority,
|
|
60664
60976
|
resolveSessionHostAppName,
|
|
60665
60977
|
resolveSessionHostAppNameResolution,
|