@earendil-works/pi-durable 0.87.0 → 0.99.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 +72 -1
- package/dist/documents.d.ts +66 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +98 -0
- package/dist/documents.js.map +1 -0
- package/dist/entries.d.ts +13 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +18 -0
- package/dist/entries.js.map +1 -0
- package/dist/env/index.d.ts +131 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +45 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/node.d.ts +45 -0
- package/dist/env/node.d.ts.map +1 -0
- package/dist/env/node.js +908 -0
- package/dist/env/node.js.map +1 -0
- package/dist/env/utils/adaptive-publisher.d.ts +24 -0
- package/dist/env/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/env/utils/adaptive-publisher.js +79 -0
- package/dist/env/utils/adaptive-publisher.js.map +1 -0
- package/dist/env/utils/output-capture.d.ts +31 -0
- package/dist/env/utils/output-capture.d.ts.map +1 -0
- package/dist/env/utils/output-capture.js +238 -0
- package/dist/env/utils/output-capture.js.map +1 -0
- package/dist/env/utils/truncate.d.ts +71 -0
- package/dist/env/utils/truncate.d.ts.map +1 -0
- package/dist/env/utils/truncate.js +294 -0
- package/dist/env/utils/truncate.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/harness/config.d.ts +20 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +18 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +27 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +109 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/generation.d.ts +36 -0
- package/dist/harness/generation.d.ts.map +1 -0
- package/dist/harness/generation.js +266 -0
- package/dist/harness/generation.js.map +1 -0
- package/dist/harness/harness.d.ts +10 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +266 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/live.d.ts +40 -0
- package/dist/harness/live.d.ts.map +1 -0
- package/dist/harness/live.js +41 -0
- package/dist/harness/live.js.map +1 -0
- package/dist/harness/prompt.d.ts +24 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +88 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/registry.d.ts +7 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +309 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/scheduler.d.ts +63 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +605 -0
- package/dist/harness/scheduler.js.map +1 -0
- package/dist/harness/submissions.d.ts +24 -0
- package/dist/harness/submissions.d.ts.map +1 -0
- package/dist/harness/submissions.js +133 -0
- package/dist/harness/submissions.js.map +1 -0
- package/dist/harness/types.d.ts +358 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/util.d.ts +14 -0
- package/dist/harness/util.d.ts.map +1 -0
- package/dist/harness/util.js +55 -0
- package/dist/harness/util.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/session/forks.d.ts +10 -0
- package/dist/session/forks.d.ts.map +1 -0
- package/dist/session/forks.js +42 -0
- package/dist/session/forks.js.map +1 -0
- package/dist/session/observation.d.ts +28 -0
- package/dist/session/observation.d.ts.map +1 -0
- package/dist/session/observation.js +280 -0
- package/dist/session/observation.js.map +1 -0
- package/dist/session/session.d.ts +59 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +380 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/transaction.d.ts +105 -0
- package/dist/session/transaction.d.ts.map +1 -0
- package/dist/session/transaction.js +718 -0
- package/dist/session/transaction.js.map +1 -0
- package/dist/storage/jsonl/index.d.ts +2 -0
- package/dist/storage/jsonl/index.d.ts.map +1 -0
- package/dist/storage/jsonl/index.js +2 -0
- package/dist/storage/jsonl/index.js.map +1 -0
- package/dist/storage/jsonl/node.d.ts +6 -0
- package/dist/storage/jsonl/node.d.ts.map +1 -0
- package/dist/storage/jsonl/node.js +8 -0
- package/dist/storage/jsonl/node.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +115 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -0
- package/dist/storage/jsonl/storage.js +681 -0
- package/dist/storage/jsonl/storage.js.map +1 -0
- package/dist/storage/memory.d.ts +60 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +681 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/sqlite/database.d.ts +30 -0
- package/dist/storage/sqlite/database.d.ts.map +1 -0
- package/dist/storage/sqlite/database.js +2 -0
- package/dist/storage/sqlite/database.js.map +1 -0
- package/dist/storage/sqlite/index.d.ts +4 -0
- package/dist/storage/sqlite/index.d.ts.map +1 -0
- package/dist/storage/sqlite/index.js +3 -0
- package/dist/storage/sqlite/index.js.map +1 -0
- package/dist/storage/sqlite/migrations.d.ts +11 -0
- package/dist/storage/sqlite/migrations.d.ts.map +1 -0
- package/dist/storage/sqlite/migrations.js +111 -0
- package/dist/storage/sqlite/migrations.js.map +1 -0
- package/dist/storage/sqlite/node.d.ts +25 -0
- package/dist/storage/sqlite/node.d.ts.map +1 -0
- package/dist/storage/sqlite/node.js +97 -0
- package/dist/storage/sqlite/node.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +51 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/storage/sqlite/storage.js +628 -0
- package/dist/storage/sqlite/storage.js.map +1 -0
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +5 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing/assertions.d.ts +5 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +25 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/runner.d.ts +10 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +14 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/storage-benchmark.d.ts +41 -0
- package/dist/testing/storage-benchmark.d.ts.map +1 -0
- package/dist/testing/storage-benchmark.js +351 -0
- package/dist/testing/storage-benchmark.js.map +1 -0
- package/dist/testing/storage-conformance.d.ts +4 -0
- package/dist/testing/storage-conformance.d.ts.map +1 -0
- package/dist/testing/storage-conformance.js +1122 -0
- package/dist/testing/storage-conformance.js.map +1 -0
- package/dist/testing/types.d.ts +19 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/dist/types.d.ts +525 -78
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/chord-delta-findings.md +1 -1
- package/docs/pico-v5-chord-usage.md +90 -102
- package/docs/pico-v5-handoff.md +493 -253
- package/docs/pico-v5.md +1901 -848
- package/package.json +47 -5
- package/dist/memory-storage.d.ts +0 -37
- package/dist/memory-storage.d.ts.map +0 -1
- package/dist/memory-storage.js +0 -322
- package/dist/memory-storage.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/harness/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport type { AssistantMessage, Message, ToolCall, ToolResultMessage } from \"@earendil-works/pi-ai\";\nimport type { SessionImpl } from \"../session/session.ts\";\nimport type { ContextEdit, ConversationId, Cursor, EntryId, EntryRecord, Storage } from \"../types.ts\";\nimport type { ContextView } from \"./types.ts\";\n\nconst SCAN_PAGE_SIZE = 256;\nconst EXCLUDED_STOP_REASONS: ReadonlySet<AssistantMessage[\"stopReason\"]> = new Set([\"aborted\", \"error\", \"deferred\"]);\nconst MISSING_RESULT_TEXT = \"Tool result unavailable: history ends before this call completed.\";\n\n/** Head marker and newest visible entry that fix one committed context range. */\nexport type ContextBounds = {\n\treadonly head: (EntryRecord & { readonly head: EntryId }) | undefined;\n\treadonly tail: EntryId;\n};\n\n/**\n * Capture the bounds of the current context, or of the context cut off at the visible entry `at`, with two O(1)\n * reads. Run this on the Session line; entries at or below the tail are immutable, so `deriveContext()` can then scan\n * them off the line.\n */\nexport async function captureContextBounds(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextBounds | undefined> {\n\tlet tail: EntryId | undefined;\n\tif (at === undefined) {\n\t\ttail = (await storage.scanEntries({ conversationId }, 1, undefined, context)).items[0]?.id;\n\t\tif (tail === undefined) return undefined;\n\t} else {\n\t\tif ((await storage.entry(conversationId, at, context)) === undefined) {\n\t\t\tthrow new Error(`Entry ${at} is not visible from conversation ${conversationId}`);\n\t\t}\n\t\ttail = at;\n\t}\n\treturn { head: await storage.findLatestHeadMarker(conversationId, tail, context), tail };\n}\n\n/** Committed context of one conversation: bounds captured on the Session line, entries derived off it. */\nexport async function readContext(\n\tsession: SessionImpl,\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tcontext: Context,\n\tat?: EntryId,\n): Promise<ContextView> {\n\tconst bounds = await session.readOnLine(() => captureContextBounds(storage, conversationId, context, at));\n\treturn deriveContext(storage, conversationId, bounds, context);\n}\n\n/**\n * Derive the active transcript and model context of one conversation within captured bounds.\n *\n * H = newest visible head marker; the range runs from `H.head` (or transcript start) through the tail. Per target,\n * the newest edit in the range wins. Context entries are H followed by the range's non-head entries.\n */\nexport async function deriveContext(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<ContextView> {\n\tif (bounds === undefined) return { head: undefined, entries: [], messages: [] };\n\tconst head = bounds.head;\n\tconst range: EntryRecord[] = [];\n\tlet cursor: Cursor | undefined;\n\tdo {\n\t\tconst page = await storage.scanEntries(\n\t\t\thead === undefined\n\t\t\t\t? { conversationId, maxEntryId: bounds.tail }\n\t\t\t\t: { conversationId, minEntryId: head.head, maxEntryId: bounds.tail },\n\t\t\tSCAN_PAGE_SIZE,\n\t\t\tcursor,\n\t\t\tcontext,\n\t\t);\n\t\trange.push(...page.items);\n\t\tcursor = page.next;\n\t} while (cursor !== undefined);\n\trange.reverse();\n\n\tconst edits = new Map<EntryId, ContextEdit>();\n\tfor (const entry of range) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\n\tconst entries = head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\n\tconst messages: Message[] = [];\n\tfor (const entry of entries) {\n\t\tconst edit = edits.get(entry.id);\n\t\tif (edit?.action === \"omit\") continue;\n\t\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\t\tfor (const message of contributed) {\n\t\t\tif (message.role === \"assistant\" && EXCLUDED_STOP_REASONS.has(message.stopReason)) continue;\n\t\t\tmessages.push(message);\n\t\t}\n\t}\n\treturn { head, entries, messages: orderToolResults(messages) };\n}\n\n/**\n * Place each assistant's tool results directly after it in call order. Results are taken from the messages before\n * the next assistant; a missing result is synthesized and unmatched results are dropped.\n */\nfunction orderToolResults(messages: readonly Message[]): Message[] {\n\tconst ordered: Message[] = [];\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index]!;\n\t\tif (message.role === \"toolResult\") continue;\n\t\tordered.push(message);\n\t\tif (message.role !== \"assistant\") continue;\n\t\tconst calls = message.content.filter((content): content is ToolCall => content.type === \"toolCall\");\n\t\tif (calls.length === 0) continue;\n\t\tconst results = new Map<string, number>();\n\t\tfor (let next = index + 1; next < messages.length && messages[next]!.role !== \"assistant\"; next++) {\n\t\t\tconst candidate = messages[next]!;\n\t\t\tif (candidate.role === \"toolResult\" && !results.has(candidate.toolCallId)) {\n\t\t\t\tresults.set(candidate.toolCallId, next);\n\t\t\t}\n\t\t}\n\t\tfor (const call of calls) {\n\t\t\tconst resultIndex = results.get(call.id);\n\t\t\tordered.push(resultIndex === undefined ? missingResult(call, message.timestamp) : messages[resultIndex]!);\n\t\t}\n\t}\n\treturn ordered;\n}\n\nfunction missingResult(call: ToolCall, timestamp: number): ToolResultMessage {\n\treturn {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: call.id,\n\t\ttoolName: call.name,\n\t\tcontent: [{ type: \"text\", text: MISSING_RESULT_TEXT }],\n\t\tisError: true,\n\t\tdetails: { reason: \"missing_result\" },\n\t\ttimestamp,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type DeferredHandle, type ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { EntryId } from "../types.ts";
|
|
3
|
+
import type { ConversationStreamOptions, ModelRef } from "./types.ts";
|
|
4
|
+
export type GenerationInput = Record<string, never>;
|
|
5
|
+
export type GenerationCheckpoint = {
|
|
6
|
+
phase: "prepare";
|
|
7
|
+
attempt: number;
|
|
8
|
+
} | {
|
|
9
|
+
phase: "request";
|
|
10
|
+
attempt: number;
|
|
11
|
+
model: ModelRef;
|
|
12
|
+
thinkingLevel: ModelThinkingLevel;
|
|
13
|
+
/** Configured request options when preparation committed; a resend after recovery uses them unchanged. */
|
|
14
|
+
streamOptions: ConversationStreamOptions;
|
|
15
|
+
/** Newest entry included in the request. */
|
|
16
|
+
cutoff: EntryId;
|
|
17
|
+
} | {
|
|
18
|
+
phase: "retry";
|
|
19
|
+
attempt: number;
|
|
20
|
+
until: number;
|
|
21
|
+
} | {
|
|
22
|
+
phase: "poll";
|
|
23
|
+
attempt: number;
|
|
24
|
+
model: ModelRef;
|
|
25
|
+
handle: DeferredHandle;
|
|
26
|
+
pollAt: number;
|
|
27
|
+
};
|
|
28
|
+
export type GenerationResult = {
|
|
29
|
+
entryId: EntryId;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Built-in generation task: prepares the positional system prompt, requests or polls the model, retries, and classifies
|
|
33
|
+
* the response. The run's inputs live in `pi.live.run`.
|
|
34
|
+
*/
|
|
35
|
+
export declare const GenerationTask: import("../types.ts").Task<GenerationInput, GenerationCheckpoint, GenerationResult, object>;
|
|
36
|
+
//# sourceMappingURL=generation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generation.d.ts","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"AACA,OAAO,EAGN,KAAK,cAAc,EAInB,KAAK,kBAAkB,EAGvB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EAAkB,OAAO,EAAkC,MAAM,aAAa,CAAC;AAI3F,OAAO,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAEpD,MAAM,MAAM,oBAAoB,GAC7B;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrC;IACA,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,QAAQ,CAAC;IAChB,aAAa,EAAE,kBAAkB,CAAC;IAClC,0GAA0G;IAC1G,aAAa,EAAE,yBAAyB,CAAC;IACzC,4CAA4C;IAC5C,MAAM,EAAE,OAAO,CAAC;CACf,GACD;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F,MAAM,MAAM,gBAAgB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAQpD;;;GAGG;AACH,eAAO,MAAM,cAAc,6FAiGzB,CAAC"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { copyJson } from "@earendil-works/chord";
|
|
2
|
+
import { isRetryableAssistantError, retryDelayMs, } from "@earendil-works/pi-ai";
|
|
3
|
+
import { AssistantEntry, SystemEntry } from "../entries.js";
|
|
4
|
+
import { defineTask } from "../tasks.js";
|
|
5
|
+
import { ConversationConfig, DEFAULT_RETRY_POLICY } from "./config.js";
|
|
6
|
+
import { endRun, LiveDoc } from "./live.js";
|
|
7
|
+
import { planSystemEntries, renderSections, replaySections } from "./prompt.js";
|
|
8
|
+
const PARTIAL_THROTTLE_MS = 100;
|
|
9
|
+
const DEFAULT_POLL_AFTER_MS = 5000;
|
|
10
|
+
/**
|
|
11
|
+
* Built-in generation task: prepares the positional system prompt, requests or polls the model, retries, and classifies
|
|
12
|
+
* the response. The run's inputs live in `pi.live.run`.
|
|
13
|
+
*/
|
|
14
|
+
export const GenerationTask = defineTask({
|
|
15
|
+
name: "pi.generation",
|
|
16
|
+
version: 1,
|
|
17
|
+
initial: () => ({ phase: "prepare", attempt: 1 }),
|
|
18
|
+
phases: {
|
|
19
|
+
/**
|
|
20
|
+
* Render the system prompt and append the positional `pi.system` entries it needs, then move to `request`. Only
|
|
21
|
+
* the Harness writes to a busy conversation, so the transcript read here is still the tail at the commit.
|
|
22
|
+
*/
|
|
23
|
+
prepare: async (task, runtime, context) => {
|
|
24
|
+
const { conversationId, registry } = runtime;
|
|
25
|
+
for (const failure of registry.failures())
|
|
26
|
+
if (failure.kind === "section")
|
|
27
|
+
runtime.report(failure.error);
|
|
28
|
+
const { model, thinkingLevel, streamOptions } = (await runtime.snapshot(ConversationConfig, conversationId, context)) ??
|
|
29
|
+
ConversationConfig.definition.initial();
|
|
30
|
+
if (model === undefined || runtime.models.getModel(model.provider, model.modelId) === undefined) {
|
|
31
|
+
return failNoModel(runtime, model, context);
|
|
32
|
+
}
|
|
33
|
+
const view = await runtime.context(conversationId, context);
|
|
34
|
+
const shown = replaySections(view.messages);
|
|
35
|
+
const input = {
|
|
36
|
+
conversationId,
|
|
37
|
+
tools: [],
|
|
38
|
+
shown: Object.fromEntries(shown),
|
|
39
|
+
model,
|
|
40
|
+
thinkingLevel,
|
|
41
|
+
read: runtime,
|
|
42
|
+
};
|
|
43
|
+
const report = (error) => runtime.report(error);
|
|
44
|
+
const desired = await renderSections(registry.sections(), input, shown, report, context);
|
|
45
|
+
const entries = planSystemEntries(view, desired, runtime.now());
|
|
46
|
+
await runtime.commit(async (tx) => {
|
|
47
|
+
let cutoff = (await tx.scanEntries({ conversationId }, 1)).items[0]?.id;
|
|
48
|
+
for (const entry of entries)
|
|
49
|
+
cutoff = (await tx.appendEntry(SystemEntry, conversationId, entry)).id;
|
|
50
|
+
if (cutoff === undefined)
|
|
51
|
+
throw new Error(`Conversation ${conversationId} has no entries to send`);
|
|
52
|
+
const { attempt } = task.state.checkpoint;
|
|
53
|
+
const request = { attempt, model, thinkingLevel, streamOptions: streamOptions ?? {}, cutoff };
|
|
54
|
+
return { status: "running", checkpoint: { phase: "request", ...request } };
|
|
55
|
+
}, context);
|
|
56
|
+
},
|
|
57
|
+
request: async (task, runtime, context) => {
|
|
58
|
+
const { attempt, model: ref, thinkingLevel, streamOptions, cutoff } = task.state.checkpoint;
|
|
59
|
+
const conversationId = runtime.conversationId;
|
|
60
|
+
await runtime.commit(async (tx) => {
|
|
61
|
+
const live = await tx.doc(LiveDoc, conversationId);
|
|
62
|
+
await convertPartial(tx, live, conversationId);
|
|
63
|
+
live.generation = { attempt };
|
|
64
|
+
return undefined;
|
|
65
|
+
}, context);
|
|
66
|
+
const model = runtime.models.getModel(ref.provider, ref.modelId);
|
|
67
|
+
if (model === undefined)
|
|
68
|
+
return failNoModel(runtime, ref, context);
|
|
69
|
+
const view = await runtime.context(conversationId, context, cutoff);
|
|
70
|
+
const options = {
|
|
71
|
+
...streamOptions,
|
|
72
|
+
signal: runtime.signal,
|
|
73
|
+
...(thinkingLevel === "off" ? {} : { reasoning: thinkingLevel }),
|
|
74
|
+
};
|
|
75
|
+
const message = await streamResponse(runtime, model, view.messages, options, attempt, context);
|
|
76
|
+
await classify(runtime, attempt, ref, undefined, message, context);
|
|
77
|
+
},
|
|
78
|
+
retry: async (task, runtime, context) => {
|
|
79
|
+
const { attempt, until } = task.state.checkpoint;
|
|
80
|
+
await runtime.sleep(until, context);
|
|
81
|
+
await runtime.commit(async (tx) => {
|
|
82
|
+
(await tx.doc(LiveDoc, runtime.conversationId)).generation = { attempt: attempt + 1 };
|
|
83
|
+
return { status: "running", checkpoint: { phase: "prepare", attempt: attempt + 1 } };
|
|
84
|
+
}, context);
|
|
85
|
+
},
|
|
86
|
+
poll: async (task, runtime, context) => {
|
|
87
|
+
const { attempt, model: ref, handle, pollAt } = task.state.checkpoint;
|
|
88
|
+
const model = runtime.models.getModel(ref.provider, ref.modelId);
|
|
89
|
+
if (model === undefined)
|
|
90
|
+
return failNoModel(runtime, ref, context);
|
|
91
|
+
await runtime.sleep(pollAt, context);
|
|
92
|
+
const message = await runtime.models.fetchDeferred(model, handle, { signal: runtime.signal });
|
|
93
|
+
await classify(runtime, attempt, ref, pollAt, message, context);
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
abort: async (task, runtime, context) => {
|
|
97
|
+
const checkpoint = task.state.checkpoint;
|
|
98
|
+
if (checkpoint.phase === "poll") {
|
|
99
|
+
const model = runtime.models.getModel(checkpoint.model.provider, checkpoint.model.modelId);
|
|
100
|
+
if (model !== undefined) {
|
|
101
|
+
try {
|
|
102
|
+
await runtime.models.cancelDeferred(model, checkpoint.handle, { signal: runtime.signal });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
runtime.report(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const conversationId = runtime.conversationId;
|
|
110
|
+
await runtime.commit(async (tx) => {
|
|
111
|
+
const live = await tx.doc(LiveDoc, conversationId);
|
|
112
|
+
await convertPartial(tx, live, conversationId);
|
|
113
|
+
endRun(tx, live, runtime.taskId, { status: "unanswered", reason: "aborted" });
|
|
114
|
+
return { status: "terminal", outcome: { status: "aborted" } };
|
|
115
|
+
}, context);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
/** Settle the run's inputs `unanswered` with `no_model` and fail. */
|
|
119
|
+
async function failNoModel(runtime, ref, context) {
|
|
120
|
+
const message = ref === undefined ? "No model is configured" : `Model ${ref.provider}/${ref.modelId} is not available`;
|
|
121
|
+
await runtime.commit(async (tx) => {
|
|
122
|
+
const live = await tx.doc(LiveDoc, runtime.conversationId);
|
|
123
|
+
endRun(tx, live, runtime.taskId, { status: "unanswered", reason: "no_model" });
|
|
124
|
+
return { status: "terminal", outcome: { status: "failed", error: { message, detail: { reason: "no_model" } } } };
|
|
125
|
+
}, context);
|
|
126
|
+
}
|
|
127
|
+
/** Append a committed partial left by an interrupted attempt as an aborted assistant entry; the caller replaces `generation`. */
|
|
128
|
+
async function convertPartial(tx, live, conversationId) {
|
|
129
|
+
const partial = live.generation?.message;
|
|
130
|
+
if (partial === undefined)
|
|
131
|
+
return;
|
|
132
|
+
const message = copyJson(partial);
|
|
133
|
+
await tx.appendEntry(AssistantEntry, conversationId, { model: [{ ...message, stopReason: "aborted" }] });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Stream one request and return the terminal message. Partials commit as trailing writes at most every 100 ms with one
|
|
137
|
+
* commit in flight; `finally` stops the throttle and awaits that commit, so no stale partial lands after the outcome.
|
|
138
|
+
*/
|
|
139
|
+
async function streamResponse(runtime, model, messages, options, attempt, context) {
|
|
140
|
+
let pending;
|
|
141
|
+
let timer;
|
|
142
|
+
let inFlight;
|
|
143
|
+
let stopped = false;
|
|
144
|
+
const flush = () => {
|
|
145
|
+
timer = undefined;
|
|
146
|
+
const partial = pending;
|
|
147
|
+
pending = undefined;
|
|
148
|
+
if (partial === undefined || stopped)
|
|
149
|
+
return;
|
|
150
|
+
inFlight = (async () => {
|
|
151
|
+
// Copy synchronously: the provider keeps mutating its partial.
|
|
152
|
+
const message = copyJson(partial, { omitUndefinedProperties: true });
|
|
153
|
+
await runtime.commit(async (tx) => {
|
|
154
|
+
const live = await tx.doc(LiveDoc, runtime.conversationId);
|
|
155
|
+
live.generation ??= { attempt };
|
|
156
|
+
assignJson(live.generation, "message", message);
|
|
157
|
+
return undefined;
|
|
158
|
+
}, context);
|
|
159
|
+
})()
|
|
160
|
+
.catch((error) => {
|
|
161
|
+
// Rejections after an abort mark or close are expected; the committed state stays consistent.
|
|
162
|
+
if (!runtime.signal.aborted)
|
|
163
|
+
runtime.report(error);
|
|
164
|
+
})
|
|
165
|
+
.finally(() => {
|
|
166
|
+
inFlight = undefined;
|
|
167
|
+
if (pending !== undefined && !stopped)
|
|
168
|
+
timer = setTimeout(flush, PARTIAL_THROTTLE_MS);
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
try {
|
|
172
|
+
const events = runtime.models.streamSimple(model, { messages: [...messages] }, options);
|
|
173
|
+
for await (const event of events) {
|
|
174
|
+
if (event.type === "done" || event.type === "error")
|
|
175
|
+
continue;
|
|
176
|
+
pending = event.partial;
|
|
177
|
+
if (timer === undefined && inFlight === undefined)
|
|
178
|
+
timer = setTimeout(flush, PARTIAL_THROTTLE_MS);
|
|
179
|
+
}
|
|
180
|
+
return await events.result();
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
stopped = true;
|
|
184
|
+
clearTimeout(timer);
|
|
185
|
+
await inFlight;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Classify a terminal provider message in one commit that also clears the partial. `previousPollAt` is set when the
|
|
190
|
+
* message came from polling, so a still deferred result polls strictly later.
|
|
191
|
+
*/
|
|
192
|
+
async function classify(runtime, attempt, ref, previousPollAt, message, context) {
|
|
193
|
+
// An abort mark or close: the abort invocation or the reopened run handles the committed state.
|
|
194
|
+
runtime.signal.throwIfAborted();
|
|
195
|
+
const conversationId = runtime.conversationId;
|
|
196
|
+
if (message.stopReason === "deferred" && message.deferred !== undefined) {
|
|
197
|
+
const handle = message.deferred;
|
|
198
|
+
const pollAt = Math.max(runtime.now() + (handle.pollAfterMs ?? DEFAULT_POLL_AFTER_MS), previousPollAt === undefined ? Number.NEGATIVE_INFINITY : previousPollAt + 1);
|
|
199
|
+
await runtime.commit(async (tx) => {
|
|
200
|
+
(await tx.doc(LiveDoc, conversationId)).generation = { attempt, deferred: { pollAt } };
|
|
201
|
+
return { status: "running", checkpoint: { phase: "poll", attempt, model: ref, handle, pollAt } };
|
|
202
|
+
}, context);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const policy = (await runtime.snapshot(ConversationConfig, conversationId, context))?.retry ?? DEFAULT_RETRY_POLICY;
|
|
206
|
+
const retry = message.stopReason === "error" &&
|
|
207
|
+
isRetryableAssistantError(message) &&
|
|
208
|
+
policy.enabled &&
|
|
209
|
+
attempt <= policy.maxRetries;
|
|
210
|
+
const until = retry ? runtime.now() + retryDelayMs(policy, attempt) : 0;
|
|
211
|
+
await runtime.commit(async (tx) => {
|
|
212
|
+
const live = await tx.doc(LiveDoc, conversationId);
|
|
213
|
+
const entry = await tx.appendEntry(AssistantEntry, conversationId, { model: [message] });
|
|
214
|
+
switch (message.stopReason) {
|
|
215
|
+
case "stop":
|
|
216
|
+
case "length":
|
|
217
|
+
// Until the tool chain exists, a tool call settles as the answer.
|
|
218
|
+
case "toolUse":
|
|
219
|
+
endRun(tx, live, runtime.taskId, { status: "done", answer: entry.id });
|
|
220
|
+
return { status: "terminal", outcome: { status: "completed", result: { entryId: entry.id } } };
|
|
221
|
+
}
|
|
222
|
+
if (retry) {
|
|
223
|
+
live.generation = { attempt, retry: { at: until, error: message.errorMessage ?? "" } };
|
|
224
|
+
return { status: "running", checkpoint: { phase: "retry", attempt, until } };
|
|
225
|
+
}
|
|
226
|
+
const text = message.errorMessage ?? `Model response ended with stop reason ${message.stopReason}`;
|
|
227
|
+
endRun(tx, live, runtime.taskId, { status: "unanswered", reason: "model_error", detail: text });
|
|
228
|
+
return {
|
|
229
|
+
status: "terminal",
|
|
230
|
+
outcome: { status: "failed", error: { message: text, detail: { reason: "model_error" } } },
|
|
231
|
+
};
|
|
232
|
+
}, context);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Assign `value` at `target[key]` leaf by leaf. Chord records a container assignment as one full set and only emits an
|
|
236
|
+
* append when a string leaf is reassigned with a longer string, so writing the partial whole would store and publish the
|
|
237
|
+
* complete message on every flush.
|
|
238
|
+
*/
|
|
239
|
+
function assignJson(target, key, value) {
|
|
240
|
+
const slots = target;
|
|
241
|
+
const current = slots[key];
|
|
242
|
+
if (isRecord(current) && isRecord(value)) {
|
|
243
|
+
for (const name of Object.keys(current))
|
|
244
|
+
if (!Object.hasOwn(value, name))
|
|
245
|
+
delete current[name];
|
|
246
|
+
for (const [name, child] of Object.entries(value))
|
|
247
|
+
assignJson(current, name, child);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (Array.isArray(current) && Array.isArray(value) && current.length <= value.length) {
|
|
251
|
+
const items = current;
|
|
252
|
+
for (let index = 0; index < value.length; index++) {
|
|
253
|
+
if (index < items.length)
|
|
254
|
+
assignJson(items, index, value[index]);
|
|
255
|
+
else
|
|
256
|
+
items.push(value[index]);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (current !== value)
|
|
261
|
+
slots[key] = value;
|
|
262
|
+
}
|
|
263
|
+
function isRecord(value) {
|
|
264
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=generation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generation.js","sourceRoot":"","sources":["../../src/harness/generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,QAAQ,EAA8B,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAIN,yBAAyB,EAIzB,YAAY,GAEZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAkB,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAyBhF,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAA0D;IACjG,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACjD,MAAM,EAAE;QACP;;;WAGG;QACH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACzC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;YAC7C,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE;gBAAE,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;oBAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACzG,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,GAC5C,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,kBAAkB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC;gBACrE,kBAAkB,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YACzC,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;gBACjG,OAAO,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG;gBACb,cAAc;gBACd,KAAK,EAAE,EAAE;gBACT,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;gBAChC,KAAK;gBACL,aAAa;gBACb,IAAI,EAAE,OAAO;aACb,CAAC;YACF,MAAM,MAAM,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACzF,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YAChE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACjC,IAAI,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gBACxE,KAAK,MAAM,KAAK,IAAI,OAAO;oBAAE,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpG,IAAI,MAAM,KAAK,SAAS;oBAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,cAAc,yBAAyB,CAAC,CAAC;gBACnG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;gBAC1C,MAAM,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;gBAC9F,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC;YAC5E,CAAC,EAAE,OAAO,CAAC,CAAC;QACb,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACzC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YAC5F,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;YAC9C,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;gBACnD,MAAM,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;gBAC/C,IAAI,CAAC,UAAU,GAAG,EAAE,OAAO,EAAE,CAAC;gBAC9B,OAAO,SAAS,CAAC;YAClB,CAAC,EAAE,OAAO,CAAC,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YACnE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YACpE,MAAM,OAAO,GAAwB;gBACpC,GAAG,aAAa;gBAChB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,GAAG,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;aAChE,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/F,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACvC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YACjD,MAAM,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACpC,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACjC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,EAAE,CAAC;gBACtF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,EAAE,EAAE,CAAC;YACtF,CAAC,EAAE,OAAO,CAAC,CAAC;QACb,CAAC;QACD,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACtC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;YACtE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YACnE,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACrC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAC9F,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACjE,CAAC;KACD;IACD,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;QACzC,IAAI,UAAU,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3F,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,IAAI,CAAC;oBACJ,MAAM,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC3F,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvB,CAAC;YACF,CAAC;QACF,CAAC;QACD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC9C,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YACnD,MAAM,cAAc,CAAC,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;YAC/C,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC9E,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;QAC/D,CAAC,EAAE,OAAO,CAAC,CAAC;IACb,CAAC;CACD,CAAC,CAAC;AAEH,qEAAqE;AACrE,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,GAAyB,EAAE,OAAgB;IACvF,MAAM,OAAO,GACZ,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,OAAO,mBAAmB,CAAC;IACxG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/E,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC;IAClH,CAAC,EAAE,OAAO,CAAC,CAAC;AACb,CAAC;AAED,iIAAiI;AACjI,KAAK,UAAU,cAAc,CAAC,EAAM,EAAE,IAAsB,EAAE,cAA8B;IAC3F,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;IACzC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO;IAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAgC,CAAC;IACjE,MAAM,EAAE,CAAC,WAAW,CAAC,cAAc,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;AAC1G,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,cAAc,CAC5B,OAAgB,EAChB,KAAiB,EACjB,QAA4B,EAC5B,OAA4B,EAC5B,OAAe,EACf,OAAgB;IAEhB,IAAI,OAAqC,CAAC;IAC1C,IAAI,KAAgD,CAAC;IACrD,IAAI,QAAmC,CAAC;IACxC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,KAAK,GAAG,GAAS,EAAE;QACxB,KAAK,GAAG,SAAS,CAAC;QAClB,MAAM,OAAO,GAAG,OAAO,CAAC;QACxB,OAAO,GAAG,SAAS,CAAC;QACpB,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO;YAAE,OAAO;QAC7C,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,+DAA+D;YAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;gBAC3D,IAAI,CAAC,UAAU,KAAK,EAAE,OAAO,EAAE,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,UAA8C,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;gBACpF,OAAO,SAAS,CAAC;YAClB,CAAC,EAAE,OAAO,CAAC,CAAC;QACb,CAAC,CAAC,EAAE;aACF,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACzB,8FAA8F;YAC9F,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpD,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACb,QAAQ,GAAG,SAAS,CAAC;YACrB,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,OAAO;gBAAE,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACxF,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,SAAS;YAC9D,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;YACxB,IAAI,KAAK,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS;gBAAE,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;QACnG,CAAC;QACD,OAAO,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IAC9B,CAAC;YAAS,CAAC;QACV,OAAO,GAAG,IAAI,CAAC;QACf,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,QAAQ,CAAC;IAChB,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,QAAQ,CACtB,OAAgB,EAChB,OAAe,EACf,GAAa,EACb,cAAkC,EAClC,OAAyB,EACzB,OAAgB;IAEhB,gGAAgG;IAChG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAChC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC9C,IAAI,OAAO,CAAC,UAAU,KAAK,UAAU,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzE,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CACtB,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,qBAAqB,CAAC,EAC7D,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAC5E,CAAC;QACF,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACjC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC;YACvF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QAClG,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,OAAO;IACR,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,kBAAkB,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,oBAAoB,CAAC;IACpH,MAAM,KAAK,GACV,OAAO,CAAC,UAAU,KAAK,OAAO;QAC9B,yBAAyB,CAAC,OAAO,CAAC;QAClC,MAAM,CAAC,OAAO;QACd,OAAO,IAAI,MAAM,CAAC,UAAU,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,EAAiB,EAAE;QAChD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,WAAW,CAAC,cAAc,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzF,QAAQ,OAAO,CAAC,UAAU,EAAE,CAAC;YAC5B,KAAK,MAAM,CAAC;YACZ,KAAK,QAAQ,CAAC;YACd,kEAAkE;YAClE,KAAK,SAAS;gBACb,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC;QACjG,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,UAAU,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,IAAI,EAAE,EAAE,EAAE,CAAC;YACvF,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9E,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,IAAI,yCAAyC,OAAO,CAAC,UAAU,EAAE,CAAC;QACnG,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAChG,OAAO;YACN,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE;SAC1F,CAAC;IACH,CAAC,EAAE,OAAO,CAAC,CAAC;AACb,CAAC;AAID;;;;GAIG;AACH,SAAS,UAAU,CAAC,MAAqB,EAAE,GAAoB,EAAE,KAAgB;IAChF,MAAM,KAAK,GAAG,MAA4C,CAAC;IAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;gBAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/F,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACpF,OAAO;IACR,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACtF,MAAM,KAAK,GAAG,OAAsB,CAAC;QACrC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACnD,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM;gBAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAE,CAAC,CAAC;;gBAC7D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAE,CAAC,CAAC;QAChC,CAAC;QACD,OAAO;IACR,CAAC;IACD,IAAI,OAAO,KAAK,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAC3C,CAAC;AAED,SAAS,QAAQ,CAAC,KAA4B;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["import { type Context, copyJson, type Draft, type JsonValue } from \"@earendil-works/chord\";\nimport {\n\ttype Api,\n\ttype AssistantMessage,\n\ttype DeferredHandle,\n\tisRetryableAssistantError,\n\ttype Message,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\tretryDelayMs,\n\ttype SimpleStreamOptions,\n} from \"@earendil-works/pi-ai\";\nimport { AssistantEntry, SystemEntry } from \"../entries.ts\";\nimport { defineTask } from \"../tasks.ts\";\nimport type { ConversationId, EntryId, NextTaskState, TaskRuntime, Tx } from \"../types.ts\";\nimport { ConversationConfig, DEFAULT_RETRY_POLICY } from \"./config.ts\";\nimport { endRun, LiveDoc, type LiveState } from \"./live.ts\";\nimport { planSystemEntries, renderSections, replaySections } from \"./prompt.ts\";\nimport type { ConversationStreamOptions, ModelRef } from \"./types.ts\";\n\nexport type GenerationInput = Record<string, never>;\n\nexport type GenerationCheckpoint =\n\t| { phase: \"prepare\"; attempt: number }\n\t| {\n\t\t\tphase: \"request\";\n\t\t\tattempt: number;\n\t\t\tmodel: ModelRef;\n\t\t\tthinkingLevel: ModelThinkingLevel;\n\t\t\t/** Configured request options when preparation committed; a resend after recovery uses them unchanged. */\n\t\t\tstreamOptions: ConversationStreamOptions;\n\t\t\t/** Newest entry included in the request. */\n\t\t\tcutoff: EntryId;\n\t }\n\t| { phase: \"retry\"; attempt: number; until: number }\n\t| { phase: \"poll\"; attempt: number; model: ModelRef; handle: DeferredHandle; pollAt: number };\n\nexport type GenerationResult = { entryId: EntryId };\n\ntype Runtime = TaskRuntime<GenerationInput, GenerationCheckpoint, GenerationResult, object>;\ntype Next = NextTaskState<GenerationCheckpoint, GenerationResult>;\n\nconst PARTIAL_THROTTLE_MS = 100;\nconst DEFAULT_POLL_AFTER_MS = 5000;\n\n/**\n * Built-in generation task: prepares the positional system prompt, requests or polls the model, retries, and classifies\n * the response. The run's inputs live in `pi.live.run`.\n */\nexport const GenerationTask = defineTask<GenerationInput, GenerationCheckpoint, GenerationResult>({\n\tname: \"pi.generation\",\n\tversion: 1,\n\tinitial: () => ({ phase: \"prepare\", attempt: 1 }),\n\tphases: {\n\t\t/**\n\t\t * Render the system prompt and append the positional `pi.system` entries it needs, then move to `request`. Only\n\t\t * the Harness writes to a busy conversation, so the transcript read here is still the tail at the commit.\n\t\t */\n\t\tprepare: async (task, runtime, context) => {\n\t\t\tconst { conversationId, registry } = runtime;\n\t\t\tfor (const failure of registry.failures()) if (failure.kind === \"section\") runtime.report(failure.error);\n\t\t\tconst { model, thinkingLevel, streamOptions } =\n\t\t\t\t(await runtime.snapshot(ConversationConfig, conversationId, context)) ??\n\t\t\t\tConversationConfig.definition.initial();\n\t\t\tif (model === undefined || runtime.models.getModel(model.provider, model.modelId) === undefined) {\n\t\t\t\treturn failNoModel(runtime, model, context);\n\t\t\t}\n\t\t\tconst view = await runtime.context(conversationId, context);\n\t\t\tconst shown = replaySections(view.messages);\n\t\t\tconst input = {\n\t\t\t\tconversationId,\n\t\t\t\ttools: [],\n\t\t\t\tshown: Object.fromEntries(shown),\n\t\t\t\tmodel,\n\t\t\t\tthinkingLevel,\n\t\t\t\tread: runtime,\n\t\t\t};\n\t\t\tconst report = (error: unknown) => runtime.report(error);\n\t\t\tconst desired = await renderSections(registry.sections(), input, shown, report, context);\n\t\t\tconst entries = planSystemEntries(view, desired, runtime.now());\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tlet cutoff = (await tx.scanEntries({ conversationId }, 1)).items[0]?.id;\n\t\t\t\tfor (const entry of entries) cutoff = (await tx.appendEntry(SystemEntry, conversationId, entry)).id;\n\t\t\t\tif (cutoff === undefined) throw new Error(`Conversation ${conversationId} has no entries to send`);\n\t\t\t\tconst { attempt } = task.state.checkpoint;\n\t\t\t\tconst request = { attempt, model, thinkingLevel, streamOptions: streamOptions ?? {}, cutoff };\n\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"request\", ...request } };\n\t\t\t}, context);\n\t\t},\n\t\trequest: async (task, runtime, context) => {\n\t\t\tconst { attempt, model: ref, thinkingLevel, streamOptions, cutoff } = task.state.checkpoint;\n\t\t\tconst conversationId = runtime.conversationId;\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst live = await tx.doc(LiveDoc, conversationId);\n\t\t\t\tawait convertPartial(tx, live, conversationId);\n\t\t\t\tlive.generation = { attempt };\n\t\t\t\treturn undefined;\n\t\t\t}, context);\n\t\t\tconst model = runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (model === undefined) return failNoModel(runtime, ref, context);\n\t\t\tconst view = await runtime.context(conversationId, context, cutoff);\n\t\t\tconst options: SimpleStreamOptions = {\n\t\t\t\t...streamOptions,\n\t\t\t\tsignal: runtime.signal,\n\t\t\t\t...(thinkingLevel === \"off\" ? {} : { reasoning: thinkingLevel }),\n\t\t\t};\n\t\t\tconst message = await streamResponse(runtime, model, view.messages, options, attempt, context);\n\t\t\tawait classify(runtime, attempt, ref, undefined, message, context);\n\t\t},\n\t\tretry: async (task, runtime, context) => {\n\t\t\tconst { attempt, until } = task.state.checkpoint;\n\t\t\tawait runtime.sleep(until, context);\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\t(await tx.doc(LiveDoc, runtime.conversationId)).generation = { attempt: attempt + 1 };\n\t\t\t\treturn { status: \"running\", checkpoint: { phase: \"prepare\", attempt: attempt + 1 } };\n\t\t\t}, context);\n\t\t},\n\t\tpoll: async (task, runtime, context) => {\n\t\t\tconst { attempt, model: ref, handle, pollAt } = task.state.checkpoint;\n\t\t\tconst model = runtime.models.getModel(ref.provider, ref.modelId);\n\t\t\tif (model === undefined) return failNoModel(runtime, ref, context);\n\t\t\tawait runtime.sleep(pollAt, context);\n\t\t\tconst message = await runtime.models.fetchDeferred(model, handle, { signal: runtime.signal });\n\t\t\tawait classify(runtime, attempt, ref, pollAt, message, context);\n\t\t},\n\t},\n\tabort: async (task, runtime, context) => {\n\t\tconst checkpoint = task.state.checkpoint;\n\t\tif (checkpoint.phase === \"poll\") {\n\t\t\tconst model = runtime.models.getModel(checkpoint.model.provider, checkpoint.model.modelId);\n\t\t\tif (model !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tawait runtime.models.cancelDeferred(model, checkpoint.handle, { signal: runtime.signal });\n\t\t\t\t} catch (error) {\n\t\t\t\t\truntime.report(error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst conversationId = runtime.conversationId;\n\t\tawait runtime.commit(async (tx) => {\n\t\t\tconst live = await tx.doc(LiveDoc, conversationId);\n\t\t\tawait convertPartial(tx, live, conversationId);\n\t\t\tendRun(tx, live, runtime.taskId, { status: \"unanswered\", reason: \"aborted\" });\n\t\t\treturn { status: \"terminal\", outcome: { status: \"aborted\" } };\n\t\t}, context);\n\t},\n});\n\n/** Settle the run's inputs `unanswered` with `no_model` and fail. */\nasync function failNoModel(runtime: Runtime, ref: ModelRef | undefined, context: Context): Promise<void> {\n\tconst message =\n\t\tref === undefined ? \"No model is configured\" : `Model ${ref.provider}/${ref.modelId} is not available`;\n\tawait runtime.commit(async (tx) => {\n\t\tconst live = await tx.doc(LiveDoc, runtime.conversationId);\n\t\tendRun(tx, live, runtime.taskId, { status: \"unanswered\", reason: \"no_model\" });\n\t\treturn { status: \"terminal\", outcome: { status: \"failed\", error: { message, detail: { reason: \"no_model\" } } } };\n\t}, context);\n}\n\n/** Append a committed partial left by an interrupted attempt as an aborted assistant entry; the caller replaces `generation`. */\nasync function convertPartial(tx: Tx, live: Draft<LiveState>, conversationId: ConversationId): Promise<void> {\n\tconst partial = live.generation?.message;\n\tif (partial === undefined) return;\n\tconst message = copyJson(partial) as unknown as AssistantMessage;\n\tawait tx.appendEntry(AssistantEntry, conversationId, { model: [{ ...message, stopReason: \"aborted\" }] });\n}\n\n/**\n * Stream one request and return the terminal message. Partials commit as trailing writes at most every 100 ms with one\n * commit in flight; `finally` stops the throttle and awaits that commit, so no stale partial lands after the outcome.\n */\nasync function streamResponse(\n\truntime: Runtime,\n\tmodel: Model<Api>,\n\tmessages: readonly Message[],\n\toptions: SimpleStreamOptions,\n\tattempt: number,\n\tcontext: Context,\n): Promise<AssistantMessage> {\n\tlet pending: AssistantMessage | undefined;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet inFlight: Promise<void> | undefined;\n\tlet stopped = false;\n\tconst flush = (): void => {\n\t\ttimer = undefined;\n\t\tconst partial = pending;\n\t\tpending = undefined;\n\t\tif (partial === undefined || stopped) return;\n\t\tinFlight = (async () => {\n\t\t\t// Copy synchronously: the provider keeps mutating its partial.\n\t\t\tconst message = copyJson(partial, { omitUndefinedProperties: true });\n\t\t\tawait runtime.commit(async (tx) => {\n\t\t\t\tconst live = await tx.doc(LiveDoc, runtime.conversationId);\n\t\t\t\tlive.generation ??= { attempt };\n\t\t\t\tassignJson(live.generation as Draft<Record<string, JsonValue>>, \"message\", message);\n\t\t\t\treturn undefined;\n\t\t\t}, context);\n\t\t})()\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// Rejections after an abort mark or close are expected; the committed state stays consistent.\n\t\t\t\tif (!runtime.signal.aborted) runtime.report(error);\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tinFlight = undefined;\n\t\t\t\tif (pending !== undefined && !stopped) timer = setTimeout(flush, PARTIAL_THROTTLE_MS);\n\t\t\t});\n\t};\n\ttry {\n\t\tconst events = runtime.models.streamSimple(model, { messages: [...messages] }, options);\n\t\tfor await (const event of events) {\n\t\t\tif (event.type === \"done\" || event.type === \"error\") continue;\n\t\t\tpending = event.partial;\n\t\t\tif (timer === undefined && inFlight === undefined) timer = setTimeout(flush, PARTIAL_THROTTLE_MS);\n\t\t}\n\t\treturn await events.result();\n\t} finally {\n\t\tstopped = true;\n\t\tclearTimeout(timer);\n\t\tawait inFlight;\n\t}\n}\n\n/**\n * Classify a terminal provider message in one commit that also clears the partial. `previousPollAt` is set when the\n * message came from polling, so a still deferred result polls strictly later.\n */\nasync function classify(\n\truntime: Runtime,\n\tattempt: number,\n\tref: ModelRef,\n\tpreviousPollAt: number | undefined,\n\tmessage: AssistantMessage,\n\tcontext: Context,\n): Promise<void> {\n\t// An abort mark or close: the abort invocation or the reopened run handles the committed state.\n\truntime.signal.throwIfAborted();\n\tconst conversationId = runtime.conversationId;\n\tif (message.stopReason === \"deferred\" && message.deferred !== undefined) {\n\t\tconst handle = message.deferred;\n\t\tconst pollAt = Math.max(\n\t\t\truntime.now() + (handle.pollAfterMs ?? DEFAULT_POLL_AFTER_MS),\n\t\t\tpreviousPollAt === undefined ? Number.NEGATIVE_INFINITY : previousPollAt + 1,\n\t\t);\n\t\tawait runtime.commit(async (tx) => {\n\t\t\t(await tx.doc(LiveDoc, conversationId)).generation = { attempt, deferred: { pollAt } };\n\t\t\treturn { status: \"running\", checkpoint: { phase: \"poll\", attempt, model: ref, handle, pollAt } };\n\t\t}, context);\n\t\treturn;\n\t}\n\tconst policy = (await runtime.snapshot(ConversationConfig, conversationId, context))?.retry ?? DEFAULT_RETRY_POLICY;\n\tconst retry =\n\t\tmessage.stopReason === \"error\" &&\n\t\tisRetryableAssistantError(message) &&\n\t\tpolicy.enabled &&\n\t\tattempt <= policy.maxRetries;\n\tconst until = retry ? runtime.now() + retryDelayMs(policy, attempt) : 0;\n\tawait runtime.commit(async (tx): Promise<Next> => {\n\t\tconst live = await tx.doc(LiveDoc, conversationId);\n\t\tconst entry = await tx.appendEntry(AssistantEntry, conversationId, { model: [message] });\n\t\tswitch (message.stopReason) {\n\t\t\tcase \"stop\":\n\t\t\tcase \"length\":\n\t\t\t// Until the tool chain exists, a tool call settles as the answer.\n\t\t\tcase \"toolUse\":\n\t\t\t\tendRun(tx, live, runtime.taskId, { status: \"done\", answer: entry.id });\n\t\t\t\treturn { status: \"terminal\", outcome: { status: \"completed\", result: { entryId: entry.id } } };\n\t\t}\n\t\tif (retry) {\n\t\t\tlive.generation = { attempt, retry: { at: until, error: message.errorMessage ?? \"\" } };\n\t\t\treturn { status: \"running\", checkpoint: { phase: \"retry\", attempt, until } };\n\t\t}\n\t\tconst text = message.errorMessage ?? `Model response ended with stop reason ${message.stopReason}`;\n\t\tendRun(tx, live, runtime.taskId, { status: \"unanswered\", reason: \"model_error\", detail: text });\n\t\treturn {\n\t\t\tstatus: \"terminal\",\n\t\t\toutcome: { status: \"failed\", error: { message: text, detail: { reason: \"model_error\" } } },\n\t\t};\n\t}, context);\n}\n\ntype JsonContainer = Record<string, JsonValue> | JsonValue[];\n\n/**\n * Assign `value` at `target[key]` leaf by leaf. Chord records a container assignment as one full set and only emits an\n * append when a string leaf is reassigned with a longer string, so writing the partial whole would store and publish the\n * complete message on every flush.\n */\nfunction assignJson(target: JsonContainer, key: string | number, value: JsonValue): void {\n\tconst slots = target as Record<string | number, JsonValue>;\n\tconst current = slots[key];\n\tif (isRecord(current) && isRecord(value)) {\n\t\tfor (const name of Object.keys(current)) if (!Object.hasOwn(value, name)) delete current[name];\n\t\tfor (const [name, child] of Object.entries(value)) assignJson(current, name, child);\n\t\treturn;\n\t}\n\tif (Array.isArray(current) && Array.isArray(value) && current.length <= value.length) {\n\t\tconst items = current as JsonValue[];\n\t\tfor (let index = 0; index < value.length; index++) {\n\t\t\tif (index < items.length) assignJson(items, index, value[index]!);\n\t\t\telse items.push(value[index]!);\n\t\t}\n\t\treturn;\n\t}\n\tif (current !== value) slots[key] = value;\n}\n\nfunction isRecord(value: JsonValue | undefined): value is Record<string, JsonValue> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Context } from "@earendil-works/chord";
|
|
2
|
+
import type { Storage } from "../types.ts";
|
|
3
|
+
import type { HarnessOptions, Harness as HarnessType, ToolRegistration } from "./types.ts";
|
|
4
|
+
/** Durable agent harness over one Session. */
|
|
5
|
+
export type Harness = HarnessType;
|
|
6
|
+
export declare const Harness: {
|
|
7
|
+
/** Open a Harness over storage. The registry may keep changing while the Harness runs. */
|
|
8
|
+
open<Tool extends ToolRegistration>(storage: Storage, options: HarnessOptions<Tool>, context: Context): Promise<Harness>;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/harness/harness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAoB,MAAM,uBAAuB,CAAC;AAKvE,OAAO,KAAK,EASX,OAAO,EAKP,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAQX,cAAc,EACd,OAAO,IAAI,WAAW,EAOtB,gBAAgB,EAChB,MAAM,YAAY,CAAC;AAyTpB,8CAA8C;AAC9C,MAAM,MAAM,OAAO,GAAG,WAAW,CAAC;AAElC,eAAO,MAAM,OAAO;IACnB,0FAA0F;IACpF,IAAI,CAAC,IAAI,SAAS,gBAAgB,WAC9B,OAAO,WACP,cAAc,CAAC,IAAI,CAAC,WACpB,OAAO,GACd,OAAO,CAAC,OAAO,CAAC;CAoBnB,CAAC"}
|