@adhdev/mesh-shared 1.0.60-rc.73 → 1.0.60-rc.74
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 +4 -6
- package/dist/git-diff.d.ts +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +84 -269
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -250
- package/dist/index.mjs.map +1 -1
- package/dist/json.d.ts +4 -0
- package/dist/mesh-tool-names.d.ts +11 -7
- package/dist/mesh-vocabulary.d.ts +1 -2
- package/dist/outbound-message.d.ts +1 -2
- package/dist/protocol/daemon-server.d.ts +8 -31
- package/dist/session-launch.d.ts +0 -8
- package/dist/slot-proposal.d.ts +1 -27
- package/dist/turn-ipc-mission.d.ts +0 -7
- package/dist/turn-ipc-queue.d.ts +10 -35
- package/dist/turn-ipc-stats.d.ts +72 -0
- package/dist/turn-ipc.d.ts +4 -4
- package/package.json +1 -1
- package/src/brain-routing.ts +6 -8
- package/src/git-diff.ts +48 -0
- package/src/index.ts +1 -1
- package/src/json.ts +10 -0
- package/src/mesh-tool-names.ts +20 -25
- package/src/mesh-vocabulary.ts +1 -2
- package/src/outbound-message.ts +1 -2
- package/src/protocol/daemon-server.ts +3 -34
- package/src/session-launch.ts +2 -4
- package/src/slot-proposal.ts +1 -47
- package/src/turn-ipc-mission.ts +7 -21
- package/src/turn-ipc-queue.ts +40 -102
- package/src/turn-ipc-stats.ts +193 -0
- package/src/turn-ipc.ts +5 -11
- package/dist/magi.d.ts +0 -281
- package/dist/turn-ipc-graph.d.ts +0 -195
- package/src/magi.ts +0 -310
- package/src/turn-ipc-graph.ts +0 -477
package/dist/json.d.ts
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export type JsonRecord = Record<string, unknown>;
|
|
11
11
|
export declare function readRecord(value: unknown): JsonRecord;
|
|
12
|
+
/** A plain-object record, or undefined — for callers that must tell "absent" from "empty". */
|
|
13
|
+
export declare function readOptionalRecord(value: unknown): JsonRecord | undefined;
|
|
12
14
|
export declare function readString(...values: unknown[]): string | undefined;
|
|
15
|
+
/** The first non-blank trimmed string, or '' — readString for callers that want a plain string. */
|
|
16
|
+
export declare function readText(...values: unknown[]): string;
|
|
13
17
|
export declare function readNumber(...values: unknown[]): number | undefined;
|
|
14
18
|
export declare function readBoolean(...values: unknown[]): boolean | undefined;
|
|
15
19
|
export declare function readStringArray(value: unknown): string[];
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* Order mirrors mcp-server `ALL_MESH_TOOLS` for easy visual diffing, but the consistency
|
|
19
19
|
* checks are set-based (order-insensitive).
|
|
20
20
|
*/
|
|
21
|
-
export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_route_preview", "mesh_list_nodes", "mesh_enqueue_batch", "mesh_enqueue_task", "mesh_view_queue", "
|
|
21
|
+
export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_route_preview", "mesh_list_nodes", "mesh_enqueue_batch", "mesh_enqueue_task", "mesh_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_read_terminal", "mesh_send_keys", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_answer_question", "mesh_list_pending_approvals", "mesh_create", "mesh_add_node", "mesh_clone_node", "mesh_remove_node", "mesh_cleanup_worktree_nodes", "mesh_refine_node", "mesh_refine_batch", "mesh_config", "mesh_init", "mesh_refine_plan", "mesh_cleanup_sessions", "mesh_task_history", "mesh_ledger_query", "mesh_note", "mesh_reconcile_ledger", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_node_slots", "mesh_coordinator_prompt_append"];
|
|
22
22
|
export type CanonicalMeshToolName = typeof CANONICAL_MESH_TOOL_NAMES[number];
|
|
23
23
|
/** The count the `NN tools` barrel doc comments and consistency test assert against. */
|
|
24
|
-
export declare const CANONICAL_MESH_TOOL_COUNT:
|
|
24
|
+
export declare const CANONICAL_MESH_TOOL_COUNT: 42;
|
|
25
25
|
/**
|
|
26
26
|
* Tool names retired by the 2026-09-26 tool-surface consolidation (60 → 48).
|
|
27
27
|
* Every capability behind them is still reachable: each retired name maps to the
|
|
@@ -33,8 +33,7 @@ export declare const CANONICAL_MESH_TOOL_COUNT: 48;
|
|
|
33
33
|
* test asserts none of these names is advertised anywhere in the prompt.
|
|
34
34
|
*
|
|
35
35
|
* `args` is the discriminator to add; every other argument keeps its name.
|
|
36
|
-
* `note`
|
|
37
|
-
* `mesh_graph_gate_claim({ extend_seconds })` extend-only mode).
|
|
36
|
+
* `note` carries any extra guidance when an argument moved in a rename.
|
|
38
37
|
*/
|
|
39
38
|
export declare const RETIRED_MESH_TOOLS: Readonly<Record<string, {
|
|
40
39
|
readonly tool: CanonicalMeshToolName;
|
|
@@ -42,8 +41,13 @@ export declare const RETIRED_MESH_TOOLS: Readonly<Record<string, {
|
|
|
42
41
|
readonly note?: string;
|
|
43
42
|
}>>;
|
|
44
43
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
|
|
44
|
+
* Tool names removed outright (no merged replacement), mapped to the guidance a
|
|
45
|
+
* coordinator on an old prompt needs to do the same job with the remaining tools.
|
|
46
|
+
*/
|
|
47
|
+
export declare const REMOVED_MESH_TOOLS: Readonly<Record<string, string>>;
|
|
48
|
+
/**
|
|
49
|
+
* The error text for a call to a retired or removed tool name, or null when
|
|
50
|
+
* `name` is neither. One wording shared by every MCP mode so the redirect reads
|
|
51
|
+
* the same wherever an old prompt calls it.
|
|
48
52
|
*/
|
|
49
53
|
export declare function retiredMeshToolError(name: string): string | null;
|
|
@@ -38,8 +38,7 @@ export type MeshThinkingLevel = typeof MESH_THINKING_LEVELS[number];
|
|
|
38
38
|
export declare const MESH_DELIVERY_MODES: readonly ["when_idle", "interrupt"];
|
|
39
39
|
export type MeshDeliveryMode = typeof MESH_DELIVERY_MODES[number];
|
|
40
40
|
/**
|
|
41
|
-
* What to do with a node's sessions when the node is removed
|
|
42
|
-
* settles. Declared here so daemon-core (`RepoMeshSessionCleanupMode`), the
|
|
41
|
+
* What to do with a node's sessions when the node is removed. Declared here so daemon-core (`RepoMeshSessionCleanupMode`), the
|
|
43
42
|
* web dashboard picker and the MCP schemas all read one list.
|
|
44
43
|
*/
|
|
45
44
|
export declare const MESH_SESSION_CLEANUP_MODES: readonly ["preserve", "stop", "delete_stopped", "stop_and_delete"];
|
|
@@ -132,9 +132,8 @@ export type SendRefusal = 'not_ready' | 'send_in_flight' | 'not_generating' | 'n
|
|
|
132
132
|
* a turn is generating (`send_now`). Written, not yet answered;
|
|
133
133
|
* `interrupt` — the turn in flight was stopped first, then this body was
|
|
134
134
|
* written (or re-parked) as a new turn;
|
|
135
|
-
* `acp` — handed to an ACP agent's `session/prompt`.
|
|
136
135
|
*/
|
|
137
|
-
export type SubmitRoute = 'pty' | 'agent_queue' | 'interrupt'
|
|
136
|
+
export type SubmitRoute = 'pty' | 'agent_queue' | 'interrupt';
|
|
138
137
|
/** The stop key pressed by an `interrupt` submit (logs / operator display). */
|
|
139
138
|
export interface SubmitInterruptDetail {
|
|
140
139
|
keyName: string;
|
|
@@ -157,11 +157,6 @@ export type AuthOkWirePayload = {
|
|
|
157
157
|
machineNickname: string | null;
|
|
158
158
|
seqscribeFleetSecret?: string;
|
|
159
159
|
seqscribeFleetSecretVersion?: number;
|
|
160
|
-
/** Beacon board seed for cold start (advisory, may be absent). */
|
|
161
|
-
beaconSeed?: {
|
|
162
|
-
reports: unknown[];
|
|
163
|
-
truncated: number;
|
|
164
|
-
};
|
|
165
160
|
limits: AuthOkPlanLimits;
|
|
166
161
|
};
|
|
167
162
|
export type AuthErrorWirePayload = {
|
|
@@ -207,14 +202,6 @@ export type DaemonErrorWirePayload = {
|
|
|
207
202
|
message?: string;
|
|
208
203
|
detail?: unknown;
|
|
209
204
|
};
|
|
210
|
-
export type BeaconVectorsWirePayload = {
|
|
211
|
-
op: 'put';
|
|
212
|
-
report: unknown;
|
|
213
|
-
} | {
|
|
214
|
-
op: 'get';
|
|
215
|
-
topics: string[];
|
|
216
|
-
requestId: string;
|
|
217
|
-
};
|
|
218
205
|
export declare const P2P_SIGNAL_TYPES: readonly ["p2p_ready", "p2p_offer", "p2p_answer", "p2p_ice"];
|
|
219
206
|
export type P2PSignalType = typeof P2P_SIGNAL_TYPES[number];
|
|
220
207
|
export declare const isP2PSignalType: (value: unknown) => value is "p2p_ready" | "p2p_offer" | "p2p_answer" | "p2p_ice";
|
|
@@ -258,9 +245,9 @@ export declare const isMeshP2PSignalType: (value: unknown) => value is "mesh_p2p
|
|
|
258
245
|
export type MeshP2PSignalWirePayload = {
|
|
259
246
|
senderDaemonId: string;
|
|
260
247
|
} & Record<string, unknown>;
|
|
261
|
-
export declare const DAEMON_TO_SERVER_TYPES: readonly ["auth", "status_report", "status_heartbeat", "status_event", "command_result", "error", "agent_event", "
|
|
248
|
+
export declare const DAEMON_TO_SERVER_TYPES: readonly ["auth", "status_report", "status_heartbeat", "status_event", "command_result", "error", "agent_event", "daemon_mesh_command", "p2p_offer", "p2p_ice"];
|
|
262
249
|
export type DaemonToServerType = typeof DAEMON_TO_SERVER_TYPES[number];
|
|
263
|
-
export declare const isDaemonToServerType: (value: unknown) => value is "error" | "p2p_offer" | "p2p_ice" | "auth" | "status_report" | "status_heartbeat" | "status_event" | "command_result" | "agent_event" | "
|
|
250
|
+
export declare const isDaemonToServerType: (value: unknown) => value is "error" | "p2p_offer" | "p2p_ice" | "auth" | "status_report" | "status_heartbeat" | "status_event" | "command_result" | "agent_event" | "daemon_mesh_command";
|
|
264
251
|
export type DaemonAuthMsg = Envelope<'auth', DaemonAuthWirePayload>;
|
|
265
252
|
export type StatusReportMsg = Envelope<'status_report', CloudStatusReportWirePayload>;
|
|
266
253
|
export type StatusHeartbeatMsg = Envelope<'status_heartbeat', StatusHeartbeatWirePayload>;
|
|
@@ -269,7 +256,6 @@ export type CommandResultMsg = Envelope<'command_result', CommandResultWirePaylo
|
|
|
269
256
|
export type DaemonErrorMsg = Envelope<'error', DaemonErrorWirePayload>;
|
|
270
257
|
/** Agent-level event forwarded verbatim to the `agent:status` webhook (throttled server-side). Dynamic by design. */
|
|
271
258
|
export type AgentEventMsg = Envelope<'agent_event', CommandPayload>;
|
|
272
|
-
export type BeaconVectorsMsg = Envelope<'beacon_vectors', BeaconVectorsWirePayload>;
|
|
273
259
|
export type DaemonP2POfferMsg = Envelope<'p2p_offer', P2POfferWirePayload>;
|
|
274
260
|
export type DaemonP2PIceMsg = Envelope<'p2p_ice', P2PIceWirePayload>;
|
|
275
261
|
/**
|
|
@@ -285,7 +271,7 @@ export type DaemonMeshCommandMsg = {
|
|
|
285
271
|
args: Record<string, unknown>;
|
|
286
272
|
timestamp: number;
|
|
287
273
|
};
|
|
288
|
-
export type DaemonToServerMsg = DaemonAuthMsg | StatusReportMsg | StatusHeartbeatMsg | DaemonStatusEventMsg | CommandResultMsg | DaemonErrorMsg | AgentEventMsg |
|
|
274
|
+
export type DaemonToServerMsg = DaemonAuthMsg | StatusReportMsg | StatusHeartbeatMsg | DaemonStatusEventMsg | CommandResultMsg | DaemonErrorMsg | AgentEventMsg | DaemonMeshCommandMsg | DaemonP2POfferMsg | DaemonP2PIceMsg;
|
|
289
275
|
/** Members that carry a `payload` object (everything except `daemon_mesh_command`). */
|
|
290
276
|
export type DaemonToServerPayloadType = Exclude<DaemonToServerMsg, DaemonMeshCommandMsg>['type'];
|
|
291
277
|
export type DaemonToServerPayloadOf<T extends DaemonToServerPayloadType> = Extract<DaemonToServerMsg, {
|
|
@@ -297,9 +283,9 @@ export type DaemonToServerPayloadOf<T extends DaemonToServerPayloadType> = Extra
|
|
|
297
283
|
* token string because nothing downstream can proceed without one.
|
|
298
284
|
*/
|
|
299
285
|
export declare function decodeDaemonToServer(raw: unknown): DaemonToServerMsg | null;
|
|
300
|
-
export declare const SERVER_TO_DAEMON_CONTROL_TYPES: readonly ["auth_ok", "auth_error", "machine_evicted", "force_disconnect", "token_revoked", "version_mismatch", "force_update_required", "command", "agent_command", "resolve_action", "
|
|
286
|
+
export declare const SERVER_TO_DAEMON_CONTROL_TYPES: readonly ["auth_ok", "auth_error", "machine_evicted", "force_disconnect", "token_revoked", "version_mismatch", "force_update_required", "command", "agent_command", "resolve_action", "daemon_mesh_result", "p2p_ready", "p2p_offer", "p2p_answer", "p2p_ice", "mesh_p2p_ready", "mesh_p2p_offer", "mesh_p2p_answer", "mesh_p2p_ice"];
|
|
301
287
|
export type ServerToDaemonControlType = typeof SERVER_TO_DAEMON_CONTROL_TYPES[number];
|
|
302
|
-
export declare const isServerToDaemonControlType: (value: unknown) => value is "p2p_ready" | "p2p_offer" | "p2p_answer" | "p2p_ice" | "mesh_p2p_ready" | "mesh_p2p_offer" | "mesh_p2p_answer" | "mesh_p2p_ice" | "command" | "auth_ok" | "auth_error" | "machine_evicted" | "force_disconnect" | "token_revoked" | "version_mismatch" | "force_update_required" | "agent_command" | "resolve_action" | "
|
|
288
|
+
export declare const isServerToDaemonControlType: (value: unknown) => value is "p2p_ready" | "p2p_offer" | "p2p_answer" | "p2p_ice" | "mesh_p2p_ready" | "mesh_p2p_offer" | "mesh_p2p_answer" | "mesh_p2p_ice" | "command" | "auth_ok" | "auth_error" | "machine_evicted" | "force_disconnect" | "token_revoked" | "version_mismatch" | "force_update_required" | "agent_command" | "resolve_action" | "daemon_mesh_result";
|
|
303
289
|
/** Optional routing fields the server stamps on relayed commands. */
|
|
304
290
|
type ServerRelayFields = {
|
|
305
291
|
id?: MessageId;
|
|
@@ -324,15 +310,6 @@ export type ServerAgentCommandMsg = Envelope<'agent_command', {
|
|
|
324
310
|
export type ServerResolveActionMsg = Envelope<'resolve_action', CommandPayload> & ServerRelayFields;
|
|
325
311
|
export type ServerP2PSignalMsg = Envelope<P2PSignalType, RelayedP2PSignalWirePayload> & ServerRelayFields;
|
|
326
312
|
export type ServerMeshP2PSignalMsg = Envelope<MeshP2PSignalType, MeshP2PSignalWirePayload> & ServerRelayFields;
|
|
327
|
-
/** Reply to a `beacon_vectors` GET, correlated by `requestId` (top-level, no payload). A PUT gets no reply. */
|
|
328
|
-
export type BeaconVectorsResultMsg = {
|
|
329
|
-
type: 'beacon_vectors_result';
|
|
330
|
-
requestId: string;
|
|
331
|
-
success: boolean;
|
|
332
|
-
reports?: unknown[];
|
|
333
|
-
truncated?: number;
|
|
334
|
-
error?: string;
|
|
335
|
-
};
|
|
336
313
|
/** Correlated ack/nack of a `daemon_mesh_command` (top-level, no payload). */
|
|
337
314
|
export type DaemonMeshResultMsg = {
|
|
338
315
|
type: 'daemon_mesh_result';
|
|
@@ -341,7 +318,7 @@ export type DaemonMeshResultMsg = {
|
|
|
341
318
|
result?: unknown;
|
|
342
319
|
error?: string;
|
|
343
320
|
};
|
|
344
|
-
export type ServerToDaemonControlMsg = AuthOkMsg | AuthErrorMsg | MachineEvictedMsg | ForceDisconnectMsg | TokenRevokedMsg | VersionMismatchMsg | ForceUpdateRequiredMsg | ServerCommandMsg | ServerAgentCommandMsg | ServerResolveActionMsg |
|
|
321
|
+
export type ServerToDaemonControlMsg = AuthOkMsg | AuthErrorMsg | MachineEvictedMsg | ForceDisconnectMsg | TokenRevokedMsg | VersionMismatchMsg | ForceUpdateRequiredMsg | ServerCommandMsg | ServerAgentCommandMsg | ServerResolveActionMsg | DaemonMeshResultMsg | ServerP2PSignalMsg | ServerMeshP2PSignalMsg;
|
|
345
322
|
declare const directCommandBrand: unique symbol;
|
|
346
323
|
/**
|
|
347
324
|
* A command name relayed as the frame `type` — the OPEN namespace the REST API
|
|
@@ -364,7 +341,7 @@ export type ServerToDaemonMsg = ServerToDaemonControlMsg | ServerDirectCommandMs
|
|
|
364
341
|
export type ServerToDaemonType = ServerToDaemonMsg['type'];
|
|
365
342
|
export declare function isServerDirectCommandMsg(msg: ServerToDaemonMsg): msg is ServerDirectCommandMsg;
|
|
366
343
|
/** Members of the control set that carry a `payload` object. */
|
|
367
|
-
export type ServerToDaemonPayloadType = Exclude<ServerToDaemonControlMsg,
|
|
344
|
+
export type ServerToDaemonPayloadType = Exclude<ServerToDaemonControlMsg, DaemonMeshResultMsg>['type'];
|
|
368
345
|
export type ServerToDaemonPayloadOf<T extends ServerToDaemonPayloadType> = Extract<ServerToDaemonControlMsg, {
|
|
369
346
|
type: T;
|
|
370
347
|
}>['payload'];
|
|
@@ -372,7 +349,7 @@ export declare function isDaemonDirectCommandType(value: unknown): value is Daem
|
|
|
372
349
|
/**
|
|
373
350
|
* Parse boundary for the daemon: is `raw` a frame this link accepts from the server?
|
|
374
351
|
* Control members are checked for their discriminant and envelope shape; the
|
|
375
|
-
*
|
|
352
|
+
* payload-less `daemon_mesh_result` reply needs its `requestId`; any other well-formed
|
|
376
353
|
* command name becomes a `ServerDirectCommandMsg`.
|
|
377
354
|
*/
|
|
378
355
|
export declare function decodeServerToDaemon(raw: unknown): ServerToDaemonMsg | null;
|
package/dist/session-launch.d.ts
CHANGED
|
@@ -19,14 +19,6 @@
|
|
|
19
19
|
* passed through {@link sanitizeModelIdentifier}) and `modelSource` (an enum,
|
|
20
20
|
* passed through {@link isModelAxisSource}). Never widen that to the record.
|
|
21
21
|
*/
|
|
22
|
-
/**
|
|
23
|
-
* Where a model / thinking-level value came from.
|
|
24
|
-
*
|
|
25
|
-
* `restore` is deliberately NOT a member: a hosted session re-attached after a
|
|
26
|
-
* daemon restart keeps the ORIGINAL source (the user still picked that model);
|
|
27
|
-
* only `SessionLaunchRecord.launchedBy` records that the session came back via
|
|
28
|
-
* restore.
|
|
29
|
-
*/
|
|
30
22
|
export declare const MODEL_AXIS_SOURCES: readonly ["user", "remembered", "mesh_slot", "task_override", "provider_default", "unspecified"];
|
|
31
23
|
export type ModelAxisSource = typeof MODEL_AXIS_SOURCES[number];
|
|
32
24
|
export declare function isModelAxisSource(value: unknown): value is ModelAxisSource;
|
package/dist/slot-proposal.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI auto-detect → capability-slot
|
|
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
|
|
@@ -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[];
|
|
@@ -7,8 +7,6 @@ import { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
|
7
7
|
export declare const MESH_MISSION_STATUSES: readonly ["active", "paused", "completed", "abandoned"];
|
|
8
8
|
export type MeshMissionStatusValue = typeof MESH_MISSION_STATUSES[number];
|
|
9
9
|
export declare const isMeshMissionStatusValue: (value: unknown) => value is "active" | "completed" | "paused" | "abandoned";
|
|
10
|
-
export declare const MESH_MISSION_SOURCES: readonly ["magi", "coordinator"];
|
|
11
|
-
export type MeshMissionSourceValue = typeof MESH_MISSION_SOURCES[number];
|
|
12
10
|
export interface MissionBriefWire {
|
|
13
11
|
goal: string;
|
|
14
12
|
constraints?: readonly string[];
|
|
@@ -25,7 +23,6 @@ export interface MissionUpsertRequest {
|
|
|
25
23
|
/** Free text — see file-header note on why this is fine over local IPC. */
|
|
26
24
|
goal?: string;
|
|
27
25
|
status?: MeshMissionStatusValue;
|
|
28
|
-
source?: MeshMissionSourceValue;
|
|
29
26
|
/**
|
|
30
27
|
* H2: `undefined` = do not touch the stored brief. `null` = explicitly clear it.
|
|
31
28
|
* A present object with no usable `goal` is treated by the daemon's own
|
|
@@ -40,7 +37,6 @@ export interface MeshMissionRecordWire {
|
|
|
40
37
|
title: string;
|
|
41
38
|
goal: string;
|
|
42
39
|
status: MeshMissionStatusValue;
|
|
43
|
-
source?: MeshMissionSourceValue;
|
|
44
40
|
/** H2: absent = no brief attached to this mission. */
|
|
45
41
|
brief?: MissionBriefWire;
|
|
46
42
|
}
|
|
@@ -68,7 +64,6 @@ export interface MissionListQueryRequest {
|
|
|
68
64
|
meshId: string;
|
|
69
65
|
statuses?: readonly MeshMissionStatusValue[];
|
|
70
66
|
verbose?: boolean;
|
|
71
|
-
includeMagi?: boolean;
|
|
72
67
|
withStats?: boolean;
|
|
73
68
|
limit?: number;
|
|
74
69
|
historyIdLimit?: number;
|
|
@@ -110,7 +105,6 @@ export interface MissionListSummaryVerboseWire {
|
|
|
110
105
|
title: string;
|
|
111
106
|
goal: string;
|
|
112
107
|
status: MeshMissionStatusValue;
|
|
113
|
-
source?: MeshMissionSourceValue;
|
|
114
108
|
tasks: MeshMissionTaskAggregateWire;
|
|
115
109
|
stats?: MeshMissionStatsWire;
|
|
116
110
|
/** H2: absent = no brief attached. */
|
|
@@ -128,7 +122,6 @@ export interface MissionListSummarySlimWire {
|
|
|
128
122
|
goalPreview: string;
|
|
129
123
|
goalTruncated: boolean;
|
|
130
124
|
status: MeshMissionStatusValue;
|
|
131
|
-
source?: MeshMissionSourceValue;
|
|
132
125
|
tasks: MeshMissionTaskAggregateWire;
|
|
133
126
|
stats?: MeshMissionStatsWire;
|
|
134
127
|
/** H2: absent = no brief attached. */
|
package/dist/turn-ipc-queue.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* turn-ipc — local records, queue composites and active work (C-W9a):
|
|
3
|
-
* record_local, queue_query, queue_enqueue,
|
|
4
|
-
* queue_requeue, direct_dispatch_record,
|
|
3
|
+
* record_local, queue_query, queue_enqueue, queue_enqueue_batch, queue_cancel,
|
|
4
|
+
* queue_requeue, direct_dispatch_record, active_work_query,
|
|
5
5
|
* recovery_context_query. Part of the turn-ipc wire contract (./turn-ipc.ts).
|
|
6
6
|
*/
|
|
7
7
|
import { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
@@ -47,7 +47,7 @@ export interface QueueQueryRequest {
|
|
|
47
47
|
/** With `withCounts`: also count terminal rows whose `updated_at` is older than this age. */
|
|
48
48
|
historicalOlderThanMs?: number;
|
|
49
49
|
/**
|
|
50
|
-
* Also return `dependencyHeads`: id/status/
|
|
50
|
+
* Also return `dependencyHeads`: id/status/cancelReason of every
|
|
51
51
|
* row the returned entries list in `dependsOn` that is not itself returned —
|
|
52
52
|
* enough to annotate dependency state without reading the whole queue.
|
|
53
53
|
*/
|
|
@@ -57,7 +57,6 @@ export interface QueueQueryRequest {
|
|
|
57
57
|
export interface QueueDependencyHeadWire {
|
|
58
58
|
id: string;
|
|
59
59
|
status: string;
|
|
60
|
-
blockedReason?: string;
|
|
61
60
|
cancelReason?: string;
|
|
62
61
|
}
|
|
63
62
|
export interface QueueQueryResponse {
|
|
@@ -77,37 +76,29 @@ export interface QueueEnqueueRequest {
|
|
|
77
76
|
/** Free text (the task message — see the section note). */
|
|
78
77
|
message: string;
|
|
79
78
|
options?: Record<string, unknown>;
|
|
80
|
-
/** `recordSingleEnqueueDecision` args minus `taskId` (the daemon fills it). */
|
|
81
|
-
decision?: Record<string, unknown>;
|
|
82
79
|
}
|
|
83
80
|
export interface QueueEnqueueResponse {
|
|
84
81
|
entry: QueueEntryWire;
|
|
85
82
|
}
|
|
86
83
|
export declare function decodeQueueEnqueueRequest(value: unknown): QueueEnqueueRequest | null;
|
|
87
84
|
export declare function decodeQueueEnqueueResponse(value: unknown): QueueEnqueueResponse | null;
|
|
88
|
-
export interface
|
|
85
|
+
export interface QueueEnqueueBatchRequest {
|
|
89
86
|
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
90
87
|
meshId: string;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
plan?: Record<string, unknown>;
|
|
96
|
-
/** Audit context: batchId / missionId / coordinatorSessionId / onDependencyFailure / orchestrationDecision / taskCount. */
|
|
97
|
-
audit?: Record<string, unknown>;
|
|
98
|
-
}
|
|
99
|
-
export type QueueEnqueueGraphResponse = {
|
|
88
|
+
/** `MeshTaskBatchEntrySpec[]` (daemon-core, JSON passthrough). */
|
|
89
|
+
specs: readonly Record<string, unknown>[];
|
|
90
|
+
}
|
|
91
|
+
export type QueueEnqueueBatchResponse = {
|
|
100
92
|
ok: true;
|
|
101
93
|
tasks: readonly QueueEntryWire[];
|
|
102
|
-
graph?: Record<string, unknown>;
|
|
103
94
|
} | {
|
|
104
95
|
ok: false;
|
|
105
96
|
refusalCode?: string;
|
|
106
97
|
message: string;
|
|
107
98
|
extra?: Record<string, unknown>;
|
|
108
99
|
};
|
|
109
|
-
export declare function
|
|
110
|
-
export declare function
|
|
100
|
+
export declare function decodeQueueEnqueueBatchRequest(value: unknown): QueueEnqueueBatchRequest | null;
|
|
101
|
+
export declare function decodeQueueEnqueueBatchResponse(value: unknown): QueueEnqueueBatchResponse | null;
|
|
111
102
|
export interface QueueCancelRequest {
|
|
112
103
|
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
113
104
|
meshId: string;
|
|
@@ -143,28 +134,12 @@ export interface DirectDispatchRecordRequest {
|
|
|
143
134
|
message: string;
|
|
144
135
|
/** `recordDirectDispatchTask` options minus `id` (JSON passthrough). */
|
|
145
136
|
task?: Record<string, unknown>;
|
|
146
|
-
/** `recordDirectDispatchDecision` args minus `taskId` (JSON passthrough). */
|
|
147
|
-
decision?: Record<string, unknown>;
|
|
148
137
|
}
|
|
149
138
|
export interface DirectDispatchRecordResponse {
|
|
150
139
|
taskRecorded: boolean;
|
|
151
|
-
decisionRecorded: boolean;
|
|
152
140
|
}
|
|
153
141
|
export declare function decodeDirectDispatchRecordRequest(value: unknown): DirectDispatchRecordRequest | null;
|
|
154
142
|
export declare function decodeDirectDispatchRecordResponse(value: unknown): DirectDispatchRecordResponse | null;
|
|
155
|
-
export declare const GRAPH_AUDIT_EVENTS: readonly ["gate_claimed", "gate_released", "gate_abandoned", "node_patched"];
|
|
156
|
-
export type GraphAuditEvent = typeof GRAPH_AUDIT_EVENTS[number];
|
|
157
|
-
export interface GraphAuditRecordRequest {
|
|
158
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
159
|
-
meshId: string;
|
|
160
|
-
event: GraphAuditEvent;
|
|
161
|
-
fields: Record<string, unknown>;
|
|
162
|
-
}
|
|
163
|
-
export interface GraphAuditRecordResponse {
|
|
164
|
-
recorded: boolean;
|
|
165
|
-
}
|
|
166
|
-
export declare function decodeGraphAuditRecordRequest(value: unknown): GraphAuditRecordRequest | null;
|
|
167
|
-
export declare function decodeGraphAuditRecordResponse(value: unknown): GraphAuditRecordResponse | null;
|
|
168
143
|
export interface ActiveWorkQueryRequest {
|
|
169
144
|
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
170
145
|
meshId: string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turn-ipc — task stats and maintenance commands: task_stats_query,
|
|
3
|
+
* prune_stale_direct, orphaned_pin_notify. Part of the turn-ipc wire contract
|
|
4
|
+
* (./turn-ipc.ts).
|
|
5
|
+
*/
|
|
6
|
+
import { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
7
|
+
export interface TaskStatsQueryRequest {
|
|
8
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
9
|
+
meshId: string;
|
|
10
|
+
taskIds?: readonly string[];
|
|
11
|
+
missionId?: string;
|
|
12
|
+
tail?: number;
|
|
13
|
+
/** Also compute `computeMeshMissionStats` — requires `missionId`. */
|
|
14
|
+
rollup?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Batch rollups: `computeMeshMissionStats` for every id, over ONE queue read and
|
|
17
|
+
* ONE record read (answers `missions`, `tasks` empty). Exclusive with
|
|
18
|
+
* `taskIds`/`missionId`. An older daemon rejects the key.
|
|
19
|
+
*/
|
|
20
|
+
missionIds?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export interface TaskStatsQueryResponse {
|
|
23
|
+
/** `MeshTaskStats[]`, JSON passthrough. */
|
|
24
|
+
tasks: readonly Record<string, unknown>[];
|
|
25
|
+
/** `MeshMissionStats`, JSON passthrough; present only when `rollup: true` was requested and satisfiable. */
|
|
26
|
+
mission?: Record<string, unknown>;
|
|
27
|
+
/** Batch mode: `MeshMissionStats` per requested mission id, JSON passthrough. */
|
|
28
|
+
missions?: Record<string, Record<string, unknown>>;
|
|
29
|
+
}
|
|
30
|
+
export declare function decodeTaskStatsQueryRequest(value: unknown): TaskStatsQueryRequest | null;
|
|
31
|
+
export declare function decodeTaskStatsQueryResponse(value: unknown): TaskStatsQueryResponse | null;
|
|
32
|
+
export interface PruneStaleDirectRequest {
|
|
33
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
34
|
+
meshId: string;
|
|
35
|
+
execute?: boolean;
|
|
36
|
+
includeTerminal?: boolean;
|
|
37
|
+
source?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface PruneStaleDirectResponse {
|
|
40
|
+
mode: 'execute' | 'dry_run';
|
|
41
|
+
includeTerminal: boolean;
|
|
42
|
+
candidateCount: number;
|
|
43
|
+
/** `MeshActiveWorkRecord[]`, JSON passthrough. */
|
|
44
|
+
prunable: readonly Record<string, unknown>[];
|
|
45
|
+
prunedCount: number;
|
|
46
|
+
preservedUnacknowledged: readonly Record<string, unknown>[];
|
|
47
|
+
preservedLedgerOnly: readonly Record<string, unknown>[];
|
|
48
|
+
preservedNotOrphan: readonly Record<string, unknown>[];
|
|
49
|
+
}
|
|
50
|
+
export declare function decodePruneStaleDirectRequest(value: unknown): PruneStaleDirectRequest | null;
|
|
51
|
+
export declare function decodePruneStaleDirectResponse(value: unknown): PruneStaleDirectResponse | null;
|
|
52
|
+
export interface OrphanedPinNotifyRequest {
|
|
53
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION;
|
|
54
|
+
meshId: string;
|
|
55
|
+
stoppedSessionId: string;
|
|
56
|
+
excludeTaskId?: string;
|
|
57
|
+
cause?: string;
|
|
58
|
+
nodeId?: string;
|
|
59
|
+
coordinatorSessionId?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface OrphanedPinnedTaskWire {
|
|
62
|
+
taskId: string;
|
|
63
|
+
title: string;
|
|
64
|
+
targetSessionId: string;
|
|
65
|
+
targetNodeId?: string;
|
|
66
|
+
missionId?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface OrphanedPinNotifyResponse {
|
|
69
|
+
orphans: readonly OrphanedPinnedTaskWire[];
|
|
70
|
+
}
|
|
71
|
+
export declare function decodeOrphanedPinNotifyRequest(value: unknown): OrphanedPinNotifyRequest | null;
|
|
72
|
+
export declare function decodeOrphanedPinNotifyResponse(value: unknown): OrphanedPinNotifyResponse | null;
|
package/dist/turn-ipc.d.ts
CHANGED
|
@@ -52,14 +52,14 @@
|
|
|
52
52
|
* LAYOUT
|
|
53
53
|
* -------
|
|
54
54
|
* This module holds the error codes and the command registry; the per-command
|
|
55
|
-
* contracts live in ./turn-ipc-{turn,mission,queue,
|
|
55
|
+
* contracts live in ./turn-ipc-{turn,mission,queue,stats}.ts (re-exported here)
|
|
56
56
|
* over the shared guard primitives in ./turn-ipc-guards.ts.
|
|
57
57
|
*/
|
|
58
58
|
export { TURN_IPC_PROTOCOL_VERSION } from './turn-ipc-guards';
|
|
59
59
|
export * from './turn-ipc-turn';
|
|
60
60
|
export * from './turn-ipc-mission';
|
|
61
61
|
export * from './turn-ipc-queue';
|
|
62
|
-
export * from './turn-ipc-
|
|
62
|
+
export * from './turn-ipc-stats';
|
|
63
63
|
/**
|
|
64
64
|
* Closed error-code union every command response may carry instead of (or in
|
|
65
65
|
* addition to, for the client-side wrapper) a result.
|
|
@@ -85,6 +85,6 @@ export interface TurnIpcError {
|
|
|
85
85
|
}
|
|
86
86
|
export declare function isTurnIpcError(value: unknown): value is TurnIpcError;
|
|
87
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", "
|
|
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
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" | "
|
|
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/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,
|
|
@@ -118,8 +116,8 @@ export function normalizeDifficultyBrainMap(raw: unknown): DifficultyBrainMap {
|
|
|
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
|
/**
|
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'
|
package/src/json.ts
CHANGED
|
@@ -14,6 +14,11 @@ export function readRecord(value: unknown): JsonRecord {
|
|
|
14
14
|
return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : {}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/** A plain-object record, or undefined — for callers that must tell "absent" from "empty". */
|
|
18
|
+
export function readOptionalRecord(value: unknown): JsonRecord | undefined {
|
|
19
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
export function readString(...values: unknown[]): string | undefined {
|
|
18
23
|
for (const value of values) {
|
|
19
24
|
if (typeof value !== 'string') continue
|
|
@@ -23,6 +28,11 @@ export function readString(...values: unknown[]): string | undefined {
|
|
|
23
28
|
return undefined
|
|
24
29
|
}
|
|
25
30
|
|
|
31
|
+
/** The first non-blank trimmed string, or '' — readString for callers that want a plain string. */
|
|
32
|
+
export function readText(...values: unknown[]): string {
|
|
33
|
+
return readString(...values) ?? ''
|
|
34
|
+
}
|
|
35
|
+
|
|
26
36
|
export function readNumber(...values: unknown[]): number | undefined {
|
|
27
37
|
for (const value of values) {
|
|
28
38
|
if (typeof value === 'number' && Number.isFinite(value)) return value
|