@earendil-works/pi-durable 0.99.1 → 1.0.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/CHANGELOG.md +7 -0
- package/README.md +557 -46
- package/dist/entries.d.ts +20 -2
- package/dist/entries.d.ts.map +1 -1
- package/dist/entries.js +14 -1
- package/dist/entries.js.map +1 -1
- package/dist/env/index.d.ts +18 -38
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js +2 -0
- package/dist/env/index.js.map +1 -1
- package/dist/env/node.d.ts +2 -0
- package/dist/env/node.d.ts.map +1 -1
- package/dist/env/node.js +53 -58
- package/dist/env/node.js.map +1 -1
- package/dist/harness/agent.d.ts +31 -0
- package/dist/harness/agent.d.ts.map +1 -0
- package/dist/harness/agent.js +209 -0
- package/dist/harness/agent.js.map +1 -0
- package/dist/harness/compaction.d.ts +56 -0
- package/dist/harness/compaction.d.ts.map +1 -0
- package/dist/harness/compaction.js +382 -0
- package/dist/harness/compaction.js.map +1 -0
- package/dist/harness/context.d.ts +8 -0
- package/dist/harness/context.d.ts.map +1 -1
- package/dist/harness/context.js +32 -21
- package/dist/harness/context.js.map +1 -1
- package/dist/harness/define.d.ts +18 -0
- package/dist/harness/define.d.ts.map +1 -0
- package/dist/harness/define.js +25 -0
- package/dist/harness/define.js.map +1 -0
- package/dist/harness/events.d.ts +166 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +319 -0
- package/dist/harness/events.js.map +1 -0
- package/dist/harness/generation.d.ts +36 -7
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +356 -82
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js +141 -106
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +59 -0
- package/dist/harness/inbox.d.ts.map +1 -0
- package/dist/harness/inbox.js +97 -0
- package/dist/harness/inbox.js.map +1 -0
- package/dist/harness/json.d.ts +10 -0
- package/dist/harness/json.d.ts.map +1 -0
- package/dist/harness/json.js +33 -0
- package/dist/harness/json.js.map +1 -0
- package/dist/harness/live.d.ts +66 -7
- package/dist/harness/live.d.ts.map +1 -1
- package/dist/harness/live.js +71 -7
- package/dist/harness/live.js.map +1 -1
- package/dist/harness/output.d.ts +62 -0
- package/dist/harness/output.d.ts.map +1 -0
- package/dist/harness/output.js +261 -0
- package/dist/harness/output.js.map +1 -0
- package/dist/harness/prompt.d.ts +9 -6
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +57 -14
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/registry.d.ts +2 -3
- package/dist/harness/registry.d.ts.map +1 -1
- package/dist/harness/registry.js +69 -277
- package/dist/harness/registry.js.map +1 -1
- package/dist/harness/scheduler.d.ts +42 -12
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +649 -75
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +14 -9
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +76 -34
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +49 -0
- package/dist/harness/task-graph.d.ts.map +1 -0
- package/dist/harness/task-graph.js +163 -0
- package/dist/harness/task-graph.js.map +1 -0
- package/dist/harness/tool.d.ts +36 -0
- package/dist/harness/tool.d.ts.map +1 -0
- package/dist/harness/tool.js +415 -0
- package/dist/harness/tool.js.map +1 -0
- package/dist/harness/types.d.ts +330 -125
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/usage.d.ts +18 -0
- package/dist/harness/usage.d.ts.map +1 -0
- package/dist/harness/usage.js +57 -0
- package/dist/harness/usage.js.map +1 -0
- package/dist/harness/view.d.ts +43 -0
- package/dist/harness/view.d.ts.map +1 -0
- package/dist/harness/view.js +189 -0
- package/dist/harness/view.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/session/observation.d.ts +18 -11
- package/dist/session/observation.d.ts.map +1 -1
- package/dist/session/observation.js +24 -13
- package/dist/session/observation.js.map +1 -1
- package/dist/session/session.d.ts +10 -1
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +23 -5
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts +12 -3
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +90 -31
- package/dist/session/transaction.js.map +1 -1
- package/dist/storage/jsonl/storage.d.ts +22 -2
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/jsonl/storage.js +1 -1
- package/dist/storage/jsonl/storage.js.map +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/database.d.ts +26 -19
- package/dist/storage/sqlite/database.d.ts.map +1 -1
- package/dist/storage/sqlite/database.js.map +1 -1
- package/dist/storage/sqlite/index.d.ts +1 -1
- package/dist/storage/sqlite/index.d.ts.map +1 -1
- package/dist/storage/sqlite/index.js.map +1 -1
- package/dist/storage/sqlite/migrations.js +7 -7
- package/dist/storage/sqlite/migrations.js.map +1 -1
- package/dist/storage/sqlite/node.d.ts +23 -7
- package/dist/storage/sqlite/node.d.ts.map +1 -1
- package/dist/storage/sqlite/node.js +129 -47
- package/dist/storage/sqlite/node.js.map +1 -1
- package/dist/storage/sqlite/storage.d.ts +12 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +121 -129
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/testing/storage-benchmark.d.ts.map +1 -1
- package/dist/testing/storage-benchmark.js +0 -1
- package/dist/testing/storage-benchmark.js.map +1 -1
- package/dist/testing/storage-conformance.d.ts.map +1 -1
- package/dist/testing/storage-conformance.js +34 -2
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/tools/bash.d.ts +28 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +70 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit-diff.d.ts +89 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +386 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +18 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +117 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/env.d.ts +5 -0
- package/dist/tools/env.d.ts.map +1 -0
- package/dist/tools/env.js +7 -0
- package/dist/tools/env.js.map +1 -0
- package/dist/tools/file-mutation-queue.d.ts +9 -0
- package/dist/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/tools/file-mutation-queue.js +52 -0
- package/dist/tools/file-mutation-queue.js.map +1 -0
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.d.ts.map +1 -0
- package/dist/tools/image.js +92 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/path-utils.d.ts +5 -0
- package/dist/tools/path-utils.d.ts.map +1 -0
- package/dist/tools/path-utils.js +26 -0
- package/dist/tools/path-utils.js.map +1 -0
- package/dist/tools/read.d.ts +17 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +101 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +31 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/{env/utils/truncate.d.ts → truncate.d.ts} +1 -17
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +146 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +97 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +32 -14
- package/dist/env/utils/adaptive-publisher.d.ts +0 -24
- package/dist/env/utils/adaptive-publisher.d.ts.map +0 -1
- package/dist/env/utils/adaptive-publisher.js +0 -79
- package/dist/env/utils/adaptive-publisher.js.map +0 -1
- package/dist/env/utils/output-capture.d.ts +0 -31
- package/dist/env/utils/output-capture.d.ts.map +0 -1
- package/dist/env/utils/output-capture.js +0 -238
- package/dist/env/utils/output-capture.js.map +0 -1
- package/dist/env/utils/truncate.d.ts.map +0 -1
- package/dist/env/utils/truncate.js +0 -294
- package/dist/env/utils/truncate.js.map +0 -1
- package/dist/harness/config.d.ts +0 -20
- package/dist/harness/config.d.ts.map +0 -1
- package/dist/harness/config.js +0 -18
- package/dist/harness/config.js.map +0 -1
- package/docs/chord-delta-findings.md +0 -358
- package/docs/pico-v5-chord-usage.md +0 -373
- package/docs/pico-v5-handoff.md +0 -581
- package/docs/pico-v5.md +0 -3084
|
@@ -1 +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"]}
|
|
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: [], contributions: [], messages: [] };\n\tconst head = bounds.head;\n\tconst range = await scanRange(storage, conversationId, bounds, context);\n\tconst edits = new Map<EntryId, ContextEdit>();\n\t// Edits of every entry in the range count, including older head markers that `selectActive()` drops.\n\tfor (const entry of range) for (const edit of entry.edits ?? []) edits.set(edit.target, edit);\n\n\tconst entries = selectActive(head, range);\n\tconst contributions = entries.map((entry): Message[] => {\n\t\tconst edit = edits.get(entry.id);\n\t\tif (edit?.action === \"omit\") return [];\n\t\tconst contributed = edit?.action === \"replace\" ? edit.messages : (entry.model ?? []);\n\t\treturn contributed.filter(\n\t\t\t(message) => message.role !== \"assistant\" || !EXCLUDED_STOP_REASONS.has(message.stopReason),\n\t\t);\n\t});\n\treturn { head, entries, contributions, messages: orderToolResults(contributions.flat()) };\n}\n\n/** The raw active entries within captured bounds, without deriving model context. */\nexport async function activeEntries(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds | undefined,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\n\tif (bounds === undefined) return [];\n\treturn selectActive(bounds.head, await scanRange(storage, conversationId, bounds, context));\n}\n\n/** Visible entries from the head marker's head, or transcript start, through the tail, oldest first. */\nasync function scanRange(\n\tstorage: Storage,\n\tconversationId: ConversationId,\n\tbounds: ContextBounds,\n\tcontext: Context,\n): Promise<EntryRecord[]> {\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\treturn range.reverse();\n}\n\n/** The head marker followed by the range's non-head entries, or the whole range without a marker. */\nfunction selectActive(head: ContextBounds[\"head\"], range: EntryRecord[]): EntryRecord[] {\n\treturn head === undefined ? range : [head, ...range.filter((entry) => entry.head === undefined)];\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 */\nexport function 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,18 @@
|
|
|
1
|
+
import type { JsonValue } from "@earendil-works/chord";
|
|
2
|
+
import type { TSchema } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { AnyTask, Extension, HookRegistration, HooksOf, PromptSection, ToolRegistration, Wrap } from "./types.ts";
|
|
4
|
+
/** Identity function that types an extension. */
|
|
5
|
+
export declare function defineExtension<Tool extends ToolRegistration = ToolRegistration>(extension: Extension<Tool>): Extension<Tool>;
|
|
6
|
+
/** Identity function that types a tool: `args` from `parameters`, details from what it reports. */
|
|
7
|
+
export declare function defineTool<TParameters extends TSchema, TDetails extends JsonValue = JsonValue>(tool: ToolRegistration<TParameters, TDetails>): ToolRegistration<TParameters, TDetails>;
|
|
8
|
+
/** A prompt section; tagged unless `tag` is false. */
|
|
9
|
+
export declare function section<Tool extends ToolRegistration = ToolRegistration>(key: string, render: PromptSection<Tool>["render"], options?: {
|
|
10
|
+
readonly tag?: boolean;
|
|
11
|
+
}): PromptSection<Tool>;
|
|
12
|
+
/** Hook handlers for tasks with `task`'s name. */
|
|
13
|
+
export declare function hook<K extends AnyTask>(task: K, handlers: Partial<HooksOf<K>>): HookRegistration;
|
|
14
|
+
/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */
|
|
15
|
+
export declare function wrapTool<Tool extends ToolRegistration>(tool: Tool, wrapper: (tool: Tool) => Tool): Wrap<Tool>;
|
|
16
|
+
/** Wrap the section `key` wherever the wrapping extension is selected. */
|
|
17
|
+
export declare function wrapSection<Tool extends ToolRegistration = ToolRegistration>(key: string, wrapper: (section: PromptSection<Tool>) => PromptSection<Tool>): Wrap<Tool>;
|
|
18
|
+
//# sourceMappingURL=define.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvH,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EAC/E,SAAS,EAAE,SAAS,CAAC,IAAI,CAAC,GACxB,SAAS,CAAC,IAAI,CAAC,CAEjB;AAED,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,WAAW,SAAS,OAAO,EAAE,QAAQ,SAAS,SAAS,GAAG,SAAS,EAC7F,IAAI,EAAE,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,GAC3C,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAEzC;AAED,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EACvE,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EACrC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAClC,aAAa,CAAC,IAAI,CAAC,CAErB;AAED,kDAAkD;AAClD,wBAAgB,IAAI,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAEhG;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,SAAS,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAE7G;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,SAAS,gBAAgB,GAAG,gBAAgB,EAC3E,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,GAC5D,IAAI,CAAC,IAAI,CAAC,CAEZ"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Identity function that types an extension. */
|
|
2
|
+
export function defineExtension(extension) {
|
|
3
|
+
return extension;
|
|
4
|
+
}
|
|
5
|
+
/** Identity function that types a tool: `args` from `parameters`, details from what it reports. */
|
|
6
|
+
export function defineTool(tool) {
|
|
7
|
+
return tool;
|
|
8
|
+
}
|
|
9
|
+
/** A prompt section; tagged unless `tag` is false. */
|
|
10
|
+
export function section(key, render, options) {
|
|
11
|
+
return options?.tag === undefined ? { key, render } : { key, render, tag: options.tag };
|
|
12
|
+
}
|
|
13
|
+
/** Hook handlers for tasks with `task`'s name. */
|
|
14
|
+
export function hook(task, handlers) {
|
|
15
|
+
return { task: task.definition.name, handlers };
|
|
16
|
+
}
|
|
17
|
+
/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */
|
|
18
|
+
export function wrapTool(tool, wrapper) {
|
|
19
|
+
return { tool: tool.name, wrap: wrapper };
|
|
20
|
+
}
|
|
21
|
+
/** Wrap the section `key` wherever the wrapping extension is selected. */
|
|
22
|
+
export function wrapSection(key, wrapper) {
|
|
23
|
+
return { section: key, wrap: wrapper };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=define.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/harness/define.ts"],"names":[],"mappings":"AAIA,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAC9B,SAA0B;IAE1B,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,UAAU,CACzB,IAA6C;IAE7C,OAAO,IAAI,CAAC;AACb,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,OAAO,CACtB,GAAW,EACX,MAAqC,EACrC,OAAoC;IAEpC,OAAO,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;AACzF,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,IAAI,CAAoB,IAAO,EAAE,QAA6B;IAC7E,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;AACjD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAgC,IAAU,EAAE,OAA6B;IAChG,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAC1B,GAAW,EACX,OAA8D;IAE9D,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import type { JsonValue } from \"@earendil-works/chord\";\nimport type { TSchema } from \"@earendil-works/pi-ai\";\nimport type { AnyTask, Extension, HookRegistration, HooksOf, PromptSection, ToolRegistration, Wrap } from \"./types.ts\";\n\n/** Identity function that types an extension. */\nexport function defineExtension<Tool extends ToolRegistration = ToolRegistration>(\n\textension: Extension<Tool>,\n): Extension<Tool> {\n\treturn extension;\n}\n\n/** Identity function that types a tool: `args` from `parameters`, details from what it reports. */\nexport function defineTool<TParameters extends TSchema, TDetails extends JsonValue = JsonValue>(\n\ttool: ToolRegistration<TParameters, TDetails>,\n): ToolRegistration<TParameters, TDetails> {\n\treturn tool;\n}\n\n/** A prompt section; tagged unless `tag` is false. */\nexport function section<Tool extends ToolRegistration = ToolRegistration>(\n\tkey: string,\n\trender: PromptSection<Tool>[\"render\"],\n\toptions?: { readonly tag?: boolean },\n): PromptSection<Tool> {\n\treturn options?.tag === undefined ? { key, render } : { key, render, tag: options.tag };\n}\n\n/** Hook handlers for tasks with `task`'s name. */\nexport function hook<K extends AnyTask>(task: K, handlers: Partial<HooksOf<K>>): HookRegistration {\n\treturn { task: task.definition.name, handlers };\n}\n\n/** Wrap the tool named like `tool` wherever the wrapping extension is selected. */\nexport function wrapTool<Tool extends ToolRegistration>(tool: Tool, wrapper: (tool: Tool) => Tool): Wrap<Tool> {\n\treturn { tool: tool.name, wrap: wrapper };\n}\n\n/** Wrap the section `key` wherever the wrapping extension is selected. */\nexport function wrapSection<Tool extends ToolRegistration = ToolRegistration>(\n\tkey: string,\n\twrapper: (section: PromptSection<Tool>) => PromptSection<Tool>,\n): Wrap<Tool> {\n\treturn { section: key, wrap: wrapper };\n}\n"]}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { Context, JsonValue } from "@earendil-works/chord";
|
|
2
|
+
import type { AssistantMessage, Message, Usage } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { ConversationId, EntryRecord, JsonObject, SubmissionId, SubmissionRecord, TaskId, WatchEnd } from "../types.ts";
|
|
4
|
+
import type { InboxItem } from "./inbox.ts";
|
|
5
|
+
import type { CompactionStatus, ToolSlot } from "./live.ts";
|
|
6
|
+
import type { AgentState, CompactionReason, Harness, ToolDiagnostic } from "./types.ts";
|
|
7
|
+
import { type UsageState } from "./usage.ts";
|
|
8
|
+
type Block = AssistantMessage["content"][number];
|
|
9
|
+
type QueuedItem = {
|
|
10
|
+
id: SubmissionId;
|
|
11
|
+
mode: InboxItem["mode"];
|
|
12
|
+
};
|
|
13
|
+
/** One change to the in-flight assistant message, relative to that message. */
|
|
14
|
+
export type MessageChange = {
|
|
15
|
+
type: "text_start" | "thinking_start" | "toolcall_start";
|
|
16
|
+
contentIndex: number;
|
|
17
|
+
block: Block;
|
|
18
|
+
} | {
|
|
19
|
+
type: "text_delta" | "thinking_delta";
|
|
20
|
+
contentIndex: number;
|
|
21
|
+
delta: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: "toolcall_delta";
|
|
24
|
+
contentIndex: number;
|
|
25
|
+
path: readonly (string | number)[];
|
|
26
|
+
delta: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: "block";
|
|
29
|
+
contentIndex: number;
|
|
30
|
+
block: Block;
|
|
31
|
+
} | {
|
|
32
|
+
type: "message";
|
|
33
|
+
message: AssistantMessage;
|
|
34
|
+
};
|
|
35
|
+
export type SnapshotEvent = {
|
|
36
|
+
type: "snapshot";
|
|
37
|
+
entries: readonly EntryRecord[];
|
|
38
|
+
run?: {
|
|
39
|
+
inputs: readonly SubmissionId[];
|
|
40
|
+
};
|
|
41
|
+
/** Current generation attempt: its in-flight partial, retry backoff, or deferred poll. */
|
|
42
|
+
generation?: {
|
|
43
|
+
attempt: number;
|
|
44
|
+
message?: AssistantMessage;
|
|
45
|
+
retry?: {
|
|
46
|
+
at: number;
|
|
47
|
+
error: string;
|
|
48
|
+
};
|
|
49
|
+
deferred?: {
|
|
50
|
+
pollAt: number;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
tools: readonly ToolSlot[];
|
|
54
|
+
/** `pi.live.compactions`: live compactions with their attempt and retry backoff. */
|
|
55
|
+
compactions: readonly CompactionStatus[];
|
|
56
|
+
inbox: readonly QueuedItem[];
|
|
57
|
+
/** `pi.agent`; `{}` when absent. */
|
|
58
|
+
agent: AgentState;
|
|
59
|
+
usage: UsageState;
|
|
60
|
+
};
|
|
61
|
+
/** Experimental agent event, shaped like the coding agent's session events (spec §9.4). */
|
|
62
|
+
export type AgentEvent = SnapshotEvent | {
|
|
63
|
+
type: "run_start";
|
|
64
|
+
inputs: readonly SubmissionId[];
|
|
65
|
+
} | {
|
|
66
|
+
type: "run_end";
|
|
67
|
+
inputs: readonly SubmissionId[];
|
|
68
|
+
} | {
|
|
69
|
+
type: "turn_start";
|
|
70
|
+
} | {
|
|
71
|
+
type: "turn_end";
|
|
72
|
+
} | {
|
|
73
|
+
type: "message_start";
|
|
74
|
+
message: Message;
|
|
75
|
+
}
|
|
76
|
+
/** `usage` is the partial's current usage, as in the coding agent's JSON mode. */
|
|
77
|
+
| {
|
|
78
|
+
type: "message_update";
|
|
79
|
+
usage: Usage;
|
|
80
|
+
changes: readonly MessageChange[];
|
|
81
|
+
} | {
|
|
82
|
+
type: "message_end";
|
|
83
|
+
entry: EntryRecord;
|
|
84
|
+
} | {
|
|
85
|
+
type: "tool_execution_start";
|
|
86
|
+
toolCallId: string;
|
|
87
|
+
toolName: string;
|
|
88
|
+
args: JsonObject;
|
|
89
|
+
} | {
|
|
90
|
+
type: "tool_execution_update";
|
|
91
|
+
toolCallId: string;
|
|
92
|
+
toolName: string;
|
|
93
|
+
/** A front trim and then an append of the retained window, or its replacement. */
|
|
94
|
+
output?: {
|
|
95
|
+
trimStart?: number;
|
|
96
|
+
append?: string;
|
|
97
|
+
} | {
|
|
98
|
+
set: string;
|
|
99
|
+
};
|
|
100
|
+
details?: JsonValue;
|
|
101
|
+
diagnostics?: readonly ToolDiagnostic[];
|
|
102
|
+
}
|
|
103
|
+
/** `entry` is absent when the tool task faulted or was orphaned. */
|
|
104
|
+
| {
|
|
105
|
+
type: "tool_execution_end";
|
|
106
|
+
toolCallId: string;
|
|
107
|
+
toolName: string;
|
|
108
|
+
entry?: EntryRecord;
|
|
109
|
+
} | {
|
|
110
|
+
type: "inbox_update";
|
|
111
|
+
items: readonly QueuedItem[];
|
|
112
|
+
} | {
|
|
113
|
+
type: "submission";
|
|
114
|
+
record: SubmissionRecord;
|
|
115
|
+
} | {
|
|
116
|
+
type: "auto_retry_start";
|
|
117
|
+
attempt: number;
|
|
118
|
+
at: number;
|
|
119
|
+
errorMessage: string;
|
|
120
|
+
} | {
|
|
121
|
+
type: "auto_retry_end";
|
|
122
|
+
attempt: number;
|
|
123
|
+
} | {
|
|
124
|
+
type: "deferred_poll";
|
|
125
|
+
pollAt: number;
|
|
126
|
+
} | {
|
|
127
|
+
type: "entry_appended";
|
|
128
|
+
entry: EntryRecord;
|
|
129
|
+
} | {
|
|
130
|
+
type: "agent_changed";
|
|
131
|
+
agent: AgentState;
|
|
132
|
+
} | {
|
|
133
|
+
type: "usage_changed";
|
|
134
|
+
usage: UsageState;
|
|
135
|
+
} | {
|
|
136
|
+
type: "task_failed";
|
|
137
|
+
taskId: TaskId;
|
|
138
|
+
kind: string;
|
|
139
|
+
message: string;
|
|
140
|
+
} | {
|
|
141
|
+
type: "compaction_start";
|
|
142
|
+
taskId: TaskId;
|
|
143
|
+
reason: CompactionReason;
|
|
144
|
+
blocking: boolean;
|
|
145
|
+
}
|
|
146
|
+
/** The task's receipt tells whether it produced a summary; the summary entry has its own events. */
|
|
147
|
+
| {
|
|
148
|
+
type: "compaction_end";
|
|
149
|
+
taskId: TaskId;
|
|
150
|
+
reason: CompactionReason;
|
|
151
|
+
};
|
|
152
|
+
/** Serialized stream of one conversation's event batches, one per commit. */
|
|
153
|
+
export interface AgentEventStream {
|
|
154
|
+
/** The `snapshot` event at attachment. */
|
|
155
|
+
readonly snapshot: SnapshotEvent;
|
|
156
|
+
start(listener: (events: readonly AgentEvent[], context: Context) => Promise<void>): void;
|
|
157
|
+
stop(): Promise<WatchEnd>;
|
|
158
|
+
readonly closed: Promise<WatchEnd>;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Experimental: attach to one conversation's agent events (spec §9.4). The snapshot and the registration for later
|
|
162
|
+
* commits are captured atomically on the Session line; overflow replaces undelivered batches with one snapshot.
|
|
163
|
+
*/
|
|
164
|
+
export declare function watchEvents(harness: Harness, conversationId: ConversationId, context: Context): Promise<AgentEventStream>;
|
|
165
|
+
export {};
|
|
166
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/harness/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { CommittedWatch } from "../session/observation.js";
|
|
2
|
+
import { AgentDoc } from "./agent.js";
|
|
3
|
+
import { UsageDoc } from "./usage.js";
|
|
4
|
+
import { scanAll } from "./util.js";
|
|
5
|
+
import { conversationViews } from "./view.js";
|
|
6
|
+
function parts(view) {
|
|
7
|
+
return {
|
|
8
|
+
live: (view.docs["pi.live"] ?? {}),
|
|
9
|
+
inbox: view.docs["pi.inbox"],
|
|
10
|
+
agent: view.docs["pi.agent"],
|
|
11
|
+
usage: view.docs["pi.usage"],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function snapshotOf(view) {
|
|
15
|
+
const { live, inbox, agent, usage } = parts(view);
|
|
16
|
+
return {
|
|
17
|
+
type: "snapshot",
|
|
18
|
+
entries: view.entries,
|
|
19
|
+
...(live.run === undefined ? {} : { run: { inputs: live.run.inputs } }),
|
|
20
|
+
...(live.generation === undefined ? {} : { generation: live.generation }),
|
|
21
|
+
tools: live.tools ?? [],
|
|
22
|
+
compactions: live.compactions ?? [],
|
|
23
|
+
inbox: queued(inbox),
|
|
24
|
+
agent: agent ?? AgentDoc.definition.initial(),
|
|
25
|
+
usage: usage ?? UsageDoc.definition.initial(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function queued(inbox) {
|
|
29
|
+
return (inbox?.items ?? []).map((item) => ({ id: item.id, mode: item.mode }));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Experimental: attach to one conversation's agent events (spec §9.4). The snapshot and the registration for later
|
|
33
|
+
* commits are captured atomically on the Session line; overflow replaces undelivered batches with one snapshot.
|
|
34
|
+
*/
|
|
35
|
+
export async function watchEvents(harness, conversationId, context) {
|
|
36
|
+
let watch;
|
|
37
|
+
let snapshot;
|
|
38
|
+
await conversationViews(harness).attach(conversationId, async (initial, release, storage) => {
|
|
39
|
+
// Generations whose held outcome already ended their turn, read on the line with the snapshot.
|
|
40
|
+
const query = { conversationId, kind: "pi.generation", status: "completing" };
|
|
41
|
+
const completing = await scanAll((cursor) => storage.scanTasks(query, 100, cursor, context));
|
|
42
|
+
const held = new Set(completing.map((record) => record.id));
|
|
43
|
+
let current = initial;
|
|
44
|
+
snapshot = snapshotOf(initial);
|
|
45
|
+
// Batches are the watch's values; an overflow delivers a snapshot of the newest view instead.
|
|
46
|
+
watch = new CommittedWatch([], release, () => [snapshotOf(current)]);
|
|
47
|
+
return {
|
|
48
|
+
publication: (before, after, ops, publication, commitContext) => {
|
|
49
|
+
current = after;
|
|
50
|
+
const events = translate(conversationId, before, after, ops, publication, held);
|
|
51
|
+
if (events.length > 0)
|
|
52
|
+
watch.advance(events, [], commitContext);
|
|
53
|
+
},
|
|
54
|
+
closeSession: () => watch.closeSession(),
|
|
55
|
+
};
|
|
56
|
+
}, context);
|
|
57
|
+
// Like a watch, the acquisition context governs the stream's lifetime.
|
|
58
|
+
const signal = context.abortSignal;
|
|
59
|
+
if (signal?.aborted) {
|
|
60
|
+
watch.cancel();
|
|
61
|
+
throw signal.reason;
|
|
62
|
+
}
|
|
63
|
+
if (signal !== undefined)
|
|
64
|
+
watch.observeCancellation(signal);
|
|
65
|
+
return {
|
|
66
|
+
snapshot,
|
|
67
|
+
start: (listener) => watch.start((events, _ops, deliveryContext) => listener(events, deliveryContext)),
|
|
68
|
+
stop: () => watch.stop(),
|
|
69
|
+
closed: watch.closed,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** The tool result for `callId` among `entries`. */
|
|
73
|
+
function resultOf(entries, callId) {
|
|
74
|
+
return entries.find((entry) => {
|
|
75
|
+
const message = entry.model?.[0];
|
|
76
|
+
return message?.role === "toolResult" && message.toolCallId === callId;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Every event one publication causes, in the order of spec §9.4. */
|
|
80
|
+
function translate(conversationId, before, after, viewOps, publication, held) {
|
|
81
|
+
const entries = [];
|
|
82
|
+
const tasks = new Map();
|
|
83
|
+
const submissions = [];
|
|
84
|
+
for (const change of publication.changes) {
|
|
85
|
+
if (change.type === "entry" && change.value.conversationId === conversationId)
|
|
86
|
+
entries.push(change.value);
|
|
87
|
+
if (change.type === "task" && change.value.conversationId === conversationId)
|
|
88
|
+
tasks.set(change.value.id, change.value);
|
|
89
|
+
if (change.type === "submission" && change.value.conversationId === conversationId) {
|
|
90
|
+
submissions.push(change.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (viewOps.length === 0 && entries.length === 0 && tasks.size === 0 && submissions.length === 0)
|
|
94
|
+
return [];
|
|
95
|
+
// Entries are appended in ID order; submission records are published in the order the commit first touched them.
|
|
96
|
+
submissions.sort((a, b) => a.id - b.id);
|
|
97
|
+
const was = parts(before);
|
|
98
|
+
const now = parts(after);
|
|
99
|
+
const events = [];
|
|
100
|
+
// Progress: tool starts, the in-flight message, tool updates, retry and deferred state.
|
|
101
|
+
const slotsBefore = new Map((was.live.tools ?? []).map((slot) => [slot.callId, slot]));
|
|
102
|
+
const slots = now.live.tools ?? [];
|
|
103
|
+
for (const slot of slots) {
|
|
104
|
+
if (slot.status !== "running" || slotsBefore.get(slot.callId)?.status === "running")
|
|
105
|
+
continue;
|
|
106
|
+
const checkpoint = slot.taskId === undefined ? undefined : tasks.get(slot.taskId)?.state.checkpoint;
|
|
107
|
+
const args = checkpoint?.arguments ?? {};
|
|
108
|
+
events.push({ type: "tool_execution_start", toolCallId: slot.callId, toolName: slot.name, args });
|
|
109
|
+
}
|
|
110
|
+
const partialBefore = was.live.generation?.message;
|
|
111
|
+
const partial = now.live.generation?.message;
|
|
112
|
+
if (partial !== undefined && partialBefore === undefined)
|
|
113
|
+
events.push({ type: "message_start", message: partial });
|
|
114
|
+
else if (partial !== undefined && partial !== partialBefore) {
|
|
115
|
+
events.push({ type: "message_update", usage: partial.usage, changes: messageChanges(viewOps, partial) });
|
|
116
|
+
}
|
|
117
|
+
for (const [index, slot] of slots.entries()) {
|
|
118
|
+
const previous = slotsBefore.get(slot.callId);
|
|
119
|
+
if (slot.status !== "running" || previous?.status !== "running")
|
|
120
|
+
continue;
|
|
121
|
+
const update = toolUpdate(viewOps, index, slot, previous);
|
|
122
|
+
if (update === undefined)
|
|
123
|
+
continue;
|
|
124
|
+
events.push({ type: "tool_execution_update", toolCallId: slot.callId, toolName: slot.name, ...update });
|
|
125
|
+
}
|
|
126
|
+
const generation = now.live.generation;
|
|
127
|
+
const generationBefore = was.live.generation;
|
|
128
|
+
if (generation?.retry !== undefined && generationBefore?.retry === undefined) {
|
|
129
|
+
const { at, error } = generation.retry;
|
|
130
|
+
events.push({ type: "auto_retry_start", attempt: generation.attempt, at, errorMessage: error });
|
|
131
|
+
}
|
|
132
|
+
if (generationBefore?.retry !== undefined && generation?.retry === undefined) {
|
|
133
|
+
events.push({ type: "auto_retry_end", attempt: generationBefore.attempt });
|
|
134
|
+
}
|
|
135
|
+
if (generation?.deferred !== undefined && generation.deferred.pollAt !== generationBefore?.deferred?.pollAt) {
|
|
136
|
+
events.push({ type: "deferred_poll", pollAt: generation.deferred.pollAt });
|
|
137
|
+
}
|
|
138
|
+
// Tools that end in this commit: a slot that becomes done, one created done (a call not offered), or an unfinished
|
|
139
|
+
// one that vanishes because its run ended. A done slot that vanishes ended earlier.
|
|
140
|
+
const toolEnds = [];
|
|
141
|
+
const endTool = (callId, name, entryId) => {
|
|
142
|
+
const entry = entries.find((candidate) => candidate.id === entryId);
|
|
143
|
+
toolEnds.push({
|
|
144
|
+
type: "tool_execution_end",
|
|
145
|
+
toolCallId: callId,
|
|
146
|
+
toolName: name,
|
|
147
|
+
...(entry === undefined ? {} : { entry }),
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
for (const previous of slotsBefore.values()) {
|
|
151
|
+
if (previous.status === "done")
|
|
152
|
+
continue;
|
|
153
|
+
const slot = slots.find((candidate) => candidate.callId === previous.callId);
|
|
154
|
+
if (slot?.status === "done")
|
|
155
|
+
endTool(previous.callId, previous.name, slot.entry);
|
|
156
|
+
// A slot whose run ended in this commit may have had its result appended with it, as for unstarted calls.
|
|
157
|
+
else if (slot === undefined)
|
|
158
|
+
endTool(previous.callId, previous.name, resultOf(entries, previous.callId)?.id);
|
|
159
|
+
}
|
|
160
|
+
for (const slot of slots) {
|
|
161
|
+
if (slot.status === "done" && !slotsBefore.has(slot.callId))
|
|
162
|
+
endTool(slot.callId, slot.name, slot.entry);
|
|
163
|
+
}
|
|
164
|
+
// Entries in append order; a tool's end directly precedes its result's message, as in the coding agent.
|
|
165
|
+
let assistantAppended = false;
|
|
166
|
+
for (const entry of entries) {
|
|
167
|
+
events.push(...toolEnds.filter((end) => end.entry === entry));
|
|
168
|
+
const message = entry.model?.[0];
|
|
169
|
+
if (message === undefined) {
|
|
170
|
+
events.push({ type: "entry_appended", entry });
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
// A streamed answer already started with its first partial.
|
|
174
|
+
const streamed = message.role === "assistant" && partialBefore !== undefined && !assistantAppended;
|
|
175
|
+
if (message.role === "assistant")
|
|
176
|
+
assistantAppended = true;
|
|
177
|
+
if (!streamed)
|
|
178
|
+
events.push({ type: "message_start", message });
|
|
179
|
+
events.push({ type: "message_end", entry });
|
|
180
|
+
}
|
|
181
|
+
// Ends without a result entry: a faulted or orphaned tool, or one whose run ended.
|
|
182
|
+
events.push(...toolEnds.filter((end) => end.entry === undefined));
|
|
183
|
+
// Compaction ends, task failures, then turn and run ends.
|
|
184
|
+
const compactionsBefore = was.live.compactions ?? [];
|
|
185
|
+
const compactions = now.live.compactions ?? [];
|
|
186
|
+
for (const { taskId, reason } of compactionsBefore) {
|
|
187
|
+
if (!compactions.some((status) => status.taskId === taskId)) {
|
|
188
|
+
events.push({ type: "compaction_end", taskId, reason });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// A generation's turn ends when its outcome is committed: at a `completing` hold or at terminal, whichever comes
|
|
192
|
+
// first, so a successor created at the hold starts after it.
|
|
193
|
+
let turnEnded = false;
|
|
194
|
+
for (const task of tasks.values()) {
|
|
195
|
+
const status = task.state.status;
|
|
196
|
+
if (task.kind === "pi.generation" && status === "completing" && !held.has(task.id)) {
|
|
197
|
+
held.add(task.id);
|
|
198
|
+
turnEnded = true;
|
|
199
|
+
}
|
|
200
|
+
if (status !== "terminal")
|
|
201
|
+
continue;
|
|
202
|
+
if (task.kind === "pi.generation" && !held.delete(task.id))
|
|
203
|
+
turnEnded = true;
|
|
204
|
+
const outcome = task.state.outcome;
|
|
205
|
+
if (outcome.status === "faulted" || outcome.status === "orphaned") {
|
|
206
|
+
const message = outcome.status === "faulted" ? outcome.error.message : outcome.reason;
|
|
207
|
+
events.push({ type: "task_failed", taskId: task.id, kind: task.kind, message });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (turnEnded)
|
|
211
|
+
events.push({ type: "turn_end" });
|
|
212
|
+
const run = now.live.run;
|
|
213
|
+
const runBefore = was.live.run;
|
|
214
|
+
const runChanged = run?.inputs[0] !== runBefore?.inputs[0];
|
|
215
|
+
if (runBefore !== undefined && runChanged)
|
|
216
|
+
events.push({ type: "run_end", inputs: runBefore.inputs });
|
|
217
|
+
// Submissions, document state, then what began.
|
|
218
|
+
for (const record of submissions)
|
|
219
|
+
events.push({ type: "submission", record });
|
|
220
|
+
if (now.inbox !== was.inbox)
|
|
221
|
+
events.push({ type: "inbox_update", items: queued(now.inbox) });
|
|
222
|
+
// A retired document reads as its initial value, as in a snapshot.
|
|
223
|
+
if (now.agent !== was.agent) {
|
|
224
|
+
events.push({ type: "agent_changed", agent: now.agent ?? AgentDoc.definition.initial() });
|
|
225
|
+
}
|
|
226
|
+
if (now.usage !== was.usage)
|
|
227
|
+
events.push({ type: "usage_changed", usage: now.usage ?? UsageDoc.definition.initial() });
|
|
228
|
+
for (const { taskId, reason, blocking } of compactions) {
|
|
229
|
+
if (!compactionsBefore.some((status) => status.taskId === taskId)) {
|
|
230
|
+
events.push({ type: "compaction_start", taskId, reason, blocking });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (run !== undefined && runChanged)
|
|
234
|
+
events.push({ type: "run_start", inputs: run.inputs });
|
|
235
|
+
if (run !== undefined && run.taskId !== runBefore?.taskId && tasks.get(run.taskId)?.kind === "pi.generation") {
|
|
236
|
+
events.push({ type: "turn_start" });
|
|
237
|
+
}
|
|
238
|
+
return events;
|
|
239
|
+
}
|
|
240
|
+
const PARTIAL_PATH = ["docs", "pi.live", "generation", "message"];
|
|
241
|
+
/** Translate the view operations on the in-flight message into message changes (spec §9.4). */
|
|
242
|
+
function messageChanges(viewOps, message) {
|
|
243
|
+
const changes = [];
|
|
244
|
+
// A block sent whole already holds every later change to it in this batch.
|
|
245
|
+
const whole = new Set();
|
|
246
|
+
for (const op of viewOps) {
|
|
247
|
+
// View operations never replace the root.
|
|
248
|
+
const path = op[1];
|
|
249
|
+
if (!startsWith(path, PARTIAL_PATH)) {
|
|
250
|
+
// The whole message or generation was replaced.
|
|
251
|
+
if (startsWith(PARTIAL_PATH, path))
|
|
252
|
+
return [{ type: "message", message }];
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const rest = path.slice(PARTIAL_PATH.length);
|
|
256
|
+
if (rest[0] === "usage")
|
|
257
|
+
continue;
|
|
258
|
+
if (rest[0] !== "content")
|
|
259
|
+
return [{ type: "message", message }];
|
|
260
|
+
if (rest.length === 1) {
|
|
261
|
+
if (op[0] !== "p" || op[3] !== 0)
|
|
262
|
+
return [{ type: "message", message }];
|
|
263
|
+
for (const [offset, block] of op[4].entries()) {
|
|
264
|
+
const type = block.type === "text" ? "text_start" : block.type === "thinking" ? "thinking_start" : "toolcall_start";
|
|
265
|
+
changes.push({ type, contentIndex: op[2] + offset, block });
|
|
266
|
+
}
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const contentIndex = rest[1];
|
|
270
|
+
const field = rest[2];
|
|
271
|
+
if (whole.has(contentIndex))
|
|
272
|
+
continue;
|
|
273
|
+
if (op[0] === "a" && rest.length === 3 && (field === "text" || field === "thinking")) {
|
|
274
|
+
changes.push({ type: field === "text" ? "text_delta" : "thinking_delta", contentIndex, delta: op[2] });
|
|
275
|
+
}
|
|
276
|
+
else if (op[0] === "a" && field === "arguments") {
|
|
277
|
+
changes.push({ type: "toolcall_delta", contentIndex, path: rest.slice(3), delta: op[2] });
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
whole.add(contentIndex);
|
|
281
|
+
changes.push({ type: "block", contentIndex, block: message.content[contentIndex] });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return changes;
|
|
285
|
+
}
|
|
286
|
+
/** Output, details, and diagnostics changes of a running slot, from the view operations on it. */
|
|
287
|
+
function toolUpdate(viewOps, index, slot, previous) {
|
|
288
|
+
const outputPath = ["docs", "pi.live", "tools", index, "output"];
|
|
289
|
+
let trimStart = 0;
|
|
290
|
+
let append = "";
|
|
291
|
+
let set = false;
|
|
292
|
+
for (const op of viewOps) {
|
|
293
|
+
if (!startsWith(op[1], outputPath))
|
|
294
|
+
continue;
|
|
295
|
+
if (op[0] === "t")
|
|
296
|
+
trimStart += op[2];
|
|
297
|
+
else if (op[0] === "a")
|
|
298
|
+
append += op[2];
|
|
299
|
+
else
|
|
300
|
+
set = true;
|
|
301
|
+
}
|
|
302
|
+
let output;
|
|
303
|
+
if (set || (slot.output !== previous.output && trimStart === 0 && append === ""))
|
|
304
|
+
output = { set: slot.output ?? "" };
|
|
305
|
+
else if (trimStart > 0 || append !== "") {
|
|
306
|
+
output = { ...(trimStart > 0 ? { trimStart } : {}), ...(append === "" ? {} : { append }) };
|
|
307
|
+
}
|
|
308
|
+
// A safe replay clears a running slot's progress: removed details send `null`, removed diagnostics `[]`.
|
|
309
|
+
const update = {
|
|
310
|
+
...(output === undefined ? {} : { output }),
|
|
311
|
+
...(slot.details === previous.details ? {} : { details: slot.details ?? null }),
|
|
312
|
+
...(slot.diagnostics === previous.diagnostics ? {} : { diagnostics: slot.diagnostics ?? [] }),
|
|
313
|
+
};
|
|
314
|
+
return Object.keys(update).length === 0 ? undefined : update;
|
|
315
|
+
}
|
|
316
|
+
function startsWith(path, prefix) {
|
|
317
|
+
return prefix.length <= path.length && prefix.every((segment, index) => path[index] === segment);
|
|
318
|
+
}
|
|
319
|
+
//# sourceMappingURL=events.js.map
|