@adhdev/mesh-shared 1.0.60-rc.8 → 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.
Files changed (67) hide show
  1. package/dist/brain-routing.d.ts +6 -8
  2. package/dist/git-diff.d.ts +21 -0
  3. package/dist/index.d.ts +12 -1
  4. package/dist/index.js +2876 -77
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +2625 -72
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/interpolation.d.ts +0 -9
  9. package/dist/json.d.ts +4 -0
  10. package/dist/keyed-doc-delta.d.ts +105 -0
  11. package/dist/mesh-tool-names.d.ts +31 -2
  12. package/dist/mesh-vocabulary.d.ts +66 -0
  13. package/dist/mission-brief.d.ts +62 -0
  14. package/dist/outbound-message.d.ts +201 -0
  15. package/dist/path-ownership.d.ts +101 -0
  16. package/dist/protocol/daemon-server.d.ts +356 -0
  17. package/dist/protocol/dashboard-daemon-p2p.d.ts +213 -0
  18. package/dist/protocol/dashboard-server.d.ts +240 -0
  19. package/dist/protocol/dashboard-wire-version.d.ts +21 -0
  20. package/dist/protocol/envelope.d.ts +55 -0
  21. package/dist/protocol/index.d.ts +16 -0
  22. package/dist/session-launch.d.ts +105 -0
  23. package/dist/session-status.d.ts +75 -0
  24. package/dist/slot-proposal.d.ts +2 -28
  25. package/dist/turn-evidence.d.ts +434 -0
  26. package/dist/turn-ipc-guards.d.ts +18 -0
  27. package/dist/turn-ipc-mission.d.ts +250 -0
  28. package/dist/turn-ipc-queue.d.ts +196 -0
  29. package/dist/turn-ipc-stats.d.ts +72 -0
  30. package/dist/turn-ipc-turn.d.ts +176 -0
  31. package/dist/turn-ipc.d.ts +90 -0
  32. package/dist/types.d.ts +11 -0
  33. package/dist/worker-protocol.d.ts +69 -0
  34. package/dist/ws-protocol.d.ts +69 -41
  35. package/package.json +1 -1
  36. package/src/brain-routing.ts +9 -11
  37. package/src/git-diff.ts +48 -0
  38. package/src/index.ts +12 -1
  39. package/src/interpolation.ts +0 -18
  40. package/src/json.ts +10 -0
  41. package/src/keyed-doc-delta.ts +413 -0
  42. package/src/mesh-tool-names.ts +66 -25
  43. package/src/mesh-vocabulary.ts +94 -0
  44. package/src/mission-brief.ts +153 -0
  45. package/src/outbound-message.ts +319 -0
  46. package/src/path-ownership.ts +271 -0
  47. package/src/protocol/daemon-server.ts +510 -0
  48. package/src/protocol/dashboard-daemon-p2p.ts +273 -0
  49. package/src/protocol/dashboard-server.ts +340 -0
  50. package/src/protocol/dashboard-wire-version.ts +27 -0
  51. package/src/protocol/envelope.ts +75 -0
  52. package/src/protocol/index.ts +17 -0
  53. package/src/session-launch.ts +245 -0
  54. package/src/session-status.ts +164 -0
  55. package/src/slot-proposal.ts +2 -48
  56. package/src/turn-evidence.ts +596 -0
  57. package/src/turn-ipc-guards.ts +61 -0
  58. package/src/turn-ipc-mission.ts +647 -0
  59. package/src/turn-ipc-queue.ts +493 -0
  60. package/src/turn-ipc-stats.ts +193 -0
  61. package/src/turn-ipc-turn.ts +453 -0
  62. package/src/turn-ipc.ts +137 -0
  63. package/src/types.ts +8 -0
  64. package/src/worker-protocol.ts +164 -0
  65. package/src/ws-protocol.ts +98 -93
  66. package/dist/magi.d.ts +0 -281
  67. package/src/magi.ts +0 -310
