@guuey/chat 0.4.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.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/history-inputs.d.ts +24 -0
- package/dist/history-inputs.d.ts.map +1 -0
- package/dist/history-inputs.js +34 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/plan.d.ts +5 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +629 -0
- package/dist/policy.d.ts +110 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +75 -0
- package/dist/react/components.d.ts +87 -0
- package/dist/react/components.d.ts.map +1 -0
- package/dist/react/components.js +270 -0
- package/dist/react/guuey-chat.d.ts +84 -0
- package/dist/react/guuey-chat.d.ts.map +1 -0
- package/dist/react/guuey-chat.js +103 -0
- package/dist/react/markdown.d.ts +32 -0
- package/dist/react/markdown.d.ts.map +1 -0
- package/dist/react/markdown.js +40 -0
- package/dist/react/theme-css.d.ts +16 -0
- package/dist/react/theme-css.d.ts.map +1 -0
- package/dist/react/theme-css.js +37 -0
- package/dist/react/transcript.d.ts +42 -0
- package/dist/react/transcript.d.ts.map +1 -0
- package/dist/react/transcript.js +88 -0
- package/dist/react/use-transcript.d.ts +39 -0
- package/dist/react/use-transcript.d.ts.map +1 -0
- package/dist/react/use-transcript.js +201 -0
- package/dist/react.d.ts +21 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +20 -0
- package/dist/strings.d.ts +74 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +45 -0
- package/dist/theme.d.ts +99 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +182 -0
- package/dist/types.d.ts +283 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +87 -0
- package/src/corpus/README.md +40 -0
- package/src/corpus/__snapshots__/corpus.test.ts.snap +1590 -0
- package/src/corpus/capture.ts +67 -0
- package/src/corpus/captures/issue2627-render-capture.coalesced.sse.txt +173 -0
- package/src/corpus/drive.ts +184 -0
- package/src/corpus/fixtures.ts +338 -0
- package/src/history-inputs.ts +48 -0
- package/src/index.ts +58 -0
- package/src/plan.ts +740 -0
- package/src/policy.ts +146 -0
- package/src/react/components.tsx +655 -0
- package/src/react/guuey-chat.tsx +227 -0
- package/src/react/markdown.tsx +114 -0
- package/src/react/theme-css.ts +50 -0
- package/src/react/transcript.tsx +187 -0
- package/src/react/use-transcript.ts +274 -0
- package/src/react.tsx +51 -0
- package/src/strings.ts +144 -0
- package/src/theme.ts +195 -0
- package/src/types.ts +320 -0
- package/styles.css +514 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture-derived corpus inputs (guuey#135 wave 3c — the lesson from 3b's
|
|
3
|
+
* dogfood finding 1: the hand-driven corpus passed while the PRODUCTION
|
|
4
|
+
* fold shape failed, because real pods fold tool results into separate
|
|
5
|
+
* `role: "tool"` messages the hand-built sequences never produce).
|
|
6
|
+
*
|
|
7
|
+
* The rule this module enforces: at least one corpus family is derived
|
|
8
|
+
* from a REAL redacted production capture, replayed through the REAL
|
|
9
|
+
* `invokeTurn` (parse → status → text fold → AgEvent ingest) — so the
|
|
10
|
+
* corpus can never again be green while production shapes are broken.
|
|
11
|
+
*
|
|
12
|
+
* Provenance: `captures/issue2627-render-capture.coalesced.sse.txt` is a byte copy
|
|
13
|
+
* of `apps/widget/src/fixtures/issue2627-render-capture.coalesced.sse.txt` — the
|
|
14
|
+
* redacted production SSE capture of a ggui-render turn (every id
|
|
15
|
+
* remapped to a stable synthetic one of the same shape, credentials
|
|
16
|
+
* dummied; see the widget's `scripts/derive-coalesced-capture.mjs`).
|
|
17
|
+
* COALESCED is the valid production shape; the RAW variant of the same
|
|
18
|
+
* capture deliberately parks the Reducer and stays in the widget as that
|
|
19
|
+
* regression's pin. Captures are immutable records, so the copy cannot
|
|
20
|
+
* drift.
|
|
21
|
+
*/
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { invokeTurn, type InvokeTurnEvent } from "@guuey/agent-client";
|
|
25
|
+
import { driveTurn, type DriveOptions } from "./drive.js";
|
|
26
|
+
import type { TranscriptInputs } from "../types.js";
|
|
27
|
+
|
|
28
|
+
const CAPTURES = join(import.meta.dirname, "captures");
|
|
29
|
+
|
|
30
|
+
/** The capture's (redacted) render locator — what R6's actionScope must be. */
|
|
31
|
+
export const CAPTURE_RENDER_URI =
|
|
32
|
+
"ui://ggui/render/render_00000000-0000-4000-8000-300000000001/c10a20553df2349b";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Replay a capture through the REAL `invokeTurn`, chunked adversarially
|
|
36
|
+
* (fixed-size slices that split SSE frames mid-line) so the parse path is
|
|
37
|
+
* exercised the way a network delivers it, not a fixture's convenience.
|
|
38
|
+
*/
|
|
39
|
+
export async function captureTurnEvents(
|
|
40
|
+
file: string,
|
|
41
|
+
chunkSize = 512,
|
|
42
|
+
): Promise<InvokeTurnEvent[]> {
|
|
43
|
+
const text = readFileSync(join(CAPTURES, file), "utf8");
|
|
44
|
+
const transport = async function* (): AsyncGenerator<string> {
|
|
45
|
+
for (let at = 0; at < text.length; at += chunkSize) {
|
|
46
|
+
yield text.slice(at, at + chunkSize);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const events: InvokeTurnEvent[] = [];
|
|
50
|
+
const req = {
|
|
51
|
+
url: "https://capture.replay.invalid/agent/invoke",
|
|
52
|
+
body: { input: "capture replay" },
|
|
53
|
+
signal: new AbortController().signal,
|
|
54
|
+
};
|
|
55
|
+
for await (const event of invokeTurn(req, transport)) events.push(event);
|
|
56
|
+
return events;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The ggui-render production capture as `TranscriptInputs`. */
|
|
60
|
+
export async function productionGguiRenderCapture(
|
|
61
|
+
opts: DriveOptions = {},
|
|
62
|
+
): Promise<TranscriptInputs> {
|
|
63
|
+
const events = await captureTurnEvents("issue2627-render-capture.coalesced.sse.txt");
|
|
64
|
+
// The capture ends without a `done` frame (capture-time truncation);
|
|
65
|
+
// the corpus family asserts the SETTLED plan, so the drive settles it.
|
|
66
|
+
return driveTurn(events, { userText: "capture replay", finalStatus: "ready", ...opts });
|
|
67
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
event: session
|
|
2
|
+
data: {"sessionId":"00000000-0000-4000-8000-100000000001","userId":"g_000000000000000000000000000001","authMode":"anonymous","threadId":"00000000-0000-4000-8000-100000000002"}
|
|
3
|
+
|
|
4
|
+
event: message
|
|
5
|
+
data: {"type":"turn.start","seq":0,"turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003"}
|
|
6
|
+
|
|
7
|
+
event: message
|
|
8
|
+
data: {"type":"message.start","seq":1,"id":"msg_000000000000000000000001","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
9
|
+
|
|
10
|
+
event: message
|
|
11
|
+
data: {"type":"reasoning.start","seq":2,"id":"msg_000000000000000000000001:reasoning:0","messageId":"msg_000000000000000000000001","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
12
|
+
|
|
13
|
+
event: message
|
|
14
|
+
data: {"type":"reasoning.delta","seq":3,"id":"msg_000000000000000000000001:reasoning:0","messageId":"msg_000000000000000000000001","delta":"","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
15
|
+
|
|
16
|
+
event: message
|
|
17
|
+
data: {"type":"reasoning.end","seq":4,"id":"msg_000000000000000000000001:reasoning:0","messageId":"msg_000000000000000000000001","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
18
|
+
|
|
19
|
+
event: message
|
|
20
|
+
data: {"type":"reasoning.opaque","seq":5,"id":"msg_000000000000000000000001:reasoning:0","messageId":"msg_000000000000000000000001","kind":"signature","value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","turnId":"turn_00000000-0000-4000-8000-200000000001","provider":"anthropic"}
|
|
21
|
+
|
|
22
|
+
event: message
|
|
23
|
+
data: {"type":"tool.start","seq":6,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000001","name":"mcp__todo__todo_create","index":0,"messageId":"msg_000000000000000000000001","providerExecuted":false}
|
|
24
|
+
|
|
25
|
+
event: message
|
|
26
|
+
data: {"type":"tool.args.delta","seq":7,"toolCallId":"toolu_000000000000000000000001","delta":"{\"title\":\"Buy milk\"}"}
|
|
27
|
+
|
|
28
|
+
event: message
|
|
29
|
+
data: {"type":"tool.args.assembled","seq":8,"toolCallId":"toolu_000000000000000000000001","input":{"title":"Buy milk"}}
|
|
30
|
+
|
|
31
|
+
event: message
|
|
32
|
+
data: {"type":"message.end","seq":9,"id":"msg_000000000000000000000001","usage":{"inputTokens":2,"outputTokens":2,"cacheReadTokens":11664,"cacheWriteTokens":206,"cumulative":true}}
|
|
33
|
+
|
|
34
|
+
event: message
|
|
35
|
+
data: {"type":"tool.done","seq":10,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000001","content":[{"type":"text","text":"{\"id\":\"00000000-0000-4000-8000-100000000004\",\"title\":\"Buy milk\",\"done\":false}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000001:result","structuredContent":{"id":"00000000-0000-4000-8000-100000000004","title":"Buy milk","done":false}}
|
|
36
|
+
|
|
37
|
+
event: message
|
|
38
|
+
data: {"type":"message.start","seq":11,"id":"msg_000000000000000000000002","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
39
|
+
|
|
40
|
+
event: message
|
|
41
|
+
data: {"type":"reasoning.start","seq":12,"id":"msg_000000000000000000000002:reasoning:0","messageId":"msg_000000000000000000000002","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
42
|
+
|
|
43
|
+
event: message
|
|
44
|
+
data: {"type":"reasoning.delta","seq":13,"id":"msg_000000000000000000000002:reasoning:0","messageId":"msg_000000000000000000000002","delta":"","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
45
|
+
|
|
46
|
+
event: message
|
|
47
|
+
data: {"type":"reasoning.end","seq":14,"id":"msg_000000000000000000000002:reasoning:0","messageId":"msg_000000000000000000000002","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
48
|
+
|
|
49
|
+
event: message
|
|
50
|
+
data: {"type":"reasoning.opaque","seq":15,"id":"msg_000000000000000000000002:reasoning:0","messageId":"msg_000000000000000000000002","kind":"signature","value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","turnId":"turn_00000000-0000-4000-8000-200000000001","provider":"anthropic"}
|
|
51
|
+
|
|
52
|
+
event: message
|
|
53
|
+
data: {"type":"tool.start","seq":16,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000002","name":"mcp__todo__todo_list","index":0,"messageId":"msg_000000000000000000000002","providerExecuted":false}
|
|
54
|
+
|
|
55
|
+
event: message
|
|
56
|
+
data: {"type":"tool.args.delta","seq":17,"toolCallId":"toolu_000000000000000000000002","delta":"{}"}
|
|
57
|
+
|
|
58
|
+
event: message
|
|
59
|
+
data: {"type":"tool.args.assembled","seq":18,"toolCallId":"toolu_000000000000000000000002","input":{}}
|
|
60
|
+
|
|
61
|
+
event: message
|
|
62
|
+
data: {"type":"tool.start","seq":19,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000003","name":"mcp__ggui__ggui_discover","index":0,"messageId":"msg_000000000000000000000002","providerExecuted":false}
|
|
63
|
+
|
|
64
|
+
event: message
|
|
65
|
+
data: {"type":"tool.args.delta","seq":20,"toolCallId":"toolu_000000000000000000000003","delta":"{}"}
|
|
66
|
+
|
|
67
|
+
event: message
|
|
68
|
+
data: {"type":"tool.args.assembled","seq":21,"toolCallId":"toolu_000000000000000000000003","input":{}}
|
|
69
|
+
|
|
70
|
+
event: message
|
|
71
|
+
data: {"type":"message.end","seq":22,"id":"msg_000000000000000000000002","usage":{"inputTokens":2,"outputTokens":2,"cacheReadTokens":11870,"cacheWriteTokens":128,"cumulative":true}}
|
|
72
|
+
|
|
73
|
+
event: message
|
|
74
|
+
data: {"type":"tool.done","seq":23,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000002","content":[{"type":"text","text":"{\"todos\":[{\"id\":\"00000000-0000-4000-8000-100000000004\",\"title\":\"Buy milk\",\"done\":false}]}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000002:result","structuredContent":{"todos":[{"id":"00000000-0000-4000-8000-100000000004","title":"Buy milk","done":false}]}}
|
|
75
|
+
|
|
76
|
+
event: message
|
|
77
|
+
data: {"type":"tool.done","seq":24,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000003","content":[{"type":"text","text":"{\"protocolVersion\":\"draft-2026-06-12\",\"contentTypes\":[\"application/javascript+react\"],\"shellTypes\":[\"chat\",\"fullscreen\",\"spatial\"],\"adapterTypes\":[\"voice\",\"camera\",\"location\",\"bluetooth\"],\"componentCapabilities\":[\"network\",\"storage\",\"dom\",\"eval\",\"navigation\",\"device\"]}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000003:result","structuredContent":{"protocolVersion":"draft-2026-06-12","contentTypes":["application/javascript+react"],"shellTypes":["chat","fullscreen","spatial"],"adapterTypes":["voice","camera","location","bluetooth"],"componentCapabilities":["network","storage","dom","eval","navigation","device"]}}
|
|
78
|
+
|
|
79
|
+
event: message
|
|
80
|
+
data: {"type":"message.start","seq":25,"id":"msg_000000000000000000000003","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
81
|
+
|
|
82
|
+
event: message
|
|
83
|
+
data: {"type":"reasoning.start","seq":26,"id":"msg_000000000000000000000003:reasoning:0","messageId":"msg_000000000000000000000003","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
84
|
+
|
|
85
|
+
event: message
|
|
86
|
+
data: {"type":"reasoning.delta","seq":27,"id":"msg_000000000000000000000003:reasoning:0","messageId":"msg_000000000000000000000003","delta":"","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
87
|
+
|
|
88
|
+
event: message
|
|
89
|
+
data: {"type":"reasoning.end","seq":28,"id":"msg_000000000000000000000003:reasoning:0","messageId":"msg_000000000000000000000003","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
90
|
+
|
|
91
|
+
event: message
|
|
92
|
+
data: {"type":"reasoning.opaque","seq":29,"id":"msg_000000000000000000000003:reasoning:0","messageId":"msg_000000000000000000000003","kind":"signature","value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","turnId":"turn_00000000-0000-4000-8000-200000000001","provider":"anthropic"}
|
|
93
|
+
|
|
94
|
+
event: message
|
|
95
|
+
data: {"type":"tool.start","seq":30,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000004","name":"mcp__ggui__ggui_handshake","index":0,"messageId":"msg_000000000000000000000003","providerExecuted":false}
|
|
96
|
+
|
|
97
|
+
event: message
|
|
98
|
+
data: {"type":"tool.args.delta","seq":31,"toolCallId":"toolu_000000000000000000000004","delta":"{\"intent\":\"Show a rich todo list card with items that can be toggled done/undone\",\"blueprintDraft\":{\"contract\":{\"propsSpec\":{\"properties\":{\"todos\":{\"schema\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"},\"done\":{\"type\":\"boolean\"}},\"required\":[\"id\",\"title\",\"done\"]}},\"description\":\"List of todo items\"}}},\"actionSpec\":{\"toggle\":{\"label\":\"Toggle done\",\"description\":\"Toggle a todo's done state\",\"schema\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\"}},\"required\":[\"id\"]},\"nextStep\":\"todo_toggle\"}},\"agentCapabilities\":{\"tools\":{\"todo_toggle\":{\"toolInfo\":{\"description\":\"Flip a todo's done state.\",\"inputSchema\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"string\",\"description\":\"Todo id.\"}},\"required\":[\"id\"]}},\"serverInfo\":{\"name\":\"todo\"}}}}}}}"}
|
|
99
|
+
|
|
100
|
+
event: message
|
|
101
|
+
data: {"type":"tool.args.assembled","seq":32,"toolCallId":"toolu_000000000000000000000004","input":{"intent":"Show a rich todo list card with items that can be toggled done/undone","blueprintDraft":{"contract":{"propsSpec":{"properties":{"todos":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"done":{"type":"boolean"}},"required":["id","title","done"]}},"description":"List of todo items"}}},"actionSpec":{"toggle":{"label":"Toggle done","description":"Toggle a todo's done state","schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"nextStep":"todo_toggle"}},"agentCapabilities":{"tools":{"todo_toggle":{"toolInfo":{"description":"Flip a todo's done state.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"Todo id."}},"required":["id"]}},"serverInfo":{"name":"todo"}}}}}}}}
|
|
102
|
+
|
|
103
|
+
event: message
|
|
104
|
+
data: {"type":"message.end","seq":33,"id":"msg_000000000000000000000003","usage":{"inputTokens":2,"outputTokens":4,"cacheReadTokens":11998,"cacheWriteTokens":320,"cumulative":true}}
|
|
105
|
+
|
|
106
|
+
event: message
|
|
107
|
+
data: {"type":"tool.done","seq":34,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000004","content":[{"type":"text","text":"{\"handshakeId\":\"00000000-0000-4000-8000-100000000005\",\"action\":\"create\",\"suggestion\":{\"origin\":\"agent\",\"rationale\":\"agent draft passed validateContract; accepted verbatim (origin: agent)\",\"blueprintMeta\":{\"contractHash\":\"c10a20553df2349b\",\"variance\":{}},\"proposedContractSummary\":\"slots=∅; actions=toggle(id); streams=∅; props=todos:array\",\"validationFindings\":[{\"code\":\"LINT_MISSING_USAGE\",\"severity\":\"warn\",\"path\":\"agentCapabilities.tools.todo_toggle.usage\",\"message\":\"agentCapabilities.tools.todo_toggle has no 'usage' prose. The agent's reasoning loop reads usage as context-of-use; tools without it tend to get invoked at the wrong time.\"},{\"code\":\"LINT_MISSING_EXAMPLE\",\"severity\":\"warn\",\"path\":\"agentCapabilities.tools.todo_toggle.example\",\"message\":\"agentCapabilities.tools.todo_toggle has no 'example'. Examples ground the agent's invocation patterns; tools without one are harder to use correctly on the first call.\"}]},\"nextStep\":{\"tool\":\"ggui_render\",\"example\":\"ggui_render({\\\"handshakeId\\\":\\\"00000000-0000-4000-8000-100000000005\\\",\\\"props\\\":{}})\"}}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000004:result","structuredContent":{"handshakeId":"00000000-0000-4000-8000-100000000005","action":"create","suggestion":{"origin":"agent","rationale":"agent draft passed validateContract; accepted verbatim (origin: agent)","blueprintMeta":{"contractHash":"c10a20553df2349b","variance":{}},"proposedContractSummary":"slots=∅; actions=toggle(id); streams=∅; props=todos:array","validationFindings":[{"code":"LINT_MISSING_USAGE","severity":"warn","path":"agentCapabilities.tools.todo_toggle.usage","message":"agentCapabilities.tools.todo_toggle has no 'usage' prose. The agent's reasoning loop reads usage as context-of-use; tools without it tend to get invoked at the wrong time."},{"code":"LINT_MISSING_EXAMPLE","severity":"warn","path":"agentCapabilities.tools.todo_toggle.example","message":"agentCapabilities.tools.todo_toggle has no 'example'. Examples ground the agent's invocation patterns; tools without one are harder to use correctly on the first call."}]},"nextStep":{"tool":"ggui_render","example":"ggui_render({\"handshakeId\":\"00000000-0000-4000-8000-100000000005\",\"props\":{}})"}}}
|
|
108
|
+
|
|
109
|
+
event: message
|
|
110
|
+
data: {"type":"message.start","seq":35,"id":"msg_000000000000000000000004","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
111
|
+
|
|
112
|
+
event: message
|
|
113
|
+
data: {"type":"tool.start","seq":36,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000005","name":"mcp__ggui__ggui_render","index":0,"messageId":"msg_000000000000000000000004","providerExecuted":false}
|
|
114
|
+
|
|
115
|
+
event: message
|
|
116
|
+
data: {"type":"tool.args.delta","seq":37,"toolCallId":"toolu_000000000000000000000005","delta":"{\"handshakeId\":\"00000000-0000-4000-8000-100000000005\",\"props\":{\"todos\":[{\"id\":\"00000000-0000-4000-8000-100000000004\",\"title\":\"Buy milk\",\"done\":false}]}}"}
|
|
117
|
+
|
|
118
|
+
event: message
|
|
119
|
+
data: {"type":"tool.args.assembled","seq":38,"toolCallId":"toolu_000000000000000000000005","input":{"handshakeId":"00000000-0000-4000-8000-100000000005","props":{"todos":[{"id":"00000000-0000-4000-8000-100000000004","title":"Buy milk","done":false}]}}}
|
|
120
|
+
|
|
121
|
+
event: message
|
|
122
|
+
data: {"type":"message.end","seq":39,"id":"msg_000000000000000000000004","usage":{"inputTokens":2,"outputTokens":66,"cacheReadTokens":12318,"cacheWriteTokens":956,"cumulative":true}}
|
|
123
|
+
|
|
124
|
+
event: message
|
|
125
|
+
data: {"type":"tool.done","seq":40,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000005","content":[{"type":"text","text":"{\"sessionId\":\"render_00000000-0000-4000-8000-300000000001\",\"resourceUri\":\"ui://ggui/render/render_00000000-0000-4000-8000-300000000001/c10a20553df2349b\",\"action\":\"create\",\"contractHash\":\"c10a20553df2349b\",\"blueprintId\":\"bp_00000000-0000-4000-8000-400000000001\",\"variantKey\":\"44136fa355b3678a\",\"cache\":{\"hit\":false,\"llmCallsAvoided\":0,\"kind\":\"cold\",\"reason\":\"cold: generated fresh — no stored component was reused for this render\"},\"nextStep\":{\"tool\":\"ggui_consume\",\"description\":\"Drain the action pipe for this render — long-polls until a user gesture arrives or 15s timeout.\",\"example\":\"ggui_consume({ sessionId: \\\"render_00000000-0000-4000-8000-300000000001\\\" })\",\"args\":{\"sessionId\":\"render_00000000-0000-4000-8000-300000000001\"}}}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000005:result","uiData":{"sessionId":"render_00000000-0000-4000-8000-300000000001","resourceUri":"ui://ggui/render/render_00000000-0000-4000-8000-300000000001/c10a20553df2349b","action":"create","contractHash":"c10a20553df2349b","blueprintId":"bp_00000000-0000-4000-8000-400000000001","variantKey":"44136fa355b3678a","cache":{"hit":false,"llmCallsAvoided":0,"kind":"cold","reason":"cold: generated fresh — no stored component was reused for this render"},"nextStep":{"tool":"ggui_consume","description":"Drain the action pipe for this render — long-polls until a user gesture arrives or 15s timeout.","example":"ggui_consume({ sessionId: \"render_00000000-0000-4000-8000-300000000001\" })","args":{"sessionId":"render_00000000-0000-4000-8000-300000000001"}}},"_meta":{"ai.ggui/render":{"sessionId":"render_00000000-0000-4000-8000-300000000001","appId":"APP00000","runtimeUrl":"https://dev.mcp.sandbox.ggui.ai/_ggui/iframe-runtime.js","wsUrl":"wss://dev.mcp.sandbox.ggui.ai/ws","wsToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJraW5kIjoid3MiLCJpYXQiOjAsImV4cCI6MH0.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","expiresAt":"2026-07-29T11:07:58.000Z","lastSequence":0,"propsJson":"{\"todos\":[{\"id\":\"00000000-0000-4000-8000-100000000004\",\"title\":\"Buy milk\",\"done\":false}]}"},"ui":{"resourceUri":"ui://ggui/render/render_00000000-0000-4000-8000-300000000001/c10a20553df2349b"},"ui/resourceUri":"ui://ggui/render/render_00000000-0000-4000-8000-300000000001/c10a20553df2349b"}}
|
|
126
|
+
|
|
127
|
+
event: message
|
|
128
|
+
data: {"type":"message.start","seq":41,"id":"msg_000000000000000000000005","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
129
|
+
|
|
130
|
+
event: message
|
|
131
|
+
data: {"type":"text.start","seq":42,"id":"msg_000000000000000000000005:text:0","messageId":"msg_000000000000000000000005","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
132
|
+
|
|
133
|
+
event: message
|
|
134
|
+
data: {"type":"text.delta","seq":43,"id":"msg_000000000000000000000005:text:0","messageId":"msg_000000000000000000000005","delta":"I created \"Buy milk\" and rendered your todo list as a rich UI card — it currently shows that one item, unchecked. I'll keep listening for any toggle actions you make in the card.","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
135
|
+
|
|
136
|
+
event: message
|
|
137
|
+
data: {"type":"text.end","seq":44,"id":"msg_000000000000000000000005:text:0","messageId":"msg_000000000000000000000005","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
138
|
+
|
|
139
|
+
event: message
|
|
140
|
+
data: {"type":"tool.start","seq":45,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000006","name":"mcp__ggui__ggui_consume","index":0,"messageId":"msg_000000000000000000000005","providerExecuted":false}
|
|
141
|
+
|
|
142
|
+
event: message
|
|
143
|
+
data: {"type":"tool.args.delta","seq":46,"toolCallId":"toolu_000000000000000000000006","delta":"{\"sessionId\":\"render_00000000-0000-4000-8000-300000000001\",\"timeout\":20}"}
|
|
144
|
+
|
|
145
|
+
event: message
|
|
146
|
+
data: {"type":"tool.args.assembled","seq":47,"toolCallId":"toolu_000000000000000000000006","input":{"sessionId":"render_00000000-0000-4000-8000-300000000001","timeout":20}}
|
|
147
|
+
|
|
148
|
+
event: message
|
|
149
|
+
data: {"type":"message.end","seq":48,"id":"msg_000000000000000000000005","usage":{"inputTokens":2,"outputTokens":1,"cacheReadTokens":13274,"cacheWriteTokens":543,"cumulative":true}}
|
|
150
|
+
|
|
151
|
+
event: message
|
|
152
|
+
data: {"type":"tool.done","seq":49,"turnId":"turn_00000000-0000-4000-8000-200000000001","toolCallId":"toolu_000000000000000000000006","content":[{"type":"text","text":"{\"events\":[],\"status\":\"active\"}"}],"outcome":"ok","isError":false,"messageId":"toolu_000000000000000000000006:result","structuredContent":{"events":[],"status":"active"}}
|
|
153
|
+
|
|
154
|
+
event: message
|
|
155
|
+
data: {"type":"message.start","seq":50,"id":"msg_000000000000000000000006","turnId":"turn_00000000-0000-4000-8000-200000000001","threadId":"00000000-0000-4000-8000-100000000003","role":"assistant","model":"claude-sonnet-5"}
|
|
156
|
+
|
|
157
|
+
event: message
|
|
158
|
+
data: {"type":"text.start","seq":51,"id":"msg_000000000000000000000006:text:0","messageId":"msg_000000000000000000000006","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
159
|
+
|
|
160
|
+
event: message
|
|
161
|
+
data: {"type":"text.delta","seq":52,"id":"msg_000000000000000000000006:text:0","messageId":"msg_000000000000000000000006","delta":"No actions yet — the card is live and waiting for you. Let me know if you'd like to check it off, add more todos, or anything else, and I'll update the UI accordingly.","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
162
|
+
|
|
163
|
+
event: message
|
|
164
|
+
data: {"type":"text.end","seq":53,"id":"msg_000000000000000000000006:text:0","messageId":"msg_000000000000000000000006","turnId":"turn_00000000-0000-4000-8000-200000000001"}
|
|
165
|
+
|
|
166
|
+
event: message
|
|
167
|
+
data: {"type":"message.end","seq":54,"id":"msg_000000000000000000000006","usage":{"inputTokens":2,"outputTokens":1,"cacheReadTokens":13817,"cacheWriteTokens":189,"cumulative":true}}
|
|
168
|
+
|
|
169
|
+
event: message
|
|
170
|
+
data: {"type":"turn.done","seq":55,"turnId":"turn_00000000-0000-4000-8000-200000000001","outcome":{"type":"success","result":"No actions yet — the card is live and waiting for you. Let me know if you'd like to check it off, add more todos, or anything else, and I'll update the UI accordingly."},"finishReason":"stop","usage":{"inputTokens":12,"outputTokens":1112,"cacheReadTokens":74941,"cacheWriteTokens":2342,"serverToolRequests":0,"costUsd":0.0479808,"cumulative":true,"byModel":{"claude-sonnet-5":{"inputTokens":12,"outputTokens":1112,"cacheReadTokens":74941,"cacheWriteTokens":2342,"costUsd":0.0479808,"serverToolRequests":0,"cumulative":true}}}}
|
|
171
|
+
|
|
172
|
+
event: done
|
|
173
|
+
data: {"stopReason":"end_turn","threadId":"00000000-0000-4000-8000-100000000002","userSeq":1,"agentSeq":2}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The corpus's turn driver: recorded `InvokeTurnEvent[]` sequences →
|
|
3
|
+
* `TranscriptInputs`, applying the SAME accumulation rules the live hook
|
|
4
|
+
* documents (status transitions, cumulative text, agEvents → the REAL
|
|
5
|
+
* `@silverprotocol/core` Reducer). Test-only plumbing — excluded from the
|
|
6
|
+
* build; the 3b live/history assemblers are the production twins.
|
|
7
|
+
*/
|
|
8
|
+
import { Reducer, type AgEvent } from "@silverprotocol/core";
|
|
9
|
+
import type { InvokeTurnEvent } from "@guuey/agent-client";
|
|
10
|
+
import type { PromptItemInput, TranscriptInputs, TranscriptMessage } from "../types.js";
|
|
11
|
+
|
|
12
|
+
export interface DriveOptions {
|
|
13
|
+
userText?: string;
|
|
14
|
+
clientMessageId?: string;
|
|
15
|
+
/** Override the derived status (mid-stream plan points). */
|
|
16
|
+
finalStatus?: TranscriptInputs["status"];
|
|
17
|
+
statusElapsedMs?: number;
|
|
18
|
+
aborted?: boolean;
|
|
19
|
+
adopted?: boolean;
|
|
20
|
+
sendStates?: TranscriptInputs["sendStates"];
|
|
21
|
+
viewPhases?: TranscriptInputs["viewPhases"];
|
|
22
|
+
historyCards?: TranscriptInputs["historyCards"];
|
|
23
|
+
historyState?: TranscriptInputs["historyState"];
|
|
24
|
+
/** Resolve accumulated prompts to this state (fixture 4's answered leg). */
|
|
25
|
+
promptState?: PromptItemInput["state"];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function driveTurn(events: InvokeTurnEvent[], opts: DriveOptions = {}): TranscriptInputs {
|
|
29
|
+
const reducer = new Reducer();
|
|
30
|
+
let sawAgEvents = false;
|
|
31
|
+
let status: TranscriptInputs["status"] = "connecting";
|
|
32
|
+
let assistantText = "";
|
|
33
|
+
let activeTool: string | null = null;
|
|
34
|
+
let error: TranscriptInputs["error"] = null;
|
|
35
|
+
const prompts: PromptItemInput[] = [];
|
|
36
|
+
let done = false;
|
|
37
|
+
|
|
38
|
+
for (const ev of events) {
|
|
39
|
+
switch (ev.kind) {
|
|
40
|
+
case "session":
|
|
41
|
+
if (status === "connecting") status = "thinking";
|
|
42
|
+
break;
|
|
43
|
+
case "message":
|
|
44
|
+
if (ev.status !== undefined) status = ev.status;
|
|
45
|
+
if (ev.activeTool !== undefined) activeTool = ev.activeTool;
|
|
46
|
+
assistantText = ev.assistantText;
|
|
47
|
+
for (const ag of ev.agEvents) {
|
|
48
|
+
sawAgEvents = true;
|
|
49
|
+
reducer.push(ag);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
case "error":
|
|
53
|
+
error = { message: ev.message, code: ev.code };
|
|
54
|
+
status = "ready";
|
|
55
|
+
break;
|
|
56
|
+
case "profile-consent":
|
|
57
|
+
prompts.push({
|
|
58
|
+
id: `consent-${prompts.length}`,
|
|
59
|
+
kind: "consent",
|
|
60
|
+
appId: ev.request.appId,
|
|
61
|
+
requested: ev.request.requested,
|
|
62
|
+
state: opts.promptState ?? "pending",
|
|
63
|
+
});
|
|
64
|
+
break;
|
|
65
|
+
case "profile-link":
|
|
66
|
+
prompts.push({
|
|
67
|
+
id: `link-${prompts.length}`,
|
|
68
|
+
kind: "link",
|
|
69
|
+
appId: ev.request.appId,
|
|
70
|
+
requested: ev.request.requested,
|
|
71
|
+
state: opts.promptState ?? "pending",
|
|
72
|
+
});
|
|
73
|
+
break;
|
|
74
|
+
case "done":
|
|
75
|
+
done = true;
|
|
76
|
+
status = "ready";
|
|
77
|
+
activeTool = null;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (opts.aborted === true) status = "ready";
|
|
82
|
+
|
|
83
|
+
const messages: TranscriptMessage[] = [];
|
|
84
|
+
if (opts.userText !== undefined) {
|
|
85
|
+
messages.push({
|
|
86
|
+
role: "user",
|
|
87
|
+
text: opts.userText,
|
|
88
|
+
...(opts.clientMessageId !== undefined ? { clientMessageId: opts.clientMessageId } : {}),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// A settled BYPASS turn's text moves into the flat transcript (the hook's
|
|
92
|
+
// behaviour); silver turns stay owned by the fold.
|
|
93
|
+
if (done && !sawAgEvents && assistantText !== "" && opts.aborted !== true) {
|
|
94
|
+
messages.push({ role: "assistant", text: assistantText });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
result: sawAgEvents ? reducer.result() : null,
|
|
99
|
+
assistantText,
|
|
100
|
+
status: opts.finalStatus ?? status,
|
|
101
|
+
statusElapsedMs: opts.statusElapsedMs ?? 0,
|
|
102
|
+
activeTool,
|
|
103
|
+
error,
|
|
104
|
+
prompts,
|
|
105
|
+
messages,
|
|
106
|
+
...(opts.sendStates !== undefined ? { sendStates: opts.sendStates } : {}),
|
|
107
|
+
...(opts.viewPhases !== undefined ? { viewPhases: opts.viewPhases } : {}),
|
|
108
|
+
...(opts.historyCards !== undefined ? { historyCards: opts.historyCards } : {}),
|
|
109
|
+
...(opts.historyState !== undefined ? { historyState: opts.historyState } : {}),
|
|
110
|
+
...(opts.aborted === true ? { aborted: true } : {}),
|
|
111
|
+
...(opts.adopted === true ? { adopted: true } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── Event builders (shared fixture vocabulary) ───────────────────────────
|
|
116
|
+
|
|
117
|
+
export const THREAD = "thread-corpus";
|
|
118
|
+
export const TURN = "turn-corpus";
|
|
119
|
+
export const MSG = "msg-corpus";
|
|
120
|
+
|
|
121
|
+
/** A per-fixture monotonically increasing seq source. */
|
|
122
|
+
export function seqSource(): () => number {
|
|
123
|
+
let n = 0;
|
|
124
|
+
return () => n++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The silver turn's lifecycle boot: turn.start + an open assistant message. */
|
|
128
|
+
export function boot(next: () => number): AgEvent[] {
|
|
129
|
+
return [
|
|
130
|
+
{ type: "turn.start", threadId: THREAD, turnId: TURN, seq: next() },
|
|
131
|
+
{ type: "message.start", id: MSG, role: "assistant", turnId: TURN, threadId: THREAD, seq: next() },
|
|
132
|
+
];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** A complete streamed text part. */
|
|
136
|
+
export function textPart(next: () => number, id: string, text: string): AgEvent[] {
|
|
137
|
+
return [
|
|
138
|
+
{ type: "text.start", id, seq: next() },
|
|
139
|
+
{ type: "text.delta", id, delta: text, seq: next() },
|
|
140
|
+
{ type: "text.end", id, seq: next() },
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A complete tool call: start + assembled args + done. */
|
|
145
|
+
export function toolPart(
|
|
146
|
+
next: () => number,
|
|
147
|
+
toolCallId: string,
|
|
148
|
+
name: string,
|
|
149
|
+
result: Partial<Extract<AgEvent, { type: "tool.done" }>> = {},
|
|
150
|
+
): AgEvent[] {
|
|
151
|
+
return [
|
|
152
|
+
{ type: "tool.start", toolCallId, name, seq: next() },
|
|
153
|
+
{ type: "tool.args.assembled", toolCallId, input: { q: name }, seq: next() },
|
|
154
|
+
{
|
|
155
|
+
type: "tool.done",
|
|
156
|
+
toolCallId,
|
|
157
|
+
content: [{ type: "text", text: "ok" }],
|
|
158
|
+
outcome: "ok",
|
|
159
|
+
...result,
|
|
160
|
+
seq: next(),
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Wrap agEvents into the `message` frame the pod emits. */
|
|
166
|
+
export function frame(
|
|
167
|
+
agEvents: AgEvent[],
|
|
168
|
+
extra: {
|
|
169
|
+
status?: "thinking" | "using-tool" | "responding";
|
|
170
|
+
activeTool?: string | null;
|
|
171
|
+
text?: string;
|
|
172
|
+
} = {},
|
|
173
|
+
): InvokeTurnEvent {
|
|
174
|
+
return {
|
|
175
|
+
kind: "message",
|
|
176
|
+
...(extra.status !== undefined ? { status: extra.status } : {}),
|
|
177
|
+
...(extra.activeTool !== undefined ? { activeTool: extra.activeTool } : {}),
|
|
178
|
+
assistantText: extra.text ?? "",
|
|
179
|
+
agEvents,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export const session: InvokeTurnEvent = { kind: "session", threadId: THREAD };
|
|
184
|
+
export const doneEvent: InvokeTurnEvent = { kind: "done", stopReason: "end_turn" };
|