@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.
- 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/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
|
-
}
|