@@ -0,0 +1,240 @@
1
+ /**
2
+ * protocol/dashboard-server — the dashboard ↔ UserSessionDO WebSocket link,
3
+ * plus the share-viewer ↔ SharedSessionDO link the same dashboard bundle speaks.
4
+ *
5
+ * Wiring-unification Phase A2 (docs/design/2026-09-23-wiring-unification.md §3 A2).
6
+ *
7
+ * Before this file web-cloud `ws.ts` declared its own `IncomingWSMessage`
8
+ * union and `WSEventType`, the server built every outbound frame as an object
9
+ * literal, and the two disagreed: the server's `connected` frame was missing
10
+ * from the dashboard's type, while the dashboard typed `plan_limit_error`,
11
+ * `plan_limit_warning`, `daemon_screenshot` and `agent_event` that no server
12
+ * path has emitted since the dashboard WS command relay was removed. Those four
13
+ * are gone; `connected` is in.
14
+ *
15
+ * Content boundary: this link is server WS. It carries routing metadata,
16
+ * signaling and the approved approval-modal text only (`modalMessage` /
17
+ * `modalButtons`). Chat content rides the P2P link (./dashboard-daemon-p2p.ts).
18
+ */
19
+ import { type Envelope } from './envelope';
20
+ import { type DaemonStatusEventName, type P2PIceWirePayload, type P2POfferWirePayload, type P2PStatusWireSummary, type SeqscribeStatusWireSummary, type ReleaseChannelWire, type VersionUpdatePolicyWire, type VersionUpdateReasonWire } from './daemon-server';
21
+ /** Server-originated events the dashboard receives in addition to the daemon set. */
22
+ export declare const SERVER_STATUS_EVENT_NAMES: readonly ["daemon:disconnect", "team:session_viewed", "team:view_request", "team:view_request_approved", "team:view_request_rejected"];
23
+ /** Mirror of daemon-core `DashboardStatusEventName`; pinned equal by the server at compile time. */
24
+ export declare const DASHBOARD_STATUS_EVENT_NAMES: readonly ["agent:generating_started", "agent:waiting_approval", "agent:waiting_choice", "agent:generating_completed", "agent:stopped", "monitor:no_progress", "daemon:disconnect", "team:session_viewed", "team:view_request", "team:view_request_approved", "team:view_request_rejected"];
25
+ export type DashboardStatusEventName = DaemonStatusEventName | typeof SERVER_STATUS_EVENT_NAMES[number];
26
+ export declare const isDashboardStatusEventName: (value: unknown) => value is "agent:generating_started" | "agent:waiting_approval" | "agent:waiting_choice" | "agent:generating_completed" | "agent:stopped" | "monitor:no_progress" | "daemon:disconnect" | "team:session_viewed" | "team:view_request" | "team:view_request_approved" | "team:view_request_rejected";
27
+ /**
28
+ * Sanitized status event as the server relays it (UserSession
29
+ * `buildDashboardStatusEvent`). Mirror of daemon-core
30
+ * `DashboardStatusEventPayload` minus the P2P-only structured prompt fields
31
+ * (`interactivePrompt`/`promptId`/`multiSelect`), which the WS relay never
32
+ * carries — they are agent-authored content.
33
+ */
34
+ export type DashboardStatusEventWirePayload = {
35
+ event: DashboardStatusEventName;
36
+ timestamp: number;
37
+ daemonId?: string;
38
+ providerType?: string;
39
+ targetSessionId?: string;
40
+ duration?: number;
41
+ elapsedSec?: number;
42
+ modalMessage?: string;
43
+ modalButtons?: string[];
44
+ requestId?: string;
45
+ requesterName?: string;
46
+ targetName?: string;
47
+ orgId?: string;
48
+ permission?: string;
49
+ shareUrl?: string;
50
+ shareToken?: string;
51
+ viewerName?: string;
52
+ };
53
+ /**
54
+ * Cold-start machine card. Mirror of daemon-core `DashboardBootstrapDaemonEntry`
55
+ * (which extends `Partial<CloudDaemonSummaryEntry>`), field for field, so the
56
+ * server's entry assigns in and the dashboard's daemon-core-typed consumer
57
+ * assigns out without a cast.
58
+ */
59
+ export type BootstrapDaemonWireEntry = {
60
+ id: string;
61
+ type?: string;
62
+ machineId?: string;
63
+ platform?: string;
64
+ hostname?: string;
65
+ nickname?: string;
66
+ version?: string;
67
+ p2p?: P2PStatusWireSummary;
68
+ seqscribe?: SeqscribeStatusWireSummary;
69
+ cdpConnected?: boolean;
70
+ serverVersion?: string;
71
+ releaseChannel?: ReleaseChannelWire;
72
+ updateChannel?: ReleaseChannelWire;
73
+ updatePolicy?: VersionUpdatePolicyWire;
74
+ updateCommand?: string;
75
+ versionMismatch?: boolean;
76
+ versionUpdateRequired?: boolean;
77
+ versionUpdateReason?: VersionUpdateReasonWire;
78
+ timestamp?: number;
79
+ };
80
+ export declare const SYSTEM_ANNOUNCEMENT_TYPES: readonly ["info", "warning", "critical"];
81
+ export type SystemAnnouncementTypeWire = typeof SYSTEM_ANNOUNCEMENT_TYPES[number];
82
+ /** Admin-authored banner; `null` clears it. Mirror of server `ActiveSystemAnnouncement` / web-cloud `SystemAnnouncement`. */
83
+ export type SystemAnnouncementWire = {
84
+ id: string;
85
+ message: string;
86
+ type: SystemAnnouncementTypeWire;
87
+ createdAt: string | null;
88
+ expiresAt: string | null;
89
+ };
90
+ export type DashboardBootstrapWirePayload = {
91
+ daemons: BootstrapDaemonWireEntry[];
92
+ announcement?: SystemAnnouncementWire | null;
93
+ };
94
+ export type DashboardConnectedWirePayload = {
95
+ message?: string;
96
+ peerId: string;
97
+ plan: string;
98
+ dashboardCount: number;
99
+ };
100
+ /** Daemon auth rejection relayed to the owner's dashboards — enum reason and counters only (no server-authored sentence). */
101
+ export type DashboardAuthErrorWirePayload = {
102
+ reason: string;
103
+ limit?: number;
104
+ current?: number;
105
+ };
106
+ export type SystemAnnouncementWirePayload = {
107
+ announcement: SystemAnnouncementWire | null;
108
+ };
109
+ export type DashboardP2POfferWirePayload = {
110
+ daemonId: string;
111
+ peerId?: string;
112
+ sdp: string;
113
+ type: string;
114
+ };
115
+ export type DashboardP2PIceWirePayload = {
116
+ daemonId: string;
117
+ peerId?: string;
118
+ candidate: string;
119
+ mid?: string;
120
+ sdpMid?: string | null;
121
+ sdpMLineIndex?: number | null;
122
+ };
123
+ export declare const USER_SESSION_TO_DASHBOARD_TYPES: readonly ["connected", "initial_state", "daemon_status", "status_event", "system_announcement", "auth_error", "heartbeat", "p2p_offer", "p2p_ice"];
124
+ export type UserSessionToDashboardType = typeof USER_SESSION_TO_DASHBOARD_TYPES[number];
125
+ export declare const isUserSessionToDashboardType: (value: unknown) => value is "p2p_offer" | "p2p_ice" | "status_event" | "auth_error" | "connected" | "initial_state" | "daemon_status" | "system_announcement" | "heartbeat";
126
+ export type DashboardConnectedMsg = Envelope<'connected', DashboardConnectedWirePayload>;
127
+ export type DashboardInitialStateMsg = Envelope<'initial_state', DashboardBootstrapWirePayload>;
128
+ export type DashboardDaemonStatusMsg = Envelope<'daemon_status', DashboardBootstrapWirePayload>;
129
+ export type DashboardStatusEventMsg = Envelope<'status_event', DashboardStatusEventWirePayload>;
130
+ export type DashboardSystemAnnouncementMsg = Envelope<'system_announcement', SystemAnnouncementWirePayload>;
131
+ export type DashboardAuthErrorMsg = Envelope<'auth_error', DashboardAuthErrorWirePayload>;
132
+ /**
133
+ * Reply to the dashboard's heartbeat. Carries the cached announcement so a
134
+ * stale banner self-corrects; the field is optional because the reply is first
135
+ * of all a liveness signal and must not be dropped by an older server's
136
+ * payload-less spelling.
137
+ */
138
+ export type DashboardHeartbeatReplyMsg = Envelope<'heartbeat', Partial<SystemAnnouncementWirePayload>>;
139
+ export type DashboardP2POfferMsg = Envelope<'p2p_offer', DashboardP2POfferWirePayload>;
140
+ export type DashboardP2PIceMsg = Envelope<'p2p_ice', DashboardP2PIceWirePayload>;
141
+ export type UserSessionToDashboardMsg = DashboardConnectedMsg | DashboardInitialStateMsg | DashboardDaemonStatusMsg | DashboardStatusEventMsg | DashboardSystemAnnouncementMsg | DashboardAuthErrorMsg | DashboardHeartbeatReplyMsg | DashboardP2POfferMsg | DashboardP2PIceMsg;
142
+ export type UserSessionToDashboardPayloadOf<T extends UserSessionToDashboardType> = Extract<UserSessionToDashboardMsg, {
143
+ type: T;
144
+ }>['payload'];
145
+ /** Parse boundary for the dashboard. A heartbeat reply is accepted without a payload (liveness first). */
146
+ export declare function decodeUserSessionToDashboard(raw: unknown): UserSessionToDashboardMsg | null;
147
+ export declare const DASHBOARD_TO_USER_SESSION_TYPES: readonly ["heartbeat", "refresh", "p2p_ready", "p2p_answer", "p2p_ice"];
148
+ export type DashboardToUserSessionType = typeof DASHBOARD_TO_USER_SESSION_TYPES[number];
149
+ export declare const isDashboardToUserSessionType: (value: unknown) => value is "p2p_ready" | "p2p_answer" | "p2p_ice" | "heartbeat" | "refresh";
150
+ /** Signals a dashboard (answerer) sends toward a daemon; `daemonId` routes, `peerId` identifies the tab. */
151
+ export declare const DASHBOARD_P2P_SIGNAL_TYPES: readonly ["p2p_ready", "p2p_answer", "p2p_ice"];
152
+ export type DashboardP2PSignalType = typeof DASHBOARD_P2P_SIGNAL_TYPES[number];
153
+ export declare const isDashboardP2PSignalType: (value: unknown) => value is "p2p_ready" | "p2p_answer" | "p2p_ice";
154
+ export type DashboardP2PReadyWirePayload = {
155
+ daemonId: string;
156
+ peerId: string;
157
+ };
158
+ export type DashboardP2PAnswerWirePayload = {
159
+ daemonId: string;
160
+ peerId: string;
161
+ sdp: string;
162
+ type: string;
163
+ };
164
+ export type DashboardP2PLocalIceWirePayload = {
165
+ daemonId: string;
166
+ peerId: string;
167
+ candidate: string;
168
+ sdpMid: string | null;
169
+ sdpMLineIndex: number | null;
170
+ };
171
+ export type DashboardHeartbeatMsg = {
172
+ type: 'heartbeat';
173
+ timestamp?: number;
174
+ };
175
+ export type DashboardRefreshMsg = {
176
+ type: 'refresh';
177
+ };
178
+ export type DashboardP2PReadyMsg = Envelope<'p2p_ready', DashboardP2PReadyWirePayload>;
179
+ export type DashboardP2PAnswerMsg = Envelope<'p2p_answer', DashboardP2PAnswerWirePayload>;
180
+ export type DashboardP2PLocalIceMsg = Envelope<'p2p_ice', DashboardP2PLocalIceWirePayload>;
181
+ export type DashboardToUserSessionMsg = DashboardHeartbeatMsg | DashboardRefreshMsg | DashboardP2PReadyMsg | DashboardP2PAnswerMsg | DashboardP2PLocalIceMsg;
182
+ export type DashboardToUserSessionPayloadOf<T extends DashboardP2PSignalType> = Extract<DashboardToUserSessionMsg, {
183
+ type: T;
184
+ }>['payload'];
185
+ /** Parse boundary for UserSessionDO. Signals need a `daemonId` to route; without one there is nothing to do. */
186
+ export declare function decodeDashboardToUserSession(raw: unknown): DashboardToUserSessionMsg | null;
187
+ export declare const SHARED_SESSION_TO_VIEWER_TYPES: readonly ["share:connected", "share:revoked", "share:expired", "share:error", "share:viewer_count", "p2p_offer", "p2p_ice"];
188
+ export type SharedSessionToViewerType = typeof SHARED_SESSION_TO_VIEWER_TYPES[number];
189
+ export declare const isSharedSessionToViewerType: (value: unknown) => value is "p2p_offer" | "p2p_ice" | "share:connected" | "share:revoked" | "share:expired" | "share:error" | "share:viewer_count";
190
+ /** Frames on this link are FLAT (no `payload`), except the relayed signals. Kept as the viewer page reads them. */
191
+ export type ShareConnectedMsg = {
192
+ type: 'share:connected';
193
+ viewerId: string;
194
+ viewerName: string;
195
+ permission: string;
196
+ mode: string;
197
+ providerType: string;
198
+ targetSessionId?: string;
199
+ ownerName: string;
200
+ daemonId: string;
201
+ iceServers: unknown[];
202
+ requiresAuth: boolean;
203
+ };
204
+ export type ShareRevokedMsg = {
205
+ type: 'share:revoked';
206
+ };
207
+ export type ShareExpiredMsg = {
208
+ type: 'share:expired';
209
+ };
210
+ export type ShareErrorMsg = {
211
+ type: 'share:error';
212
+ error: string;
213
+ };
214
+ export type ShareViewerCountMsg = {
215
+ type: 'share:viewer_count';
216
+ count: number;
217
+ maxViewers: number;
218
+ };
219
+ /**
220
+ * Daemon signal relayed to the addressed viewer, payload as the daemon sent it;
221
+ * `from` is the daemon DO id, which the viewer page folds in as `daemonId`.
222
+ */
223
+ export type ShareRelayedP2POfferMsg = {
224
+ type: 'p2p_offer';
225
+ payload: P2POfferWirePayload;
226
+ from: string;
227
+ };
228
+ export type ShareRelayedP2PIceMsg = {
229
+ type: 'p2p_ice';
230
+ payload: P2PIceWirePayload;
231
+ from: string;
232
+ };
233
+ export type SharedSessionToViewerMsg = ShareConnectedMsg | ShareRevokedMsg | ShareExpiredMsg | ShareErrorMsg | ShareViewerCountMsg | ShareRelayedP2POfferMsg | ShareRelayedP2PIceMsg;
234
+ export declare const VIEWER_TO_SHARED_SESSION_TYPES: readonly ["p2p_ready", "p2p_offer", "p2p_answer", "p2p_ice"];
235
+ export type ViewerToSharedSessionType = typeof VIEWER_TO_SHARED_SESSION_TYPES[number];
236
+ export declare const isViewerToSharedSessionType: (value: unknown) => value is "p2p_ready" | "p2p_offer" | "p2p_answer" | "p2p_ice";
237
+ /** The viewer's signal as sent; the DO stamps `daemonId`/`peerId`/`sharePermission` before relaying. */
238
+ export type ViewerToSharedSessionMsg = Envelope<ViewerToSharedSessionType, Record<string, unknown>>;
239
+ /** Parse boundary for SharedSessionDO: signaling frames only. */
240
+ export declare function decodeViewerToSharedSession(raw: unknown): ViewerToSharedSessionMsg | null;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * protocol/dashboard-wire-version — the dashboard ↔ daemon wire version.
3
+ *
4
+ * Both dashboards (cloud over P2P, standalone over WS) state the version they
5
+ * speak on every topic subscribe; the daemon states its own on the standalone
6
+ * hello, on every keyed snapshot frame, and in the `protocol_mismatch` reply it
7
+ * sends INSTEAD of state when the two differ. There is no dual-format serving:
8
+ * a mismatched pair renders no state at all —
9
+ * - the page is older than the daemon → the page must reload (its bundle
10
+ * cannot read the daemon's frames);
11
+ * - the daemon is older than the page → the daemon must be updated.
12
+ *
13
+ * Bump this whenever a frame shape changes incompatibly; one release carries
14
+ * the same value on both ends.
15
+ * 1 — implicit (frames before versioning).
16
+ * 2 — daemon.metadata deltas on the keyed-doc engine (2026-09-29).
17
+ */
18
+ export declare const DASHBOARD_WIRE_VERSION = 2;
19
+ export type DashboardWireCompat = 'ok' | 'reload_required' | 'daemon_update_required';
20
+ /** How a page speaking `pageVersion` relates to a daemon speaking `daemonVersion` (absent = 1). */
21
+ export declare function compareDashboardWireVersion(pageVersion: number, daemonVersion: number | null | undefined): DashboardWireCompat;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * protocol/envelope — helpers shared by the three link protocols.
3
+ *
4
+ * Wiring-unification Phase A2 (docs/design/2026-09-23-wiring-unification.md).
5
+ *
6
+ * Every link (daemon↔server WS, dashboard↔UserSession WS, dashboard↔daemon
7
+ * P2P DataChannel) is described the same way:
8
+ *
9
+ * - a `const` tuple of message-kind names per direction;
10
+ * - a discriminated union of `{ type, ... }` frames derived from it;
11
+ * - a `decode<Link><Direction>(raw)` parse-boundary function that checks the
12
+ * `type` is a member and the frame carries the MINIMAL shape the union
13
+ * promises (an object payload where one is required, the top-level fields
14
+ * a payload-less frame is keyed by). Semantic validation — is this session
15
+ * id known, is the SDP well formed — stays with the handler that owns it.
16
+ *
17
+ * mesh-shared is a dependency-free leaf, so payload types here are STRUCTURAL
18
+ * mirrors of the richer daemon-core types (`RoutingSessionEntry`,
19
+ * `DaemonStatusEventPayload`, ...): daemon-core, the server and the web assign
20
+ * their richer types into these, never the other way round. A type-only import
21
+ * of daemon-core would make the published `.d.ts` of this package reference a
22
+ * package that depends on it — a cycle a leaf must not create.
23
+ */
24
+ export type MessageId = string;
25
+ /** `{ type, payload }` frame with the optional correlation/ordering fields every link uses. */
26
+ export type Envelope<T extends string, P> = {
27
+ type: T;
28
+ payload: P;
29
+ id?: MessageId;
30
+ timestamp?: number;
31
+ };
32
+ /** Genuinely dynamic payloads (command arguments and results). Allowed by design; the union stays exhaustive on `type`. */
33
+ export type CommandPayload = Record<string, unknown>;
34
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
35
+ export declare function isNonEmptyString(value: unknown): value is string;
36
+ export declare function isFiniteNumber(value: unknown): value is number;
37
+ export declare function makeTypeGuard<T extends readonly string[]>(names: T): (value: unknown) => value is T[number];
38
+ /**
39
+ * Compile-time proof that a name tuple and a union have exactly the same
40
+ * members. Used as `const _: AssertSameMembers<typeof NAMES, Union> = true`.
41
+ */
42
+ export type AssertSameMembers<Tuple extends readonly string[], Union extends string> = [
43
+ Exclude<Union, Tuple[number]>,
44
+ Exclude<Tuple[number], Union>
45
+ ] extends [never, never] ? true : never;
46
+ /** Narrow a frame's `type` to a union member, or null. */
47
+ export declare function decodeTypeField<T extends string>(raw: unknown, isType: (value: unknown) => value is T): {
48
+ type: T;
49
+ frame: Record<string, unknown>;
50
+ } | null;
51
+ /** Read the optional envelope fields a frame may carry, dropping malformed ones. */
52
+ export declare function readEnvelopeFields(frame: Record<string, unknown>): {
53
+ id?: MessageId;
54
+ timestamp?: number;
55
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * protocol — typed wire contracts for the three transport links.
3
+ *
4
+ * ./daemon-server.ts daemon ↔ Workers server WS
5
+ * ./dashboard-server.ts dashboard ↔ UserSessionDO WS (+ share viewer ↔ SharedSessionDO)
6
+ * ./dashboard-daemon-p2p.ts dashboard ↔ daemon DataChannel
7
+ *
8
+ * Each exports name tuples, discriminated unions, type guards and a
9
+ * `decode<Link><Direction>(raw)` parse-boundary function. ../ws-protocol.ts
10
+ * keeps the pre-existing name-only aliases derived from these.
11
+ */
12
+ export * from './envelope';
13
+ export * from './daemon-server';
14
+ export * from './dashboard-server';
15
+ export * from './dashboard-daemon-p2p';
16
+ export * from './dashboard-wire-version';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * session-launch — how a session was launched: provider, model and thinking
3
+ * level, and WHERE each value came from.
4
+ *
5
+ * Wiring-unification Phase E (docs/design/2026-09-23-wiring-unification.md §7,
6
+ * RC5). Before this record the only trace of a session's model was a display
7
+ * string in recent-activity (`buildLegacyModelModeSummaryMetadata`), so nobody
8
+ * could answer "did I pick this model, did the dashboard restore it from last
9
+ * time, did a mesh slot choose it, or is it just the provider default?" — and
10
+ * after a daemon restart not even the value survived.
11
+ *
12
+ * The daemon owns the record (daemon-core `SessionRegistry`); this file holds the
13
+ * shape and the pure readers so daemon-core, web-core, the server and the MCP
14
+ * server all read one definition. It is a dependency-free leaf, like the rest of
15
+ * mesh-shared.
16
+ *
17
+ * ★Server content boundary (CLAUDE.md): the full record rides P2P only. The
18
+ * server path carries exactly two derived fields — `model` (an identifier,
19
+ * passed through {@link sanitizeModelIdentifier}) and `modelSource` (an enum,
20
+ * passed through {@link isModelAxisSource}). Never widen that to the record.
21
+ */
22
+ export declare const MODEL_AXIS_SOURCES: readonly ["user", "remembered", "mesh_slot", "task_override", "provider_default", "unspecified"];
23
+ export type ModelAxisSource = typeof MODEL_AXIS_SOURCES[number];
24
+ export declare function isModelAxisSource(value: unknown): value is ModelAxisSource;
25
+ /**
26
+ * Which surface started the session. `restore` means "re-attached to a hosted
27
+ * runtime that outlived a daemon restart" — the axis sources are preserved.
28
+ */
29
+ export declare const SESSION_LAUNCHED_BY: readonly ["dashboard", "mesh", "cli", "api", "restore"];
30
+ export type SessionLaunchedBy = typeof SESSION_LAUNCHED_BY[number];
31
+ export declare function isSessionLaunchedBy(value: unknown): value is SessionLaunchedBy;
32
+ /** How a value entered the axis history. */
33
+ export type ModelSelectionVia = 'launch' | 'change_model' | 'observed';
34
+ export interface ModelSelectionHistoryEntry {
35
+ at: number;
36
+ value: string;
37
+ via: ModelSelectionVia;
38
+ }
39
+ /** One axis (model, or thinking level) of a session launch. */
40
+ export interface ModelSelection {
41
+ /** What the caller asked for. Absent = nothing requested. */
42
+ requested?: string;
43
+ source: ModelAxisSource;
44
+ /**
45
+ * The value actually handed to the provider: after `modelLaunchValueMap`
46
+ * (CLI `--model` template) or the ACP `setConfigOption` call. Absent when the
47
+ * request was not applied (no template, the config call failed) or nothing
48
+ * was requested.
49
+ */
50
+ launchValue?: string;
51
+ /**
52
+ * The provider default this session is expected to run with when nothing was
53
+ * requested (`source === 'provider_default'` only): discovery `models[0]`,
54
+ * else the manifest's first option. Nothing was passed to the provider.
55
+ */
56
+ resolvedDefault?: string;
57
+ /** Last explicit runtime change (`change_model` / `set_thought_level`). */
58
+ current?: string;
59
+ /** Last value the provider itself reported (statusline, native history). */
60
+ observed?: string;
61
+ observedAt?: number;
62
+ /** Every value change in order. The last entry is the value in force. */
63
+ history: ModelSelectionHistoryEntry[];
64
+ }
65
+ export interface SessionLaunchRecord {
66
+ sessionId: string;
67
+ providerType: string;
68
+ providerVersion?: string;
69
+ providerChannel?: 'stable' | 'preview';
70
+ /** `restore` = re-attached after a daemon restart; the axis sources stay the original ones. */
71
+ launchedBy: SessionLaunchedBy;
72
+ launchedAt: number;
73
+ workspace?: string;
74
+ model: ModelSelection;
75
+ thinkingLevel: ModelSelection;
76
+ autoApproveModeId?: string;
77
+ /**
78
+ * Reserved by the design (§7 E1) for sanitized launch argv. Not populated:
79
+ * argv carries coordinator system-prompt text and config paths, and the
80
+ * session-host record already keeps the raw argv for restore.
81
+ */
82
+ cliArgs?: string[];
83
+ }
84
+ /** History is bounded; the first `launch` entry is always kept. */
85
+ export declare const MODEL_SELECTION_HISTORY_LIMIT = 20;
86
+ /** The value in force now: the latest change, else the launch-time value. */
87
+ export declare function effectiveModelSelectionValue(selection: ModelSelection | null | undefined): string | undefined;
88
+ /** The value the session started with (what was passed, else asked for, else the default). */
89
+ export declare function launchModelSelectionValue(selection: ModelSelection | null | undefined): string | undefined;
90
+ export interface ModelSelectionDisplay {
91
+ /** The value in force now. */
92
+ value: string;
93
+ /** The value the session launched with, when it differs from `value`. */
94
+ changedFrom?: string;
95
+ source: ModelAxisSource;
96
+ }
97
+ /** Pure projection for UI chips: "sonnet · chosen", "sonnet → opus (changed)". */
98
+ export declare function describeModelSelection(selection: ModelSelection | null | undefined): ModelSelectionDisplay | undefined;
99
+ export declare function sanitizeModelIdentifier(value: unknown): string | undefined;
100
+ /**
101
+ * Validate a record that crossed a process or storage boundary (session-host
102
+ * record meta on restore, a P2P snapshot). Returns undefined for anything that
103
+ * is not recognisably a launch record; unknown keys are dropped.
104
+ */
105
+ export declare function parseSessionLaunchRecord(raw: unknown): SessionLaunchRecord | undefined;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * session-status — the ONE session status vocabulary.
3
+ *
4
+ * Wiring-unification Phase A1 (docs/design/2026-09-23-wiring-unification.md).
5
+ *
6
+ * Before this file, the same eleven-member union was declared three times in
7
+ * daemon-core (`SessionStatus`, `ManagedStatus`, and a stale `AgentStatus`) and
8
+ * five independent "is it busy?" sets disagreed with each other — for example
9
+ * `waiting_approval` counted as busy in three of them and idle in the other
10
+ * two. Every consumer now derives from here:
11
+ *
12
+ * - the canonical union `SessionStatus` and its member list `SESSION_STATUSES`;
13
+ * - a behavioural CLASS per status (`SESSION_STATUS_CLASS`) — the only thing
14
+ * send / dispatch / UI logic may branch on;
15
+ * - an alias table for the raw spellings providers and status lanes still
16
+ * emit (`running`, `streaming`, `no_progress`, …) so classification is total.
17
+ *
18
+ * This lives in mesh-shared (a dependency-free leaf) because web-core must not
19
+ * value-import the daemon-core barrel, and the server has no daemon-core at all.
20
+ */
21
+ export declare const SESSION_STATUSES: readonly ["idle", "generating", "waiting_approval", "waiting_choice", "finalizing", "error", "stopped", "starting", "panel_hidden", "not_monitored", "disconnected"];
22
+ export type SessionStatus = typeof SESSION_STATUSES[number];
23
+ export declare const RECENT_SESSION_BUCKETS: readonly ["needs_attention", "working", "task_complete", "idle"];
24
+ export type RecentSessionBucket = typeof RECENT_SESSION_BUCKETS[number];
25
+ /**
26
+ * Behavioural class of a status.
27
+ *
28
+ * working — the agent owns the turn; input is queued, not delivered.
29
+ * blocked — the agent is waiting on a human decision (approval / choice);
30
+ * alive, and the turn is not over.
31
+ * ready — the agent will accept input now.
32
+ * dead — the session will never accept input again.
33
+ * unknown — the spelling is not one we classify (callers must fail safe).
34
+ */
35
+ export type SessionStatusClass = 'working' | 'blocked' | 'ready' | 'dead' | 'unknown';
36
+ export declare const SESSION_STATUS_CLASS: Record<SessionStatus, Exclude<SessionStatusClass, 'unknown'>>;
37
+ /**
38
+ * Raw spellings that still reach status consumers from IDE/CDP providers, the
39
+ * chat-tail status lane and legacy adapters, mapped onto the canonical member
40
+ * that carries the same behavioural meaning. Keep this table exhaustive over
41
+ * what the five pre-unification busy sets accepted; a spelling missing here
42
+ * classifies as `unknown`, which every consumer must treat as "do not assume
43
+ * idle".
44
+ */
45
+ export declare const SESSION_STATUS_ALIASES: Readonly<Record<string, SessionStatus>>;
46
+ export declare function isSessionStatus(value: unknown): value is SessionStatus;
47
+ /** Canonical member for a raw status spelling, or null when unrecognised. */
48
+ export declare function normalizeSessionStatus(raw: unknown): SessionStatus | null;
49
+ export declare function classifySessionStatus(raw: unknown): SessionStatusClass;
50
+ export declare function isWorkingStatus(raw: unknown): boolean;
51
+ export declare function isBlockedStatus(raw: unknown): boolean;
52
+ export declare function isReadyStatus(raw: unknown): boolean;
53
+ export declare function isDeadStatus(raw: unknown): boolean;
54
+ /**
55
+ * "Busy" in the sense every pre-unification set meant: the agent cannot take a
56
+ * new instruction right now, whether because it is working or because it is
57
+ * blocked on a human. Dead sessions are NOT busy — they are gone, and callers
58
+ * that need to distinguish must check `isDeadStatus` first.
59
+ */
60
+ export declare function isBusyStatus(raw: unknown): boolean;
61
+ export declare function statusesOfClass(cls: Exclude<SessionStatusClass, 'unknown'>): SessionStatus[];
62
+ /**
63
+ * Mesh-record terminal spellings: a session RECORD in one of these is gone
64
+ * (the vocabulary's `dead` class covers the canonical members; records also
65
+ * carry `failed` / `terminated` / `exited` / `closed`).
66
+ */
67
+ export declare const TERMINAL_SESSION_RECORD_STATUSES: readonly ["stopped", "failed", "terminated", "exited", "closed"];
68
+ export declare function isTerminalSessionRecordStatus(raw: unknown): boolean;
69
+ /**
70
+ * THE "idle — can take a task now" predicate over a session state/record (its
71
+ * top-level `status` and chat lane). Every idle-candidate check (queue drain,
72
+ * auto-launch orphan sweep, dispatch pick) uses this one, so they cannot
73
+ * disagree about which sessions are candidates.
74
+ */
75
+ export declare function isIdleSessionState(state: unknown): boolean;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * CLI auto-detect → capability-slot / MAGI-panel PROPOSAL generator.
2
+ * CLI auto-detect → capability-slot PROPOSAL generator.
3
3
  *
4
4
  * Detection of installed CLI providers already exists per node (the status
5
5
  * snapshot's `availableProviders`), and applying a slot profile already exists
6
- * (`mesh_node_slots_set`, dry-run by default). What was missing is the bit in
6
+ * (`mesh_node_slots` action "set", dry-run by default). What was missing is the bit in
7
7
  * between: turning "these CLIs are installed on this node" into a concrete
8
8
  * `NodeCapabilitySlot[]` draft the operator can review. This module is that
9
9
  * bridge, and nothing more — it is a PURE proposal generator. It never writes;
@@ -27,7 +27,6 @@
27
27
  * need it, and it is pure data + pure functions on plain objects.
28
28
  */
29
29
  import { type MeshTaskDifficulty, type NodeCapabilitySlot } from './brain-routing';
30
- import type { MagiSlot } from './magi';
31
30
  /**
32
31
  * One provider's seeded slot recipe. A provider may map to MORE THAN ONE slot
33
32
  * (claude-cli does: a wide sonnet slot plus a narrow opus slot for the hard
@@ -120,28 +119,3 @@ export interface SlotProposal {
120
119
  * returned proposal can report what a wholesale write would destroy.
121
120
  */
122
121
  export declare function buildSlotProposal(detected: readonly DetectedCliProvider[], currentSlots?: readonly NodeCapabilitySlot[]): SlotProposal;
123
- /**
124
- * Build a MAGI panel proposal from the same detections.
125
- *
126
- * ─── Deliberately narrow ──────────────────────────────────────────────────────
127
- *
128
- * MAGI's value is provider INDEPENDENCE: replicas from different providers
129
- * (ideally different machines) answering the same question, so agreement means
130
- * something. Detection tells us which providers exist — that is exactly enough
131
- * to propose one panel of distinct providers, and no more.
132
- *
133
- * What detection does NOT tell us is which provider suits which review KIND
134
- * (rca vs design vs claim_audit). Nothing in any manifest grades a provider for
135
- * root-cause analysis over design review, and inventing a per-kind assignment
136
- * would fabricate a rationale that does not exist. So this proposes ONE panel of
137
- * the detected providers and leaves the kind binding to the operator; the caller
138
- * decides which `task_kind` to bind it to via the existing dry-run tool.
139
- *
140
- * Ordering follows {@link CLI_SLOT_RECIPES} insertion order (recipe-known
141
- * providers first, in table order), so the panel leads with the providers whose
142
- * suitability is actually attested.
143
- */
144
- export declare function buildMagiPanelProposal(detected: readonly DetectedCliProvider[], opts?: {
145
- nodeId?: string;
146
- maxSlots?: number;
147
- }): MagiSlot[];