@you-agent-factory/factory-emulator 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE.md +19 -1
  2. package/README.md +305 -286
  3. package/dist/compatibility.d.ts +19 -0
  4. package/dist/compatibility.js +156 -0
  5. package/dist/event-sink.d.ts +29 -0
  6. package/dist/event-sink.js +58 -0
  7. package/dist/generated/scenario-schema.d.ts +268 -0
  8. package/dist/generated/scenario-schema.js +319 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.js +8 -0
  11. package/dist/logical-move.d.ts +10 -0
  12. package/dist/logical-move.js +18 -0
  13. package/dist/recording-sink.d.ts +22 -0
  14. package/dist/recording-sink.js +122 -0
  15. package/dist/runtime-reference-conformance.d.ts +20 -0
  16. package/dist/runtime-reference-conformance.js +138 -0
  17. package/dist/runtime-reference-evidence.d.ts +9 -0
  18. package/dist/runtime-reference-evidence.js +193 -0
  19. package/dist/runtime-reference-fixtures.d.ts +7 -0
  20. package/dist/runtime-reference-fixtures.js +308 -0
  21. package/dist/runtime-reference.d.ts +48 -0
  22. package/dist/runtime-reference.js +143 -0
  23. package/dist/scenario-contracts.d.ts +81 -0
  24. package/dist/scenario-contracts.js +11 -0
  25. package/dist/scenario.d.ts +271 -0
  26. package/dist/scenario.js +333 -0
  27. package/dist/scheduler.d.ts +24 -0
  28. package/dist/scheduler.js +104 -0
  29. package/dist/session-contracts.d.ts +262 -0
  30. package/dist/session-contracts.js +74 -0
  31. package/dist/session.d.ts +4 -0
  32. package/dist/session.js +1490 -0
  33. package/dist/submission-replay.d.ts +14 -0
  34. package/dist/submission-replay.js +96 -0
  35. package/dist/submission-validation.d.ts +3 -0
  36. package/dist/submission-validation.js +113 -0
  37. package/examples/customer-support.scenario.v1.json +45 -0
  38. package/package.json +44 -22
  39. package/schema/scenario.schema.json +171 -0
  40. package/generated/factory-emulator-scenario.schema.json +0 -191
  41. package/generated/factory.schema.json +0 -2606
  42. package/src/contracts.ts +0 -41
  43. package/src/data-error.js +0 -21
  44. package/src/data-only.js +0 -208
  45. package/src/emulator.js +0 -195
  46. package/src/emulator.ts +0 -86
  47. package/src/examples.js +0 -86
  48. package/src/examples.ts +0 -11
  49. package/src/generated/factory-schema.d.ts +0 -3
  50. package/src/generated/factory-schema.js +0 -5
  51. package/src/generated/scenario-schema.d.ts +0 -4
  52. package/src/generated/scenario-schema.js +0 -6
  53. package/src/generated/scenario.ts +0 -103
  54. package/src/identity.js +0 -55
  55. package/src/index.js +0 -34
  56. package/src/index.ts +0 -102
  57. package/src/limits.js +0 -125
  58. package/src/parser.js +0 -113
  59. package/src/parser.ts +0 -56
  60. package/src/scheduler.js +0 -374
  61. package/src/semantics.js +0 -354
  62. package/src/semantics.ts +0 -38
  63. package/src/session.js +0 -1201
  64. package/src/session.ts +0 -324
  65. package/src/sinks.js +0 -118
  66. package/src/sinks.ts +0 -56
  67. package/src/support.js +0 -334
  68. package/src/support.ts +0 -15
  69. package/src/virtual-time.js +0 -36
