@earendil-works/pi-agent-core 0.84.4 → 0.85.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 +4 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +9 -0
- package/dist/agent-loop.js.map +1 -1
- package/dist/harness/agent-harness.d.ts +639 -384
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +5 -251
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +12 -6
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +33 -26
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +30 -8
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +53 -63
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/config.d.ts +9 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +27 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +9 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +13 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +24 -28
- package/dist/harness/env/nodejs.d.ts.map +1 -1
- package/dist/harness/env/nodejs.js +239 -86
- package/dist/harness/env/nodejs.js.map +1 -1
- package/dist/harness/events.d.ts +20 -38
- package/dist/harness/events.d.ts.map +1 -1
- package/dist/harness/events.js +240 -55
- package/dist/harness/events.js.map +1 -1
- package/dist/harness/execution/assistant.d.ts +42 -0
- package/dist/harness/execution/assistant.d.ts.map +1 -0
- package/dist/harness/execution/assistant.js +82 -0
- package/dist/harness/execution/assistant.js.map +1 -0
- package/dist/harness/execution/effect-gate.d.ts +22 -0
- package/dist/harness/execution/effect-gate.d.ts.map +1 -0
- package/dist/harness/execution/effect-gate.js +49 -0
- package/dist/harness/execution/effect-gate.js.map +1 -0
- package/dist/harness/execution/tools.d.ts +67 -0
- package/dist/harness/execution/tools.d.ts.map +1 -0
- package/dist/harness/execution/tools.js +121 -0
- package/dist/harness/execution/tools.js.map +1 -0
- package/dist/harness/hooks.d.ts +38 -0
- package/dist/harness/hooks.d.ts.map +1 -0
- package/dist/harness/hooks.js +406 -0
- package/dist/harness/hooks.js.map +1 -0
- package/dist/harness/messages.d.ts +2 -2
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/prompt-templates.d.ts +3 -2
- package/dist/harness/prompt-templates.d.ts.map +1 -1
- package/dist/harness/prompt-templates.js +17 -17
- package/dist/harness/prompt-templates.js.map +1 -1
- package/dist/harness/result.d.ts +93 -0
- package/dist/harness/result.d.ts.map +1 -1
- package/dist/harness/result.js +40 -0
- package/dist/harness/result.js.map +1 -1
- package/dist/harness/runtime/drive/boundary.d.ts +28 -0
- package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
- package/dist/harness/runtime/drive/boundary.js +170 -0
- package/dist/harness/runtime/drive/boundary.js.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
- package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
- package/dist/harness/runtime/drive/checkpoint.js +133 -0
- package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
- package/dist/harness/runtime/drive/deferred.d.ts +15 -0
- package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
- package/dist/harness/runtime/drive/deferred.js +179 -0
- package/dist/harness/runtime/drive/deferred.js.map +1 -0
- package/dist/harness/runtime/drive/generation.d.ts +8 -0
- package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
- package/dist/harness/runtime/drive/generation.js +205 -0
- package/dist/harness/runtime/drive/generation.js.map +1 -0
- package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
- package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
- package/dist/harness/runtime/drive/reconcile.js +140 -0
- package/dist/harness/runtime/drive/reconcile.js.map +1 -0
- package/dist/harness/runtime/drive/recovery.d.ts +8 -0
- package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
- package/dist/harness/runtime/drive/recovery.js +85 -0
- package/dist/harness/runtime/drive/recovery.js.map +1 -0
- package/dist/harness/runtime/drive/response.d.ts +23 -0
- package/dist/harness/runtime/drive/response.d.ts.map +1 -0
- package/dist/harness/runtime/drive/response.js +394 -0
- package/dist/harness/runtime/drive/response.js.map +1 -0
- package/dist/harness/runtime/drive/retry.d.ts +4 -0
- package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
- package/dist/harness/runtime/drive/retry.js +37 -0
- package/dist/harness/runtime/drive/retry.js.map +1 -0
- package/dist/harness/runtime/drive/structural.d.ts +32 -0
- package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
- package/dist/harness/runtime/drive/structural.js +929 -0
- package/dist/harness/runtime/drive/structural.js.map +1 -0
- package/dist/harness/runtime/drive/terminal.d.ts +7 -0
- package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
- package/dist/harness/runtime/drive/terminal.js +49 -0
- package/dist/harness/runtime/drive/terminal.js.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
- package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tool-placement.js +244 -0
- package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
- package/dist/harness/runtime/drive/tools.d.ts +6 -0
- package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
- package/dist/harness/runtime/drive/tools.js +449 -0
- package/dist/harness/runtime/drive/tools.js.map +1 -0
- package/dist/harness/runtime/drive.d.ts +6 -0
- package/dist/harness/runtime/drive.d.ts.map +1 -0
- package/dist/harness/runtime/drive.js +93 -0
- package/dist/harness/runtime/drive.js.map +1 -0
- package/dist/harness/runtime/harness.d.ts +59 -0
- package/dist/harness/runtime/harness.d.ts.map +1 -0
- package/dist/harness/runtime/harness.js +311 -0
- package/dist/harness/runtime/harness.js.map +1 -0
- package/dist/harness/runtime/index.d.ts +2 -0
- package/dist/harness/runtime/index.d.ts.map +1 -0
- package/dist/harness/runtime/index.js +2 -0
- package/dist/harness/runtime/index.js.map +1 -0
- package/dist/harness/runtime/lane.d.ts +101 -0
- package/dist/harness/runtime/lane.d.ts.map +1 -0
- package/dist/harness/runtime/lane.js +1575 -0
- package/dist/harness/runtime/lane.js.map +1 -0
- package/dist/harness/runtime/progress.d.ts +15 -0
- package/dist/harness/runtime/progress.d.ts.map +1 -0
- package/dist/harness/runtime/progress.js +66 -0
- package/dist/harness/runtime/progress.js.map +1 -0
- package/dist/harness/runtime/reducer.d.ts +5 -0
- package/dist/harness/runtime/reducer.d.ts.map +1 -0
- package/dist/harness/runtime/reducer.js +240 -0
- package/dist/harness/runtime/reducer.js.map +1 -0
- package/dist/harness/runtime/restore.d.ts +24 -0
- package/dist/harness/runtime/restore.d.ts.map +1 -0
- package/dist/harness/runtime/restore.js +117 -0
- package/dist/harness/runtime/restore.js.map +1 -0
- package/dist/harness/runtime/transcript.d.ts +21 -0
- package/dist/harness/runtime/transcript.d.ts.map +1 -0
- package/dist/harness/runtime/transcript.js +72 -0
- package/dist/harness/runtime/transcript.js.map +1 -0
- package/dist/harness/runtime/types.d.ts +105 -0
- package/dist/harness/runtime/types.d.ts.map +1 -0
- package/dist/harness/runtime/types.js +61 -0
- package/dist/harness/runtime/types.js.map +1 -0
- package/dist/harness/session/commit.d.ts +53 -0
- package/dist/harness/session/commit.d.ts.map +1 -0
- package/dist/harness/session/commit.js +57 -0
- package/dist/harness/session/commit.js.map +1 -0
- package/dist/harness/session/context.d.ts +6 -18
- package/dist/harness/session/context.d.ts.map +1 -1
- package/dist/harness/session/context.js +31 -49
- package/dist/harness/session/context.js.map +1 -1
- package/dist/harness/session/fork-policy.d.ts +13 -0
- package/dist/harness/session/fork-policy.d.ts.map +1 -0
- package/dist/harness/session/fork-policy.js +22 -0
- package/dist/harness/session/fork-policy.js.map +1 -0
- package/dist/harness/session/fork.d.ts +18 -0
- package/dist/harness/session/fork.d.ts.map +1 -0
- package/dist/harness/session/fork.js +127 -0
- package/dist/harness/session/fork.js.map +1 -0
- package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
- package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
- package/dist/harness/session/in-memory-storage-state.js +216 -0
- package/dist/harness/session/in-memory-storage-state.js.map +1 -0
- package/dist/harness/session/index.d.ts +9 -5
- package/dist/harness/session/index.d.ts.map +1 -1
- package/dist/harness/session/index.js +7 -4
- package/dist/harness/session/index.js.map +1 -1
- package/dist/harness/session/jsonl/codec.d.ts +19 -8
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/dist/harness/session/jsonl/codec.js +34 -218
- package/dist/harness/session/jsonl/codec.js.map +1 -1
- package/dist/harness/session/jsonl/index.d.ts +4 -0
- package/dist/harness/session/jsonl/index.d.ts.map +1 -0
- package/dist/harness/session/jsonl/index.js +4 -0
- package/dist/harness/session/jsonl/index.js.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts +18 -0
- package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
- package/dist/harness/session/jsonl/legacy-v3.js +383 -0
- package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +26 -19
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
- package/dist/harness/session/jsonl/repo.js +266 -156
- package/dist/harness/session/jsonl/repo.js.map +1 -1
- package/dist/harness/session/jsonl/storage.d.ts +39 -41
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
- package/dist/harness/session/jsonl/storage.js +238 -181
- package/dist/harness/session/jsonl/storage.js.map +1 -1
- package/dist/harness/session/jsonl/types.d.ts +23 -22
- package/dist/harness/session/jsonl/types.d.ts.map +1 -1
- package/dist/harness/session/jsonl/types.js +2 -1
- package/dist/harness/session/jsonl/types.js.map +1 -1
- package/dist/harness/session/memory.d.ts +46 -40
- package/dist/harness/session/memory.d.ts.map +1 -1
- package/dist/harness/session/memory.js +346 -113
- package/dist/harness/session/memory.js.map +1 -1
- package/dist/harness/session/mutation-line.d.ts +8 -0
- package/dist/harness/session/mutation-line.d.ts.map +1 -0
- package/dist/harness/session/mutation-line.js +21 -0
- package/dist/harness/session/mutation-line.js.map +1 -0
- package/dist/harness/session/session.d.ts +78 -46
- package/dist/harness/session/session.d.ts.map +1 -1
- package/dist/harness/session/session.js +343 -209
- package/dist/harness/session/session.js.map +1 -1
- package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
- package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/datasets.js +24 -0
- package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
- package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
- package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
- package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/benchmark/storage.js +114 -0
- package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts +21 -0
- package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/session-repo.js +590 -0
- package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
- package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
- package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance/storage.js +600 -0
- package/dist/harness/session/testing/conformance/storage.js.map +1 -0
- package/dist/harness/session/testing/gating-storage.d.ts +26 -0
- package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/gating-storage.js +100 -0
- package/dist/harness/session/testing/gating-storage.js.map +1 -0
- package/dist/harness/session/testing/index.d.ts +9 -2
- package/dist/harness/session/testing/index.d.ts.map +1 -1
- package/dist/harness/session/testing/index.js +8 -1
- package/dist/harness/session/testing/index.js.map +1 -1
- package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
- package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
- package/dist/harness/session/testing/instrumented-storage.js +16 -0
- package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
- package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
- package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
- package/dist/harness/session/testing/storage-decorator.js +41 -0
- package/dist/harness/session/testing/storage-decorator.js.map +1 -0
- package/dist/harness/session/testing/types.d.ts +5 -7
- package/dist/harness/session/testing/types.d.ts.map +1 -1
- package/dist/harness/session/testing/types.js.map +1 -1
- package/dist/harness/session/types.d.ts +424 -259
- package/dist/harness/session/types.d.ts.map +1 -1
- package/dist/harness/session/types.js +7 -8
- package/dist/harness/session/types.js.map +1 -1
- package/dist/harness/session/values.d.ts +95 -0
- package/dist/harness/session/values.d.ts.map +1 -0
- package/dist/harness/session/values.js +81 -0
- package/dist/harness/session/values.js.map +1 -0
- package/dist/harness/skills.d.ts +3 -2
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +29 -26
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/telemetry.d.ts +64 -43
- package/dist/harness/telemetry.d.ts.map +1 -1
- package/dist/harness/telemetry.js +45 -26
- package/dist/harness/telemetry.js.map +1 -1
- package/dist/harness/tools/bash.d.ts +4 -4
- package/dist/harness/tools/bash.d.ts.map +1 -1
- package/dist/harness/tools/bash.js +66 -93
- package/dist/harness/tools/bash.js.map +1 -1
- package/dist/harness/tools/edit.d.ts.map +1 -1
- package/dist/harness/tools/edit.js +10 -10
- package/dist/harness/tools/edit.js.map +1 -1
- package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
- package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/harness/tools/file-mutation-queue.js +5 -5
- package/dist/harness/tools/file-mutation-queue.js.map +1 -1
- package/dist/harness/tools/path-utils.d.ts +3 -2
- package/dist/harness/tools/path-utils.d.ts.map +1 -1
- package/dist/harness/tools/path-utils.js +5 -5
- package/dist/harness/tools/path-utils.js.map +1 -1
- package/dist/harness/tools/read.d.ts +2 -1
- package/dist/harness/tools/read.d.ts.map +1 -1
- package/dist/harness/tools/read.js +4 -6
- package/dist/harness/tools/read.js.map +1 -1
- package/dist/harness/tools/write.d.ts.map +1 -1
- package/dist/harness/tools/write.js +7 -7
- package/dist/harness/tools/write.js.map +1 -1
- package/dist/harness/types.d.ts +108 -45
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
- package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/harness/utils/adaptive-publisher.js +79 -0
- package/dist/harness/utils/adaptive-publisher.js.map +1 -0
- package/dist/harness/utils/output-capture.d.ts +31 -0
- package/dist/harness/utils/output-capture.d.ts.map +1 -0
- package/dist/harness/utils/output-capture.js +217 -0
- package/dist/harness/utils/output-capture.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +12 -5
- package/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/dist/harness/utils/shell-output.js +64 -174
- package/dist/harness/utils/shell-output.js.map +1 -1
- package/dist/harness/utils/truncate.d.ts +1 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -1
- package/dist/harness/utils/truncate.js +1 -1
- package/dist/harness/utils/truncate.js.map +1 -1
- package/dist/harness/utils/usage.d.ts +4 -0
- package/dist/harness/utils/usage.d.ts.map +1 -0
- package/dist/harness/utils/usage.js +33 -0
- package/dist/harness/utils/usage.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -6
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +2 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +40 -10
- package/dist/proxy.js.map +1 -1
- package/dist/search/index.d.ts +24 -16
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +1 -1
- package/dist/search/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +25 -4
- package/dist/harness/reducer.d.ts +0 -100
- package/dist/harness/reducer.d.ts.map +0 -1
- package/dist/harness/reducer.js +0 -415
- package/dist/harness/reducer.js.map +0 -1
- package/dist/harness/session/jsonl/errors.d.ts +0 -9
- package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
- package/dist/harness/session/jsonl/errors.js +0 -19
- package/dist/harness/session/jsonl/errors.js.map +0 -1
- package/dist/harness/session/jsonl.d.ts +0 -3
- package/dist/harness/session/jsonl.d.ts.map +0 -1
- package/dist/harness/session/jsonl.js +0 -2
- package/dist/harness/session/jsonl.js.map +0 -1
- package/dist/harness/session/state.d.ts +0 -65
- package/dist/harness/session/state.d.ts.map +0 -1
- package/dist/harness/session/state.js +0 -319
- package/dist/harness/session/state.js.map +0 -1
- package/dist/harness/session/testing/conformance.d.ts +0 -4
- package/dist/harness/session/testing/conformance.d.ts.map +0 -1
- package/dist/harness/session/testing/conformance.js +0 -762
- package/dist/harness/session/testing/conformance.js.map +0 -1
- package/dist/search/scanning.d.ts +0 -29
- package/dist/search/scanning.d.ts.map +0 -1
- package/dist/search/scanning.js +0 -103
- package/dist/search/scanning.js.map +0 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { insertEntry } from "../../session/commit.js";
|
|
2
|
+
import { SessionInvariantError } from "../../session/session.js";
|
|
3
|
+
import { branchTip, deleteValue, pendingEntry, setValue } from "../../session/values.js";
|
|
4
|
+
import { committedEntryEvents, entryLifecycleEvents, readBoundedContext, readLaneQueues } from "../transcript.js";
|
|
5
|
+
import { operationCleanupWrites, operationResultRecord } from "./terminal.js";
|
|
6
|
+
export function normalizedRetryPolicy(lane) {
|
|
7
|
+
const retry = lane.readConfig().retryPolicy;
|
|
8
|
+
return retry.enabled
|
|
9
|
+
? { maxAttempts: retry.maxRetries + 1, baseDelayMs: retry.baseDelayMs }
|
|
10
|
+
: { maxAttempts: 1, baseDelayMs: retry.baseDelayMs };
|
|
11
|
+
}
|
|
12
|
+
export function assistantReadyAtBoundary(lane, state, scope, triggerEntryId, overflowRecoveryUsed) {
|
|
13
|
+
const config = lane.readConfig();
|
|
14
|
+
return {
|
|
15
|
+
...scope,
|
|
16
|
+
at: "assistant.ready",
|
|
17
|
+
generationContext: {
|
|
18
|
+
stepId: lane.session.idGenerator.next(),
|
|
19
|
+
triggerEntryId,
|
|
20
|
+
configuration: state.configuration,
|
|
21
|
+
streamOptions: config.streamOptions,
|
|
22
|
+
retryPolicy: normalizedRetryPolicy(lane),
|
|
23
|
+
overflowRecoveryUsed,
|
|
24
|
+
},
|
|
25
|
+
nextAttempt: 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Select and materialize one boundary's lane-owned input without committing it. */
|
|
29
|
+
export async function planBoundaryInbox(lane, drive, state, scope, reader, tipId, followUpWhenNoTrigger) {
|
|
30
|
+
const steer = state.inbox.filter((item) => item.kind === "steer");
|
|
31
|
+
const selectedSteer = scope.settings.steeringMode === "all" ? steer : steer.slice(0, 1);
|
|
32
|
+
let selected = state.inbox.filter((item) => item.kind === "write" || selectedSteer.some((candidate) => candidate.entryId === item.entryId));
|
|
33
|
+
const load = (items) => Promise.all(items.map(async (item) => {
|
|
34
|
+
const stored = await reader.getValue(pendingEntry(item.entryId), drive.context);
|
|
35
|
+
if (stored === undefined) {
|
|
36
|
+
throw new SessionInvariantError(`Pending ${item.kind} entry ${item.entryId} is missing its payload`);
|
|
37
|
+
}
|
|
38
|
+
if (item.kind !== "write" && stored.value.type !== "message") {
|
|
39
|
+
throw new SessionInvariantError(`Queued ${item.kind} entry ${item.entryId} is not a message`);
|
|
40
|
+
}
|
|
41
|
+
return { item, pending: stored.value };
|
|
42
|
+
}));
|
|
43
|
+
let pending = await load(selected);
|
|
44
|
+
const projects = (value) => value.type === "message" || lane.readConfig().entryProjectors[value.customType] !== undefined;
|
|
45
|
+
if (followUpWhenNoTrigger && !pending.some(({ pending: value }) => projects(value))) {
|
|
46
|
+
const followUp = state.inbox.filter((item) => item.kind === "followUp");
|
|
47
|
+
const selectedFollowUp = scope.settings.followUpMode === "all" ? followUp : followUp.slice(0, 1);
|
|
48
|
+
selected = [...selected, ...selectedFollowUp].sort((a, b) => state.inbox.indexOf(a) - state.inbox.indexOf(b));
|
|
49
|
+
pending = await load(selected);
|
|
50
|
+
}
|
|
51
|
+
let parentId = tipId;
|
|
52
|
+
let triggerEntryId;
|
|
53
|
+
const entries = pending.map(({ item, pending: value }) => {
|
|
54
|
+
const entry = value.type === "message"
|
|
55
|
+
? { id: item.entryId, parentId, type: "message", message: value.payload }
|
|
56
|
+
: {
|
|
57
|
+
id: item.entryId,
|
|
58
|
+
parentId,
|
|
59
|
+
type: "custom",
|
|
60
|
+
customType: value.customType,
|
|
61
|
+
...(value.payload === undefined ? {} : { data: value.payload }),
|
|
62
|
+
};
|
|
63
|
+
parentId = item.entryId;
|
|
64
|
+
if (projects(value))
|
|
65
|
+
triggerEntryId = item.entryId;
|
|
66
|
+
return entry;
|
|
67
|
+
});
|
|
68
|
+
const selectedIds = new Set(selected.map((item) => item.entryId));
|
|
69
|
+
const inbox = state.inbox.filter((item) => !selectedIds.has(item.entryId));
|
|
70
|
+
const queues = selected.length === 0 ? undefined : await readLaneQueues(reader, inbox, drive.context);
|
|
71
|
+
return {
|
|
72
|
+
entries,
|
|
73
|
+
writes: [
|
|
74
|
+
...entries.map((entry) => insertEntry(entry)),
|
|
75
|
+
...selected.map((item) => deleteValue(pendingEntry(item.entryId))),
|
|
76
|
+
...(entries.length === 0 ? [] : [setValue(branchTip(lane.name), parentId)]),
|
|
77
|
+
],
|
|
78
|
+
tipId: parentId,
|
|
79
|
+
inbox,
|
|
80
|
+
...(triggerEntryId === undefined ? {} : { triggerEntryId }),
|
|
81
|
+
...(queues === undefined ? {} : { queues }),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function boundaryPlacementEvents(placement, commit, firstWriteIndex, lane, runId) {
|
|
85
|
+
return [
|
|
86
|
+
...committedEntryEvents(placement.entries, commit, lane, runId, firstWriteIndex),
|
|
87
|
+
...(placement.queues === undefined ? [] : [{ type: "queue_update", lane, queues: placement.queues }]),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
/** Replan after before_run_end and commit either renewed work or the terminal run result. */
|
|
91
|
+
export async function finishRunBoundary(lane, drive, capability, continuation, plannedEntryIds, pendingEvents = []) {
|
|
92
|
+
const context = await readBoundedContext(lane, drive, capability);
|
|
93
|
+
if (context.kind === "cancel_requested")
|
|
94
|
+
return { kind: "continue" };
|
|
95
|
+
const hook = await lane.hooks.runWithGate("before_run_end", { lane: lane.name, runId: drive.operationId, messages: context.value }, drive.gate, drive.context);
|
|
96
|
+
const followUp = hook?.followUp === undefined
|
|
97
|
+
? undefined
|
|
98
|
+
: {
|
|
99
|
+
id: lane.session.idGenerator.next(),
|
|
100
|
+
message: { role: "user", content: hook.followUp, timestamp: Date.now() },
|
|
101
|
+
};
|
|
102
|
+
const result = await lane.continueOperation(capability, async (state, current, meta, reader) => {
|
|
103
|
+
const placement = await planBoundaryInbox(lane, drive, state, current, reader, state.tipId, true);
|
|
104
|
+
if (placement.triggerEntryId !== undefined) {
|
|
105
|
+
return {
|
|
106
|
+
kind: "commit",
|
|
107
|
+
writes: placement.writes,
|
|
108
|
+
operationState: assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId, false),
|
|
109
|
+
lane: { tipId: placement.tipId, inbox: placement.inbox },
|
|
110
|
+
materialize: () => ({ kind: "continue" }),
|
|
111
|
+
events: (commit) => [
|
|
112
|
+
...pendingEvents,
|
|
113
|
+
...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const hookPlanIsCurrent = placement.entries.length === plannedEntryIds.length &&
|
|
118
|
+
placement.entries.every((entry, index) => entry.id === plannedEntryIds[index]);
|
|
119
|
+
if (hookPlanIsCurrent && followUp !== undefined) {
|
|
120
|
+
const entry = {
|
|
121
|
+
id: followUp.id,
|
|
122
|
+
parentId: placement.tipId,
|
|
123
|
+
type: "message",
|
|
124
|
+
message: followUp.message,
|
|
125
|
+
};
|
|
126
|
+
const entryWriteIndex = placement.writes.length;
|
|
127
|
+
return {
|
|
128
|
+
kind: "commit",
|
|
129
|
+
writes: [...placement.writes, insertEntry(entry), setValue(branchTip(lane.name), followUp.id)],
|
|
130
|
+
operationState: assistantReadyAtBoundary(lane, state, current, followUp.id, false),
|
|
131
|
+
lane: { tipId: followUp.id, inbox: placement.inbox },
|
|
132
|
+
materialize: () => ({ kind: "continue" }),
|
|
133
|
+
events: (commit) => [
|
|
134
|
+
...pendingEvents,
|
|
135
|
+
...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
136
|
+
...entryLifecycleEvents({ ...entry, seq: commit.seqs[entryWriteIndex], timestamp: commit.timestamp }, lane.name, drive.operationId),
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (placement.tipId === null)
|
|
141
|
+
throw new SessionInvariantError("Completed run has no tip");
|
|
142
|
+
if (continuation.includeFinalAssistant && current.latestAssistantEntryId === null) {
|
|
143
|
+
throw new SessionInvariantError("Completed run is missing its final assistant");
|
|
144
|
+
}
|
|
145
|
+
const record = operationResultRecord(meta, "completed", placement.tipId);
|
|
146
|
+
const cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);
|
|
147
|
+
return {
|
|
148
|
+
kind: "finish",
|
|
149
|
+
writes: [...placement.writes, ...cleanup],
|
|
150
|
+
record,
|
|
151
|
+
lane: { tipId: placement.tipId, inbox: placement.inbox },
|
|
152
|
+
materialize: () => ({ kind: "settled", outcome: record }),
|
|
153
|
+
events: (commit) => [
|
|
154
|
+
...pendingEvents,
|
|
155
|
+
...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
156
|
+
{
|
|
157
|
+
type: "run_end",
|
|
158
|
+
lane: lane.name,
|
|
159
|
+
runId: drive.operationId,
|
|
160
|
+
status: "completed",
|
|
161
|
+
fromTipId: meta.sourceTipId,
|
|
162
|
+
tipId: placement.tipId,
|
|
163
|
+
endedAt: record.endedAt,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}, drive.context);
|
|
168
|
+
return result.kind === "cancel_requested" ? { kind: "continue" } : result.value;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../../../src/harness/runtime/drive/boundary.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessEvent, LaneQueuedItem } from \"../../agent-harness.ts\";\nimport { insertEntry } from \"../../session/commit.ts\";\nimport { SessionInvariantError } from \"../../session/session.ts\";\nimport type {\n\tAssistantReadyOperation,\n\tCheckpointOperation,\n\tCommitResult,\n\tNewEntry,\n\tNormalizedRetryPolicy,\n\tOperationScope,\n\tSessionReader,\n\tSummaryDecidingOperation,\n\tSummaryEffectPendingOperation,\n\tSummaryReadyOperation,\n\tWrite,\n} from \"../../session/types.ts\";\nimport { branchTip, deleteValue, pendingEntry, setValue } from \"../../session/values.ts\";\nimport type { Lane } from \"../lane.ts\";\nimport { committedEntryEvents, entryLifecycleEvents, readBoundedContext, readLaneQueues } from \"../transcript.ts\";\nimport type { Drive, LaneState, ProcedureResult } from \"../types.ts\";\nimport { operationCleanupWrites, operationResultRecord } from \"./terminal.ts\";\n\nexport interface BoundaryFinishPending {\n\tkind: \"finish_pending\";\n\tentryIds: string[];\n}\n\nexport interface BoundaryPlacement {\n\tentries: NewEntry[];\n\twrites: Write[];\n\ttipId: string | null;\n\tinbox: LaneState[\"inbox\"];\n\ttriggerEntryId?: string;\n\tqueues?: LaneQueuedItem[];\n}\n\ntype FinishBoundaryOperation =\n\t| CheckpointOperation\n\t| SummaryDecidingOperation\n\t| SummaryReadyOperation\n\t| SummaryEffectPendingOperation;\n\nexport function normalizedRetryPolicy<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n): NormalizedRetryPolicy {\n\tconst retry = lane.readConfig().retryPolicy;\n\treturn retry.enabled\n\t\t? { maxAttempts: retry.maxRetries + 1, baseDelayMs: retry.baseDelayMs }\n\t\t: { maxAttempts: 1, baseDelayMs: retry.baseDelayMs };\n}\n\nexport function assistantReadyAtBoundary<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tstate: LaneState,\n\tscope: OperationScope,\n\ttriggerEntryId: string,\n\toverflowRecoveryUsed: boolean,\n): AssistantReadyOperation {\n\tconst config = lane.readConfig();\n\treturn {\n\t\t...scope,\n\t\tat: \"assistant.ready\",\n\t\tgenerationContext: {\n\t\t\tstepId: lane.session.idGenerator.next(),\n\t\t\ttriggerEntryId,\n\t\t\tconfiguration: state.configuration,\n\t\t\tstreamOptions: config.streamOptions,\n\t\t\tretryPolicy: normalizedRetryPolicy(lane),\n\t\t\toverflowRecoveryUsed,\n\t\t},\n\t\tnextAttempt: 1,\n\t};\n}\n\n/** Select and materialize one boundary's lane-owned input without committing it. */\nexport async function planBoundaryInbox<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tstate: LaneState,\n\tscope: OperationScope,\n\treader: SessionReader,\n\ttipId: string | null,\n\tfollowUpWhenNoTrigger: boolean,\n): Promise<BoundaryPlacement> {\n\tconst steer = state.inbox.filter((item) => item.kind === \"steer\");\n\tconst selectedSteer = scope.settings.steeringMode === \"all\" ? steer : steer.slice(0, 1);\n\tlet selected = state.inbox.filter(\n\t\t(item) => item.kind === \"write\" || selectedSteer.some((candidate) => candidate.entryId === item.entryId),\n\t);\n\tconst load = (items: typeof selected) =>\n\t\tPromise.all(\n\t\t\titems.map(async (item) => {\n\t\t\t\tconst stored = await reader.getValue(pendingEntry(item.entryId), drive.context);\n\t\t\t\tif (stored === undefined) {\n\t\t\t\t\tthrow new SessionInvariantError(`Pending ${item.kind} entry ${item.entryId} is missing its payload`);\n\t\t\t\t}\n\t\t\t\tif (item.kind !== \"write\" && stored.value.type !== \"message\") {\n\t\t\t\t\tthrow new SessionInvariantError(`Queued ${item.kind} entry ${item.entryId} is not a message`);\n\t\t\t\t}\n\t\t\t\treturn { item, pending: stored.value };\n\t\t\t}),\n\t\t);\n\tlet pending = await load(selected);\n\tconst projects = (value: (typeof pending)[number][\"pending\"]): boolean =>\n\t\tvalue.type === \"message\" || lane.readConfig().entryProjectors[value.customType] !== undefined;\n\tif (followUpWhenNoTrigger && !pending.some(({ pending: value }) => projects(value))) {\n\t\tconst followUp = state.inbox.filter((item) => item.kind === \"followUp\");\n\t\tconst selectedFollowUp = scope.settings.followUpMode === \"all\" ? followUp : followUp.slice(0, 1);\n\t\tselected = [...selected, ...selectedFollowUp].sort((a, b) => state.inbox.indexOf(a) - state.inbox.indexOf(b));\n\t\tpending = await load(selected);\n\t}\n\n\tlet parentId = tipId;\n\tlet triggerEntryId: string | undefined;\n\tconst entries: NewEntry[] = pending.map(({ item, pending: value }) => {\n\t\tconst entry: NewEntry =\n\t\t\tvalue.type === \"message\"\n\t\t\t\t? { id: item.entryId, parentId, type: \"message\", message: value.payload }\n\t\t\t\t: {\n\t\t\t\t\t\tid: item.entryId,\n\t\t\t\t\t\tparentId,\n\t\t\t\t\t\ttype: \"custom\",\n\t\t\t\t\t\tcustomType: value.customType,\n\t\t\t\t\t\t...(value.payload === undefined ? {} : { data: value.payload }),\n\t\t\t\t\t};\n\t\tparentId = item.entryId;\n\t\tif (projects(value)) triggerEntryId = item.entryId;\n\t\treturn entry;\n\t});\n\tconst selectedIds = new Set(selected.map((item) => item.entryId));\n\tconst inbox = state.inbox.filter((item) => !selectedIds.has(item.entryId));\n\tconst queues = selected.length === 0 ? undefined : await readLaneQueues(reader, inbox, drive.context);\n\treturn {\n\t\tentries,\n\t\twrites: [\n\t\t\t...entries.map((entry) => insertEntry(entry)),\n\t\t\t...selected.map((item) => deleteValue(pendingEntry(item.entryId))),\n\t\t\t...(entries.length === 0 ? [] : [setValue(branchTip(lane.name), parentId)]),\n\t\t],\n\t\ttipId: parentId,\n\t\tinbox,\n\t\t...(triggerEntryId === undefined ? {} : { triggerEntryId }),\n\t\t...(queues === undefined ? {} : { queues }),\n\t};\n}\n\nexport function boundaryPlacementEvents(\n\tplacement: BoundaryPlacement,\n\tcommit: CommitResult,\n\tfirstWriteIndex: number,\n\tlane: string,\n\trunId: string,\n): HarnessEvent[] {\n\treturn [\n\t\t...committedEntryEvents(placement.entries, commit, lane, runId, firstWriteIndex),\n\t\t...(placement.queues === undefined ? [] : [{ type: \"queue_update\" as const, lane, queues: placement.queues }]),\n\t];\n}\n\n/** Replan after before_run_end and commit either renewed work or the terminal run result. */\nexport async function finishRunBoundary<TContext extends object | undefined, TState extends FinishBoundaryOperation>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tcapability: TState,\n\tcontinuation: Extract<CheckpointOperation[\"continuation\"], { kind: \"may_finish\" }>,\n\tplannedEntryIds: readonly string[],\n\tpendingEvents: HarnessEvent[] = [],\n): Promise<ProcedureResult> {\n\tconst context = await readBoundedContext(lane, drive, capability);\n\tif (context.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tconst hook = await lane.hooks.runWithGate(\n\t\t\"before_run_end\",\n\t\t{ lane: lane.name, runId: drive.operationId, messages: context.value },\n\t\tdrive.gate,\n\t\tdrive.context,\n\t);\n\tconst followUp =\n\t\thook?.followUp === undefined\n\t\t\t? undefined\n\t\t\t: {\n\t\t\t\t\tid: lane.session.idGenerator.next(),\n\t\t\t\t\tmessage: { role: \"user\" as const, content: hook.followUp, timestamp: Date.now() },\n\t\t\t\t};\n\tconst result = await lane.continueOperation<TState, ProcedureResult>(\n\t\tcapability,\n\t\tasync (state, current, meta, reader) => {\n\t\t\tconst placement = await planBoundaryInbox(lane, drive, state, current, reader, state.tipId, true);\n\t\t\tif (placement.triggerEntryId !== undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: placement.writes,\n\t\t\t\t\toperationState: assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId, false),\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => [\n\t\t\t\t\t\t...pendingEvents,\n\t\t\t\t\t\t...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst hookPlanIsCurrent =\n\t\t\t\tplacement.entries.length === plannedEntryIds.length &&\n\t\t\t\tplacement.entries.every((entry, index) => entry.id === plannedEntryIds[index]);\n\t\t\tif (hookPlanIsCurrent && followUp !== undefined) {\n\t\t\t\tconst entry: NewEntry = {\n\t\t\t\t\tid: followUp.id,\n\t\t\t\t\tparentId: placement.tipId,\n\t\t\t\t\ttype: \"message\",\n\t\t\t\t\tmessage: followUp.message,\n\t\t\t\t};\n\t\t\t\tconst entryWriteIndex = placement.writes.length;\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: [...placement.writes, insertEntry(entry), setValue(branchTip(lane.name), followUp.id)],\n\t\t\t\t\toperationState: assistantReadyAtBoundary(lane, state, current, followUp.id, false),\n\t\t\t\t\tlane: { tipId: followUp.id, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => [\n\t\t\t\t\t\t...pendingEvents,\n\t\t\t\t\t\t...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t\t\t...entryLifecycleEvents(\n\t\t\t\t\t\t\t{ ...entry, seq: commit.seqs[entryWriteIndex]!, timestamp: commit.timestamp },\n\t\t\t\t\t\t\tlane.name,\n\t\t\t\t\t\t\tdrive.operationId,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (placement.tipId === null) throw new SessionInvariantError(\"Completed run has no tip\");\n\t\t\tif (continuation.includeFinalAssistant && current.latestAssistantEntryId === null) {\n\t\t\t\tthrow new SessionInvariantError(\"Completed run is missing its final assistant\");\n\t\t\t}\n\t\t\tconst record = operationResultRecord(meta, \"completed\", placement.tipId);\n\t\t\tconst cleanup = await operationCleanupWrites(reader, drive.operationId, current, drive.context);\n\t\t\treturn {\n\t\t\t\tkind: \"finish\",\n\t\t\t\twrites: [...placement.writes, ...cleanup],\n\t\t\t\trecord,\n\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\tmaterialize: () => ({ kind: \"settled\", outcome: record }) as const,\n\t\t\t\tevents: (commit) => [\n\t\t\t\t\t...pendingEvents,\n\t\t\t\t\t...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"run_end\",\n\t\t\t\t\t\tlane: lane.name,\n\t\t\t\t\t\trunId: drive.operationId,\n\t\t\t\t\t\tstatus: \"completed\",\n\t\t\t\t\t\tfromTipId: meta.sourceTipId,\n\t\t\t\t\t\ttipId: placement.tipId,\n\t\t\t\t\t\tendedAt: record.endedAt,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n\treturn result.kind === \"cancel_requested\" ? { kind: \"continue\" } : result.value;\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CheckpointOperation, type StartingOperation } from "../../session/types.ts";
|
|
2
|
+
import type { Lane } from "../lane.ts";
|
|
3
|
+
import type { Drive, ProcedureResult } from "../types.ts";
|
|
4
|
+
/** Consume before_run and commit the initial checkpoint. */
|
|
5
|
+
export declare function startRun<TContext extends object | undefined>(lane: Lane<TContext>, drive: Drive, run: StartingOperation): Promise<ProcedureResult>;
|
|
6
|
+
/** Advance one durable run boundary with at most one commit. */
|
|
7
|
+
export declare function runCheckpoint<TContext extends object | undefined>(lane: Lane<TContext>, drive: Drive, run: CheckpointOperation): Promise<ProcedureResult>;
|
|
8
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../../../src/harness/runtime/drive/checkpoint.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,KAAK,mBAAmB,EAIxB,KAAK,iBAAiB,EAEtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU1D,4DAA4D;AAC5D,wBAAsB,QAAQ,CAAC,QAAQ,SAAS,MAAM,GAAG,SAAS,EACjE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EACpB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,iBAAiB,GACpB,OAAO,CAAC,eAAe,CAAC,CA+D1B;AAED,gEAAgE;AAChE,wBAAsB,aAAa,CAAC,QAAQ,SAAS,MAAM,GAAG,SAAS,EACtE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EACpB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,mBAAmB,GACtB,OAAO,CAAC,eAAe,CAAC,CA2F1B","sourcesContent":["import { insertEntry } from \"../../session/commit.ts\";\nimport { SessionInvariantError } from \"../../session/session.ts\";\nimport {\n\ttype CheckpointOperation,\n\ttype MessageEntry,\n\ttype NewEntry,\n\toperationScopeOf,\n\ttype StartingOperation,\n\ttype SummaryDecidingOperation,\n} from \"../../session/types.ts\";\nimport { branchTip, operationPreparation, setValue } from \"../../session/values.ts\";\nimport type { Lane } from \"../lane.ts\";\nimport { chainEntries, committedEntryEvents } from \"../transcript.ts\";\nimport type { Drive, ProcedureResult } from \"../types.ts\";\nimport {\n\tassistantReadyAtBoundary,\n\ttype BoundaryFinishPending,\n\tboundaryPlacementEvents,\n\tfinishRunBoundary,\n\tplanBoundaryInbox,\n} from \"./boundary.ts\";\nimport { prepareCompactionThreshold } from \"./structural.ts\";\n\n/** Consume before_run and commit the initial checkpoint. */\nexport async function startRun<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\trun: StartingOperation,\n): Promise<ProcedureResult> {\n\tconst prompt = await lane.continueOperation(\n\t\trun,\n\t\tasync (_state, _current, meta, reader) => {\n\t\t\tif (meta.intent.kind !== \"run\") throw new SessionInvariantError(\"Run operation has non-run intent\");\n\t\t\tconst entries = await reader.getEntries(meta.intent.promptEntryIds, drive.context);\n\t\t\tconst messages = meta.intent.promptEntryIds.map((id) => {\n\t\t\t\tconst entry = entries.get(id);\n\t\t\t\tif (entry?.type !== \"message\") {\n\t\t\t\t\tthrow new SessionInvariantError(`Run prompt entry ${id} is missing its message`);\n\t\t\t\t}\n\t\t\t\treturn entry.message;\n\t\t\t});\n\t\t\treturn { kind: \"return\", result: messages };\n\t\t},\n\t\tdrive.context,\n\t);\n\tif (prompt.kind === \"cancel_requested\") return { kind: \"continue\" };\n\n\tconst hook = await lane.hooks.runWithGate(\n\t\t\"before_run\",\n\t\t{ lane: lane.name, runId: drive.operationId, prompt: prompt.value, resources: lane.readConfig().resources },\n\t\tdrive.gate,\n\t\tdrive.context,\n\t);\n\tconst injected = hook?.messages ?? [];\n\tfor (const message of injected) {\n\t\tif (message.role === \"assistant\" && message.stopReason === \"pending\") {\n\t\t\tthrow new SessionInvariantError(\"before_run returned a pending assistant message\");\n\t\t}\n\t}\n\tconst reserved = injected.map((message) => ({ id: lane.session.idGenerator.next(), message }));\n\n\tconst result = await lane.continueOperation(\n\t\trun,\n\t\t(state, current) => {\n\t\t\tconst entries: NewEntry<MessageEntry>[] = chainEntries(\n\t\t\t\tstate.tipId,\n\t\t\t\treserved.map(({ id, message }) => ({ id, type: \"message\" as const, message })),\n\t\t\t);\n\t\t\tconst triggerEntryId = entries.at(-1)?.id ?? state.tipId;\n\t\t\tif (triggerEntryId === null) throw new SessionInvariantError(\"Run start has no trigger entry\");\n\t\t\tconst nextState: CheckpointOperation = {\n\t\t\t\t...operationScopeOf(current),\n\t\t\t\tat: \"checkpoint\",\n\t\t\t\tcontinuation: { kind: \"need_assistant\", overflowRecoveryUsed: false },\n\t\t\t\ttriggerEntryId,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tkind: \"commit\",\n\t\t\t\twrites: [\n\t\t\t\t\t...entries.map((entry) => insertEntry(entry)),\n\t\t\t\t\t...(entries.length === 0 ? [] : [setValue(branchTip(lane.name), triggerEntryId)]),\n\t\t\t\t],\n\t\t\t\toperationState: nextState,\n\t\t\t\tlane: { tipId: triggerEntryId },\n\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\tevents: (commit) => committedEntryEvents(entries, commit, lane.name, drive.operationId),\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n\treturn result.kind === \"cancel_requested\" ? { kind: \"continue\" } : result.value;\n}\n\n/** Advance one durable run boundary with at most one commit. */\nexport async function runCheckpoint<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\trun: CheckpointOperation,\n): Promise<ProcedureResult> {\n\tconst threshold = await prepareCompactionThreshold(lane, drive, run);\n\tif (threshold.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tconst planned = await lane.continueOperation<CheckpointOperation, ProcedureResult | BoundaryFinishPending>(\n\t\trun,\n\t\tasync (state, current, _meta, reader) => {\n\t\t\tconst placement = await planBoundaryInbox(\n\t\t\t\tlane,\n\t\t\t\tdrive,\n\t\t\t\tstate,\n\t\t\t\tcurrent,\n\t\t\t\treader,\n\t\t\t\tstate.tipId,\n\t\t\t\tthreshold.value === undefined && current.continuation.kind === \"may_finish\",\n\t\t\t);\n\t\t\tif (placement.triggerEntryId !== undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: placement.writes,\n\t\t\t\t\toperationState: assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId, false),\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (threshold.value !== undefined) {\n\t\t\t\tconst structural: SummaryDecidingOperation = {\n\t\t\t\t\t...operationScopeOf(current),\n\t\t\t\t\tat: \"summary.deciding\",\n\t\t\t\t\ttask: {\n\t\t\t\t\t\ttaskId: threshold.value.taskId,\n\t\t\t\t\t\treason: \"threshold\",\n\t\t\t\t\t\tboundary: {\n\t\t\t\t\t\t\tkind: \"resume_checkpoint\",\n\t\t\t\t\t\t\tresumeAfter: { continuation: current.continuation, triggerEntryId: current.triggerEntryId },\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: [\n\t\t\t\t\t\t...placement.writes,\n\t\t\t\t\t\tsetValue(\n\t\t\t\t\t\t\toperationPreparation(drive.operationId, threshold.value.taskId),\n\t\t\t\t\t\t\tthreshold.value.preparation,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\toperationState: structural,\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => [\n\t\t\t\t\t\t...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"compaction_start\",\n\t\t\t\t\t\t\tlane: lane.name,\n\t\t\t\t\t\t\trunId: drive.operationId,\n\t\t\t\t\t\t\treason: \"threshold\",\n\t\t\t\t\t\t\tstartedAt: commit.timestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (current.continuation.kind === \"need_assistant\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: placement.writes,\n\t\t\t\t\toperationState: assistantReadyAtBoundary(\n\t\t\t\t\t\tlane,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tcurrent,\n\t\t\t\t\t\tcurrent.triggerEntryId,\n\t\t\t\t\t\tcurrent.continuation.overflowRecoveryUsed,\n\t\t\t\t\t),\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"return\",\n\t\t\t\tresult: { kind: \"finish_pending\", entryIds: placement.entries.map((entry) => entry.id) } as const,\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n\tif (planned.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tif (planned.value.kind !== \"finish_pending\") return planned.value;\n\tif (run.continuation.kind !== \"may_finish\") {\n\t\tthrow new SessionInvariantError(\"Checkpoint finish mediation requires a finish continuation\");\n\t}\n\treturn finishRunBoundary(lane, drive, run, run.continuation, planned.value.entryIds);\n}\n"]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { insertEntry } from "../../session/commit.js";
|
|
2
|
+
import { SessionInvariantError } from "../../session/session.js";
|
|
3
|
+
import { operationScopeOf, } from "../../session/types.js";
|
|
4
|
+
import { branchTip, operationPreparation, setValue } from "../../session/values.js";
|
|
5
|
+
import { chainEntries, committedEntryEvents } from "../transcript.js";
|
|
6
|
+
import { assistantReadyAtBoundary, boundaryPlacementEvents, finishRunBoundary, planBoundaryInbox, } from "./boundary.js";
|
|
7
|
+
import { prepareCompactionThreshold } from "./structural.js";
|
|
8
|
+
/** Consume before_run and commit the initial checkpoint. */
|
|
9
|
+
export async function startRun(lane, drive, run) {
|
|
10
|
+
const prompt = await lane.continueOperation(run, async (_state, _current, meta, reader) => {
|
|
11
|
+
if (meta.intent.kind !== "run")
|
|
12
|
+
throw new SessionInvariantError("Run operation has non-run intent");
|
|
13
|
+
const entries = await reader.getEntries(meta.intent.promptEntryIds, drive.context);
|
|
14
|
+
const messages = meta.intent.promptEntryIds.map((id) => {
|
|
15
|
+
const entry = entries.get(id);
|
|
16
|
+
if (entry?.type !== "message") {
|
|
17
|
+
throw new SessionInvariantError(`Run prompt entry ${id} is missing its message`);
|
|
18
|
+
}
|
|
19
|
+
return entry.message;
|
|
20
|
+
});
|
|
21
|
+
return { kind: "return", result: messages };
|
|
22
|
+
}, drive.context);
|
|
23
|
+
if (prompt.kind === "cancel_requested")
|
|
24
|
+
return { kind: "continue" };
|
|
25
|
+
const hook = await lane.hooks.runWithGate("before_run", { lane: lane.name, runId: drive.operationId, prompt: prompt.value, resources: lane.readConfig().resources }, drive.gate, drive.context);
|
|
26
|
+
const injected = hook?.messages ?? [];
|
|
27
|
+
for (const message of injected) {
|
|
28
|
+
if (message.role === "assistant" && message.stopReason === "pending") {
|
|
29
|
+
throw new SessionInvariantError("before_run returned a pending assistant message");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const reserved = injected.map((message) => ({ id: lane.session.idGenerator.next(), message }));
|
|
33
|
+
const result = await lane.continueOperation(run, (state, current) => {
|
|
34
|
+
const entries = chainEntries(state.tipId, reserved.map(({ id, message }) => ({ id, type: "message", message })));
|
|
35
|
+
const triggerEntryId = entries.at(-1)?.id ?? state.tipId;
|
|
36
|
+
if (triggerEntryId === null)
|
|
37
|
+
throw new SessionInvariantError("Run start has no trigger entry");
|
|
38
|
+
const nextState = {
|
|
39
|
+
...operationScopeOf(current),
|
|
40
|
+
at: "checkpoint",
|
|
41
|
+
continuation: { kind: "need_assistant", overflowRecoveryUsed: false },
|
|
42
|
+
triggerEntryId,
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
kind: "commit",
|
|
46
|
+
writes: [
|
|
47
|
+
...entries.map((entry) => insertEntry(entry)),
|
|
48
|
+
...(entries.length === 0 ? [] : [setValue(branchTip(lane.name), triggerEntryId)]),
|
|
49
|
+
],
|
|
50
|
+
operationState: nextState,
|
|
51
|
+
lane: { tipId: triggerEntryId },
|
|
52
|
+
materialize: () => ({ kind: "continue" }),
|
|
53
|
+
events: (commit) => committedEntryEvents(entries, commit, lane.name, drive.operationId),
|
|
54
|
+
};
|
|
55
|
+
}, drive.context);
|
|
56
|
+
return result.kind === "cancel_requested" ? { kind: "continue" } : result.value;
|
|
57
|
+
}
|
|
58
|
+
/** Advance one durable run boundary with at most one commit. */
|
|
59
|
+
export async function runCheckpoint(lane, drive, run) {
|
|
60
|
+
const threshold = await prepareCompactionThreshold(lane, drive, run);
|
|
61
|
+
if (threshold.kind === "cancel_requested")
|
|
62
|
+
return { kind: "continue" };
|
|
63
|
+
const planned = await lane.continueOperation(run, async (state, current, _meta, reader) => {
|
|
64
|
+
const placement = await planBoundaryInbox(lane, drive, state, current, reader, state.tipId, threshold.value === undefined && current.continuation.kind === "may_finish");
|
|
65
|
+
if (placement.triggerEntryId !== undefined) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "commit",
|
|
68
|
+
writes: placement.writes,
|
|
69
|
+
operationState: assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId, false),
|
|
70
|
+
lane: { tipId: placement.tipId, inbox: placement.inbox },
|
|
71
|
+
materialize: () => ({ kind: "continue" }),
|
|
72
|
+
events: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (threshold.value !== undefined) {
|
|
76
|
+
const structural = {
|
|
77
|
+
...operationScopeOf(current),
|
|
78
|
+
at: "summary.deciding",
|
|
79
|
+
task: {
|
|
80
|
+
taskId: threshold.value.taskId,
|
|
81
|
+
reason: "threshold",
|
|
82
|
+
boundary: {
|
|
83
|
+
kind: "resume_checkpoint",
|
|
84
|
+
resumeAfter: { continuation: current.continuation, triggerEntryId: current.triggerEntryId },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
kind: "commit",
|
|
90
|
+
writes: [
|
|
91
|
+
...placement.writes,
|
|
92
|
+
setValue(operationPreparation(drive.operationId, threshold.value.taskId), threshold.value.preparation),
|
|
93
|
+
],
|
|
94
|
+
operationState: structural,
|
|
95
|
+
lane: { tipId: placement.tipId, inbox: placement.inbox },
|
|
96
|
+
materialize: () => ({ kind: "continue" }),
|
|
97
|
+
events: (commit) => [
|
|
98
|
+
...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
99
|
+
{
|
|
100
|
+
type: "compaction_start",
|
|
101
|
+
lane: lane.name,
|
|
102
|
+
runId: drive.operationId,
|
|
103
|
+
reason: "threshold",
|
|
104
|
+
startedAt: commit.timestamp,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (current.continuation.kind === "need_assistant") {
|
|
110
|
+
return {
|
|
111
|
+
kind: "commit",
|
|
112
|
+
writes: placement.writes,
|
|
113
|
+
operationState: assistantReadyAtBoundary(lane, state, current, current.triggerEntryId, current.continuation.overflowRecoveryUsed),
|
|
114
|
+
lane: { tipId: placement.tipId, inbox: placement.inbox },
|
|
115
|
+
materialize: () => ({ kind: "continue" }),
|
|
116
|
+
events: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
kind: "return",
|
|
121
|
+
result: { kind: "finish_pending", entryIds: placement.entries.map((entry) => entry.id) },
|
|
122
|
+
};
|
|
123
|
+
}, drive.context);
|
|
124
|
+
if (planned.kind === "cancel_requested")
|
|
125
|
+
return { kind: "continue" };
|
|
126
|
+
if (planned.value.kind !== "finish_pending")
|
|
127
|
+
return planned.value;
|
|
128
|
+
if (run.continuation.kind !== "may_finish") {
|
|
129
|
+
throw new SessionInvariantError("Checkpoint finish mediation requires a finish continuation");
|
|
130
|
+
}
|
|
131
|
+
return finishRunBoundary(lane, drive, run, run.continuation, planned.value.entryIds);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../../../src/harness/runtime/drive/checkpoint.ts"],"names":[],"mappings":"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","sourcesContent":["import { insertEntry } from \"../../session/commit.ts\";\nimport { SessionInvariantError } from \"../../session/session.ts\";\nimport {\n\ttype CheckpointOperation,\n\ttype MessageEntry,\n\ttype NewEntry,\n\toperationScopeOf,\n\ttype StartingOperation,\n\ttype SummaryDecidingOperation,\n} from \"../../session/types.ts\";\nimport { branchTip, operationPreparation, setValue } from \"../../session/values.ts\";\nimport type { Lane } from \"../lane.ts\";\nimport { chainEntries, committedEntryEvents } from \"../transcript.ts\";\nimport type { Drive, ProcedureResult } from \"../types.ts\";\nimport {\n\tassistantReadyAtBoundary,\n\ttype BoundaryFinishPending,\n\tboundaryPlacementEvents,\n\tfinishRunBoundary,\n\tplanBoundaryInbox,\n} from \"./boundary.ts\";\nimport { prepareCompactionThreshold } from \"./structural.ts\";\n\n/** Consume before_run and commit the initial checkpoint. */\nexport async function startRun<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\trun: StartingOperation,\n): Promise<ProcedureResult> {\n\tconst prompt = await lane.continueOperation(\n\t\trun,\n\t\tasync (_state, _current, meta, reader) => {\n\t\t\tif (meta.intent.kind !== \"run\") throw new SessionInvariantError(\"Run operation has non-run intent\");\n\t\t\tconst entries = await reader.getEntries(meta.intent.promptEntryIds, drive.context);\n\t\t\tconst messages = meta.intent.promptEntryIds.map((id) => {\n\t\t\t\tconst entry = entries.get(id);\n\t\t\t\tif (entry?.type !== \"message\") {\n\t\t\t\t\tthrow new SessionInvariantError(`Run prompt entry ${id} is missing its message`);\n\t\t\t\t}\n\t\t\t\treturn entry.message;\n\t\t\t});\n\t\t\treturn { kind: \"return\", result: messages };\n\t\t},\n\t\tdrive.context,\n\t);\n\tif (prompt.kind === \"cancel_requested\") return { kind: \"continue\" };\n\n\tconst hook = await lane.hooks.runWithGate(\n\t\t\"before_run\",\n\t\t{ lane: lane.name, runId: drive.operationId, prompt: prompt.value, resources: lane.readConfig().resources },\n\t\tdrive.gate,\n\t\tdrive.context,\n\t);\n\tconst injected = hook?.messages ?? [];\n\tfor (const message of injected) {\n\t\tif (message.role === \"assistant\" && message.stopReason === \"pending\") {\n\t\t\tthrow new SessionInvariantError(\"before_run returned a pending assistant message\");\n\t\t}\n\t}\n\tconst reserved = injected.map((message) => ({ id: lane.session.idGenerator.next(), message }));\n\n\tconst result = await lane.continueOperation(\n\t\trun,\n\t\t(state, current) => {\n\t\t\tconst entries: NewEntry<MessageEntry>[] = chainEntries(\n\t\t\t\tstate.tipId,\n\t\t\t\treserved.map(({ id, message }) => ({ id, type: \"message\" as const, message })),\n\t\t\t);\n\t\t\tconst triggerEntryId = entries.at(-1)?.id ?? state.tipId;\n\t\t\tif (triggerEntryId === null) throw new SessionInvariantError(\"Run start has no trigger entry\");\n\t\t\tconst nextState: CheckpointOperation = {\n\t\t\t\t...operationScopeOf(current),\n\t\t\t\tat: \"checkpoint\",\n\t\t\t\tcontinuation: { kind: \"need_assistant\", overflowRecoveryUsed: false },\n\t\t\t\ttriggerEntryId,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tkind: \"commit\",\n\t\t\t\twrites: [\n\t\t\t\t\t...entries.map((entry) => insertEntry(entry)),\n\t\t\t\t\t...(entries.length === 0 ? [] : [setValue(branchTip(lane.name), triggerEntryId)]),\n\t\t\t\t],\n\t\t\t\toperationState: nextState,\n\t\t\t\tlane: { tipId: triggerEntryId },\n\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\tevents: (commit) => committedEntryEvents(entries, commit, lane.name, drive.operationId),\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n\treturn result.kind === \"cancel_requested\" ? { kind: \"continue\" } : result.value;\n}\n\n/** Advance one durable run boundary with at most one commit. */\nexport async function runCheckpoint<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\trun: CheckpointOperation,\n): Promise<ProcedureResult> {\n\tconst threshold = await prepareCompactionThreshold(lane, drive, run);\n\tif (threshold.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tconst planned = await lane.continueOperation<CheckpointOperation, ProcedureResult | BoundaryFinishPending>(\n\t\trun,\n\t\tasync (state, current, _meta, reader) => {\n\t\t\tconst placement = await planBoundaryInbox(\n\t\t\t\tlane,\n\t\t\t\tdrive,\n\t\t\t\tstate,\n\t\t\t\tcurrent,\n\t\t\t\treader,\n\t\t\t\tstate.tipId,\n\t\t\t\tthreshold.value === undefined && current.continuation.kind === \"may_finish\",\n\t\t\t);\n\t\t\tif (placement.triggerEntryId !== undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: placement.writes,\n\t\t\t\t\toperationState: assistantReadyAtBoundary(lane, state, current, placement.triggerEntryId, false),\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (threshold.value !== undefined) {\n\t\t\t\tconst structural: SummaryDecidingOperation = {\n\t\t\t\t\t...operationScopeOf(current),\n\t\t\t\t\tat: \"summary.deciding\",\n\t\t\t\t\ttask: {\n\t\t\t\t\t\ttaskId: threshold.value.taskId,\n\t\t\t\t\t\treason: \"threshold\",\n\t\t\t\t\t\tboundary: {\n\t\t\t\t\t\t\tkind: \"resume_checkpoint\",\n\t\t\t\t\t\t\tresumeAfter: { continuation: current.continuation, triggerEntryId: current.triggerEntryId },\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: [\n\t\t\t\t\t\t...placement.writes,\n\t\t\t\t\t\tsetValue(\n\t\t\t\t\t\t\toperationPreparation(drive.operationId, threshold.value.taskId),\n\t\t\t\t\t\t\tthreshold.value.preparation,\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\toperationState: structural,\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => [\n\t\t\t\t\t\t...boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"compaction_start\",\n\t\t\t\t\t\t\tlane: lane.name,\n\t\t\t\t\t\t\trunId: drive.operationId,\n\t\t\t\t\t\t\treason: \"threshold\",\n\t\t\t\t\t\t\tstartedAt: commit.timestamp,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (current.continuation.kind === \"need_assistant\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"commit\",\n\t\t\t\t\twrites: placement.writes,\n\t\t\t\t\toperationState: assistantReadyAtBoundary(\n\t\t\t\t\t\tlane,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tcurrent,\n\t\t\t\t\t\tcurrent.triggerEntryId,\n\t\t\t\t\t\tcurrent.continuation.overflowRecoveryUsed,\n\t\t\t\t\t),\n\t\t\t\t\tlane: { tipId: placement.tipId, inbox: placement.inbox },\n\t\t\t\t\tmaterialize: () => ({ kind: \"continue\" }) as const,\n\t\t\t\t\tevents: (commit) => boundaryPlacementEvents(placement, commit, 0, lane.name, drive.operationId),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"return\",\n\t\t\t\tresult: { kind: \"finish_pending\", entryIds: placement.entries.map((entry) => entry.id) } as const,\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n\tif (planned.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tif (planned.value.kind !== \"finish_pending\") return planned.value;\n\tif (run.continuation.kind !== \"may_finish\") {\n\t\tthrow new SessionInvariantError(\"Checkpoint finish mediation requires a finish continuation\");\n\t}\n\treturn finishRunBoundary(lane, drive, run, run.continuation, planned.value.entryIds);\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DeferredHandle } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Context } from "../../context.ts";
|
|
3
|
+
import { type DeferredEffectPendingOperation, type DeferredSuspendedOperation, type SessionReader } from "../../session/types.ts";
|
|
4
|
+
import type { Lane } from "../lane.ts";
|
|
5
|
+
import type { Drive, ProcedureResult } from "../types.ts";
|
|
6
|
+
type DeferredLeaf = DeferredSuspendedOperation | DeferredEffectPendingOperation;
|
|
7
|
+
export declare function readDeferredSourceHandle(reader: SessionReader, deferred: DeferredLeaf, context: Context): Promise<DeferredHandle>;
|
|
8
|
+
/** Poll one durably suspended deferred response when this pass carries a permit. */
|
|
9
|
+
export declare function runDeferredSuspended<TContext extends object | undefined>(lane: Lane<TContext>, drive: Drive, deferred: DeferredSuspendedOperation): Promise<ProcedureResult>;
|
|
10
|
+
/** Replace one orphaned unknown-outcome poll under fresh ids when this pass carries a permit. */
|
|
11
|
+
export declare function recoverDeferredPoll<TContext extends object | undefined>(lane: Lane<TContext>, drive: Drive, deferred: DeferredEffectPendingOperation): Promise<ProcedureResult>;
|
|
12
|
+
/** Advance or report the wait for one deferred run phase. */
|
|
13
|
+
export declare function runDeferred<TContext extends object | undefined>(lane: Lane<TContext>, drive: Drive, deferred: DeferredSuspendedOperation | DeferredEffectPendingOperation): Promise<ProcedureResult>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=deferred.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deferred.d.ts","sourceRoot":"","sources":["../../../../src/harness/runtime/drive/deferred.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,cAAc,EAAS,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,KAAK,OAAO,EAAwC,MAAM,kBAAkB,CAAC;AAItF,OAAO,EACN,KAAK,8BAA8B,EACnC,KAAK,0BAA0B,EAK/B,KAAK,aAAa,EAElB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAA2B,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnF,KAAK,YAAY,GAAG,0BAA0B,GAAG,8BAA8B,CAAC;AA0BhF,wBAAsB,wBAAwB,CAC7C,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,cAAc,CAAC,CAqBzB;AA2LD,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,QAAQ,SAAS,MAAM,GAAG,SAAS,EACvE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EACpB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,0BAA0B,GAClC,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED,iGAAiG;AACjG,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,MAAM,GAAG,SAAS,EACtE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EACpB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,8BAA8B,GACtC,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,QAAQ,SAAS,MAAM,GAAG,SAAS,EAC9D,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,EACpB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,0BAA0B,GAAG,8BAA8B,GACnE,OAAO,CAAC,eAAe,CAAC,CAI1B","sourcesContent":["import type { Api, DeferredHandle, Model } from \"@earendil-works/pi-ai\";\nimport { type Context, getTelemetryContext, withAbortSignal } from \"../../context.ts\";\nimport { consumeAssistantStream } from \"../../execution/assistant.ts\";\nimport { applyStreamOptionsPatch } from \"../../hooks.ts\";\nimport { SessionInvariantError } from \"../../session/session.ts\";\nimport {\n\ttype DeferredEffectPendingOperation,\n\ttype DeferredSuspendedOperation,\n\ttype LaneConfiguration,\n\ttype OperationError,\n\ttype OperationScope,\n\toperationScopeOf,\n\ttype SessionReader,\n\ttype SettledAssistantMessage,\n} from \"../../session/types.ts\";\nimport { deleteList, pendingAssistantFrames } from \"../../session/values.ts\";\nimport type { AgentHarnessStreamOptions } from \"../../types.ts\";\nimport type { Lane } from \"../lane.ts\";\nimport type { ContinueOperationResult, Drive, ProcedureResult } from \"../types.ts\";\nimport { openAssistantResponse, publishConfigurationFailure, publishResponse } from \"./response.ts\";\n\ntype DeferredLeaf = DeferredSuspendedOperation | DeferredEffectPendingOperation;\n/** Deferred effect payload without the run-wide scope fields. */\ntype EffectPendingFields = Omit<DeferredEffectPendingOperation, keyof OperationScope>;\n\ntype PreparedDeferredPoll = {\n\tkind: \"ready\";\n\tsource: DeferredHandle;\n\tmodel: Model<Api>;\n\tpoll: number;\n\tstreamOptions: AgentHarnessStreamOptions;\n};\n\ntype DeferredPreparation =\n\t| PreparedDeferredPoll\n\t| { kind: \"cancel_requested\" }\n\t| { kind: \"waiting\"; source: DeferredHandle }\n\t| { kind: \"configuration_failure\" };\n\nfunction configurationError(identity: LaneConfiguration[\"model\"]): OperationError {\n\treturn {\n\t\tcode: \"model_unavailable\",\n\t\tmessage: \"The configured model is unavailable in this process\",\n\t\tdetails: identity,\n\t};\n}\n\nexport async function readDeferredSourceHandle(\n\treader: SessionReader,\n\tdeferred: DeferredLeaf,\n\tcontext: Context,\n): Promise<DeferredHandle> {\n\tconst source = (await reader.getEntries([deferred.sourceEntryId], context)).get(deferred.sourceEntryId);\n\tif (\n\t\tsource?.type !== \"message\" ||\n\t\tsource.message.role !== \"assistant\" ||\n\t\tsource.message.stopReason !== \"deferred\" ||\n\t\tsource.message.deferred === undefined\n\t) {\n\t\tthrow new SessionInvariantError(`Deferred source ${deferred.sourceEntryId} is missing its assistant handle`);\n\t}\n\tconst handle = source.message.deferred;\n\tconst identity = deferred.configuration.model;\n\tif (\n\t\thandle.id.length === 0 ||\n\t\thandle.provider !== identity.provider ||\n\t\thandle.modelId !== identity.modelId ||\n\t\thandle.api !== source.message.api\n\t) {\n\t\tthrow new SessionInvariantError(`Deferred source ${deferred.sourceEntryId} has an invalid handle`);\n\t}\n\treturn handle;\n}\n\nasync function readSourceHandle<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tdeferred: DeferredLeaf,\n): Promise<ContinueOperationResult<DeferredHandle>> {\n\treturn lane.continueOperation(\n\t\tdeferred,\n\t\tasync (_state, _current, _meta, reader) => ({\n\t\t\tkind: \"return\",\n\t\t\tresult: await readDeferredSourceHandle(reader, deferred, drive.context),\n\t\t}),\n\t\tdrive.context,\n\t);\n}\n\nasync function prepareDeferredPoll<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\texpected: DeferredLeaf,\n): Promise<DeferredPreparation> {\n\tconst source = await readSourceHandle(lane, drive, expected);\n\tif (source.kind === \"cancel_requested\") return source;\n\tif (drive.deferredPermits === 0) return { kind: \"waiting\", source: source.value };\n\n\tconst identity = expected.configuration.model;\n\tconst model = lane.models.getModel(identity.provider, identity.modelId);\n\tif (model === undefined) return { kind: \"configuration_failure\" };\n\tconst baseOptions: AgentHarnessStreamOptions = { ...expected.streamOptions, deferred: false };\n\tconst poll = expected.at === \"deferred.suspended\" ? expected.poll + 1 : expected.poll;\n\tconst beforeRequest = await lane.hooks.runWithGate(\n\t\t\"before_request\",\n\t\t{\n\t\t\tlane: lane.name,\n\t\t\trunId: drive.operationId,\n\t\t\tmodel,\n\t\t\tstep: \"deferred\",\n\t\t\tattempt: poll,\n\t\t\tstreamOptions: baseOptions,\n\t\t},\n\t\tdrive.gate,\n\t\tdrive.context,\n\t);\n\treturn {\n\t\tkind: \"ready\",\n\t\tsource: source.value,\n\t\tmodel,\n\t\tpoll,\n\t\tstreamOptions: {\n\t\t\t...(beforeRequest?.streamOptions === undefined\n\t\t\t\t? baseOptions\n\t\t\t\t: applyStreamOptionsPatch(baseOptions, beforeRequest.streamOptions)),\n\t\t\tdeferred: false,\n\t\t},\n\t};\n}\n\nasync function publishPollIntent<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tdeferred: DeferredLeaf,\n\tprepared: PreparedDeferredPoll,\n\trecovery: boolean,\n): Promise<ContinueOperationResult<DeferredEffectPendingOperation>> {\n\tconst at = Date.now();\n\tconst next: EffectPendingFields = {\n\t\tat: \"deferred.effect_pending\",\n\t\tstepId: deferred.stepId,\n\t\tsourceEntryId: deferred.sourceEntryId,\n\t\tpoll: prepared.poll,\n\t\tresponseEntryId: lane.session.idGenerator.next(at),\n\t\tusageId: lane.session.idGenerator.next(at),\n\t\tconfiguration: deferred.configuration,\n\t\tstreamOptions: deferred.streamOptions,\n\t};\n\treturn lane.continueOperation(\n\t\tdeferred,\n\t\t(_state, current) => {\n\t\t\tconst nextState: DeferredEffectPendingOperation = { ...operationScopeOf(current), ...next };\n\t\t\treturn {\n\t\t\t\tkind: \"commit\",\n\t\t\t\twrites:\n\t\t\t\t\tdeferred.at === \"deferred.effect_pending\"\n\t\t\t\t\t\t? [deleteList(pendingAssistantFrames(drive.operationId, deferred.responseEntryId))]\n\t\t\t\t\t\t: [],\n\t\t\t\toperationState: nextState,\n\t\t\t\tmaterialize: () => {\n\t\t\t\t\tdrive.deferredPermits--;\n\t\t\t\t\treturn nextState;\n\t\t\t\t},\n\t\t\t\tevents: () => [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"run_resume\",\n\t\t\t\t\t\tlane: lane.name,\n\t\t\t\t\t\trunId: drive.operationId,\n\t\t\t\t\t\t...(recovery ? { recovery: true as const } : {}),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"turn_start\",\n\t\t\t\t\t\tlane: lane.name,\n\t\t\t\t\t\trunId: drive.operationId,\n\t\t\t\t\t\tturnId: `${next.stepId}:poll:${next.poll}`,\n\t\t\t\t\t\t...(recovery ? { recovery: true as const } : {}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t},\n\t\tdrive.context,\n\t);\n}\n\nasync function performDeferredPoll<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tprepared: PreparedDeferredPoll,\n\tintent: DeferredEffectPendingOperation,\n\trecovery: boolean,\n): Promise<SettledAssistantMessage> {\n\tconst response = openAssistantResponse(lane, drive, intent.responseEntryId, recovery);\n\tlet metadata: { status?: number; headers?: Record<string, string> } = {};\n\tconst admitted = withAbortSignal(drive.gate.signal, drive.context);\n\tconst stream = drive.gate.admit(() =>\n\t\tlane.models.streamDeferred(prepared.model, prepared.source, {\n\t\t\twait: 0,\n\t\t\tsignal: admitted.abortSignal,\n\t\t\ttelemetryContext: getTelemetryContext(admitted),\n\t\t\ttimeoutMs: prepared.streamOptions.timeoutMs,\n\t\t\tmaxRetries: prepared.streamOptions.maxRetries,\n\t\t\tmaxRetryDelayMs: prepared.streamOptions.maxRetryDelayMs,\n\t\t\theaders: prepared.streamOptions.headers,\n\t\t\tonPayload: async (payload, requestModel) => {\n\t\t\t\tconst result = await lane.hooks.runWithGate(\n\t\t\t\t\t\"before_payload\",\n\t\t\t\t\t{ lane: lane.name, runId: drive.operationId, model: requestModel, payload },\n\t\t\t\t\tdrive.gate,\n\t\t\t\t\tdrive.context,\n\t\t\t\t);\n\t\t\t\treturn result?.payload;\n\t\t\t},\n\t\t\tonResponse: (response) => {\n\t\t\t\tmetadata = { status: response.status, headers: response.headers };\n\t\t\t},\n\t\t}),\n\t);\n\n\ttry {\n\t\treturn await consumeAssistantStream(\n\t\t\tstream,\n\t\t\tresponse.observer,\n\t\t\t(message, context) => response.afterResponse(message, metadata, context),\n\t\t\tdrive.context,\n\t\t);\n\t} finally {\n\t\tawait response.close();\n\t}\n}\n\nasync function pollDeferred<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\texpected: DeferredLeaf,\n\trecovery: boolean,\n): Promise<ProcedureResult> {\n\tconst prepared = await prepareDeferredPoll(lane, drive, expected);\n\tif (prepared.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tif (prepared.kind === \"waiting\") {\n\t\treturn {\n\t\t\tkind: \"waiting\",\n\t\t\toutcome: {\n\t\t\t\tkind: \"waiting\",\n\t\t\t\toperationId: drive.operationId,\n\t\t\t\treason: \"deferred\",\n\t\t\t\tdeferred: prepared.source,\n\t\t\t},\n\t\t};\n\t}\n\tif (prepared.kind === \"configuration_failure\") {\n\t\treturn publishConfigurationFailure(lane, drive, expected, configurationError(expected.configuration.model));\n\t}\n\n\tconst intent = await publishPollIntent(lane, drive, expected, prepared, recovery);\n\tif (intent.kind === \"cancel_requested\") return { kind: \"continue\" };\n\tconst response = await performDeferredPoll(lane, drive, prepared, intent.value, recovery);\n\treturn publishResponse(lane, drive, intent.value, response, recovery ? { recovery: true } : {});\n}\n\n/** Poll one durably suspended deferred response when this pass carries a permit. */\nexport function runDeferredSuspended<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tdeferred: DeferredSuspendedOperation,\n): Promise<ProcedureResult> {\n\treturn pollDeferred(lane, drive, deferred, false);\n}\n\n/** Replace one orphaned unknown-outcome poll under fresh ids when this pass carries a permit. */\nexport function recoverDeferredPoll<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tdeferred: DeferredEffectPendingOperation,\n): Promise<ProcedureResult> {\n\treturn pollDeferred(lane, drive, deferred, true);\n}\n\n/** Advance or report the wait for one deferred run phase. */\nexport function runDeferred<TContext extends object | undefined>(\n\tlane: Lane<TContext>,\n\tdrive: Drive,\n\tdeferred: DeferredSuspendedOperation | DeferredEffectPendingOperation,\n): Promise<ProcedureResult> {\n\treturn deferred.at === \"deferred.suspended\"\n\t\t? runDeferredSuspended(lane, drive, deferred)\n\t\t: recoverDeferredPoll(lane, drive, deferred);\n}\n"]}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { getTelemetryContext, withAbortSignal } from "../../context.js";
|
|
2
|
+
import { consumeAssistantStream } from "../../execution/assistant.js";
|
|
3
|
+
import { applyStreamOptionsPatch } from "../../hooks.js";
|
|
4
|
+
import { SessionInvariantError } from "../../session/session.js";
|
|
5
|
+
import { operationScopeOf, } from "../../session/types.js";
|
|
6
|
+
import { deleteList, pendingAssistantFrames } from "../../session/values.js";
|
|
7
|
+
import { openAssistantResponse, publishConfigurationFailure, publishResponse } from "./response.js";
|
|
8
|
+
function configurationError(identity) {
|
|
9
|
+
return {
|
|
10
|
+
code: "model_unavailable",
|
|
11
|
+
message: "The configured model is unavailable in this process",
|
|
12
|
+
details: identity,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function readDeferredSourceHandle(reader, deferred, context) {
|
|
16
|
+
const source = (await reader.getEntries([deferred.sourceEntryId], context)).get(deferred.sourceEntryId);
|
|
17
|
+
if (source?.type !== "message" ||
|
|
18
|
+
source.message.role !== "assistant" ||
|
|
19
|
+
source.message.stopReason !== "deferred" ||
|
|
20
|
+
source.message.deferred === undefined) {
|
|
21
|
+
throw new SessionInvariantError(`Deferred source ${deferred.sourceEntryId} is missing its assistant handle`);
|
|
22
|
+
}
|
|
23
|
+
const handle = source.message.deferred;
|
|
24
|
+
const identity = deferred.configuration.model;
|
|
25
|
+
if (handle.id.length === 0 ||
|
|
26
|
+
handle.provider !== identity.provider ||
|
|
27
|
+
handle.modelId !== identity.modelId ||
|
|
28
|
+
handle.api !== source.message.api) {
|
|
29
|
+
throw new SessionInvariantError(`Deferred source ${deferred.sourceEntryId} has an invalid handle`);
|
|
30
|
+
}
|
|
31
|
+
return handle;
|
|
32
|
+
}
|
|
33
|
+
async function readSourceHandle(lane, drive, deferred) {
|
|
34
|
+
return lane.continueOperation(deferred, async (_state, _current, _meta, reader) => ({
|
|
35
|
+
kind: "return",
|
|
36
|
+
result: await readDeferredSourceHandle(reader, deferred, drive.context),
|
|
37
|
+
}), drive.context);
|
|
38
|
+
}
|
|
39
|
+
async function prepareDeferredPoll(lane, drive, expected) {
|
|
40
|
+
const source = await readSourceHandle(lane, drive, expected);
|
|
41
|
+
if (source.kind === "cancel_requested")
|
|
42
|
+
return source;
|
|
43
|
+
if (drive.deferredPermits === 0)
|
|
44
|
+
return { kind: "waiting", source: source.value };
|
|
45
|
+
const identity = expected.configuration.model;
|
|
46
|
+
const model = lane.models.getModel(identity.provider, identity.modelId);
|
|
47
|
+
if (model === undefined)
|
|
48
|
+
return { kind: "configuration_failure" };
|
|
49
|
+
const baseOptions = { ...expected.streamOptions, deferred: false };
|
|
50
|
+
const poll = expected.at === "deferred.suspended" ? expected.poll + 1 : expected.poll;
|
|
51
|
+
const beforeRequest = await lane.hooks.runWithGate("before_request", {
|
|
52
|
+
lane: lane.name,
|
|
53
|
+
runId: drive.operationId,
|
|
54
|
+
model,
|
|
55
|
+
step: "deferred",
|
|
56
|
+
attempt: poll,
|
|
57
|
+
streamOptions: baseOptions,
|
|
58
|
+
}, drive.gate, drive.context);
|
|
59
|
+
return {
|
|
60
|
+
kind: "ready",
|
|
61
|
+
source: source.value,
|
|
62
|
+
model,
|
|
63
|
+
poll,
|
|
64
|
+
streamOptions: {
|
|
65
|
+
...(beforeRequest?.streamOptions === undefined
|
|
66
|
+
? baseOptions
|
|
67
|
+
: applyStreamOptionsPatch(baseOptions, beforeRequest.streamOptions)),
|
|
68
|
+
deferred: false,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function publishPollIntent(lane, drive, deferred, prepared, recovery) {
|
|
73
|
+
const at = Date.now();
|
|
74
|
+
const next = {
|
|
75
|
+
at: "deferred.effect_pending",
|
|
76
|
+
stepId: deferred.stepId,
|
|
77
|
+
sourceEntryId: deferred.sourceEntryId,
|
|
78
|
+
poll: prepared.poll,
|
|
79
|
+
responseEntryId: lane.session.idGenerator.next(at),
|
|
80
|
+
usageId: lane.session.idGenerator.next(at),
|
|
81
|
+
configuration: deferred.configuration,
|
|
82
|
+
streamOptions: deferred.streamOptions,
|
|
83
|
+
};
|
|
84
|
+
return lane.continueOperation(deferred, (_state, current) => {
|
|
85
|
+
const nextState = { ...operationScopeOf(current), ...next };
|
|
86
|
+
return {
|
|
87
|
+
kind: "commit",
|
|
88
|
+
writes: deferred.at === "deferred.effect_pending"
|
|
89
|
+
? [deleteList(pendingAssistantFrames(drive.operationId, deferred.responseEntryId))]
|
|
90
|
+
: [],
|
|
91
|
+
operationState: nextState,
|
|
92
|
+
materialize: () => {
|
|
93
|
+
drive.deferredPermits--;
|
|
94
|
+
return nextState;
|
|
95
|
+
},
|
|
96
|
+
events: () => [
|
|
97
|
+
{
|
|
98
|
+
type: "run_resume",
|
|
99
|
+
lane: lane.name,
|
|
100
|
+
runId: drive.operationId,
|
|
101
|
+
...(recovery ? { recovery: true } : {}),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: "turn_start",
|
|
105
|
+
lane: lane.name,
|
|
106
|
+
runId: drive.operationId,
|
|
107
|
+
turnId: `${next.stepId}:poll:${next.poll}`,
|
|
108
|
+
...(recovery ? { recovery: true } : {}),
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}, drive.context);
|
|
113
|
+
}
|
|
114
|
+
async function performDeferredPoll(lane, drive, prepared, intent, recovery) {
|
|
115
|
+
const response = openAssistantResponse(lane, drive, intent.responseEntryId, recovery);
|
|
116
|
+
let metadata = {};
|
|
117
|
+
const admitted = withAbortSignal(drive.gate.signal, drive.context);
|
|
118
|
+
const stream = drive.gate.admit(() => lane.models.streamDeferred(prepared.model, prepared.source, {
|
|
119
|
+
wait: 0,
|
|
120
|
+
signal: admitted.abortSignal,
|
|
121
|
+
telemetryContext: getTelemetryContext(admitted),
|
|
122
|
+
timeoutMs: prepared.streamOptions.timeoutMs,
|
|
123
|
+
maxRetries: prepared.streamOptions.maxRetries,
|
|
124
|
+
maxRetryDelayMs: prepared.streamOptions.maxRetryDelayMs,
|
|
125
|
+
headers: prepared.streamOptions.headers,
|
|
126
|
+
onPayload: async (payload, requestModel) => {
|
|
127
|
+
const result = await lane.hooks.runWithGate("before_payload", { lane: lane.name, runId: drive.operationId, model: requestModel, payload }, drive.gate, drive.context);
|
|
128
|
+
return result?.payload;
|
|
129
|
+
},
|
|
130
|
+
onResponse: (response) => {
|
|
131
|
+
metadata = { status: response.status, headers: response.headers };
|
|
132
|
+
},
|
|
133
|
+
}));
|
|
134
|
+
try {
|
|
135
|
+
return await consumeAssistantStream(stream, response.observer, (message, context) => response.afterResponse(message, metadata, context), drive.context);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
await response.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function pollDeferred(lane, drive, expected, recovery) {
|
|
142
|
+
const prepared = await prepareDeferredPoll(lane, drive, expected);
|
|
143
|
+
if (prepared.kind === "cancel_requested")
|
|
144
|
+
return { kind: "continue" };
|
|
145
|
+
if (prepared.kind === "waiting") {
|
|
146
|
+
return {
|
|
147
|
+
kind: "waiting",
|
|
148
|
+
outcome: {
|
|
149
|
+
kind: "waiting",
|
|
150
|
+
operationId: drive.operationId,
|
|
151
|
+
reason: "deferred",
|
|
152
|
+
deferred: prepared.source,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (prepared.kind === "configuration_failure") {
|
|
157
|
+
return publishConfigurationFailure(lane, drive, expected, configurationError(expected.configuration.model));
|
|
158
|
+
}
|
|
159
|
+
const intent = await publishPollIntent(lane, drive, expected, prepared, recovery);
|
|
160
|
+
if (intent.kind === "cancel_requested")
|
|
161
|
+
return { kind: "continue" };
|
|
162
|
+
const response = await performDeferredPoll(lane, drive, prepared, intent.value, recovery);
|
|
163
|
+
return publishResponse(lane, drive, intent.value, response, recovery ? { recovery: true } : {});
|
|
164
|
+
}
|
|
165
|
+
/** Poll one durably suspended deferred response when this pass carries a permit. */
|
|
166
|
+
export function runDeferredSuspended(lane, drive, deferred) {
|
|
167
|
+
return pollDeferred(lane, drive, deferred, false);
|
|
168
|
+
}
|
|
169
|
+
/** Replace one orphaned unknown-outcome poll under fresh ids when this pass carries a permit. */
|
|
170
|
+
export function recoverDeferredPoll(lane, drive, deferred) {
|
|
171
|
+
return pollDeferred(lane, drive, deferred, true);
|
|
172
|
+
}
|
|
173
|
+
/** Advance or report the wait for one deferred run phase. */
|
|
174
|
+
export function runDeferred(lane, drive, deferred) {
|
|
175
|
+
return deferred.at === "deferred.suspended"
|
|
176
|
+
? runDeferredSuspended(lane, drive, deferred)
|
|
177
|
+
: recoverDeferredPoll(lane, drive, deferred);
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=deferred.js.map
|