@qvac/harness 0.7.1 → 1.0.0-rc.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/README.md +224 -0
- package/dist-lib/index.d.ts +55 -3
- package/dist-lib/index.js +721 -236
- package/dist-lib/lib/capabilities/completion-engine/abort-polyfill.d.ts +1 -0
- package/dist-lib/lib/capabilities/completion-engine/abort-polyfill.js +10 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/answering.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/answering.js +17 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/approval.d.ts +4 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/approval.js +29 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/direct-search.d.ts +4 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/direct-search.js +73 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execute.d.ts +41 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execute.js +49 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/executing.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/executing.js +102 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execution-plan.d.ts +47 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/execution-plan.js +87 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/finishing.d.ts +2 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/finishing.js +32 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspect-preload.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspect-preload.js +75 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspecting.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/inspecting.js +152 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/loading-skill.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/loading-skill.js +94 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preload-failure.d.ts +9 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preload-failure.js +11 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preloading.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preloading.js +114 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preparing.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/preparing.js +198 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejecting.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejecting.js +71 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejection.d.ts +18 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/rejection.js +70 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/resolving.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/resolving.js +60 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/stream.d.ts +21 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/stream.js +48 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/streaming.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/streaming.js +156 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/wrap-up.d.ts +7 -0
- package/dist-lib/lib/capabilities/completion-engine/actors/wrap-up.js +61 -0
- package/dist-lib/lib/capabilities/completion-engine/driver.d.ts +25 -0
- package/dist-lib/lib/capabilities/completion-engine/driver.js +553 -0
- package/dist-lib/lib/capabilities/completion-engine/durable.d.ts +61 -0
- package/dist-lib/lib/capabilities/completion-engine/durable.js +150 -0
- package/dist-lib/lib/capabilities/completion-engine/operations.d.ts +6 -0
- package/dist-lib/lib/capabilities/completion-engine/operations.js +239 -0
- package/dist-lib/lib/capabilities/completion-engine/ports.d.ts +3 -0
- package/dist-lib/lib/capabilities/completion-engine/ports.js +267 -0
- package/dist-lib/lib/capabilities/completion-engine/session.d.ts +76 -0
- package/dist-lib/lib/capabilities/completion-engine/session.js +70 -0
- package/dist-lib/lib/capabilities/completion-loop/index.d.ts +80 -0
- package/dist-lib/lib/capabilities/completion-loop/index.js +158 -0
- package/dist-lib/lib/capabilities/completion-loop/metrics.d.ts +5 -0
- package/dist-lib/lib/capabilities/completion-loop/metrics.js +29 -0
- package/dist-lib/lib/capabilities/completion-loop/round.d.ts +33 -0
- package/dist-lib/lib/capabilities/completion-loop/round.js +353 -0
- package/dist-lib/lib/capabilities/completion-loop/tool-execution.d.ts +60 -0
- package/dist-lib/lib/capabilities/completion-loop/tool-execution.js +270 -0
- package/dist-lib/lib/capabilities/completion-loop/turns.d.ts +23 -0
- package/dist-lib/lib/capabilities/completion-loop/turns.js +112 -0
- package/dist-lib/lib/capabilities/completion-runtime.d.ts +46 -0
- package/dist-lib/lib/capabilities/completion-runtime.js +74 -0
- package/dist-lib/lib/capabilities/completion.d.ts +18 -335
- package/dist-lib/lib/capabilities/completion.js +20 -1301
- package/dist-lib/lib/capabilities/download.d.ts +1 -1
- package/dist-lib/lib/capabilities/download.js +7 -2
- package/dist-lib/lib/capabilities/invoke.js +5 -3
- package/dist-lib/lib/capabilities/tool-plan.d.ts +35 -0
- package/dist-lib/lib/capabilities/tool-plan.js +66 -0
- package/dist-lib/lib/capabilities/tool-turns.d.ts +11 -2
- package/dist-lib/lib/capabilities/tool-turns.js +30 -2
- package/dist-lib/lib/capabilities/types.d.ts +287 -0
- package/dist-lib/lib/capabilities/types.js +1 -0
- package/dist-lib/lib/completion-lifecycle.d.ts +14 -0
- package/dist-lib/lib/completion-lifecycle.js +126 -0
- package/dist-lib/lib/connect.d.ts +10 -3
- package/dist-lib/lib/connect.js +329 -23
- package/dist-lib/lib/contract-types.d.ts +19 -2
- package/dist-lib/lib/harness-lifecycle.d.ts +66 -0
- package/dist-lib/lib/harness-lifecycle.js +129 -0
- package/dist-lib/lib/helpers/detach-json.d.ts +1 -0
- package/dist-lib/lib/helpers/detach-json.js +146 -0
- package/dist-lib/lib/helpers/markup-scanner.d.ts +26 -0
- package/dist-lib/lib/helpers/markup-scanner.js +156 -0
- package/dist-lib/lib/helpers/markup-stream-filter.js +138 -35
- package/dist-lib/lib/helpers/race-abort.d.ts +9 -4
- package/dist-lib/lib/helpers/race-abort.js +2 -7
- package/dist-lib/lib/helpers/recovery-required.d.ts +1 -0
- package/dist-lib/lib/helpers/recovery-required.js +1 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +10 -2
- package/dist-lib/lib/helpers/run-outcome.js +8 -5
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +2 -0
- package/dist-lib/lib/helpers/tool-call-markup.js +197 -82
- package/dist-lib/lib/hooks/built-in/arg-repair.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.js +1 -0
- package/dist-lib/lib/hooks/built-in/egress.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/sandbox.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/sandbox.js +6 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.d.ts +1 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +4 -1
- package/dist-lib/lib/hooks/pipeline.js +24 -2
- package/dist-lib/lib/hooks/run.d.ts +2 -1
- package/dist-lib/lib/hooks/run.js +19 -7
- package/dist-lib/lib/hooks/types.d.ts +17 -6
- package/dist-lib/lib/machine/completion/actions/approval.d.ts +3 -0
- package/dist-lib/lib/machine/completion/actions/approval.js +16 -0
- package/dist-lib/lib/machine/completion/actions/completion.d.ts +23 -0
- package/dist-lib/lib/machine/completion/actions/completion.js +142 -0
- package/dist-lib/lib/machine/completion/actions/skills.d.ts +9 -0
- package/dist-lib/lib/machine/completion/actions/skills.js +83 -0
- package/dist-lib/lib/machine/completion/actions/tools.d.ts +16 -0
- package/dist-lib/lib/machine/completion/actions/tools.js +173 -0
- package/dist-lib/lib/machine/completion/context.d.ts +9 -0
- package/dist-lib/lib/machine/completion/context.js +44 -0
- package/dist-lib/lib/machine/completion/contract.d.ts +6 -0
- package/dist-lib/lib/machine/completion/contract.js +1 -0
- package/dist-lib/lib/machine/completion/guards/retry.d.ts +9 -0
- package/dist-lib/lib/machine/completion/guards/retry.js +25 -0
- package/dist-lib/lib/machine/completion/implementations.d.ts +81 -0
- package/dist-lib/lib/machine/completion/implementations.js +152 -0
- package/dist-lib/lib/machine/completion/machine.d.ts +375 -0
- package/dist-lib/lib/machine/completion/machine.js +511 -0
- package/dist-lib/lib/machine/completion/ports.d.ts +65 -0
- package/dist-lib/lib/machine/completion/ports.js +224 -0
- package/dist-lib/lib/machine/completion/rules.d.ts +39 -0
- package/dist-lib/lib/machine/completion/rules.js +115 -0
- package/dist-lib/lib/machine/completion/types.d.ts +493 -0
- package/dist-lib/lib/machine/completion/types.js +1 -0
- package/dist-lib/lib/machine/engine.d.ts +93 -0
- package/dist-lib/lib/machine/engine.js +483 -0
- package/dist-lib/lib/machine/errors.d.ts +44 -0
- package/dist-lib/lib/machine/errors.js +373 -0
- package/dist-lib/lib/machine/guards.d.ts +2 -0
- package/dist-lib/lib/machine/guards.js +6 -0
- package/dist-lib/lib/machine/index.d.ts +20 -0
- package/dist-lib/lib/machine/index.js +11 -0
- package/dist-lib/lib/machine/replay-output.d.ts +24 -0
- package/dist-lib/lib/machine/replay-output.js +50 -0
- package/dist-lib/lib/machine/stable-stringify.d.ts +1 -0
- package/dist-lib/lib/machine/stable-stringify.js +11 -0
- package/dist-lib/lib/models/model-pool.d.ts +14 -6
- package/dist-lib/lib/models/model-pool.js +95 -30
- package/dist-lib/lib/run-log/history-codec.d.ts +23 -0
- package/dist-lib/lib/run-log/history-codec.js +973 -0
- package/dist-lib/lib/run-log/patch.d.ts +6 -0
- package/dist-lib/lib/run-log/patch.js +93 -0
- package/dist-lib/lib/run-log/persisted-actor.d.ts +18 -0
- package/dist-lib/lib/run-log/persisted-actor.js +218 -0
- package/dist-lib/lib/run-log/protocol-types.d.ts +153 -0
- package/dist-lib/lib/run-log/protocol-types.js +1 -0
- package/dist-lib/lib/run-log/protocol.d.ts +27 -0
- package/dist-lib/lib/run-log/protocol.js +1056 -0
- package/dist-lib/lib/run-log/replay.d.ts +25 -0
- package/dist-lib/lib/run-log/replay.js +129 -0
- package/dist-lib/lib/run-log/run-journal.d.ts +125 -0
- package/dist-lib/lib/run-log/run-journal.js +783 -0
- package/dist-lib/lib/run-log-inspect.d.ts +25 -0
- package/dist-lib/lib/run-log-inspect.js +627 -0
- package/dist-lib/lib/run-log-protocol.d.ts +2 -0
- package/dist-lib/lib/run-log-protocol.js +1 -0
- package/dist-lib/lib/run-log-redact.d.ts +27 -0
- package/dist-lib/lib/run-log-redact.js +1107 -0
- package/dist-lib/lib/serve.js +307 -11
- package/dist-lib/lib/spawn.js +3 -2
- package/dist-lib/lib/tool-call-display.d.ts +1 -21
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +2 -1
- package/dist-lib/lib/tools/exec.d.ts +2 -1
- package/dist-lib/lib/tools/exec.js +57 -2
- package/dist-lib/lib/tools/generate-image.js +1 -1
- package/dist-lib/lib/tools/generate-music.js +1 -1
- package/dist-lib/lib/tools/http-request.d.ts +2 -1
- package/dist-lib/lib/tools/http-request.js +2 -1
- package/dist-lib/lib/tools/mcp-call.js +8 -1
- package/dist-lib/lib/tools/operations/builders.d.ts +1 -0
- package/dist-lib/lib/tools/operations/builders.js +1 -0
- package/dist-lib/lib/tools/operations/compile.js +11 -1
- package/dist-lib/lib/tools/operations/manifest.js +1 -0
- package/dist-lib/lib/tools/outbound-guard.d.ts +8 -6
- package/dist-lib/lib/tools/outbound-guard.js +25 -8
- package/dist-lib/lib/tools/skills/catalog.d.ts +1 -0
- package/dist-lib/lib/tools/skills/catalog.js +35 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +1 -0
- package/dist-lib/lib/tools/skills/cli-schema.js +25 -0
- package/dist-lib/lib/wire.d.ts +5 -0
- package/dist-lib/lib/wire.js +128 -5
- package/dist-lib/schema/harness.js +20 -5
- package/dist-lib/spec/hrpc/messages.js +95 -15
- package/dist-lib/spec/hyperschema/index.js +95 -15
- package/dist-lib/spec/hyperschema/schema.json +69 -1
- package/dist-lib/spec/hyperschema/types.d.ts +10 -3
- package/package.json +20 -2
- package/patches/@qvac%2Finference@0.20.0.patch +67 -0
- package/patches/bare-stow@0.1.5.patch +68 -0
- package/patches/hypercore-blob-server@1.15.0.patch +15 -0
- package/patches/localwatch@1.4.3.patch +13 -0
- package/patches/pyodide@314.0.2.patch +144 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { waitFor } from 'xstate';
|
|
2
|
+
import { createCompletionMachine } from './completion/machine.js';
|
|
3
|
+
import { createInvokeTracker } from './completion/ports.js';
|
|
4
|
+
import { isRecord } from '../run-log/patch.js';
|
|
5
|
+
import { replayFinalRound, replaySuccessOutput } from './replay-output.js';
|
|
6
|
+
import { AbortedError, didDrainTimeOut, raceDrain, rehydrateError, tagError } from './errors.js';
|
|
7
|
+
import { createPersistedActor } from '../run-log/persisted-actor.js';
|
|
8
|
+
import { stableStringify } from './stable-stringify.js';
|
|
9
|
+
import { applyEngineStep, captureRunRecord, captureEngineStep, createEngineStep, prepareEngineBoot, transitionName } from '../run-log/protocol.js';
|
|
10
|
+
export { stableStringify } from './stable-stringify.js';
|
|
11
|
+
import { MACHINE_VERSION, XSTATE_VERSION, RUN_LOG_VERSION, ENGINE_BOUNDARY_BOOT, RunJournal, acquireRun, copy } from '../run-log/run-journal.js';
|
|
12
|
+
function nameOf(named) {
|
|
13
|
+
return transitionName(named);
|
|
14
|
+
}
|
|
15
|
+
function stripTransition(transition) {
|
|
16
|
+
const out = { eventType: transition.eventType };
|
|
17
|
+
if (transition.target?.length) {
|
|
18
|
+
out.target = transition.target.map((node) => node.id);
|
|
19
|
+
}
|
|
20
|
+
if (transition.guard) {
|
|
21
|
+
out.guard = nameOf(transition.guard);
|
|
22
|
+
}
|
|
23
|
+
if (transition.actions.length) {
|
|
24
|
+
out.actions = transition.actions.map(nameOf);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
let cachedDefinition;
|
|
29
|
+
function machineDefinition() {
|
|
30
|
+
cachedDefinition ??= stripNode(createCompletionMachine().root);
|
|
31
|
+
return cachedDefinition;
|
|
32
|
+
}
|
|
33
|
+
export function completionMachineDefinition() {
|
|
34
|
+
return copy(machineDefinition());
|
|
35
|
+
}
|
|
36
|
+
function stripNode(node) {
|
|
37
|
+
const out = {
|
|
38
|
+
id: node.id,
|
|
39
|
+
type: node.type,
|
|
40
|
+
tags: [...node.tags],
|
|
41
|
+
entry: node.entry.map(nameOf),
|
|
42
|
+
exit: node.exit.map(nameOf),
|
|
43
|
+
invoke: node.invoke.map((invoke) => ({
|
|
44
|
+
id: invoke.id,
|
|
45
|
+
src: typeof invoke.src === 'string' ? invoke.src : nameOf(invoke.src)
|
|
46
|
+
}))
|
|
47
|
+
};
|
|
48
|
+
const initial = node.initial?.target?.[0]?.id;
|
|
49
|
+
if (initial) {
|
|
50
|
+
const prefix = `${node.id}.`;
|
|
51
|
+
out.initial = initial.startsWith(prefix) ? initial.slice(prefix.length) : initial;
|
|
52
|
+
}
|
|
53
|
+
const children = Object.entries(node.states);
|
|
54
|
+
if (children.length) {
|
|
55
|
+
out.states = Object.fromEntries(children.map(([key, child]) => [key, stripNode(child)]));
|
|
56
|
+
}
|
|
57
|
+
if (node.transitions.size) {
|
|
58
|
+
out.on = Object.fromEntries([...node.transitions].map(([event, list]) => [event, list.map(stripTransition)]));
|
|
59
|
+
}
|
|
60
|
+
if (node.always?.length) {
|
|
61
|
+
out.always = node.always.map(stripTransition);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
export const HOST_APPEND_TIMEOUT_MS = 5000;
|
|
66
|
+
function readRestoredSnapshot(value) {
|
|
67
|
+
if (!isRecord(value) || Array.isArray(value))
|
|
68
|
+
return undefined;
|
|
69
|
+
const context = isRecord(value.context) && !Array.isArray(value.context) ? value.context : undefined;
|
|
70
|
+
return { status: value.status, output: value.output, context };
|
|
71
|
+
}
|
|
72
|
+
function isActiveIdle(snapshot) {
|
|
73
|
+
const value = snapshot.value;
|
|
74
|
+
return isRecord(value) && value.active === 'idle';
|
|
75
|
+
}
|
|
76
|
+
function isPausedOrAwaitingRecovery(snapshot) {
|
|
77
|
+
return snapshot.hasTag('paused');
|
|
78
|
+
}
|
|
79
|
+
function wasInActiveState(snapshot, name) {
|
|
80
|
+
if (isRecord(snapshot.value) && snapshot.value.active === name) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
const historyValue = Reflect.get(snapshot, 'historyValue');
|
|
84
|
+
if (!isRecord(historyValue)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return Object.values(historyValue).some((nodes) => Array.isArray(nodes) &&
|
|
88
|
+
nodes.some((node) => isRecord(node) && node.id === `completion.active.${name}`));
|
|
89
|
+
}
|
|
90
|
+
export async function* runEngine(run, options = {}) {
|
|
91
|
+
const { runId } = run;
|
|
92
|
+
const log = boundaryLog(options, runId);
|
|
93
|
+
const journal = new RunJournal(log, {
|
|
94
|
+
boot: log[ENGINE_BOUNDARY_BOOT](),
|
|
95
|
+
redact: options.redact,
|
|
96
|
+
redactShared: options.redactShared,
|
|
97
|
+
redactOperationResult: options.redactOperationResult,
|
|
98
|
+
redactRecord: options.redactRecord
|
|
99
|
+
});
|
|
100
|
+
const core = journal.core;
|
|
101
|
+
const release = acquireRun(run.lockOwner ?? options.lockOwner ?? core, runId);
|
|
102
|
+
try {
|
|
103
|
+
if (!journal.restored) {
|
|
104
|
+
await journal.restore();
|
|
105
|
+
}
|
|
106
|
+
else if (journal.knownLength !== core.length) {
|
|
107
|
+
throw new Error(`Run ${runId} changed while opening`);
|
|
108
|
+
}
|
|
109
|
+
if (run.resume) {
|
|
110
|
+
if (!journal.metadata) {
|
|
111
|
+
throw new Error(`Run ${runId} does not exist`);
|
|
112
|
+
}
|
|
113
|
+
const requestMatches = run.request === undefined ||
|
|
114
|
+
stableStringify(journal.metadata.request) === stableStringify(run.request);
|
|
115
|
+
if (journal.metadata.runId !== runId ||
|
|
116
|
+
(run.requestDigest !== undefined
|
|
117
|
+
? journal.metadata.requestDigest !== run.requestDigest
|
|
118
|
+
: !requestMatches)) {
|
|
119
|
+
throw new Error('Resume cannot replace the original model, conversation, or execution configuration');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
if (run.request === undefined) {
|
|
124
|
+
throw new Error('A new run requires a request identity');
|
|
125
|
+
}
|
|
126
|
+
if (core.length) {
|
|
127
|
+
throw new Error(`Run ${runId} already exists; use resume mode`);
|
|
128
|
+
}
|
|
129
|
+
if (run.recovery?.length) {
|
|
130
|
+
throw new Error('Recovery resolutions require resume mode');
|
|
131
|
+
}
|
|
132
|
+
const metadata = {
|
|
133
|
+
version: RUN_LOG_VERSION,
|
|
134
|
+
type: 'metadata',
|
|
135
|
+
machine: MACHINE_VERSION,
|
|
136
|
+
definition: machineDefinition(),
|
|
137
|
+
xstate: XSTATE_VERSION,
|
|
138
|
+
runId,
|
|
139
|
+
request: run.request,
|
|
140
|
+
requestDigest: run.requestDigest,
|
|
141
|
+
digestRunId: run.requestDigest ? runId : undefined,
|
|
142
|
+
budget: run.budget,
|
|
143
|
+
startedAt: run.now ? run.now() : undefined
|
|
144
|
+
};
|
|
145
|
+
await journal.append(metadata);
|
|
146
|
+
}
|
|
147
|
+
const restored = readRestoredSnapshot(journal.snapshot);
|
|
148
|
+
if (restored && restored.context?.runId !== runId) {
|
|
149
|
+
throw new Error('Run checkpoint identity does not match its metadata');
|
|
150
|
+
}
|
|
151
|
+
await journal.resolve(run.recovery ?? []);
|
|
152
|
+
if (restored?.status === 'done') {
|
|
153
|
+
yield { type: run.resume ? 'run-resumed' : 'run-started', runId };
|
|
154
|
+
const round = restored.context?.round;
|
|
155
|
+
if (run.resume && typeof round === 'number') {
|
|
156
|
+
yield { type: 'round-reset', runId, round, replacedTypes: ['content', 'thinking'] };
|
|
157
|
+
}
|
|
158
|
+
const output = restored.output;
|
|
159
|
+
if (isRecord(output) && output.status === 'failure') {
|
|
160
|
+
const failure = restored.context?.failure;
|
|
161
|
+
const fallback = typeof output.error === 'string' ? output.error : 'Completion failed';
|
|
162
|
+
throw rehydrateError(failure ? tagError(failure) : undefined, fallback);
|
|
163
|
+
}
|
|
164
|
+
yield* replaySuccessOutput({ runId, round, context: restored.context, output });
|
|
165
|
+
return { status: 'replayed' };
|
|
166
|
+
}
|
|
167
|
+
yield { type: run.resume ? 'run-resumed' : 'run-started', runId };
|
|
168
|
+
const invokes = createInvokeTracker();
|
|
169
|
+
const boot = immutableBoot(getCurrentBoot(log, journal));
|
|
170
|
+
const mailbox = createEventMailbox();
|
|
171
|
+
const { ports, context } = await run.start({
|
|
172
|
+
operations: createOperationPort(journal),
|
|
173
|
+
invokes,
|
|
174
|
+
boot
|
|
175
|
+
});
|
|
176
|
+
return yield* drive({
|
|
177
|
+
journal,
|
|
178
|
+
run,
|
|
179
|
+
machine: createCompletionMachine(context, ports.now, {
|
|
180
|
+
ports,
|
|
181
|
+
journal,
|
|
182
|
+
invokes,
|
|
183
|
+
emitContent: (event) => mailbox.emit(event)
|
|
184
|
+
}),
|
|
185
|
+
invokes,
|
|
186
|
+
now: ports.now,
|
|
187
|
+
durable: typeof options.onStep === 'function',
|
|
188
|
+
onDetached: options.onDetached,
|
|
189
|
+
mailbox
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
if (journal.detachedEffects) {
|
|
194
|
+
journal.sealed = true;
|
|
195
|
+
}
|
|
196
|
+
release();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function* drive({ journal, run, machine, invokes, now, durable, onDetached, mailbox }) {
|
|
200
|
+
const { runId, signal } = run;
|
|
201
|
+
const pausedMs = run.resume && journal.snapshotSavedAt !== undefined
|
|
202
|
+
? Math.max(0, now() - journal.snapshotSavedAt)
|
|
203
|
+
: 0;
|
|
204
|
+
const { queue } = mailbox;
|
|
205
|
+
let detached = false;
|
|
206
|
+
const detach = () => {
|
|
207
|
+
detached = true;
|
|
208
|
+
if (!journal.detachedEffects) {
|
|
209
|
+
journal.detachedEffects = invokes.waitForInvokes();
|
|
210
|
+
onDetached?.(journal.detachedEffects, runId);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
let wake = () => { };
|
|
214
|
+
let wakeVersion = 0;
|
|
215
|
+
const notify = () => {
|
|
216
|
+
wakeVersion++;
|
|
217
|
+
wake();
|
|
218
|
+
};
|
|
219
|
+
mailbox.notify = notify;
|
|
220
|
+
const { actor, persistence } = await createPersistedActor(journal, machine, notify, now);
|
|
221
|
+
actor.on('*', (event) => {
|
|
222
|
+
queue.push(event);
|
|
223
|
+
notify();
|
|
224
|
+
});
|
|
225
|
+
actor.subscribe({ next: notify, error: notify });
|
|
226
|
+
const pause = () => {
|
|
227
|
+
if (actor.getSnapshot().status === 'active') {
|
|
228
|
+
actor.send({ type: 'PAUSE' });
|
|
229
|
+
}
|
|
230
|
+
notify();
|
|
231
|
+
};
|
|
232
|
+
signal?.addEventListener('abort', pause);
|
|
233
|
+
try {
|
|
234
|
+
await persistence.checkpoint();
|
|
235
|
+
const restored = actor.getSnapshot();
|
|
236
|
+
actor.start();
|
|
237
|
+
if (run.resume) {
|
|
238
|
+
if (!actor.getSnapshot().hasTag('paused')) {
|
|
239
|
+
pause();
|
|
240
|
+
}
|
|
241
|
+
const result = await drainResume({ actor, invokes, persistence, durable, detach });
|
|
242
|
+
if (result) {
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
yield* replayInterruptedGeneration(runId, restored);
|
|
246
|
+
const uncertain = journal.uncertainWrite();
|
|
247
|
+
if (uncertain) {
|
|
248
|
+
actor.send({ type: 'RECOVERY_REQUIRED', operation: uncertain });
|
|
249
|
+
}
|
|
250
|
+
else if (!signal?.aborted) {
|
|
251
|
+
actor.send({ type: 'RESUME', pausedMs });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (signal?.aborted) {
|
|
255
|
+
pause();
|
|
256
|
+
}
|
|
257
|
+
else if (isActiveIdle(actor.getSnapshot())) {
|
|
258
|
+
actor.send({ type: 'START' });
|
|
259
|
+
}
|
|
260
|
+
while (true) {
|
|
261
|
+
if (journal.hasPendingFlush()) {
|
|
262
|
+
await persistence.flush();
|
|
263
|
+
}
|
|
264
|
+
yield* drainQueue(queue);
|
|
265
|
+
if (isActiveIdle(actor.getSnapshot()) && !signal?.aborted) {
|
|
266
|
+
actor.send({ type: 'START' });
|
|
267
|
+
}
|
|
268
|
+
const snapshot = actor.getSnapshot();
|
|
269
|
+
if (snapshot.status === 'error') {
|
|
270
|
+
return { status: 'error', snapshot, error: snapshot.error };
|
|
271
|
+
}
|
|
272
|
+
if (snapshot.status === 'done') {
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
const isSettling = settling(snapshot);
|
|
276
|
+
const settlingResult = isSettling
|
|
277
|
+
? await drainSettling({ actor, persistence, durable, detach })
|
|
278
|
+
: undefined;
|
|
279
|
+
if (settlingResult) {
|
|
280
|
+
yield* drainQueue(queue);
|
|
281
|
+
return settlingResult;
|
|
282
|
+
}
|
|
283
|
+
if (isSettling) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (isPausedOrAwaitingRecovery(snapshot)) {
|
|
287
|
+
return yield* finishPaused({
|
|
288
|
+
actor,
|
|
289
|
+
invokes,
|
|
290
|
+
persistence,
|
|
291
|
+
durable,
|
|
292
|
+
detach,
|
|
293
|
+
journal,
|
|
294
|
+
queue,
|
|
295
|
+
runId
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
const observedWakeVersion = wakeVersion;
|
|
299
|
+
if (queue.length) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
await new Promise((resolve) => {
|
|
303
|
+
wake = resolve;
|
|
304
|
+
if (wakeVersion !== observedWakeVersion) {
|
|
305
|
+
resolve();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
await persistence.flush();
|
|
310
|
+
yield* drainQueue(queue);
|
|
311
|
+
return { status: 'done', snapshot: actor.getSnapshot() };
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
signal?.removeEventListener('abort', pause);
|
|
315
|
+
try {
|
|
316
|
+
if (actor.getSnapshot().status === 'active') {
|
|
317
|
+
pause();
|
|
318
|
+
}
|
|
319
|
+
if (!detached && (await invokes.drainTimedOut())) {
|
|
320
|
+
detach();
|
|
321
|
+
}
|
|
322
|
+
if (await shouldDetachWhileClosing({ detached, journal, actor })) {
|
|
323
|
+
detach();
|
|
324
|
+
}
|
|
325
|
+
await persistence.checkpoint();
|
|
326
|
+
await persistence.flush();
|
|
327
|
+
run.settled?.(actor.getSnapshot());
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
await persistence.dispose();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async function drainResume({ actor, invokes, persistence, durable, detach }) {
|
|
335
|
+
if (await invokes.drainTimedOut()) {
|
|
336
|
+
return interruptedResult({ actor, persistence, durable, detach });
|
|
337
|
+
}
|
|
338
|
+
if (!settling(actor.getSnapshot())) {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
const timedOut = didDrainTimeOut(await raceDrain(waitFor(actor, (state) => !settling(state))));
|
|
342
|
+
return timedOut ? interruptedResult({ actor, persistence, durable, detach }) : undefined;
|
|
343
|
+
}
|
|
344
|
+
async function drainSettling(options) {
|
|
345
|
+
const { actor, persistence, durable, detach } = options;
|
|
346
|
+
const timedOut = didDrainTimeOut(await raceDrain(waitFor(actor, (state) => !settling(state))));
|
|
347
|
+
return timedOut ? interruptedResult({ actor, persistence, durable, detach }) : undefined;
|
|
348
|
+
}
|
|
349
|
+
async function interruptedResult({ actor, persistence, durable, detach }) {
|
|
350
|
+
detach();
|
|
351
|
+
await persistence.flush();
|
|
352
|
+
const snapshot = actor.getSnapshot();
|
|
353
|
+
return durable
|
|
354
|
+
? { status: 'paused', snapshot }
|
|
355
|
+
: { status: 'error', snapshot, error: tagError(new AbortedError()) };
|
|
356
|
+
}
|
|
357
|
+
async function detachIfDrained(invokes, detach) {
|
|
358
|
+
if (await invokes.drainTimedOut()) {
|
|
359
|
+
detach();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function pendingRecovery(snapshot, journal) {
|
|
363
|
+
const recovery = snapshot.context.recovery;
|
|
364
|
+
return recovery && journal.getReceipt(recovery.id)?.status !== 'result' ? recovery : undefined;
|
|
365
|
+
}
|
|
366
|
+
async function* finishPaused({ actor, invokes, persistence, durable, detach, journal, queue, runId }) {
|
|
367
|
+
await detachIfDrained(invokes, detach);
|
|
368
|
+
await persistence.flush();
|
|
369
|
+
const snapshot = actor.getSnapshot();
|
|
370
|
+
const recovery = pendingRecovery(snapshot, journal);
|
|
371
|
+
if (recovery) {
|
|
372
|
+
const event = { type: 'recovery-required', runId, operation: copy(recovery) };
|
|
373
|
+
yield event;
|
|
374
|
+
}
|
|
375
|
+
yield* drainQueue(queue);
|
|
376
|
+
return durable
|
|
377
|
+
? { status: 'paused', snapshot }
|
|
378
|
+
: { status: 'error', snapshot, error: tagError(new AbortedError()) };
|
|
379
|
+
}
|
|
380
|
+
async function shouldDetachWhileClosing({ detached, journal, actor }) {
|
|
381
|
+
if (detached || journal.failure || !settling(actor.getSnapshot())) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
return didDrainTimeOut(await raceDrain(waitFor(actor, (snapshot) => !settling(snapshot))));
|
|
385
|
+
}
|
|
386
|
+
function* replayInterruptedGeneration(runId, snapshot) {
|
|
387
|
+
const { round, lastRound } = snapshot.context;
|
|
388
|
+
const finishingRound = lastRound &&
|
|
389
|
+
(wasInActiveState(snapshot, 'answering') ||
|
|
390
|
+
wasInActiveState(snapshot, 'finishing') ||
|
|
391
|
+
(wasInActiveState(snapshot, 'resolving') && !lastRound.toolCalls.length));
|
|
392
|
+
const shouldReplay = wasInActiveState(snapshot, 'generating') ||
|
|
393
|
+
wasInActiveState(snapshot, 'wrapping') ||
|
|
394
|
+
finishingRound;
|
|
395
|
+
if (!shouldReplay) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
yield { type: 'round-reset', runId, round };
|
|
399
|
+
if (finishingRound && lastRound) {
|
|
400
|
+
yield* replayFinalRound({ runId, round, last: lastRound, response: snapshot.context.response });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function settling(snapshot) {
|
|
404
|
+
const value = snapshot.value;
|
|
405
|
+
return value === 'pausing';
|
|
406
|
+
}
|
|
407
|
+
function* drainQueue(queue) {
|
|
408
|
+
while (queue.length) {
|
|
409
|
+
const event = queue.shift();
|
|
410
|
+
if (event) {
|
|
411
|
+
yield event;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function createEventMailbox() {
|
|
416
|
+
const mailbox = {
|
|
417
|
+
queue: [],
|
|
418
|
+
notify() { },
|
|
419
|
+
emit(event) {
|
|
420
|
+
mailbox.queue.push(event);
|
|
421
|
+
mailbox.notify();
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
return mailbox;
|
|
425
|
+
}
|
|
426
|
+
function boundaryLog(options, runId) {
|
|
427
|
+
let boot = prepareEngineBoot(options.boot);
|
|
428
|
+
const log = {
|
|
429
|
+
[ENGINE_BOUNDARY_BOOT]: () => boot,
|
|
430
|
+
get length() {
|
|
431
|
+
return boot.length;
|
|
432
|
+
},
|
|
433
|
+
ready: async () => { },
|
|
434
|
+
get: async () => {
|
|
435
|
+
return null;
|
|
436
|
+
},
|
|
437
|
+
async append(value) {
|
|
438
|
+
const step = createEngineStep(boot.nextSeq, boot.length, [captureRunRecord(value)]);
|
|
439
|
+
const prepared = options.redactStep ? captureEngineStep(options.redactStep(step)) : step;
|
|
440
|
+
const next = applyEngineStep(boot, prepared);
|
|
441
|
+
const append = options.onStep?.(prepared);
|
|
442
|
+
if (append) {
|
|
443
|
+
const configured = options.hostAppendTimeoutMs;
|
|
444
|
+
const timeoutMs = typeof configured === 'number' && Number.isFinite(configured) && configured > 0
|
|
445
|
+
? Math.min(configured, 2 ** 31 - 1)
|
|
446
|
+
: HOST_APPEND_TIMEOUT_MS;
|
|
447
|
+
const acknowledged = await raceDrain(append, timeoutMs);
|
|
448
|
+
if (didDrainTimeOut(acknowledged)) {
|
|
449
|
+
try {
|
|
450
|
+
options.onDetached?.(append, runId);
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
}
|
|
454
|
+
throw new Error(`Host run-log append did not acknowledge within ${timeoutMs}ms`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
boot = next;
|
|
458
|
+
return boot;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
return log;
|
|
462
|
+
}
|
|
463
|
+
function getCurrentBoot(log, journal) {
|
|
464
|
+
const readBoot = Reflect.get(log, ENGINE_BOUNDARY_BOOT);
|
|
465
|
+
return typeof readBoot === 'function' ? readBoot.call(log) : journal.exportBoot();
|
|
466
|
+
}
|
|
467
|
+
function createOperationPort(journal) {
|
|
468
|
+
return Object.freeze({
|
|
469
|
+
flush: () => journal.flush(),
|
|
470
|
+
operation: operationPort(journal),
|
|
471
|
+
getReceipt: (id) => journal.getReceipt(id),
|
|
472
|
+
commitResultFacts: (id, facts) => journal.commitResultFacts(id, facts),
|
|
473
|
+
uncertainWrite: () => journal.uncertainWrite(),
|
|
474
|
+
settledCall: (receiptId) => journal.settledCall(receiptId),
|
|
475
|
+
pendingArgumentsWereRedacted: (snapshot) => journal.pendingArgumentsWereRedacted(snapshot)
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function operationPort(journal) {
|
|
479
|
+
return journal.operation.bind(journal);
|
|
480
|
+
}
|
|
481
|
+
function immutableBoot(boot) {
|
|
482
|
+
return prepareEngineBoot(boot);
|
|
483
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Operation } from '../run-log/protocol-types.ts';
|
|
2
|
+
export type { Operation } from '../run-log/protocol-types.ts';
|
|
3
|
+
export declare class RecoveryRequired extends Error {
|
|
4
|
+
readonly operation: Operation;
|
|
5
|
+
constructor(operation: Operation);
|
|
6
|
+
}
|
|
7
|
+
export declare class AbortedError extends Error {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare class ToolPolicyChanged extends Error {
|
|
11
|
+
constructor(tool: string);
|
|
12
|
+
}
|
|
13
|
+
export declare const EFFECT_DRAIN_TIMEOUT_MS = 5000;
|
|
14
|
+
declare const TIMED_OUT: unique symbol;
|
|
15
|
+
export declare function raceDrain<T>(promise: Promise<T>, timeoutMs?: number): Promise<T | typeof TIMED_OUT>;
|
|
16
|
+
export declare function didDrainTimeOut(value: unknown): value is typeof TIMED_OUT;
|
|
17
|
+
export interface ErrorCause {
|
|
18
|
+
message: string;
|
|
19
|
+
name: string;
|
|
20
|
+
code?: string;
|
|
21
|
+
stack?: string;
|
|
22
|
+
cause?: ErrorCause;
|
|
23
|
+
}
|
|
24
|
+
export type EngineError = {
|
|
25
|
+
tag: 'recovery';
|
|
26
|
+
message: string;
|
|
27
|
+
name: string;
|
|
28
|
+
operation: Operation;
|
|
29
|
+
stack?: string;
|
|
30
|
+
} | {
|
|
31
|
+
tag: 'aborted';
|
|
32
|
+
message: string;
|
|
33
|
+
name: string;
|
|
34
|
+
stack?: string;
|
|
35
|
+
} | {
|
|
36
|
+
tag: 'failed';
|
|
37
|
+
message: string;
|
|
38
|
+
name: string;
|
|
39
|
+
code?: string;
|
|
40
|
+
stack?: string;
|
|
41
|
+
cause?: ErrorCause;
|
|
42
|
+
};
|
|
43
|
+
export declare function tagError(error: unknown): EngineError;
|
|
44
|
+
export declare function rehydrateError(value: EngineError | ErrorCause | null | undefined, fallbackMessage?: string): Error;
|