@opengeni/react 0.27.0 → 0.29.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 (43) hide show
  1. package/README.md +32 -0
  2. package/dist/{chunk-4F2YLLHP.js → chunk-AVLMIWYO.js} +3 -12
  3. package/dist/chunk-AVLMIWYO.js.map +1 -0
  4. package/dist/{chunk-NBYNFI5T.js → chunk-CSJBF3QJ.js} +5 -3
  5. package/dist/chunk-CSJBF3QJ.js.map +1 -0
  6. package/dist/{chunk-XVVQP7A4.js → chunk-IPVOVOW6.js} +2 -2
  7. package/dist/{chunk-OZDLELJQ.js → chunk-OKXCECLG.js} +55 -10
  8. package/dist/chunk-OKXCECLG.js.map +1 -0
  9. package/dist/{chunk-GQN2QIR2.js → chunk-ZMNPFZWL.js} +238 -87
  10. package/dist/chunk-ZMNPFZWL.js.map +1 -0
  11. package/dist/composer.js +2 -2
  12. package/dist/index.d.ts +7 -34
  13. package/dist/index.js +7 -5
  14. package/dist/index.js.map +1 -1
  15. package/dist/{queue-surface-implementation-3UW3MIIR.js → queue-surface-implementation-UQ535OKQ.js} +44 -12
  16. package/dist/queue-surface-implementation-UQ535OKQ.js.map +1 -0
  17. package/dist/{session-LsampPc6.d.ts → session-DaONt-dY.d.ts} +1 -1
  18. package/dist/{session-ui-BPOV-tuY.d.ts → session-ui-B5_roA95.d.ts} +81 -5
  19. package/dist/session-ui.d.ts +2 -2
  20. package/dist/session-ui.js +4 -2
  21. package/dist/session.d.ts +2 -2
  22. package/dist/session.js +3 -3
  23. package/dist/{use-turn-queue-aDnQvzVz.d.ts → use-turn-queue-D_1a8W_l.d.ts} +26 -4
  24. package/package.json +2 -2
  25. package/src/components/message-timeline.tsx +88 -0
  26. package/src/components/queue-surface-implementation.tsx +79 -11
  27. package/src/components/queue-surface.tsx +5 -5
  28. package/src/hooks/use-composer.ts +1 -15
  29. package/src/hooks/use-turn-queue.ts +8 -0
  30. package/src/index.ts +9 -0
  31. package/src/session-ui.ts +9 -0
  32. package/src/session.ts +2 -0
  33. package/src/timeline/index.ts +13 -2
  34. package/src/timeline/parsers.ts +3 -31
  35. package/src/timeline/projection.ts +73 -9
  36. package/src/timeline/turn-summary.tsx +243 -56
  37. package/src/timeline/types.ts +27 -1
  38. package/dist/chunk-4F2YLLHP.js.map +0 -1
  39. package/dist/chunk-GQN2QIR2.js.map +0 -1
  40. package/dist/chunk-NBYNFI5T.js.map +0 -1
  41. package/dist/chunk-OZDLELJQ.js.map +0 -1
  42. package/dist/queue-surface-implementation-3UW3MIIR.js.map +0 -1
  43. /package/dist/{chunk-XVVQP7A4.js.map → chunk-IPVOVOW6.js.map} +0 -0
@@ -11,6 +11,7 @@ import type {
11
11
  ActivityItem,
12
12
  AuthNeededItem,
13
13
  GoalItem,
14
+ MachineInputBatchItem,
14
15
  MemoryItem,
15
16
  SandboxItem,
16
17
  SessionStatusItem,
@@ -108,14 +109,14 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
108
109
  goalText: childCompletion.goalText,
109
110
  evidence: childCompletion.evidence,
110
111
  pausedReason: childCompletion.pausedReason,
111
- text: typeof payload.text === "string" ? payload.text : "",
112
+ text: stringValue(payload.text),
112
113
  });
113
114
  break;
114
115
  }
