@adhdev/mesh-shared 1.0.82-rc.4 → 1.0.82-rc.40
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/assistant-protocol.d.ts +161 -6
- package/dist/decision-push-relay.d.ts +20 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1197 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1107 -118
- package/dist/index.mjs.map +1 -1
- package/dist/launch-defaults.d.ts +196 -0
- package/dist/mesh-tool-names.d.ts +2 -2
- package/dist/mesh-vocabulary.d.ts +13 -4
- package/dist/mission-git-state.d.ts +83 -0
- package/dist/mission-items.d.ts +202 -0
- package/dist/node-facts.d.ts +75 -1
- package/dist/outbound-message.d.ts +9 -2
- package/dist/path-ownership.d.ts +1 -1
- package/dist/protocol/daemon-server.d.ts +42 -2
- package/dist/protocol/dashboard-daemon-p2p.d.ts +1 -1
- package/dist/protocol/dashboard-server.d.ts +20 -2
- package/dist/provider-approval.d.ts +36 -0
- package/dist/provider-question.d.ts +19 -0
- package/dist/quota-freshness.d.ts +65 -0
- package/dist/relay-reprobe.d.ts +52 -0
- package/dist/release.d.ts +90 -0
- package/dist/role-launch.d.ts +11 -0
- package/dist/session-launch.d.ts +10 -22
- package/dist/slot-proposal.d.ts +14 -0
- package/dist/task-source-phase.d.ts +75 -0
- package/dist/turn-evidence.d.ts +29 -13
- package/dist/turn-ipc-mission.d.ts +68 -1
- package/dist/turn-ipc-queue.d.ts +19 -0
- package/dist/turn-ipc.d.ts +2 -2
- package/dist/types.d.ts +1 -0
- package/dist/worker-protocol.d.ts +1 -1
- package/package.json +1 -1
- package/src/assistant-protocol.ts +302 -6
- package/src/daemon-normalize.ts +7 -5
- package/src/decision-push-relay.ts +23 -0
- package/src/index.ts +12 -0
- package/src/keyed-doc-delta.ts +9 -1
- package/src/launch-defaults.ts +170 -0
- package/src/mesh-tool-names.ts +16 -0
- package/src/mesh-vocabulary.ts +12 -2
- package/src/mission-git-state.ts +112 -0
- package/src/mission-items.ts +421 -0
- package/src/node-facts.ts +70 -1
- package/src/outbound-message.ts +19 -0
- package/src/path-ownership.ts +4 -6
- package/src/protocol/daemon-server.ts +37 -2
- package/src/protocol/dashboard-server.ts +13 -2
- package/src/provider-approval.ts +59 -0
- package/src/provider-question.ts +31 -0
- package/src/quota-freshness.ts +139 -0
- package/src/relay-reprobe.ts +117 -0
- package/src/release.ts +121 -0
- package/src/role-launch.ts +51 -0
- package/src/session-launch.ts +17 -1
- package/src/slot-proposal.ts +61 -2
- package/src/task-source-phase.ts +61 -0
- package/src/turn-evidence.ts +27 -20
- package/src/turn-ipc-mission.ts +121 -11
- package/src/turn-ipc-queue.ts +29 -1
- package/src/turn-ipc-turn.ts +1 -1
- package/src/turn-ipc.ts +1 -0
- package/src/types.ts +1 -0
- package/src/worker-protocol.ts +15 -0
|
@@ -126,7 +126,7 @@ export type SendPolicy = {
|
|
|
126
126
|
* `unsupported_input`), which `satisfies readonly SendRefusal[]` there keeps
|
|
127
127
|
* honest at compile time.
|
|
128
128
|
*/
|
|
129
|
-
export type SendRefusal = 'not_ready' | 'send_in_flight' | 'not_generating' | 'not_supported' | 'platform_unsupported' | 'modal_parked' | 'interrupt_not_implemented' | 'interrupt_refused' | 'idle_timeout' | 'session_exited' | 'unsupported_input' | 'no_target' | 'bootstrap_pending' | 'duplicate_dispatch' | 'internal_error';
|
|
129
|
+
export type SendRefusal = 'not_ready' | 'send_in_flight' | 'not_generating' | 'not_supported' | 'platform_unsupported' | 'modal_parked' | 'interrupt_not_implemented' | 'interrupt_refused' | 'idle_timeout' | 'session_exited' | 'unsupported_input' | 'no_target' | 'bootstrap_pending' | 'duplicate_dispatch' | 'new_work_held' | 'internal_error';
|
|
130
130
|
/**
|
|
131
131
|
* Which write path carried a delivered/queued message (Phase D2, additive
|
|
132
132
|
* detail — callers branch on `kind`; `route` only refines the report):
|
|
@@ -197,9 +197,16 @@ export declare function isOutboundMessageOrigin(value: unknown): value is Outbou
|
|
|
197
197
|
export declare const SEND_POLICY_MODES: readonly ["queue", "send_now", "interrupt"];
|
|
198
198
|
export declare function isSendPolicyMode(value: unknown): value is SendPolicy['mode'];
|
|
199
199
|
export declare function isSendPolicy(value: unknown): value is SendPolicy;
|
|
200
|
-
export declare const SEND_REFUSAL_REASONS: readonly ["not_ready", "send_in_flight", "not_generating", "not_supported", "platform_unsupported", "modal_parked", "interrupt_not_implemented", "interrupt_refused", "idle_timeout", "session_exited", "unsupported_input", "no_target", "bootstrap_pending", "duplicate_dispatch", "internal_error"];
|
|
200
|
+
export declare const SEND_REFUSAL_REASONS: readonly ["not_ready", "send_in_flight", "not_generating", "not_supported", "platform_unsupported", "modal_parked", "interrupt_not_implemented", "interrupt_refused", "idle_timeout", "session_exited", "unsupported_input", "no_target", "bootstrap_pending", "duplicate_dispatch", "new_work_held", "internal_error"];
|
|
201
201
|
export declare function isSendRefusal(value: unknown): value is SendRefusal;
|
|
202
202
|
export declare function isSubmitOutcome(value: unknown): value is SubmitOutcome;
|
|
203
203
|
export declare function isInputEnvelopeWire(value: unknown): value is InputEnvelopeWire;
|
|
204
204
|
/** Minimal structural check for the wire boundary — not a full decoder (mesh-shared stays a pure leaf; semantic validation stays with the handler that owns it). */
|
|
205
205
|
export declare function isOutboundMessage(value: unknown): value is OutboundMessage;
|
|
206
|
+
/**
|
|
207
|
+
* Drop the `<pasted_content>` envelope lines Claude Code wraps around a pasted
|
|
208
|
+
* input. Only a text that OPENS with the envelope is touched; anything else is
|
|
209
|
+
* returned unchanged. Shared across daemon transcript merge and web echo/display
|
|
210
|
+
* normalization so wrapped turns correlate identically.
|
|
211
|
+
*/
|
|
212
|
+
export declare function unwrapPastedContent(text: string): string;
|
package/dist/path-ownership.d.ts
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
/** One normalized ownership entry: a repo-relative path, optionally a `**` subtree prefix. */
|
|
35
35
|
export interface OwnedPathEntry {
|
|
36
|
-
/** Repo-relative path with forward slashes, no leading `./`, no trailing slash. */
|
|
36
|
+
/** Repo-relative path with forward slashes, no leading `./`, no trailing slash; empty for the whole-repo subtree. */
|
|
37
37
|
readonly path: string;
|
|
38
38
|
/** True when this entry was declared as `<path>/**` (owns the whole subtree). */
|
|
39
39
|
readonly subtree: boolean;
|
|
@@ -32,15 +32,38 @@ import { type CommandPayload, type Envelope, type MessageId } from './envelope';
|
|
|
32
32
|
* time (UserSession.ts), so adding a member here without adding it there — or
|
|
33
33
|
* vice versa — fails `tsc`.
|
|
34
34
|
*/
|
|
35
|
-
export declare const DAEMON_STATUS_EVENT_NAMES: readonly ["agent:generating_started", "agent:waiting_approval", "agent:waiting_choice", "agent:generating_completed", "agent:stopped", "monitor:no_progress"];
|
|
35
|
+
export declare const DAEMON_STATUS_EVENT_NAMES: readonly ["agent:generating_started", "agent:waiting_approval", "agent:waiting_choice", "agent:generating_completed", "agent:stopped", "monitor:no_progress", "agent:decision_needed"];
|
|
36
36
|
export type DaemonStatusEventName = typeof DAEMON_STATUS_EVENT_NAMES[number];
|
|
37
|
-
export declare const isDaemonStatusEventName: (value: unknown) => value is "agent:generating_started" | "agent:waiting_approval" | "agent:waiting_choice" | "agent:generating_completed" | "agent:stopped" | "monitor:no_progress";
|
|
37
|
+
export declare const isDaemonStatusEventName: (value: unknown) => value is "agent:generating_started" | "agent:waiting_approval" | "agent:waiting_choice" | "agent:generating_completed" | "agent:stopped" | "monitor:no_progress" | "agent:decision_needed";
|
|
38
38
|
/**
|
|
39
39
|
* Server-bound status event. Content boundary: identifiers, enums, counters
|
|
40
40
|
* and booleans only — plus the approved approval-modal exception
|
|
41
41
|
* (`modalMessage`/`modalButtons`, CLAUDE.md "Push notification exception").
|
|
42
42
|
* Mirror of daemon-core `DaemonStatusEventPayload`.
|
|
43
43
|
*/
|
|
44
|
+
/**
|
|
45
|
+
* Owner-decision push payload (design §9, owner decision D1 2026-10-28). The ONLY mission text that
|
|
46
|
+
* reaches the server: the question (<=80 chars) and up to 3 option labels (<=24 chars). Never
|
|
47
|
+
* `context`, free-text answers, mission goal/summary or chat content.
|
|
48
|
+
*/
|
|
49
|
+
export type DecisionPushWire = {
|
|
50
|
+
meshId: string;
|
|
51
|
+
missionId: string;
|
|
52
|
+
itemId: string;
|
|
53
|
+
question: string;
|
|
54
|
+
options: Array<{
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
}>;
|
|
58
|
+
recommended?: string;
|
|
59
|
+
urgency: 'normal' | 'blocking';
|
|
60
|
+
dueAt?: string;
|
|
61
|
+
/** `new` = freshly asked; `remind` = a blocking decision crossed its due time. */
|
|
62
|
+
reason: 'new' | 'remind';
|
|
63
|
+
};
|
|
64
|
+
export declare const DECISION_PUSH_QUESTION_MAX = 80;
|
|
65
|
+
export declare const DECISION_PUSH_OPTION_LABEL_MAX = 24;
|
|
66
|
+
export declare const DECISION_PUSH_OPTIONS_MAX = 3;
|
|
44
67
|
export type DaemonStatusEventWirePayload = {
|
|
45
68
|
event: DaemonStatusEventName;
|
|
46
69
|
timestamp: number;
|
|
@@ -52,8 +75,13 @@ export type DaemonStatusEventWirePayload = {
|
|
|
52
75
|
elapsedSec?: number;
|
|
53
76
|
modalMessage?: string;
|
|
54
77
|
modalButtons?: string[];
|
|
78
|
+
modalKey?: string;
|
|
79
|
+
approvalEvidence?: 'live' | 'cached' | 'unavailable';
|
|
80
|
+
waitKind?: 'external';
|
|
55
81
|
surfaceHidden?: boolean;
|
|
56
82
|
muted?: boolean;
|
|
83
|
+
/** Present only on `agent:decision_needed`. */
|
|
84
|
+
decision?: DecisionPushWire;
|
|
57
85
|
};
|
|
58
86
|
/** Routing-only session entry the daemon reports over the server WS. Mirror of daemon-core `RoutingSessionEntry`. */
|
|
59
87
|
export type RoutingSessionWireEntry = {
|
|
@@ -213,11 +241,17 @@ export type P2POfferWirePayload = {
|
|
|
213
241
|
sdp: string;
|
|
214
242
|
type: string;
|
|
215
243
|
peerId: string;
|
|
244
|
+
attemptId?: number;
|
|
245
|
+
attemptEpoch?: string;
|
|
246
|
+
probe?: boolean;
|
|
216
247
|
};
|
|
217
248
|
export type P2PIceWirePayload = {
|
|
218
249
|
candidate: string;
|
|
219
250
|
mid: string;
|
|
220
251
|
peerId: string;
|
|
252
|
+
attemptId?: number;
|
|
253
|
+
attemptEpoch?: string;
|
|
254
|
+
probe?: boolean;
|
|
221
255
|
};
|
|
222
256
|
/**
|
|
223
257
|
* A dashboard-originated signal as relayed to the daemon. `peerId` is stamped
|
|
@@ -229,6 +263,12 @@ export type P2PIceWirePayload = {
|
|
|
229
263
|
export type RelayedP2PSignalWirePayload = {
|
|
230
264
|
peerId?: string;
|
|
231
265
|
daemonId?: string;
|
|
266
|
+
attemptId?: number;
|
|
267
|
+
attemptEpoch?: string;
|
|
268
|
+
/** p2p_ready only: the dashboard's stale-connect backoff index (see DashboardP2PReadyWirePayload). */
|
|
269
|
+
backoffStep?: number;
|
|
270
|
+
/** Make-before-break relay->direct re-probe of an existing peer (see DashboardP2PReadyWirePayload). */
|
|
271
|
+
probe?: boolean;
|
|
232
272
|
sharePermission?: string;
|
|
233
273
|
shareTargetSessionId?: string;
|
|
234
274
|
sdp?: string;
|
|
@@ -34,7 +34,7 @@ export type ChunkFrame<T extends string> = {
|
|
|
34
34
|
export declare function isChunkType(value: unknown): value is `${string}_chunk`;
|
|
35
35
|
export declare const DASHBOARD_TO_DAEMON_P2P_TYPES: readonly ["ping", "pong", "command", "input", "pty_input", "pty_resize", "screenshot_start", "screenshot_stop", "subscribe", "unsubscribe", "seqscribe_session_interest", "read", "write", "list"];
|
|
36
36
|
export type DashboardToDaemonP2PType = typeof DASHBOARD_TO_DAEMON_P2P_TYPES[number];
|
|
37
|
-
export declare const isDashboardToDaemonP2PType: (value: unknown) => value is "list" | "command" | "ping" | "pong" | "input" | "pty_input" | "pty_resize" | "screenshot_start" | "screenshot_stop" | "subscribe" | "unsubscribe" | "seqscribe_session_interest" | "
|
|
37
|
+
export declare const isDashboardToDaemonP2PType: (value: unknown) => value is "read" | "list" | "command" | "ping" | "pong" | "input" | "pty_input" | "pty_resize" | "screenshot_start" | "screenshot_stop" | "subscribe" | "unsubscribe" | "seqscribe_session_interest" | "write";
|
|
38
38
|
export type DashboardToDaemonChunkType = `${DashboardToDaemonP2PType}_chunk`;
|
|
39
39
|
export declare function isDashboardToDaemonChunkType(value: unknown): value is DashboardToDaemonChunkType;
|
|
40
40
|
export type P2PPingMsg = {
|
|
@@ -21,9 +21,9 @@ import { type DaemonStatusEventName, type P2PIceWirePayload, type P2POfferWirePa
|
|
|
21
21
|
/** Server-originated events the dashboard receives in addition to the daemon set. */
|
|
22
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
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"];
|
|
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", "agent:decision_needed", "daemon:disconnect", "team:session_viewed", "team:view_request", "team:view_request_approved", "team:view_request_rejected"];
|
|
25
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";
|
|
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" | "agent:decision_needed" | "daemon:disconnect" | "team:session_viewed" | "team:view_request" | "team:view_request_approved" | "team:view_request_rejected";
|
|
27
27
|
/**
|
|
28
28
|
* Sanitized status event as the server relays it (UserSession
|
|
29
29
|
* `buildDashboardStatusEvent`). Mirror of daemon-core
|
|
@@ -109,12 +109,18 @@ export type SystemAnnouncementWirePayload = {
|
|
|
109
109
|
export type DashboardP2POfferWirePayload = {
|
|
110
110
|
daemonId: string;
|
|
111
111
|
peerId?: string;
|
|
112
|
+
attemptId?: number;
|
|
113
|
+
attemptEpoch?: string;
|
|
114
|
+
probe?: boolean;
|
|
112
115
|
sdp: string;
|
|
113
116
|
type: string;
|
|
114
117
|
};
|
|
115
118
|
export type DashboardP2PIceWirePayload = {
|
|
116
119
|
daemonId: string;
|
|
117
120
|
peerId?: string;
|
|
121
|
+
attemptId?: number;
|
|
122
|
+
attemptEpoch?: string;
|
|
123
|
+
probe?: boolean;
|
|
118
124
|
candidate: string;
|
|
119
125
|
mid?: string;
|
|
120
126
|
sdpMid?: string | null;
|
|
@@ -151,19 +157,31 @@ export declare const isDashboardToUserSessionType: (value: unknown) => value is
|
|
|
151
157
|
export declare const DASHBOARD_P2P_SIGNAL_TYPES: readonly ["p2p_ready", "p2p_answer", "p2p_ice"];
|
|
152
158
|
export type DashboardP2PSignalType = typeof DASHBOARD_P2P_SIGNAL_TYPES[number];
|
|
153
159
|
export declare const isDashboardP2PSignalType: (value: unknown) => value is "p2p_ready" | "p2p_answer" | "p2p_ice";
|
|
160
|
+
/** `backoffStep`: the dashboard's stale-connect backoff index (0 = 15s, 1 = 30s, 2 = 60s), so the daemon holds the connecting slot for the same window. */
|
|
161
|
+
/** `probe`: identifier-only flag — this signal belongs to a make-before-break relay->direct re-probe of the SAME logical peer (never a new peer for the connection cap). */
|
|
154
162
|
export type DashboardP2PReadyWirePayload = {
|
|
155
163
|
daemonId: string;
|
|
156
164
|
peerId: string;
|
|
165
|
+
attemptId?: number;
|
|
166
|
+
attemptEpoch?: string;
|
|
167
|
+
backoffStep?: number;
|
|
168
|
+
probe?: boolean;
|
|
157
169
|
};
|
|
158
170
|
export type DashboardP2PAnswerWirePayload = {
|
|
159
171
|
daemonId: string;
|
|
160
172
|
peerId: string;
|
|
161
173
|
sdp: string;
|
|
162
174
|
type: string;
|
|
175
|
+
attemptId?: number;
|
|
176
|
+
attemptEpoch?: string;
|
|
177
|
+
probe?: boolean;
|
|
163
178
|
};
|
|
164
179
|
export type DashboardP2PLocalIceWirePayload = {
|
|
165
180
|
daemonId: string;
|
|
166
181
|
peerId: string;
|
|
182
|
+
attemptId?: number;
|
|
183
|
+
attemptEpoch?: string;
|
|
184
|
+
probe?: boolean;
|
|
167
185
|
candidate: string;
|
|
168
186
|
sdpMid: string | null;
|
|
169
187
|
sdpMLineIndex: number | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Provider permissions are runtime decisions, never owner write ratification. */
|
|
2
|
+
export interface ProviderApproval {
|
|
3
|
+
source: 'provider_modal' | 'legacy_provider' | 'status_only' | 'ledger_approval';
|
|
4
|
+
/** Exact Inbox actions or the existing session handler; never inferred consent. */
|
|
5
|
+
compatibility?: 'exact' | 'session_only' | 'unknown';
|
|
6
|
+
type: 'tool_permission' | 'provider_setup' | 'unknown';
|
|
7
|
+
evidence: 'live' | 'cached' | 'unavailable';
|
|
8
|
+
lifecycle: 'pending' | 'resolved';
|
|
9
|
+
modalKey: string | null;
|
|
10
|
+
createdAt: number | null;
|
|
11
|
+
resolvedAt: number | null;
|
|
12
|
+
description: string;
|
|
13
|
+
runtimeDirectory?: string;
|
|
14
|
+
displayedDirectory?: string | null;
|
|
15
|
+
buttons: string[];
|
|
16
|
+
actionable: boolean;
|
|
17
|
+
providerType?: string;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
nodeId?: string | null;
|
|
20
|
+
meshId?: string | null;
|
|
21
|
+
taskId?: string | null;
|
|
22
|
+
attemptId?: string | null;
|
|
23
|
+
generation?: number | null;
|
|
24
|
+
}
|
|
25
|
+
/** Exact resource identity required by the approvals-only action path. */
|
|
26
|
+
export interface ProviderApprovalTarget {
|
|
27
|
+
modalKey: string;
|
|
28
|
+
attemptId: string | null;
|
|
29
|
+
generation: number | null;
|
|
30
|
+
}
|
|
31
|
+
/** Confirmed authority for the new Inbox path. Legacy handlers retain their own checks. */
|
|
32
|
+
export declare function isActionableProviderApproval(approval: ProviderApproval | null | undefined): boolean;
|
|
33
|
+
/** An explicit null suppresses the resource; only missing metadata uses compatibility. */
|
|
34
|
+
export declare function readProviderDecision(approval: ProviderApproval | null | undefined, waiting: boolean, hasModal?: boolean): ProviderApproval | null;
|
|
35
|
+
/** Host metadata is durable: identity and clocks only, never modal text or directories. */
|
|
36
|
+
export declare function providerApprovalSnapshot(approval: ProviderApproval | null | undefined): ProviderApproval | null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Ephemeral provenance supplied by the owning daemon, never inferred from prompt text. */
|
|
2
|
+
export interface ProviderQuestionBinding {
|
|
3
|
+
transport: 'codex_rpc' | 'codex_async';
|
|
4
|
+
sessionId: string;
|
|
5
|
+
generation: string;
|
|
6
|
+
threadId: string;
|
|
7
|
+
turnId: string;
|
|
8
|
+
itemId: string;
|
|
9
|
+
requestId?: string | number;
|
|
10
|
+
}
|
|
11
|
+
export type ProviderQuestionLifecycle = 'pending' | 'submitted' | 'resolved' | 'completed' | 'cancelled' | 'expired' | 'unknown';
|
|
12
|
+
export interface ProviderQuestionEpisode {
|
|
13
|
+
blocking?: boolean;
|
|
14
|
+
binding?: ProviderQuestionBinding;
|
|
15
|
+
lifecycle?: ProviderQuestionLifecycle;
|
|
16
|
+
/** Async dismiss is product-local; it is not a native tool cancellation. */
|
|
17
|
+
support?: 'request_bound' | 'product_local' | 'uncertain';
|
|
18
|
+
}
|
|
19
|
+
export declare function isProviderQuestionBinding(value: unknown): value is ProviderQuestionBinding;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota snapshot freshness — the ONE decision every display surface shares.
|
|
3
|
+
*
|
|
4
|
+
* Why this lives in the leaf package: the dashboard chips (web-core
|
|
5
|
+
* `quotaWindowCue`), the `adhdev quota` CLI (daemon-core `quota/cli.ts`) and the
|
|
6
|
+
* coordinator-facing `mesh_status` fold (mcp-server `summarizeNodeQuota`) each
|
|
7
|
+
* carried a hand-copied version of this rule, and a fix to one left the other
|
|
8
|
+
* two telling a different story about the same snapshot. They all call
|
|
9
|
+
* `assessQuotaFreshness` now; only the WORDING stays per-surface.
|
|
10
|
+
*
|
|
11
|
+
* Pure: plain objects in, plain object out, no clock except the `now` argument.
|
|
12
|
+
*/
|
|
13
|
+
import type { MeshNodeFactsProviderQuota } from './node-facts';
|
|
14
|
+
export type QuotaFreshnessCue = 'refreshing' | 'stale';
|
|
15
|
+
export interface QuotaFreshness {
|
|
16
|
+
/** Undefined = nothing to say (a fresh reading, or no retained numbers). */
|
|
17
|
+
cue?: QuotaFreshnessCue;
|
|
18
|
+
/**
|
|
19
|
+
* Only the USER can produce a new reading (aged-out Claude statusline;
|
|
20
|
+
* expired CLI access token — the daemon never redeems it). The surface
|
|
21
|
+
* should say what to run, not promise a self-heal.
|
|
22
|
+
*/
|
|
23
|
+
userMustAct: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Unix ms by which the daemon will have looked again, for a `stale` reading
|
|
26
|
+
* the daemon re-checks on its own. Absent when the user must act, when the
|
|
27
|
+
* reading is `refreshing` (a retry is already scheduled), or when the check
|
|
28
|
+
* is already due.
|
|
29
|
+
*/
|
|
30
|
+
nextCheckAt?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* How long past `metadata.retryAtMs` a retry still reads as "in flight". The
|
|
34
|
+
* retry timer fires at retryAtMs and the probe itself takes a few seconds; a
|
|
35
|
+
* retryAtMs well in the past with no newer attempt means no retry is coming.
|
|
36
|
+
*/
|
|
37
|
+
export declare const QUOTA_RETRY_INFLIGHT_GRACE_MS = 60000;
|
|
38
|
+
/**
|
|
39
|
+
* The daemon's backfill horizon: a snapshot is re-probed once it is this old
|
|
40
|
+
* even on an idle machine. Mirrors daemon-core `QUOTA_ROUTABLE_MAX_AGE_MS`
|
|
41
|
+
* (quota/refresh.ts); daemon-core's quota-freshness test pins the two equal.
|
|
42
|
+
*/
|
|
43
|
+
export declare const QUOTA_BACKFILL_HORIZON_MS: number;
|
|
44
|
+
/**
|
|
45
|
+
* Which freshness cue a snapshot's retained numbers should carry.
|
|
46
|
+
*
|
|
47
|
+
* - `stale` + `userMustAct` — nothing will refresh it without the user:
|
|
48
|
+
* · `no-data` on any provider EXCEPT codex (Claude's statusline aged out —
|
|
49
|
+
* a session must run). Codex's `no-data` is the daemon re-reading the
|
|
50
|
+
* rollout files itself (≈60s while a CLI is active, ≤60 min idle), so it
|
|
51
|
+
* is `stale` WITHOUT userMustAct;
|
|
52
|
+
* · `expired-token` on Grok/Kimi/antigravity — only the owning CLI
|
|
53
|
+
* renews it on use. An idle CLI has no background renewal cadence.
|
|
54
|
+
* A scheduled quota retry does not itself renew credentials.
|
|
55
|
+
* - `refreshing` — retained numbers after a transient failure with a retry
|
|
56
|
+
* actually pending. ★Honest about the end of the retry budget: once the
|
|
57
|
+
* daemon has stopped retrying (`metadata.retryExhausted`, or `retryAtMs`
|
|
58
|
+
* long past with no newer attempt) the numbers are `stale` with the next
|
|
59
|
+
* scheduled check, not "refreshing" for the up-to-60 minutes until the
|
|
60
|
+
* hourly backfill. A snapshot with NO retry bookkeeping at all (a daemon
|
|
61
|
+
* predating the stamp) keeps the old `refreshing` reading.
|
|
62
|
+
*/
|
|
63
|
+
export declare function assessQuotaFreshness(quota: MeshNodeFactsProviderQuota, now?: number): QuotaFreshness;
|
|
64
|
+
/** Whole minutes (min 1) until `nextCheckAt`, for the "next check ~Nm" wording. */
|
|
65
|
+
export declare function minutesUntilQuotaCheck(nextCheckAt: number, now?: number): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relay -> direct re-probe scheduler (pure, dependency-free).
|
|
3
|
+
*
|
|
4
|
+
* A P2P connection that settled on a TURN relay never looks for a direct path
|
|
5
|
+
* again. The owning transport (dashboard `P2PConnection`, daemon peer manager)
|
|
6
|
+
* uses this scheduler to periodically attempt a make-before-break probe: a
|
|
7
|
+
* SECOND peer connection (STUN-only) is built next to the working relay one and
|
|
8
|
+
* only swapped in once it is verified direct. The scheduler owns nothing but
|
|
9
|
+
* timing: backoff 60s -> 5min (doubling), jitter, and the stop conditions. The
|
|
10
|
+
* transport supplies `shouldProbe` (open + idle + visible) and `probe`.
|
|
11
|
+
*/
|
|
12
|
+
export type RelayReprobeOutcome = 'direct' | 'relay' | 'failed' | 'skipped';
|
|
13
|
+
export interface RelayReprobeTiming {
|
|
14
|
+
/** First delay and the unit of growth (default 60s). */
|
|
15
|
+
baseDelayMs?: number;
|
|
16
|
+
/** Backoff ceiling before jitter (default 5min). */
|
|
17
|
+
maxDelayMs?: number;
|
|
18
|
+
/** +/- fraction applied to every delay (default 0.2). */
|
|
19
|
+
jitterRatio?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const RELAY_REPROBE_BASE_DELAY_MS = 60000;
|
|
22
|
+
export declare const RELAY_REPROBE_MAX_DELAY_MS: number;
|
|
23
|
+
export declare const RELAY_REPROBE_JITTER_RATIO = 0.2;
|
|
24
|
+
/** Delay before the next attempt after `failures` consecutive non-direct outcomes. */
|
|
25
|
+
export declare function computeRelayReprobeDelay(failures: number, random: () => number, timing?: RelayReprobeTiming): number;
|
|
26
|
+
export interface RelayReprobeSchedulerOptions extends RelayReprobeTiming {
|
|
27
|
+
random: () => number;
|
|
28
|
+
setTimer: (callback: () => void, ms: number) => unknown;
|
|
29
|
+
clearTimer: (handle: unknown) => void;
|
|
30
|
+
/** Connected over relay, idle, tab visible, auth epoch unchanged... Re-checked on every tick. */
|
|
31
|
+
shouldProbe: () => boolean;
|
|
32
|
+
/** One make-before-break attempt. Must never throw into the scheduler; a rejection counts as 'failed'. */
|
|
33
|
+
probe: () => Promise<RelayReprobeOutcome>;
|
|
34
|
+
}
|
|
35
|
+
export declare class RelayReprobeScheduler {
|
|
36
|
+
private readonly options;
|
|
37
|
+
private handle;
|
|
38
|
+
private running;
|
|
39
|
+
private inFlight;
|
|
40
|
+
private failureCount;
|
|
41
|
+
private generation;
|
|
42
|
+
constructor(options: RelayReprobeSchedulerOptions);
|
|
43
|
+
get isRunning(): boolean;
|
|
44
|
+
get failures(): number;
|
|
45
|
+
get probing(): boolean;
|
|
46
|
+
/** Begin probing (idempotent). Call when a connection is selected as relay. */
|
|
47
|
+
start(): void;
|
|
48
|
+
/** Stop for good (direct path found, connection closed, auth epoch changed). */
|
|
49
|
+
stop(): void;
|
|
50
|
+
private schedule;
|
|
51
|
+
private tick;
|
|
52
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* release — types, limits and pure helpers for release objects
|
|
3
|
+
* (docs/design/2026-11-01-release-objects.md).
|
|
4
|
+
*
|
|
5
|
+
* A release is a mesh-scoped entity that groups MISSIONS (explicit membership) and records
|
|
6
|
+
* deploy facts. It is distinct from the vendor release stamp (a build channel stamp) and
|
|
7
|
+
* from `.adhdev/preview-deploy.json` (the per-checkout deploy record it snapshots).
|
|
8
|
+
* Membership is only ever created by an explicit call; git evaluation and deploy heads
|
|
9
|
+
* produce suggestions, never members. Dependency-free leaf.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MESH_RELEASE_CHANNELS: readonly ["preview", "stable"];
|
|
12
|
+
export type MeshReleaseChannel = typeof MESH_RELEASE_CHANNELS[number];
|
|
13
|
+
export declare function isMeshReleaseChannel(value: unknown): value is MeshReleaseChannel;
|
|
14
|
+
export declare const MESH_RELEASE_STATUSES: readonly ["planned", "frozen", "deployed", "verified", "superseded", "cancelled"];
|
|
15
|
+
export type ReleaseStatus = typeof MESH_RELEASE_STATUSES[number];
|
|
16
|
+
export declare function isReleaseStatus(value: unknown): value is ReleaseStatus;
|
|
17
|
+
export declare const RELEASE_MEMBER_STATES: readonly ["member", "moved_out", "removed"];
|
|
18
|
+
export type ReleaseMemberState = typeof RELEASE_MEMBER_STATES[number];
|
|
19
|
+
/** Who may write release membership. The daemon never adds a member on its own. */
|
|
20
|
+
export declare const RELEASE_ACTORS: readonly ["coordinator", "owner", "assistant", "script"];
|
|
21
|
+
export type ReleaseActor = typeof RELEASE_ACTORS[number];
|
|
22
|
+
export declare const RELEASE_EVENT_TYPES: readonly ["created", "status_changed", "scope_added", "scope_moved", "scope_removed", "deploy_recorded", "changelog_drafted", "changelog_applied"];
|
|
23
|
+
export type ReleaseEventType = typeof RELEASE_EVENT_TYPES[number];
|
|
24
|
+
/** Mission-side events recorded when membership changes (so the mission history shows it). */
|
|
25
|
+
export declare const MISSION_RELEASE_EVENT_TYPES: readonly ["release_attached", "release_moved", "release_detached"];
|
|
26
|
+
export declare const RELEASE_LIMITS: {
|
|
27
|
+
readonly name: 80;
|
|
28
|
+
readonly note: 500;
|
|
29
|
+
/** `mesh_missions.release_note`: one user/operator-visible sentence. */
|
|
30
|
+
readonly releaseNote: 400;
|
|
31
|
+
readonly missionsPerCall: 100;
|
|
32
|
+
readonly changelogEntries: 300;
|
|
33
|
+
};
|
|
34
|
+
/** Deploy targets snapshot (a copy of `.adhdev/preview-deploy.json` `targets`). */
|
|
35
|
+
export interface ReleaseDeployTarget {
|
|
36
|
+
commit?: string;
|
|
37
|
+
deployedAt?: string;
|
|
38
|
+
previewVersion?: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
export type ReleaseTargets = Partial<Record<'npm' | 'server' | 'web', ReleaseDeployTarget>> & Record<string, ReleaseDeployTarget | undefined>;
|
|
42
|
+
export declare const CHANGELOG_SECTIONS: readonly ["Added", "Fixed", "Changed"];
|
|
43
|
+
export type ChangelogSection = typeof CHANGELOG_SECTIONS[number];
|
|
44
|
+
export interface ChangelogDraftEntry {
|
|
45
|
+
missionId: string;
|
|
46
|
+
/** `<!-- m:<id8> -->` — the idempotency marker `--apply` writes after the line. */
|
|
47
|
+
marker: string;
|
|
48
|
+
section: ChangelogSection;
|
|
49
|
+
/** The sentence that would be written to CHANGELOG.md. */
|
|
50
|
+
text: string;
|
|
51
|
+
source: 'release_note' | 'summary';
|
|
52
|
+
/** True when `text` is not user-facing wording yet (carries the needs-wording marker). */
|
|
53
|
+
needsWording?: boolean;
|
|
54
|
+
/** Evidence kept in the draft JSON only; never written to CHANGELOG.md. */
|
|
55
|
+
evidence?: {
|
|
56
|
+
gitState?: string;
|
|
57
|
+
outcome?: string;
|
|
58
|
+
shas?: string[];
|
|
59
|
+
};
|
|
60
|
+
/** Non-blocking lint warnings (internal ids / wave numbers / forbidden phrases). */
|
|
61
|
+
lint?: string[];
|
|
62
|
+
}
|
|
63
|
+
export interface ChangelogDraftExcluded {
|
|
64
|
+
missionId: string;
|
|
65
|
+
reason: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ChangelogDraft {
|
|
68
|
+
generatedAt: string;
|
|
69
|
+
entries: ChangelogDraftEntry[];
|
|
70
|
+
/** Member missions without a `release_note` (target 0 at deploy time). */
|
|
71
|
+
needsNote: string[];
|
|
72
|
+
excluded: ChangelogDraftExcluded[];
|
|
73
|
+
/** Member missions whose refs are not confirmed on the default branch (`not_landed` / `unknown`). */
|
|
74
|
+
warnings: Array<{
|
|
75
|
+
missionId: string;
|
|
76
|
+
gitState: string;
|
|
77
|
+
message: string;
|
|
78
|
+
}>;
|
|
79
|
+
appliedAt?: string;
|
|
80
|
+
}
|
|
81
|
+
export declare const NEEDS_WORDING_MARKER = "[needs user-facing wording]";
|
|
82
|
+
/** Marker that makes `--apply` idempotent: one per mission entry inside CHANGELOG.md. */
|
|
83
|
+
export declare function changelogMissionMarker(missionId: string): string;
|
|
84
|
+
/** Default section for an entry; `[Fixed]`/`[Added]`/`[Changed]` prefixes on the note pin it. */
|
|
85
|
+
export declare function classifyChangelogSection(text: string, hint?: string): {
|
|
86
|
+
section: ChangelogSection;
|
|
87
|
+
text: string;
|
|
88
|
+
};
|
|
89
|
+
/** Phrases that must not reach a public CHANGELOG (internal waves, rc numbering, ids). Warning-only. */
|
|
90
|
+
export declare function lintChangelogText(text: string): string[];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Persisted role-launch intent. Restart replays this selection rather than
|
|
2
|
+
* consulting newer machine defaults; restore preserves its original provenance. */
|
|
3
|
+
import { type LaunchDefaultRole, type LaunchDefaultSelection, type LaunchDefaultAxis, type LaunchDefaultRef } from './launch-defaults';
|
|
4
|
+
export interface RoleLaunchSelection {
|
|
5
|
+
role: LaunchDefaultRole;
|
|
6
|
+
selection: LaunchDefaultSelection;
|
|
7
|
+
applied: Record<keyof LaunchDefaultSelection, LaunchDefaultAxis>;
|
|
8
|
+
defaultRef?: LaunchDefaultRef;
|
|
9
|
+
documentRevision: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseRoleLaunchSelection(value: unknown): RoleLaunchSelection | undefined;
|
package/dist/session-launch.d.ts
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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"];
|
|
1
|
+
import { type RoleLaunchSelection } from './role-launch';
|
|
2
|
+
export declare const MODEL_AXIS_SOURCES: readonly ["user", "remembered", "stored_default", "project_default", "mesh_slot", "task_override", "provider_default", "unspecified"];
|
|
23
3
|
export type ModelAxisSource = typeof MODEL_AXIS_SOURCES[number];
|
|
24
4
|
export declare function isModelAxisSource(value: unknown): value is ModelAxisSource;
|
|
25
5
|
/**
|
|
@@ -61,10 +41,18 @@ export interface ModelSelection {
|
|
|
61
41
|
/** Last value the provider itself reported (statusline, native history). */
|
|
62
42
|
observed?: string;
|
|
63
43
|
observedAt?: number;
|
|
44
|
+
/**
|
|
45
|
+
* The provider SILENTLY substituted another model for the requested one
|
|
46
|
+
* (antigravity: `model X is not recognized … Using "Y" instead`). `observed`
|
|
47
|
+
* then holds the model actually running. P2P/launch-record only — the
|
|
48
|
+
* server path never carries it (see the content boundary above).
|
|
49
|
+
*/
|
|
50
|
+
fallback?: boolean;
|
|
64
51
|
/** Every value change in order. The last entry is the value in force. */
|
|
65
52
|
history: ModelSelectionHistoryEntry[];
|
|
66
53
|
}
|
|
67
54
|
export interface SessionLaunchRecord {
|
|
55
|
+
roleLaunch?: RoleLaunchSelection;
|
|
68
56
|
sessionId: string;
|
|
69
57
|
providerType: string;
|
|
70
58
|
providerVersion?: string;
|
package/dist/slot-proposal.d.ts
CHANGED
|
@@ -35,6 +35,13 @@ import { type MeshTaskDifficulty, type NodeCapabilitySlot } from './brain-routin
|
|
|
35
35
|
export interface CliSlotRecipe {
|
|
36
36
|
/** Optional model to pin on the slot. Best-effort at launch. */
|
|
37
37
|
model?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Optional ordered candidate models to prefer when the provider's live
|
|
40
|
+
* `modelOptions` (from model discovery) is present on `DetectedCliProvider`.
|
|
41
|
+
* The first candidate present in `modelOptions` wins; falls back to `model`
|
|
42
|
+
* when `modelOptions` is absent or contains none of the candidates.
|
|
43
|
+
*/
|
|
44
|
+
modelCandidates?: readonly string[];
|
|
38
45
|
/** Optional thinking level, in the provider's own vocabulary. */
|
|
39
46
|
thinkingLevel?: string;
|
|
40
47
|
/** Difficulty range this slot handles. Empty = general-purpose. */
|
|
@@ -76,6 +83,13 @@ export interface DetectedCliProvider {
|
|
|
76
83
|
displayName?: string;
|
|
77
84
|
/** Detected version, when known. Display only — never affects the mapping. */
|
|
78
85
|
version?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Live discovered / resolved `modelOptions` for this provider on the node,
|
|
88
|
+
* when available on the status catalog. When present and non-empty, recipes
|
|
89
|
+
* prefer the first matching model from `[recipe.model, ...recipe.modelCandidates]`
|
|
90
|
+
* that exists in `modelOptions`.
|
|
91
|
+
*/
|
|
92
|
+
modelOptions?: readonly string[];
|
|
79
93
|
}
|
|
80
94
|
/** One proposed slot plus why it was proposed. */
|
|
81
95
|
export interface ProposedSlotEntry {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { OwnedPathEntry } from './path-ownership';
|
|
2
|
+
export type MeshTaskPhase = 'source' | 'integration';
|
|
3
|
+
/** Host-produced evidence. Never accepted from MCP/peer-supplied paths or branch labels. */
|
|
4
|
+
export interface PhysicalOwnershipScope {
|
|
5
|
+
version: 1;
|
|
6
|
+
repoIdentity: string;
|
|
7
|
+
machineId: string;
|
|
8
|
+
nodeId: string;
|
|
9
|
+
worktree: string;
|
|
10
|
+
worktreeIdentity: string;
|
|
11
|
+
gitDir: string;
|
|
12
|
+
commonGitDir: string;
|
|
13
|
+
phase: MeshTaskPhase;
|
|
14
|
+
gitResources: {
|
|
15
|
+
index: string;
|
|
16
|
+
refs: string;
|
|
17
|
+
hooks: string;
|
|
18
|
+
config: string;
|
|
19
|
+
};
|
|
20
|
+
resources: Array<{
|
|
21
|
+
path: string;
|
|
22
|
+
identity?: string;
|
|
23
|
+
subtree: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
declaration: readonly OwnedPathEntry[];
|
|
26
|
+
}
|
|
27
|
+
/** A source packet is an input to integration, never release/feature acceptance. */
|
|
28
|
+
export interface MeshSourceCandidate {
|
|
29
|
+
version: 1;
|
|
30
|
+
readiness: 'uncommitted' | 'verified_source_candidate';
|
|
31
|
+
taskId: string;
|
|
32
|
+
missionId: string;
|
|
33
|
+
revision: number;
|
|
34
|
+
repoIdentity: string;
|
|
35
|
+
machineId: string;
|
|
36
|
+
canonicalWorktree: string;
|
|
37
|
+
rootBaseSha: string;
|
|
38
|
+
ossBaseSha: string;
|
|
39
|
+
rootCandidateSha?: string;
|
|
40
|
+
ossCandidateSha?: string;
|
|
41
|
+
patchSha256: string;
|
|
42
|
+
manifestSha256: string;
|
|
43
|
+
touchedPaths: string[];
|
|
44
|
+
tests: Array<{
|
|
45
|
+
command: string;
|
|
46
|
+
exit: number;
|
|
47
|
+
logSha256: string;
|
|
48
|
+
}>;
|
|
49
|
+
settledCommands: boolean;
|
|
50
|
+
integrationTaskId: string;
|
|
51
|
+
logicalOverlapTaskIds: string[];
|
|
52
|
+
}
|
|
53
|
+
/** Product B consumes one frozen batch; normal hooks/parity/gates remain required. */
|
|
54
|
+
export interface MeshIntegrationBatchContract {
|
|
55
|
+
version: 1;
|
|
56
|
+
batchId: string;
|
|
57
|
+
candidates: MeshSourceCandidate[];
|
|
58
|
+
integrationOrder: string[];
|
|
59
|
+
approvedRootSha: string;
|
|
60
|
+
approvedOssSha: string;
|
|
61
|
+
bundleInvocationCount: 1;
|
|
62
|
+
requiredGates: string[];
|
|
63
|
+
normalHooksRequired: true;
|
|
64
|
+
independentReviewRequired: true;
|
|
65
|
+
ownerLandingApprovalRequired: true;
|
|
66
|
+
}
|
|
67
|
+
/** These only rank legal targets; targetNodeId/SessionId and requiredTags remain hard. */
|
|
68
|
+
export interface MeshRoutingPreferences {
|
|
69
|
+
nodeIds?: string[];
|
|
70
|
+
providers?: string[];
|
|
71
|
+
}
|
|
72
|
+
export interface MeshReviewConstraint {
|
|
73
|
+
sourceTaskId: string;
|
|
74
|
+
differentProvider: true;
|
|
75
|
+
}
|