agent-session-replayer 0.1.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/README.md +239 -0
- package/dist/chunk-QE3DOXM4.js +142 -0
- package/dist/chunk-QE3DOXM4.js.map +1 -0
- package/dist/index.cjs +573 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +427 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.cjs +169 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +67 -0
- package/dist/schema.d.ts +67 -0
- package/dist/schema.js +16 -0
- package/dist/schema.js.map +1 -0
- package/dist/styles.css +1 -0
- package/dist/types-DBa28H8n.d.cts +63 -0
- package/dist/types-DBa28H8n.d.ts +63 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Agent Session Replayer
|
|
2
|
+
|
|
3
|
+
`agent-session-replayer` is an SSR-safe React component for replaying a fixed, scripted implementer/reviewer session. It does not run a model, invoke tools, or execute code: the whole story comes from the data you provide.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add agent-session-replayer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
React 18 and 19 are peer dependencies. Import the precompiled stylesheet once; consumers do not need Tailwind CSS.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { AgentSessionReplayer, type AgentSession, type AgentSessionReplayerProps } from "agent-session-replayer";
|
|
15
|
+
import "agent-session-replayer/styles.css";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Development
|
|
19
|
+
|
|
20
|
+
This repository uses Bun and commits `bun.lock` as its lockfile.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun install
|
|
24
|
+
bun run dev
|
|
25
|
+
bun run test
|
|
26
|
+
bun run typecheck
|
|
27
|
+
bun run build:package
|
|
28
|
+
bun run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Minimal complete replay
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { AgentSessionReplayer, type AgentSession, type AgentSessionReplayerProps } from "agent-session-replayer";
|
|
35
|
+
import "agent-session-replayer/styles.css";
|
|
36
|
+
|
|
37
|
+
const agents: AgentSessionReplayerProps["agents"] = {
|
|
38
|
+
implementer: {
|
|
39
|
+
id: "claude-code",
|
|
40
|
+
name: "Claude",
|
|
41
|
+
role: "Implementer",
|
|
42
|
+
context: "the repository and approved task",
|
|
43
|
+
},
|
|
44
|
+
reviewer: {
|
|
45
|
+
id: "reviewer",
|
|
46
|
+
name: "Review agent",
|
|
47
|
+
role: "Adversarial reviewer",
|
|
48
|
+
context: "the diff and acceptance criteria",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const cases: AgentSession[] = [{
|
|
53
|
+
id: "checkout-fix",
|
|
54
|
+
title: "Fix checkout total",
|
|
55
|
+
summary: "A deterministic implementation and review replay.",
|
|
56
|
+
repository: "acme/storefront",
|
|
57
|
+
branch: "fix/checkout-total",
|
|
58
|
+
events: [{
|
|
59
|
+
id: "task",
|
|
60
|
+
type: "task_received",
|
|
61
|
+
actor: "implementer",
|
|
62
|
+
title: "Read the task",
|
|
63
|
+
summary: "Confirm the requested checkout behavior.",
|
|
64
|
+
blocks: [{
|
|
65
|
+
id: "request",
|
|
66
|
+
kind: "message",
|
|
67
|
+
content: "Correct the checkout total and add a regression test.",
|
|
68
|
+
}],
|
|
69
|
+
}],
|
|
70
|
+
}];
|
|
71
|
+
|
|
72
|
+
export function Demo() {
|
|
73
|
+
return <AgentSessionReplayer agents={agents} cases={cases} />;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Props
|
|
78
|
+
|
|
79
|
+
All objects are strict at runtime: unknown keys are rejected. Required strings must be non-empty. Invalid props throw during render with an error beginning `AgentSessionReplayer received invalid props:`.
|
|
80
|
+
|
|
81
|
+
| Prop | Type | Required / default | Rules and behavior |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| `agents` | `Record<"implementer" \| "reviewer", AgentIdentity>` | Required | Must contain exactly `implementer` and `reviewer` identities. |
|
|
84
|
+
| `cases` | `AgentSession[]` | Required | Non-empty sessions. Case IDs are unique across the array. |
|
|
85
|
+
| `typingSpeed` | `number` | `110` | Finite, greater than zero; graphemes revealed per second. |
|
|
86
|
+
| `eventDelayMs` | `number` | `500` | Finite, zero or greater; delay between completed events. |
|
|
87
|
+
| `height` | `number` | `720` | Finite, greater than zero; rendered as pixels. |
|
|
88
|
+
| `colors` | `AgentSessionColors` | Optional | Scoped CSS-variable overrides listed below. |
|
|
89
|
+
| `caseIndex` | `number` | Optional | Controlled case index. It must be an integer in the `cases` bounds. |
|
|
90
|
+
| `initialCaseIndex` | `number` | `0` | Uncontrolled starting index. It must be an integer in the `cases` bounds. |
|
|
91
|
+
| `className` | `string` | Optional | Added to the player root element. |
|
|
92
|
+
| `onCaseChange` | `(index, item) => void` | Optional | Called only for user navigation requests; controlled parents must update `caseIndex`. |
|
|
93
|
+
| `onEventStart` | `(event, item) => void` | Optional | Called once before an event reveals its first grapheme. |
|
|
94
|
+
| `onEventComplete` | `(event, item) => void` | Optional | Called once after the event finishes revealing. Interrupted events do not complete. |
|
|
95
|
+
| `onCaseComplete` | `(item) => void` | Optional | Called after the final event completion for a case. |
|
|
96
|
+
|
|
97
|
+
## Controlled and uncontrolled navigation
|
|
98
|
+
|
|
99
|
+
Omit `caseIndex` to let the component own navigation. Use `initialCaseIndex` to start at another case.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<AgentSessionReplayer agents={agents} cases={cases} initialCaseIndex={1} />
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Supply `caseIndex` to control navigation. The callback reports a request; the parent updates the value.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { useState } from "react";
|
|
109
|
+
|
|
110
|
+
const [caseIndex, setCaseIndex] = useState(0);
|
|
111
|
+
|
|
112
|
+
<AgentSessionReplayer
|
|
113
|
+
agents={agents}
|
|
114
|
+
cases={cases}
|
|
115
|
+
caseIndex={caseIndex}
|
|
116
|
+
onCaseChange={(nextIndex) => setCaseIndex(nextIndex)}
|
|
117
|
+
/>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Replay data schema
|
|
121
|
+
|
|
122
|
+
### `AgentIdentity`
|
|
123
|
+
|
|
124
|
+
| Field | Type | Rules |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| `id` | `string` | Non-empty identity ID. |
|
|
127
|
+
| `name` | `string` | Non-empty visible name. |
|
|
128
|
+
| `role` | `string` | Non-empty visible role. |
|
|
129
|
+
| `context` | `string` | Non-empty description of the agent's working context. |
|
|
130
|
+
|
|
131
|
+
### `AgentSession`
|
|
132
|
+
|
|
133
|
+
| Field | Type | Rules |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `id` | `string` | Non-empty and unique across `cases`. |
|
|
136
|
+
| `title` | `string` | Non-empty case title. |
|
|
137
|
+
| `summary` | `string` | Non-empty case summary. |
|
|
138
|
+
| `repository` | `string` | Non-empty repository label. |
|
|
139
|
+
| `branch` | `string` | Non-empty branch label. |
|
|
140
|
+
| `events` | `AgentSessionEvent[]` | Non-empty. Event IDs are unique within the case. |
|
|
141
|
+
|
|
142
|
+
### `AgentSessionEvent`
|
|
143
|
+
|
|
144
|
+
| Field | Type | Rules |
|
|
145
|
+
| --- | --- | --- |
|
|
146
|
+
| `id` | `string` | Non-empty and unique within its case. |
|
|
147
|
+
| `type` | `AgentEventType` | One of the event literals below. |
|
|
148
|
+
| `actor` | `"implementer" \| "reviewer"` | Agent that produced the event. |
|
|
149
|
+
| `title` | `string` | Non-empty event title. |
|
|
150
|
+
| `summary` | `string` | Non-empty collapsed-event summary. |
|
|
151
|
+
| `blocks` | `AgentSessionBlock[]` | Non-empty. Block IDs are unique within the event. |
|
|
152
|
+
|
|
153
|
+
`AgentEventType` is one of:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
"task_received" | "plan" | "patch" | "review_request" | "review_start"
|
|
157
|
+
| "blocking_finding" | "revision" | "verification" | "approval"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### `AgentSessionBlock`
|
|
161
|
+
|
|
162
|
+
| Field | Type | Rules |
|
|
163
|
+
| --- | --- | --- |
|
|
164
|
+
| `id` | `string` | Non-empty and unique within its event. |
|
|
165
|
+
| `kind` | `AgentBlockKind` | One of the block literals below. |
|
|
166
|
+
| `title` | `string` | Optional; when supplied it must be non-empty. |
|
|
167
|
+
| `content` | `string` | Non-empty visible block content. |
|
|
168
|
+
| `language` | `string` | Optional; when supplied it must be non-empty. |
|
|
169
|
+
|
|
170
|
+
`AgentBlockKind` is one of:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
"message" | "code" | "tool_call" | "tool_output" | "finding" | "patch" | "git_diff" | "status" | "result"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Lifecycle ordering
|
|
177
|
+
|
|
178
|
+
For each replayed event, `onEventStart(event, case)` fires once before text starts revealing, then `onEventComplete(event, case)` fires after its final grapheme. On the final event, `onEventComplete` fires before `onCaseComplete(case)`. Restarting or navigating to a case begins a new run and may fire the callbacks again for that run.
|
|
179
|
+
|
|
180
|
+
## Validation failures
|
|
181
|
+
|
|
182
|
+
The component validates the resolved props, including defaults, immediately before playback starts. It reports all detected issues in one error with nested paths.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// Throws: AgentSessionReplayer received invalid props:
|
|
186
|
+
// cases[0].events[0].blocks[0].content: Too small: expected string to have >=1 characters
|
|
187
|
+
<AgentSessionReplayer agents={agents} cases={[{
|
|
188
|
+
...cases[0],
|
|
189
|
+
events: [{ ...cases[0].events[0], blocks: [{ ...cases[0].events[0].blocks[0], content: "" }] }],
|
|
190
|
+
}]} />
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Theme overrides
|
|
194
|
+
|
|
195
|
+
`colors` supplies scoped values for these CSS variables. Values can be any CSS color value accepted by the browser.
|
|
196
|
+
|
|
197
|
+
| Key | CSS variable |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `background` | `--asr-background` |
|
|
200
|
+
| `surface` | `--asr-surface` |
|
|
201
|
+
| `border` | `--asr-border` |
|
|
202
|
+
| `text` | `--asr-text` |
|
|
203
|
+
| `muted` | `--asr-muted` |
|
|
204
|
+
| `implementer` | `--asr-implementer` |
|
|
205
|
+
| `reviewer` | `--asr-reviewer` |
|
|
206
|
+
| `success` | `--asr-success` |
|
|
207
|
+
| `danger` | `--asr-danger` |
|
|
208
|
+
| `focus` | `--asr-focus` |
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
<AgentSessionReplayer
|
|
212
|
+
agents={agents}
|
|
213
|
+
cases={cases}
|
|
214
|
+
colors={{ background: "#080b12", reviewer: "#ff8a65", focus: "#8ab4f8" }}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Rendering, motion, and accessibility
|
|
219
|
+
|
|
220
|
+
The package is safe to import and server-render: browser APIs and timers are accessed only in effects, so server markup and the initial client markup are deterministic. When `prefers-reduced-motion: reduce` is active, events complete without the typing and collapse animations.
|
|
221
|
+
|
|
222
|
+
Navigation uses labeled buttons and each expanded event is exposed as an article with its event title. Keep the supplied titles and summaries meaningful so the replay remains understandable to assistive technology.
|
|
223
|
+
|
|
224
|
+
The player always includes a quiet `devos` attribution link in its in-frame footer.
|
|
225
|
+
|
|
226
|
+
## Bun development
|
|
227
|
+
|
|
228
|
+
This workspace uses Bun 1.3.8 and `bun.lock`.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
bun install
|
|
232
|
+
bun run dev
|
|
233
|
+
bun run test
|
|
234
|
+
bun run typecheck
|
|
235
|
+
bun run build
|
|
236
|
+
bun run build:package
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The root commands run the demo workspace. `bun run build:package` builds the embeddable package into `packages/agent-session-replayer/dist`.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/validation.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var nonEmpty = z.string().min(1);
|
|
4
|
+
var actorSchema = z.enum(["implementer", "reviewer"]);
|
|
5
|
+
var eventTypeSchema = z.enum([
|
|
6
|
+
"task_received",
|
|
7
|
+
"plan",
|
|
8
|
+
"patch",
|
|
9
|
+
"review_request",
|
|
10
|
+
"review_start",
|
|
11
|
+
"blocking_finding",
|
|
12
|
+
"revision",
|
|
13
|
+
"verification",
|
|
14
|
+
"approval"
|
|
15
|
+
]);
|
|
16
|
+
var blockKindSchema = z.enum([
|
|
17
|
+
"message",
|
|
18
|
+
"code",
|
|
19
|
+
"tool_call",
|
|
20
|
+
"tool_output",
|
|
21
|
+
"finding",
|
|
22
|
+
"patch",
|
|
23
|
+
"git_diff",
|
|
24
|
+
"status",
|
|
25
|
+
"result"
|
|
26
|
+
]);
|
|
27
|
+
var agentSchema = z.object({
|
|
28
|
+
id: nonEmpty,
|
|
29
|
+
name: nonEmpty,
|
|
30
|
+
role: nonEmpty,
|
|
31
|
+
context: nonEmpty
|
|
32
|
+
}).strict();
|
|
33
|
+
var blockSchema = z.object({
|
|
34
|
+
id: nonEmpty,
|
|
35
|
+
kind: blockKindSchema,
|
|
36
|
+
title: nonEmpty.optional(),
|
|
37
|
+
content: nonEmpty,
|
|
38
|
+
language: nonEmpty.optional()
|
|
39
|
+
}).strict();
|
|
40
|
+
var eventSchema = z.object({
|
|
41
|
+
id: nonEmpty,
|
|
42
|
+
type: eventTypeSchema,
|
|
43
|
+
actor: actorSchema,
|
|
44
|
+
title: nonEmpty,
|
|
45
|
+
summary: nonEmpty,
|
|
46
|
+
blocks: z.array(blockSchema).min(1).describe("Block IDs must be unique within each event. The runtime parser enforces this constraint.")
|
|
47
|
+
}).strict().superRefine((event, context) => {
|
|
48
|
+
if (new Set(event.blocks.map(({ id }) => id)).size !== event.blocks.length) {
|
|
49
|
+
context.addIssue({
|
|
50
|
+
code: "custom",
|
|
51
|
+
path: ["blocks"],
|
|
52
|
+
message: "Block IDs must be unique within an event"
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
var sessionSchema = z.object({
|
|
57
|
+
id: nonEmpty,
|
|
58
|
+
title: nonEmpty,
|
|
59
|
+
summary: nonEmpty,
|
|
60
|
+
repository: nonEmpty,
|
|
61
|
+
branch: nonEmpty,
|
|
62
|
+
events: z.array(eventSchema).min(1).describe("Event IDs must be unique within each case. The runtime parser enforces this constraint.")
|
|
63
|
+
}).strict().superRefine((session, context) => {
|
|
64
|
+
if (new Set(session.events.map(({ id }) => id)).size !== session.events.length) {
|
|
65
|
+
context.addIssue({
|
|
66
|
+
code: "custom",
|
|
67
|
+
path: ["events"],
|
|
68
|
+
message: "Event IDs must be unique within a case"
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
var colorsSchema = z.object({
|
|
73
|
+
background: z.string().optional(),
|
|
74
|
+
surface: z.string().optional(),
|
|
75
|
+
border: z.string().optional(),
|
|
76
|
+
text: z.string().optional(),
|
|
77
|
+
muted: z.string().optional(),
|
|
78
|
+
implementer: z.string().optional(),
|
|
79
|
+
reviewer: z.string().optional(),
|
|
80
|
+
success: z.string().optional(),
|
|
81
|
+
danger: z.string().optional(),
|
|
82
|
+
focus: z.string().optional()
|
|
83
|
+
}).strict();
|
|
84
|
+
var replayContentShape = {
|
|
85
|
+
agents: z.object({ implementer: agentSchema, reviewer: agentSchema }).strict(),
|
|
86
|
+
cases: z.array(sessionSchema).min(1).describe("Case IDs must be unique across this array. The runtime parser enforces this constraint.")
|
|
87
|
+
};
|
|
88
|
+
function addReplayContentIssues(value, context) {
|
|
89
|
+
if (new Set(value.cases.map(({ id }) => id)).size !== value.cases.length) {
|
|
90
|
+
context.addIssue({ code: "custom", path: ["cases"], message: "Case IDs must be unique" });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
var replayContentSchema = z.object(replayContentShape).strict().superRefine(addReplayContentIssues);
|
|
94
|
+
var propsSchema = z.object({
|
|
95
|
+
...replayContentShape,
|
|
96
|
+
typingSpeed: z.number().finite().positive(),
|
|
97
|
+
eventDelayMs: z.number().finite().nonnegative(),
|
|
98
|
+
height: z.number({ error: "height must be greater than zero." }).refine((value) => Number.isFinite(value) && value > 0, "height must be greater than zero."),
|
|
99
|
+
colors: colorsSchema.optional(),
|
|
100
|
+
caseIndex: z.number().int().nonnegative().optional(),
|
|
101
|
+
initialCaseIndex: z.number().int().nonnegative(),
|
|
102
|
+
className: z.string().optional(),
|
|
103
|
+
onCaseChange: z.function().optional(),
|
|
104
|
+
onEventStart: z.function().optional(),
|
|
105
|
+
onEventComplete: z.function().optional(),
|
|
106
|
+
onCaseComplete: z.function().optional()
|
|
107
|
+
}).strict().superRefine((props, context) => {
|
|
108
|
+
addReplayContentIssues(props, context);
|
|
109
|
+
for (const key of ["caseIndex", "initialCaseIndex"]) {
|
|
110
|
+
const value = props[key];
|
|
111
|
+
if (value !== void 0 && value >= props.cases.length) {
|
|
112
|
+
context.addIssue({
|
|
113
|
+
code: "custom",
|
|
114
|
+
path: [key],
|
|
115
|
+
message: `${key} must reference an available case`
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
function formatPath(path) {
|
|
121
|
+
const formatted = path.reduce((result, part) => typeof part === "number" ? `${result}[${part}]` : result ? `${result}.${String(part)}` : String(part), "");
|
|
122
|
+
return formatted || "$";
|
|
123
|
+
}
|
|
124
|
+
function parseReplayerProps(value) {
|
|
125
|
+
const result = propsSchema.safeParse(value);
|
|
126
|
+
if (result.success) return result.data;
|
|
127
|
+
const details = result.error.issues.map((issue) => `${formatPath(issue.path)}: ${issue.message}`).join("; ");
|
|
128
|
+
throw new Error(`AgentSessionReplayer received invalid props: ${details}`);
|
|
129
|
+
}
|
|
130
|
+
function parseAgentSessionContent(value) {
|
|
131
|
+
const result = replayContentSchema.safeParse(value);
|
|
132
|
+
if (result.success) return result.data;
|
|
133
|
+
const details = result.error.issues.map((issue) => `${formatPath(issue.path)}: ${issue.message}`).join("; ");
|
|
134
|
+
throw new Error(`Replay content is invalid: ${details}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
replayContentSchema,
|
|
139
|
+
parseReplayerProps,
|
|
140
|
+
parseAgentSessionContent
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=chunk-QE3DOXM4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { AgentSessionContent, AgentSessionReplayerProps } from \"./types\";\n\nconst nonEmpty = z.string().min(1);\nconst actorSchema = z.enum([\"implementer\", \"reviewer\"]);\nconst eventTypeSchema = z.enum([\n \"task_received\", \"plan\", \"patch\", \"review_request\", \"review_start\",\n \"blocking_finding\", \"revision\", \"verification\", \"approval\",\n]);\nconst blockKindSchema = z.enum([\n \"message\", \"code\", \"tool_call\", \"tool_output\", \"finding\", \"patch\",\n \"git_diff\", \"status\", \"result\",\n]);\n\nconst agentSchema = z.object({\n id: nonEmpty,\n name: nonEmpty,\n role: nonEmpty,\n context: nonEmpty,\n}).strict();\n\nconst blockSchema = z.object({\n id: nonEmpty,\n kind: blockKindSchema,\n title: nonEmpty.optional(),\n content: nonEmpty,\n language: nonEmpty.optional(),\n}).strict();\n\nconst eventSchema = z.object({\n id: nonEmpty,\n type: eventTypeSchema,\n actor: actorSchema,\n title: nonEmpty,\n summary: nonEmpty,\n blocks: z.array(blockSchema).min(1)\n .describe(\"Block IDs must be unique within each event. The runtime parser enforces this constraint.\"),\n}).strict().superRefine((event, context) => {\n if (new Set(event.blocks.map(({ id }) => id)).size !== event.blocks.length) {\n context.addIssue({\n code: \"custom\",\n path: [\"blocks\"],\n message: \"Block IDs must be unique within an event\",\n });\n }\n});\n\nconst sessionSchema = z.object({\n id: nonEmpty,\n title: nonEmpty,\n summary: nonEmpty,\n repository: nonEmpty,\n branch: nonEmpty,\n events: z.array(eventSchema).min(1)\n .describe(\"Event IDs must be unique within each case. The runtime parser enforces this constraint.\"),\n}).strict().superRefine((session, context) => {\n if (new Set(session.events.map(({ id }) => id)).size !== session.events.length) {\n context.addIssue({\n code: \"custom\",\n path: [\"events\"],\n message: \"Event IDs must be unique within a case\",\n });\n }\n});\n\nconst colorsSchema = z.object({\n background: z.string().optional(),\n surface: z.string().optional(),\n border: z.string().optional(),\n text: z.string().optional(),\n muted: z.string().optional(),\n implementer: z.string().optional(),\n reviewer: z.string().optional(),\n success: z.string().optional(),\n danger: z.string().optional(),\n focus: z.string().optional(),\n}).strict();\n\nconst replayContentShape = {\n agents: z.object({ implementer: agentSchema, reviewer: agentSchema }).strict(),\n cases: z.array(sessionSchema).min(1)\n .describe(\"Case IDs must be unique across this array. The runtime parser enforces this constraint.\"),\n};\n\nfunction addReplayContentIssues(\n value: { cases: Array<{ id: string }> },\n context: z.RefinementCtx,\n) {\n if (new Set(value.cases.map(({ id }) => id)).size !== value.cases.length) {\n context.addIssue({ code: \"custom\", path: [\"cases\"], message: \"Case IDs must be unique\" });\n }\n}\n\nexport const replayContentSchema = z.object(replayContentShape)\n .strict()\n .superRefine(addReplayContentIssues);\n\nconst propsSchema = z.object({\n ...replayContentShape,\n typingSpeed: z.number().finite().positive(),\n eventDelayMs: z.number().finite().nonnegative(),\n height: z.number({ error: \"height must be greater than zero.\" })\n .refine((value) => Number.isFinite(value) && value > 0, \"height must be greater than zero.\"),\n colors: colorsSchema.optional(),\n caseIndex: z.number().int().nonnegative().optional(),\n initialCaseIndex: z.number().int().nonnegative(),\n className: z.string().optional(),\n onCaseChange: z.function().optional(),\n onEventStart: z.function().optional(),\n onEventComplete: z.function().optional(),\n onCaseComplete: z.function().optional(),\n}).strict().superRefine((props, context) => {\n addReplayContentIssues(props, context);\n\n for (const key of [\"caseIndex\", \"initialCaseIndex\"] as const) {\n const value = props[key];\n if (value !== undefined && value >= props.cases.length) {\n context.addIssue({\n code: \"custom\",\n path: [key],\n message: `${key} must reference an available case`,\n });\n }\n }\n});\n\ntype ParsedProps = AgentSessionReplayerProps & Required<Pick<AgentSessionReplayerProps,\n \"typingSpeed\" | \"eventDelayMs\" | \"height\" | \"initialCaseIndex\"\n>>;\n\nfunction formatPath(path: PropertyKey[]): string {\n const formatted = path.reduce<string>((result, part) => (\n typeof part === \"number\"\n ? `${result}[${part}]`\n : result ? `${result}.${String(part)}` : String(part)\n ), \"\");\n\n return formatted || \"$\";\n}\n\nexport function parseReplayerProps(value: unknown): ParsedProps {\n const result = propsSchema.safeParse(value);\n if (result.success) return result.data as ParsedProps;\n\n const details = result.error.issues\n .map((issue) => `${formatPath(issue.path)}: ${issue.message}`)\n .join(\"; \");\n throw new Error(`AgentSessionReplayer received invalid props: ${details}`);\n}\n\nexport function parseAgentSessionContent(value: unknown): AgentSessionContent {\n const result = replayContentSchema.safeParse(value);\n if (result.success) return result.data as AgentSessionContent;\n\n const details = result.error.issues\n .map((issue) => `${formatPath(issue.path)}: ${issue.message}`)\n .join(\"; \");\n throw new Error(`Replay content is invalid: ${details}`);\n}\n"],"mappings":"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","names":[]}
|