115
116
  items.push({
116
117
  kind: "user-message",
117
118
  id: event.id,
118
- text: typeof payload.text === "string" ? payload.text : "",
119
+ text: stringValue(payload.text),
119
120
  resources: resourceRefs(payload.resources),
120
121
  tools: toolRefs(payload.tools),
121
122
  occurredAt: event.occurredAt,
@@ -123,8 +124,22 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
123
124
  break;
124
125
  }
125
126
 
127
+ case "system.update.delivered": {
128
+ const inputs = machineInputMembers(payload.members);
129
+ if (inputs.length === 0) break;
130
+ closeStreamingTail();
131
+ items.push({
132
+ kind: "machine-input-batch",
133
+ id: event.id,
134
+ turnId,
135
+ members: inputs,
136
+ occurredAt: event.occurredAt,
137
+ });
138
+ break;
139
+ }
140
+
126
141
  case "agent.message.delta": {
127
- const text = typeof payload.text === "string" ? payload.text : "";
142
+ const text = stringValue(payload.text);
128
143
  if (!text) {
129
144
  break;
130
145
  }
@@ -146,7 +161,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
146
161
  }
147
162
 
148
163
  case "agent.message.completed": {
149
- const text = typeof payload.text === "string" ? payload.text : "";
164
+ const text = stringValue(payload.text);
150
165
  // Reconcile the most recent same-turn agent message — even when
151
166
  // activity (tool calls, reasoning) landed after its deltas — so the
152
167
  // completed text never duplicates the streamed one.
@@ -454,7 +469,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
454
469
  kind: "auth-needed",
455
470
  id: event.id,
456
471
  turnId,
457
- providerDomain: typeof payload.providerDomain === "string" ? payload.providerDomain : "",
472
+ providerDomain: stringValue(payload.providerDomain),
458
473
  connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
459
474
  reason: authNeededReason(payload.reason),
460
475
  scopes: stringList(payload.scopes),
@@ -968,7 +983,12 @@ function foldSettledTurn(groups: TimelineGroup[], turnEnd: TurnEndItem): void {
968
983
  }
969
984
 
970
985
  function isTurnBoundary(group: TimelineGroup | undefined): boolean {
971
- return group?.kind === "turn" || (group?.kind === "item" && group.item.kind === "user-message");
986
+ return (
987
+ group?.kind === "turn" ||
988
+ (group?.kind === "item" &&
989
+ (group.item.kind === "user-message" ||
990
+ (group.item.kind === "notice" && group.item.tone === "input")))
991
+ );
972
992
  }
973
993
 
974
994
  function belongsToDifferentTurn(group: TimelineGroup | undefined, turnId: string | null): boolean {
@@ -1029,6 +1049,50 @@ function asRecord(value: unknown): Record<string, unknown> {
1029
1049
  return value !== null && typeof value === "object" ? (value as Record<string, unknown>) : {};
1030
1050
  }
1031
1051
 
1052
+ function stringValue(value: unknown): string {
1053
+ return typeof value === "string" ? value : "";
1054
+ }
1055
+
1056
+ function machineInputMembers(value: unknown): MachineInputBatchItem["members"] {
1057
+ const kinds = new Set<MachineInputBatchItem["members"][number]["kind"]>([
1058
+ "scheduled_occurrence",
1059
+ "goal_continuation",
1060
+ "agent_message",
1061
+ "agent_steer_instruction",
1062
+ "child_terminal_result",
1063
+ ]);
1064
+ const classifications = new Set<MachineInputBatchItem["members"][number]["classification"]>([
1065
+ "success",
1066
+ "failure",
1067
+ "action_required",
1068
+ "info",
1069
+ ]);
1070
+ return Array.isArray(value)
1071
+ ? value.flatMap((candidate) => {
1072
+ const member = asRecord(candidate);
1073
+ return typeof member.id === "string" &&
1074
+ typeof member.kind === "string" &&
1075
+ kinds.has(member.kind as MachineInputBatchItem["members"][number]["kind"]) &&
1076
+ typeof member.classification === "string" &&
1077
+ classifications.has(
1078
+ member.classification as MachineInputBatchItem["members"][number]["classification"],
1079
+ ) &&
1080
+ typeof member.sourceId === "string"
1081
+ ? [
1082
+ {
1083
+ id: member.id,
1084
+ kind: member.kind as MachineInputBatchItem["members"][number]["kind"],
1085
+ classification:
1086
+ member.classification as MachineInputBatchItem["members"][number]["classification"],
1087
+ sourceId: member.sourceId,
1088
+ summary: stringValue(member.summary),
1089
+ },
1090
+ ]
1091
+ : [];
1092
+ })
1093
+ : [];
1094
+ }
1095
+
1032
1096
  const SESSION_STATUSES: readonly SessionStatus[] = [
1033
1097
  "queued",
1034
1098
  "running",
@@ -1201,8 +1265,8 @@ function memoryItem(
1201
1265
  id,
1202
1266
  turnId,
1203
1267
  variant: type === "memory.corrected" ? "corrected" : "saved",
1204
- memoryKind: typeof payload.kind === "string" ? payload.kind : "",
1205
- preview: typeof payload.preview === "string" ? payload.preview : "",
1268
+ memoryKind: stringValue(payload.kind),
1269
+ preview: stringValue(payload.preview),
1206
1270
  ...(payload.deduped === true ? { deduped: true } : {}),
1207
1271
  ...(replacementPreview ? { replacementPreview } : {}),
1208
1272
  ...(action ? { action } : {}),
@@ -1245,7 +1309,7 @@ function reasoningText(payload: unknown): string {
1245
1309
  return content
1246
1310
  .map((part) => {
1247
1311
  const text = asRecord(part).text;
1248
- return typeof text === "string" ? text : "";
1312
+ return stringValue(text);
1249
1313
  })
1250
1314
  .join("");
1251
1315
  }
@@ -1,12 +1,12 @@
1
1
  import { CheckIcon, ChevronRightIcon, CircleSlashIcon, TriangleAlertIcon } from "lucide-react";
2
- import { useState } from "react";
2
+ import { Component, useMemo, useState, type ReactNode } from "react";
3
3
  import { Collapsible } from "radix-ui";
4
4
  import { cn } from "../lib/cn";
5
5
  import { useForcedDefaultOpen } from "./disclosure-context";
6
6
  import { useEntranceAnimation } from "./entrance";
7
7
  import { applyPatchOps, isApplyPatch, mediaPreviewFact, screenshotDataUrl } from "./parsers";
8
8
  import { rawTypeOf } from "./registry";
9
- import type { ActivityItem, TurnOutcome } from "./types";
9
+ import type { ActivityItem, ToolCallItem, TurnOutcome } from "./types";
10
10
  export type { TurnOutcome } from "./types";
11
11
 
12
12
  /* ----------------------------------------------------------------------------
@@ -21,6 +21,67 @@ export type { TurnOutcome } from "./types";
21
21
  reader chooses to look inside it.
22
22
  -------------------------------------------------------------------------- */
23
23
 
24
+ export const BUILT_IN_TURN_SUMMARY_FACET_IDS = [
25
+ "steps",
26
+ "files",
27
+ "commands",
28
+ "screenshots",
29
+ "memories",
30
+ "duration",
31
+ ] as const;
32
+
33
+ export type BuiltInTurnSummaryFacetId = (typeof BUILT_IN_TURN_SUMMARY_FACET_IDS)[number];
34
+
35
+ export type TurnSummaryContext = Readonly<{
36
+ /** Every normalized activity item folded into this summary. */
37
+ items: readonly ActivityItem[];
38
+ /** Tool calls from `items`, retained in timeline order for convenient aggregation. */
39
+ toolCalls: readonly ToolCallItem[];
40
+ /** The settled turn verdict, or absent for a neutral/incomplete cluster. */
41
+ outcome: TurnOutcome | undefined;
42
+ /** The bounded failure reason rendered by the enclosing summary, when present. */
43
+ failureText: string | undefined;
44
+ /** Total turn duration when the enclosing group has both valid timestamps. */
45
+ durationMs: number | undefined;
46
+ /** False when any projected activity is still running or streaming. */
47
+ settled: boolean;
48
+ }>;
49
+
50
+ export type TurnSummaryFacetResult = Readonly<{
51
+ icon?: ReactNode;
52
+ content: ReactNode;
53
+ ariaLabel?: string;
54
+ title?: string;
55
+ }>;
56
+
57
+ export type TurnSummaryFacet = Readonly<{
58
+ /** Stable identity used for removal and deterministic de-duplication. */
59
+ id: string;
60
+ /** Return null when this facet has nothing useful to show. */
61
+ summarize(context: TurnSummaryContext): TurnSummaryFacetResult | null;
62
+ }>;
63
+
64
+ type ModifyTurnSummaryFacets = Readonly<{
65
+ /** Appended after the remaining built-ins, in supplied order. */
66
+ add?: readonly TurnSummaryFacet[];
67
+ /** Built-ins to omit before custom facets are appended. */
68
+ remove?: readonly BuiltInTurnSummaryFacetId[];
69
+ replace?: never;
70
+ }>;
71
+
72
+ type ReplaceTurnSummaryFacets = Readonly<{
73
+ /** The complete ordered facet list. Mutually exclusive with add/remove. */
74
+ replace: readonly TurnSummaryFacet[];
75
+ add?: never;
76
+ remove?: never;
77
+ }>;
78
+
79
+ export type TurnSummaryFacetConfiguration = ModifyTurnSummaryFacets | ReplaceTurnSummaryFacets;
80
+
81
+ export type TurnSummaryOptions = Readonly<{
82
+ facets?: TurnSummaryFacetConfiguration;
83
+ }>;
84
+
24
85
  export type TurnSummaryProps = {
25
86
  /** The activity items in the turn (used only to compute the facet counts). */
26
87
  items: ActivityItem[];
@@ -43,8 +104,10 @@ export type TurnSummaryProps = {
43
104
  * of nodes, never a stack of boxes-in-boxes. The top-level fold stays a chip.
44
105
  */
45
106
  bare?: boolean | undefined;
107
+ /** Per-instance facet customization. Omit to preserve the built-in summary exactly. */
108
+ facets?: TurnSummaryFacetConfiguration | undefined;
46
109
  /** The rendered activity rail revealed on expand. */
47
- children: React.ReactNode;
110
+ children: ReactNode;
48
111
  };
49
112
 
50
113
  export function TurnSummary({
@@ -54,6 +117,7 @@ export function TurnSummary({
54
117
  durationMs,
55
118
  defaultOpen,
56
119
  bare,
120
+ facets: facetConfiguration,
57
121
  children,
58
122
  }: TurnSummaryProps) {
59
123
  // An explicit `defaultOpen` always wins; otherwise an ancestor may seed it
@@ -61,7 +125,28 @@ export function TurnSummary({
61
125
  const forcedDefaultOpen = useForcedDefaultOpen();
62
126
  const [open, setOpen] = useState(defaultOpen ?? forcedDefaultOpen ?? false);
63
127
  const enter = useEntranceAnimation();
64
- const facets = summarizeTurn(items, durationMs);
128
+ const context = useMemo(
129
+ () => createTurnSummaryContext(items, outcome, failureText, durationMs),
130
+ [items, outcome, failureText, durationMs],
131
+ );
132
+ const facetDefinitions = useMemo(
133
+ () => resolveTurnSummaryFacets(facetConfiguration),
134
+ [facetConfiguration],
135
+ );
136
+ const facets = useMemo(
137
+ () =>
138
+ facetDefinitions.flatMap((facet) => {
139
+ try {
140
+ const result = facet.summarize(context);
141
+ return result && hasFacetContent(result.content) ? [{ facet, result }] : [];
142
+ } catch {
143
+ // A host extension is presentation-only. It must never take down the
144
+ // durable timeline or hide the remaining built-in evidence.
145
+ return [];
146
+ }
147
+ }),
148
+ [context, facetDefinitions],
149
+ );
65
150
 
66
151
  return (
67
152
  <Collapsible.Root
@@ -117,7 +202,21 @@ export function TurnSummary({
117
202
  )}
118
203
  </span>
119
204
  <span className={cn("min-w-0 flex-1 truncate", bare ? "text-og-sm" : "text-og-fg-muted")}>
120
- {facets}
205
+ {facets.map(({ facet, result }, index) => (
206
+ <FacetRenderBoundary key={facet.id}>
207
+ <>
208
+ {index > 0 ? " · " : null}
209
+ <span aria-label={result.ariaLabel} title={result.title}>
210
+ {result.icon ? (
211
+ <span aria-hidden className="mr-1 inline-flex align-[-0.125em]">
212
+ {result.icon}
213
+ </span>
214
+ ) : null}
215
+ {result.content}
216
+ </span>
217
+ </>
218
+ </FacetRenderBoundary>
219
+ ))}
121
220
  {outcome === "failed" && failureText ? (
122
221
  <span className="text-og-status-failed"> · {failureText}</span>
123
222
  ) : null}
@@ -150,63 +249,151 @@ export function TurnSummary({
150
249
  );
151
250
  }
152
251
 
153
- /** Compose the facet summary line ("14 steps · 3 files · 2 commands · 1 screenshot · 4m"). */
154
- function summarizeTurn(items: ActivityItem[], durationMs?: number): string {
155
- let files = 0;
156
- let commands = 0;
157
- let screenshots = 0;
158
- let memoriesSaved = 0;
159
- let memoriesUpdated = 0;
160
- for (const item of items) {
161
- if (item.kind === "memory") {
162
- // A memory write is a first-class facet — the "wrote 1 memory" signal the
163
- // fold should make prominent — counted saved vs updated separately.
164
- if (item.variant === "corrected") {
165
- memoriesUpdated += 1;
166
- } else {
167
- memoriesSaved += 1;
168
- }
169
- continue;
252
+ function createTurnSummaryContext(
253
+ items: ActivityItem[],
254
+ outcome: TurnOutcome | undefined,
255
+ failureText: string | undefined,
256
+ durationMs: number | undefined,
257
+ ): TurnSummaryContext {
258
+ const itemSnapshot = Object.freeze([...items]);
259
+ const toolCalls = Object.freeze(
260
+ itemSnapshot.filter((item): item is ToolCallItem => item.kind === "tool-call"),
261
+ );
262
+ const settled = itemSnapshot.every((item) => {
263
+ if (item.kind === "reasoning") {
264
+ return !item.streaming;
170
265
  }
171
- if (item.kind !== "tool-call") {
172
- continue;
266
+ if (item.kind === "tool-call" || item.kind === "worker" || item.kind === "sandbox") {
267
+ return item.status !== "running";
173
268
  }
174
- // `item` is narrowed to ToolCallItem by the guard above — no cast needed.
175
- if (isApplyPatch(item)) {
176
- files += applyPatchOps(item.raw).length;
177
- } else if (item.name === "exec_command") {
178
- commands += 1;
179
- } else if (
180
- rawTypeOf(item) === "computer_call" ||
181
- item.name === "computer_call" ||
182
- item.name === "computer_screenshot"
183
- ) {
184
- if (screenshotDataUrl(item.output) !== null || mediaPreviewFact(item.output) !== null) {
185
- screenshots += 1;
269
+ return true;
270
+ });
271
+ return Object.freeze({
272
+ items: itemSnapshot,
273
+ toolCalls,
274
+ outcome,
275
+ failureText,
276
+ durationMs,
277
+ settled,
278
+ });
279
+ }
280
+
281
+ const BUILT_IN_TURN_SUMMARY_FACETS: readonly TurnSummaryFacet[] = Object.freeze([
282
+ {
283
+ id: "steps",
284
+ summarize: ({ items }) => ({
285
+ content: `${items.length} ${items.length === 1 ? "step" : "steps"}`,
286
+ }),
287
+ },
288
+ {
289
+ id: "files",
290
+ summarize: ({ toolCalls }) => {
291
+ let files = 0;
292
+ for (const item of toolCalls) {
293
+ if (isApplyPatch(item)) {
294
+ files += applyPatchOps(item.raw).length;
295
+ }
186
296
  }
297
+ return files ? { content: `${files} ${files === 1 ? "file" : "files"} edited` } : null;
298
+ },
299
+ },
300
+ {
301
+ id: "commands",
302
+ summarize: ({ toolCalls }) => {
303
+ const commands = toolCalls.filter((item) => item.name === "exec_command").length;
304
+ return commands
305
+ ? { content: `${commands} ${commands === 1 ? "command" : "commands"}` }
306
+ : null;
307
+ },
308
+ },
309
+ {
310
+ id: "screenshots",
311
+ summarize: ({ toolCalls }) => {
312
+ let screenshots = 0;
313
+ for (const item of toolCalls) {
314
+ if (
315
+ (rawTypeOf(item) === "computer_call" ||
316
+ item.name === "computer_call" ||
317
+ item.name === "computer_screenshot") &&
318
+ (screenshotDataUrl(item.output) !== null || mediaPreviewFact(item.output) !== null)
319
+ ) {
320
+ screenshots += 1;
321
+ }
322
+ }
323
+ return screenshots
324
+ ? {
325
+ content: `${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`,
326
+ }
327
+ : null;
328
+ },
329
+ },
330
+ {
331
+ id: "memories",
332
+ summarize: ({ items }) => {
333
+ let saved = 0;
334
+ let updated = 0;
335
+ for (const item of items) {
336
+ if (item.kind !== "memory") {
337
+ continue;
338
+ }
339
+ if (item.variant === "corrected") {
340
+ updated += 1;
341
+ } else {
342
+ saved += 1;
343
+ }
344
+ }
345
+ const parts: string[] = [];
346
+ if (saved) {
347
+ parts.push(`${saved} ${saved === 1 ? "memory" : "memories"} saved`);
348
+ }
349
+ if (updated) {
350
+ parts.push(`${updated} ${updated === 1 ? "memory" : "memories"} updated`);
351
+ }
352
+ return parts.length > 0 ? { content: parts.join(" · ") } : null;
353
+ },
354
+ },
355
+ {
356
+ id: "duration",
357
+ summarize: ({ durationMs }) => {
358
+ const duration = formatDurationFacet(durationMs);
359
+ return duration ? { content: duration } : null;
360
+ },
361
+ },
362
+ ]);
363
+
364
+ function resolveTurnSummaryFacets(
365
+ configuration: TurnSummaryFacetConfiguration | undefined,
366
+ ): readonly TurnSummaryFacet[] {
367
+ const requested: readonly TurnSummaryFacet[] = configuration?.replace ?? [
368
+ ...BUILT_IN_TURN_SUMMARY_FACETS.filter(
369
+ (facet) => !configuration?.remove?.includes(facet.id as BuiltInTurnSummaryFacetId),
370
+ ),
371
+ ...(configuration?.add ?? []),
372
+ ];
373
+ const seen = new Set<string>();
374
+ return requested.filter((facet) => {
375
+ if (!facet.id || seen.has(facet.id)) {
376
+ return false;
187
377
  }
378
+ seen.add(facet.id);
379
+ return true;
380
+ });
381
+ }
382
+
383
+ function hasFacetContent(content: ReactNode): boolean {
384
+ return content !== null && content !== undefined && content !== false && content !== "";
385
+ }
386
+
387
+ class FacetRenderBoundary extends Component<{ children: ReactNode }, { failed: boolean }> {
388
+ state = { failed: false };
389
+
390
+ static getDerivedStateFromError(): { failed: boolean } {
391
+ return { failed: true };
188
392
  }
189
- const parts = [`${items.length} ${items.length === 1 ? "step" : "steps"}`];
190
- if (files) {
191
- parts.push(`${files} ${files === 1 ? "file" : "files"} edited`);
192
- }
193
- if (commands) {
194
- parts.push(`${commands} ${commands === 1 ? "command" : "commands"}`);
195
- }
196
- if (screenshots) {
197
- parts.push(`${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`);
198
- }
199
- if (memoriesSaved) {
200
- parts.push(`${memoriesSaved} ${memoriesSaved === 1 ? "memory" : "memories"} saved`);
201
- }
202
- if (memoriesUpdated) {
203
- parts.push(`${memoriesUpdated} ${memoriesUpdated === 1 ? "memory" : "memories"} updated`);
204
- }
205
- const duration = formatDurationFacet(durationMs);
206
- if (duration) {
207
- parts.push(duration);
393
+
394
+ render(): ReactNode {
395
+ return this.state.failed ? null : this.props.children;
208
396
  }
209
- return parts.join(" · ");
210
397
  }
211
398
 
212
399
  function formatDurationFacet(durationMs: number | undefined): string | null {
@@ -218,7 +218,7 @@ export type GoalItem = {
218
218
  export type NoticeItem = {
219
219
  kind: "notice";
220
220
  id: string;
221
- tone: "waiting" | "cancelled" | "failed";
221
+ tone: "waiting" | "cancelled" | "failed" | "input";
222
222
  text: string;
223
223
  /** Optional evidence kept inspectable without overwhelming the main rail. */
224
224
  details?: { label: string; value: unknown };
@@ -226,6 +226,31 @@ export type NoticeItem = {
226
226
  occurredAt: string;
227
227
  };
228
228
 
229
+ export type MachineInputMember = {
230
+ id: string;
231
+ kind:
232
+ | "scheduled_occurrence"
233
+ | "goal_continuation"
234
+ | "agent_message"
235
+ | "agent_steer_instruction"
236
+ | "child_terminal_result";
237
+ classification: "success" | "failure" | "action_required" | "info";
238
+ sourceId: string;
239
+ summary: string;
240
+ };
241
+
242
+ /**
243
+ * One or more durable non-human inputs that joined the following agent turn.
244
+ * This is communication, not a warning or a protocol-debug payload.
245
+ */
246
+ export type MachineInputBatchItem = {
247
+ kind: "machine-input-batch";
248
+ id: string;
249
+ turnId: string | null;
250
+ members: MachineInputMember[];
251
+ occurredAt: string;
252
+ };
253
+
229
254
  /**
230
255
  * A tool call hit a missing or lapsed connection. The broker reports that
231
256
  * condition as a tool error and the turn continues; reconnecting never resumes
@@ -278,6 +303,7 @@ export type TimelineItem =
278
303
  | SessionStatusItem
279
304
  | GoalItem
280
305
  | NoticeItem
306
+ | MachineInputBatchItem
281
307
  | AuthNeededItem
282
308
  | MemoryItem
283
309
  | FleetDecisionItem