package/src/scheduler.js DELETED
@@ -1,374 +0,0 @@
1
- import { deriveFactoryEmulatorIdentity } from "./identity.js";
2
- import {
3
- resolveEmulatorScenarioResult,
4
- selectEmulatorRule,
5
- } from "./semantics.js";
6
- import {
7
- FactoryEmulatorDurationError,
8
- stateAtElapsed,
9
- virtualTimeAt,
10
- } from "./virtual-time.js";
11
-
12
- /** Calculates one deterministic scheduler batch without performing sink IO. */
13
- export function calculateNextSchedulerBatch({
14
- createEvent,
15
- identityCoordinates,
16
- plan,
17
- scenario,
18
- state,
19
- }) {
20
- if (plan === undefined) {
21
- return undefined;
22
- }
23
- const plannedWorks = plan.workIndexes.map((index) => ({
24
- index,
25
- work: state.works[index],
26
- }));
27
- if (plan.kind === "completion") {
28
- return calculateCompletions({
29
- createEvent,
30
- identityCoordinates,
31
- scenario,
32
- state,
33
- completedWorks: plannedWorks,
34
- });
35
- }
36
- return calculateDispatchStarts({
37
- createEvent,
38
- identityCoordinates,
39
- scenario,
40
- state,
41
- readyWorks: plannedWorks,
42
- workIndexBySubmissionId: plan.workIndexBySubmissionId,
43
- });
44
- }
45
-
46
- /**
47
- * Inspects a bounded slice of retained Work before an atomic scheduler batch is
48
- * materialized. The returned continuation is explicit plain state and can be
49
- * resumed after the host receives a task turn.
50
- */
51
- export function inspectNextSchedulerBatch({
52
- continuation,
53
- maximumWorkItems,
54
- state,
55
- }) {
56
- const inspection = continuation === undefined
57
- ? {
58
- index: 0,
59
- earliestElapsedMs: undefined,
60
- earliestWorkIndexes: [],
61
- hasWaitingWork: false,
62
- readyWorkIndexes: [],
63
- workIndexBySubmissionId: {},
64
- }
65
- : continuation;
66
- let examined = 0;
67
-
68
- while (examined < maximumWorkItems && inspection.index < state.works.length) {
69
- const workIndex = inspection.index;
70
- const work = state.works[workIndex];
71
- inspection.index += 1;
72
- examined += 1;
73
- if (!Object.hasOwn(inspection.workIndexBySubmissionId, work.submissionId)) {
74
- Object.defineProperty(inspection.workIndexBySubmissionId, work.submissionId, {
75
- configurable: true,
76
- enumerable: true,
77
- value: workIndex,
78
- writable: true,
79
- });
80
- }
81
- if (work.phase === "active") {
82
- const deadline = work.dispatch.dueElapsedMs;
83
- if (
84
- inspection.earliestElapsedMs === undefined
85
- || deadline < inspection.earliestElapsedMs
86
- ) {
87
- inspection.earliestElapsedMs = deadline;
88
- inspection.earliestWorkIndexes = [workIndex];
89
- } else if (deadline === inspection.earliestElapsedMs) {
90
- boundedPush(
91
- inspection.earliestWorkIndexes,
92
- workIndex,
93
- maximumWorkItems,
94
- );
95
- }
96
- } else if (work.phase === "ready") {
97
- boundedPush(
98
- inspection.readyWorkIndexes,
99
- workIndex,
100
- maximumWorkItems,
101
- );
102
- } else if (work.phase === "waiting") {
103
- inspection.hasWaitingWork = true;
104
- }
105
- }
106
-
107
- if (inspection.index === state.works.length) {
108
- const completionIsDue = inspection.earliestElapsedMs !== undefined
109
- && inspection.earliestElapsedMs <= state.virtualElapsedMs;
110
- if (completionIsDue) {
111
- return completedInspection(
112
- "completion",
113
- inspection.earliestWorkIndexes,
114
- inspection.workIndexBySubmissionId,
115
- );
116
- }
117
- if (inspection.readyWorkIndexes.length > 0) {
118
- return completedInspection(
119
- "dispatch",
120
- inspection.readyWorkIndexes,
121
- inspection.workIndexBySubmissionId,
122
- );
123
- }
124
- return inspection.earliestElapsedMs === undefined
125
- ? { done: true, batch: undefined, hasWaitingWork: inspection.hasWaitingWork }
126
- : completedInspection(
127
- "completion",
128
- inspection.earliestWorkIndexes,
129
- inspection.workIndexBySubmissionId,
130
- );
131
- }
132
- return { done: false, continuation: inspection };
133
- }
134
-
135
- function boundedPush(values, value, maximum) {
136
- if (values.length <= maximum) {
137
- values.push(value);
138
- }
139
- }
140
-
141
- function completedInspection(kind, workIndexes, workIndexBySubmissionId) {
142
- return {
143
- done: true,
144
- batch: { kind, workItems: workIndexes.length },
145
- hasWaitingWork: false,
146
- plan: { kind, workIndexes, workIndexBySubmissionId },
147
- };
148
- }
149
-
150
- function calculateDispatchStarts({
151
- createEvent,
152
- identityCoordinates,
153
- scenario,
154
- state,
155
- readyWorks,
156
- workIndexBySubmissionId,
157
- }) {
158
- const ruleCursors = { ...state.ruleCursors };
159
- const replacements = new Map();
160
- let eventOrdinal = 0;
161
- const events = readyWorks.flatMap(({ index, work }) => {
162
- const submission = { ...work, id: work.submissionId };
163
- const rule = selectEmulatorRule(scenario, submission);
164
- const invocationIndex = rule === undefined
165
- ? 0
166
- : ownNumberOrZero(ruleCursors, rule.id);
167
- const resolution = resolveEmulatorScenarioResult(
168
- scenario,
169
- submission,
170
- invocationIndex,
171
- );
172
- if (rule !== undefined) {
173
- defineOwnValue(ruleCursors, rule.id, invocationIndex + 1);
174
- }
175
- const outcome = resolutionOutcome(resolution);
176
- if (outcome === undefined) {
177
- replacements.set(index, { ...work, phase: "waiting" });
178
- return [];
179
- }
180
- const transitionId = rule?.id ?? "emulator-unmatched";
181
- const lineageTokenId = resolveLineageTokenId({
182
- identityCoordinates,
183
- outcome,
184
- scenario,
185
- state,
186
- work,
187
- workIndexBySubmissionId,
188
- });
189
- const dispatchCoordinates = {
190
- ...identityCoordinates,
191
- workId: work.workId,
192
- tokenId: work.tokenId,
193
- lineageTokenId,
194
- transitionId,
195
- invocationIndex,
196
- };
197
- const dispatchId = deriveFactoryEmulatorIdentity("dispatch", dispatchCoordinates);
198
- const completionId = deriveFactoryEmulatorIdentity("completion", dispatchCoordinates);
199
- const outputTokenId = deriveFactoryEmulatorIdentity("token", {
200
- ...dispatchCoordinates,
201
- dispatchId,
202
- completionId,
203
- });
204
- const durationMs = outcome.durationMs ?? 0;
205
- const dueElapsedMs = state.virtualElapsedMs + durationMs;
206
- if (!Number.isSafeInteger(dueElapsedMs)) {
207
- throw new FactoryEmulatorDurationError(durationMs);
208
- }
209
- virtualTimeAt(scenario, dueElapsedMs);
210
- replacements.set(index, {
211
- ...work,
212
- phase: "active",
213
- dispatch: {
214
- dispatchId,
215
- completionId,
216
- lineageTokenId,
217
- outputTokenId,
218
- transitionId,
219
- startedElapsedMs: state.virtualElapsedMs,
220
- dueElapsedMs,
221
- outcome,
222
- },
223
- });
224
- const sequence = state.counters.events + eventOrdinal;
225
- eventOrdinal += 1;
226
- return [createEvent({
227
- identityCoordinates,
228
- sequence,
229
- tick: state.counters.events,
230
- sessionId: state.sessionId,
231
- eventTime: state.virtualTime,
232
- type: "DISPATCH_REQUEST",
233
- context: dispatchContext(work, dispatchId),
234
- payload: {
235
- transitionId,
236
- inputs: [{ workId: work.workId }],
237
- },
238
- })];
239
- });
240
- return {
241
- elapsedMs: state.virtualElapsedMs,
242
- batch: events.length === 0 ? undefined : { events },
243
- state: {
244
- ...state,
245
- ruleCursors,
246
- counters: {
247
- ...state.counters,
248
- events: state.counters.events + events.length,
249
- dispatches: state.counters.dispatches + events.length,
250
- },
251
- },
252
- workReplacements: replacements,
253
- };
254
- }
255
-
256
- function calculateCompletions({
257
- createEvent,
258
- identityCoordinates,
259
- scenario,
260
- state,
261
- completedWorks,
262
- }) {
263
- const elapsedMs = completedWorks[0].work.dispatch.dueElapsedMs;
264
- const eventTime = virtualTimeAt(scenario, elapsedMs);
265
- const replacements = new Map();
266
- const events = completedWorks.map(({ index, work }, ordinal) => {
267
- const { dispatch } = work;
268
- const accepted = dispatch.outcome.kind === "complete";
269
- replacements.set(index, {
270
- ...work,
271
- phase: "completed",
272
- tokenId: dispatch.outputTokenId,
273
- ...(accepted && dispatch.outcome.output !== undefined
274
- ? { output: dispatch.outcome.output }
275
- : {}),
276
- ...(!accepted ? { rejectionReason: dispatch.outcome.reason } : {}),
277
- });
278
- return createEvent({
279
- identityCoordinates,
280
- sequence: state.counters.events + ordinal,
281
- tick: state.counters.events,
282
- sessionId: state.sessionId,
283
- eventTime,
284
- type: "DISPATCH_RESPONSE",
285
- context: dispatchContext(work, dispatch.dispatchId),
286
- payload: {
287
- completionId: dispatch.completionId,
288
- transitionId: dispatch.transitionId,
289
- outcome: accepted ? "ACCEPTED" : "REJECTED",
290
- ...(!accepted ? { feedback: dispatch.outcome.reason } : {}),
291
- durationMillis: dispatch.dueElapsedMs - dispatch.startedElapsedMs,
292
- metadata: {
293
- emulatorLineageTokenId: dispatch.lineageTokenId,
294
- emulatorTokenId: dispatch.outputTokenId,
295
- },
296
- },
297
- });
298
- });
299
- return {
300
- elapsedMs,
301
- batch: { events },
302
- state: {
303
- ...stateAtElapsed(scenario, state, elapsedMs),
304
- counters: {
305
- ...state.counters,
306
- events: state.counters.events + events.length,
307
- completions: state.counters.completions + events.length,
308
- },
309
- },
310
- workReplacements: replacements,
311
- };
312
- }
313
-
314
- function resolutionOutcome(resolution) {
315
- if (resolution.kind === "outcome") {
316
- return resolution.outcome;
317
- }
318
- if (resolution.behavior.kind === "reject") {
319
- return { kind: "reject", reason: resolution.behavior.reason };
320
- }
321
- return undefined;
322
- }
323
-
324
- function resolveLineageTokenId({
325
- identityCoordinates,
326
- outcome,
327
- scenario,
328
- state,
329
- work,
330
- workIndexBySubmissionId,
331
- }) {
332
- const cursor = outcome?.kind === "complete" ? outcome.lineageCursor : undefined;
333
- if (cursor?.kind === "initialSubmission") {
334
- const workIndex = Object.hasOwn(workIndexBySubmissionId, cursor.submissionId)
335
- ? workIndexBySubmissionId[cursor.submissionId]
336
- : undefined;
337
- return state.works[workIndex]?.tokenId;
338
- }
339
- if (cursor?.kind === "scriptedOutcome") {
340
- const ruleIndex = scenario.rules.findIndex((rule) => rule.id === cursor.ruleId);
341
- return deriveFactoryEmulatorIdentity("token", {
342
- ...identityCoordinates,
343
- lineage: {
344
- kind: "scriptedOutcome",
345
- ruleIndex,
346
- ruleId: cursor.ruleId,
347
- outcomeIndex: cursor.outcomeIndex,
348
- },
349
- });
350
- }
351
- return work.tokenId;
352
- }
353
-
354
- function ownNumberOrZero(values, name) {
355
- return Object.hasOwn(values, name) ? values[name] : 0;
356
- }
357
-
358
- function defineOwnValue(values, name, value) {
359
- Object.defineProperty(values, name, {
360
- configurable: true,
361
- enumerable: true,
362
- value,
363
- writable: true,
364
- });
365
- }
366
- function dispatchContext(work, dispatchId) {
367
- return {
368
- dispatchId,
369
- requestId: work.requestId,
370
- traceIds: [work.traceId],
371
- workIds: [work.workId],
372
- currentChainingTraceId: work.traceId,
373
- };
374
- }