@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,366 @@
1
+ import type { SessionEvent } from "@opengeni/sdk";
2
+ import type { FleetDecisionItem, FleetDecisionScoreItem } from "./types";
3
+
4
+ const FLEET_ACTUAL_OUTCOMES = ["selected", "waiting", "none"] as const;
5
+ const FLEET_ACTUAL_REASONS = [
6
+ "lease_reused",
7
+ "pin",
8
+ "rotation",
9
+ "active",
10
+ "all_capped",
11
+ "none",
12
+ ] as const;
13
+ const FLEET_SHADOW_OUTCOMES = ["selected", "paced", "none"] as const;
14
+ const FLEET_SHADOW_REASONS = [
15
+ "fenced_in_flight",
16
+ "fenced_candidate_missing",
17
+ "admission_paced",
18
+ "no_eligible_candidate",
19
+ "overlay_isolated_empty",
20
+ "best_score",
21
+ "affinity_best",
22
+ "hysteresis_hold",
23
+ ] as const;
24
+ const FLEET_COMPARISONS = [
25
+ "match",
26
+ "different_candidate",
27
+ "different_outcome",
28
+ "not_comparable_truncated",
29
+ ] as const;
30
+ const FLEET_CONFIDENCES = ["unknown", "low", "medium", "high"] as const;
31
+ const FLEET_ADMISSION_OUTCOMES = ["admit", "pace"] as const;
32
+ const FLEET_ADMISSION_REASONS = [
33
+ "fenced_in_flight",
34
+ "pacing_disabled",
35
+ "capacity_unknown",
36
+ "capacity_available",
37
+ "work_conserving_borrow",
38
+ "manager_priority",
39
+ "standard_starvation_bound",
40
+ "capacity_saturated",
41
+ "emergency_fuse",
42
+ ] as const;
43
+ type FleetAdmissionReason = (typeof FLEET_ADMISSION_REASONS)[number];
44
+ const FLEET_ADMISSION_REASON_CLASSIFICATION = {
45
+ fenced_in_flight: "admit",
46
+ pacing_disabled: "admit",
47
+ capacity_unknown: "admit",
48
+ capacity_available: "admit",
49
+ work_conserving_borrow: "admit",
50
+ manager_priority: "pace",
51
+ standard_starvation_bound: "admit",
52
+ capacity_saturated: "pace",
53
+ emergency_fuse: "pace",
54
+ } as const satisfies Record<FleetAdmissionReason, "admit" | "pace">;
55
+ const FLEET_REJECTION_REASONS = [
56
+ "allocator_disabled",
57
+ "unavailable",
58
+ "cooling",
59
+ "quota_ceiling",
60
+ "overlay_isolation",
61
+ ] as const;
62
+ const FLEET_MAX_CANDIDATES = 32;
63
+ const FLEET_MAX_COUNT = 1_000_000;
64
+
65
+ /**
66
+ * Project the identity-free fleet event into the deliberately smaller UI
67
+ * contract. This is a trust boundary: session event payloads are untyped, so
68
+ * arbitrary strings (fingerprints, provider errors, future metadata) must never
69
+ * become renderable copy. Only fixed enums and event-local aliases cross it.
70
+ */
71
+ export default function fleetDecisionItem(
72
+ event: SessionEvent,
73
+ payload: Record<string, unknown>,
74
+ ): FleetDecisionItem | null {
75
+ const actual = asRecord(payload.actual);
76
+ const replay = asRecord(payload.replay);
77
+ const input = asRecord(replay.input);
78
+ const decision = asRecord(replay.decision);
79
+ const admission = asRecord(decision.admission);
80
+ if (
81
+ payload.schemaVersion !== 1 ||
82
+ payload.mode !== "shadow" ||
83
+ replay.schemaVersion !== 1 ||
84
+ replay.mode !== "shadow" ||
85
+ replay.policyVersion !== "adaptive-shadow-v1"
86
+ ) {
87
+ return null;
88
+ }
89
+
90
+ const actualOutcome = fleetEnum(actual.outcome, FLEET_ACTUAL_OUTCOMES);
91
+ const actualReason = fleetEnum(actual.reason, FLEET_ACTUAL_REASONS);
92
+ const shadowOutcome = fleetEnum(decision.outcome, FLEET_SHADOW_OUTCOMES);
93
+ const shadowReason = fleetEnum(decision.reason, FLEET_SHADOW_REASONS);
94
+ const comparison = fleetEnum(payload.comparison, FLEET_COMPARISONS);
95
+ const confidence = fleetEnum(decision.confidence, FLEET_CONFIDENCES);
96
+ const admissionOutcome = fleetEnum(admission.outcome, FLEET_ADMISSION_OUTCOMES);
97
+ const admissionReason = fleetEnum(admission.reason, FLEET_ADMISSION_REASONS);
98
+ if (
99
+ !actualOutcome ||
100
+ !actualReason ||
101
+ !shadowOutcome ||
102
+ !shadowReason ||
103
+ !comparison ||
104
+ !confidence ||
105
+ !admissionOutcome ||
106
+ !admissionReason
107
+ ) {
108
+ return null;
109
+ }
110
+ if (
111
+ typeof admission.borrowedIdleCapacity !== "boolean" ||
112
+ typeof decision.borrowedOverlayCapacity !== "boolean"
113
+ ) {
114
+ return null;
115
+ }
116
+
117
+ const actualCandidateKey = fleetCandidateKey(actual.candidateKey);
118
+ const shadowCandidateKey = fleetCandidateKey(decision.selectedCandidateKey);
119
+ if (actualCandidateKey === undefined || shadowCandidateKey === undefined) {
120
+ return null;
121
+ }
122
+ if (
123
+ (actualOutcome === "selected") !== (actualCandidateKey !== null) ||
124
+ (shadowOutcome === "selected") !== (shadowCandidateKey !== null)
125
+ ) {
126
+ return null;
127
+ }
128
+
129
+ const candidates = input.candidates;
130
+ if (!Array.isArray(candidates) || candidates.length > FLEET_MAX_CANDIDATES) {
131
+ return null;
132
+ }
133
+ const candidateKeys = new Set<string>();
134
+ for (const candidate of candidates) {
135
+ const key = asRecord(candidate).key;
136
+ if (!isFleetCandidateAlias(key) || candidateKeys.has(key)) {
137
+ return null;
138
+ }
139
+ candidateKeys.add(key);
140
+ }
141
+
142
+ const truncatedCandidateCount = fleetCount(replay.truncatedCandidateCount);
143
+ const strandedEligibleCount = fleetCount(decision.strandedEligibleCount);
144
+ if (
145
+ truncatedCandidateCount === null ||
146
+ strandedEligibleCount === null ||
147
+ strandedEligibleCount > candidates.length ||
148
+ !fleetDecisionSemanticsAreConsistent({
149
+ actualOutcome,
150
+ actualReason,
151
+ shadowOutcome,
152
+ shadowReason,
153
+ admissionOutcome,
154
+ admissionReason,
155
+ borrowedIdleCapacity: admission.borrowedIdleCapacity,
156
+ borrowedOverlayCapacity: decision.borrowedOverlayCapacity,
157
+ strandedEligibleCount,
158
+ })
159
+ ) {
160
+ return null;
161
+ }
162
+ if (shadowCandidateKey !== null && !candidateKeys.has(shadowCandidateKey)) {
163
+ return null;
164
+ }
165
+ if (
166
+ actualCandidateKey !== null &&
167
+ !candidateKeys.has(actualCandidateKey) &&
168
+ !(comparison === "not_comparable_truncated" && truncatedCandidateCount > 0)
169
+ ) {
170
+ return null;
171
+ }
172
+ if (
173
+ comparison === "not_comparable_truncated" &&
174
+ (truncatedCandidateCount === 0 ||
175
+ actualCandidateKey === null ||
176
+ candidateKeys.has(actualCandidateKey))
177
+ ) {
178
+ return null;
179
+ }
180
+
181
+ if (
182
+ !fleetComparisonIsConsistent(
183
+ comparison,
184
+ actualOutcome,
185
+ actualCandidateKey,
186
+ shadowOutcome,
187
+ shadowCandidateKey,
188
+ )
189
+ ) {
190
+ return null;
191
+ }
192
+
193
+ const rawScores = decision.scores;
194
+ if (!Array.isArray(rawScores)) {
195
+ return null;
196
+ }
197
+ const scores: FleetDecisionScoreItem[] = [];
198
+ const scoreKeys = new Set<string>();
199
+ for (const value of rawScores.slice(0, FLEET_MAX_CANDIDATES)) {
200
+ const score = asRecord(value);
201
+ const candidateKey = score.candidateKey;
202
+ const parsedRejectionReason =
203
+ score.rejectionReason === null
204
+ ? null
205
+ : fleetEnum(score.rejectionReason, FLEET_REJECTION_REASONS);
206
+ if (score.rejectionReason !== null && parsedRejectionReason === null) {
207
+ return null;
208
+ }
209
+ const rejectionReason = parsedRejectionReason;
210
+ const total = fleetFiniteNumber(score.total);
211
+ const scoreConfidence = fleetEnum(score.confidence, FLEET_CONFIDENCES);
212
+ if (
213
+ !isFleetCandidateAlias(candidateKey) ||
214
+ !candidateKeys.has(candidateKey) ||
215
+ scoreKeys.has(candidateKey) ||
216
+ typeof score.eligible !== "boolean" ||
217
+ total === null ||
218
+ !scoreConfidence ||
219
+ (score.eligible ? rejectionReason !== null : rejectionReason === null)
220
+ ) {
221
+ return null;
222
+ }
223
+ scoreKeys.add(candidateKey);
224
+ scores.push({
225
+ candidateKey,
226
+ eligible: score.eligible,
227
+ rejectionReason,
228
+ total,
229
+ confidence: scoreConfidence,
230
+ });
231
+ }
232
+ if (shadowCandidateKey !== null && !scoreKeys.has(shadowCandidateKey)) {
233
+ return null;
234
+ }
235
+
236
+ return {
237
+ kind: "fleet-decision",
238
+ id: event.id,
239
+ turnId: event.turnId ?? null,
240
+ policyVersion: "adaptive-shadow-v1",
241
+ actualOutcome,
242
+ actualCandidateKey,
243
+ actualReason,
244
+ shadowOutcome,
245
+ shadowCandidateKey,
246
+ shadowReason,
247
+ comparison,
248
+ confidence,
249
+ admissionOutcome,
250
+ admissionReason,
251
+ borrowedIdleCapacity: admission.borrowedIdleCapacity,
252
+ borrowedOverlayCapacity: decision.borrowedOverlayCapacity,
253
+ strandedEligibleCount,
254
+ candidateCount: candidates.length,
255
+ truncatedCandidateCount,
256
+ scoreRowsTruncatedCount: Math.max(0, rawScores.length - FLEET_MAX_CANDIDATES),
257
+ scores,
258
+ occurredAt: event.occurredAt,
259
+ };
260
+ }
261
+
262
+ function fleetEnum<const Values extends readonly string[]>(
263
+ value: unknown,
264
+ values: Values,
265
+ ): Values[number] | null {
266
+ return typeof value === "string" && (values as readonly string[]).includes(value)
267
+ ? (value as Values[number])
268
+ : null;
269
+ }
270
+
271
+ /** c00..czz: ordinal aliases scoped to one event, never stable account ids. */
272
+ function isFleetCandidateAlias(value: unknown): value is string {
273
+ return typeof value === "string" && /^c[0-9a-z]{2}$/.test(value);
274
+ }
275
+
276
+ /** `undefined` denotes malformed; `null` is a valid no-selection value. */
277
+ function fleetCandidateKey(value: unknown): string | null | undefined {
278
+ return value === null ? null : isFleetCandidateAlias(value) ? value : undefined;
279
+ }
280
+
281
+ function fleetCount(value: unknown): number | null {
282
+ return typeof value === "number" &&
283
+ Number.isSafeInteger(value) &&
284
+ value >= 0 &&
285
+ value <= FLEET_MAX_COUNT
286
+ ? value
287
+ : null;
288
+ }
289
+
290
+ function fleetFiniteNumber(value: unknown): number | null {
291
+ return typeof value === "number" &&
292
+ Number.isFinite(value) &&
293
+ Math.abs(value) <= Number.MAX_SAFE_INTEGER
294
+ ? value
295
+ : null;
296
+ }
297
+
298
+ function fleetComparisonIsConsistent(
299
+ comparison: FleetDecisionItem["comparison"],
300
+ actualOutcome: FleetDecisionItem["actualOutcome"],
301
+ actualCandidateKey: string | null,
302
+ shadowOutcome: FleetDecisionItem["shadowOutcome"],
303
+ shadowCandidateKey: string | null,
304
+ ): boolean {
305
+ if (comparison === "not_comparable_truncated") {
306
+ return actualOutcome === "selected" && actualCandidateKey !== null;
307
+ }
308
+ const comparableActualOutcome = actualOutcome === "selected" ? "selected" : "none";
309
+ const comparableShadowOutcome = shadowOutcome === "selected" ? "selected" : "none";
310
+ if (comparison === "different_outcome") {
311
+ return comparableActualOutcome !== comparableShadowOutcome;
312
+ }
313
+ if (comparableActualOutcome !== comparableShadowOutcome) {
314
+ return false;
315
+ }
316
+ if (comparison === "different_candidate") {
317
+ return comparableActualOutcome === "selected" && actualCandidateKey !== shadowCandidateKey;
318
+ }
319
+ return comparableActualOutcome !== "selected" || actualCandidateKey === shadowCandidateKey;
320
+ }
321
+
322
+ function fleetDecisionSemanticsAreConsistent(input: {
323
+ actualOutcome: FleetDecisionItem["actualOutcome"];
324
+ actualReason: FleetDecisionItem["actualReason"];
325
+ shadowOutcome: FleetDecisionItem["shadowOutcome"];
326
+ shadowReason: FleetDecisionItem["shadowReason"];
327
+ admissionOutcome: FleetDecisionItem["admissionOutcome"];
328
+ admissionReason: FleetDecisionItem["admissionReason"];
329
+ borrowedIdleCapacity: boolean;
330
+ borrowedOverlayCapacity: boolean;
331
+ strandedEligibleCount: number;
332
+ }): boolean {
333
+ const actualConsistent =
334
+ input.actualOutcome === "selected"
335
+ ? ["lease_reused", "pin", "rotation", "active"].includes(input.actualReason)
336
+ : input.actualOutcome === "waiting"
337
+ ? input.actualReason === "all_capped"
338
+ : input.actualReason === "none";
339
+ const shadowConsistent =
340
+ input.shadowOutcome === "selected"
341
+ ? ["fenced_in_flight", "best_score", "affinity_best", "hysteresis_hold"].includes(
342
+ input.shadowReason,
343
+ )
344
+ : input.shadowOutcome === "paced"
345
+ ? input.shadowReason === "admission_paced"
346
+ : ["fenced_candidate_missing", "no_eligible_candidate", "overlay_isolated_empty"].includes(
347
+ input.shadowReason,
348
+ );
349
+ // Keep this mapping exhaustive so a newly accepted admission reason cannot
350
+ // silently be dropped from the paced/admitted event consistency check.
351
+ const pacedAdmissionReason =
352
+ FLEET_ADMISSION_REASON_CLASSIFICATION[input.admissionReason] === "pace";
353
+ return (
354
+ actualConsistent &&
355
+ shadowConsistent &&
356
+ (input.admissionOutcome === "pace") === pacedAdmissionReason &&
357
+ (input.shadowOutcome === "paced") === (input.admissionOutcome === "pace") &&
358
+ input.borrowedIdleCapacity === (input.admissionReason === "work_conserving_borrow") &&
359
+ (!input.borrowedOverlayCapacity ||
360
+ (input.shadowOutcome === "selected" && input.strandedEligibleCount === 0))
361
+ );
362
+ }
363
+
364
+ function asRecord(value: unknown): Record<string, unknown> {
365
+ return value !== null && typeof value === "object" ? (value as Record<string, unknown>) : {};
366
+ }