@opengeni/react 0.20.0 → 0.25.0

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 (60) hide show
  1. package/dist/{chunk-6BXBGJ3B.js → chunk-6XUS5VFM.js} +2 -2
  2. package/dist/{chunk-MRSECXRP.js → chunk-AVPU5PMC.js} +87 -57
  3. package/dist/chunk-AVPU5PMC.js.map +1 -0
  4. package/dist/{chunk-HHFA4AFX.js → chunk-I3BJZIG5.js} +15 -1
  5. package/dist/chunk-I3BJZIG5.js.map +1 -0
  6. package/dist/{chunk-LAZ2A743.js → chunk-RDPDU4TA.js} +8 -7
  7. package/dist/chunk-RDPDU4TA.js.map +1 -0
  8. package/dist/{chunk-6UT5OC3P.js → chunk-SHOFILHJ.js} +130 -16
  9. package/dist/chunk-SHOFILHJ.js.map +1 -0
  10. package/dist/{chunk-EBTT3MYN.js → chunk-YDNWMKXO.js} +11 -1
  11. package/dist/chunk-YDNWMKXO.js.map +1 -0
  12. package/dist/{composer-DvUaa5ki.d.ts → composer-BXb0Q1HF.d.ts} +19 -5
  13. package/dist/composer.d.ts +3 -3
  14. package/dist/composer.js +3 -3
  15. package/dist/fleet-decision-projection-SLNZMS42.js +241 -0
  16. package/dist/fleet-decision-projection-SLNZMS42.js.map +1 -0
  17. package/dist/fleet-decision-row-GGCAT4E4.js +351 -0
  18. package/dist/fleet-decision-row-GGCAT4E4.js.map +1 -0
  19. package/dist/index.d.ts +14 -11
  20. package/dist/index.js +466 -244
  21. package/dist/index.js.map +1 -1
  22. package/dist/machines.d.ts +1 -1
  23. package/dist/machines.js +2 -2
  24. package/dist/{queue-surface-implementation-NQMV2ML3.js → queue-surface-implementation-3UW3MIIR.js} +9 -9
  25. package/dist/queue-surface-implementation-3UW3MIIR.js.map +1 -0
  26. package/dist/{session-Gd4iVPYw.d.ts → session-BEvtFWhe.d.ts} +71 -24
  27. package/dist/{session-context-DIFFlXKc.d.ts → session-context-rgrfElMl.d.ts} +7 -1
  28. package/dist/session.d.ts +3 -3
  29. package/dist/session.js +7 -3
  30. package/dist/{use-composer-BCfm4mzX.d.ts → use-composer-BnEzheSk.d.ts} +13 -20
  31. package/package.json +2 -2
  32. package/src/client.ts +4 -0
  33. package/src/components/composer.tsx +7 -6
  34. package/src/components/message-timeline.tsx +197 -25
  35. package/src/components/queue-draft-policy.ts +18 -0
  36. package/src/components/queue-surface-implementation.tsx +6 -10
  37. package/src/components/queue-surface.tsx +1 -0
  38. package/src/hooks/use-composer.ts +143 -83
  39. package/src/hooks/use-file-attachments.ts +95 -2
  40. package/src/hooks/use-goal.ts +22 -4
  41. package/src/hooks/use-session-mcp-approval-policy.ts +161 -0
  42. package/src/hooks/use-workspace-sessions.ts +30 -5
  43. package/src/index.ts +9 -0
  44. package/src/session-context.ts +25 -0
  45. package/src/session.ts +10 -0
  46. package/src/timeline/activity-rail.tsx +17 -1
  47. package/src/timeline/fleet-decision-projection.ts +366 -0
  48. package/src/timeline/fleet-decision-row.tsx +412 -0
  49. package/src/timeline/index.ts +2 -0
  50. package/src/timeline/projection.ts +19 -8
  51. package/src/timeline/tool-display-name.ts +16 -0
  52. package/src/timeline/tool-renderers.tsx +1 -1
  53. package/src/timeline/types.ts +76 -16
  54. package/dist/chunk-6UT5OC3P.js.map +0 -1
  55. package/dist/chunk-EBTT3MYN.js.map +0 -1
  56. package/dist/chunk-HHFA4AFX.js.map +0 -1
  57. package/dist/chunk-LAZ2A743.js.map +0 -1
  58. package/dist/chunk-MRSECXRP.js.map +0 -1
  59. package/dist/queue-surface-implementation-NQMV2ML3.js.map +0 -1
  60. /package/dist/{chunk-6BXBGJ3B.js.map → chunk-6XUS5VFM.js.map} +0 -0
