@opengeni/react 0.23.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 (38) hide show
  1. package/dist/{chunk-UQS7OQDE.js → chunk-AVPU5PMC.js} +6 -4
  2. package/dist/chunk-AVPU5PMC.js.map +1 -0
  3. package/dist/{chunk-CATATY77.js → chunk-RDPDU4TA.js} +7 -6
  4. package/dist/chunk-RDPDU4TA.js.map +1 -0
  5. package/dist/{chunk-UAHPKLB6.js → chunk-SHOFILHJ.js} +18 -8
  6. package/dist/chunk-SHOFILHJ.js.map +1 -0
  7. package/dist/{composer-CVm5uoUI.d.ts → composer-BXb0Q1HF.d.ts} +18 -4
  8. package/dist/composer.d.ts +2 -2
  9. package/dist/composer.js +2 -2
  10. package/dist/fleet-decision-projection-SLNZMS42.js +241 -0
  11. package/dist/fleet-decision-projection-SLNZMS42.js.map +1 -0
  12. package/dist/fleet-decision-row-GGCAT4E4.js +351 -0
  13. package/dist/fleet-decision-row-GGCAT4E4.js.map +1 -0
  14. package/dist/index.d.ts +12 -9
  15. package/dist/index.js +332 -224
  16. package/dist/index.js.map +1 -1
  17. package/dist/{session-Db00TRwl.d.ts → session-BEvtFWhe.d.ts} +40 -14
  18. package/dist/session.d.ts +2 -2
  19. package/dist/session.js +2 -2
  20. package/dist/{use-composer-By1wn3MM.d.ts → use-composer-BnEzheSk.d.ts} +8 -2
  21. package/package.json +2 -2
  22. package/src/components/composer.tsx +7 -6
  23. package/src/components/message-timeline.tsx +2 -2
  24. package/src/hooks/use-composer.ts +15 -5
  25. package/src/hooks/use-file-attachments.ts +95 -2
  26. package/src/hooks/use-goal.ts +22 -4
  27. package/src/hooks/use-workspace-sessions.ts +30 -5
  28. package/src/timeline/activity-rail.tsx +17 -1
  29. package/src/timeline/fleet-decision-projection.ts +366 -0
  30. package/src/timeline/fleet-decision-row.tsx +412 -0
  31. package/src/timeline/index.ts +2 -0
  32. package/src/timeline/projection.ts +19 -8
  33. package/src/timeline/tool-display-name.ts +16 -0
  34. package/src/timeline/tool-renderers.tsx +1 -1
  35. package/src/timeline/types.ts +68 -1
  36. package/dist/chunk-CATATY77.js.map +0 -1
  37. package/dist/chunk-UAHPKLB6.js.map +0 -1
  38. package/dist/chunk-UQS7OQDE.js.map +0 -1
@@ -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
@@ -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;
@@ -220,10 +280,17 @@ export type TimelineItem =
220
280
  | NoticeItem
221
281
  | AuthNeededItem
222
282
  | MemoryItem
283
+ | FleetDecisionItem
223
284
  | TurnEndItem;
224
285
 
225
286
  /** Activity items cluster between chat messages (reasoning, tools, workers, sandbox, memory). */
226
- export type ActivityItem = ReasoningItem | ToolCallItem | WorkerItem | SandboxItem | MemoryItem;
287
+ export type ActivityItem =
288
+ | ReasoningItem
289
+ | ToolCallItem
290
+ | WorkerItem
291
+ | SandboxItem
292
+ | MemoryItem
293
+ | FleetDecisionItem;
227
294
 
228
295
  export type TimelineGroup =
229
296
  | { kind: "item"; item: TimelineItem }