@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
package/dist/index.mjs
CHANGED
|
@@ -423,10 +423,10 @@ function readInjected(value) {
|
|
|
423
423
|
}
|
|
424
424
|
function getDaemonBuildInfo() {
|
|
425
425
|
if (cached) return cached;
|
|
426
|
-
const commit = readInjected(true ? "
|
|
427
|
-
const commitShort = readInjected(true ? "
|
|
428
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
429
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
426
|
+
const commit = readInjected(true ? "39e0687342226d334e36c982bd8988802f43dd3e" : void 0) ?? "unknown";
|
|
427
|
+
const commitShort = readInjected(true ? "39e06873" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
428
|
+
const version = readInjected(true ? "0.9.82-rc.568" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
429
|
+
const builtAt = readInjected(true ? "2026-07-18T14:18:06.483Z" : void 0);
|
|
430
430
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
431
431
|
return cached;
|
|
432
432
|
}
|
|
@@ -14976,12 +14976,16 @@ function buildMeshActiveWork(opts) {
|
|
|
14976
14976
|
for (const task of opts.queue || []) {
|
|
14977
14977
|
if (task.status !== "pending" && task.status !== "assigned") continue;
|
|
14978
14978
|
const { title, summary: summary2 } = summarizeMessage(task.message || "");
|
|
14979
|
+
const queueNodeId = task.assignedNodeId || task.targetNodeId;
|
|
14980
|
+
const queueSessionId = task.assignedSessionId || task.targetSessionId;
|
|
14981
|
+
const queueLive = task.status === "assigned" ? sessionStatusFromNodes(opts.nodes, queueNodeId ?? void 0, queueSessionId ?? void 0) : {};
|
|
14982
|
+
const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "generating" ? queueLive.status : task.status;
|
|
14979
14983
|
records.push({
|
|
14980
14984
|
taskId: task.id,
|
|
14981
14985
|
source: "queue",
|
|
14982
|
-
status:
|
|
14983
|
-
nodeId:
|
|
14984
|
-
sessionId:
|
|
14986
|
+
status: queueStatus,
|
|
14987
|
+
nodeId: queueNodeId,
|
|
14988
|
+
sessionId: queueSessionId,
|
|
14985
14989
|
taskTitle: title,
|
|
14986
14990
|
taskSummary: summary2,
|
|
14987
14991
|
message: task.message,
|
|
@@ -20564,12 +20568,15 @@ function evaluateMeshEventSuppression(args, ctx) {
|
|
|
20564
20568
|
}
|
|
20565
20569
|
return null;
|
|
20566
20570
|
}
|
|
20567
|
-
function
|
|
20571
|
+
function shouldSuppressAutoApprovingWorkerApproval(components, sessionId) {
|
|
20568
20572
|
if (!sessionId) return false;
|
|
20569
20573
|
try {
|
|
20570
|
-
const
|
|
20574
|
+
const instance = components.instanceManager?.getInstance?.(sessionId);
|
|
20575
|
+
const state = instance?.getState?.();
|
|
20571
20576
|
const settings = state?.settings || {};
|
|
20572
|
-
|
|
20577
|
+
if (settings.autoApprove !== true) return false;
|
|
20578
|
+
const resolvedLocally = instance?.approvalRecentlyResolvedLocally;
|
|
20579
|
+
return typeof resolvedLocally === "function" ? resolvedLocally.call(instance) === true : false;
|
|
20573
20580
|
} catch {
|
|
20574
20581
|
return false;
|
|
20575
20582
|
}
|
|
@@ -20714,8 +20721,8 @@ function injectMeshSystemMessage(components, args) {
|
|
|
20714
20721
|
}
|
|
20715
20722
|
}
|
|
20716
20723
|
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
20717
|
-
if (args.event === "agent:waiting_approval" &&
|
|
20718
|
-
LOG.info("MeshEvents", `Suppressed agent:waiting_approval for auto-approving worker session ${eventSessionId || "(unknown)"} (mesh ${args.meshId}) \u2014 modal
|
|
20724
|
+
if (args.event === "agent:waiting_approval" && shouldSuppressAutoApprovingWorkerApproval(components, eventSessionId)) {
|
|
20725
|
+
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`);
|
|
20719
20726
|
traceMeshEventDrop("waiting_approval_auto_approving_worker", traceCtx);
|
|
20720
20727
|
return { success: true, forwarded: 0, suppressed: true, autoApprovingWorkerApproval: true };
|
|
20721
20728
|
}
|
|
@@ -22414,6 +22421,14 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
|
|
|
22414
22421
|
}
|
|
22415
22422
|
return delivered;
|
|
22416
22423
|
}
|
|
22424
|
+
function assignedRowLiveStatusIsAwaitingApproval(mesh, nodeId, sessionId) {
|
|
22425
|
+
if (!nodeId || !sessionId) return false;
|
|
22426
|
+
try {
|
|
22427
|
+
return sessionStatusFromNodes(mesh.nodes, nodeId, sessionId).status === "awaiting_approval";
|
|
22428
|
+
} catch {
|
|
22429
|
+
return false;
|
|
22430
|
+
}
|
|
22431
|
+
}
|
|
22417
22432
|
async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
22418
22433
|
const meshId = mesh.id;
|
|
22419
22434
|
const assigned = getQueue(meshId, { status: ["assigned"] });
|
|
@@ -22445,6 +22460,15 @@ async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
|
22445
22460
|
} else {
|
|
22446
22461
|
if (verdict === "IDLE_CONFIRMED") {
|
|
22447
22462
|
deliveredUnconsumedUnknownStreak.delete(shortStreakKey);
|
|
22463
|
+
} else if (assignedRowLiveStatusIsAwaitingApproval(mesh, row.assignedNodeId, row.assignedSessionId)) {
|
|
22464
|
+
traceMeshEventDrop("short_redrive_deferred_awaiting_approval", {
|
|
22465
|
+
taskId: row.id,
|
|
22466
|
+
sessionId: row.assignedSessionId,
|
|
22467
|
+
nodeId: row.assignedNodeId,
|
|
22468
|
+
meshId,
|
|
22469
|
+
event: "agent:waiting_approval"
|
|
22470
|
+
}, "live_awaiting_approval");
|
|
22471
|
+
continue;
|
|
22448
22472
|
} else {
|
|
22449
22473
|
const streak = (deliveredUnconsumedUnknownStreak.get(shortStreakKey) ?? 0) + 1;
|
|
22450
22474
|
deliveredUnconsumedUnknownStreak.set(shortStreakKey, streak);
|
|
@@ -22507,6 +22531,15 @@ async function recoverStrandedAssignedDispatches(components, mesh, store) {
|
|
|
22507
22531
|
if (verdict === "IDLE_CONFIRMED") {
|
|
22508
22532
|
deliveredNoTurnUnknownStreak.delete(streakKey);
|
|
22509
22533
|
reclaimReason = "delivered_no_turn_deadline";
|
|
22534
|
+
} else if (assignedRowLiveStatusIsAwaitingApproval(mesh, row.assignedNodeId, row.assignedSessionId)) {
|
|
22535
|
+
traceMeshEventDrop("reclaim_deferred_awaiting_approval", {
|
|
22536
|
+
taskId: row.id,
|
|
22537
|
+
sessionId: row.assignedSessionId,
|
|
22538
|
+
nodeId: row.assignedNodeId,
|
|
22539
|
+
meshId,
|
|
22540
|
+
event: "agent:waiting_approval"
|
|
22541
|
+
}, "live_awaiting_approval");
|
|
22542
|
+
continue;
|
|
22510
22543
|
} else {
|
|
22511
22544
|
const streak = (deliveredNoTurnUnknownStreak.get(streakKey) ?? 0) + 1;
|
|
22512
22545
|
deliveredNoTurnUnknownStreak.set(streakKey, streak);
|
|
@@ -23074,6 +23107,7 @@ var init_mesh_reconcile_loop = __esm({
|
|
|
23074
23107
|
init_mesh_reconcile_config();
|
|
23075
23108
|
init_mesh_remote_event_pull();
|
|
23076
23109
|
init_mesh_completion_synthesis();
|
|
23110
|
+
init_mesh_active_work();
|
|
23077
23111
|
init_mesh_reconcile_v2_backstop();
|
|
23078
23112
|
init_mesh_reconcile_acked_hold();
|
|
23079
23113
|
coordinatorModalParkState = /* @__PURE__ */ new Map();
|
|
@@ -39008,7 +39042,13 @@ async function handleResolveAction(h, args) {
|
|
|
39008
39042
|
LOG.info("Command", `[resolveAction] CLI PTY \u2192 stale_prompt (already resolved within cooldown)`);
|
|
39009
39043
|
return { success: true, stalePrompt: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
39010
39044
|
}
|
|
39011
|
-
if (typeof adapter.
|
|
39045
|
+
if (typeof adapter.resolveModalMatched === "function") {
|
|
39046
|
+
const matched = adapter.resolveModalMatched(buttonIndex);
|
|
39047
|
+
if (!matched) {
|
|
39048
|
+
LOG.warn("Command", `[resolveAction] CLI PTY \u2192 no button matched for buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}" (modal not resolved)`);
|
|
39049
|
+
return { success: false, error: "Approval button index did not map to a visible modal button", buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
39050
|
+
}
|
|
39051
|
+
} else if (typeof adapter.resolveModal === "function") {
|
|
39012
39052
|
adapter.resolveModal(buttonIndex);
|
|
39013
39053
|
} else {
|
|
39014
39054
|
const keys = "\x1B[B".repeat(Math.max(0, buttonIndex)) + "\r";
|
|
@@ -43601,7 +43641,7 @@ var FsmDriver = class {
|
|
|
43601
43641
|
this.handleClickControl(cmd.control_id, cmd.payload);
|
|
43602
43642
|
return;
|
|
43603
43643
|
case "click_modal_button":
|
|
43604
|
-
this.
|
|
43644
|
+
this.clickModalButton(cmd.index);
|
|
43605
43645
|
return;
|
|
43606
43646
|
case "attach_image":
|
|
43607
43647
|
this.handleAttachImage(cmd.blob, cmd.mime);
|
|
@@ -44344,11 +44384,24 @@ var FsmDriver = class {
|
|
|
44344
44384
|
}
|
|
44345
44385
|
}
|
|
44346
44386
|
}
|
|
44387
|
+
/**
|
|
44388
|
+
* BUTTON-INDEX-MISMAP (Fix C.3): public modal-click entry that returns whether a button
|
|
44389
|
+
* matching the requested FSM display index was actually found and its confirm keys were
|
|
44390
|
+
* dispatched. The old private handleClickModalButton silently `return`ed on a miss (no
|
|
44391
|
+
* modal captured, or no button whose `.index` equals the requested display index), so a
|
|
44392
|
+
* mis-mapped index looked identical to a successful press. Callers that need to know
|
|
44393
|
+
* whether the click landed (mesh_approve → resolveModal) can now observe the miss instead
|
|
44394
|
+
* of reporting success into the void. The generic `dispatch('click_modal_button')` path
|
|
44395
|
+
* keeps ignoring the return (fire-and-forget UI clicks).
|
|
44396
|
+
*/
|
|
44397
|
+
clickModalButton(index) {
|
|
44398
|
+
return this.handleClickModalButton(index);
|
|
44399
|
+
}
|
|
44347
44400
|
handleClickModalButton(index) {
|
|
44348
44401
|
const m = this.currentEval?.modal;
|
|
44349
|
-
if (!m) return;
|
|
44402
|
+
if (!m) return false;
|
|
44350
44403
|
const btn = m.buttons.find((b) => b.index === index);
|
|
44351
|
-
if (!btn) return;
|
|
44404
|
+
if (!btn) return false;
|
|
44352
44405
|
const rule = stateById(this.spec, this.currentStateId)?.extract?.buttons;
|
|
44353
44406
|
if (rule?.select_mode === "arrow_keys") {
|
|
44354
44407
|
const from = m.buttons.find((b) => b.current)?.index ?? 1;
|
|
@@ -44360,9 +44413,10 @@ var FsmDriver = class {
|
|
|
44360
44413
|
const confirm = (rule.key_for_index || "\r").replace(/\{index\}/g, "") || "\r";
|
|
44361
44414
|
if (nav) this.adapter.send_keys(nav);
|
|
44362
44415
|
this.submitModalConfirm(confirm);
|
|
44363
|
-
return;
|
|
44416
|
+
return true;
|
|
44364
44417
|
}
|
|
44365
44418
|
this.submitModalConfirm(btn.key);
|
|
44419
|
+
return true;
|
|
44366
44420
|
}
|
|
44367
44421
|
/**
|
|
44368
44422
|
* Submit a modal-confirm key sequence (the choice key + its trailing CR).
|
|
@@ -45794,7 +45848,20 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
45794
45848
|
// modal this frame still stays waiting_approval (no activeModal yet).
|
|
45795
45849
|
// `kind` carries the semantic modal class through to the auto-approve
|
|
45796
45850
|
// gate so a /model picker (kind='picker') is never auto-answered.
|
|
45797
|
-
|
|
45851
|
+
// BUTTON-INDEX-MISMAP (Fix C.1): keep `buttons` as the label list every
|
|
45852
|
+
// existing consumer (pickApprovalButton, mesh_approve, auto-approve) reads,
|
|
45853
|
+
// but ALSO surface `buttonMeta` carrying each button's real FSM display index
|
|
45854
|
+
// alongside its label. A partial/non-contiguous modal (display indices [1,3,4]
|
|
45855
|
+
// at array positions [0,1,2]) then no longer loses the index → label mapping
|
|
45856
|
+
// once it leaves the adapter: a consumer that has an array position can recover
|
|
45857
|
+
// the true FSM index without re-parsing. resolveModal() below relies on the same
|
|
45858
|
+
// ordered list to translate an array position to the correct FSM index.
|
|
45859
|
+
activeModal: modal ? {
|
|
45860
|
+
message: modal.title ?? state.label,
|
|
45861
|
+
buttons: modal.buttons.map((b) => b.label),
|
|
45862
|
+
buttonMeta: modal.buttons.map((b) => ({ index: b.index, label: b.label })),
|
|
45863
|
+
kind: modal.kind ?? null
|
|
45864
|
+
} : null,
|
|
45798
45865
|
activeInteractivePrompt: this.activeInteractivePrompt,
|
|
45799
45866
|
...sessionFields
|
|
45800
45867
|
};
|
|
@@ -45966,7 +46033,12 @@ var SpecCliAdapter = class _SpecCliAdapter {
|
|
|
45966
46033
|
this.driver.dispatch({ kind: "resize", cols, rows });
|
|
45967
46034
|
}
|
|
45968
46035
|
resolveModal(buttonIndex) {
|
|
45969
|
-
this.
|
|
46036
|
+
this.resolveModalMatched(buttonIndex);
|
|
46037
|
+
}
|
|
46038
|
+
resolveModalMatched(buttonIndex) {
|
|
46039
|
+
const buttons = this.latestModal?.buttons ?? [];
|
|
46040
|
+
const target = buttonIndex >= 0 && buttonIndex < buttons.length ? buttons[buttonIndex].index : buttonIndex + 1;
|
|
46041
|
+
return this.driver.clickModalButton(target);
|
|
45970
46042
|
}
|
|
45971
46043
|
async resolveAction(data) {
|
|
45972
46044
|
const args = data && typeof data === "object" ? data : {};
|
|
@@ -47126,6 +47198,15 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47126
47198
|
* keystroke until the modal *content* has settled.
|
|
47127
47199
|
*/
|
|
47128
47200
|
static AUTO_APPROVE_SETTLE_MS = 600;
|
|
47201
|
+
/**
|
|
47202
|
+
* APPROVAL-INBOX-BLINDSPOT (Fix A): how long after a LOCAL auto-approve fire the mesh
|
|
47203
|
+
* event forwarder still treats the modal as "being resolved locally" and suppresses the
|
|
47204
|
+
* coordinator notification. Chosen to comfortably cover the resolveModal → PTY absorb →
|
|
47205
|
+
* status-leaves-approval round trip (incl. the win32 CR-resend loop) while staying short
|
|
47206
|
+
* enough that a modal which auto-approve fired at but did NOT resolve re-surfaces to the
|
|
47207
|
+
* coordinator on the next event. Aligned with the adapter's own approval cooldown scale.
|
|
47208
|
+
*/
|
|
47209
|
+
static APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS = 8e3;
|
|
47129
47210
|
/**
|
|
47130
47211
|
* Busy-side hysteresis for the settle gate. A momentary `generating` flip
|
|
47131
47212
|
* while the SAME approval modal's button block is still on screen (its
|
|
@@ -47398,6 +47479,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47398
47479
|
// signature) while a genuinely closed modal — buttons empty continuously past
|
|
47399
47480
|
// the continuity window — is still recognised and resets the gate.
|
|
47400
47481
|
autoApproveLastModalSeenAt = 0;
|
|
47482
|
+
// APPROVAL-INBOX-BLINDSPOT (Fix A): wall-clock of the last time this session actually
|
|
47483
|
+
// FIRED a local auto-approve resolveModal (the settle gate passed → resolveModal
|
|
47484
|
+
// dispatched). The mesh event forwarder keys its agent:waiting_approval suppression on
|
|
47485
|
+
// this + a cooldown so it only drops the coordinator notification when we can positively
|
|
47486
|
+
// confirm the modal was (or is being) resolved LOCALLY. If auto-approve is merely
|
|
47487
|
+
// *configured* on but has NOT recently fired for this modal, the raw waiting_approval is
|
|
47488
|
+
// forwarded so a task_approval_needed ledger row is created and the coordinator/inbox is
|
|
47489
|
+
// told — closing the blind spot where a never-resolving worker approval was silently
|
|
47490
|
+
// dropped just because settings.autoApprove===true.
|
|
47491
|
+
lastAutoApproveFiredAt = 0;
|
|
47401
47492
|
// AUTOAPPROVE-FLAP-INBOX-MISSING sticky-approval overlay (see APPROVAL_STICKY_FLAP_MS).
|
|
47402
47493
|
// The wall-clock of the last frame where the RAW adapter reported waiting_approval with
|
|
47403
47494
|
// a CONCRETE modal (buttons present), the cached modal to re-present across a busy blip,
|
|
@@ -47883,6 +47974,33 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47883
47974
|
}
|
|
47884
47975
|
return null;
|
|
47885
47976
|
}
|
|
47977
|
+
/**
|
|
47978
|
+
* APPROVAL-INBOX-BLINDSPOT (Fix A): true when this session's approval modal was — or is
|
|
47979
|
+
* being — resolved LOCALLY within the recent cooldown. Two independent positive signals:
|
|
47980
|
+
* (1) auto-approve fired its resolveModal within APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS
|
|
47981
|
+
* (lastAutoApproveFiredAt), or
|
|
47982
|
+
* (2) the underlying adapter reports isApprovalRecentlyResolved() — its own resolve
|
|
47983
|
+
* cooldown, which also covers a dashboard / mesh_approve resolution.
|
|
47984
|
+
* The mesh event forwarder uses this to decide whether an agent:waiting_approval from an
|
|
47985
|
+
* auto-approving worker can be safely SUPPRESSED (a local resolution is in flight) or must
|
|
47986
|
+
* be FORWARDED (auto-approve is configured but has NOT actually resolved this modal, so the
|
|
47987
|
+
* coordinator/inbox must be told). Keying suppression on real resolution — not just the
|
|
47988
|
+
* autoApprove *intent* — is the blind-spot fix: a never-resolving worker approval is no
|
|
47989
|
+
* longer silently dropped.
|
|
47990
|
+
*/
|
|
47991
|
+
approvalRecentlyResolvedLocally(now = Date.now()) {
|
|
47992
|
+
if (this.lastAutoApproveFiredAt && now - this.lastAutoApproveFiredAt < _CliProviderInstance.APPROVAL_LOCAL_RESOLUTION_COOLDOWN_MS) {
|
|
47993
|
+
return true;
|
|
47994
|
+
}
|
|
47995
|
+
try {
|
|
47996
|
+
const adapter = this.adapter;
|
|
47997
|
+
if (typeof adapter.isApprovalRecentlyResolved === "function") {
|
|
47998
|
+
return adapter.isApprovalRecentlyResolved() === true;
|
|
47999
|
+
}
|
|
48000
|
+
} catch {
|
|
48001
|
+
}
|
|
48002
|
+
return false;
|
|
48003
|
+
}
|
|
47886
48004
|
/**
|
|
47887
48005
|
* NOTIF-HELD-DRAIN: true when this `waiting_approval` is a routine, transient tool-consent
|
|
47888
48006
|
* of an autonomously-progressing mesh session rather than a genuine human-await modal —
|
|
@@ -48240,18 +48358,30 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48240
48358
|
* the dashboard tail-repair cache — a display value, not a completion decision.
|
|
48241
48359
|
*/
|
|
48242
48360
|
lastVisibleAssistantSummary(messages) {
|
|
48243
|
-
|
|
48361
|
+
return this.lastVisibleAssistantSummaryDetail(messages).content;
|
|
48362
|
+
}
|
|
48363
|
+
// Like lastVisibleAssistantSummary but also returns the source bubble's own
|
|
48364
|
+
// timestamp (ms), so a cached summary can later be turn-scoped: the display
|
|
48365
|
+
// cache is populated from an UNSCOPED tail read (it must show the answer as
|
|
48366
|
+
// soon as native-history has it), so it can hold a bubble that predates the
|
|
48367
|
+
// current turn. Recording the bubble's timestamp lets the weak-completion
|
|
48368
|
+
// fallback reject a turn-stale cached summary instead of re-leaking the exact
|
|
48369
|
+
// stale bubble the turn-boundary gate already rejected (FALSE-IDLE Defect 1c).
|
|
48370
|
+
lastVisibleAssistantSummaryDetail(messages) {
|
|
48371
|
+
if (!Array.isArray(messages)) return { content: "" };
|
|
48244
48372
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
48245
48373
|
const m = messages[i];
|
|
48246
48374
|
const role = typeof m?.role === "string" ? m.role : "";
|
|
48247
48375
|
const kind = typeof m?.kind === "string" ? m.kind : "";
|
|
48248
48376
|
if (role === "system") continue;
|
|
48249
48377
|
if (kind === "tool" || kind === "activity") continue;
|
|
48250
|
-
if (role === "user" || role === "human") return "";
|
|
48251
|
-
if (role === "assistant")
|
|
48252
|
-
|
|
48378
|
+
if (role === "user" || role === "human") return { content: "" };
|
|
48379
|
+
if (role === "assistant") {
|
|
48380
|
+
return { content: flattenContent(m.content).trim(), timestampMs: readChatMessageTimestampMs(m) };
|
|
48381
|
+
}
|
|
48382
|
+
return { content: "" };
|
|
48253
48383
|
}
|
|
48254
|
-
return "";
|
|
48384
|
+
return { content: "" };
|
|
48255
48385
|
}
|
|
48256
48386
|
/**
|
|
48257
48387
|
* NOTIF Defect-B: the final assistant summary this instance ALREADY parsed and
|
|
@@ -48272,6 +48402,28 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48272
48402
|
const content = typeof cached3?.content === "string" ? cached3.content.trim() : "";
|
|
48273
48403
|
return content;
|
|
48274
48404
|
}
|
|
48405
|
+
// FALSE-IDLE Defect 1c: turn-scoped view of the cached completion summary.
|
|
48406
|
+
// The cache is populated from an UNSCOPED tail read (lastVisibleAssistant‑
|
|
48407
|
+
// SummaryDetail) so the dashboard can show the answer the instant native-history
|
|
48408
|
+
// has it — which means it can hold a bubble that PREDATES the producing turn.
|
|
48409
|
+
// The weak-completion (missing_final_assistant) emit path falls back to the cache
|
|
48410
|
+
// for finalSummary; without turn-scoping it would re-surface the exact stale
|
|
48411
|
+
// mid-turn bubble the turn-boundary gate already rejected as evidence, freezing
|
|
48412
|
+
// that stale text as the completion's finalSummary. Consult the cache only when
|
|
48413
|
+
// its source bubble is proven in-turn (timestamp at/after turnStartedAt). When no
|
|
48414
|
+
// boundary is known (turnStartedAt falsy) or the cache carries no source timestamp
|
|
48415
|
+
// (legacy writes), behaviour is identical to the unscoped read.
|
|
48416
|
+
cachedInTurnCompletionSummaryContent(turnStartedAt) {
|
|
48417
|
+
const cached3 = this.lastCompletionSummary;
|
|
48418
|
+
const content = typeof cached3?.content === "string" ? cached3.content.trim() : "";
|
|
48419
|
+
if (!content) return "";
|
|
48420
|
+
const hasBoundary = typeof turnStartedAt === "number" && Number.isFinite(turnStartedAt) && turnStartedAt > 0;
|
|
48421
|
+
const ts2 = cached3?.sourceTimestampMs;
|
|
48422
|
+
if (hasBoundary && typeof ts2 === "number" && Number.isFinite(ts2) && ts2 < turnStartedAt) {
|
|
48423
|
+
return "";
|
|
48424
|
+
}
|
|
48425
|
+
return content;
|
|
48426
|
+
}
|
|
48275
48427
|
completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
|
|
48276
48428
|
const turnClosed = !this.hasAdapterPendingResponse();
|
|
48277
48429
|
if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
|
|
@@ -48285,9 +48437,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48285
48437
|
if (externalMessages) {
|
|
48286
48438
|
const injectedTaskGenerating = this.injectedTaskHasStartedGenerating();
|
|
48287
48439
|
const present = injectedTaskGenerating && turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt);
|
|
48288
|
-
const lastVisibleAssistant = this.
|
|
48289
|
-
if (lastVisibleAssistant) {
|
|
48290
|
-
this.lastCompletionSummary = { content: lastVisibleAssistant, receivedAt: Date.now() };
|
|
48440
|
+
const lastVisibleAssistant = this.lastVisibleAssistantSummaryDetail(externalMessages);
|
|
48441
|
+
if (lastVisibleAssistant.content) {
|
|
48442
|
+
this.lastCompletionSummary = { content: lastVisibleAssistant.content, receivedAt: Date.now(), sourceTimestampMs: lastVisibleAssistant.timestampMs };
|
|
48291
48443
|
}
|
|
48292
48444
|
return {
|
|
48293
48445
|
present,
|
|
@@ -48311,7 +48463,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48311
48463
|
const externalMessages = this.readExternalCompletionMessages();
|
|
48312
48464
|
const externalSummary = externalMessages ? extractFinalSummaryFromMessagesAfter(externalMessages, turnStartedAt) : "";
|
|
48313
48465
|
if (externalSummary) {
|
|
48314
|
-
this.lastCompletionSummary = { content: externalSummary, receivedAt: Date.now() };
|
|
48466
|
+
this.lastCompletionSummary = { content: externalSummary, receivedAt: Date.now(), sourceTimestampMs: typeof turnStartedAt === "number" ? turnStartedAt : void 0 };
|
|
48315
48467
|
return externalSummary;
|
|
48316
48468
|
}
|
|
48317
48469
|
return parsedSummary || void 0;
|
|
@@ -48326,7 +48478,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48326
48478
|
} catch (error) {
|
|
48327
48479
|
parseError = error?.message || String(error);
|
|
48328
48480
|
}
|
|
48329
|
-
const evidence = this.completionFinalAssistantEvidence(parsed?.messages);
|
|
48481
|
+
const evidence = this.completionFinalAssistantEvidence(parsed?.messages, args.pending.turnStartedAt);
|
|
48330
48482
|
if (evidence.source === "external-native") {
|
|
48331
48483
|
this.recordPendingTranscriptProbe(args.pending);
|
|
48332
48484
|
}
|
|
@@ -48335,7 +48487,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48335
48487
|
const lastVisibleRole = typeof lastVisible?.role === "string" ? lastVisible.role.trim().toLowerCase() : null;
|
|
48336
48488
|
const lastVisibleKind = typeof lastVisible?.kind === "string" ? lastVisible.kind : null;
|
|
48337
48489
|
const lastVisibleContentLength = lastVisible ? flattenContent(lastVisible.content).trim().length : 0;
|
|
48338
|
-
const cachedSummary = evidence.present ? "" : this.
|
|
48490
|
+
const cachedSummary = evidence.present ? "" : this.cachedInTurnCompletionSummaryContent(args.pending.turnStartedAt);
|
|
48339
48491
|
const creditedFromCache = !evidence.present && cachedSummary.length > 0;
|
|
48340
48492
|
const finalAssistantPresent = evidence.present || creditedFromCache;
|
|
48341
48493
|
const finalAssistantEvidenceSource = evidence.present ? evidence.source : creditedFromCache ? "cached-summary" : evidence.source;
|
|
@@ -48999,7 +49151,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48999
49151
|
// real answer (lastCompletionSummary). Fall back to the cache so the notification
|
|
49000
49152
|
// carries the summary that mesh_read_chat.summary already shows — consistent with
|
|
49001
49153
|
// completionDiagnostic.finalAssistantPresent being credited from the same cache.
|
|
49002
|
-
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) || this.
|
|
49154
|
+
finalSummary: this.completionFinalSummary(this.adapter?.getScriptParsedStatus()?.messages, pending.turnStartedAt) || this.cachedInTurnCompletionSummaryContent(pending.turnStartedAt) || (blockReason.startsWith("parsed_status:") ? "" : void 0),
|
|
49003
49155
|
completionDiagnostic
|
|
49004
49156
|
});
|
|
49005
49157
|
this.completedDebouncePending = null;
|
|
@@ -49233,8 +49385,18 @@ ${buttons.join("\n")}`;
|
|
|
49233
49385
|
this.lastAutoApprovalSignature = "";
|
|
49234
49386
|
}, 5e3);
|
|
49235
49387
|
this.recordAutoApproval(modal?.message, buttonLabel, now);
|
|
49388
|
+
this.lastAutoApproveFiredAt = now;
|
|
49236
49389
|
setTimeout(() => {
|
|
49237
|
-
this.adapter
|
|
49390
|
+
const adapter = this.adapter;
|
|
49391
|
+
if (typeof adapter.resolveModalMatched === "function") {
|
|
49392
|
+
const matched = adapter.resolveModalMatched(buttonIndex);
|
|
49393
|
+
if (!matched) {
|
|
49394
|
+
if (this.lastAutoApproveFiredAt === now) this.lastAutoApproveFiredAt = 0;
|
|
49395
|
+
LOG.warn("CLI", `[${this.type}] auto-approve resolveModal matched no button (index ${buttonIndex}) \u2014 surfacing approval to coordinator`);
|
|
49396
|
+
}
|
|
49397
|
+
} else {
|
|
49398
|
+
adapter.resolveModal?.(buttonIndex);
|
|
49399
|
+
}
|
|
49238
49400
|
}, 0);
|
|
49239
49401
|
return autoApproveActive;
|
|
49240
49402
|
}
|