@@ -0,0 +1,412 @@
1
+ import type { FleetDecisionItem } from "./types";
2
+
3
+ type RowJsx = typeof import("react/jsx-runtime").jsx;
4
+ type RowJsxs = typeof import("react/jsx-runtime").jsxs;
5
+
6
+ function GaugeMark({ className, j, s }: { className: string; j: RowJsx; s: RowJsxs }) {
7
+ return s("svg", {
8
+ "aria-hidden": "true",
9
+ className,
10
+ fill: "none",
11
+ height: "24",
12
+ stroke: "currentColor",
13
+ strokeLinecap: "round",
14
+ strokeLinejoin: "round",
15
+ strokeWidth: "2",
16
+ viewBox: "0 0 24 24",
17
+ width: "24",
18
+ children: [
19
+ j("path", { d: "m 12 14 4-4" }, "needle"),
20
+ j("path", { d: "M3.34 19a10 10 0 1 1 17.32 0" }, "arc"),
21
+ ],
22
+ });
23
+ }
24
+
25
+ const FLEET_ACTUAL_REASON_LABEL: Record<FleetDecisionItem["actualReason"], string> = {
26
+ lease_reused: "Reused the fenced lease",
27
+ pin: "Kept the session pin",
28
+ rotation: "Rotated for capacity",
29
+ active: "Used the active subscription",
30
+ all_capped: "All observed subscriptions were capped",
31
+ none: "No production candidate was selected",
32
+ };
33
+
34
+ const FLEET_SHADOW_REASON_LABEL: Record<FleetDecisionItem["shadowReason"], string> = {
35
+ fenced_in_flight: "Kept the fenced in-flight assignment",
36
+ fenced_candidate_missing: "The fenced assignment was missing from the snapshot",
37
+ admission_paced: "Admission would have been paced",
38
+ no_eligible_candidate: "No candidate passed the policy checks",
39
+ overlay_isolated_empty: "Explicit isolation left no eligible candidate",
40
+ best_score: "Selected the lowest-pressure candidate",
41
+ affinity_best: "Session affinity was already best",
42
+ hysteresis_hold: "Hysteresis avoided a low-value switch",
43
+ };
44
+
45
+ const FLEET_COMPARISON_LABEL: Record<FleetDecisionItem["comparison"], string> = {
46
+ match: "Production and shadow matched",
47
+ different_candidate: "Shadow preferred another candidate",
48
+ different_outcome: "Production and shadow outcomes differed",
49
+ not_comparable_truncated: "Comparison limited by bounded candidate data",
50
+ };
51
+
52
+ const FLEET_ADMISSION_REASON_LABEL: Record<FleetDecisionItem["admissionReason"], string> = {
53
+ fenced_in_flight: "In-flight assignment was fenced",
54
+ pacing_disabled: "Admission pacing was disabled",
55
+ capacity_unknown: "Capacity was unknown, so work remained admissible",
56
+ capacity_available: "Observed capacity was available",
57
+ work_conserving_borrow: "Idle capacity was borrowed for standard work",
58
+ manager_priority: "Standard work was paced for queued manager demand",
59
+ standard_starvation_bound: "The standard-work starvation bound was reached",
60
+ capacity_saturated: "Observed dynamic capacity was saturated",
61
+ emergency_fuse: "The emergency fuse blocked new work",
62
+ };
63
+
64
+ const FLEET_REJECTION_LABEL: Record<
65
+ Exclude<FleetDecisionItem["scores"][number]["rejectionReason"], null>,
66
+ string
67
+ > = {
68
+ allocator_disabled: "Allocator disabled",
69
+ unavailable: "Unavailable",
70
+ cooling: "Cooling down",
71
+ quota_ceiling: "Quota ceiling reached",
72
+ overlay_isolation: "Outside explicit isolation",
73
+ };
74
+
75
+ const FLEET_CONFIDENCE_LABEL: Record<FleetDecisionItem["confidence"], string> = {
76
+ unknown: "Unknown confidence",
77
+ low: "Low confidence",
78
+ medium: "Medium confidence",
79
+ high: "High confidence",
80
+ };
81
+
82
+ export default function FleetDecisionRow({
83
+ item,
84
+ d: ActivityDisclosure,
85
+ b: BodyNote,
86
+ j,
87
+ s,
88
+ }: {
89
+ item: FleetDecisionItem;
90
+ d: typeof import("./shared").ActivityDisclosure;
91
+ b: typeof import("./shared").BodyNote;
92
+ j: RowJsx;
93
+ s: RowJsxs;
94
+ }) {
95
+ const scoreRowsHidden = item.scoreRowsTruncatedCount > 0;
96
+ const scores =
97
+ item.scores.length > 0
98
+ ? j(
99
+ "div",
100
+ {
101
+ className: "min-w-0",
102
+ children: [
103
+ j(
104
+ "h4",
105
+ {
106
+ className: "text-og-sm font-medium text-og-fg",
107
+ children: "Bounded candidate scores",
108
+ },
109
+ "heading",
110
+ ),
111
+ j(
112
+ "p",
113
+ {
114
+ className: "mt-0.5 text-og-xs leading-5 text-og-fg-subtle",
115
+ children:
116
+ "Lower scores rank first. Candidate aliases are temporary and local to this event.",
117
+ },
118
+ "description",
119
+ ),
120
+ j(
121
+ "ul",
122
+ {
123
+ className: "mt-2 flex min-w-0 flex-col gap-1.5",
124
+ "aria-label": "Candidate scores",
125
+ children: item.scores.map((score) =>
126
+ s(
127
+ "li",
128
+ {
129
+ className:
130
+ "flex min-w-0 flex-col gap-1 rounded-og-sm bg-og-surface-1 px-2.5 py-2 text-og-sm sm:flex-row sm:items-center sm:gap-3",
131
+ children: [
132
+ j(
133
+ "span",
134
+ {
135
+ className: "font-og-mono text-og-fg",
136
+ children: score.candidateKey,
137
+ },
138
+ "candidate",
139
+ ),
140
+ j(
141
+ "span",
142
+ {
143
+ className: "min-w-0 text-og-fg-muted sm:flex-1",
144
+ children: score.eligible
145
+ ? "Eligible"
146
+ : score.rejectionReason
147
+ ? FLEET_REJECTION_LABEL[score.rejectionReason]
148
+ : "Unavailable",
149
+ },
150
+ "eligibility",
151
+ ),
152
+ s(
153
+ "span",
154
+ {
155
+ className: "font-og-mono text-og-fg-subtle",
156
+ children: [
157
+ "score ",
158
+ formatFleetScore(score.total),
159
+ " ·",
160
+ " ",
161
+ FLEET_CONFIDENCE_LABEL[score.confidence],
162
+ ],
163
+ },
164
+ "score",
165
+ ),
166
+ ],
167
+ },
168
+ score.candidateKey,
169
+ ),
170
+ ),
171
+ },
172
+ "rows",
173
+ ),
174
+ ],
175
+ },
176
+ "score-list",
177
+ )
178
+ : null;
179
+ const truncated =
180
+ item.truncatedCandidateCount > 0
181
+ ? j(
182
+ BodyNote,
183
+ {
184
+ tone: "muted",
185
+ children: [
186
+ item.truncatedCandidateCount,
187
+ " additional ",
188
+ item.truncatedCandidateCount === 1 ? "candidate was" : "candidates were",
189
+ " excluded from this bounded replay record.",
190
+ ],
191
+ },
192
+ "truncated",
193
+ )
194
+ : null;
195
+ const hidden = scoreRowsHidden
196
+ ? j(
197
+ BodyNote,
198
+ {
199
+ tone: "muted",
200
+ children: [
201
+ item.scoreRowsTruncatedCount,
202
+ " additional ",
203
+ item.scoreRowsTruncatedCount === 1 ? "score row was" : "score rows were",
204
+ " hidden by this view’s safety limit.",
205
+ ],
206
+ },
207
+ "hidden",
208
+ )
209
+ : null;
210
+ return j(ActivityDisclosure, {
211
+ icon: j(GaugeMark, { className: "size-3.5", j, s }),
212
+ iconTone: "muted",
213
+ title: "Fleet policy shadow",
214
+ preview: FLEET_COMPARISON_LABEL[item.comparison],
215
+ chip: {
216
+ tone: "muted",
217
+ text:
218
+ item.comparison === "match"
219
+ ? "matched"
220
+ : item.comparison === "not_comparable_truncated"
221
+ ? "limited"
222
+ : "different",
223
+ },
224
+ children: s("section", {
225
+ "aria-label": "Fleet policy shadow details",
226
+ className: "flex min-w-0 flex-col gap-3",
227
+ children: [
228
+ j(
229
+ BodyNote,
230
+ {
231
+ tone: "muted",
232
+ children:
233
+ "Shadow observation only — it did not change the subscription serving this turn.",
234
+ },
235
+ "observation",
236
+ ),
237
+ j(
238
+ "dl",
239
+ {
240
+ className: "grid min-w-0 grid-cols-1 gap-x-5 gap-y-3 sm:grid-cols-2",
241
+ children: [
242
+ j(
243
+ FleetDetail,
244
+ {
245
+ term: "Production",
246
+ value: fleetOutcomeLabel(item.actualOutcome, item.actualCandidateKey),
247
+ note: FLEET_ACTUAL_REASON_LABEL[item.actualReason],
248
+ j,
249
+ s,
250
+ },
251
+ "production",
252
+ ),
253
+ j(
254
+ FleetDetail,
255
+ {
256
+ term: "Shadow policy",
257
+ value: fleetOutcomeLabel(item.shadowOutcome, item.shadowCandidateKey),
258
+ note: FLEET_SHADOW_REASON_LABEL[item.shadowReason],
259
+ j,
260
+ s,
261
+ },
262
+ "shadow",
263
+ ),
264
+ j(
265
+ FleetDetail,
266
+ {
267
+ term: "Comparison",
268
+ value: FLEET_COMPARISON_LABEL[item.comparison],
269
+ j,
270
+ s,
271
+ },
272
+ "comparison",
273
+ ),
274
+ j(
275
+ FleetDetail,
276
+ {
277
+ term: "Decision confidence",
278
+ value: FLEET_CONFIDENCE_LABEL[item.confidence],
279
+ j,
280
+ s,
281
+ },
282
+ "confidence",
283
+ ),
284
+ j(
285
+ FleetDetail,
286
+ {
287
+ term: "Admission",
288
+ value:
289
+ item.admissionOutcome === "admit"
290
+ ? "Would admit new work"
291
+ : "Would pace new work",
292
+ note: FLEET_ADMISSION_REASON_LABEL[item.admissionReason],
293
+ j,
294
+ s,
295
+ },
296
+ "admission",
297
+ ),
298
+ j(
299
+ FleetDetail,
300
+ {
301
+ term: "Observed candidates",
302
+ value: `${item.candidateCount} in the bounded replay`,
303
+ j,
304
+ s,
305
+ },
306
+ "candidate-count",
307
+ ),
308
+ j(
309
+ FleetDetail,
310
+ {
311
+ term: "Idle-capacity borrowing",
312
+ value: item.borrowedIdleCapacity ? "Borrowed for standard work" : "Not borrowed",
313
+ j,
314
+ s,
315
+ },
316
+ "idle-capacity",
317
+ ),
318
+ j(
319
+ FleetDetail,
320
+ {
321
+ term: "Named-policy borrowing",
322
+ value: item.borrowedOverlayCapacity
323
+ ? "Borrowed outside the preferred group"
324
+ : "Not borrowed",
325
+ j,
326
+ s,
327
+ },
328
+ "named-policy",
329
+ ),
330
+ j(
331
+ FleetDetail,
332
+ {
333
+ term: "Stranded capacity",
334
+ value:
335
+ item.strandedEligibleCount === 0
336
+ ? "None"
337
+ : `${item.strandedEligibleCount} eligible ${item.strandedEligibleCount === 1 ? "candidate" : "candidates"}`,
338
+ note:
339
+ item.strandedEligibleCount > 0
340
+ ? "Capacity excluded by explicit isolation; existing fenced turns are unchanged."
341
+ : undefined,
342
+ j,
343
+ s,
344
+ },
345
+ "stranded-capacity",
346
+ ),
347
+ ],
348
+ },
349
+ "details",
350
+ ),
351
+ scores,
352
+ truncated,
353
+ hidden,
354
+ ],
355
+ }),
356
+ });
357
+ }
358
+
359
+ function FleetDetail({
360
+ term,
361
+ value,
362
+ note,
363
+ j,
364
+ s,
365
+ }: {
366
+ term: string;
367
+ value: string;
368
+ note?: string | undefined;
369
+ j: RowJsx;
370
+ s: RowJsxs;
371
+ }) {
372
+ return j("div", {
373
+ className: "min-w-0",
374
+ children: [
375
+ j(
376
+ "dt",
377
+ {
378
+ className: "text-og-xs font-medium uppercase tracking-wide text-og-fg-subtle",
379
+ children: term,
380
+ },
381
+ "term",
382
+ ),
383
+ s(
384
+ "dd",
385
+ {
386
+ className: "mt-0.5 break-words text-og-sm text-og-fg-muted",
387
+ children: [
388
+ j("span", { className: "text-og-fg", children: value }, "value"),
389
+ note
390
+ ? j("span", { className: "mt-0.5 block leading-5", children: note }, "note")
391
+ : null,
392
+ ],
393
+ },
394
+ "description",
395
+ ),
396
+ ],
397
+ });
398
+ }
399
+
400
+ function fleetOutcomeLabel(
401
+ outcome: FleetDecisionItem["actualOutcome"] | FleetDecisionItem["shadowOutcome"],
402
+ candidateKey: string | null,
403
+ ): string {
404
+ if (outcome === "selected" && candidateKey) return `Selected ${candidateKey}`;
405
+ if (outcome === "waiting") return "Waiting for capacity";
406
+ if (outcome === "paced") return "Paced before placement";
407
+ return "No candidate selected";
408
+ }
409
+
410
+ function formatFleetScore(value: number): string {
411
+ return new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(value);
412
+ }
@@ -30,6 +30,8 @@ export type {
30
30
  ActivityItem,
31
31
  AgentMessageItem,
32
32
  AuthNeededItem,
33
+ FleetDecisionItem,
34
+ FleetDecisionScoreItem,
33
35
  GoalItem,
34
36
  MemoryItem,
35
37
  NoticeItem,
@@ -1,4 +1,5 @@
1
1
  import type { SessionEvent, SessionStatus } from "@opengeni/sdk";
2
+ const { default: fleetDecisionItem } = await import("./fleet-decision-projection");
2
3
  import {
3
4
  CREDIT_EXHAUSTION_MESSAGE,
4
5
  humanizeFailureReason,
@@ -19,6 +20,12 @@ import type {
19
20
  ToolCallItem,
20
21
  WorkerItem,
21
22
  } from "./types";
23
+ /** Readable label for a tool call, without leaking an MCP server prefix. */
24
+ export function toolDisplayName(name: string): string {
25
+ const boundary = name.indexOf("__");
26
+ const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
27
+ return toolPart.replace(/[_-]+/g, " ").trim();
28
+ }
22
29
 
23
30
  /* ----------------------------------------------------------------------------
24
31
  Timeline projection
@@ -554,6 +561,15 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
554
561
  break;
555
562
  }
556
563
 
564
+ case "codex.fleet.decision": {
565
+ const decision = fleetDecisionItem(event, payload);
566
+ if (decision) {
567
+ closeStreamingTail();
568
+ items.push(decision);
569
+ }
570
+ break;
571
+ }
572
+
557
573
  case "goal.set":
558
574
  case "goal.updated":
559
575
  case "goal.completed":
@@ -643,6 +659,7 @@ function isActivityItem(item: TimelineItem): item is ActivityItem {
643
659
  case "worker":
644
660
  case "sandbox":
645
661
  case "memory":
662
+ case "fleet-decision":
646
663
  return true;
647
664
  default:
648
665
  return false;
@@ -822,7 +839,8 @@ function isTurnExecutionEvidence(type: string): boolean {
822
839
  type === "credential.auth_needed" ||
823
840
  type.startsWith("rig.setup.") ||
824
841
  type === "codex.capacity.waiting" ||
825
- type === "codex.capacity.resumed"
842
+ type === "codex.capacity.resumed" ||
843
+ type === "codex.fleet.decision"
826
844
  );
827
845
  }
828
846
 
@@ -1307,10 +1325,3 @@ function looksLikeId(value: string): boolean {
1307
1325
  * just the tool ("list organizations"). Names without the `__` boundary (plain
1308
1326
  * built-ins like "session_create") are unaffected.
1309
1327
  */
1310
- export function toolDisplayName(name: string): string {
1311
- // The prefix is a single LEFT boundary (`registryId__toolName`), so split on
1312
- // the FIRST `__` — the tool name itself may contain `__` and must survive whole.
1313
- const boundary = name.indexOf("__");
1314
- const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
1315
- return toolPart.replace(/[_-]+/g, " ").trim();
1316
- }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Readable label for a tool call ("session_create" -> "session create").
3
+ *
4
+ * MCP tools are namespaced `<serverId>__<toolName>` (see prefixedMcpToolName),
5
+ * and for catalog-imported servers that serverId is an opaque slug+hash.
6
+ * De-slugging the whole thing leaked that id into the timeline; strip the
7
+ * server prefix and show just the tool. Names without the `__` boundary (plain
8
+ * built-ins like "session_create") are unaffected.
9
+ */
10
+ export function toolDisplayName(name: string): string {
11
+ // The prefix is a single LEFT boundary (`registryId__toolName`), so split on
12
+ // the FIRST `__` — the tool name itself may contain `__` and must survive whole.
13
+ const boundary = name.indexOf("__");
14
+ const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
15
+ return toolPart.replace(/[_-]+/g, " ").trim();
16
+ }
@@ -52,7 +52,7 @@ import {
52
52
  type DisclosureChip,
53
53
  } from "./shared";
54
54
  import { RawPatch, ToolDiff } from "./tool-diff";
55
- import { toolDisplayName } from "./projection";
55
+ import { toolDisplayName } from "./tool-display-name";
56
56
 
57
57
  /* ----------------------------------------------------------------------------
58
58
  Per-tool renderers
@@ -1,4 +1,4 @@
1
- import type { ResourceRef, SessionStatus, ToolRef } from "@opengeni/sdk";
1
+ import type { ResourceRef, SessionStatus, ToolAuthNeededPayload, ToolRef } from "@opengeni/sdk";
2
2
 
3
3
  /* ----------------------------------------------------------------------------
4
4
  Timeline item types
@@ -140,6 +140,66 @@ export type MemoryItem = {
140
140
  occurredAt: string;
141
141
  };
142
142
 
143
+ export type FleetDecisionScoreItem = {
144
+ candidateKey: string;
145
+ eligible: boolean;
146
+ rejectionReason:
147
+ | "allocator_disabled"
148
+ | "unavailable"
149
+ | "cooling"
150
+ | "quota_ceiling"
151
+ | "overlay_isolation"
152
+ | null;
153
+ total: number;
154
+ confidence: "unknown" | "low" | "medium" | "high";
155
+ };
156
+
157
+ /**
158
+ * One production-vs-shadow placement explanation. Candidate keys are
159
+ * event-local aliases only; no credential/account identity reaches this item.
160
+ */
161
+ export type FleetDecisionItem = {
162
+ kind: "fleet-decision";
163
+ id: string;
164
+ turnId: string | null;
165
+ policyVersion: "adaptive-shadow-v1";
166
+ actualOutcome: "selected" | "waiting" | "none";
167
+ actualCandidateKey: string | null;
168
+ actualReason: "lease_reused" | "pin" | "rotation" | "active" | "all_capped" | "none";
169
+ shadowOutcome: "selected" | "paced" | "none";
170
+ shadowCandidateKey: string | null;
171
+ shadowReason:
172
+ | "fenced_in_flight"
173
+ | "fenced_candidate_missing"
174
+ | "admission_paced"
175
+ | "no_eligible_candidate"
176
+ | "overlay_isolated_empty"
177
+ | "best_score"
178
+ | "affinity_best"
179
+ | "hysteresis_hold";
180
+ comparison: "match" | "different_candidate" | "different_outcome" | "not_comparable_truncated";
181
+ confidence: "unknown" | "low" | "medium" | "high";
182
+ admissionOutcome: "admit" | "pace";
183
+ admissionReason:
184
+ | "fenced_in_flight"
185
+ | "pacing_disabled"
186
+ | "capacity_unknown"
187
+ | "capacity_available"
188
+ | "work_conserving_borrow"
189
+ | "manager_priority"
190
+ | "standard_starvation_bound"
191
+ | "capacity_saturated"
192
+ | "emergency_fuse";
193
+ borrowedIdleCapacity: boolean;
194
+ borrowedOverlayCapacity: boolean;
195
+ strandedEligibleCount: number;
196
+ candidateCount: number;
197
+ truncatedCandidateCount: number;
198
+ scoreRowsTruncatedCount: number;
199
+ scores: FleetDecisionScoreItem[];
200
+ occurredAt: string;
201
+ };
202
+
143
203
  export type SessionStatusItem = {
144
204
  kind: "session-status";
145
205
  id: string;
@@ -167,12 +227,12 @@ export type NoticeItem = {
167
227
  };
168
228
 
169
229
  /**
170
- * A tool call hit a connection that needs action or cannot satisfy the selected
171
- * endpoint/resource contract. Carries the
172
- * structured `tool.auth_needed` or `credential.auth_needed` payload so the renderer can draw a clean inline
173
- * reconnect affordance when recovery is possible, or a calm unavailable state
174
- * for incompatible auth/resource containment. The app can start the right
175
- * recovery flow (OAuth reconnect for the surviving connection, or credential
230
+ * A tool call hit a missing or lapsed connection. The broker reports that
231
+ * condition as a tool error and the turn continues; reconnecting never resumes
232
+ * or replays the original call. Carries the structured `tool.auth_needed`
233
+ * payload so the renderer can draw a clean inline recovery affordance (provider
234
+ * logo + one human line + a Connect/Reconnect button) and the app can start the
235
+ * right flow (OAuth reconnect for the surviving connection, or credential
176
236
  * re-entry for an api-key one). The `reason` shapes the human copy but is never
177
237
  * shown raw.
178
238
  */
@@ -184,14 +244,7 @@ export type AuthNeededItem = {
184
244
  providerDomain: string;
185
245
  /** The lapsed connection to reconnect, when the row survived. */
186
246
  connectionId: string | null;
187
- reason:
188
- | "missing_connection"
189
- | "expired"
190
- | "insufficient_scope"
191
- | "refresh_failed"
192
- | "unsupported_auth"
193
- | "resource_scope_unavailable"
194
- | null;
247
+ reason: ToolAuthNeededPayload["reason"] | null;
195
248
  /** Scopes the provider now needs; may inform the copy, never shown as a raw label. */
196
249
  scopes: string[];
197
250
  /** The OAuth `resource` (RFC 8707) the reconnect should target, when supplied. */
@@ -227,10 +280,17 @@ export type TimelineItem =
227
280
  | NoticeItem
228
281
  | AuthNeededItem
229
282
  | MemoryItem
283
+ | FleetDecisionItem
230
284
  | TurnEndItem;
231
285
 
232
286
  /** Activity items cluster between chat messages (reasoning, tools, workers, sandbox, memory). */
233
- export type ActivityItem = ReasoningItem | ToolCallItem | WorkerItem | SandboxItem | MemoryItem;
287
+ export type ActivityItem =
288
+ | ReasoningItem
289
+ | ToolCallItem
290
+ | WorkerItem
291
+ | SandboxItem
292
+ | MemoryItem
293
+ | FleetDecisionItem;
234
294
 
235
295
  export type TimelineGroup =
236
296
  | { kind: "item"; item: TimelineItem }