@adhdev/daemon-core 0.9.82-rc.271 → 0.9.82-rc.273
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/index.js +20 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20 -17
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-missions.d.ts +10 -0
- package/dist/repo-mesh-types.d.ts +8 -0
- package/package.json +2 -2
- package/src/commands/router.ts +14 -21
- package/src/mesh/mesh-missions.ts +21 -0
- package/src/providers/cli-provider-instance.ts +23 -0
- package/src/repo-mesh-types.ts +8 -0
|
@@ -50,6 +50,16 @@ export declare function summarizeMissionTasks(meshId: string, missionId: string)
|
|
|
50
50
|
export declare function summarizeMeshMission(meshId: string, mission: MeshMissionRecord): MeshMissionSummary;
|
|
51
51
|
/** Active mission summaries for mesh_status / coordinator prompt injection. */
|
|
52
52
|
export declare function getActiveMeshMissionSummaries(meshId: string): MeshMissionSummary[];
|
|
53
|
+
/**
|
|
54
|
+
* Mission summaries for the mesh_status dashboard surface: every active/paused
|
|
55
|
+
* mission plus a capped, newest-first slice of completed/abandoned history so
|
|
56
|
+
* the dashboard can render a collapsible "history" section without unbounded
|
|
57
|
+
* payload growth. Returned newest-first within each group (active/paused first,
|
|
58
|
+
* then history), so the frontend can split on `status` directly.
|
|
59
|
+
*/
|
|
60
|
+
export declare function getMeshStatusMissionSummaries(meshId: string, options?: {
|
|
61
|
+
historyLimit?: number;
|
|
62
|
+
}): MeshMissionSummary[];
|
|
53
63
|
/**
|
|
54
64
|
* M3-3: render active missions as a prompt section for {{mission}}.
|
|
55
65
|
* Empty string when no active mission — the prompt stays byte-identical to
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* IMPORTANT: This file must remain runtime-free (types only).
|
|
12
12
|
*/
|
|
13
13
|
import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
14
|
+
import type { MeshMissionSummary } from './mesh/mesh-missions.js';
|
|
14
15
|
export interface RepoMesh {
|
|
15
16
|
id: string;
|
|
16
17
|
name: string;
|
|
@@ -337,6 +338,13 @@ export interface RepoMeshStatus {
|
|
|
337
338
|
nodes: RepoMeshNodeStatus[];
|
|
338
339
|
queue?: RepoMeshQueueStatus;
|
|
339
340
|
ledger?: RepoMeshLedgerStatus;
|
|
341
|
+
/**
|
|
342
|
+
* Mission summaries for the dashboard overview. Active/paused missions plus a
|
|
343
|
+
* capped, newest-first slice of completed/abandoned history. Omitted by older
|
|
344
|
+
* daemons — the dashboard must treat this as optional and render an empty
|
|
345
|
+
* state when absent. Split on each entry's `status` for live vs. history.
|
|
346
|
+
*/
|
|
347
|
+
missions?: MeshMissionSummary[];
|
|
340
348
|
}
|
|
341
349
|
export type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
|
|
342
350
|
import type { RepoMeshSessionStatus } from '@adhdev/mesh-shared';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.273",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.273",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
package/src/commands/router.ts
CHANGED
|
@@ -5259,27 +5259,17 @@ export class DaemonCommandRouter {
|
|
|
5259
5259
|
}
|
|
5260
5260
|
|
|
5261
5261
|
case 'launch_cli': {
|
|
5262
|
-
//
|
|
5263
|
-
//
|
|
5264
|
-
//
|
|
5265
|
-
//
|
|
5266
|
-
//
|
|
5267
|
-
//
|
|
5268
|
-
//
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
const isMeshWorkerLaunch = !!launchSettings
|
|
5274
|
-
&& (readStringValue(launchSettings.meshNodeFor) || launchSettings.launchedByCoordinator === true);
|
|
5275
|
-
const hasCoordinatorDaemonId = !!launchSettings && !!readStringValue(launchSettings.meshCoordinatorDaemonId);
|
|
5276
|
-
if (launchSettings && isMeshWorkerLaunch && !hasCoordinatorDaemonId) {
|
|
5277
|
-
try {
|
|
5278
|
-
const localDaemonId = readStringValue(loadConfig().machineId);
|
|
5279
|
-
if (localDaemonId) launchSettings.meshCoordinatorDaemonId = localDaemonId;
|
|
5280
|
-
} catch { /* best-effort — launch proceeds without the stamp */ }
|
|
5281
|
-
}
|
|
5282
|
-
}
|
|
5262
|
+
// The coordinator routing anchor (meshCoordinatorDaemonId) is stamped
|
|
5263
|
+
// upstream by mesh_launch_session, which resolves
|
|
5264
|
+
// coordinatorNode.daemonId || ctx.localDaemonId || ctx.localMachineId and
|
|
5265
|
+
// fail-closes for a remote node when none resolve. We deliberately do NOT
|
|
5266
|
+
// self-stamp this daemon's own id when the field is missing: for a
|
|
5267
|
+
// P2P-relayed remote worker launch, stamping the worker's own id would make
|
|
5268
|
+
// the self-forward gate (mesh-events-coordinator: sameDaemonId) treat the
|
|
5269
|
+
// worker as its own coordinator, suppressing the spontaneous completion-event
|
|
5270
|
+
// forward and leaving the event in the pending inbox until a read_chat
|
|
5271
|
+
// reconcile drains it. If the anchor is genuinely absent here, leave it
|
|
5272
|
+
// absent rather than poison the routing.
|
|
5283
5273
|
const launchResult = await this.deps.cliManager.handleCliCommand(cmd, args);
|
|
5284
5274
|
// Bug C fix (part 1): when launching a mesh node worker session, surface
|
|
5285
5275
|
// bootstrapPending:true if the node's worktree bootstrap is still running.
|
|
@@ -8428,6 +8418,8 @@ export class DaemonCommandRouter {
|
|
|
8428
8418
|
nodes: mesh.nodes || [],
|
|
8429
8419
|
liveSessionRecords: liveMeshSessions,
|
|
8430
8420
|
});
|
|
8421
|
+
const { getMeshStatusMissionSummaries } = await import('../mesh/mesh-missions.js');
|
|
8422
|
+
const missions = getMeshStatusMissionSummaries(meshId);
|
|
8431
8423
|
const statusResult = {
|
|
8432
8424
|
success: true,
|
|
8433
8425
|
meshId: mesh.id,
|
|
@@ -8470,6 +8462,7 @@ export class DaemonCommandRouter {
|
|
|
8470
8462
|
nodes: nodeStatuses,
|
|
8471
8463
|
queue: { tasks: queue, summary: queueSummary },
|
|
8472
8464
|
ledger: { entries: ledgerEntries, summary: ledgerSummary },
|
|
8465
|
+
...(missions.length > 0 ? { missions } : {}),
|
|
8473
8466
|
...(asyncRefineJobs.length > 0 ? { asyncRefineJobs } : {}),
|
|
8474
8467
|
...(historicalSessions ? { historicalSessions } : {}),
|
|
8475
8468
|
...(pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {}),
|
|
@@ -125,6 +125,27 @@ export function getActiveMeshMissionSummaries(meshId: string): MeshMissionSummar
|
|
|
125
125
|
return getMeshMissions(meshId, ['active']).map(mission => summarizeMeshMission(meshId, mission));
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Mission summaries for the mesh_status dashboard surface: every active/paused
|
|
130
|
+
* mission plus a capped, newest-first slice of completed/abandoned history so
|
|
131
|
+
* the dashboard can render a collapsible "history" section without unbounded
|
|
132
|
+
* payload growth. Returned newest-first within each group (active/paused first,
|
|
133
|
+
* then history), so the frontend can split on `status` directly.
|
|
134
|
+
*/
|
|
135
|
+
export function getMeshStatusMissionSummaries(
|
|
136
|
+
meshId: string,
|
|
137
|
+
options?: { historyLimit?: number },
|
|
138
|
+
): MeshMissionSummary[] {
|
|
139
|
+
const historyLimit = Math.max(0, options?.historyLimit ?? 10);
|
|
140
|
+
const all = getMeshMissions(meshId);
|
|
141
|
+
const live = all.filter(m => m.status === 'active' || m.status === 'paused');
|
|
142
|
+
const history = all
|
|
143
|
+
.filter(m => m.status === 'completed' || m.status === 'abandoned')
|
|
144
|
+
.sort((a, b) => (b.updatedAt || '').localeCompare(a.updatedAt || ''))
|
|
145
|
+
.slice(0, historyLimit);
|
|
146
|
+
return [...live, ...history].map(mission => summarizeMeshMission(meshId, mission));
|
|
147
|
+
}
|
|
148
|
+
|
|
128
149
|
/**
|
|
129
150
|
* M3-3: render active missions as a prompt section for {{mission}}.
|
|
130
151
|
* Empty string when no active mission — the prompt stays byte-identical to
|
|
@@ -1578,9 +1578,23 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1578
1578
|
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
1579
1579
|
const modal = adapterStatus.activeModal;
|
|
1580
1580
|
LOG.info('CLI', `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? 'none'}"`);
|
|
1581
|
+
// Include the FSM's approval entry seq, mirroring the auto-approve
|
|
1582
|
+
// path (maybeAutoApproveStatus) and resolveModal's sameEntryReResolve
|
|
1583
|
+
// guard. Two distinct back-to-back approvals can carry identical
|
|
1584
|
+
// message/buttons (very common with claude-cli's "Allow Bash
|
|
1585
|
+
// command?"). Without the seq their fingerprints collide and the dedup
|
|
1586
|
+
// below silently drops the second waiting_approval event — it is never
|
|
1587
|
+
// emitted, so it cannot even land in the pending inbox for a later
|
|
1588
|
+
// read_chat reconcile to recover. The seq is bumped by the FSM on every
|
|
1589
|
+
// fresh waiting_approval entry, so a new approval always yields a new
|
|
1590
|
+
// fingerprint and emits.
|
|
1591
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === 'number'
|
|
1592
|
+
? adapterStatus.approvalEntrySeq
|
|
1593
|
+
: 0;
|
|
1581
1594
|
const approvalFingerprint = JSON.stringify({
|
|
1582
1595
|
message: typeof modal?.message === 'string' ? modal.message.trim() : '',
|
|
1583
1596
|
buttons: Array.isArray(modal?.buttons) ? modal.buttons.map((button: unknown) => String(button).trim()) : [],
|
|
1597
|
+
seq: approvalEntrySeq,
|
|
1584
1598
|
});
|
|
1585
1599
|
// PTY redraws repeat the same modal content; fingerprint dedup prevents duplicate events.
|
|
1586
1600
|
// Do NOT also gate on lastStatus: consecutive approvals can arrive waiting_approval→waiting_approval
|
|
@@ -1598,6 +1612,15 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
1598
1612
|
modalButtons: modal?.buttons,
|
|
1599
1613
|
});
|
|
1600
1614
|
}
|
|
1615
|
+
} else if (newStatus === 'generating' && this.lastStatus === 'waiting_approval') {
|
|
1616
|
+
// Approval resolved and the agent resumed work. Defense-in-depth:
|
|
1617
|
+
// clear the approval emit fingerprint here too (not only on
|
|
1618
|
+
// completion at scheduleCompletedDebounceFlush). A subsequent
|
|
1619
|
+
// waiting_approval with the same modal content as the one just
|
|
1620
|
+
// resolved would otherwise collide with the stale fingerprint and be
|
|
1621
|
+
// dropped. The seq in the fingerprint already separates entries; this
|
|
1622
|
+
// reset is a belt-and-suspenders guard for the re-entry case.
|
|
1623
|
+
this.lastApprovalEventFingerprint = '';
|
|
1601
1624
|
} else if (newStatus === 'idle' && (this.lastStatus === 'generating' || this.lastStatus === 'waiting_approval')) {
|
|
1602
1625
|
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1000) : 0;
|
|
1603
1626
|
// Guard: if generatingStartedAt===0 and no debounce pending, the generating phase
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { GitRepoStatus, GitCompactSummary } from './git/git-types.js';
|
|
15
|
+
import type { MeshMissionSummary } from './mesh/mesh-missions.js';
|
|
15
16
|
|
|
16
17
|
// ─── Core Mesh Types ────────────────────────────
|
|
17
18
|
|
|
@@ -405,6 +406,13 @@ export interface RepoMeshStatus {
|
|
|
405
406
|
nodes: RepoMeshNodeStatus[];
|
|
406
407
|
queue?: RepoMeshQueueStatus;
|
|
407
408
|
ledger?: RepoMeshLedgerStatus;
|
|
409
|
+
/**
|
|
410
|
+
* Mission summaries for the dashboard overview. Active/paused missions plus a
|
|
411
|
+
* capped, newest-first slice of completed/abandoned history. Omitted by older
|
|
412
|
+
* daemons — the dashboard must treat this as optional and render an empty
|
|
413
|
+
* state when absent. Split on each entry's `status` for live vs. history.
|
|
414
|
+
*/
|
|
415
|
+
missions?: MeshMissionSummary[];
|
|
408
416
|
}
|
|
409
417
|
|
|
410
418
|
// RepoMeshSessionStatus shape now lives in @adhdev/mesh-shared (shared with
|