@opengeni/react 0.34.1 → 0.34.3
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/{chunk-A2WITZTI.js → chunk-4NMH6UJ3.js} +2 -2
- package/dist/{chunk-GZ4KR67U.js → chunk-ALA3UGWB.js} +2 -2
- package/dist/chunk-ATSTR5P3.js +138 -0
- package/dist/chunk-ATSTR5P3.js.map +1 -0
- package/dist/{chunk-KZRUYCL4.js → chunk-FT2TXNGZ.js} +5 -2
- package/dist/chunk-FT2TXNGZ.js.map +1 -0
- package/dist/{chunk-4QOQ2DSR.js → chunk-LAKLF4PA.js} +14 -1
- package/dist/chunk-LAKLF4PA.js.map +1 -0
- package/dist/{chunk-TCCE5ZU5.js → chunk-MEGI4FS6.js} +38 -9
- package/dist/chunk-MEGI4FS6.js.map +1 -0
- package/dist/{chunk-MS3FBZ6A.js → chunk-PVW56EVR.js} +105 -62
- package/dist/chunk-PVW56EVR.js.map +1 -0
- package/dist/{chunk-L7R5RK6A.js → chunk-QRYJCWGD.js} +1199 -897
- package/dist/chunk-QRYJCWGD.js.map +1 -0
- package/dist/components/chat-composer.d.ts +2 -1
- package/dist/components/composer.d.ts +8 -2
- package/dist/components/copy-button.d.ts +4 -2
- package/dist/components/tip-follow.d.ts +3 -0
- package/dist/composer.d.ts +2 -1
- package/dist/composer.js +4 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +106 -101
- package/dist/index.js.map +1 -1
- package/dist/lib/format.d.ts +2 -0
- package/dist/machines.js +2 -2
- package/dist/session-ui.js +4 -3
- package/dist/session.js +4 -4
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/parsers.d.ts +5 -0
- package/dist/timeline/projection.d.ts +1 -12
- package/dist/timeline/tool-display-name.d.ts +17 -6
- package/dist/workstream-control-event.d.ts +6 -0
- package/package.json +1 -1
- package/src/components/chat-composer.tsx +1 -1
- package/src/components/composer-transcription-control.tsx +70 -57
- package/src/components/composer.tsx +197 -126
- package/src/components/copy-button.tsx +27 -8
- package/src/components/message-timeline.tsx +236 -39
- package/src/components/tip-follow.ts +15 -0
- package/src/composer.ts +1 -1
- package/src/hooks/use-composer.ts +7 -1
- package/src/hooks/use-workspace-capture.ts +6 -0
- package/src/index.ts +2 -1
- package/src/lib/format.ts +14 -0
- package/src/timeline/index.ts +2 -0
- package/src/timeline/parsers.ts +18 -1
- package/src/timeline/projection.ts +50 -20
- package/src/timeline/registry.ts +15 -2
- package/src/timeline/shared.tsx +30 -28
- package/src/timeline/tool-display-name.ts +30 -11
- package/src/timeline/tool-renderers.tsx +347 -16
- package/src/timeline/turn-summary.tsx +5 -2
- package/src/workstream-control-event.ts +6 -0
- package/dist/chunk-4QOQ2DSR.js.map +0 -1
- package/dist/chunk-KZRUYCL4.js.map +0 -1
- package/dist/chunk-L7R5RK6A.js.map +0 -1
- package/dist/chunk-MS3FBZ6A.js.map +0 -1
- package/dist/chunk-TCCE5ZU5.js.map +0 -1
- /package/dist/{chunk-A2WITZTI.js.map → chunk-4NMH6UJ3.js.map} +0 -0
- /package/dist/{chunk-GZ4KR67U.js.map → chunk-ALA3UGWB.js.map} +0 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isCreditExhaustion,
|
|
7
7
|
tryParseJson,
|
|
8
8
|
} from "../lib/format";
|
|
9
|
+
import { mcpToolLeaf, toolMatchesLeaf } from "./tool-display-name";
|
|
9
10
|
import type {
|
|
10
11
|
AgentMessageItem,
|
|
11
12
|
ActivityItem,
|
|
@@ -22,12 +23,8 @@ import type {
|
|
|
22
23
|
ToolCallItem,
|
|
23
24
|
WorkerItem,
|
|
24
25
|
} from "./types";
|
|
25
|
-
|
|
26
|
-
export
|
|
27
|
-
const boundary = name.indexOf("__");
|
|
28
|
-
const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
|
|
29
|
-
return toolPart.replace(/[_-]+/g, " ").trim();
|
|
30
|
-
}
|
|
26
|
+
|
|
27
|
+
export { toolDisplayName, mcpToolLeaf, toolMatchesLeaf } from "./tool-display-name";
|
|
31
28
|
|
|
32
29
|
/* ----------------------------------------------------------------------------
|
|
33
30
|
Timeline projection
|
|
@@ -43,10 +40,23 @@ export function toolDisplayName(name: string): string {
|
|
|
43
40
|
memoized, unit-tested, and re-run incrementally as new events stream in.
|
|
44
41
|
-------------------------------------------------------------------------- */
|
|
45
42
|
|
|
46
|
-
/** Tool
|
|
43
|
+
/** Tool leaves on the first-party OpenGeni MCP server that operate on sessions. */
|
|
47
44
|
const WORKER_SPAWN_TOOL = "session_create";
|
|
48
45
|
const WORKER_MESSAGE_TOOL = "session_send_message";
|
|
49
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Tools whose durable side-effect events already own the timeline (MemoryRow).
|
|
49
|
+
* Emitting a generic tool-call too is double chrome — skip the call.
|
|
50
|
+
*
|
|
51
|
+
* Goal tools are intentionally NOT landmark-only: an agent `goal_set` /
|
|
52
|
+
* `goal_update` / `goal_complete` / `goal_pause` stays an in-cluster tool row,
|
|
53
|
+
* and the matching `goal.*` session event is suppressed below when `actor` is
|
|
54
|
+
* `"agent"`. That keeps mid-turn goal tools from splitting the step rail with
|
|
55
|
+
* a breakaway GoalRow pill. Non-agent goal events (API, create-session,
|
|
56
|
+
* system auto-pause, continuations) still render as landmarks.
|
|
57
|
+
*/
|
|
58
|
+
const LANDMARK_ONLY_TOOL_LEAVES = new Set(["memory_save", "memory_correct"]);
|
|
59
|
+
|
|
50
60
|
export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
51
61
|
const items: TimelineItem[] = [];
|
|
52
62
|
const prescan = prescanTurnAnchors(events);
|
|
@@ -186,6 +196,9 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
186
196
|
open.text = text || open.text;
|
|
187
197
|
}
|
|
188
198
|
open.streaming = false;
|
|
199
|
+
// Completion time is what the footer shows ("finished at"); keep the
|
|
200
|
+
// first-delta stamp only until this event arrives.
|
|
201
|
+
open.occurredAt = event.occurredAt;
|
|
189
202
|
// The SDK can emit a hosted-tool item only after its provider-native
|
|
190
203
|
// operation has completed, even though answer deltas were already
|
|
191
204
|
// streamed. The completed message event is the durable ordering
|
|
@@ -237,13 +250,16 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
237
250
|
const callId = typeof payload.id === "string" ? payload.id : null;
|
|
238
251
|
const args = payload.arguments ?? null;
|
|
239
252
|
closeStreamingTail();
|
|
240
|
-
if (
|
|
253
|
+
if (
|
|
254
|
+
toolMatchesLeaf(name, WORKER_SPAWN_TOOL) ||
|
|
255
|
+
toolMatchesLeaf(name, WORKER_MESSAGE_TOOL)
|
|
256
|
+
) {
|
|
241
257
|
items.push({
|
|
242
258
|
kind: "worker",
|
|
243
259
|
id: event.id,
|
|
244
260
|
turnId,
|
|
245
261
|
callId,
|
|
246
|
-
action: name
|
|
262
|
+
action: toolMatchesLeaf(name, WORKER_SPAWN_TOOL) ? "spawn" : "message",
|
|
247
263
|
prompt: workerPrompt(args),
|
|
248
264
|
workerSessionId: extractSessionRef(args),
|
|
249
265
|
status: "running",
|
|
@@ -251,6 +267,10 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
251
267
|
});
|
|
252
268
|
break;
|
|
253
269
|
}
|
|
270
|
+
if (LANDMARK_ONLY_TOOL_LEAVES.has(mcpToolLeaf(name))) {
|
|
271
|
+
// Goal/memory landmarks arrive as goal.* / memory.* events.
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
254
274
|
// Live Responses `web_search_call` events and the later SDK
|
|
255
275
|
// `RunToolCallItem` share the same item id. Merge so mid-stream cards
|
|
256
276
|
// do not duplicate when the step finally materializes.
|
|
@@ -639,6 +659,11 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
639
659
|
case "goal.resumed":
|
|
640
660
|
case "goal.cleared":
|
|
641
661
|
case "goal.continuation": {
|
|
662
|
+
// Agent tool mutations already appear as tool-call rows in the activity
|
|
663
|
+
// cluster. Re-emitting them as GoalRow landmarks splits "N steps" mid-turn.
|
|
664
|
+
if (shouldSuppressAgentGoalLandmark(event.type, payload)) {
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
642
667
|
items.push({
|
|
643
668
|
kind: "goal",
|
|
644
669
|
id: event.id,
|
|
@@ -1423,6 +1448,22 @@ function goalText(payload: Record<string, unknown>): string | null {
|
|
|
1423
1448
|
return null;
|
|
1424
1449
|
}
|
|
1425
1450
|
|
|
1451
|
+
/**
|
|
1452
|
+
* Agent-owned goal mutations already have an in-cluster tool row. Suppress the
|
|
1453
|
+
* breakaway landmark for those only. `goal.completed` has no actor field today
|
|
1454
|
+
* and is only emitted by the agent tool, so it is always suppressed. API /
|
|
1455
|
+
* system / create-session / continuation landmarks stay visible.
|
|
1456
|
+
*/
|
|
1457
|
+
function shouldSuppressAgentGoalLandmark(type: string, payload: Record<string, unknown>): boolean {
|
|
1458
|
+
if (type === "goal.completed") {
|
|
1459
|
+
return true;
|
|
1460
|
+
}
|
|
1461
|
+
if (type === "goal.set" || type === "goal.updated" || type === "goal.paused") {
|
|
1462
|
+
return payload.actor === "agent";
|
|
1463
|
+
}
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1426
1467
|
/**
|
|
1427
1468
|
* Fold a `memory.saved` / `memory.corrected` event into a {@link MemoryItem}.
|
|
1428
1469
|
* Reads DEFENSIVELY (the payload is untyped `unknown`, no Zod schema): a missing
|
|
@@ -1563,14 +1604,3 @@ export function extractSessionRef(value: unknown, depth = 0): string | null {
|
|
|
1563
1604
|
function looksLikeId(value: string): boolean {
|
|
1564
1605
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
1565
1606
|
}
|
|
1566
|
-
|
|
1567
|
-
/**
|
|
1568
|
-
* Readable label for a tool call ("session_create" -> "session create").
|
|
1569
|
-
*
|
|
1570
|
-
* MCP tools are namespaced `<serverId>__<toolName>` (see prefixedMcpToolName),
|
|
1571
|
-
* and for catalog-imported servers that serverId is an opaque slug+hash
|
|
1572
|
-
* ("mcp-integrations-sh-supabase-com-34ed9dcf1390-0i6tcf8"). De-slugging the
|
|
1573
|
-
* whole thing leaked that id into the timeline; strip the server prefix and show
|
|
1574
|
-
* just the tool ("list organizations"). Names without the `__` boundary (plain
|
|
1575
|
-
* built-ins like "session_create") are unaffected.
|
|
1576
|
-
*/
|
package/src/timeline/registry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
|
+
import { mcpToolLeaf } from "./tool-display-name";
|
|
2
3
|
import type { ToolCallItem } from "./types";
|
|
3
4
|
|
|
4
5
|
/* ----------------------------------------------------------------------------
|
|
@@ -11,7 +12,8 @@ import type { ToolCallItem } from "./types";
|
|
|
11
12
|
Resolution order (most → least specific):
|
|
12
13
|
1. exact match on `raw.type` (e.g. "apply_patch_call", "computer_call")
|
|
13
14
|
2. exact match on the tool `name` (e.g. "exec_command", "web_search_call")
|
|
14
|
-
3.
|
|
15
|
+
3. leaf match after MCP `__` prefix (e.g. opengeni__environment_set_variable)
|
|
16
|
+
4. the registry's generic fallback
|
|
15
17
|
|
|
16
18
|
A consumer extends the defaults without forking by passing overrides to
|
|
17
19
|
`createToolRegistry` — e.g. a custom renderer for their own MCP tool, or a
|
|
@@ -89,7 +91,18 @@ export function createToolRegistry(
|
|
|
89
91
|
return byType;
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
const exact = byName.get(item.name);
|
|
95
|
+
if (exact) {
|
|
96
|
+
return exact;
|
|
97
|
+
}
|
|
98
|
+
const leaf = mcpToolLeaf(item.name);
|
|
99
|
+
if (leaf !== item.name) {
|
|
100
|
+
const byLeaf = byName.get(leaf);
|
|
101
|
+
if (byLeaf) {
|
|
102
|
+
return byLeaf;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return fallback;
|
|
93
106
|
};
|
|
94
107
|
|
|
95
108
|
return { resolve, fallback };
|
package/src/timeline/shared.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CameraIcon, CameraOffIcon, ChevronRightIcon } from "lucide-react";
|
|
2
2
|
import { useState, type ReactNode } from "react";
|
|
3
|
-
import { Collapsible } from "radix-ui";
|
|
4
3
|
import { cn } from "../lib/cn";
|
|
5
4
|
import { stringifyPayload } from "../lib/format";
|
|
6
5
|
import { useForcedDefaultOpen } from "./disclosure-context";
|
|
@@ -207,35 +206,38 @@ export function ActivityDisclosure({
|
|
|
207
206
|
);
|
|
208
207
|
}
|
|
209
208
|
|
|
209
|
+
// Native disclosure (not radix-ui Collapsible): importing `radix-ui` from the
|
|
210
|
+
// timeline registry pulls Popper into the session share-graph and crashes
|
|
211
|
+
// lazy workspace settings (`createPopperScope is not a function`).
|
|
210
212
|
return (
|
|
211
|
-
<
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
213
|
+
<div>
|
|
214
|
+
<div
|
|
215
|
+
role="button"
|
|
216
|
+
tabIndex={0}
|
|
217
|
+
aria-expanded={open}
|
|
218
|
+
data-state={open ? "open" : "closed"}
|
|
219
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
220
|
+
onKeyDown={(event) => {
|
|
221
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
222
|
+
event.preventDefault();
|
|
223
|
+
setOpen((prev) => !prev);
|
|
224
|
+
}
|
|
225
|
+
}}
|
|
226
|
+
data-status={dataStatus}
|
|
227
|
+
className={cn(
|
|
228
|
+
rowClass,
|
|
229
|
+
"cursor-pointer outline-none hover:bg-og-surface-1 hover:text-og-fg",
|
|
230
|
+
"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-0",
|
|
231
|
+
)}
|
|
232
|
+
>
|
|
233
|
+
{inner}
|
|
234
|
+
</div>
|
|
235
|
+
{open ? (
|
|
236
|
+
<div className="mb-2 ml-7 mt-1.5 flex flex-col gap-2 overflow-hidden animate-og-expand">
|
|
237
|
+
{children}
|
|
233
238
|
</div>
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
<div className="mb-2 ml-7 mt-1.5 flex flex-col gap-2">{children}</div>
|
|
237
|
-
</Collapsible.Content>
|
|
238
|
-
</Collapsible.Root>
|
|
239
|
+
) : null}
|
|
240
|
+
</div>
|
|
239
241
|
);
|
|
240
242
|
}
|
|
241
243
|
|
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* MCP / first-party tool naming helpers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* and
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* built-ins like "session_create") are unaffected.
|
|
4
|
+
* Wire names are often `<serverId>__<toolName>` (see prefixedMcpToolName).
|
|
5
|
+
* Matching and titles must use the leaf tool name so `opengeni__session_create`
|
|
6
|
+
* and bare `session_create` resolve the same UI — without inventing previews
|
|
7
|
+
* from argument JSON.
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
|
|
10
|
+
/** Leaf tool name after the first `__` server boundary (or the whole name). */
|
|
11
|
+
export function mcpToolLeaf(name: string): string {
|
|
13
12
|
const boundary = name.indexOf("__");
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return boundary >= 0 ? name.slice(boundary + 2) : name;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* True when `wireName` is exactly `leaf` or ends with `__${leaf}` (MCP prefix).
|
|
18
|
+
* Does not treat arbitrary suffixes as matches — the leaf must be the full
|
|
19
|
+
* right-hand side after `__`.
|
|
20
|
+
*/
|
|
21
|
+
export function toolMatchesLeaf(wireName: string, leaf: string): boolean {
|
|
22
|
+
return wireName === leaf || wireName.endsWith(`__${leaf}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Readable label for a tool call ("session_create" / "opengeni__session_create"
|
|
27
|
+
* → "Session create"). Title-cases the first character of the leaf phrase.
|
|
28
|
+
*/
|
|
29
|
+
export function toolDisplayName(name: string): string {
|
|
30
|
+
const phrase = mcpToolLeaf(name).replace(/[_-]+/g, " ").trim();
|
|
31
|
+
if (!phrase) {
|
|
32
|
+
return name;
|
|
33
|
+
}
|
|
34
|
+
return phrase.charAt(0).toUpperCase() + phrase.slice(1);
|
|
16
35
|
}
|