@adhdev/mesh-shared 1.0.60-rc.9 → 1.0.60
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/brain-routing.d.ts +6 -8
- package/dist/git-diff.d.ts +21 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +2876 -77
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2625 -72
- package/dist/index.mjs.map +1 -1
- package/dist/interpolation.d.ts +0 -9
- package/dist/json.d.ts +4 -0
- package/dist/keyed-doc-delta.d.ts +105 -0
- package/dist/mesh-tool-names.d.ts +31 -2
- package/dist/mesh-vocabulary.d.ts +66 -0
- package/dist/mission-brief.d.ts +62 -0
- package/dist/outbound-message.d.ts +201 -0
- package/dist/path-ownership.d.ts +101 -0
- package/dist/protocol/daemon-server.d.ts +356 -0
- package/dist/protocol/dashboard-daemon-p2p.d.ts +213 -0
- package/dist/protocol/dashboard-server.d.ts +240 -0
- package/dist/protocol/dashboard-wire-version.d.ts +21 -0
- package/dist/protocol/envelope.d.ts +55 -0
- package/dist/protocol/index.d.ts +16 -0
- package/dist/session-launch.d.ts +105 -0
- package/dist/session-status.d.ts +75 -0
- package/dist/slot-proposal.d.ts +2 -28
- package/dist/turn-evidence.d.ts +434 -0
- package/dist/turn-ipc-guards.d.ts +18 -0
- package/dist/turn-ipc-mission.d.ts +250 -0
- package/dist/turn-ipc-queue.d.ts +196 -0
- package/dist/turn-ipc-stats.d.ts +72 -0
- package/dist/turn-ipc-turn.d.ts +176 -0
- package/dist/turn-ipc.d.ts +90 -0
- package/dist/types.d.ts +11 -0
- package/dist/worker-protocol.d.ts +69 -0
- package/dist/ws-protocol.d.ts +69 -41
- package/package.json +1 -1
- package/src/brain-routing.ts +9 -11
- package/src/git-diff.ts +48 -0
- package/src/index.ts +12 -1
- package/src/interpolation.ts +0 -18
- package/src/json.ts +10 -0
- package/src/keyed-doc-delta.ts +413 -0
- package/src/mesh-tool-names.ts +66 -25
- package/src/mesh-vocabulary.ts +94 -0
- package/src/mission-brief.ts +153 -0
- package/src/outbound-message.ts +319 -0
- package/src/path-ownership.ts +271 -0
- package/src/protocol/daemon-server.ts +510 -0
- package/src/protocol/dashboard-daemon-p2p.ts +273 -0
- package/src/protocol/dashboard-server.ts +340 -0
- package/src/protocol/dashboard-wire-version.ts +27 -0
- package/src/protocol/envelope.ts +75 -0
- package/src/protocol/index.ts +17 -0
- package/src/session-launch.ts +245 -0
- package/src/session-status.ts +164 -0
- package/src/slot-proposal.ts +2 -48
- package/src/turn-evidence.ts +596 -0
- package/src/turn-ipc-guards.ts +61 -0
- package/src/turn-ipc-mission.ts +647 -0
- package/src/turn-ipc-queue.ts +493 -0
- package/src/turn-ipc-stats.ts +193 -0
- package/src/turn-ipc-turn.ts +453 -0
- package/src/turn-ipc.ts +137 -0
- package/src/types.ts +8 -0
- package/src/worker-protocol.ts +164 -0
- package/src/ws-protocol.ts +98 -93
- package/dist/magi.d.ts +0 -281
- package/src/magi.ts +0 -310
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turn-ipc — the turn-ledger commands: turn_observe, mesh_record, turn_cancel,
|
|
3
|
+
* operator_status, turn_query, mesh_index_query. Part of the turn-ipc wire
|
|
4
|
+
* contract (./turn-ipc.ts).
|
|
5
|
+
*/
|
|
6
|
+
import { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
7
|
+
import { type TurnEvidence, type TurnAttemptRef, type TurnOutcome, type ProjectedScalars, type TurnReason, type TurnEvidenceKind } from './turn-evidence';
|
|
8
|
+
/** Reducer verdict, mirrored from the C1 reducer's `reduce()` return shape. */
|
|
9
|
+
export declare const TURN_OBSERVE_VERDICTS: readonly ["applied", "recorded", "rejected"];
|
|
10
|
+
export type TurnObserveVerdict = typeof TURN_OBSERVE_VERDICTS[number];
|
|
11
|
+
export interface TurnObserveRequest {
|
|
12
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
13
|
+
evidence: TurnEvidence;
|
|
14
|
+
}
|
|
15
|
+
export interface TurnObserveResponse {
|
|
16
|
+
verdict: TurnObserveVerdict;
|
|
17
|
+
attemptRef: TurnAttemptRef;
|
|
18
|
+
/** Present only when the evidence committed the attempt (verdict 'applied' + a terminal-class kind). */
|
|
19
|
+
outcome?: TurnOutcome;
|
|
20
|
+
}
|
|
21
|
+
export declare function decodeTurnObserveRequest(value: unknown): TurnObserveRequest | null;
|
|
22
|
+
export declare function decodeTurnObserveResponse(value: unknown): TurnObserveResponse | null;
|
|
23
|
+
/**
|
|
24
|
+
* Structural pin of `seqscribe/mesh-event-projection.ts` `PROJECTED_PAYLOAD_KEYS`
|
|
25
|
+
* (daemon-core; this leaf cannot import it). A test in this package's test
|
|
26
|
+
* suite AND a daemon-core test each assert their list equals this one — see
|
|
27
|
+
* `mesh-shared/test/turn-ipc.test.ts` and the C-W6 report's break-once table.
|
|
28
|
+
* Adding a key here is the SAME assertion as adding it there: non-content
|
|
29
|
+
* (identifier, enum, boolean, or counter), never free text.
|
|
30
|
+
*/
|
|
31
|
+
export declare const MESH_RECORD_PAYLOAD_KEYS: readonly ["taskId", "deliveryId", "attemptId", "missionId", "checkpointId", "promptId", "providerSessionId", "targetNoteId", "nodeId", "sessionId", "providerType", "transport", "attemptedSessionId", "holderSessionId", "reason", "status", "outcome", "terminalKind", "event", "source", "intentionalStopReason", "retryable", "rebound", "forced", "fallback", "membershipRemoved", "requestedForce", "removedByRemoteDaemon", "completedViaReady", "intentional", "weak", "attempt", "attemptCount", "count"];
|
|
32
|
+
/** Maximum length of any string value in a `mesh_record` payload (mirrors `MAX_PROJECTED_STRING`). */
|
|
33
|
+
export declare const MAX_MESH_RECORD_STRING = 200;
|
|
34
|
+
export interface MeshRecordRequest {
|
|
35
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
36
|
+
meshId: string;
|
|
37
|
+
/** The original ledger `kind` string (daemon-core `MeshLedgerKind`; mesh-shared cannot import that union, so it is an identifier here). */
|
|
38
|
+
ledgerKind: string;
|
|
39
|
+
nodeId?: string;
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
taskId?: string;
|
|
42
|
+
/** Restricted to `MESH_RECORD_PAYLOAD_KEYS` — an unknown key is a decode failure, not a silently dropped field. */
|
|
43
|
+
payload: ProjectedScalars;
|
|
44
|
+
}
|
|
45
|
+
export interface MeshRecordResponse {
|
|
46
|
+
/** `mesh.<id>.events` append coordinates, for callers that need to reference the entry (e.g. a `ref`'d handoff). */
|
|
47
|
+
eventId: string;
|
|
48
|
+
seq: number;
|
|
49
|
+
}
|
|
50
|
+
/** Every payload key must be a member of `MESH_RECORD_PAYLOAD_KEYS` — the allow-list boundary at the wire. */
|
|
51
|
+
export declare function isMeshRecordPayload(value: unknown): value is ProjectedScalars;
|
|
52
|
+
export declare function decodeMeshRecordRequest(value: unknown): MeshRecordRequest | null;
|
|
53
|
+
export declare function decodeMeshRecordResponse(value: unknown): MeshRecordResponse | null;
|
|
54
|
+
export interface TurnCancelRequest {
|
|
55
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
56
|
+
/** Exactly one of the two must be present — the decoder rejects both-or-neither. */
|
|
57
|
+
attemptId?: string;
|
|
58
|
+
taskId?: string;
|
|
59
|
+
reason: TurnReason;
|
|
60
|
+
}
|
|
61
|
+
export interface TurnCancelResponse {
|
|
62
|
+
attemptRef: TurnAttemptRef;
|
|
63
|
+
verdict: TurnObserveVerdict;
|
|
64
|
+
}
|
|
65
|
+
export declare function decodeTurnCancelRequest(value: unknown): TurnCancelRequest | null;
|
|
66
|
+
export declare function decodeTurnCancelResponse(value: unknown): TurnCancelResponse | null;
|
|
67
|
+
/**
|
|
68
|
+
* Fire-and-forget operator/tool-call status record — replaces
|
|
69
|
+
* `recordMeshCoordinatorToolCall` (10 sites, `mesh_tool_call_log`) and doubles
|
|
70
|
+
* as the `operator_status` evidence kind's wire shape.
|
|
71
|
+
*
|
|
72
|
+
* NAMING NOTE (flagged for the C-W6 implementer, see report §"mismatch"):
|
|
73
|
+
* the C-W6 brief's DELIVERABLES describes this request as
|
|
74
|
+
* `{taskId, status: MeshTaskStatus, reason}`, but C1's `operator_status`
|
|
75
|
+
* evidence kind (`turn-evidence.ts` `OPERATOR_STATUSES`) is the closed
|
|
76
|
+
* 2-value set `'completed' | 'failed'` — a verdict on an operator ACTION, not
|
|
77
|
+
* a `MeshTaskStatus` (5-value queue-row status: pending/assigned/completed/
|
|
78
|
+
* failed/cancelled). This contract follows `turn-evidence.ts` because that is
|
|
79
|
+
* what the C1 reducer actually consumes for this evidence kind; using the
|
|
80
|
+
* 5-value `MeshTaskStatus` here would let this request carry `pending` or
|
|
81
|
+
* `assigned`, which the reducer has no transition for.
|
|
82
|
+
*/
|
|
83
|
+
export declare const OPERATOR_STATUS_VALUES: readonly ["completed", "failed"];
|
|
84
|
+
export type OperatorStatusValue = typeof OPERATOR_STATUS_VALUES[number];
|
|
85
|
+
export declare const OPERATOR_STATUS_REASON_VALUES: readonly ["operator_update", "refine_terminal", "validation_terminal"];
|
|
86
|
+
export type OperatorStatusReasonValue = typeof OPERATOR_STATUS_REASON_VALUES[number];
|
|
87
|
+
export interface OperatorStatusRequest {
|
|
88
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
89
|
+
taskId: string;
|
|
90
|
+
status: OperatorStatusValue;
|
|
91
|
+
reason: OperatorStatusReasonValue;
|
|
92
|
+
}
|
|
93
|
+
/** Acknowledgement only — this command is fire-and-forget by design (the daemon owns rate-limiting the underlying log). */
|
|
94
|
+
export interface OperatorStatusResponse {
|
|
95
|
+
accepted: true;
|
|
96
|
+
}
|
|
97
|
+
export declare function decodeOperatorStatusRequest(value: unknown): OperatorStatusRequest | null;
|
|
98
|
+
export declare function decodeOperatorStatusResponse(value: unknown): OperatorStatusResponse | null;
|
|
99
|
+
/** Row shape returned by `turn_query` — scalar projection of a `turn_attempts` row (C3 schema), never `data_json`/`payload_json` content. */
|
|
100
|
+
export interface TurnQueryAttemptRow {
|
|
101
|
+
attemptId: string;
|
|
102
|
+
generation: number;
|
|
103
|
+
meshId?: string;
|
|
104
|
+
taskId?: string;
|
|
105
|
+
sessionId: string;
|
|
106
|
+
nodeId?: string;
|
|
107
|
+
providerType?: string;
|
|
108
|
+
state: string;
|
|
109
|
+
terminalOutcome?: TurnOutcome;
|
|
110
|
+
terminalReason?: TurnReason;
|
|
111
|
+
acceptedAt: number;
|
|
112
|
+
terminalAt?: number;
|
|
113
|
+
}
|
|
114
|
+
/** Row shape returned by `turn_query` for the event stream — scalar projection of a `turn_events` row, `payload_json` NEVER included. */
|
|
115
|
+
export interface TurnQueryEventRow {
|
|
116
|
+
eventId: string;
|
|
117
|
+
attemptId?: string;
|
|
118
|
+
generation?: number;
|
|
119
|
+
sessionId: string;
|
|
120
|
+
kind: TurnEvidenceKind;
|
|
121
|
+
source: string;
|
|
122
|
+
verdict: TurnObserveVerdict | 'forwarded';
|
|
123
|
+
atMs: number;
|
|
124
|
+
}
|
|
125
|
+
export interface TurnQueryRequest {
|
|
126
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
127
|
+
meshId: string;
|
|
128
|
+
taskId?: string;
|
|
129
|
+
attemptId?: string;
|
|
130
|
+
sessionId?: string;
|
|
131
|
+
state?: string;
|
|
132
|
+
since?: number;
|
|
133
|
+
tail?: number;
|
|
134
|
+
}
|
|
135
|
+
export interface TurnQueryResponse {
|
|
136
|
+
attempts: readonly TurnQueryAttemptRow[];
|
|
137
|
+
events: readonly TurnQueryEventRow[];
|
|
138
|
+
/** C7-5: set while `mesh.<id>.events` replication is behind for this mesh — the caller must not treat a fleet-scope answer as complete. */
|
|
139
|
+
replicationPending?: boolean;
|
|
140
|
+
}
|
|
141
|
+
export declare function decodeTurnQueryRequest(value: unknown): TurnQueryRequest | null;
|
|
142
|
+
export declare function decodeTurnQueryResponse(value: unknown): TurnQueryResponse | null;
|
|
143
|
+
export declare const MESH_INDEX_QUERY_WRITER_SCOPES: readonly ["own", "fleet"];
|
|
144
|
+
export type MeshIndexQueryWriterScope = typeof MESH_INDEX_QUERY_WRITER_SCOPES[number];
|
|
145
|
+
export interface MeshIndexQueryRequest {
|
|
146
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
147
|
+
meshId: string;
|
|
148
|
+
ledgerKind?: string;
|
|
149
|
+
sessionId?: string;
|
|
150
|
+
taskId?: string;
|
|
151
|
+
writer?: MeshIndexQueryWriterScope;
|
|
152
|
+
since?: number;
|
|
153
|
+
tail?: number;
|
|
154
|
+
}
|
|
155
|
+
/** Row shape returned by `mesh_index_query` — a `mesh_topic_index` row (C3 schema). */
|
|
156
|
+
export interface MeshTopicIndexRow {
|
|
157
|
+
writer: string;
|
|
158
|
+
seq: number;
|
|
159
|
+
meshId: string;
|
|
160
|
+
eventId: string;
|
|
161
|
+
kind: string;
|
|
162
|
+
ledgerKind?: string;
|
|
163
|
+
taskId?: string;
|
|
164
|
+
sessionId?: string;
|
|
165
|
+
nodeId?: string;
|
|
166
|
+
atMs: number;
|
|
167
|
+
payload: ProjectedScalars;
|
|
168
|
+
}
|
|
169
|
+
export interface MeshIndexQueryResponse {
|
|
170
|
+
rows: readonly MeshTopicIndexRow[];
|
|
171
|
+
/** C7-5: true while `mesh.<id>.events` replication is behind for this mesh. */
|
|
172
|
+
replicationPending?: boolean;
|
|
173
|
+
}
|
|
174
|
+
export declare function decodeMeshIndexQueryRequest(value: unknown): MeshIndexQueryRequest | null;
|
|
175
|
+
export declare function isMeshIndexQueryResponse(value: unknown): value is MeshIndexQueryResponse;
|
|
176
|
+
export declare function decodeMeshIndexQueryResponse(value: unknown): MeshIndexQueryResponse | null;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turn-ipc — typed IPC command contracts for the six commands that replace
|
|
3
|
+
* mcp-server's direct `mesh-runtime.db` access (design §5 C2 "MCP server").
|
|
4
|
+
*
|
|
5
|
+
* Wiring-unification Phase C, workstream C-W6
|
|
6
|
+
* (docs/design/2026-09-23-wiring-unification.md §5 C2/C3, C9 row C-W6).
|
|
7
|
+
*
|
|
8
|
+
* WHY THIS FILE EXISTS
|
|
9
|
+
* ---------------------
|
|
10
|
+
* mcp-server is a second, in-process writer of `mesh-runtime.db` (~45-69
|
|
11
|
+
* direct-mutation call sites, ~54 reads, per the C-W6 brief). C2 moves every
|
|
12
|
+
* one of those call sites behind IPC, executed in the daemon that owns the
|
|
13
|
+
* turn ledger and the seqscribe node, so mcp-server never opens the SQLite
|
|
14
|
+
* file itself. This file is the WIRE CONTRACT for that IPC surface — request/
|
|
15
|
+
* response shapes, closed enums, runtime decoders — shared by the daemon-core
|
|
16
|
+
* command handlers (added with C-W6 proper; NOT this file) and the mcp-server
|
|
17
|
+
* client (`mcp-server/src/ipc/turn-commands.ts`, this workstream).
|
|
18
|
+
*
|
|
19
|
+
* The six commands (§5 C2):
|
|
20
|
+
* - turn_observe — submit one TurnEvidence; returns the reducer verdict.
|
|
21
|
+
* - mesh_record — append a content-free scalar record (`mesh.record`
|
|
22
|
+
* topic entry), replacing `appendLedgerEntry`.
|
|
23
|
+
* - turn_cancel — cancel an attempt (`cancel` evidence, source `mcp_probe`
|
|
24
|
+
* or `operator`).
|
|
25
|
+
* - operator_status — record a fire-and-forget operator/tool-call status
|
|
26
|
+
* (`operator_status` evidence + the former
|
|
27
|
+
* `recordMeshCoordinatorToolCall` log write).
|
|
28
|
+
* - turn_query — read attempt/event rows (own or fleet scope).
|
|
29
|
+
* - mesh_index_query — read `mesh_topic_index` rows (fleet-wide, replacing
|
|
30
|
+
* the in-memory read model / parity reads).
|
|
31
|
+
*
|
|
32
|
+
* CONTENT BOUNDARY
|
|
33
|
+
* -----------------
|
|
34
|
+
* Every request/response field is an identifier, a closed enum, a boolean, a
|
|
35
|
+
* counter or a timestamp. `turn_observe` carries a `TurnEvidence`, which is
|
|
36
|
+
* content-free by construction (`turn-evidence.ts`). `mesh_record` carries a
|
|
37
|
+
* `ProjectedScalars` payload restricted to the SAME allow-list keys
|
|
38
|
+
* `seqscribe/mesh-event-projection.ts` already reviews and ships
|
|
39
|
+
* (`PROJECTED_PAYLOAD_KEYS`, 35 keys) — this file pins a STRUCTURAL COPY of
|
|
40
|
+
* that key list (`MESH_RECORD_PAYLOAD_KEYS`) so mesh-shared does not import
|
|
41
|
+
* daemon-core (leaf constraint), with a test asserting the two lists agree.
|
|
42
|
+
* Summaries, report bodies, progress notes and modal text never travel in any
|
|
43
|
+
* of these payloads — they go to the content-class `mesh.<id>.handoff` topic
|
|
44
|
+
* and are referenced by `SummaryRef {topic, writer, seq}` (already declared
|
|
45
|
+
* in `./turn-evidence.ts`).
|
|
46
|
+
*
|
|
47
|
+
* VERSIONING
|
|
48
|
+
* -----------
|
|
49
|
+
* Every request carries `v: 1` so a future incompatible change can be
|
|
50
|
+
* detected by the responder before it tries to interpret the args.
|
|
51
|
+
*
|
|
52
|
+
* LAYOUT
|
|
53
|
+
* -------
|
|
54
|
+
* This module holds the error codes and the command registry; the per-command
|
|
55
|
+
* contracts live in ./turn-ipc-{turn,mission,queue,stats}.ts (re-exported here)
|
|
56
|
+
* over the shared guard primitives in ./turn-ipc-guards.ts.
|
|
57
|
+
*/
|
|
58
|
+
export { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
59
|
+
export * from './turn-ipc-turn';
|
|
60
|
+
export * from './turn-ipc-mission';
|
|
61
|
+
export * from './turn-ipc-queue';
|
|
62
|
+
export * from './turn-ipc-stats';
|
|
63
|
+
/**
|
|
64
|
+
* Closed error-code union every command response may carry instead of (or in
|
|
65
|
+
* addition to, for the client-side wrapper) a result.
|
|
66
|
+
*
|
|
67
|
+
* daemon_required — no daemon reachable (no IPC/local transport
|
|
68
|
+
* connection could be established). Distinct from
|
|
69
|
+
* every other failure: it means "there is nobody to
|
|
70
|
+
* ask", not "the ask failed."
|
|
71
|
+
* turn_ledger_unavailable — a daemon answered but its turn ledger is not
|
|
72
|
+
* armed yet (mid-boot, mid-migration).
|
|
73
|
+
* ledger_not_owner — the daemon that answered does not own the
|
|
74
|
+
* attempt/mesh addressed by the request (routing
|
|
75
|
+
* mistake, or the owner changed after a reclaim).
|
|
76
|
+
*/
|
|
77
|
+
export declare const TURN_IPC_ERROR_CODES: readonly ["daemon_required", "turn_ledger_unavailable", "ledger_not_owner"];
|
|
78
|
+
export type TurnIpcErrorCode = typeof TURN_IPC_ERROR_CODES[number];
|
|
79
|
+
export declare const isTurnIpcErrorCode: (value: unknown) => value is "daemon_required" | "turn_ledger_unavailable" | "ledger_not_owner";
|
|
80
|
+
/** Structured failure shape a responder returns instead of a result. Never thrown across the wire — the client maps it to a typed error (see mcp-server/src/ipc/turn-commands.ts). */
|
|
81
|
+
export interface TurnIpcError {
|
|
82
|
+
code: TurnIpcErrorCode;
|
|
83
|
+
/** Short machine-oriented detail, e.g. which daemon was expected. Never free text describing content. */
|
|
84
|
+
detail?: string;
|
|
85
|
+
}
|
|
86
|
+
export declare function isTurnIpcError(value: unknown): value is TurnIpcError;
|
|
87
|
+
/** The complete, closed set of IPC command names this contract defines. */
|
|
88
|
+
export declare const TURN_IPC_COMMANDS: readonly ["turn_observe", "mesh_record", "turn_cancel", "operator_status", "turn_query", "mesh_index_query", "mission_upsert", "mission_query", "note_upsert", "note_forget", "tool_call_record", "ledger_query", "mission_list_query", "record_local", "queue_query", "queue_enqueue", "queue_enqueue_batch", "queue_cancel", "queue_requeue", "direct_dispatch_record", "active_work_query", "recovery_context_query", "task_stats_query", "prune_stale_direct", "orphaned_pin_notify"];
|
|
89
|
+
export type TurnIpcCommand = typeof TURN_IPC_COMMANDS[number];
|
|
90
|
+
export declare const isTurnIpcCommand: (value: unknown) => value is "prune_stale_direct" | "operator_status" | "turn_observe" | "mesh_record" | "turn_cancel" | "turn_query" | "mesh_index_query" | "mission_upsert" | "mission_query" | "note_upsert" | "note_forget" | "tool_call_record" | "ledger_query" | "mission_list_query" | "record_local" | "queue_query" | "queue_enqueue" | "queue_enqueue_batch" | "queue_cancel" | "queue_requeue" | "direct_dispatch_record" | "active_work_query" | "recovery_context_query" | "task_stats_query" | "orphaned_pin_notify";
|
package/dist/types.d.ts
CHANGED
|
@@ -136,4 +136,15 @@ export interface RepoMeshSessionStatus {
|
|
|
136
136
|
startedAt?: string | null;
|
|
137
137
|
lastActivityAt?: string | null;
|
|
138
138
|
isCached?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Worker MCP delivery outcome for this session's launch (present only for a
|
|
141
|
+
* mesh-delegated launch). `reason` is a short enum code, never free text —
|
|
142
|
+
* see `WORKER_MCP_DELIVERY_REASONS` (daemon-core's `mesh/worker-mcp-isolation.ts`,
|
|
143
|
+
* the source of the vocabulary this field's values are drawn from; not
|
|
144
|
+
* re-exported here so mesh-shared stays a dependency-free leaf).
|
|
145
|
+
*/
|
|
146
|
+
workerMcp?: {
|
|
147
|
+
delivered: boolean;
|
|
148
|
+
reason?: string;
|
|
149
|
+
};
|
|
139
150
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worker-protocol — what a delegated worker is told, and what it may call.
|
|
3
|
+
*
|
|
4
|
+
* Wiring-unification Phase F1 (docs/design/2026-09-23-wiring-unification.md §7b).
|
|
5
|
+
*
|
|
6
|
+
* Measured 2026-09-23 on the live preview ledger: 635 worker attempts in 14
|
|
7
|
+
* days, 3 `report_completion` calls. The worker MCP was delivered to every
|
|
8
|
+
* provider and never used, because nothing ever told the worker it existed —
|
|
9
|
+
* the dispatched task body carried no instruction and the coordinator prompt
|
|
10
|
+
* did not mention the worker tools at all. The only text a worker saw about
|
|
11
|
+
* its tools was the tool description.
|
|
12
|
+
*
|
|
13
|
+
* This module is the single source for BOTH sides of that contract:
|
|
14
|
+
* - `WORKER_TOOLS` — exactly what `adhdev mcp --mode worker` advertises
|
|
15
|
+
* (a parity test in mcp-server pins ListTools to this tuple);
|
|
16
|
+
* - `renderWorkerProtocolFooter()` — the block appended to EVERY dispatched
|
|
17
|
+
* task body, so a worker learns the protocol from the task itself;
|
|
18
|
+
* - `renderCoordinatorWorkerSection()` — the matching paragraph the
|
|
19
|
+
* coordinator prompt carries, so the coordinator expects structured reports
|
|
20
|
+
* and stops polling for them.
|
|
21
|
+
*
|
|
22
|
+
* Wiring-unification Phase H2 adds an optional mission-brief block: when a
|
|
23
|
+
* dispatched task belongs to a mission that carries a `MissionBrief`
|
|
24
|
+
* (`./mission-brief.ts`), `renderWorkerProtocolFooter` renders it ABOVE the
|
|
25
|
+
* `WORKER_PROTOCOL_FOOTER_MARKER` line. This is deliberate placement, not an
|
|
26
|
+
* accident of insertion order: `hasWorkerProtocolFooter` / `stripWorkerProtocolFooter`
|
|
27
|
+
* both key off the marker line alone, so a body with a brief block strips
|
|
28
|
+
* identically to one without — the brief is authored content from the
|
|
29
|
+
* coordinator's point of view (like the task message itself), never part of
|
|
30
|
+
* the protocol contract the marker demarcates.
|
|
31
|
+
*/
|
|
32
|
+
import { type MissionBrief } from './mission-brief';
|
|
33
|
+
export declare const WORKER_TOOLS: readonly ["report_completion", "progress_update", "peer_context_pull", "git_status", "git_log", "git_diff"];
|
|
34
|
+
export type WorkerTool = typeof WORKER_TOOLS[number];
|
|
35
|
+
export declare function isWorkerTool(value: unknown): value is WorkerTool;
|
|
36
|
+
/** Stable marker line so a body is never footered twice and tests can find it. */
|
|
37
|
+
export declare const WORKER_PROTOCOL_FOOTER_MARKER = "--- adhdev worker protocol ---";
|
|
38
|
+
export interface WorkerProtocolFooterInput {
|
|
39
|
+
taskId?: string;
|
|
40
|
+
taskMode?: string;
|
|
41
|
+
difficulty?: string;
|
|
42
|
+
readonly?: boolean;
|
|
43
|
+
/** Number of handoff notes enclosed above the footer, when any. */
|
|
44
|
+
enclosedHandoffNotes?: number;
|
|
45
|
+
/** H2: the owning mission's brief, when this task belongs to one. Rendered above the marker line — see module doc. */
|
|
46
|
+
missionBrief?: MissionBrief;
|
|
47
|
+
}
|
|
48
|
+
export declare function hasWorkerProtocolFooter(body: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The protocol block appended to a dispatched task. Written for the model that
|
|
51
|
+
* receives it: imperative, short, and explicit about what is NOT available so
|
|
52
|
+
* the worker does not go looking for coordinator tools.
|
|
53
|
+
*/
|
|
54
|
+
export declare function renderWorkerProtocolFooter(input?: WorkerProtocolFooterInput): string;
|
|
55
|
+
/**
|
|
56
|
+
* The authored text of a dispatched body: everything before the footer marker.
|
|
57
|
+
* Ledger descriptors, replay fixtures and tests compare authored text, never
|
|
58
|
+
* the protocol block.
|
|
59
|
+
*/
|
|
60
|
+
export declare function stripWorkerProtocolFooter(body: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Append the footer to a body that does not carry one yet. Idempotent.
|
|
63
|
+
*/
|
|
64
|
+
export declare function appendWorkerProtocolFooter(body: string, input?: WorkerProtocolFooterInput): string;
|
|
65
|
+
/**
|
|
66
|
+
* The paragraph the COORDINATOR prompt carries about its workers. Kept next to
|
|
67
|
+
* the footer so the two halves of the protocol cannot drift apart.
|
|
68
|
+
*/
|
|
69
|
+
export declare function renderCoordinatorWorkerSection(): string;
|
package/dist/ws-protocol.d.ts
CHANGED
|
@@ -1,50 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ws-protocol —
|
|
3
|
-
*
|
|
2
|
+
* ws-protocol — name-only aliases for the daemon↔server WS surface and the
|
|
3
|
+
* daemon↔dashboard P2P DataChannel surface, plus the `auth_ok.payload.limits`
|
|
4
|
+
* contract.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* ServerToDaemon command set; it should adopt these unions and extend them
|
|
16
|
-
* (`ServerToDaemonMsg | <proprietary extras>`) rather than re-declaring the
|
|
17
|
-
* shared members. Members here are the ones OSS daemon-core itself produces
|
|
18
|
-
* or matches.
|
|
6
|
+
* Wiring-unification Phase A2: the typed frame unions now live in ./protocol
|
|
7
|
+
* (one file per link, payloads included). This file keeps the symbols that
|
|
8
|
+
* pre-dated them — `DaemonToServerWsMsg`, `ServerToDaemonWsMsg`,
|
|
9
|
+
* `P2PSignalingWsMsg`, `DashboardP2PMessageKind` and the two name arrays —
|
|
10
|
+
* DERIVED from the protocol unions rather than re-declared, so they cannot
|
|
11
|
+
* drift again. Before this derivation the list here said `offer|answer|ice`
|
|
12
|
+
* while the wire said `p2p_offer|p2p_answer|p2p_ice`, declared a `log` frame
|
|
13
|
+
* nothing sent or handled, omitted `daemon_mesh_command` (sent by a raw
|
|
14
|
+
* `ws.send`), and listed 12 of the ~27 P2P kinds — and nothing imported the
|
|
15
|
+
* drifted members, so nothing noticed.
|
|
19
16
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
import { type DaemonToServerType, type ServerToDaemonControlType, type P2PSignalType, type MeshP2PSignalType, type DashboardToDaemonP2PType, type DaemonToDashboardP2PType } from './protocol';
|
|
18
|
+
/** Messages the daemon sends UP to the Workers server over the WS bridge (see protocol/daemon-server.ts). */
|
|
19
|
+
export type DaemonToServerWsMsg = DaemonToServerType;
|
|
22
20
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* wake itself: `DaemonConnectionDO` has neither an alarm nor a timer, and
|
|
27
|
-
* adding one would hit the most request-quota-pressured axis in the system.
|
|
21
|
+
* Server→daemon CONTROL messages (see protocol/daemon-server.ts). The server
|
|
22
|
+
* additionally relays an open command namespace as the frame `type`
|
|
23
|
+
* (`ServerDirectCommandMsg`), which is deliberately not a member here.
|
|
28
24
|
*/
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
export type
|
|
25
|
+
export type ServerToDaemonWsMsg = ServerToDaemonControlType;
|
|
26
|
+
/** P2P signaling relayed through the server WS — the `p2p_`/`mesh_p2p_` spellings the wire actually uses. */
|
|
27
|
+
export type P2PSignalingWsMsg = P2PSignalType | MeshP2PSignalType;
|
|
28
|
+
/** Every dashboard↔daemon P2P DataChannel JSON kind, both directions (see protocol/dashboard-daemon-p2p.ts). */
|
|
29
|
+
export type DashboardP2PMessageKind = DashboardToDaemonP2PType | DaemonToDashboardP2PType;
|
|
30
|
+
export declare const DAEMON_TO_SERVER_WS_MSGS: readonly DaemonToServerWsMsg[];
|
|
31
|
+
export declare const SERVER_TO_DAEMON_WS_MSGS: readonly ServerToDaemonWsMsg[];
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* `auth_ok.payload.limits` — the plan-limit contract the server sends DOWN to
|
|
34
|
+
* the daemon for client-side enforcement.
|
|
35
|
+
*
|
|
36
|
+
* WHY THIS EXISTS: the wire spelling is `maxP2Pconnections` (lowercase `c`),
|
|
37
|
+
* inherited from `PlanLimits` in the proprietary server's plan-limits.ts. The
|
|
38
|
+
* daemon-cloud consumer independently declared the same field as
|
|
39
|
+
* `maxP2PConnections` (capital `C`) and assigned it from a `payload as any`
|
|
40
|
+
* cast — so the two spellings NEVER met under a type, TypeScript had nothing
|
|
41
|
+
* to compare, and the limit arrived as `undefined` at every comparison site.
|
|
42
|
+
* `undefined !== -1` passes the "is it unlimited?" guard, then every
|
|
43
|
+
* `count >= undefined` is false: enforcement silently degraded to unlimited on
|
|
44
|
+
* all plans, in production, for months.
|
|
45
|
+
*
|
|
46
|
+
* The lesson is NOT "pick a spelling" — it is that a cross-package wire
|
|
47
|
+
* contract asserted by hand on both ends with an `any` in between cannot fail
|
|
48
|
+
* loudly. Both producer (server) and consumer (daemon-cloud) must now import
|
|
49
|
+
* THIS symbol, so a rename breaks the build on both sides instead of silently
|
|
50
|
+
* disabling a paid-plan limit. Use `normalizeAuthOkLimits()` at the parse
|
|
51
|
+
* boundary; never re-spell these fields locally.
|
|
37
52
|
*/
|
|
38
|
-
|
|
39
|
-
/** P2P
|
|
40
|
-
|
|
53
|
+
export interface AuthOkPlanLimits {
|
|
54
|
+
/** P2P concurrent connection count (-1 = unlimited). WIRE SPELLING — lowercase `c`. */
|
|
55
|
+
maxP2Pconnections: number;
|
|
56
|
+
/** Screenshot send interval floor (seconds). 0 = real-time. */
|
|
57
|
+
screenshotIntervalSeconds: number;
|
|
58
|
+
/** Daily screenshot usage budget (minutes). -1 = unlimited. */
|
|
59
|
+
dailyScreenshotMinutes: number;
|
|
60
|
+
/** Max connectable machines/daemons (-1 = unlimited). */
|
|
61
|
+
maxMachines: number;
|
|
62
|
+
}
|
|
41
63
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* `
|
|
64
|
+
* Parse-boundary normalizer for `auth_ok.payload.limits`.
|
|
65
|
+
*
|
|
66
|
+
* Returns `null` when the payload carries no usable limits object, so callers
|
|
67
|
+
* keep their existing "no limits known yet" branch. Individual non-numeric or
|
|
68
|
+
* missing fields fall back to the UNLIMITED sentinel (-1) for count-style
|
|
69
|
+
* limits and 0 for the interval floor, matching how each consumer already
|
|
70
|
+
* treats "no constraint" — a malformed field must never be coerced to a
|
|
71
|
+
* MORE restrictive value than the server intended, or a server-side schema
|
|
72
|
+
* change would start throttling paying users.
|
|
73
|
+
*
|
|
74
|
+
* Accepts the capital-`C` misspelling as an input alias ONLY so a daemon
|
|
75
|
+
* talking to a mixed-version fleet cannot regress; output is always the
|
|
76
|
+
* canonical wire spelling.
|
|
45
77
|
*/
|
|
46
|
-
export
|
|
47
|
-
export declare const DAEMON_TO_SERVER_WS_MSGS: readonly DaemonToServerWsMsg[];
|
|
48
|
-
export declare const SERVER_TO_DAEMON_WS_MSGS: readonly ServerToDaemonWsMsg[];
|
|
49
|
-
export declare function isDaemonToServerWsMsg(value: unknown): value is DaemonToServerWsMsg;
|
|
50
|
-
export declare function isServerToDaemonWsMsg(value: unknown): value is ServerToDaemonWsMsg;
|
|
78
|
+
export declare function normalizeAuthOkLimits(raw: unknown): AuthOkPlanLimits | null;
|
package/package.json
CHANGED
package/src/brain-routing.ts
CHANGED
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
* per-difficulty "brain" preset resolves that into a concrete provider / model /
|
|
6
6
|
* thinking-level for the launched session. The goal is token economy: an `easy`
|
|
7
7
|
* task runs on a cheaper model at low reasoning effort; a `difficult` task gets a
|
|
8
|
-
* stronger model at high effort. This
|
|
9
|
-
* (
|
|
10
|
-
* *executes* a task — but it reuses the same slot shape (provider/model) and the
|
|
11
|
-
* same machine-local storage (`~/.adhdev/meshes.json`).
|
|
8
|
+
* stronger model at high effort. This picks the single brain that *executes* a
|
|
9
|
+
* task, stored machine-local (`~/.adhdev/meshes.json`).
|
|
12
10
|
*/
|
|
13
11
|
|
|
14
12
|
/** The fixed difficulty axis the coordinator classifies a task into. */
|
|
@@ -33,8 +31,8 @@ export interface BrainSlot {
|
|
|
33
31
|
|
|
34
32
|
/**
|
|
35
33
|
* Per-difficulty brain bindings for ONE mesh, stored machine-local in
|
|
36
|
-
* `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].difficultyBrains
|
|
37
|
-
*
|
|
34
|
+
* `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].difficultyBrains`).
|
|
35
|
+
* The scope is per mesh: two meshes on the same
|
|
38
36
|
* machine hold independent presets, so one can pin `difficult` to a cheaper model
|
|
39
37
|
* without changing what any other mesh runs. (It formerly sat at the config root
|
|
40
38
|
* keyed by difficulty alone — and since this map picks the MODEL a task runs on,
|
|
@@ -66,7 +64,7 @@ export function isMeshTaskDifficulty(value: unknown): value is MeshTaskDifficult
|
|
|
66
64
|
* a value that should never have been stamped provider-blind.
|
|
67
65
|
*
|
|
68
66
|
* 2. IT FOUGHT THE SLOTS. Operators express model choice per node via
|
|
69
|
-
* capability slots (`
|
|
67
|
+
* capability slots (`mesh_node_slots` action "set"), which know the provider. A
|
|
70
68
|
* mesh-wide preset that also picks a model duplicated that decision from a
|
|
71
69
|
* position with strictly less information, and needed the modelSource
|
|
72
70
|
* ('explicit' vs 'preset') precedence machinery plus a fail-closed
|
|
@@ -112,14 +110,14 @@ export function normalizeDifficultyBrainMap(raw: unknown): DifficultyBrainMap {
|
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
-
// Node capability slots (
|
|
113
|
+
// Node capability slots (node capability slots design, 2026-07-09)
|
|
116
114
|
//
|
|
117
115
|
// A node's "Preferred AI tools" list is redefined as an ordered array of
|
|
118
116
|
// capability slots. Each slot bundles what used to be scattered across
|
|
119
117
|
// providerPriority (order), a per-provider maxParallel cap, and the
|
|
120
118
|
// the owning mesh's difficultyBrains (difficulty → model/thinking). Slot order =
|
|
121
|
-
// preference. This single profile is the source of truth for task routing
|
|
122
|
-
//
|
|
119
|
+
// preference. This single profile is the source of truth for task routing and
|
|
120
|
+
// orchestrator-proposed edits.
|
|
123
121
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
122
|
|
|
125
123
|
/**
|
|
@@ -252,7 +250,7 @@ export function deriveSlotsFromLegacy(input: {
|
|
|
252
250
|
* The reverse direction of deriveSlotsFromLegacy: derive the legacy
|
|
253
251
|
* `providerPriority` order from capability slots — each slot's provider, in
|
|
254
252
|
* first-appearance order, de-duplicated. Slot order = preference is the settled
|
|
255
|
-
* slots semantics (
|
|
253
|
+
* slots semantics (node capability slots design, 2026-07-09), so this is what readers of the
|
|
256
254
|
* legacy `policy.providerPriority` field must fall back to when a node declares
|
|
257
255
|
* slots but no explicit providerPriority (otherwise such a node reads as
|
|
258
256
|
* unlaunchable even though its slots fully determine the preference order).
|
package/src/git-diff.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection of a daemon `git_diff_file` answer into the per-file row the MCP
|
|
3
|
+
* `git_diff` tool and the cloud git-diff shortcut both return: the diff text
|
|
4
|
+
* capped at `maxLines` (with a visible truncation marker), plus the file's
|
|
5
|
+
* change metadata from the diff summary.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readRecord } from './json'
|
|
9
|
+
|
|
10
|
+
export interface GitFileDiffRow {
|
|
11
|
+
path: string
|
|
12
|
+
old_path?: string | null
|
|
13
|
+
status?: string
|
|
14
|
+
diff: string
|
|
15
|
+
truncated: boolean
|
|
16
|
+
binary: boolean
|
|
17
|
+
error?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Cap diff text at `maxLines`; a truncated diff ends with a `... (truncated)` marker line. */
|
|
21
|
+
function truncateDiffText(text: string, maxLines: number): { diff: string; truncated: boolean } {
|
|
22
|
+
const lines = text.split('\n')
|
|
23
|
+
const truncated = lines.length > maxLines
|
|
24
|
+
return {
|
|
25
|
+
diff: truncated ? lines.slice(0, maxLines).join('\n') + '\n... (truncated)' : text,
|
|
26
|
+
truncated,
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** One changed file's row from its `git_diff_file` answer (either `{ diff: {...} }` or the bare diff record). */
|
|
31
|
+
export function projectFileDiffRow(
|
|
32
|
+
file: { path: string; oldPath?: string | null; status?: string },
|
|
33
|
+
rawResult: unknown,
|
|
34
|
+
maxLines: number,
|
|
35
|
+
): GitFileDiffRow {
|
|
36
|
+
const raw = readRecord(rawResult)
|
|
37
|
+
const d = readRecord(raw.diff ?? rawResult)
|
|
38
|
+
const text = typeof d.diff === 'string' ? d.diff : ''
|
|
39
|
+
const { diff, truncated } = truncateDiffText(text, maxLines)
|
|
40
|
+
return {
|
|
41
|
+
path: file.path,
|
|
42
|
+
old_path: file.oldPath ?? null,
|
|
43
|
+
status: file.status ?? 'M',
|
|
44
|
+
diff,
|
|
45
|
+
truncated,
|
|
46
|
+
binary: typeof d.binary === 'boolean' ? d.binary : false,
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ export * from './node-facts'
|
|
|
19
19
|
export * from './workspace-normalize'
|
|
20
20
|
export * from './daemon-normalize'
|
|
21
21
|
export * from './git-summarize'
|
|
22
|
-
export * from './
|
|
22
|
+
export * from './git-diff'
|
|
23
23
|
export * from './brain-routing'
|
|
24
24
|
export * from './slot-proposal'
|
|
25
25
|
export * from './interpolation'
|
|
@@ -29,3 +29,14 @@ export * from './rpc-chunking'
|
|
|
29
29
|
export * from './semver-compare'
|
|
30
30
|
export * from './ws-protocol'
|
|
31
31
|
export * from './interactive-prompt-constants'
|
|
32
|
+
export * from './session-status'
|
|
33
|
+
export * from './mesh-vocabulary'
|
|
34
|
+
export * from './outbound-message'
|
|
35
|
+
export * from './worker-protocol'
|
|
36
|
+
export * from './protocol'
|
|
37
|
+
export * from './turn-evidence'
|
|
38
|
+
export * from './session-launch'
|
|
39
|
+
export * from './turn-ipc'
|
|
40
|
+
export * from './path-ownership'
|
|
41
|
+
export * from './mission-brief'
|
|
42
|
+
export * from './keyed-doc-delta'
|
package/src/interpolation.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure {{key}} template interpolation shared by the mesh command paths.
|
|
3
|
-
*
|
|
4
|
-
* Hoisted from packages/daemon-cloud/src/mesh/mesh-interpolation.ts — the
|
|
5
|
-
* substitution is transport-agnostic (plain string/object work), so it belongs
|
|
6
|
-
* in the dependency-free mesh-shared leaf where standalone can reuse it too.
|
|
7
|
-
* The substitution semantics are byte-for-byte identical to the prior cloud copy.
|
|
8
|
-
*/
|
|
9
|
-
export function interpolateArgs(
|
|
10
|
-
args: Record<string, unknown>,
|
|
11
|
-
context: Record<string, unknown>,
|
|
12
|
-
): Record<string, unknown> {
|
|
13
|
-
const result: Record<string, unknown> = {};
|
|
14
|
-
for (const [k, v] of Object.entries(args)) {
|
|
15
|
-
result[k] = typeof v === 'string' ? interpolateString(v, context) : v;
|
|
16
|
-
}
|
|
17
|
-
return result;
|
|
18
|
-
}
|
|
19
1
|
|
|
20
2
|
export function interpolateString(template: string, ctx: Record<string, unknown>): string {
|
|
21
3
|
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|