@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.mjs
CHANGED
|
@@ -311,10 +311,10 @@ function readInjected(value) {
|
|
|
311
311
|
}
|
|
312
312
|
function getDaemonBuildInfo() {
|
|
313
313
|
if (cached) return cached;
|
|
314
|
-
const commit = readInjected(true ? "
|
|
315
|
-
const commitShort = readInjected(true ? "
|
|
316
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
317
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
314
|
+
const commit = readInjected(true ? "ca5f944b7763a621357fc28a9f62ac398b0ced6c" : void 0) ?? "unknown";
|
|
315
|
+
const commitShort = readInjected(true ? "ca5f944b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
316
|
+
const version = readInjected(true ? "0.9.82-rc.352" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
317
|
+
const builtAt = readInjected(true ? "2026-06-22T07:17:05.715Z" : void 0);
|
|
318
318
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
319
319
|
return cached;
|
|
320
320
|
}
|
|
@@ -3193,7 +3193,7 @@ function installGlobalInterceptor() {
|
|
|
3193
3193
|
function getLogPath() {
|
|
3194
3194
|
return currentLogFile;
|
|
3195
3195
|
}
|
|
3196
|
-
var LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
3196
|
+
var LEVEL_NUM, LEVEL_LABEL, currentLevel, ADHDEV_HOME, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
3197
3197
|
var init_logger = __esm({
|
|
3198
3198
|
"src/logging/logger.ts"() {
|
|
3199
3199
|
"use strict";
|
|
@@ -3201,7 +3201,8 @@ var init_logger = __esm({
|
|
|
3201
3201
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
3202
3202
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
3203
3203
|
currentLevel = "info";
|
|
3204
|
-
|
|
3204
|
+
ADHDEV_HOME = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path9.join(os3.homedir(), ".adhdev");
|
|
3205
|
+
LOG_DIR = path9.join(ADHDEV_HOME, "logs");
|
|
3205
3206
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
3206
3207
|
MAX_LOG_DAYS = 7;
|
|
3207
3208
|
try {
|
|
@@ -4001,6 +4002,7 @@ __export(mesh_work_queue_exports, {
|
|
|
4001
4002
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
4002
4003
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
4003
4004
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
4005
|
+
reclaimStrandedAssignedTask: () => reclaimStrandedAssignedTask,
|
|
4004
4006
|
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
4005
4007
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
4006
4008
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
@@ -4437,6 +4439,52 @@ function requeueTask(meshId, taskId, opts) {
|
|
|
4437
4439
|
return entry;
|
|
4438
4440
|
});
|
|
4439
4441
|
}
|
|
4442
|
+
function reclaimStrandedAssignedTask(meshId, taskId, opts) {
|
|
4443
|
+
requireMeshHostQueueOwner(opts);
|
|
4444
|
+
return withQueueLock(meshId, () => {
|
|
4445
|
+
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
4446
|
+
if (!entry) return null;
|
|
4447
|
+
if (entry.status !== "assigned") return null;
|
|
4448
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4449
|
+
const reason = opts?.reason || "assigned_stranded_dispatch_unconfirmed";
|
|
4450
|
+
const reclaims = (entry.strandedReclaimCount || 0) + 1;
|
|
4451
|
+
const prevNode = entry.assignedNodeId;
|
|
4452
|
+
const prevSession = entry.assignedSessionId;
|
|
4453
|
+
delete entry.assignedNodeId;
|
|
4454
|
+
delete entry.assignedSessionId;
|
|
4455
|
+
delete entry.assignedProviderType;
|
|
4456
|
+
delete entry.dispatchTimestamp;
|
|
4457
|
+
entry.strandedReclaimCount = reclaims;
|
|
4458
|
+
entry.updatedAt = now;
|
|
4459
|
+
if (reclaims > MAX_STRANDED_RECLAIMS) {
|
|
4460
|
+
entry.status = "failed";
|
|
4461
|
+
entry.cancelReason = `stranded_dispatch_unrecovered: reclaimed ${reclaims - 1} time(s) without a confirmed dispatch`;
|
|
4462
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4463
|
+
propagateDependencyFailure(meshId, taskId);
|
|
4464
|
+
} else {
|
|
4465
|
+
entry.status = "pending";
|
|
4466
|
+
entry.requeuedAt = now;
|
|
4467
|
+
entry.requeueReason = reason;
|
|
4468
|
+
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
4469
|
+
}
|
|
4470
|
+
try {
|
|
4471
|
+
appendLedgerEntry(meshId, {
|
|
4472
|
+
kind: "task_reclaimed",
|
|
4473
|
+
nodeId: prevNode,
|
|
4474
|
+
sessionId: prevSession,
|
|
4475
|
+
payload: {
|
|
4476
|
+
taskId,
|
|
4477
|
+
reason,
|
|
4478
|
+
...typeof opts?.ageMs === "number" ? { ageMs: opts.ageMs } : {},
|
|
4479
|
+
reclaimCount: reclaims,
|
|
4480
|
+
outcome: entry.status
|
|
4481
|
+
}
|
|
4482
|
+
});
|
|
4483
|
+
} catch {
|
|
4484
|
+
}
|
|
4485
|
+
return entry;
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4440
4488
|
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
4441
4489
|
return withQueueLock(meshId, () => {
|
|
4442
4490
|
const store = MeshRuntimeStore.getInstance();
|
|
@@ -4550,7 +4598,7 @@ function recordMeshToolCall(opts) {
|
|
|
4550
4598
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
4551
4599
|
}
|
|
4552
4600
|
}
|
|
4553
|
-
var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS;
|
|
4601
|
+
var ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS;
|
|
4554
4602
|
var init_mesh_work_queue = __esm({
|
|
4555
4603
|
"src/mesh/mesh-work-queue.ts"() {
|
|
4556
4604
|
"use strict";
|
|
@@ -4559,6 +4607,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4559
4607
|
init_mesh_runtime_store();
|
|
4560
4608
|
init_mesh_config();
|
|
4561
4609
|
init_logger();
|
|
4610
|
+
init_mesh_ledger();
|
|
4562
4611
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
4563
4612
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
4564
4613
|
MESH_TASK_MODES = ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"];
|
|
@@ -4611,6 +4660,7 @@ var init_mesh_work_queue = __esm({
|
|
|
4611
4660
|
]);
|
|
4612
4661
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
4613
4662
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
4663
|
+
MAX_STRANDED_RECLAIMS = 3;
|
|
4614
4664
|
}
|
|
4615
4665
|
});
|
|
4616
4666
|
|
|
@@ -5496,6 +5546,22 @@ var init_mesh_runtime_store = __esm({
|
|
|
5496
5546
|
updatedAt: r.updated_at
|
|
5497
5547
|
}));
|
|
5498
5548
|
}
|
|
5549
|
+
/**
|
|
5550
|
+
* Bug B watchdog support: true when at least one delivery record for the task has
|
|
5551
|
+
* reached a confirmed-handed-off status (delivered / acked / completed). The
|
|
5552
|
+
* assigned-stranded watchdog uses this to distinguish a dispatch that was never
|
|
5553
|
+
* confirmed (reclaimable) from one that WAS handed to the worker (a genuinely
|
|
5554
|
+
* in-flight or completion-lost task, which is PHASE 4's responsibility, not this
|
|
5555
|
+
* watchdog's). Indexed by (mesh_id, task_id).
|
|
5556
|
+
*/
|
|
5557
|
+
taskHasConfirmedDelivery(meshId, taskId) {
|
|
5558
|
+
const row = this.db.prepare(`
|
|
5559
|
+
SELECT 1 FROM mesh_session_delivery
|
|
5560
|
+
WHERE mesh_id = ? AND task_id = ? AND status IN ('delivered','acked','completed')
|
|
5561
|
+
LIMIT 1
|
|
5562
|
+
`).get(meshId, taskId);
|
|
5563
|
+
return !!row;
|
|
5564
|
+
}
|
|
5499
5565
|
expireStaleSessionDeliveries(meshId) {
|
|
5500
5566
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5501
5567
|
this.db.prepare(`
|
|
@@ -7919,6 +7985,35 @@ function meshNodeIdMatches(node, candidateId) {
|
|
|
7919
7985
|
if (!trimmed) return false;
|
|
7920
7986
|
return normalizeMeshNodeId(node) === trimmed;
|
|
7921
7987
|
}
|
|
7988
|
+
function machineCoreFromDaemonId(id) {
|
|
7989
|
+
const trimmed = readString5(id);
|
|
7990
|
+
if (!trimmed) return void 0;
|
|
7991
|
+
for (const prefix of DAEMON_ID_PREFIXES) {
|
|
7992
|
+
if (trimmed.startsWith(prefix)) {
|
|
7993
|
+
const core = trimmed.slice(prefix.length).trim();
|
|
7994
|
+
return core || void 0;
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
return trimmed;
|
|
7998
|
+
}
|
|
7999
|
+
function expandDaemonIdForms(ids) {
|
|
8000
|
+
const list = Array.isArray(ids) ? ids : ids != null ? [ids] : [];
|
|
8001
|
+
const out = [];
|
|
8002
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8003
|
+
const add = (value) => {
|
|
8004
|
+
if (!value || seen.has(value)) return;
|
|
8005
|
+
seen.add(value);
|
|
8006
|
+
out.push(value);
|
|
8007
|
+
};
|
|
8008
|
+
for (const raw of list) add(readString5(raw));
|
|
8009
|
+
for (const raw of list) {
|
|
8010
|
+
const core = machineCoreFromDaemonId(readString5(raw));
|
|
8011
|
+
if (!core || !core.startsWith("mach_")) continue;
|
|
8012
|
+
add(core);
|
|
8013
|
+
for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`);
|
|
8014
|
+
}
|
|
8015
|
+
return out;
|
|
8016
|
+
}
|
|
7922
8017
|
function summarizeGitShape(status) {
|
|
7923
8018
|
const record = readRecord3(status);
|
|
7924
8019
|
if (!Object.keys(record).length) return null;
|
|
@@ -7953,9 +8048,11 @@ function summarizeGitShape(status) {
|
|
|
7953
8048
|
submodules
|
|
7954
8049
|
};
|
|
7955
8050
|
}
|
|
8051
|
+
var DAEMON_ID_PREFIXES;
|
|
7956
8052
|
var init_dist = __esm({
|
|
7957
8053
|
"../mesh-shared/dist/index.mjs"() {
|
|
7958
8054
|
"use strict";
|
|
8055
|
+
DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
|
|
7959
8056
|
}
|
|
7960
8057
|
});
|
|
7961
8058
|
|
|
@@ -8615,17 +8712,7 @@ import { appendFileSync as appendFileSync2, existsSync as existsSync14, readFile
|
|
|
8615
8712
|
import { join as join15 } from "path";
|
|
8616
8713
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
8617
8714
|
function normalizeCoordinatorDaemonIds(coordinatorDaemonId) {
|
|
8618
|
-
|
|
8619
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8620
|
-
const out = [];
|
|
8621
|
-
for (const id of raw) {
|
|
8622
|
-
if (typeof id !== "string") continue;
|
|
8623
|
-
const trimmed = id.trim();
|
|
8624
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
8625
|
-
seen.add(trimmed);
|
|
8626
|
-
out.push(trimmed);
|
|
8627
|
-
}
|
|
8628
|
-
return out;
|
|
8715
|
+
return expandDaemonIdForms(coordinatorDaemonId);
|
|
8629
8716
|
}
|
|
8630
8717
|
function readRefineJobId2(event) {
|
|
8631
8718
|
const metadata = readRecord4(event.metadataEvent) || event;
|
|
@@ -9012,6 +9099,7 @@ var init_mesh_events_pending = __esm({
|
|
|
9012
9099
|
init_mesh_ledger();
|
|
9013
9100
|
init_mesh_runtime_store();
|
|
9014
9101
|
init_mesh_events_utils();
|
|
9102
|
+
init_dist();
|
|
9015
9103
|
REFINE_TERMINAL_EVENTS = /* @__PURE__ */ new Set(["refine:completed", "refine:failed"]);
|
|
9016
9104
|
MAX_PENDING_EVENTS_BYTES = 100 * 1024;
|
|
9017
9105
|
MAX_PENDING_EVENTS_KEEP = 50;
|
|
@@ -12466,12 +12554,9 @@ var init_snapshot = __esm({
|
|
|
12466
12554
|
// src/mesh/mesh-events-coordinator.ts
|
|
12467
12555
|
import { existsSync as existsSync17 } from "fs";
|
|
12468
12556
|
function resolveCoordinatorDrainDaemonIds(components) {
|
|
12469
|
-
const ids = /* @__PURE__ */ new Set();
|
|
12470
12557
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
12471
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
12472
12558
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
12473
|
-
|
|
12474
|
-
return [...ids];
|
|
12559
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
12475
12560
|
}
|
|
12476
12561
|
function getCachedMeshByWorkspace(workspace) {
|
|
12477
12562
|
const now = Date.now();
|
|
@@ -12622,6 +12707,55 @@ function resolveActiveDirectDispatchTaskId(meshId, sessionId) {
|
|
|
12622
12707
|
return void 0;
|
|
12623
12708
|
}
|
|
12624
12709
|
}
|
|
12710
|
+
function deliverTaskToSession(dispatchThunk, ctx) {
|
|
12711
|
+
const delivery = createSessionDelivery({
|
|
12712
|
+
meshId: ctx.meshId,
|
|
12713
|
+
nodeId: ctx.nodeId,
|
|
12714
|
+
sessionId: ctx.sessionId,
|
|
12715
|
+
providerType: ctx.providerType,
|
|
12716
|
+
taskId: ctx.task.id,
|
|
12717
|
+
kind: "task",
|
|
12718
|
+
message: ctx.task.message,
|
|
12719
|
+
status: "delivering",
|
|
12720
|
+
...ctx.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: ctx.sourceCoordinatorSessionId } : {},
|
|
12721
|
+
...ctx.sourceCoordinatorDaemonId ? { sourceCoordinatorDaemonId: ctx.sourceCoordinatorDaemonId } : {}
|
|
12722
|
+
});
|
|
12723
|
+
let dispatchPromise;
|
|
12724
|
+
try {
|
|
12725
|
+
dispatchPromise = Promise.resolve(dispatchThunk());
|
|
12726
|
+
} catch (e) {
|
|
12727
|
+
dispatchPromise = Promise.reject(e);
|
|
12728
|
+
}
|
|
12729
|
+
let timer;
|
|
12730
|
+
const guarded = Promise.race([
|
|
12731
|
+
dispatchPromise,
|
|
12732
|
+
new Promise((_, reject) => {
|
|
12733
|
+
timer = setTimeout(
|
|
12734
|
+
() => reject(new Error(`dispatch_confirm_timeout after ${DISPATCH_CONFIRM_TIMEOUT_MS}ms`)),
|
|
12735
|
+
DISPATCH_CONFIRM_TIMEOUT_MS
|
|
12736
|
+
);
|
|
12737
|
+
if (typeof timer?.unref === "function") timer.unref();
|
|
12738
|
+
})
|
|
12739
|
+
]);
|
|
12740
|
+
guarded.then(() => {
|
|
12741
|
+
if (timer) clearTimeout(timer);
|
|
12742
|
+
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12743
|
+
}).catch((e) => {
|
|
12744
|
+
if (timer) clearTimeout(timer);
|
|
12745
|
+
LOG.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
|
|
12746
|
+
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12747
|
+
updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
|
|
12748
|
+
try {
|
|
12749
|
+
appendLedgerEntry(ctx.meshId, {
|
|
12750
|
+
kind: "dispatch_failed",
|
|
12751
|
+
nodeId: ctx.nodeId,
|
|
12752
|
+
sessionId: ctx.sessionId,
|
|
12753
|
+
payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: true, transport: ctx.transport }
|
|
12754
|
+
});
|
|
12755
|
+
} catch {
|
|
12756
|
+
}
|
|
12757
|
+
});
|
|
12758
|
+
}
|
|
12625
12759
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
12626
12760
|
const mesh = getMeshWithCache(components, meshId);
|
|
12627
12761
|
const node = mesh?.nodes.find((n) => readMeshNodeId(n) === nodeId);
|
|
@@ -12640,46 +12774,33 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12640
12774
|
if (!isLocalNode) {
|
|
12641
12775
|
const localDaemonIdForDispatch = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
12642
12776
|
const sourceCoordinatorSessionId = readNonEmptyString2(task.sourceCoordinatorSessionId) || void 0;
|
|
12643
|
-
const
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
meshContext: {
|
|
12777
|
+
const dispatchMeshCommand = components.dispatchMeshCommand;
|
|
12778
|
+
const remoteDaemonId = node.daemonId;
|
|
12779
|
+
deliverTaskToSession(
|
|
12780
|
+
() => dispatchMeshCommand(remoteDaemonId, "agent_command", {
|
|
12781
|
+
targetSessionId: sessionId,
|
|
12782
|
+
cliType: providerType,
|
|
12783
|
+
action: "send_chat",
|
|
12784
|
+
message: task.message,
|
|
12785
|
+
meshContext: {
|
|
12786
|
+
meshId,
|
|
12787
|
+
nodeId,
|
|
12788
|
+
taskId: task.id,
|
|
12789
|
+
...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
|
|
12790
|
+
...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
|
|
12791
|
+
}
|
|
12792
|
+
}),
|
|
12793
|
+
{
|
|
12661
12794
|
meshId,
|
|
12662
12795
|
nodeId,
|
|
12663
|
-
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12669
|
-
}).catch((e) => {
|
|
12670
|
-
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
12671
|
-
updateSessionDeliveryStatus(delivery2.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12672
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12673
|
-
try {
|
|
12674
|
-
appendLedgerEntry(meshId, {
|
|
12675
|
-
kind: "dispatch_failed",
|
|
12676
|
-
nodeId,
|
|
12677
|
-
sessionId,
|
|
12678
|
-
payload: { taskId: task.id, deliveryId: delivery2.id, error: e?.message, retryable: true }
|
|
12679
|
-
});
|
|
12680
|
-
} catch {
|
|
12796
|
+
sessionId,
|
|
12797
|
+
providerType,
|
|
12798
|
+
task,
|
|
12799
|
+
transport: "remote",
|
|
12800
|
+
...sourceCoordinatorSessionId ? { sourceCoordinatorSessionId } : {},
|
|
12801
|
+
...localDaemonIdForDispatch ? { sourceCoordinatorDaemonId: localDaemonIdForDispatch } : {}
|
|
12681
12802
|
}
|
|
12682
|
-
|
|
12803
|
+
);
|
|
12683
12804
|
return true;
|
|
12684
12805
|
}
|
|
12685
12806
|
}
|
|
@@ -12701,39 +12822,24 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
12701
12822
|
}
|
|
12702
12823
|
} catch {
|
|
12703
12824
|
}
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
message: task.message
|
|
12721
|
-
}).then(() => {
|
|
12722
|
-
updateSessionDeliveryStatus(delivery.id, "delivered");
|
|
12723
|
-
}).catch((e) => {
|
|
12724
|
-
LOG.error("MeshQueue", `Failed to dispatch task locally to node ${nodeId}: ${e?.message}`);
|
|
12725
|
-
updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
|
|
12726
|
-
updateTaskStatus(meshId, task.id, "pending");
|
|
12727
|
-
try {
|
|
12728
|
-
appendLedgerEntry(meshId, {
|
|
12729
|
-
kind: "dispatch_failed",
|
|
12730
|
-
nodeId,
|
|
12731
|
-
sessionId,
|
|
12732
|
-
payload: { taskId: task.id, deliveryId: delivery.id, error: e?.message, retryable: true }
|
|
12733
|
-
});
|
|
12734
|
-
} catch {
|
|
12825
|
+
deliverTaskToSession(
|
|
12826
|
+
() => components.cliManager.handleCliCommand("agent_command", {
|
|
12827
|
+
targetSessionId: sessionId,
|
|
12828
|
+
cliType: providerType,
|
|
12829
|
+
action: "send_chat",
|
|
12830
|
+
message: task.message
|
|
12831
|
+
}),
|
|
12832
|
+
{
|
|
12833
|
+
meshId,
|
|
12834
|
+
nodeId,
|
|
12835
|
+
sessionId,
|
|
12836
|
+
providerType,
|
|
12837
|
+
task,
|
|
12838
|
+
transport: "local",
|
|
12839
|
+
...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { sourceCoordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {},
|
|
12840
|
+
...readNonEmptyString2(loadConfig().machineId) ? { sourceCoordinatorDaemonId: readNonEmptyString2(loadConfig().machineId) } : {}
|
|
12735
12841
|
}
|
|
12736
|
-
|
|
12842
|
+
);
|
|
12737
12843
|
return true;
|
|
12738
12844
|
}
|
|
12739
12845
|
function sweepExpiredCooldowns() {
|
|
@@ -12998,7 +13104,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
12998
13104
|
}
|
|
12999
13105
|
}
|
|
13000
13106
|
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => {
|
|
13001
|
-
if (task.targetNodeId &&
|
|
13107
|
+
if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
|
|
13002
13108
|
if (task.requiredTags?.length) {
|
|
13003
13109
|
const priorities = normalizeProviderPriority(node?.policy);
|
|
13004
13110
|
const providerCandidates = priorities.length ? priorities : [void 0];
|
|
@@ -13009,7 +13115,12 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
13009
13115
|
return true;
|
|
13010
13116
|
}) : [];
|
|
13011
13117
|
if (!candidateNodes.length) {
|
|
13012
|
-
|
|
13118
|
+
const targetPinUnmatched = !!task.targetNodeId && !(Array.isArray(mesh?.nodes) && mesh.nodes.some((n) => meshNodeIdMatches(n, task.targetNodeId)));
|
|
13119
|
+
markAutoLaunch(meshId, task.id, {
|
|
13120
|
+
status: "skipped",
|
|
13121
|
+
reason: targetPinUnmatched ? "target_node_id_unmatched" : "no_node_satisfies_required_tags",
|
|
13122
|
+
nodeId: task.targetNodeId
|
|
13123
|
+
});
|
|
13013
13124
|
continue;
|
|
13014
13125
|
}
|
|
13015
13126
|
const strategy = resolveSchedulingStrategy(mesh);
|
|
@@ -13960,7 +14071,7 @@ function setupMeshEventForwarding(components) {
|
|
|
13960
14071
|
});
|
|
13961
14072
|
});
|
|
13962
14073
|
}
|
|
13963
|
-
var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
|
|
14074
|
+
var REMOTE_IDLE_SESSION_TTL_MS, meshByWorkspaceCache, MESH_WORKSPACE_CACHE_TTL_MS, IDLE_AUTO_FAST_FORWARD_THROTTLE_MS, idleAutoFastForwardLastAttempt, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, DISPATCH_CONFIRM_TIMEOUT_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS, AUTO_LAUNCH_AWAIT_CLAIM_MS, lastAutoLaunchLedgerKey, AUTO_LAUNCH_LEDGER_DEDUP_MAX, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, MESH_FORCE_INJECT_EVENTS;
|
|
13964
14075
|
var init_mesh_events_coordinator = __esm({
|
|
13965
14076
|
"src/mesh/mesh-events-coordinator.ts"() {
|
|
13966
14077
|
"use strict";
|
|
@@ -13988,6 +14099,7 @@ var init_mesh_events_coordinator = __esm({
|
|
|
13988
14099
|
idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
|
|
13989
14100
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
13990
14101
|
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
14102
|
+
DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
|
|
13991
14103
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
13992
14104
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
13993
14105
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -14043,12 +14155,9 @@ function resolveReconcileIntervalMs() {
|
|
|
14043
14155
|
return DEFAULT_RECONCILE_INTERVAL_MS;
|
|
14044
14156
|
}
|
|
14045
14157
|
function resolveCoordinatorDaemonIds(components) {
|
|
14046
|
-
const ids = /* @__PURE__ */ new Set();
|
|
14047
14158
|
const statusInstanceId = readNonEmptyString2(components.statusInstanceId);
|
|
14048
|
-
if (statusInstanceId) ids.add(statusInstanceId);
|
|
14049
14159
|
const machineId = readNonEmptyString2(loadConfig().machineId);
|
|
14050
|
-
|
|
14051
|
-
return [...ids];
|
|
14160
|
+
return expandDaemonIdForms([statusInstanceId, machineId]);
|
|
14052
14161
|
}
|
|
14053
14162
|
function daemonHostsMesh(mesh, daemonIds) {
|
|
14054
14163
|
const host = mesh.meshHost;
|
|
@@ -14132,6 +14241,24 @@ function recordHeldTerminalEventsToLedger(meshId, drainDaemonIds, reason, heldFo
|
|
|
14132
14241
|
}
|
|
14133
14242
|
}
|
|
14134
14243
|
}
|
|
14244
|
+
function recoverStrandedAssignedDispatches(meshId, store) {
|
|
14245
|
+
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
14246
|
+
if (!assigned.length) return;
|
|
14247
|
+
const nowMs = Date.now();
|
|
14248
|
+
for (const row of assigned) {
|
|
14249
|
+
const dispatchedAtMs = Date.parse(row.dispatchTimestamp ?? "");
|
|
14250
|
+
if (!Number.isFinite(dispatchedAtMs)) continue;
|
|
14251
|
+
if (nowMs - dispatchedAtMs < ASSIGNED_STRANDED_DEADLINE_MS) continue;
|
|
14252
|
+
if (store.taskHasConfirmedDelivery(meshId, row.id)) continue;
|
|
14253
|
+
const reclaimed = reclaimStrandedAssignedTask(meshId, row.id, {
|
|
14254
|
+
reason: "assigned_stranded_dispatch_unconfirmed",
|
|
14255
|
+
ageMs: nowMs - dispatchedAtMs
|
|
14256
|
+
});
|
|
14257
|
+
if (reclaimed) {
|
|
14258
|
+
LOG.warn("MeshReconcile", `Reclaimed stranded assigned task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}, dispatched ${Math.round((nowMs - dispatchedAtMs) / 1e3)}s ago, never confirmed delivered \u2192 ${reclaimed.status})`);
|
|
14259
|
+
}
|
|
14260
|
+
}
|
|
14261
|
+
}
|
|
14135
14262
|
async function runMeshReconcileTick(components) {
|
|
14136
14263
|
const localDaemonId = readNonEmptyString2(loadConfig().machineId) || void 0;
|
|
14137
14264
|
const drainDaemonIds = resolveCoordinatorDaemonIds(components);
|
|
@@ -14161,6 +14288,17 @@ async function runMeshReconcileTick(components) {
|
|
|
14161
14288
|
}
|
|
14162
14289
|
}
|
|
14163
14290
|
}
|
|
14291
|
+
if (store) {
|
|
14292
|
+
for (const mesh of listMeshes()) {
|
|
14293
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14294
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
14295
|
+
try {
|
|
14296
|
+
recoverStrandedAssignedDispatches(mesh.id, store);
|
|
14297
|
+
} catch (e) {
|
|
14298
|
+
LOG.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
14299
|
+
}
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14164
14302
|
for (const mesh of listMeshes()) {
|
|
14165
14303
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
14166
14304
|
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
@@ -14548,7 +14686,7 @@ function setupMeshReconcileLoop(components) {
|
|
|
14548
14686
|
}
|
|
14549
14687
|
};
|
|
14550
14688
|
}
|
|
14551
|
-
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, STRICT_SESSION_MATCH_TTL_MS;
|
|
14689
|
+
var DEFAULT_RECONCILE_INTERVAL_MS, DEFAULT_AUTO_PRUNE_MIN_AGE_MS, heldEventLedgerRecorded, ASSIGNED_STRANDED_DEADLINE_MS, STRICT_SESSION_MATCH_TTL_MS;
|
|
14552
14690
|
var init_mesh_reconcile_loop = __esm({
|
|
14553
14691
|
"src/mesh/mesh-reconcile-loop.ts"() {
|
|
14554
14692
|
"use strict";
|
|
@@ -14561,6 +14699,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14561
14699
|
init_mesh_events_coordinator();
|
|
14562
14700
|
init_mesh_unresolved_forward_outbox();
|
|
14563
14701
|
init_mesh_events_utils();
|
|
14702
|
+
init_dist();
|
|
14564
14703
|
init_mesh_work_queue();
|
|
14565
14704
|
init_mesh_ledger();
|
|
14566
14705
|
init_mesh_active_work();
|
|
@@ -14569,6 +14708,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
14569
14708
|
DEFAULT_RECONCILE_INTERVAL_MS = 4e3;
|
|
14570
14709
|
DEFAULT_AUTO_PRUNE_MIN_AGE_MS = 24 * 60 * 6e4;
|
|
14571
14710
|
heldEventLedgerRecorded = /* @__PURE__ */ new Set();
|
|
14711
|
+
ASSIGNED_STRANDED_DEADLINE_MS = 5 * 6e4;
|
|
14572
14712
|
STRICT_SESSION_MATCH_TTL_MS = 6e4;
|
|
14573
14713
|
}
|
|
14574
14714
|
});
|
|
@@ -14601,6 +14741,84 @@ var init_mesh_events = __esm({
|
|
|
14601
14741
|
}
|
|
14602
14742
|
});
|
|
14603
14743
|
|
|
14744
|
+
// src/providers/approval-utils.ts
|
|
14745
|
+
function normalizeApprovalLabel(value) {
|
|
14746
|
+
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
14747
|
+
}
|
|
14748
|
+
function isNegativeApprovalLabel(value) {
|
|
14749
|
+
const label = normalizeApprovalLabel(value);
|
|
14750
|
+
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
14751
|
+
}
|
|
14752
|
+
function hasNegativeApprovalOption(buttons) {
|
|
14753
|
+
return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
|
|
14754
|
+
}
|
|
14755
|
+
function getApprovalPositiveHints(provider) {
|
|
14756
|
+
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
14757
|
+
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14758
|
+
}
|
|
14759
|
+
function pickApprovalButton(buttons, provider) {
|
|
14760
|
+
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
14761
|
+
if (labels.length === 0) {
|
|
14762
|
+
return { index: -1, label: "" };
|
|
14763
|
+
}
|
|
14764
|
+
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
14765
|
+
const hints = getApprovalPositiveHints(provider);
|
|
14766
|
+
for (const hint of hints) {
|
|
14767
|
+
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
14768
|
+
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
14769
|
+
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14770
|
+
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
14771
|
+
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
14772
|
+
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
14773
|
+
}
|
|
14774
|
+
return { index: -1, label: "" };
|
|
14775
|
+
}
|
|
14776
|
+
function pickAutoApprovalButton(buttons) {
|
|
14777
|
+
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
14778
|
+
const index = labels.findIndex(Boolean);
|
|
14779
|
+
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
14780
|
+
}
|
|
14781
|
+
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
14782
|
+
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
14783
|
+
const cleanMessage = String(modalMessage || "").trim();
|
|
14784
|
+
if (cleanMessage) lines.push(cleanMessage);
|
|
14785
|
+
return lines.join("\n");
|
|
14786
|
+
}
|
|
14787
|
+
function looksLikeActiveApprovalPromptText(content) {
|
|
14788
|
+
const text = content.trim();
|
|
14789
|
+
if (!text || text.length > 2e3) return false;
|
|
14790
|
+
const hasApprovalQuestion = /do you want to (?:proceed|allow|run|make this edit|create)/i.test(text) || /this command requires approval/i.test(text) || /quick safety check/i.test(text) || /is this a project you trust/i.test(text);
|
|
14791
|
+
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
14792
|
+
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
14793
|
+
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
14794
|
+
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
14795
|
+
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
14796
|
+
if (hasDontAskAgain && hasNoOption) return true;
|
|
14797
|
+
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
14798
|
+
return false;
|
|
14799
|
+
}
|
|
14800
|
+
var DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
14801
|
+
var init_approval_utils = __esm({
|
|
14802
|
+
"src/providers/approval-utils.ts"() {
|
|
14803
|
+
"use strict";
|
|
14804
|
+
DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
14805
|
+
"yes",
|
|
14806
|
+
"allow once",
|
|
14807
|
+
"approve",
|
|
14808
|
+
"accept",
|
|
14809
|
+
"continue",
|
|
14810
|
+
"run",
|
|
14811
|
+
"proceed",
|
|
14812
|
+
"confirm",
|
|
14813
|
+
"save",
|
|
14814
|
+
"ok",
|
|
14815
|
+
"trust",
|
|
14816
|
+
"allow",
|
|
14817
|
+
"always allow"
|
|
14818
|
+
];
|
|
14819
|
+
}
|
|
14820
|
+
});
|
|
14821
|
+
|
|
14604
14822
|
// src/logging/debug-config.ts
|
|
14605
14823
|
function normalizeCategories(categories) {
|
|
14606
14824
|
if (!Array.isArray(categories)) return [];
|
|
@@ -15587,6 +15805,27 @@ function compileSettledPromptMatchers(spec) {
|
|
|
15587
15805
|
});
|
|
15588
15806
|
return { prompt, footers };
|
|
15589
15807
|
}
|
|
15808
|
+
function extractButtonLabels(spec, text) {
|
|
15809
|
+
if (!text) return [];
|
|
15810
|
+
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
15811
|
+
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
15812
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
15813
|
+
const out = [];
|
|
15814
|
+
for (const line of text.split("\n")) {
|
|
15815
|
+
buttonRe.lastIndex = 0;
|
|
15816
|
+
const m = buttonRe.exec(line);
|
|
15817
|
+
if (!m) continue;
|
|
15818
|
+
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
15819
|
+
if (captured && captured.trim()) out.push(captured.trim());
|
|
15820
|
+
}
|
|
15821
|
+
return out;
|
|
15822
|
+
}
|
|
15823
|
+
function buttonBlockApprovalCue(spec, text) {
|
|
15824
|
+
const labels = extractButtonLabels(spec, text);
|
|
15825
|
+
if (labels.length < 2) return false;
|
|
15826
|
+
if (pickApprovalButton(labels).index < 0) return false;
|
|
15827
|
+
return hasNegativeApprovalOption(labels);
|
|
15828
|
+
}
|
|
15590
15829
|
function modalMatches(spec, input) {
|
|
15591
15830
|
const text = input.screenText ?? "";
|
|
15592
15831
|
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
@@ -15595,6 +15834,7 @@ function modalMatches(spec, input) {
|
|
|
15595
15834
|
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
15596
15835
|
if (re.test(text)) return true;
|
|
15597
15836
|
}
|
|
15837
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
15598
15838
|
return false;
|
|
15599
15839
|
}
|
|
15600
15840
|
function evaluateGroup(group, spec, input, compiled) {
|
|
@@ -15649,6 +15889,7 @@ var init_detect_status = __esm({
|
|
|
15649
15889
|
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
15650
15890
|
"use strict";
|
|
15651
15891
|
init_visible_region();
|
|
15892
|
+
init_approval_utils();
|
|
15652
15893
|
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15653
15894
|
}
|
|
15654
15895
|
});
|
|
@@ -25746,76 +25987,8 @@ function validateReadChatResultPayload(raw, source = "read_chat") {
|
|
|
25746
25987
|
return normalized;
|
|
25747
25988
|
}
|
|
25748
25989
|
|
|
25749
|
-
// src/providers/approval-utils.ts
|
|
25750
|
-
var DEFAULT_APPROVAL_POSITIVE_HINTS = [
|
|
25751
|
-
"yes",
|
|
25752
|
-
"allow once",
|
|
25753
|
-
"approve",
|
|
25754
|
-
"accept",
|
|
25755
|
-
"continue",
|
|
25756
|
-
"run",
|
|
25757
|
-
"proceed",
|
|
25758
|
-
"confirm",
|
|
25759
|
-
"save",
|
|
25760
|
-
"ok",
|
|
25761
|
-
"trust",
|
|
25762
|
-
"allow",
|
|
25763
|
-
"always allow"
|
|
25764
|
-
];
|
|
25765
|
-
function normalizeApprovalLabel(value) {
|
|
25766
|
-
return String(value || "").toLowerCase().replace(/^[\s\[(<{]*\d+(?:\s*[.)\]}>:-]|\s)+/, "").replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
25767
|
-
}
|
|
25768
|
-
function isNegativeApprovalLabel(value) {
|
|
25769
|
-
const label = normalizeApprovalLabel(value);
|
|
25770
|
-
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label) || /\bwithout\b/.test(label) || /\bdo not\b/.test(label);
|
|
25771
|
-
}
|
|
25772
|
-
function getApprovalPositiveHints(provider) {
|
|
25773
|
-
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
25774
|
-
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
25775
|
-
}
|
|
25776
|
-
function pickApprovalButton(buttons, provider) {
|
|
25777
|
-
const labels = (buttons || []).map((button) => String(button || "").trim()).filter(Boolean);
|
|
25778
|
-
if (labels.length === 0) {
|
|
25779
|
-
return { index: -1, label: "" };
|
|
25780
|
-
}
|
|
25781
|
-
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
25782
|
-
const hints = getApprovalPositiveHints(provider);
|
|
25783
|
-
for (const hint of hints) {
|
|
25784
|
-
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
25785
|
-
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
25786
|
-
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
25787
|
-
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
25788
|
-
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
25789
|
-
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
25790
|
-
}
|
|
25791
|
-
return { index: -1, label: "" };
|
|
25792
|
-
}
|
|
25793
|
-
function pickAutoApprovalButton(buttons) {
|
|
25794
|
-
const labels = (buttons || []).map((button) => String(button || "").trim());
|
|
25795
|
-
const index = labels.findIndex(Boolean);
|
|
25796
|
-
return index >= 0 ? { index, label: labels[index] } : { index: -1, label: "" };
|
|
25797
|
-
}
|
|
25798
|
-
function formatAutoApprovalMessage(modalMessage, buttonLabel) {
|
|
25799
|
-
const lines = [`Auto-approved${buttonLabel ? `: ${buttonLabel}` : ""}`];
|
|
25800
|
-
const cleanMessage = String(modalMessage || "").trim();
|
|
25801
|
-
if (cleanMessage) lines.push(cleanMessage);
|
|
25802
|
-
return lines.join("\n");
|
|
25803
|
-
}
|
|
25804
|
-
function looksLikeActiveApprovalPromptText(content) {
|
|
25805
|
-
const text = content.trim();
|
|
25806
|
-
if (!text || text.length > 2e3) return false;
|
|
25807
|
-
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);
|
|
25808
|
-
const hasNumberedChoices = /^\s*[❯›>]?\s*1[.)]\s+(?:yes|allow|proceed|run)/im.test(text) || /^\s*1[.)]\s+yes\b/im.test(text);
|
|
25809
|
-
if (hasApprovalQuestion && hasNumberedChoices) return true;
|
|
25810
|
-
const lastLines = text.split(/\r?\n/).slice(-12).join("\n");
|
|
25811
|
-
const hasDontAskAgain = /yes.*don'?t ask again/i.test(lastLines) || /yes.*always allow/i.test(lastLines);
|
|
25812
|
-
const hasNoOption = /^\s*[❯›>]?\s*\d+[.)]\s+no\b/im.test(lastLines);
|
|
25813
|
-
if (hasDontAskAgain && hasNoOption) return true;
|
|
25814
|
-
if (/what do you want to do\?/i.test(text) && /^\s*\d+[.)]\s+\S/m.test(text)) return true;
|
|
25815
|
-
return false;
|
|
25816
|
-
}
|
|
25817
|
-
|
|
25818
25990
|
// src/providers/ide-provider-instance.ts
|
|
25991
|
+
init_approval_utils();
|
|
25819
25992
|
init_provider_patch_state();
|
|
25820
25993
|
init_chat_message_normalization();
|
|
25821
25994
|
init_open_panel_support();
|
|
@@ -26899,6 +27072,7 @@ import * as fs7 from "fs";
|
|
|
26899
27072
|
import * as os10 from "os";
|
|
26900
27073
|
import * as path16 from "path";
|
|
26901
27074
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
27075
|
+
init_approval_utils();
|
|
26902
27076
|
init_coordinator_registry();
|
|
26903
27077
|
init_logger();
|
|
26904
27078
|
|
|
@@ -34887,6 +35061,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
34887
35061
|
// src/providers/cli-provider-instance.ts
|
|
34888
35062
|
init_logger();
|
|
34889
35063
|
init_control_effects();
|
|
35064
|
+
init_approval_utils();
|
|
34890
35065
|
init_provider_patch_state();
|
|
34891
35066
|
|
|
34892
35067
|
// src/providers/provider-session-id.ts
|
|
@@ -35148,6 +35323,20 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35148
35323
|
* keystroke until the modal *content* has settled.
|
|
35149
35324
|
*/
|
|
35150
35325
|
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
35326
|
+
/**
|
|
35327
|
+
* Busy-side hysteresis for the settle gate. A momentary `generating` flip
|
|
35328
|
+
* while the SAME approval modal's button block is still on screen (its
|
|
35329
|
+
* question line scrolled out of the captured frame, only the buttons + a
|
|
35330
|
+
* residual `esc to interrupt` spinner remain) briefly reports
|
|
35331
|
+
* status!=waiting_approval. Without hysteresis that flip wipes the settle
|
|
35332
|
+
* clock, and the modal→generating→modal flap restarts the 600ms window
|
|
35333
|
+
* every time so auto-approve never fires. We keep the in-progress settle
|
|
35334
|
+
* gate warm across an inactive blip up to this bound; only once the modal
|
|
35335
|
+
* has genuinely stayed gone this long (a real resolution → idle) is the
|
|
35336
|
+
* gate cleared. Bounded so a genuinely new, later approval still re-settles
|
|
35337
|
+
* from scratch rather than firing on a stale timestamp.
|
|
35338
|
+
*/
|
|
35339
|
+
static AUTO_APPROVE_GATE_HYSTERESIS_MS = 1500;
|
|
35151
35340
|
adapter;
|
|
35152
35341
|
context = null;
|
|
35153
35342
|
events = [];
|
|
@@ -35169,6 +35358,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35169
35358
|
pendingAutoApprovalSignature = "";
|
|
35170
35359
|
pendingAutoApprovalSince = 0;
|
|
35171
35360
|
autoApproveSettleTimer = null;
|
|
35361
|
+
// Wall-clock when auto-approve first observed status!=waiting_approval while
|
|
35362
|
+
// a settle gate was in progress. Drives AUTO_APPROVE_GATE_HYSTERESIS_MS so a
|
|
35363
|
+
// brief generating flip does not immediately wipe the settle clock.
|
|
35364
|
+
autoApproveInactiveSince = 0;
|
|
35172
35365
|
controlValues = {};
|
|
35173
35366
|
summaryMetadata = void 0;
|
|
35174
35367
|
appliedEffectKeys = /* @__PURE__ */ new Set();
|
|
@@ -35989,14 +36182,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
35989
36182
|
const autoApproveActive = adapterStatus?.status === "waiting_approval" && this.shouldAutoApprove();
|
|
35990
36183
|
if (!autoApproveActive) {
|
|
35991
36184
|
this.lastAutoApprovalSignature = "";
|
|
36185
|
+
if (this.pendingAutoApprovalSince) {
|
|
36186
|
+
if (!this.autoApproveInactiveSince) this.autoApproveInactiveSince = now;
|
|
36187
|
+
const goneForMs = now - this.autoApproveInactiveSince;
|
|
36188
|
+
if (goneForMs < _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS) {
|
|
36189
|
+
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
36190
|
+
this.autoApproveSettleTimer = setTimeout(() => {
|
|
36191
|
+
this.autoApproveSettleTimer = null;
|
|
36192
|
+
this.recheckAutoApproveSettled();
|
|
36193
|
+
}, _CliProviderInstance.AUTO_APPROVE_GATE_HYSTERESIS_MS - goneForMs + 20);
|
|
36194
|
+
return autoApproveActive;
|
|
36195
|
+
}
|
|
36196
|
+
}
|
|
35992
36197
|
this.pendingAutoApprovalSignature = "";
|
|
35993
36198
|
this.pendingAutoApprovalSince = 0;
|
|
36199
|
+
this.autoApproveInactiveSince = 0;
|
|
35994
36200
|
if (this.autoApproveSettleTimer) {
|
|
35995
36201
|
clearTimeout(this.autoApproveSettleTimer);
|
|
35996
36202
|
this.autoApproveSettleTimer = null;
|
|
35997
36203
|
}
|
|
35998
36204
|
return autoApproveActive;
|
|
35999
36205
|
}
|
|
36206
|
+
this.autoApproveInactiveSince = 0;
|
|
36000
36207
|
const modal = adapterStatus.activeModal;
|
|
36001
36208
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
36002
36209
|
if (!modal || buttons.length === 0) {
|
|
@@ -36006,18 +36213,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36006
36213
|
if (buttonIndex < 0) {
|
|
36007
36214
|
return autoApproveActive;
|
|
36008
36215
|
}
|
|
36009
|
-
const
|
|
36010
|
-
const signature = [
|
|
36011
|
-
approvalEntrySeq,
|
|
36216
|
+
const modalSignature = [
|
|
36012
36217
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
36013
36218
|
buttons.join("|"),
|
|
36014
36219
|
buttonIndex
|
|
36015
36220
|
].join("::");
|
|
36016
|
-
|
|
36221
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
36222
|
+
const busySignature = `${approvalEntrySeq}::${modalSignature}`;
|
|
36223
|
+
if (this.autoApproveBusy && busySignature === this.lastAutoApprovalSignature) {
|
|
36017
36224
|
return autoApproveActive;
|
|
36018
36225
|
}
|
|
36019
|
-
if (
|
|
36020
|
-
this.pendingAutoApprovalSignature =
|
|
36226
|
+
if (modalSignature !== this.pendingAutoApprovalSignature) {
|
|
36227
|
+
this.pendingAutoApprovalSignature = modalSignature;
|
|
36021
36228
|
this.pendingAutoApprovalSince = now;
|
|
36022
36229
|
}
|
|
36023
36230
|
const settledForMs = now - this.pendingAutoApprovalSince;
|
|
@@ -36034,9 +36241,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36034
36241
|
this.autoApproveSettleTimer = null;
|
|
36035
36242
|
}
|
|
36036
36243
|
this.autoApproveBusy = true;
|
|
36037
|
-
this.lastAutoApprovalSignature =
|
|
36244
|
+
this.lastAutoApprovalSignature = busySignature;
|
|
36038
36245
|
this.pendingAutoApprovalSignature = "";
|
|
36039
36246
|
this.pendingAutoApprovalSince = 0;
|
|
36247
|
+
this.autoApproveInactiveSince = 0;
|
|
36040
36248
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
36041
36249
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
36042
36250
|
this.autoApproveBusy = false;
|
|
@@ -42928,7 +43136,8 @@ init_logger();
|
|
|
42928
43136
|
import * as fs23 from "fs";
|
|
42929
43137
|
import * as path35 from "path";
|
|
42930
43138
|
import * as os26 from "os";
|
|
42931
|
-
var
|
|
43139
|
+
var ADHDEV_HOME2 = process.env.ADHDEV_CONFIG_DIR && process.env.ADHDEV_CONFIG_DIR.trim() ? process.env.ADHDEV_CONFIG_DIR.trim() : path35.join(os26.homedir(), ".adhdev");
|
|
43140
|
+
var LOG_DIR2 = path35.join(ADHDEV_HOME2, "logs");
|
|
42932
43141
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
42933
43142
|
var MAX_DAYS = 7;
|
|
42934
43143
|
try {
|
|
@@ -43783,13 +43992,14 @@ async function waitForPidExit(pid, timeoutMs) {
|
|
|
43783
43992
|
}
|
|
43784
43993
|
}
|
|
43785
43994
|
}
|
|
43786
|
-
function stopSessionHostProcesses(appName) {
|
|
43995
|
+
async function stopSessionHostProcesses(appName) {
|
|
43787
43996
|
const pidFile = path36.join(os27.homedir(), ".adhdev", `${appName}-session-host.pid`);
|
|
43997
|
+
let killedPid = null;
|
|
43788
43998
|
try {
|
|
43789
43999
|
if (fs25.existsSync(pidFile)) {
|
|
43790
44000
|
const pid = Number.parseInt(fs25.readFileSync(pidFile, "utf8").trim(), 10);
|
|
43791
44001
|
if (Number.isFinite(pid) && pid !== process.pid && isManagedSessionHostPid(pid)) {
|
|
43792
|
-
killPid(pid);
|
|
44002
|
+
if (killPid(pid)) killedPid = pid;
|
|
43793
44003
|
}
|
|
43794
44004
|
}
|
|
43795
44005
|
} catch {
|
|
@@ -43799,6 +44009,15 @@ function stopSessionHostProcesses(appName) {
|
|
|
43799
44009
|
} catch {
|
|
43800
44010
|
}
|
|
43801
44011
|
}
|
|
44012
|
+
if (killedPid !== null) {
|
|
44013
|
+
await waitForPidExit(killedPid, 15e3);
|
|
44014
|
+
}
|
|
44015
|
+
}
|
|
44016
|
+
function isRetriableInstallLockError(error) {
|
|
44017
|
+
const code = error?.code;
|
|
44018
|
+
if (code === "EBUSY" || code === "EPERM") return true;
|
|
44019
|
+
const text = `${error?.message || ""} ${error?.stderr || ""}`;
|
|
44020
|
+
return /\bEBUSY\b|\bEPERM\b|resource busy or locked/i.test(text);
|
|
43802
44021
|
}
|
|
43803
44022
|
function removeDaemonPidFile() {
|
|
43804
44023
|
const pidFile = path36.join(os27.homedir(), ".adhdev", "daemon.pid");
|
|
@@ -43878,22 +44097,37 @@ async function runDaemonUpgradeHelper(payload) {
|
|
|
43878
44097
|
appendUpgradeLog(`Waiting for parent pid ${payload.parentPid} to exit`);
|
|
43879
44098
|
await waitForPidExit(payload.parentPid, 15e3);
|
|
43880
44099
|
}
|
|
43881
|
-
stopSessionHostProcesses(sessionHostAppName);
|
|
44100
|
+
await stopSessionHostProcesses(sessionHostAppName);
|
|
43882
44101
|
removeDaemonPidFile();
|
|
43883
44102
|
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
43884
44103
|
const spec = `${payload.packageName}@${payload.targetVersion || "latest"}`;
|
|
43885
44104
|
appendUpgradeLog(`Installing ${spec}`);
|
|
43886
|
-
const
|
|
43887
|
-
|
|
43888
|
-
|
|
43889
|
-
{
|
|
43890
|
-
|
|
43891
|
-
|
|
43892
|
-
|
|
43893
|
-
|
|
43894
|
-
|
|
44105
|
+
const maxInstallAttempts = process.platform === "win32" ? 3 : 1;
|
|
44106
|
+
let installOutput = "";
|
|
44107
|
+
for (let attempt = 1; attempt <= maxInstallAttempts; attempt++) {
|
|
44108
|
+
try {
|
|
44109
|
+
installOutput = String(execFileSync5(
|
|
44110
|
+
installCommand.command,
|
|
44111
|
+
installCommand.args,
|
|
44112
|
+
{
|
|
44113
|
+
encoding: "utf8",
|
|
44114
|
+
stdio: "pipe",
|
|
44115
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
44116
|
+
env: buildInstallEnvWithNodeOnPath(),
|
|
44117
|
+
...installCommand.execOptions
|
|
44118
|
+
}
|
|
44119
|
+
));
|
|
44120
|
+
break;
|
|
44121
|
+
} catch (error) {
|
|
44122
|
+
if (attempt < maxInstallAttempts && isRetriableInstallLockError(error)) {
|
|
44123
|
+
appendUpgradeLog(`Install attempt ${attempt} hit a file lock (${error?.code || "lock"}); cleaning staging and retrying after backoff`);
|
|
44124
|
+
cleanupStaleGlobalInstallDirs(payload.packageName, installCommand.surface);
|
|
44125
|
+
await new Promise((resolve24) => setTimeout(resolve24, attempt * 1500));
|
|
44126
|
+
continue;
|
|
44127
|
+
}
|
|
44128
|
+
throw error;
|
|
43895
44129
|
}
|
|
43896
|
-
|
|
44130
|
+
}
|
|
43897
44131
|
if (installOutput.trim()) {
|
|
43898
44132
|
appendUpgradeLog(installOutput.trim());
|
|
43899
44133
|
}
|
|
@@ -52655,6 +52889,7 @@ var DaemonAgentStreamManager = class {
|
|
|
52655
52889
|
|
|
52656
52890
|
// src/agent-stream/poller.ts
|
|
52657
52891
|
init_logger();
|
|
52892
|
+
init_approval_utils();
|
|
52658
52893
|
init_chat_message_normalization();
|
|
52659
52894
|
var AgentStreamPoller = class {
|
|
52660
52895
|
deps;
|