@adhdev/daemon-core 0.9.82-rc.351 → 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.js +424 -189
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +424 -189
- 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 +9 -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/logging/command-log.ts +7 -5
- package/src/logging/logger.ts +12 -6
- package/src/mesh/mesh-events-coordinator.ts +165 -84
- package/src/mesh/mesh-events-pending.ts +14 -15
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-reconcile-loop.ts +67 -7
- package/src/mesh/mesh-runtime-store.ts +17 -0
- 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
|
|
|
@@ -5502,6 +5552,22 @@ var init_mesh_runtime_store = __esm({
|
|
|
5502
5552
|
updatedAt: r.updated_at
|
|
5503
5553
|
}));
|
|
5504
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
|
+
}
|
|
5505
5571
|
expireStaleSessionDeliveries(meshId) {
|
|
5506
5572
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5507
5573
|
this.db.prepare(`
|
|
@@ -7925,6 +7991,35 @@ function meshNodeIdMatches(node, candidateId) {
|
|
|
7925
7991
|
if (!trimmed) return false;
|
|
7926
7992
|
return normalizeMeshNodeId(node) === trimmed;
|
|
7927
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
|
+
}
|
|
7928
8023
|
function summarizeGitShape(status) {
|
|
7929
8024
|
const record = readRecord3(status);
|
|
7930
8025
|
if (!Object.keys(record).length) return null;
|
|
@@ -7959,9 +8054,11 @@ function summarizeGitShape(status) {
|
|
|
7959
8054
|
submodules
|
|
7960
8055
|
};
|
|
7961
8056
|
}
|
|
8057
|
+
var DAEMON_ID_PREFIXES;
|
|
7962
8058
|
var init_dist = __esm({
|
|
7963
8059
|
"../mesh-shared/dist/index.mjs"() {
|
|
7964
8060
|
"use strict";
|
|
8061
|
+
DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
|
|
7965
8062
|
}
|
|
7966
8063
|
});
|
|
7967
8064
|
|
|
@@ -8618,17 +8715,7 @@ var init_mesh_events_utils = __esm({
|
|
|
8618
8715
|
|
|
8619
8716
|
// src/mesh/mesh-events-pending.ts
|
|
8620
8717
|
function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
8621
|
-
|
|
8622
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8623
|
-
const out = [];
|
|
8624
|
-
for (const id of raw) {
|
|
8625
|
-
if (typeof id !== "string") continue;
|
|
8626
|
-
const trimmed = id.trim();
|
|
8627
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
8628
|
-
seen.add(trimmed);
|
|
8629
|
-
out.push(trimmed);
|
|
8630
|
-
}
|
|
8631
|
-
return out;
|
|
8718
|
+
return expandDaemonIdForms(coordinatorDaemonId);
|
|
8632
8719
|
}
|
|
8633
8720
|
function readRefineJobId2(event) {
|
|
8634
8721
|
const metadata = readRecord4(event.metadataEvent) || event;
|
|
@@ -9018,6 +9105,7 @@ var init_mesh_events_pending = __esm({
|
|
|
9018
9105
|
init_mesh_ledger();
|
|
9019
9106
|
init_mesh_runtime_store();
|
|
9020
9107
|
init_mesh_events_utils();
|
|
9108
|
+
init_dist();
|
|
9021
9109
|
REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
|
|
9022
9110
|
MAX_PENDING_EVENTS_BYTES = 100 * 1024;
|
|
9023
9111
|
MAX_PENDING_EVENTS_KEEP = 50;
|
|
@@ -12469,12 +12557,9 @@ var init_snapshot = __esm({
|
|
|
12469
12557
|
|
|
12470
12558
|
// src/mesh/mesh-events-coordinator.ts
|
|
12471
12559
|
function resolveCoordinatorDrainDaemonIds(components) {
|
|
12472
|
-
const ids = /* @__PURE__ */ new Set();
|
|
12473
12560
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
12474
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
12475
12561
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
12476
|
-
|
|
12477
|
-
return [...ids];
|
|
12562
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
12478
12563
|
}
|
|
12479
12564
|
function getCachedMeshByWorkspace(workspace) {
|
|
12480
12565
|
const now = Date.now();
|
|
@@ -12625,6 +12710,55 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
|
12625
12710
|
return void 0;
|
|
12626
12711
|
}
|
|
12627
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
|
+
}
|
|
12628
12762
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12629
12763
|
const mesh = getMeshWithCache(components, meshId);
|
|
12630
12764
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
@@ -12643,46 +12777,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12643
12777
|
if (!isLocalNode) {
|
|
12644
12778
|
const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
12645
12779
|
const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
|
|
12646
|
-
const
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
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
|
+
{
|
|
12664
12797
|
meshId,
|
|
12665
12798
|
nodeId,
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
}).catch((e) => {
|
|
12673
|
-
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
12674
|
-
updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12675
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12676
|
-
try {
|
|
12677
|
-
appendLedgerEntry(meshId, {
|
|
12678
|
-
kind: "dispatch_failed",
|
|
12679
|
-
nodeId,
|
|
12680
|
-
sessionId,
|
|
12681
|
-
payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
|
|
12682
|
-
});
|
|
12683
|
-
} catch {
|
|
12799
|
+
sessionId,
|
|
12800
|
+
providerType,
|
|
12801
|
+
task,
|
|
12802
|
+
transport: "remote",
|
|
12803
|
+
...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
|
|
12804
|
+
...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
|
|
12684
12805
|
}
|
|
12685
|
-
|
|
12806
|
+
);
|
|
12686
12807
|
return true;
|
|
12687
12808
|
}
|
|
12688
12809
|
}
|
|
@@ -12704,39 +12825,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12704
12825
|
}
|
|
12705
12826
|
} catch {
|
|
12706
12827
|
}
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
message: task.message
|
|
12724
|
-
}).then(() => {
|
|
12725
|
-
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12726
|
-
}).catch((e) => {
|
|
12727
|
-
LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
|
|
12728
|
-
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12729
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12730
|
-
try {
|
|
12731
|
-
appendLedgerEntry(meshId, {
|
|
12732
|
-
kind: "dispatch_failed",
|
|
12733
|
-
nodeId,
|
|
12734
|
-
sessionId,
|
|
12735
|
-
payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
|
|
12736
|
-
});
|
|
12737
|
-
} 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) } : {}
|
|
12738
12844
|
}
|
|
12739
|
-
|
|
12845
|
+
);
|
|
12740
12846
|
return true;
|
|
12741
12847
|
}
|
|
12742
12848
|
function sweepExpiredCooldowns() {
|
|
@@ -13001,7 +13107,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
13001
13107
|
}
|
|
13002
13108
|
}
|
|
13003
13109
|
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
13004
|
-
if (task.targetNodeId &&
|
|
13110
|
+
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
13005
13111
|
if (task.requiredTags?.length) {
|
|
13006
13112
|
const priorities = normalizeProviderPriority(node?.policy);
|
|
13007
13113
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
@@ -13012,7 +13118,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
13012
13118
|
return true;
|
|
13013
13119
|
}) : [];
|
|
13014
13120
|
if (!candidateNodes.length) {
|
|
13015
|
-
|
|
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
|
+
});
|
|
13016
13127
|
continue;
|
|
13017
13128
|
}
|
|
13018
13129
|
const strategy = resolveSchedulingStrategy(mesh);
|
|
@@ -13963,7 +14074,7 @@ function setupMeshEventForwarding(components) {
|
|
|
13963
14074
|
});
|
|
13964
14075
|
});
|
|
13965
14076
|
}
|
|
13966
|
-
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;
|
|
13967
14078
|
var init_mesh_events_coordinator = __esm({
|
|
13968
14079
|
"src/mesh/mesh-events-coordinator.ts"() {
|
|
13969
14080
|
"use strict";
|
|
@@ -13992,6 +14103,7 @@ var init_mesh_events_coordinator = __esm({
|
|
|
13992
14103
|
idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
|
|
13993
14104
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
13994
14105
|
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
14106
|
+
DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
|
|
13995
14107
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
13996
14108
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
13997
14109
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -14047,12 +14159,9 @@ function resolveReconcileIntervalMs() {
|
|
|
14047
14159
|
return DEFAULT_RECONCILE_INTERVAL_MS;
|
|
14048
14160
|
}
|
|
14049
14161
|
function resolveCoordinatorDaemonIds(components) {
|
|
14050
|
-
const ids = /* @__PURE__ */ new Set();
|
|
14051
14162
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
14052
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
14053
14163
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
14054
|
-
|
|
14055
|
-
return [...ids];
|
|
14164
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
14056
14165
|
}
|
|
14057
14166
|
function daemonHostsMesh(mesh, daemonIds) {
|
|
14058
14167
|
const host = mesh.meshHost;
|
|
@@ -14136,6 +14245,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
|
|
|
14136
14245
|
}
|
|
14137
14246
|
}
|
|
14138
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
|
+
}
|
|
14139
14266
|
async function runMeshReconcileTick(components) {
|
|
14140
14267
|
const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
14141
14268
|
const drainDaemonIds = resolveCoordinatorDaemonIds(components);
|
|
@@ -14165,6 +14292,17 @@ async function runMeshReconcileTick(components) {
|
|
|
14165
14292
|
}
|
|
14166
14293
|
}
|
|
14167
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
|
+
}
|
|
14168
14306
|
for (const mesh of listMeshes()) {
|
|
14169
14307
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14170
14308
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
@@ -14552,7 +14690,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
14552
14690
|
}
|
|
14553
14691
|
};
|
|
14554
14692
|
}
|
|
14555
|
-
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;
|
|
14556
14694
|
var init_mesh_reconcile_loop = __esm({
|
|
14557
14695
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
14558
14696
|
"use strict";
|
|
@@ -14565,6 +14703,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14565
14703
|
init_mesh_events_coordinator();
|
|
14566
14704
|
init_mesh_unresolved_forward_outbox();
|
|
14567
14705
|
init_mesh_events_utils();
|
|
14706
|
+
init_dist();
|
|
14568
14707
|
init_mesh_work_queue();
|
|
14569
14708
|
init_mesh_ledger();
|
|
14570
14709
|
init_mesh_active_work();
|
|
@@ -14573,6 +14712,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14573
14712
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
14574
14713
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
14575
14714
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
14715
|
+
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
14576
14716
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
14577
14717
|
}
|
|
14578
14718
|
});
|
|
@@ -14605,6 +14745,84 @@ var init_mesh_events = __esm({
|
|
|
14605
14745
|
}
|
|
14606
14746
|
});
|
|
14607
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
|
+
|
|
14608
14826
|
// src/logging/debug-config.ts
|
|
14609
14827
|
function normalizeCategories(categories) {
|
|
14610
14828
|
if (!Array.isArray(categories)) return [];
|
|
@@ -15591,6 +15809,27 @@ function compileSettledPromptMatchers(spec) {
|
|
|
15591
15809
|
});
|
|
15592
15810
|
return { prompt, footers };
|
|
15593
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
|
+
}
|
|
15594
15833
|
function modalMatches(spec, input) {
|
|
15595
15834
|
const text = input.screenText ?? "";
|
|
15596
15835
|
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
@@ -15599,6 +15838,7 @@ function modalMatches(spec, input) {
|
|
|
15599
15838
|
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
15600
15839
|
if (re.test(text)) return true;
|
|
15601
15840
|
}
|
|
15841
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
15602
15842
|
return false;
|
|
15603
15843
|
}
|
|
15604
15844
|
function evaluateGroup(group, spec, input, compiled) {
|
|
@@ -15653,6 +15893,7 @@ var init_detect_status = __esm({
|
|
|
15653
15893
|
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
15654
15894
|
"use strict";
|
|
15655
15895
|
init_visible_region();
|
|
15896
|
+
init_approval_utils();
|
|
15656
15897
|
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15657
15898
|
}
|
|
15658
15899
|
});
|
|
@@ -26111,76 +26352,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
26111
26352
|
return normalized;
|
|
26112
26353
|
}
|
|
26113
26354
|
|
|
26114
|
-
// src/providers/approval-utils.ts
|
|
26115
|
-
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
26116
|
-
"yes",
|
|
26117
|
-
"allow once",
|
|
26118
|
-
"approve",
|
|
26119
|
-
"accept",
|
|
26120
|
-
"continue",
|
|
26121
|
-
"run",
|
|
26122
|
-
"proceed",
|
|
26123
|
-
"confirm",
|
|
26124
|
-
"save",
|
|
26125
|
-
"ok",
|
|
26126
|
-
"trust",
|
|
26127
|
-
"allow",
|
|
26128
|
-
"always allow"
|
|
26129
|
-
];
|
|
26130
|
-
function normalizeApprovalLabel(value) {
|
|
26131
|
-
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
26132
|
-
}
|
|
26133
|
-
function isNegativeApprovalLabel(value) {
|
|
26134
|
-
const label = normalizeApprovalLabel(value);
|
|
26135
|
-
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
26136
|
-
}
|
|
26137
|
-
function getApprovalPositiveHints(provider) {
|
|
26138
|
-
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
26139
|
-
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
26140
|
-
}
|
|
26141
|
-
function pickApprovalButton(buttons, provider) {
|
|
26142
|
-
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
26143
|
-
if (labels.length === 0) {
|
|
26144
|
-
return { index: -1, label: "" };
|
|
26145
|
-
}
|
|
26146
|
-
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
26147
|
-
const hints = getApprovalPositiveHints(provider);
|
|
26148
|
-
for (const hint of hints) {
|
|
26149
|
-
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
26150
|
-
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
26151
|
-
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
26152
|
-
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
26153
|
-
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
26154
|
-
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
26155
|
-
}
|
|
26156
|
-
return { index: -1, label: "" };
|
|
26157
|
-
}
|
|
26158
|
-
function pickAutoApprovalButton(buttons) {
|
|
26159
|
-
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
26160
|
-
const index = labels.findIndex(Boolean);
|
|
26161
|
-
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
26162
|
-
}
|
|
26163
|
-
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
26164
|
-
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
26165
|
-
const cleanMessage = String(modalMessage || "").trim();
|
|
26166
|
-
if (cleanMessage) lines.push(cleanMessage);
|
|
26167
|
-
return lines.join("\n");
|
|
26168
|
-
}
|
|
26169
|
-
function looksLikeActiveApprovalPromptText(content) {
|
|
26170
|
-
const text = content.trim();
|
|
26171
|
-
if (!text || text.length > 2e3) return false;
|
|
26172
|
-
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);
|
|
26173
|
-
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
26174
|
-
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
26175
|
-
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
26176
|
-
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
26177
|
-
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
26178
|
-
if (hasDontAskAgain && hasNoOption) return true;
|
|
26179
|
-
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
26180
|
-
return false;
|
|
26181
|
-
}
|
|
26182
|
-
|
|
26183
26355
|
// src/providers/ide-provider-instance.ts
|
|
26356
|
+
init_approval_utils();
|
|
26184
26357
|
init_provider_patch_state();
|
|
26185
26358
|
init_chat_message_normalization();
|
|
26186
26359
|
init_open_panel_support();
|
|
@@ -27264,6 +27437,7 @@ var path16 = __toESM(require("path"));
|
|
|
27264
27437
|
var import_node_crypto3 = require("crypto");
|
|
27265
27438
|
init_contracts();
|
|
27266
27439
|
init_provider_input_support();
|
|
27440
|
+
init_approval_utils();
|
|
27267
27441
|
init_coordinator_registry();
|
|
27268
27442
|
init_logger();
|
|
27269
27443
|
|
|
@@ -35252,6 +35426,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
35252
35426
|
// src/providers/cli-provider-instance.ts
|
|
35253
35427
|
init_logger();
|
|
35254
35428
|
init_control_effects();
|
|
35429
|
+
init_approval_utils();
|
|
35255
35430
|
init_provider_patch_state();
|
|
35256
35431
|
|
|
35257
35432
|
// src/providers/provider-session-id.ts
|
|
@@ -35513,6 +35688,20 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35513
35688
|
* keystroke until the modal *content* has settled.
|
|
35514
35689
|
*/
|
|
35515
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;
|
|
35516
35705
|
adapter;
|
|
35517
35706
|
context = null;
|
|
35518
35707
|
events = [];
|
|
@@ -35534,6 +35723,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35534
35723
|
pendingAutoApprovalSignature = "";
|
|
35535
35724
|
pendingAutoApprovalSince = 0;
|
|
35536
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;
|
|
35537
35730
|
controlValues = {};
|
|
35538
35731
|
summaryMetadata = void 0;
|
|
35539
35732
|
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
@@ -36354,14 +36547,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36354
36547
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
36355
36548
|
if (!autoApproveActive) {
|
|
36356
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
|
+
}
|
|
36357
36562
|
this.pendingAutoApprovalSignature = "";
|
|
36358
36563
|
this.pendingAutoApprovalSince = 0;
|
|
36564
|
+
this.autoApproveInactiveSince = 0;
|
|
36359
36565
|
if (this.autoApproveSettleTimer) {
|
|
36360
36566
|
clearTimeout(this.autoApproveSettleTimer);
|
|
36361
36567
|
this.autoApproveSettleTimer = null;
|
|
36362
36568
|
}
|
|
36363
36569
|
return autoApproveActive;
|
|
36364
36570
|
}
|
|
36571
|
+
this.autoApproveInactiveSince = 0;
|
|
36365
36572
|
const modal = adapterStatus.activeModal;
|
|
36366
36573
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
36367
36574
|
if (!modal || buttons.length === 0) {
|
|
@@ -36371,18 +36578,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36371
36578
|
if (buttonIndex < 0) {
|
|
36372
36579
|
return autoApproveActive;
|
|
36373
36580
|
}
|
|
36374
|
-
const
|
|
36375
|
-
const signature = [
|
|
36376
|
-
approvalEntrySeq,
|
|
36581
|
+
const modalSignature = [
|
|
36377
36582
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
36378
36583
|
buttons.join("|"),
|
|
36379
36584
|
buttonIndex
|
|
36380
36585
|
].join("::");
|
|
36381
|
-
|
|
36586
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
36587
|
+
const busySignature = `${approvalEntrySeq}::${modalSignature}`;
|
|
36588
|
+
if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
|
|
36382
36589
|
return autoApproveActive;
|
|
36383
36590
|
}
|
|
36384
|
-
if (
|
|
36385
|
-
this.pendingAutoApprovalSignature =
|
|
36591
|
+
if (modalSignature !== this.pendingAutoApprovalSignature) {
|
|
36592
|
+
this.pendingAutoApprovalSignature = modalSignature;
|
|
36386
36593
|
this.pendingAutoApprovalSince = now;
|
|
36387
36594
|
}
|
|
36388
36595
|
const settledForMs = now - this.pendingAutoApprovalSince;
|
|
@@ -36399,9 +36606,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36399
36606
|
this.autoApproveSettleTimer = null;
|
|
36400
36607
|
}
|
|
36401
36608
|
this.autoApproveBusy = true;
|
|
36402
|
-
this.lastAutoApprovalSignature =
|
|
36609
|
+
this.lastAutoApprovalSignature = busySignature;
|
|
36403
36610
|
this.pendingAutoApprovalSignature = "";
|
|
36404
36611
|
this.pendingAutoApprovalSince = 0;
|
|
36612
|
+
this.autoApproveInactiveSince = 0;
|
|
36405
36613
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
36406
36614
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
36407
36615
|
this.autoApproveBusy = false;
|
|
@@ -43288,7 +43496,8 @@ init_logger();
|
|
|
43288
43496
|
var fs23 = __toESM(require("fs"));
|
|
43289
43497
|
var path35 = __toESM(require("path"));
|
|
43290
43498
|
var os26 = __toESM(require("os"));
|
|
43291
|
-
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");
|
|
43292
43501
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
43293
43502
|
var MAX_DAYS = 7;
|
|
43294
43503
|
try {
|
|
@@ -44143,13 +44352,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
44143
44352
|
}
|
|
44144
44353
|
}
|
|
44145
44354
|
}
|
|
44146
|
-
function stopSessionHostProcesses(appName) {
|
|
44355
|
+
async function stopSessionHostProcesses(appName) {
|
|
44147
44356
|
const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
44357
|
+
let killedPid = null;
|
|
44148
44358
|
try {
|
|
44149
44359
|
if (fs25.existsSync(pidFile)) {
|
|
44150
44360
|
const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
|
|
44151
44361
|
if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
|
|
44152
|
-
killPid(pid);
|
|
44362
|
+
if (killPid(pid)) killedPid = pid;
|
|
44153
44363
|
}
|
|
44154
44364
|
}
|
|
44155
44365
|
} catch {
|
|
@@ -44159,6 +44369,15 @@ function stopSessionHostProcesses(appName) {
|
|
|
44159
44369
|
} catch {
|
|
44160
44370
|
}
|
|
44161
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);
|
|
44162
44381
|
}
|
|
44163
44382
|
function removeDaemonPidFile() {
|
|
44164
44383
|
const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
|
|
@@ -44238,22 +44457,37 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
44238
44457
|
appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
|
|
44239
44458
|
await waitForPidExit(payload.parentPid, 15e3);
|
|
44240
44459
|
}
|
|
44241
|
-
stopSessionHostProcesses(sessionHostAppName);
|
|
44460
|
+
await stopSessionHostProcesses(sessionHostAppName);
|
|
44242
44461
|
removeDaemonPidFile();
|
|
44243
44462
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
44244
44463
|
const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
|
|
44245
44464
|
appendUpgradeLog(`Installing ${spec}`);
|
|
44246
|
-
const
|
|
44247
|
-
|
|
44248
|
-
|
|
44249
|
-
{
|
|
44250
|
-
|
|
44251
|
-
|
|
44252
|
-
|
|
44253
|
-
|
|
44254
|
-
|
|
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;
|
|
44255
44489
|
}
|
|
44256
|
-
|
|
44490
|
+
}
|
|
44257
44491
|
if (installOutput.trim()) {
|
|
44258
44492
|
appendUpgradeLog(installOutput.trim());
|
|
44259
44493
|
}
|
|
@@ -53015,6 +53249,7 @@ var DaemonAgentStreamManager = class {
|
|
|
53015
53249
|
|
|
53016
53250
|
// src/agent-stream/poller.ts
|
|
53017
53251
|
init_logger();
|
|
53252
|
+
init_approval_utils();
|
|
53018
53253
|
init_chat_message_normalization();
|
|
53019
53254
|
var AgentStreamPoller = class {
|
|
53020
53255
|
deps;
|