@you-agent-factory/factory-emulator 0.0.0 → 0.0.2
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.md +19 -1
- package/README.md +305 -286
- package/dist/compatibility.d.ts +19 -0
- package/dist/compatibility.js +156 -0
- package/dist/event-sink.d.ts +29 -0
- package/dist/event-sink.js +58 -0
- package/dist/generated/scenario-schema.d.ts +268 -0
- package/dist/generated/scenario-schema.js +319 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/logical-move.d.ts +10 -0
- package/dist/logical-move.js +18 -0
- package/dist/recording-sink.d.ts +22 -0
- package/dist/recording-sink.js +122 -0
- package/dist/runtime-reference-conformance.d.ts +20 -0
- package/dist/runtime-reference-conformance.js +138 -0
- package/dist/runtime-reference-evidence.d.ts +9 -0
- package/dist/runtime-reference-evidence.js +193 -0
- package/dist/runtime-reference-fixtures.d.ts +7 -0
- package/dist/runtime-reference-fixtures.js +308 -0
- package/dist/runtime-reference.d.ts +48 -0
- package/dist/runtime-reference.js +143 -0
- package/dist/scenario-contracts.d.ts +81 -0
- package/dist/scenario-contracts.js +11 -0
- package/dist/scenario.d.ts +271 -0
- package/dist/scenario.js +333 -0
- package/dist/scheduler.d.ts +24 -0
- package/dist/scheduler.js +104 -0
- package/dist/session-contracts.d.ts +262 -0
- package/dist/session-contracts.js +74 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +1490 -0
- package/dist/submission-replay.d.ts +14 -0
- package/dist/submission-replay.js +96 -0
- package/dist/submission-validation.d.ts +3 -0
- package/dist/submission-validation.js +113 -0
- package/examples/customer-support.scenario.v1.json +45 -0
- package/package.json +44 -22
- package/schema/scenario.schema.json +171 -0
- package/generated/factory-emulator-scenario.schema.json +0 -191
- package/generated/factory.schema.json +0 -2606
- package/src/contracts.ts +0 -41
- package/src/data-error.js +0 -21
- package/src/data-only.js +0 -208
- package/src/emulator.js +0 -195
- package/src/emulator.ts +0 -86
- package/src/examples.js +0 -86
- package/src/examples.ts +0 -11
- package/src/generated/factory-schema.d.ts +0 -3
- package/src/generated/factory-schema.js +0 -5
- package/src/generated/scenario-schema.d.ts +0 -4
- package/src/generated/scenario-schema.js +0 -6
- package/src/generated/scenario.ts +0 -103
- package/src/identity.js +0 -55
- package/src/index.js +0 -34
- package/src/index.ts +0 -102
- package/src/limits.js +0 -125
- package/src/parser.js +0 -113
- package/src/parser.ts +0 -56
- package/src/scheduler.js +0 -374
- package/src/semantics.js +0 -354
- package/src/semantics.ts +0 -38
- package/src/session.js +0 -1201
- package/src/session.ts +0 -324
- package/src/sinks.js +0 -118
- package/src/sinks.ts +0 -56
- package/src/support.js +0 -334
- package/src/support.ts +0 -15
- package/src/virtual-time.js +0 -36
package/README.md
CHANGED
|
@@ -1,286 +1,305 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
`
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
1
|
+
# Factory emulator
|
|
2
|
+
|
|
3
|
+
`@you-agent-factory/factory-emulator` is a transport-neutral, non-React package
|
|
4
|
+
for deterministic Factory emulator contracts. Scenario parsing validates both
|
|
5
|
+
the package-local schema and references to a caller-supplied UI client
|
|
6
|
+
`FactoryDefinition`.
|
|
7
|
+
|
|
8
|
+
The hosted Go runtime remains authoritative for Factory execution. This package
|
|
9
|
+
implements only the deterministic browser subset documented in the
|
|
10
|
+
[public package family guide](https://github.com/portpowered/you-agent-factory/blob/main/ui/packages/README.md);
|
|
11
|
+
canonical-event-compatible output
|
|
12
|
+
does not make it a replacement for hosted execution.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import factory from "./factory.json" with { type: "json" };
|
|
16
|
+
import scenario from "./scenario.json" with { type: "json" };
|
|
17
|
+
import { parseFactoryEmulatorScenario } from "@you-agent-factory/factory-emulator";
|
|
18
|
+
|
|
19
|
+
const parsed = parseFactoryEmulatorScenario(scenario, factory);
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The parser returns a detached scenario value. `safeParseFactoryEmulatorScenario`
|
|
23
|
+
returns all structure and semantic diagnostics without partially accepting an
|
|
24
|
+
invalid scenario.
|
|
25
|
+
|
|
26
|
+
Factories can be preflighted against the deterministic v1 execution subset
|
|
27
|
+
before any event history is emitted:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import {
|
|
31
|
+
inspectFactoryEmulatorCompatibility,
|
|
32
|
+
writeFactoryEventsIfCompatible,
|
|
33
|
+
} from "@you-agent-factory/factory-emulator";
|
|
34
|
+
|
|
35
|
+
const compatibility = inspectFactoryEmulatorCompatibility(factory);
|
|
36
|
+
if (!compatibility.supported) {
|
|
37
|
+
console.error(compatibility.diagnostics);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Incompatible Factories return every diagnostic without calling sink.write.
|
|
41
|
+
await writeFactoryEventsIfCompatible(factory, eventBatch, sink);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The inspector treats an omitted orchestrator as the documented Petri default,
|
|
45
|
+
does not mutate or retain the Factory, and reports stable codes with paths into
|
|
46
|
+
the caller-supplied UI client `FactoryDefinition`.
|
|
47
|
+
|
|
48
|
+
## Long-lived session lifecycle
|
|
49
|
+
|
|
50
|
+
Create a framework-independent session from a compatible Factory, a parsed
|
|
51
|
+
scenario, and a caller-owned event sink. Construction revalidates the inputs
|
|
52
|
+
and optional safety limits before any event is written.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import {
|
|
56
|
+
createFactoryEmulatorSession,
|
|
57
|
+
MemoryFactoryEventSink,
|
|
58
|
+
} from "@you-agent-factory/factory-emulator";
|
|
59
|
+
|
|
60
|
+
const sink = new MemoryFactoryEventSink({ maxEvents: 10_000 });
|
|
61
|
+
const session = createFactoryEmulatorSession({
|
|
62
|
+
factory,
|
|
63
|
+
scenario: parsed,
|
|
64
|
+
sink,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const before = session.status();
|
|
68
|
+
const started = await session.start();
|
|
69
|
+
await session.submit({
|
|
70
|
+
name: "follow-up",
|
|
71
|
+
workType: "ticket",
|
|
72
|
+
state: "ready",
|
|
73
|
+
input: "Customer reply",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await session.submit({
|
|
77
|
+
works: [
|
|
78
|
+
{ name: "reply", workType: "ticket", state: "ready" },
|
|
79
|
+
{ name: "approval", workType: "ticket", state: "ready" },
|
|
80
|
+
],
|
|
81
|
+
relations: [
|
|
82
|
+
{
|
|
83
|
+
type: "DEPENDS_ON",
|
|
84
|
+
sourceWorkName: "reply",
|
|
85
|
+
targetWorkName: "approval",
|
|
86
|
+
// Omit only when the target Work Type declares the default "complete".
|
|
87
|
+
requiredState: "classified",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
const dispatched = await session.advanceToNext();
|
|
92
|
+
const advanced = await session.advanceBy(250);
|
|
93
|
+
const current = session.state();
|
|
94
|
+
const closed = await session.close();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`start()` writes one atomic canonical bootstrap batch in `RUN_REQUEST`,
|
|
98
|
+
`INITIAL_STRUCTURE_REQUEST`, `SESSION_STARTED` order. The started state becomes
|
|
99
|
+
visible only after the sink accepts the complete batch. `state()` and
|
|
100
|
+
`status()` return detached, structured-cloneable snapshots; the kernel does not
|
|
101
|
+
retain playback or event history. An idle session remains open for later Work.
|
|
102
|
+
|
|
103
|
+
`submit()` accepts one validated scenario Work value, a relationship-free Work
|
|
104
|
+
array, or a `{ works, relations }` batch. `DEPENDS_ON` is the only supported
|
|
105
|
+
emulator relationship; both endpoints must be names from that batch. The
|
|
106
|
+
complete request is validated before its atomic sink write, so an invalid item
|
|
107
|
+
or graph cannot partially create Work. Submissions remain available while
|
|
108
|
+
other Work is active and after the session returns to idle.
|
|
109
|
+
|
|
110
|
+
Accepted relationship batches emit one canonical `WORK_REQUEST` carrying the
|
|
111
|
+
resolved relations, followed by one `RELATIONSHIP_CHANGE_REQUEST` per relation
|
|
112
|
+
in declared order. The complete event sequence is one sink batch, and state and
|
|
113
|
+
identity counters commit only after that batch succeeds. Use
|
|
114
|
+
`replayFactoryEmulatorSubmissions(events)` to reconstruct detached Work identity,
|
|
115
|
+
initial state, payload, and outbound relationships from canonical submission
|
|
116
|
+
events.
|
|
117
|
+
|
|
118
|
+
Virtual time advances only when the host calls `advanceBy(durationMs)` or
|
|
119
|
+
`advanceToNext()`. Ready Work starts in one deterministic scheduler batch;
|
|
120
|
+
`advanceToNext()` then jumps to the earliest due instant and completes every
|
|
121
|
+
dispatch due there in stable Work order. `advanceBy()` processes every due
|
|
122
|
+
outcome through its requested instant. Event timestamps are always the scenario
|
|
123
|
+
`startAt` plus committed virtual elapsed time; these commands use no wall-clock
|
|
124
|
+
or browser timers. Receipts, state, status, and validation errors are detached
|
|
125
|
+
structured-cloneable values.
|
|
126
|
+
|
|
127
|
+
Each scheduler batch considers at most 50 eligible bindings in deterministic
|
|
128
|
+
Work-in-queue order. Multi-input workstations require a stable binding for every
|
|
129
|
+
input, and selected candidates claim all consumable Work and declared top-level
|
|
130
|
+
resource capacity atomically. Independent candidates can start together when
|
|
131
|
+
capacity permits; active dispatches retain their claims until completion, and
|
|
132
|
+
released capacity is available to waiting Work in the following scheduler
|
|
133
|
+
batch. Work with `DEPENDS_ON` relations remains ready but cannot enter a
|
|
134
|
+
scheduler candidate until every target Work currently occupies its normalized
|
|
135
|
+
required state. Dependency eligibility is recalculated after each accepted
|
|
136
|
+
completion batch, so newly unblocked Work can dispatch in the same `advanceBy()`
|
|
137
|
+
command. Worker-only resource metadata does not change mocked execution
|
|
138
|
+
capacity.
|
|
139
|
+
|
|
140
|
+
When a target enters a failed state, every non-terminal dependent moves to its
|
|
141
|
+
own Work Type's failed state through a deterministic breadth-first closure in
|
|
142
|
+
that same completion tick. Each move emits one canonical `WORK_STATE_CHANGE`
|
|
143
|
+
with source `cascading-failure`; terminal and already-failed Work is unchanged.
|
|
144
|
+
|
|
145
|
+
The package's frozen runtime-reference corpus includes both a `DEPENDS_ON`
|
|
146
|
+
prerequisite release and a terminal-failure cascade. Conformance compares
|
|
147
|
+
dependency eligibility, dispatch selection, consumed Work, routes, terminal
|
|
148
|
+
states, and submission replay projection at every logical tick.
|
|
149
|
+
|
|
150
|
+
Accepted, continued, rejected, and failed outcomes preserve Work lineage while
|
|
151
|
+
routing to explicit destinations in declared order. Accepted fan-out supports
|
|
152
|
+
multiple outputs and both `OUTPUT_AS_PAYLOAD` and `PRESERVE_INPUT` propagation.
|
|
153
|
+
Missing routes use the supported Factory defaults: failures enter the input Work
|
|
154
|
+
Type's failed state, STANDARD rejections use the effective failure route, and
|
|
155
|
+
REPEATER rejections return to their inputs.
|
|
156
|
+
|
|
157
|
+
Supported workerless `LOGICAL_MOVE` workstations do not need scenario rules.
|
|
158
|
+
Their `VISIT_COUNT` guards read the inclusive transition counts carried by the
|
|
159
|
+
first authored input lineage, and an eligible move routes synchronously at the
|
|
160
|
+
current virtual instant without creating active worker-dispatch state. The
|
|
161
|
+
resulting canonical `DISPATCH_RESPONSE` preserves lineage, propagation mode,
|
|
162
|
+
and declared output order; session Work snapshots expose the carried `visits`
|
|
163
|
+
map for deterministic inspection. Zero-duration logical cycles share the
|
|
164
|
+
session's configured cycle and cooperative-yield boundaries.
|
|
165
|
+
|
|
166
|
+
Canonical identities and event ordering are derived from the Factory identity,
|
|
167
|
+
the complete validated scenario (including its seed), normalized command
|
|
168
|
+
inputs, command order, and virtual elapsed time. Object key insertion order,
|
|
169
|
+
wall-clock time, and host playback speed do not participate. `reset()` restores
|
|
170
|
+
the pre-start counters, rule cursors, identities, initial submissions, and
|
|
171
|
+
virtual-time origin while retaining the caller-owned sink, so the host can
|
|
172
|
+
clear or replace its own recording destination and reproduce the same supported
|
|
173
|
+
history byte for byte.
|
|
174
|
+
|
|
175
|
+
Every state-changing command retains its complete detached candidate state and
|
|
176
|
+
canonical event batch until the caller-owned sink accepts the batch. While a
|
|
177
|
+
write is pending, other state-changing commands fail with
|
|
178
|
+
`FactoryEmulatorPendingCommandError`; read-only snapshots remain available.
|
|
179
|
+
After rejection, status exposes the structured sink error and pending phase.
|
|
180
|
+
Retry the same command with the same arguments to write the byte-identical
|
|
181
|
+
batch before execution continues, or call `reset()` to explicitly discard the
|
|
182
|
+
rejected transaction and restore the pre-start state.
|
|
183
|
+
|
|
184
|
+
`close()` writes one canonical `SESSION_COMPLETED` terminal batch, then awaits
|
|
185
|
+
the sink's optional `close()` boundary before exposing the terminal closed
|
|
186
|
+
state. A terminal write or sink-close rejection remains retryable. Sink-close
|
|
187
|
+
retries never duplicate an already accepted terminal event, and successful
|
|
188
|
+
close rejects all later state-changing commands.
|
|
189
|
+
|
|
190
|
+
## Execution safety and cooperative scheduling
|
|
191
|
+
|
|
192
|
+
The session enforces deterministic safety budgets before a calculated batch is
|
|
193
|
+
sent to the sink. Defaults allow 1,000 completed dispatches, 10,000 canonical
|
|
194
|
+
events, one virtual hour, 1,000 consecutive zero-duration scheduler batches,
|
|
195
|
+
100 synchronous scheduler batches, and 1,000 Work items in one synchronous
|
|
196
|
+
batch. Overrides must be positive safe integers and cannot exceed the exported
|
|
197
|
+
`FACTORY_EMULATOR_LIMIT_HARD_CAPS`.
|
|
198
|
+
|
|
199
|
+
Crossing an event, completed-dispatch, or virtual-time budget throws
|
|
200
|
+
`FactoryEmulatorExecutionPausedError` and exposes a detached
|
|
201
|
+
`budget-exceeded` diagnostic through `status().error`. The diagnostic identifies
|
|
202
|
+
the limit, configured and observed values, and virtual-time context. The
|
|
203
|
+
calculated over-limit batch is not written or committed, and the kernel does
|
|
204
|
+
not fabricate failed Work. A consecutive zero-time scheduler chain uses the
|
|
205
|
+
distinct `zero-duration-cycle` diagnostic. Initial and runtime Work sets larger
|
|
206
|
+
than `maxSynchronousWorkItems` fail atomically with
|
|
207
|
+
`bounded-work-exceeded` before partial Work or events become visible.
|
|
208
|
+
After any execution pause, `reset()` clears the structured failure and restores
|
|
209
|
+
the deterministic pre-start state. The caller remains responsible for clearing
|
|
210
|
+
or replacing its owned event history before rerunning the same scenario; that
|
|
211
|
+
rerun emits the same canonical history and stops at the same configured
|
|
212
|
+
boundary.
|
|
213
|
+
|
|
214
|
+
Hosts can provide `yieldControl` to choose their own cooperative task boundary.
|
|
215
|
+
Long advancement commands await it after every `maxSynchronousBatches`
|
|
216
|
+
accepted scheduler batches and then resume the same serialized deterministic
|
|
217
|
+
command. The kernel itself does not create timers or Web Workers.
|
|
218
|
+
|
|
219
|
+
## Caller-owned event history
|
|
220
|
+
|
|
221
|
+
`MemoryFactoryEventSink` retains ordered Factory Events up to a required
|
|
222
|
+
caller-selected bound. Each non-empty batch is atomic: history changes only
|
|
223
|
+
after the asynchronous write resolves, and rejection or capacity overflow
|
|
224
|
+
preserves the prior snapshot. Concurrent calls are serialized in call order.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { MemoryFactoryEventSink } from "@you-agent-factory/factory-emulator";
|
|
228
|
+
|
|
229
|
+
const sink = new MemoryFactoryEventSink({ maxEvents: 1_000 });
|
|
230
|
+
await sink.write(eventBatch);
|
|
231
|
+
const detachedHistory = sink.snapshot();
|
|
232
|
+
await sink.close();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Input batches and returned snapshots are mutation-isolated. `close()` is
|
|
236
|
+
idempotent, waits for writes accepted before close, and rejects later writes
|
|
237
|
+
with a `FactoryEventSinkError` whose code is `closed`. Empty batches and
|
|
238
|
+
all-or-nothing capacity failures use the `empty_batch` and `capacity_exceeded`
|
|
239
|
+
codes. The optional `beforeWrite` hook provides caller-controlled backpressure
|
|
240
|
+
and failure injection without transferring ownership of retained history.
|
|
241
|
+
|
|
242
|
+
`RecordingFactoryEventSink` applies the same asynchronous, bounded, atomic
|
|
243
|
+
lifecycle to a canonical UI-client `FactoryRecording`. It rejects duplicate
|
|
244
|
+
event IDs, non-canonical ordering, mixed Factory or session identities, and any
|
|
245
|
+
batch that would make the full recording invalid, without changing the prior
|
|
246
|
+
snapshot.
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { RecordingFactoryEventSink } from "@you-agent-factory/factory-emulator";
|
|
250
|
+
|
|
251
|
+
const recordingSink = new RecordingFactoryEventSink({
|
|
252
|
+
maxEvents: 1_000,
|
|
253
|
+
recording: {
|
|
254
|
+
schemaVersion: "factory-recording/v1",
|
|
255
|
+
id: "customer-support-example",
|
|
256
|
+
title: "Customer support example",
|
|
257
|
+
factory,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
await recordingSink.write(eventBatch);
|
|
261
|
+
const detachedRecording = recordingSink.snapshot();
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Package verification
|
|
265
|
+
|
|
266
|
+
The package is built and verified independently from the dashboard. Run
|
|
267
|
+
`bun run verify` from this directory to check the generated schema module,
|
|
268
|
+
types, formatting and lint rules, focused tests, compiled frozen-reference
|
|
269
|
+
conformance and determinism, dependency boundary, packed inventory, and a
|
|
270
|
+
clean installed consumer. The installed consumer reruns every frozen reference
|
|
271
|
+
from the packed artifact, so fixture freshness is proven without a backend or
|
|
272
|
+
fixture generator. `bun run generate` refreshes the committed runtime schema module after editing
|
|
273
|
+
`schema/scenario.schema.json`.
|
|
274
|
+
|
|
275
|
+
`@you-agent-factory/client` is an explicit peer contract because the emulator
|
|
276
|
+
uses its canonical Factory, Factory Event, and recording APIs without bundling
|
|
277
|
+
or duplicating them. Consumers install both packages at the same version.
|
|
278
|
+
|
|
279
|
+
## Frozen runtime references
|
|
280
|
+
|
|
281
|
+
`loadFactoryEmulatorRuntimeReferences()` returns detached package-local
|
|
282
|
+
references for the documented supported subset. The loader validates each
|
|
283
|
+
fixture's public provenance, Factory and scenario, strictly ordered logical
|
|
284
|
+
ticks, and flattened Factory-event kind sequence before a conformance consumer
|
|
285
|
+
can compare emulator semantics. These references remain frontend-only and do
|
|
286
|
+
not invoke a backend, Go, or WASM fixture generator.
|
|
287
|
+
|
|
288
|
+
The corpus includes resource contention, parallel dispatch, and simultaneous
|
|
289
|
+
completion cases. Their frozen ticks preserve the selected Work, consumed Work,
|
|
290
|
+
completion outcomes, terminal routes, and replay projection so capacity and
|
|
291
|
+
same-deadline behavior remain reproducible between runs.
|
|
292
|
+
|
|
293
|
+
Each fixture records its specific public documentation provenance. In particular,
|
|
294
|
+
the repeater fixture observes a `CONTINUE` route, the routing fixture observes a
|
|
295
|
+
`REJECTED` route, the propagation fixture compares the routed payload under
|
|
296
|
+
`PRESERVE_INPUT`, and the logical-move fixture first makes its `VISIT_COUNT`
|
|
297
|
+
guard eligible before recording the workerless move's synchronous route.
|
|
298
|
+
|
|
299
|
+
`compareFactoryEmulatorRuntimeReference(reference)` runs a reference through a
|
|
300
|
+
fresh deterministic session and compares each logical tick independently. It
|
|
301
|
+
compares ordered event kinds plus normalized dispatch choices, consumed Work,
|
|
302
|
+
outcomes, routes (including routed payload), terminal states, and submission replay projection. Event
|
|
303
|
+
identities and virtual timestamps are deliberately excluded; all other compared
|
|
304
|
+
values are semantic evidence. A mismatch returns the fixture ID, first divergent
|
|
305
|
+
logical tick, comparison surface, expected value, and actual value.
|