@adhdev/daemon-core 0.9.82-rc.566 → 0.9.82-rc.568
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/cli-adapter-types.d.ts +14 -0
- package/dist/index.js +194 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +194 -32
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-active-work.d.ts +4 -0
- package/dist/providers/cli-provider-instance.d.ts +27 -0
- package/dist/providers/spec/cli-adapter.d.ts +1 -0
- package/dist/providers/spec/fsm-driver.d.ts +18 -0
- package/package.json +3 -3
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +17 -0
- package/src/commands/chat-commands-write.ts +15 -1
- package/src/mesh/mesh-active-work.ts +22 -4
- package/src/mesh/mesh-event-forwarding.ts +41 -23
- package/src/mesh/mesh-reconcile-loop.ts +52 -0
- package/src/providers/cli-provider-instance.ts +133 -14
- package/src/providers/spec/cli-adapter.ts +37 -3
- package/src/providers/spec/fsm-driver.ts +27 -5
|
@@ -13,6 +13,19 @@ export interface CliAdapterStatus {
|
|
|
13
13
|
activeModal?: {
|
|
14
14
|
message: string;
|
|
15
15
|
buttons: string[];
|
|
16
|
+
/**
|
|
17
|
+
* BUTTON-INDEX-MISMAP (Fix C.1): each button's label paired with its real FSM
|
|
18
|
+
* DISPLAYED index (evaluator's Number(m[1])). `buttons` above is the label-only list
|
|
19
|
+
* every existing consumer reads (array position === pick order); `buttonMeta` preserves
|
|
20
|
+
* the index → label mapping so a partial / non-contiguous modal (display indices [1,3,4]
|
|
21
|
+
* at array positions [0,1,2]) does not lose its true indices once the modal leaves the
|
|
22
|
+
* adapter. Present only on spec/FSM adapters; absent for adapters that surface labels
|
|
23
|
+
* alone.
|
|
24
|
+
*/
|
|
25
|
+
buttonMeta?: {
|
|
26
|
+
index: number;
|
|
27
|
+
label: string;
|
|
28
|
+
}[];
|
|
16
29
|
/**
|
|
17
30
|
* Semantic modal class, when the adapter knows it (spec/FSM path):
|
|
18
31
|
* 'approval' = tool/command/trust consent (auto-approve may fire);
|
|
@@ -155,6 +168,7 @@ export interface CliAdapter {
|
|
|
155
168
|
resolveAction?(data: unknown): Promise<void>;
|
|
156
169
|
setInteractivePromptResponse?(response: InteractivePromptResponse): Promise<void>;
|
|
157
170
|
resolveModal?(buttonIndex: number): void;
|
|
171
|
+
resolveModalMatched?(buttonIndex: number): boolean;
|
|
158
172
|
isApprovalRecentlyResolved?(): boolean;
|
|
159
173
|
setOnPtyData?(callback: (data: string) => void): void;
|
|
160
174
|
writeRaw?(data: string): void;
|
package/dist/index.js
CHANGED
|
@@ -428,10 +428,10 @@ function readInjected(value) {
|
|
|
428
428
|
}
|
|
429
429
|
function getDaemonBuildInfo() {
|
|
430
430
|
if (cached) return cached;
|
|
431
|
-
const commit = readInjected(true ? "
|
|
432
|
-
const commitShort = readInjected(true ? "
|
|
433
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
434
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
431
|
+
const commit = readInjected(true ? "39e0687342226d334e36c982bd8988802f43dd3e" : void 0) ?? "unknown";
|
|
432
|
+
const commitShort = readInjected(true ? "39e06873" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
433
|
+
const version = readInjected(true ? "0.9.82-rc.568" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
434
|
+
const builtAt = readInjected(true ? "2026-07-18T14:18:06.483Z" : void 0);
|
|
435
435
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
436
436
|
return cached;
|
|
437
437
|
}
|
|
@@ -14974,12 +14974,16 @@ function buildMeshActiveWork(opts) {
|
|
|
14974
14974
|
for (const task of opts.queue || []) {
|
|
14975
14975
|
if (task.status !== "pending" && task.status !== "assigned") continue;
|
|
14976
14976
|
const { title, summary: summary2 } = summarizeMessage(task.message || "");
|
|
14977
|
+
const queueNodeId = task.assignedNodeId || task.targetNodeId;
|
|
14978
|
+
const queueSessionId = task.assignedSessionId || task.targetSessionId;
|
|
14979
|
+
const queueLive = task.status === "assigned" ? sessionStatusFromNodes(opts.nodes, queueNodeId ?? void 0, queueSessionId ?? void 0) : {};
|
|
14980
|
+
const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "generating" ? queueLive.status : task.status;
|
|
14977
14981
|
records.push({
|
|
14978
14982
|
taskId: task.id,
|
|
14979
14983
|
source: "queue",
|
|
14980
|
-
status:
|
|
14981
|
-
nodeId:
|
|
14982
|
-
sessionId:
|
|
14984
|
+
status: queueStatus,
|
|
14985
|
+
nodeId: queueNodeId,
|
|
14986
|
+
sessionId: queueSessionId,
|
|
14983
14987
|
taskTitle: title,
|
|
14984
14988
|
taskSummary: summary2,
|
|
14985
14989
|
message: task.message,
|
|
@@ -20562,12 +20566,15 @@ function evaluateMeshEventSuppression(args, ctx) {
|
|
|
20562
20566
|
}
|
|
20563
20567
|
return null;
|
|
20564
20568
|
}
|
|
20565
|
-
function
|
|
20569
|
+
function shouldSuppressAutoApprovingWorkerApproval(components, sessionId) {
|
|
20566
20570
|
if (!sessionId) return false;
|
|
20567
20571
|
try {
|
|
20568
|
-
const
|
|
20572
|
+
const instance = components.instanceManager?.getInstance?.(sessionId);
|
|
20573
|
+
const state = instance?.getState?.();
|
|
20569
20574
|
const settings = state?.settings || {};
|
|
20570
|
-
|
|
20575
|
+
if (settings.autoApprove !== true) return false;
|
|
20576
|
+
const resolvedLocally = instance?.approvalRecentlyResolvedLocally;
|
|
20577
|
+
return typeof resolvedLocally === "function" ? resolvedLocally.call(instance) === true : false;
|
|
20571
20578
|
} catch {
|
|
20572
20579
|
return false;
|
|
20573
20580
|
}
|
|
@@ -20712,8 +20719,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
20712
20719
|
}
|
|
20713
20720
|
}
|
|
20714
20721
|
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
20715
|
-
if (args.event === "agent:waiting_approval" &&
|
|
20716
|
-
LOG.info("MeshEvents", `Suppressed agent:waiting_approval for auto-approving worker session ${eventSessionId || "(unknown)"} (mesh ${args.meshId}) \u2014 modal
|
|
20722
|
+
if (args.event === "agent:waiting_approval" && shouldSuppressAutoApprovingWorkerApproval(components, eventSessionId)) {
|
|
20723
|
+
LOG.info("MeshEvents", `Suppressed agent:waiting_approval for auto-approving worker session ${eventSessionId || "(unknown)"} (mesh ${args.meshId}) \u2014 modal resolved locally within cooldown, coordinator not notified`);
|
|
20717
20724
|
traceMeshEventDrop("waiting_approval_auto_approving_worker", traceCtx);
|
|
20718
20725
|
return { success: true, forwarded: 0, suppressed: true, autoApprovingWorkerApproval: true };
|
|
20719
20726
|
}
|
|
@@ -22412,6 +22419,14 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
|
|
|
22412
22419
|
}
|
|
22413
22420
|
return delivered;
|
|
22414
22421
|
}
|
|
22422
|
+
function assignedRowLiveStatusIsAwaitingApproval(mesh, nodeId, sessionId) {
|
|
22423
|
+
if (!nodeId || !sessionId) return false;
|
|
22424
|
+
try {
|
|
22425
|
+
return sessionStatusFromNodes(mesh.nodes, nodeId, sessionId).status === "awaiting_approval";
|
|
22426
|
+
} catch {
|
|
22427
|
+
return false;
|
|
22428
|
+
}
|
|
22429
|
+
}
|
|
22415
22430
|
async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
22416
22431
|
const meshId = mesh.id;
|
|
22417
22432
|
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
@@ -22443,6 +22458,15 @@ async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
|
22443
22458
|
} else {
|
|
22444
22459
|
if (verdict === "IDLE_CONFIRMED") {
|
|
22445
22460
|
deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
|
|
22461
|
+
} else if (assignedRowLiveStatusIsAwaitingApproval(mesh, row.assignedNodeId, row.assignedSessionId)) {
|
|
22462
|
+
traceMeshEventDrop("short_redrive_deferred_awaiting_approval", {
|
|
22463
|
+
taskId: row.id,
|
|
22464
|
+
sessionId: row.assignedSessionId,
|
|
22465
|
+
nodeId: row.assignedNodeId,
|
|
22466
|
+
meshId,
|
|
22467
|
+
event: "agent:waiting_approval"
|
|
22468
|
+
}, "live_awaiting_approval");
|
|
22469
|
+
continue;
|
|
22446
22470
|
} else {
|
|
22447
22471
|
const streak = (deliveredUnconsumedUnknownStreak.get(shortStreakKey) ?? 0) + 1;
|
|
22448
22472
|
deliveredUnconsumedUnknownStreak.set(shortStreakKey, streak);
|
|
@@ -22505,6 +22529,15 @@ async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
|
22505
22529
|
if (verdict === "IDLE_CONFIRMED") {
|
|
22506
22530
|
deliveredNoTurnUnknownStreak.delete(streakKey);
|
|
22507
22531
|
reclaimReason = "delivered_no_turn_deadline";
|
|
22532
|
+
} else if (assignedRowLiveStatusIsAwaitingApproval(mesh, row.assignedNodeId, row.assignedSessionId)) {
|
|
22533
|
+
traceMeshEventDrop("reclaim_deferred_awaiting_approval", {
|
|
22534
|
+
taskId: row.id,
|
|
22535
|
+
sessionId: row.assignedSessionId,
|
|
22536
|
+
nodeId: row.assignedNodeId,
|
|
22537
|
+
meshId,
|
|
22538
|
+
event: "agent:waiting_approval"
|
|
22539
|
+
}, "live_awaiting_approval");
|
|
22540
|
+
continue;
|
|
22508
22541
|
} else {
|
|
22509
22542
|
const streak = (deliveredNoTurnUnknownStreak.get(streakKey) ?? 0) + 1;
|
|
22510
22543
|
deliveredNoTurnUnknownStreak.set(streakKey, streak);
|
|
@@ -23072,6 +23105,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
23072
23105
|
init_mesh_reconcile_config();
|
|
23073
23106
|
init_mesh_remote_event_pull();
|
|
23074
23107
|
init_mesh_completion_synthesis();
|
|
23108
|
+
init_mesh_active_work();
|
|
23075
23109
|
init_mesh_reconcile_v2_backstop();
|
|
23076
23110
|
init_mesh_reconcile_acked_hold();
|
|
23077
23111
|
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
@@ -39441,7 +39475,13 @@ async function handleResolveAction(h, args) {
|
|
|
39441
39475
|
LOG.info("Command", `[resolveAction] CLI PTY \u2192 stale_prompt (already resolved within cooldown)`);
|
|
39442
39476
|
return { success: true, stalePrompt: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
39443
39477
|
}
|
|
39444
|
-
if (typeof adapter.
|
|
39478
|
+
if (typeof adapter.resolveModalMatched === "function") {
|
|
39479
|
+
const matched = adapter.resolveModalMatched(buttonIndex);
|
|
39480
|
+
if (!matched) {
|
|
39481
|
+
LOG.warn("Command", `[resolveAction] CLI PTY \u2192 no button matched for buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}" (modal not resolved)`);
|
|
39482
|
+
return { success: false, error: "Approval button index did not map to a visible modal button", buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
39483
|
+
}
|
|
39484
|
+
} else if (typeof adapter.resolveModal === "function") {
|
|
39445
39485
|
adapter.resolveModal(buttonIndex);
|
|
39446
39486
|
} else {
|
|
39447
39487
|
const keys = "\x1B[B".repeat(Math.max(0, buttonIndex)) + "\r";
|
|
@@ -44034,7 +44074,7 @@ var FsmDriver = class {
|
|
|
44034
44074
|
this.handleClickControl(cmd.control_id, cmd.payload);
|
|
44035
44075
|
return;
|
|
44036
44076
|
case "click_modal_button":
|
|
44037
|
-
this.
|
|
44077
|
+
this.clickModalButton(cmd.index);
|
|
44038
44078
|
return;
|
|
44039
44079
|
case "attach_image":
|
|
44040
44080
|
this.handleAttachImage(cmd.blob, cmd.mime);
|
|
@@ -44777,11 +44817,24 @@ var FsmDriver = class {
|
|
|
44777
44817
|
}
|
|
44778
44818
|
}
|
|
44779
44819
|
}
|
|
44820
|
+
/**
|
|
44821
|
+
* BUTTON-INDEX-MISMAP (Fix C.3): public modal-click entry that returns whether a button
|
|
44822
|
+
* matching the requested FSM display index was actually found and its confirm keys were
|
|
44823
|
+
* dispatched. The old private handleClickModalButton silently `return`ed on a miss (no
|
|
44824
|
+
* modal captured, or no button whose `.index` equals the requested display index), so a
|
|
44825
|
+
* mis-mapped index looked identical to a successful press. Callers that need to know
|
|
44826
|
+
* whether the click landed (mesh_approve → resolveModal) can now observe the miss instead
|
|
44827
|
+
* of reporting success into the void. The generic `dispatch('click_modal_button')` path
|
|
44828
|
+
* keeps ignoring the return (fire-and-forget UI clicks).
|
|
44829
|
+
*/
|
|
44830
|
+
clickModalButton(index) {
|
|
44831
|
+
return this.handleClickModalButton(index);
|
|
44832
|
+
}
|
|
44780
44833
|
handleClickModalButton(index) {
|
|
44781
44834
|
const m = this.currentEval?.modal;
|
|
44782
|
-
if (!m) return;
|
|
44835
|
+
if (!m) return false;
|
|
44783
44836
|
const btn = m.buttons.find((b) => b.index === index);
|
|
44784
|
-
if (!btn) return;
|
|
44837
|
+
if (!btn) return false;
|
|
44785
44838
|
const rule = stateById(this.spec, this.currentStateId)?.extract?.buttons;
|
|
44786
44839
|
if (rule?.select_mode === "arrow_keys") {
|
|
44787
44840
|
const from = m.buttons.find((b) => b.current)?.index ?? 1;
|
|
@@ -44793,9 +44846,10 @@ var FsmDriver = class {
|
|
|
44793
44846
|
const confirm = (rule.key_for_index || "\r").replace(/\{index\}/g, "") || "\r";
|
|
44794
44847
|
if (nav) this.adapter.send_keys(nav);
|
|
44795
44848
|
this.submitModalConfirm(confirm);
|
|
44796
|
-
return;
|
|
44849
|
+
return true;
|
|
44797
44850
|
}
|
|
44798
44851
|
this.submitModalConfirm(btn.key);
|
|
44852
|
+
return true;
|
|
44799
44853
|
}
|
|
44800
44854
|
/**
|
|
44801
44855
|
* Submit a modal-confirm key sequence (the choice key + its trailing CR).
|
|
@@ -46227,7 +46281,20 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
46227
46281
|
// modal this frame still stays waiting_approval (no activeModal yet).
|
|
46228
46282
|
// `kind` carries the semantic modal class through to the auto-approve
|
|
46229
46283
|
// gate so a /model picker (kind='picker') is never auto-answered.
|
|
46230
|
-
|
|
46284
|
+
// BUTTON-INDEX-MISMAP (Fix C.1): keep `buttons` as the label list every
|
|
46285
|
+
// existing consumer (pickApprovalButton, mesh_approve, auto-approve) reads,
|
|
46286
|
+
// but ALSO surface `buttonMeta` carrying each button's real FSM display index
|
|
46287
|
+
// alongside its label. A partial/non-contiguous modal (display indices [1,3,4]
|
|
46288
|
+
// at array positions [0,1,2]) then no longer loses the index → label mapping
|
|
46289
|
+
// once it leaves the adapter: a consumer that has an array position can recover
|
|
46290
|
+
// the true FSM index without re-parsing. resolveModal() below relies on the same
|
|
46291
|
+
// ordered list to translate an array position to the correct FSM index.
|
|
46292
|
+
activeModal: modal ? {
|
|
46293
|
+
message: modal.title ?? state.label,
|
|
46294
|
+
buttons: modal.buttons.map((b) => b.label),
|
|
46295
|
+
buttonMeta: modal.buttons.map((b) => ({ index: b.index, label: b.label })),
|
|
46296
|
+
kind: modal.kind ?? null
|
|
46297
|
+
} : null,
|
|
46231
46298
|
activeInteractivePrompt: this.activeInteractivePrompt,
|
|
46232
46299
|
...sessionFields
|
|
46233
46300
|
};
|
|
@@ -46399,7 +46466,12 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
46399
46466
|
this.driver.dispatch({ kind: "resize", cols, rows });
|
|
46400
46467
|
}
|
|
46401
46468
|
resolveModal(buttonIndex) {
|
|
46402
|
-
this.
|
|
46469
|
+
this.resolveModalMatched(buttonIndex);
|
|
46470
|
+
}
|
|
46471
|
+
resolveModalMatched(buttonIndex) {
|
|
46472
|
+
const buttons = this.latestModal?.buttons ?? [];
|
|
46473
|
+
const target = buttonIndex >= 0 && buttonIndex < buttons.length ? buttons[buttonIndex].index : buttonIndex + 1;
|
|
46474
|
+
return this.driver.clickModalButton(target);
|
|
46403
46475
|
}
|
|
46404
46476
|
async resolveAction(data) {
|
|
46405
46477
|
const args = data && typeof data === "object" ? data : {};
|
|
@@ -47559,6 +47631,15 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47559
47631
|
* keystroke until the modal *content* has settled.
|
|
47560
47632
|
*/
|
|
47561
47633
|
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
47634
|
+
/**
|
|
47635
|
+
* APPROVAL-INBOX-BLINDSPOT (Fix A): how long after a LOCAL auto-approve fire the mesh
|
|
47636
|
+
* event forwarder still treats the modal as "being resolved locally" and suppresses the
|
|
47637
|
+
* coordinator notification. Chosen to comfortably cover the resolveModal → PTY absorb →
|
|
47638
|
+
* status-leaves-approval round trip (incl. the win32 CR-resend loop) while staying short
|
|
47639
|
+
* enough that a modal which auto-approve fired at but did NOT resolve re-surfaces to the
|
|
47640
|
+
* coordinator on the next event. Aligned with the adapter's own approval cooldown scale.
|
|
47641
|
+
*/
|
|
47642
|
+
static APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS = 8e3;
|
|
47562
47643
|
/**
|
|
47563
47644
|
* Busy-side hysteresis for the settle gate. A momentary `generating` flip
|
|
47564
47645
|
* while the SAME approval modal's button block is still on screen (its
|
|
@@ -47831,6 +47912,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47831
47912
|
// signature) while a genuinely closed modal — buttons empty continuously past
|
|
47832
47913
|
// the continuity window — is still recognised and resets the gate.
|
|
47833
47914
|
autoApproveLastModalSeenAt = 0;
|
|
47915
|
+
// APPROVAL-INBOX-BLINDSPOT (Fix A): wall-clock of the last time this session actually
|
|
47916
|
+
// FIRED a local auto-approve resolveModal (the settle gate passed → resolveModal
|
|
47917
|
+
// dispatched). The mesh event forwarder keys its agent:waiting_approval suppression on
|
|
47918
|
+
// this + a cooldown so it only drops the coordinator notification when we can positively
|
|
47919
|
+
// confirm the modal was (or is being) resolved LOCALLY. If auto-approve is merely
|
|
47920
|
+
// *configured* on but has NOT recently fired for this modal, the raw waiting_approval is
|
|
47921
|
+
// forwarded so a task_approval_needed ledger row is created and the coordinator/inbox is
|
|
47922
|
+
// told — closing the blind spot where a never-resolving worker approval was silently
|
|
47923
|
+
// dropped just because settings.autoApprove===true.
|
|
47924
|
+
lastAutoApproveFiredAt = 0;
|
|
47834
47925
|
// AUTOAPPROVE-FLAP-INBOX-MISSING sticky-approval overlay (see APPROVAL_STICKY_FLAP_MS).
|
|
47835
47926
|
// The wall-clock of the last frame where the RAW adapter reported waiting_approval with
|
|
47836
47927
|
// a CONCRETE modal (buttons present), the cached modal to re-present across a busy blip,
|
|
@@ -48316,6 +48407,33 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48316
48407
|
}
|
|
48317
48408
|
return null;
|
|
48318
48409
|
}
|
|
48410
|
+
/**
|
|
48411
|
+
* APPROVAL-INBOX-BLINDSPOT (Fix A): true when this session's approval modal was — or is
|
|
48412
|
+
* being — resolved LOCALLY within the recent cooldown. Two independent positive signals:
|
|
48413
|
+
* (1) auto-approve fired its resolveModal within APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS
|
|
48414
|
+
* (lastAutoApproveFiredAt), or
|
|
48415
|
+
* (2) the underlying adapter reports isApprovalRecentlyResolved() — its own resolve
|
|
48416
|
+
* cooldown, which also covers a dashboard / mesh_approve resolution.
|
|
48417
|
+
* The mesh event forwarder uses this to decide whether an agent:waiting_approval from an
|
|
48418
|
+
* auto-approving worker can be safely SUPPRESSED (a local resolution is in flight) or must
|
|
48419
|
+
* be FORWARDED (auto-approve is configured but has NOT actually resolved this modal, so the
|
|
48420
|
+
* coordinator/inbox must be told). Keying suppression on real resolution — not just the
|
|
48421
|
+
* autoApprove *intent* — is the blind-spot fix: a never-resolving worker approval is no
|
|
48422
|
+
* longer silently dropped.
|
|
48423
|
+
*/
|
|
48424
|
+
approvalRecentlyResolvedLocally(now = Date.now()) {
|
|
48425
|
+
if (this.lastAutoApproveFiredAt && now - this.lastAutoApproveFiredAt < _CliProviderInstance.APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS) {
|
|
48426
|
+
return true;
|
|
48427
|
+
}
|
|
48428
|
+
try {
|
|
48429
|
+
const adapter = this.adapter;
|
|
48430
|
+
if (typeof adapter.isApprovalRecentlyResolved === "function") {
|
|
48431
|
+
return adapter.isApprovalRecentlyResolved() === true;
|
|
48432
|
+
}
|
|
48433
|
+
} catch {
|
|
48434
|
+
}
|
|
48435
|
+
return false;
|
|
48436
|
+
}
|
|
48319
48437
|
/**
|
|
48320
48438
|
* NOTIF-HELD-DRAIN: true when this `waiting_approval` is a routine, transient tool-consent
|
|
48321
48439
|
* of an autonomously-progressing mesh session rather than a genuine human-await modal —
|
|
@@ -48673,18 +48791,30 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48673
48791
|
* the dashboard tail-repair cache — a display value, not a completion decision.
|
|
48674
48792
|
*/
|
|
48675
48793
|
lastVisibleAssistantSummary(messages) {
|
|
48676
|
-
|
|
48794
|
+
return this.lastVisibleAssistantSummaryDetail(messages).content;
|
|
48795
|
+
}
|
|
48796
|
+
// Like lastVisibleAssistantSummary but also returns the source bubble's own
|
|
48797
|
+
// timestamp (ms), so a cached summary can later be turn-scoped: the display
|
|
48798
|
+
// cache is populated from an UNSCOPED tail read (it must show the answer as
|
|
48799
|
+
// soon as native-history has it), so it can hold a bubble that predates the
|
|
48800
|
+
// current turn. Recording the bubble's timestamp lets the weak-completion
|
|
48801
|
+
// fallback reject a turn-stale cached summary instead of re-leaking the exact
|
|
48802
|
+
// stale bubble the turn-boundary gate already rejected (FALSE-IDLE Defect 1c).
|
|
48803
|
+
lastVisibleAssistantSummaryDetail(messages) {
|
|
48804
|
+
if (!Array.isArray(messages)) return { content: "" };
|
|
48677
48805
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
48678
48806
|
const m = messages[i];
|
|
48679
48807
|
const role = typeof m?.role === "string" ? m.role : "";
|
|
48680
48808
|
const kind = typeof m?.kind === "string" ? m.kind : "";
|
|
48681
48809
|
if (role === "system") continue;
|
|
48682
48810
|
if (kind === "tool" || kind === "activity") continue;
|
|
48683
|
-
if (role === "user" || role === "human") return "";
|
|
48684
|
-
if (role === "assistant")
|
|
48685
|
-
|
|
48811
|
+
if (role === "user" || role === "human") return { content: "" };
|
|
48812
|
+
if (role === "assistant") {
|
|
48813
|
+
return { content: flattenContent(m.content).trim(), timestampMs: readChatMessageTimestampMs(m) };
|
|
48814
|
+
}
|
|
48815
|
+
return { content: "" };
|
|
48686
48816
|
}
|
|
48687
|
-
return "";
|
|
48817
|
+
return { content: "" };
|
|
48688
48818
|
}
|
|
48689
48819
|
/**
|
|
48690
48820
|
* NOTIF Defect-B: the final assistant summary this instance ALREADY parsed and
|
|
@@ -48705,6 +48835,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48705
48835
|
const content = typeof cached3?.content === "string" ? cached3.content.trim() : "";
|
|
48706
48836
|
return content;
|
|
48707
48837
|
}
|
|
48838
|
+
// FALSE-IDLE Defect 1c: turn-scoped view of the cached completion summary.
|
|
48839
|
+
// The cache is populated from an UNSCOPED tail read (lastVisibleAssistant‑
|
|
48840
|
+
// SummaryDetail) so the dashboard can show the answer the instant native-history
|
|
48841
|
+
// has it — which means it can hold a bubble that PREDATES the producing turn.
|
|
48842
|
+
// The weak-completion (missing_final_assistant) emit path falls back to the cache
|
|
48843
|
+
// for finalSummary; without turn-scoping it would re-surface the exact stale
|
|
48844
|
+
// mid-turn bubble the turn-boundary gate already rejected as evidence, freezing
|
|
48845
|
+
// that stale text as the completion's finalSummary. Consult the cache only when
|
|
48846
|
+
// its source bubble is proven in-turn (timestamp at/after turnStartedAt). When no
|
|
48847
|
+
// boundary is known (turnStartedAt falsy) or the cache carries no source timestamp
|
|
48848
|
+
// (legacy writes), behaviour is identical to the unscoped read.
|
|
48849
|
+
cachedInTurnCompletionSummaryContent(turnStartedAt) {
|
|
48850
|
+
const cached3 = this.lastCompletionSummary;
|
|
48851
|
+
const content = typeof cached3?.content === "string" ? cached3.content.trim() : "";
|
|
48852
|
+
if (!content) return "";
|
|
48853
|
+
const hasBoundary = typeof turnStartedAt === "number" && Number.isFinite(turnStartedAt) && turnStartedAt > 0;
|
|
48854
|
+
const ts2 = cached3?.sourceTimestampMs;
|
|
48855
|
+
if (hasBoundary && typeof ts2 === "number" && Number.isFinite(ts2) && ts2 < turnStartedAt) {
|
|
48856
|
+
return "";
|
|
48857
|
+
}
|
|
48858
|
+
return content;
|
|
48859
|
+
}
|
|
48708
48860
|
completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
|
|
48709
48861
|
const turnClosed = !this.hasAdapterPendingResponse();
|
|
48710
48862
|
if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
|
|
@@ -48718,9 +48870,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48718
48870
|
if (externalMessages) {
|
|
48719
48871
|
const injectedTaskGenerating = this.injectedTaskHasStartedGenerating();
|
|
48720
48872
|
const present = injectedTaskGenerating && turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt);
|
|
48721
|
-
const lastVisibleAssistant = this.
|
|
48722
|
-
if (lastVisibleAssistant) {
|
|
48723
|
-
this.lastCompletionSummary = { content: lastVisibleAssistant, receivedAt: Date.now() };
|
|
48873
|
+
const lastVisibleAssistant = this.lastVisibleAssistantSummaryDetail(externalMessages);
|
|
48874
|
+
if (lastVisibleAssistant.content) {
|
|
48875
|
+
this.lastCompletionSummary = { content: lastVisibleAssistant.content, receivedAt: Date.now(), sourceTimestampMs: lastVisibleAssistant.timestampMs };
|
|
48724
48876
|
}
|
|
48725
48877
|
return {
|
|
48726
48878
|
present,
|
|
@@ -48744,7 +48896,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48744
48896
|
const externalMessages = this.readExternalCompletionMessages();
|
|
48745
48897
|
const externalSummary = externalMessages ? extractFinalSummaryFromMessagesAfter(externalMessages, turnStartedAt) : "";
|
|
48746
48898
|
if (externalSummary) {
|
|
48747
|
-
this.lastCompletionSummary = { content: externalSummary, receivedAt: Date.now() };
|
|
48899
|
+
this.lastCompletionSummary = { content: externalSummary, receivedAt: Date.now(), sourceTimestampMs: typeof turnStartedAt === "number" ? turnStartedAt : void 0 };
|
|
48748
48900
|
return externalSummary;
|
|
48749
48901
|
}
|
|
48750
48902
|
return parsedSummary || void 0;
|
|
@@ -48759,7 +48911,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48759
48911
|
} catch (error) {
|
|
48760
48912
|
parseError = error?.message || String(error);
|
|
48761
48913
|
}
|
|
48762
|
-
const evidence = this.completionFinalAssistantEvidence(parsed?.messages);
|
|
48914
|
+
const evidence = this.completionFinalAssistantEvidence(parsed?.messages, args.pending.turnStartedAt);
|
|
48763
48915
|
if (evidence.source === "external-native") {
|
|
48764
48916
|
this.recordPendingTranscriptProbe(args.pending);
|
|
48765
48917
|
}
|
|
@@ -48768,7 +48920,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48768
48920
|
const lastVisibleRole = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : null;
|
|
48769
48921
|
const lastVisibleKind = typeof lastVisible?.kind === "string" ? lastVisible.kind : null;
|
|
48770
48922
|
const lastVisibleContentLength = lastVisible ? flattenContent(lastVisible.content).trim().length : 0;
|
|
48771
|
-
const cachedSummary = evidence.present ? "" : this.
|
|
48923
|
+
const cachedSummary = evidence.present ? "" : this.cachedInTurnCompletionSummaryContent(args.pending.turnStartedAt);
|
|
48772
48924
|
const creditedFromCache = !evidence.present && cachedSummary.length > 0;
|
|
48773
48925
|
const finalAssistantPresent = evidence.present || creditedFromCache;
|
|
48774
48926
|
const finalAssistantEvidenceSource = evidence.present ? evidence.source : creditedFromCache ? "cached-summary" : evidence.source;
|
|
@@ -49432,7 +49584,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
49432
49584
|
// real answer (lastCompletionSummary). Fall back to the cache so the notification
|
|
49433
49585
|
// carries the summary that mesh_read_chat.summary already shows — consistent with
|
|
49434
49586
|
// completionDiagnostic.finalAssistantPresent being credited from the same cache.
|
|
49435
|
-
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) || this.
|
|
49587
|
+
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) || this.cachedInTurnCompletionSummaryContent(pending.turnStartedAt) || (blockReason.startsWith("parsed_status:") ? "" : void 0),
|
|
49436
49588
|
completionDiagnostic
|
|
49437
49589
|
});
|
|
49438
49590
|
this.completedDebouncePending = null;
|
|
@@ -49666,8 +49818,18 @@ ${buttons.join("\n")}`;
|
|
|
49666
49818
|
this.lastAutoApprovalSignature = "";
|
|
49667
49819
|
}, 5e3);
|
|
49668
49820
|
this.recordAutoApproval(modal?.message, buttonLabel, now);
|
|
49821
|
+
this.lastAutoApproveFiredAt = now;
|
|
49669
49822
|
setTimeout(() => {
|
|
49670
|
-
this.adapter
|
|
49823
|
+
const adapter = this.adapter;
|
|
49824
|
+
if (typeof adapter.resolveModalMatched === "function") {
|
|
49825
|
+
const matched = adapter.resolveModalMatched(buttonIndex);
|
|
49826
|
+
if (!matched) {
|
|
49827
|
+
if (this.lastAutoApproveFiredAt === now) this.lastAutoApproveFiredAt = 0;
|
|
49828
|
+
LOG.warn("CLI", `[${this.type}] auto-approve resolveModal matched no button (index ${buttonIndex}) \u2014 surfacing approval to coordinator`);
|
|
49829
|
+
}
|
|
49830
|
+
} else {
|
|
49831
|
+
adapter.resolveModal?.(buttonIndex);
|
|
49832
|
+
}
|
|
49671
49833
|
}, 0);
|
|
49672
49834
|
return autoApproveActive;
|
|
49673
49835
|
}
|