From d5de1ad7c978bdae9431f6ee42fe7c1c9085ab55 Mon Sep 17 00:00:00 2001 From: Surma Date: Sat, 7 Nov 2026 13:50:09 +0000 Subject: [PATCH] Use shared models service for subagents and bundle every JS test --- extensions/models/README.md | 3 + extensions/models/api.ts | 3 +- extensions/models/server.ts | 20 +- extensions/subagents/children.ts | 32 +- extensions/subagents/definitions.ts | 17 - extensions/subagents/server.ts | 20 +- host/js/chain-test-bundle.js | 26 + host/js/core-ext-test-bundle.js | 1158 +++++++++++++++----------- host/js/package.json | 2 +- host/js/plan-todos-test-bundle.js | 1164 ++++++++++++++++----------- host/js/subagents-test-bundle.js | 364 +++++---- host/js/subagents-test.ts | 21 + host/tests/subagents.rs | 27 +- 13 files changed, 1661 insertions(+), 1196 deletions(-) delete mode 100644 extensions/subagents/definitions.ts diff --git a/extensions/models/README.md b/extensions/models/README.md index 4986bb0..664a0e5 100644 --- a/extensions/models/README.md +++ b/extensions/models/README.md @@ -3,6 +3,9 @@ The `models` service starts a conversation with `start({agent, cwd, ownership?}, context)`. The `agent` field names a built-in definition or a project overlay. The service stores that name in `models.profile`. +`discover(cwd)` exposes the same live definition resolver to subagents. +`configure(tx, id, {agent, inheritedTools, excludeTools?, model?, thinking?})` configures an existing child in its creation transaction. +It retains the parent's tool scope, explicit overrides, and exclusions for subsequent run boundaries. It applies the definition during creation and before each new run. Definitions without an explicit model follow their family, then the default family. diff --git a/extensions/models/api.ts b/extensions/models/api.ts index 56d96e9..7b031a8 100644 --- a/extensions/models/api.ts +++ b/extensions/models/api.ts @@ -3,5 +3,6 @@ import { defineDoc } from '@earendil-works/pi-durable' export const Profile = defineDoc({kind:'models.profile',version:1,scope:'conversation',history:'latest',fork:'current',initial:()=>({}),checkpointWhen:()=>false}) /** Optional external pin provider. The service returns a logical provider/modelId reference. */ export const Pin = {name:'models.pin'} -/** start({agent, cwd, ownership?}, context) creates a definition-based conversation. */ +/** discover(cwd) reads definitions. configure(tx, id, options) applies a definition to an existing child. + * start({agent, cwd, ownership?}, context) creates a definition-based conversation. */ export const ModelsApi = {name:'models'} diff --git a/extensions/models/server.ts b/extensions/models/server.ts index 2bbdf19..1b06731 100644 --- a/extensions/models/server.ts +++ b/extensions/models/server.ts @@ -45,21 +45,33 @@ export default function models(host: any) { if (profile.agent && !definition) throw new Error(`Unknown agent ${profile.agent}: ${JSON.stringify(config.errors)}`) const selected = definition?.runs ?? definitionRuns({} as any,config.families,Object.values(config.families).map(spec=>spec.model.replace(/:(off|minimal|low|medium|high|xhigh|max)$/, ''))) const pinned = await host.want(Pin)?.pin(tx, id) - const ref = pinned ?? selected.model + const ref = pinned ?? profile.model ?? selected.model register(ref) state.model = ref - if (selected.thinking) state.thinkingLevel = selected.thinking + if (profile.thinking ?? selected.thinking) state.thinkingLevel = profile.thinking ?? selected.thinking else delete state.thinkingLevel if (definition) { state.instructions = definition.instructions if (profile.inheritedTools === undefined) profile.inheritedTools = state.tools ?? null const inherited = profile.inheritedTools - if (definition.tools || Array.isArray(inherited)) state.tools = toolsFor(definition,inherited ?? []) - else state.tools = {remove:[...new Set([...(inherited?.remove ?? []),...(definition.denyTools ?? [])])]} + if (definition.tools || Array.isArray(inherited)) state.tools = toolsFor(definition,inherited ?? []).filter(name=>!profile.excludeTools?.includes(name)) + else state.tools = {remove:[...new Set([...(inherited?.remove ?? []),...(definition.denyTools ?? []),...(profile.excludeTools ?? [])])]} } return ref } host.implement(ModelsApi, { + discover: read, + configure: async (tx: any, id: number, options: any) => { + const profile = await tx.doc(Profile,id) + profile.agent = options.agent + profile.inheritedTools = options.inheritedTools + profile.excludeTools = options.excludeTools ?? [] + if (options.model) profile.model = options.model + else delete profile.model + if (options.thinking) profile.thinking = options.thinking + else delete profile.thinking + return apply(tx,id) + }, resolve: apply, start: async ({agent, cwd, ownership = {kind:'ownerless'}}: any, context: any) => { const conversation = await host.harness().createConversation({ownership, agent:{cwd}, init:async (tx: any, id: number)=>{ diff --git a/extensions/subagents/children.ts b/extensions/subagents/children.ts index 30f6952..6c6d94f 100644 --- a/extensions/subagents/children.ts +++ b/extensions/subagents/children.ts @@ -1,6 +1,6 @@ /** Child configuration and start events share the transaction that creates ownership. */ import { AgentDoc, configure } from '@earendil-works/pi-durable' -import { toolsFor } from '../core/agents.ts' +import { ModelsApi } from '../models/api.ts' import { Started } from './refs.ts' import { Profile } from './api.ts' export async function started(host: any, tx: any, conversation: any, agent: string, kind: 'child'|'standing'|'branch') { @@ -11,27 +11,35 @@ export async function started(host: any, tx: any, conversation: any, agent: stri ...(thinkingLevel ? {thinking:thinkingLevel} : {}), ...(parent === undefined ? {} : {parent})}) } export async function createChild(host: any, tx: any, options: any) { - const {parentAgent:parent, definition} = options + const {definition} = options const child = options.fork ? await tx.forkConversation(options.fork.conversationId, options.fork.at, {ownership:options.owner}) : await tx.createConversation({ownership:options.owner}) - await configure(tx, child.id, childConfiguration(options)) + await configureChild(host, tx, child.id, options) Object.assign(await tx.doc(host.metadata, child.id), {label:options.label, title:options.title}) if (definition) (await tx.doc(Profile, child.id)).definition = definition.name if (definition?.summaries === false) await host.want({name:'conversations'})?.setAutomaticSummaries(tx, child.id, false) return child } -/** Standing refreshes and new children apply the same definition and tool denials. */ -export function childConfiguration(options: any) { - const {parentAgent:parent, definition} = options - const inherited = parent.tools.map((tool: any) => tool.name) - const names = (definition ? toolsFor(definition, inherited) : inherited).filter((name: string) => !options.excludeTools?.includes(name)) +/** Standing refreshes and new children share the models service and persistent definition profile. */ +export async function configureChild(host: any, tx: any, id: number, options: any) { + await configure(tx, id, childConfiguration(options)) + if (options.definition) await host.need(ModelsApi).configure(tx, id, { + agent:options.definition.name, + inheritedTools:options.parentAgent.tools.map((tool: any)=>tool.name), + excludeTools:options.excludeTools, + model:options.model, + thinking:options.thinking, + }) +} +function childConfiguration(options: any) { + const {parentAgent:parent} = options return { - tools:parent.tools.filter((tool: any) => names.includes(tool.name)), + tools:parent.tools.filter((tool: any) => !options.excludeTools?.includes(tool.name)), extensions:parent.extensions.filter((extension: any) => !options.excludeExtensions?.includes(extension.name)), - instructions:definition?.instructions ?? parent.instructions ?? null, - model:options.model ?? definition?.runs?.model ?? parent.model ?? null, - thinkingLevel:options.thinking ?? definition?.runs?.thinking ?? definition?.thinking ?? parent.thinkingLevel ?? null, + instructions:parent.instructions ?? null, + model:options.model ?? parent.model ?? null, + thinkingLevel:options.thinking ?? parent.thinkingLevel ?? null, cwd:options.cwd ?? parent.cwd ?? '/src/sausage', } } diff --git a/extensions/subagents/definitions.ts b/extensions/subagents/definitions.ts deleted file mode 100644 index 37f1b97..0000000 --- a/extensions/subagents/definitions.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** Temporary world-file resolver until the models service supplies definitions. */ -import { discoverAgents, parseFamilies } from '../core/agents.ts' -export async function discover(host: any, cwd = '/src/sausage') { - const root = '/src/sausage' - const families = parseFamilies(await host.fs.read(`${root}/agents/models.json`)) - const models = host.models().getModels().map((model: any) => `${model.provider}/${model.id}`) - const files: {path:string; text:string}[] = [] - for (const directory of [`${root}/agents`, `${cwd}/.sausage/agents`]) { - let entries - try { entries = await host.fs.list(directory) } catch (error) { if ((error as any).code === 'not_found') continue; throw error } - for (const entry of entries) if (entry.kind === 'file' && entry.name.endsWith('.md')) { - const path = `${directory}/${entry.name}` - files.push({path, text:await host.fs.read(path)}) - } - } - return discoverAgents(files, root, cwd, families, models) -} diff --git a/extensions/subagents/server.ts b/extensions/subagents/server.ts index 3cbc908..25a80f1 100644 --- a/extensions/subagents/server.ts +++ b/extensions/subagents/server.ts @@ -3,11 +3,11 @@ import { BACKGROUND_CONTEXT, withCancel } from '@earendil-works/chord/context' import type { Context } from '@earendil-works/chord' import { bool, int, obj, oneOf, opt, str } from '@earendil-works/pi-schema' import type { AssistantMessage } from '@earendil-works/pi-ai' -import { AgentDoc, AssistantEntry, configure, ToolResultEntry, defineExtension, defineTask, defineTool, LiveDoc, InboxDoc, GenerationTask, hook, ROOT_CONVERSATION_ID, section } from '@earendil-works/pi-durable' +import { AgentDoc, AssistantEntry, ToolResultEntry, defineExtension, defineTask, defineTool, LiveDoc, InboxDoc, GenerationTask, hook, ROOT_CONVERSATION_ID, section } from '@earendil-works/pi-durable' import type { ConversationId, Cursor, EntryId, EntryRecord, TaskId, ToolExecutionApi, SubmissionRecord } from '@earendil-works/pi-durable' import { Subagents, Standing, Profile, Runs, SubagentsApi } from './api.ts' -import { createChild, childConfiguration, started } from './children.ts' -import { discover } from './definitions.ts' +import { createChild, configureChild, started } from './children.ts' +import { ModelsApi } from '../models/api.ts' export const docs = [Subagents, Standing, Profile, Runs] export const runsLabel = (runs: any) => runs ? `${runs.model.provider}/${runs.model.modelId}${runs.thinking ? ` · ${runs.thinking}` : ''}` : '' const onCommit = (harness: any, callback: any) => { @@ -298,15 +298,16 @@ async function waitInterruptibly( } export default function subagents(host: any) { + const models = host.need(ModelsApi) host.implement(SubagentsApi, { createChild: async (tx: any, options: any) => { const child = await createChild(host, tx, options) await started(host, tx, child, options.definition?.name ?? options.label, 'child') return child }, - residents: async () => (await discover(host)).definitions.filter((d: any) => d.resident).map(({name, description}: any) => ({name, description})), + residents: async () => (await models.discover('/src/sausage')).definitions.filter((d: any) => d.resident).map(({name, description}: any) => ({name, description})), standing: async (name: string, ctx: Context) => { - const found = await discover(host) + const found = await models.discover('/src/sausage') const definition = found.definitions.find((d: any) => d.name === name && d.resident) if (!definition) throw new Error(`Unknown standing agent @${name}`) const root = (await host.harness().conversation(ROOT_CONVERSATION_ID, ctx))! @@ -318,8 +319,7 @@ export default function subagents(host: any) { if (existing) { await lifecycle?.setStanding(tx, existing.id, true) await lifecycle?.setAutomaticSummaries(tx, existing.id, false) - const own = await tx.doc(AgentDoc, existing.id) - await configure(tx, existing.id, childConfiguration({definition, parentAgent:{...parentAgent, model:own.model ?? parentAgent.model, thinkingLevel:own.thinkingLevel ?? parentAgent.thinkingLevel}})) + await configureChild(host, tx, existing.id, {definition, parentAgent}) return existing.id } const child = await createChild(host, tx, {owner: {kind:'ownerless'}, definition, parentAgent, metadata:host.metadata, label:name, title:`@${name}`}) @@ -425,7 +425,7 @@ export default function subagents(host: any) { await recovery }) host.onOpen(async () => { - const found = await discover(host) + const found = await models.discover('/src/sausage') if (!found.errors.length) await host.harness().commit(async (tx: any) => { const index = await tx.doc(Standing, ROOT_CONVERSATION_ID) for (const name of Object.keys(index)) if (!found.definitions.some((d: any) => d.name === name && d.resident)) { @@ -540,7 +540,7 @@ export default function subagents(host: any) { ['subagent', 'branch', 'automation'].includes(tool.name), ) if (!usesDefinitions) return undefined - const discovered = await discover(host, input.agent.cwd) + const discovered = await models.discover(input.agent.cwd ?? '/src/sausage') const definitions = discovered.definitions .map((agent) => { const runs = runsLabel(agent.runs) @@ -614,7 +614,7 @@ export default function subagents(host: any) { const definitions = args.action === 'start' && !existingAction ? ( - await discover(host, parent.cwd) + await models.discover(parent.cwd ?? '/src/sausage') ).definitions : undefined const definition = definitions && (await spawnDefinition(args, definitions)) diff --git a/host/js/chain-test-bundle.js b/host/js/chain-test-bundle.js index c65cb71..2b9c29d 100644 --- a/host/js/chain-test-bundle.js +++ b/host/js/chain-test-bundle.js @@ -21391,6 +21391,32 @@ function environment(cwd = "/", conversation) { return env; } +// events.ts +function defineEvent(name, options = {}) { + const kind = `event.${name}`; + return { name, kind, version: options.version ?? 1, is: (event) => event?.kind === kind }; +} +var NoticeEvent = defineEvent("sausage.notice"); +var EVENT_FEED = "sausage.event-feed"; +var EventLog = defineDoc({ + kind: "sausage.events", + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ cursors: {} }) +}); +var EventFeed = defineDoc({ + kind: EVENT_FEED, + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ events: [] }) +}); + // extensions.ts var ops2 = Deno.core.ops; var encoder3 = new TextEncoder(); diff --git a/host/js/core-ext-test-bundle.js b/host/js/core-ext-test-bundle.js index 03d0cad..5032622 100644 --- a/host/js/core-ext-test-bundle.js +++ b/host/js/core-ext-test-bundle.js @@ -5781,13 +5781,13 @@ function cleanFrom(schema, value2) { } if (!isPlainObject2(value2)) return value2; - const record = recordOf(schema); + const record2 = recordOf(schema); const properties = isSchemaObject2(schema.properties) ? schema.properties : void 0; - if (properties === void 0 && record === void 0 && !isSchemaObject2(schema.additionalProperties)) + if (properties === void 0 && record2 === void 0 && !isSchemaObject2(schema.additionalProperties)) return value2; for (const key of Object.keys(value2)) { const known = properties !== void 0 && Object.hasOwn(properties, key) ? properties[key] : void 0; - const matched = known ?? (record?.[0].test(key) ? record[1] : void 0); + const matched = known ?? (record2?.[0].test(key) ? record2[1] : void 0); if (matched !== void 0) value2[key] = cleanFrom(asSchema(matched), value2[key]); else if (keepsUnknown(schema.additionalProperties, value2[key])) @@ -5827,7 +5827,7 @@ function withDefaults(schema, value2) { if (!isPlainObject2(defaulted)) return defaulted; const properties = isSchemaObject2(schema.properties) ? schema.properties : void 0; - const record = recordOf(schema); + const record2 = recordOf(schema); if (properties !== void 0) { const required = schema.required ?? []; for (const [key, property] of Object.entries(properties)) { @@ -5836,17 +5836,17 @@ function withDefaults(schema, value2) { continue; defaulted[key] = item; } - } else if (record !== void 0 && isSchemaObject2(record[1]) && "default" in record[1]) { + } else if (record2 !== void 0 && isSchemaObject2(record2[1]) && "default" in record2[1]) { for (const key of Object.keys(defaulted)) { - if (record[0].test(key)) - defaulted[key] = withDefaults(record[1], defaulted[key]); + if (record2[0].test(key)) + defaulted[key] = withDefaults(record2[1], defaulted[key]); } } if (!isSchemaObject2(schema.additionalProperties)) return defaulted; for (const key of Object.keys(defaulted)) { const known = properties !== void 0 && Object.hasOwn(properties, key); - if (!known && !record?.[0].test(key)) + if (!known && !record2?.[0].test(key)) defaulted[key] = withDefaults(schema.additionalProperties, defaulted[key]); } return defaulted; @@ -9057,10 +9057,10 @@ function merge2(previous, next) { // node_modules/@earendil-works/chord/dist/delta/index.js var isObj = (value2) => value2 !== null && typeof value2 === "object"; -function overlap(a, b, scan, probe = 64, maxCandidates = 8) { - if (a.length === 0 || b.length === 0 || scan === 0) +function overlap(a, b, scan2, probe = 64, maxCandidates = 8) { + if (a.length === 0 || b.length === 0 || scan2 === 0) return 0; - const tail = a.length > scan ? a.slice(a.length - scan) : a; + const tail = a.length > scan2 ? a.slice(a.length - scan2) : a; for (const h of [Math.min(probe, b.length), 1]) { const head = b.slice(0, h); let tried = 0; @@ -16026,25 +16026,25 @@ function documentCreate(definition, address, id) { }; } } -function checkRecordScope(definition, record) { - if (record.scope.kind !== definition.scope || record.scope.kind === "conversation" && (record.history !== definition.history || record.fork !== definition.fork)) { - throw new TypeError(`Document ${record.id} (${record.kind}) does not match the supplied definition semantics`); +function checkRecordScope(definition, record2) { + if (record2.scope.kind !== definition.scope || record2.scope.kind === "conversation" && (record2.history !== definition.history || record2.fork !== definition.fork)) { + throw new TypeError(`Document ${record2.id} (${record2.kind}) does not match the supplied definition semantics`); } } -function checkRecordVersion(definition, record, version) { +function checkRecordVersion(definition, record2, version) { if (version > definition.version) { - throw new Error(`Document ${record.id} (${record.kind}) has newer version ${version} than ${definition.version}`); + throw new Error(`Document ${record2.id} (${record2.kind}) has newer version ${version} than ${definition.version}`); } if (version < definition.version && definition.migrate === void 0) { - throw new Error(`Document ${record.id} (${record.kind}) requires migration from version ${version}`); + throw new Error(`Document ${record2.id} (${record2.kind}) requires migration from version ${version}`); } } function materializeDocument(definition, stored) { return materializeDocumentValue(definition, stored.record, stored.version, stored.value); } -function materializeDocumentValue(definition, record, version, value2) { - checkRecordScope(definition, record); - checkRecordVersion(definition, record, version); +function materializeDocumentValue(definition, record2, version, value2) { + checkRecordScope(definition, record2); + checkRecordVersion(definition, record2, version); if (version === definition.version) return value2; return copyJson(definition.migrate(value2, version)); @@ -16234,7 +16234,7 @@ var CachedStorage = class { const cached = this.#conversations.get(id); if (cached !== void 0) return Promise.resolve(cached); - return this.#keep(id, () => this.#storage.conversation(id, context), (record) => this.#conversations.set(id, record)); + return this.#keep(id, () => this.#storage.conversation(id, context), (record2) => this.#conversations.set(id, record2)); } scanConversations(query, limit, cursor, context) { return this.#storage.scanConversations(query, limit, cursor, context); @@ -16254,7 +16254,7 @@ var CachedStorage = class { const cached = this.#liveTasks.get(id) ?? this.#awaited.get(id)?.record ?? this.#tasks.get(id); if (cached !== void 0) return Promise.resolve(cached); - return this.#keep(id, () => this.#storage.task(id, context), (record) => this.#keepTask(record)); + return this.#keep(id, () => this.#storage.task(id, context), (record2) => this.#keepTask(record2)); } scanTasks(query, limit, cursor, context) { return this.#storage.scanTasks(query, limit, cursor, context); @@ -16294,30 +16294,30 @@ var CachedStorage = class { }; } /** Keep a frozen task record: a live one until its task ends, an ended one in the bounded table. */ - #keepTask(record) { - if (record.state.status === "terminal") { - this.#liveTasks.delete(record.id); - const awaited = this.#awaited.get(record.id); + #keepTask(record2) { + if (record2.state.status === "terminal") { + this.#liveTasks.delete(record2.id); + const awaited = this.#awaited.get(record2.id); if (awaited !== void 0) - awaited.record = record; + awaited.record = record2; else - this.#tasks.set(record.id, record); + this.#tasks.set(record2.id, record2); return; } - this.#tasks.delete(record.id); - this.#liveTasks.set(record.id, record); + this.#tasks.delete(record2.id); + this.#liveTasks.set(record2.id, record2); } /** * Follow a task's waits from its written record: a wait holds the ended records of its `on` until the task, after * leaving the wait, changes again (or ends), as the phase that resumes reads their outcomes before its next commit. */ - #followWait(record) { - const wait = this.#waits.get(record.id); - if (record.state.status === "waiting") { + #followWait(record2) { + const wait = this.#waits.get(record2.id); + if (record2.state.status === "waiting") { if (wait !== void 0) this.#release(wait.on); - const on = record.state.on; - this.#waits.set(record.id, { on, left: false }); + const on = record2.state.on; + this.#waits.set(record2.id, { on, left: false }); for (const id of on) { let awaited = this.#awaited.get(id); if (awaited === void 0) { @@ -16332,11 +16332,11 @@ var CachedStorage = class { } if (wait === void 0) return; - if (!wait.left && record.state.status !== "terminal") { + if (!wait.left && record2.state.status !== "terminal") { wait.left = true; return; } - this.#waits.delete(record.id); + this.#waits.delete(record2.id); this.#release(wait.on); } /** A wait on `on` is over: what no other wait names goes back to the bounded table. */ @@ -16676,14 +16676,14 @@ async function collectCopies(storage2, scope, at, policy, childConversationId, c ...source.key === void 0 ? {} : { key: source.key }, scope: { kind: "conversation", conversationId: childConversationId } }; - const record = source.history === "latest" ? { ...identity, history: "latest", fork: source.fork } : { ...identity, history: "rewindable", fork: source.fork }; - const copyAddress = addressId(record); + const record2 = source.history === "latest" ? { ...identity, history: "latest", fork: source.fork } : { ...identity, history: "rewindable", fork: source.fork }; + const copyAddress = addressId(record2); if (copiedAddresses.has(copyAddress)) { - const member = record.key === void 0 ? record.kind : `${record.kind}/${record.key}`; + const member = record2.key === void 0 ? record2.kind : `${record2.kind}/${record2.key}`; throw new Error(`Fork selects multiple source documents for ${member}`); } copiedAddresses.add(copyAddress); - copies.push({ record, source: { id: source.id, at } }); + copies.push({ record: record2, source: { id: source.id, at } }); } cursor = page3.next; } while (cursor !== void 0); @@ -16812,7 +16812,7 @@ var Transaction = class { throw new Error(`Conversation owner task ${ownerTaskId} does not exist`); owner = { conversationId: task.conversationId, taskId: ownerTaskId }; } - const record = { + const record2 = { id, ...parent === void 0 ? {} : { parent }, ...owner === void 0 ? {} : { owner } @@ -16833,10 +16833,10 @@ var Transaction = class { if (parent !== void 0) this.#forkSourceConversationIds.add(parent.conversationId); this.#createdConversationIds.add(id); - this.#writes.push({ type: "conversation", value: record }); - await this.#host.conversationCreated(this, record); + this.#writes.push({ type: "conversation", value: record2 }); + await this.#host.conversationCreated(this, record2); this.#assertOpen(); - return record; + return record2; } beforeInput(conversationId, input) { return this.#read("beforeInput", () => this.#host.beforeInput(this, conversationId, input, this.#context)); @@ -16853,15 +16853,15 @@ var Transaction = class { const id = await this.#host.storage.mintId(); this.#assertOpen(); const { head, ...rest } = value2; - const record = copyJson({ + const record2 = copyJson({ ...rest, id, conversationId, head: head === "self" ? id : head, byTaskId: this.#scope.taskId }, TABLE_JSON_COPY_OPTIONS); - this.#writes.push({ type: "entry", value: record }); - return record; + this.#writes.push({ type: "entry", value: record2 }); + return record2; }); } createTask(task, input, options) { @@ -16903,7 +16903,7 @@ var Transaction = class { const checkpoint = definition.initial(input); const id = await this.#host.storage.mintId(); this.#assertOpen(); - const record = copyJson({ + const record2 = copyJson({ id, conversationId, kind: definition.name, @@ -16917,7 +16917,7 @@ var Transaction = class { ...options.abandonOnRestart === true ? { abandonOnRestart: true } : {}, state: { status: "pending", checkpoint } }, TABLE_JSON_COPY_OPTIONS); - this.#tasksById.set(id, { write: { kind: "create", record } }); + this.#tasksById.set(id, { write: { kind: "create", record: record2 } }); return id; }); } @@ -16928,9 +16928,9 @@ var Transaction = class { this.#assertOpen(); const id = await this.#host.storage.mintId(); this.#assertOpen(); - const record = copyJson({ ...create, id }, TABLE_JSON_COPY_OPTIONS); - this.#submissions.set(id, record); - return record; + const record2 = copyJson({ ...create, id }, TABLE_JSON_COPY_OPTIONS); + this.#submissions.set(id, record2); + return record2; }); } /** @@ -17104,12 +17104,12 @@ var Transaction = class { const gate = this.#host.touch(documentKey(entry2.addressId)); if (gate !== void 0) await gate; - const record = this.#host.cached(entry2.addressId)?.record ?? await this.#host.storage.findDocument(entry2.address, "current", this.#context); + const record2 = this.#host.cached(entry2.addressId)?.record ?? await this.#host.storage.findDocument(entry2.address, "current", this.#context); this.#assertOpen(); - if (record === void 0) + if (record2 === void 0) return; - checkRecordScope(entry2.definition, record); - entry2.target = { kind: "retire-only", record }; + checkRecordScope(entry2.definition, record2); + entry2.target = { kind: "retire-only", record: record2 }; } // ─── Settlement ───────────────────────────────────────────────────────── /** Seal after callback failure: abort every change and observe every pending operation. */ @@ -17164,8 +17164,8 @@ var Transaction = class { if (plan.retire || change?.loaded === void 0 || content?.type !== "document.change") return void 0; const { loaded, prepared, version } = change; - const record = loaded.record; - if (record.scope.kind !== "conversation" || record.history !== "latest" || record.fork !== "initial") { + const record2 = loaded.record; + if (record2.scope.kind !== "conversation" || record2.history !== "latest" || record2.fork !== "initial") { return void 0; } if (loaded.storedVersion !== version) @@ -17187,12 +17187,12 @@ var Transaction = class { continue; } tracker.adopt(prepared); - const record = loaded.record; + const record2 = loaded.record; const conversationId = plan.conversationId; publications.push({ type: "document", kind: "change", - record, + record: record2, conversationId, version, value: prepared.value, @@ -17264,9 +17264,9 @@ var Transaction = class { let stored; for (const plan of this.#plans) { const committed = "createdAt" in plan.record; - let record = committed ? plan.record : { ...plan.record, createdAt: seq }; + let record2 = committed ? plan.record : { ...plan.record, createdAt: seq }; if (plan.retire) - record = { ...record, retiredAt: seq }; + record2 = { ...record2, retiredAt: seq }; const change = plan.change; if (change !== void 0) { const { tracker, prepared, loaded, version } = change; @@ -17286,7 +17286,7 @@ var Transaction = class { } else if (!plan.retire) { this.#host.install({ addressId: plan.addressId, - record, + record: record2, storedVersion: version, valueVersion: version, deltasSinceBase: 0, @@ -17295,8 +17295,8 @@ var Transaction = class { } } if (plan.retire && committed) - this.#host.evict(plan.addressId, record.id); - const publication = describe(plan, record); + this.#host.evict(plan.addressId, record2.id); + const publication = describe(plan, record2); if (publication === void 0) continue; const held2 = plan.held; @@ -17348,11 +17348,11 @@ var Transaction = class { plan.retire = true; retiring.add(plan.record.id); } - const retire = (record) => { - if (retiring.has(record.id)) + const retire = (record2) => { + if (retiring.has(record2.id)) return; - plans.push({ addressId: addressId(record), record, retire: true }); - retiring.add(record.id); + plans.push({ addressId: addressId(record2), record: record2, retire: true }); + retiring.add(record2.id); }; for (const taskId of terminalTaskIds) { if (this.#tasksById.get(taskId)?.write?.kind === "create") @@ -17362,15 +17362,15 @@ var Transaction = class { await gate; const known = this.#host.taskDocuments(taskId); if (known !== void 0) { - for (const record of known) - retire(record); + for (const record2 of known) + retire(record2); continue; } let cursor; do { const page3 = await storage2.scanDocuments({ scope: { kind: "task", taskId }, at: "current" }, INTERNAL_SCAN_PAGE_SIZE, cursor, this.#context); - for (const record of page3.items) - retire(record); + for (const record2 of page3.items) + retire(record2); cursor = page3.next; } while (cursor !== void 0); } @@ -17435,9 +17435,9 @@ var Transaction = class { owners.push({ what: "Conversation owner task", taskId: write.value.owner.taskId }); } for (const task of this.#tasksById.values()) { - const record = task.write?.kind === "create" ? task.write.record : void 0; - if (record?.owner !== void 0) - owners.push({ what: "Task owner", taskId: record.owner }); + const record2 = task.write?.kind === "create" ? task.write.record : void 0; + if (record2?.owner !== void 0) + owners.push({ what: "Task owner", taskId: record2.owner }); } for (const { what, taskId } of owners) { const task = await this.#currentTask(taskId); @@ -17454,12 +17454,12 @@ var Transaction = class { for (const plan of plans) { if (plan.content === void 0 && !plan.retire) continue; - const record = plan.record; - if (this.#forkSourceDocumentIds.has(record.id)) { - throw new Error(`Cannot change fork source document ${record.id} in the fork transaction`); + const record2 = plan.record; + if (this.#forkSourceDocumentIds.has(record2.id)) { + throw new Error(`Cannot change fork source document ${record2.id} in the fork transaction`); } const scope = plan.record.scope; - if (scope.kind === "conversation" && this.#forkSourceConversationIds.has(scope.conversationId) && record.scope.kind === "conversation" && record.fork === "current") { + if (scope.kind === "conversation" && this.#forkSourceConversationIds.has(scope.conversationId) && record2.scope.kind === "conversation" && record2.fork === "current") { throw new Error(`Cannot fork conversation ${scope.conversationId} while changing its current-policy documents`); } } @@ -17556,12 +17556,12 @@ function planDocument(document) { switch (target.kind) { case "created": { const prepared = document.prepared; - const { record, version, tracker } = target; + const { record: record2, version, tracker } = target; const content = { version, kind: "base", value: prepared.value }; return { ...plan, - record, - content: { type: "document.create", record, content }, + record: record2, + content: { type: "document.create", record: record2, content }, change: { tracker, prepared, version } }; } @@ -17591,12 +17591,12 @@ function planDocument(document) { } } } -function describe(plan, record) { +function describe(plan, record2) { const conversationId = plan.conversationId; if (plan.retire) - return { type: "document", kind: "retire", record, conversationId }; + return { type: "document", kind: "retire", record: record2, conversationId }; if (plan.content?.type === "document.copy") { - return { type: "document.copy", record, conversationId, source: plan.content.source }; + return { type: "document.copy", record: record2, conversationId, source: plan.content.source }; } const change = plan.change; if (change === void 0 || !publishes(plan)) @@ -17605,7 +17605,7 @@ function describe(plan, record) { return { type: "document", kind: "change", - record, + record: record2, conversationId, version: change.version, value: change.prepared.value, @@ -18029,7 +18029,7 @@ var SessionImpl = class { this.#dropDocument(id); }, beforeInput: (tx, id, input, context) => this.beforeInput(tx, id, input, context), - conversationCreated: (tx, record) => this.conversationCreated(tx, record), + conversationCreated: (tx, record2) => this.conversationCreated(tx, record2), taskDocuments: (taskId) => this.#taskDocuments.get(taskId)?.values(), latestEntryId: (conversationId, context) => this.latestEntryId(conversationId, context), latestHeadMarker: (conversationId, context) => this.latestHeadMarker(conversationId, void 0, context) @@ -18285,12 +18285,12 @@ var SessionImpl = class { ...resolved.address, scope: { kind: "conversation", conversationId: storedEntry.entry.conversationId } }; - const record = await this.#gated.findDocument(address, storedEntry.commitSeq, context); - if (record === void 0) + const record2 = await this.#gated.findDocument(address, storedEntry.commitSeq, context); + if (record2 === void 0) return { value: void 0 }; - const stored = await this.#gated.document(record.id, storedEntry.commitSeq, context); + const stored = await this.#gated.document(record2.id, storedEntry.commitSeq, context); if (stored === void 0) { - throw new Error(`Historical document ${record.id} (${record.kind}) cannot be read`); + throw new Error(`Historical document ${record2.id} (${record2.kind}) cannot be read`); } return { value: materializeDocument(definition, stored) }; }); @@ -18735,13 +18735,13 @@ var SessionImpl = class { const changes = []; const keys = []; for (const hold of holds) { - const { record, tracker, valueVersion: version } = hold.loaded; + const { record: record2, tracker, valueVersion: version } = hold.loaded; const value2 = tracker.value; const stored = storeHeld(hold, NO_OPERATIONS, value2, version); - writes.push({ type: "document.change", id: record.id, content: stored.content }); - keys.push(documentKey(hold.loaded.addressId), incarnationKey(record.id)); - const conversationId = record.scope.kind === "conversation" ? record.scope.conversationId : void 0; - changes.push({ type: "document", kind: "change", record, conversationId, version, value: value2, ops: stored.ops }); + writes.push({ type: "document.change", id: record2.id, content: stored.content }); + keys.push(documentKey(hold.loaded.addressId), incarnationKey(record2.id)); + const conversationId = record2.scope.kind === "conversation" ? record2.scope.conversationId : void 0; + changes.push({ type: "document", kind: "change", record: record2, conversationId, version, value: value2, ops: stored.ops }); } const publish2 = (seq) => { for (const [index2, hold] of holds.entries()) { @@ -19007,12 +19007,12 @@ var SessionImpl = class { }, context); this.#dropDocument(addressId2); } - const record = await this.#storage.findDocument(address, "current", context); - if (record === void 0) + const record2 = await this.#storage.findDocument(address, "current", context); + if (record2 === void 0) return void 0; - const stored = await this.#storage.document(record.id, "current", context); + const stored = await this.#storage.document(record2.id, "current", context); if (stored === void 0) - throw new Error(`Current document ${record.id} (${record.kind}) cannot be read`); + throw new Error(`Current document ${record2.id} (${record2.kind}) cannot be read`); const value2 = materializeDocument(definition, stored); const loaded = { addressId: addressId2, @@ -19250,16 +19250,16 @@ function endWatch(watch) { function endGuarded(guarded) { guarded.end(ENDED2); } -function atAddress(record, address) { - if (record.kind !== address.kind || record.key !== address.key) +function atAddress(record2, address) { + if (record2.kind !== address.kind || record2.key !== address.key) return false; - switch (record.scope.kind) { + switch (record2.scope.kind) { case "session": return address.scope.kind === "session"; case "conversation": - return address.scope.kind === "conversation" && address.scope.conversationId === record.scope.conversationId; + return address.scope.kind === "conversation" && address.scope.conversationId === record2.scope.conversationId; case "task": - return address.scope.kind === "task" && address.scope.taskId === record.scope.taskId; + return address.scope.kind === "task" && address.scope.taskId === record2.scope.taskId; } } function cancellationError(signal) { @@ -20832,11 +20832,11 @@ var Waiters = class { waiter.reject(error); } }; -async function scanAll(scan) { +async function scanAll(scan2) { const items = []; let cursor; do { - const page3 = await scan(cursor); + const page3 = await scan2(cursor); items.push(...page3.items); cursor = page3.next; } while (cursor !== void 0); @@ -20862,14 +20862,14 @@ function conversationEntries(conversationId, query) { var SCAN_PAGE_SIZE3 = 256; var NO_KEYS2 = []; var LIVE_STATUSES = ["pending", "running", "waiting", "sleeping", "completing"]; -function blockedSubject(record) { +function blockedSubject(record2) { return { - id: record.id, - kind: record.kind, - version: record.version, - abortRequested: record.abortRequested, - ...record.abortReason === void 0 ? {} : { abortReason: record.abortReason }, - status: record.state.status + id: record2.id, + kind: record2.kind, + version: record2.version, + abortRequested: record2.abortRequested, + ...record2.abortReason === void 0 ? {} : { abortReason: record2.abortReason }, + status: record2.state.status }; } var CONTINUE = { kind: "continue" }; @@ -21052,20 +21052,20 @@ var TaskScheduler = class { scans.push(await scanAll((cursor) => tx.scanTasks({ status }, SCAN_PAGE_SIZE3, cursor))); } for (const records of scans) { - for (const record of records) { - this.#track(void 0, record); - this.#resumed.add(record.id); + for (const record2 of records) { + this.#track(void 0, record2); + this.#resumed.add(record2.id); if (this.#readOnly) continue; - if (record.abandonOnRestart === true) - this.#abandoned.add(record.id); - if (record.state.status === "running") { - const restarts = (record.restarts ?? 0) + 1; - const pending = withState(record, { status: "pending", checkpoint: record.state.checkpoint }); + if (record2.abandonOnRestart === true) + this.#abandoned.add(record2.id); + if (record2.state.status === "running") { + const restarts = (record2.restarts ?? 0) + 1; + const pending = withState(record2, { status: "pending", checkpoint: record2.state.checkpoint }); tx.setTask({ ...pending, restarts }); } - if (record.state.status === "waiting" && record.state.policy === "failFast") { - this.#failFastChecks.add(record.id); + if (record2.state.status === "waiting" && record2.state.policy === "failFast") { + this.#failFastChecks.add(record2.id); } } } @@ -21091,11 +21091,11 @@ var TaskScheduler = class { * Why a live task cannot run here, if it cannot: its definition is missing, too old, or failed to migrate. A running * task, an abort-marked one, and one holding its outcome are never blocked. */ - blockedReason(record) { - const status = record.status; + blockedReason(record2) { + const status = record2.status; if (status === "running" || status === "completing" || status === "terminal") return void 0; - if (record.abortRequested && record.abortReason === void 0) + if (record2.abortRequested && record2.abortReason === void 0) return void 0; let snapshot; try { @@ -21106,7 +21106,7 @@ var TaskScheduler = class { this.#snapshotReported = true; return void 0; } - const fit = this.#fit(record, snapshot.task(record.kind)); + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return fit.reason; return fit.migrates && fit.task.definition.migrate === void 0 ? "migration_failed" : void 0; @@ -21127,9 +21127,9 @@ var TaskScheduler = class { const active = this.#invocations.size > 0 || this.#reconcilingWork > 0 || this.#reconcileWork() || this.#runsNow(now); let at; for (const id of this.#sleeping) { - const record = this.#live.get(id); - const state = record?.state; - if (state?.status === "sleeping" && !record.abortRequested && state.until > now) { + const record2 = this.#live.get(id); + const state = record2?.state; + if (state?.status === "sleeping" && !record2.abortRequested && state.until > now) { at = at === void 0 ? state.until : Math.min(at, state.until); } } @@ -21139,17 +21139,17 @@ var TaskScheduler = class { #runsNow(now) { let snapshot; let snapshotFailed = false; - for (const record of this.#live.values()) { - const state = record.state; + for (const record2 of this.#live.values()) { + const state = record2.state; if (state.status === "completing") continue; - if (state.status === "waiting" && !record.abortRequested && state.on.some((id) => this.#live.has(id))) + if (state.status === "waiting" && !record2.abortRequested && state.on.some((id) => this.#live.has(id))) continue; - if (state.status === "sleeping" && !record.abortRequested && state.until > now) + if (state.status === "sleeping" && !record2.abortRequested && state.until > now) continue; - if (record.abortRequested && this.#waitingOn(record, true).length > 0) + if (record2.abortRequested && this.#waitingOn(record2, true).length > 0) continue; - if (!record.abortRequested || record.abortReason !== void 0) { + if (!record2.abortRequested || record2.abortReason !== void 0) { if (snapshot === void 0 && !snapshotFailed) { try { snapshot = this.#registry.snapshot(); @@ -21158,7 +21158,7 @@ var TaskScheduler = class { this.#report(error, REGISTRY); } } - if (snapshot !== void 0 && "reason" in this.#fit(record, snapshot.task(record.kind))) + if (snapshot !== void 0 && "reason" in this.#fit(record2, snapshot.task(record2.kind))) continue; } return true; @@ -21178,10 +21178,10 @@ var TaskScheduler = class { if (stopped.length === 0) return; await this.#writeAtClose(() => stopped.flatMap((invocation) => { - const record = this.#live.get(invocation.taskId); - if (record?.state.status !== "running") + const record2 = this.#live.get(invocation.taskId); + if (record2?.state.status !== "running") return []; - const value2 = withState(record, { status: "pending", checkpoint: record.state.checkpoint }); + const value2 = withState(record2, { status: "pending", checkpoint: record2.state.checkpoint }); return [{ type: "task", value: value2 }]; })).catch(() => { }); @@ -21228,10 +21228,10 @@ var TaskScheduler = class { if (await this.#region(() => this.#liveRecord(id) !== void 0)) { return { promise: this.#taskWaiters.add(id, context) }; } - const record = await storage2.task(id, context); - if (record === void 0) + const record2 = await storage2.task(id, context); + if (record2 === void 0) throw new Error(`Task ${id} does not exist`); - return { promise: Promise.resolve(record) }; + return { promise: Promise.resolve(record2) }; }); return found.promise; } @@ -21288,7 +21288,7 @@ var TaskScheduler = class { membershipKeys(tx) { const tasks = tx.stagedTasks(); const conversations = tx.stagedConversations(); - if (tasks.length === 0 && conversations.every((record) => record.owner === void 0)) + if (tasks.length === 0 && conversations.every((record2) => record2.owner === void 0)) return NO_KEYS2; return this.#membershipKeys(tx, tasks, conversations); } @@ -21297,13 +21297,13 @@ var TaskScheduler = class { const overlay = overlayOf(tx); const created = new Set(tx.createdTaskIds()); const reached = /* @__PURE__ */ new Map(); - for (const record of tasks) { - if (intentChanges(this.#live.get(record.id), record)) + for (const record2 of tasks) { + if (intentChanges(this.#live.get(record2.id), record2)) keys.add(INTENT_KEY); - if (!created.has(record.id) && record.state.status !== "terminal") + if (!created.has(record2.id) && record2.state.status !== "terminal") continue; - await this.#loadChain(parentOf(record), overlay); - if (await this.#chainKeys(parentNode(record), overlay, keys, reached) !== void 0) + await this.#loadChain(parentOf(record2), overlay); + if (await this.#chainKeys(parentNode(record2), overlay, keys, reached) !== void 0) keys.add(ROOTS_KEY); } for (const conversation of conversations) { @@ -21340,18 +21340,18 @@ var TaskScheduler = class { root = conversation; continue; } - let record = overlay.tasks.get(node); - if (record === void 0) { + let record2 = overlay.tasks.get(node); + if (record2 === void 0) { const gate = this.#session.touch(taskKey(node)); if (gate !== void 0) { await gate; await this.#loadChain({ task: node }, overlay); } - record = this.#live.get(node); + record2 = this.#live.get(node); } - if (record !== void 0 && record.state.status !== "terminal") + if (record2 !== void 0 && record2.state.status !== "terminal") break; - const above = record ?? this.#settled.get(node); + const above = record2 ?? this.#settled.get(node); node = above === void 0 ? void 0 : parentNode(above); } for (const key of walked) @@ -21376,41 +21376,41 @@ var TaskScheduler = class { if (change.type !== "task") continue; changed = true; - const record = change.value; - const previous = this.#live.get(record.id); - if (failedOutcome(record) && (previous === void 0 || !failedOutcome(previous))) - failed.push(record.id); - this.#track(previous, record); - const liveness = previous === void 0 || record.state.status === "terminal"; - if (liveness && dirty !== void 0 && !this.#addScopesAbove(record, dirty)) + const record2 = change.value; + const previous = this.#live.get(record2.id); + if (failedOutcome(record2) && (previous === void 0 || !failedOutcome(previous))) + failed.push(record2.id); + this.#track(previous, record2); + const liveness = previous === void 0 || record2.state.status === "terminal"; + if (liveness && dirty !== void 0 && !this.#addScopesAbove(record2, dirty)) dirty = void 0; - if (record.state.status === "terminal") { - this.#failedMigrations.delete(record.id); - this.#failFastChecks.delete(record.id); - this.#taskWaiters.resolve(record.id, record); + if (record2.state.status === "terminal") { + this.#failedMigrations.delete(record2.id); + this.#failFastChecks.delete(record2.id); + this.#taskWaiters.resolve(record2.id, record2); this.#scheduleReconcile(); continue; } - if (record.abortRequested && previous?.abortRequested !== true) { + if (record2.abortRequested && previous?.abortRequested !== true) { this.#cascadePending = true; - const invocation = this.#invocations.get(record.id); + const invocation = this.#invocations.get(record2.id); if (invocation?.mode === "run") queueMicrotask(() => invocation.controller.abort()); } - if (previous?.abortReason !== void 0 && record.abortReason === void 0) + if (previous?.abortReason !== void 0 && record2.abortReason === void 0) this.#cascadePending = true; - const status = record.state.status; + const status = record2.state.status; if (status === "completing" && previous?.state.status !== "completing") { - if (cancellationIntent(record)) + if (cancellationIntent(record2)) this.#cascadePending = true; this.#scheduleReconcile(); } - if (status === "waiting" && record.state.policy === "failFast" && previous?.state.status !== "waiting") { - this.#failFastChecks.add(record.id); + if (status === "waiting" && record2.state.policy === "failFast" && previous?.state.status !== "waiting") { + this.#failFastChecks.add(record2.id); this.#scheduleReconcile(); } if (previous === void 0) - updated.push(record); + updated.push(record2); } for (const id of failed) { for (const waiter of this.#waiters.get(id) ?? []) { @@ -21431,10 +21431,10 @@ var TaskScheduler = class { this.#cascadePending = true; } } - for (const record of updated) { - if (this.#unloaded.has(record.id)) + for (const record2 of updated) { + if (this.#unloaded.has(record2.id)) this.#scheduleReconcile(); - else if (!record.background && !record.abortRequested && this.#belowCancelled(parentOf(record))) { + else if (!record2.background && !record2.abortRequested && this.#belowCancelled(parentOf(record2))) { this.#cascadePending = true; } } @@ -21450,8 +21450,8 @@ var TaskScheduler = class { * Add the conversations above `record` to `scopes`: those whose idleness a change of it can change. False when a node * on the way up is not known. A conversation already in `scopes` has everything above it there too. */ - #addScopesAbove(record, scopes) { - for (const step of this.#above(parentOf(record))) { + #addScopesAbove(record2, scopes) { + for (const step of this.#above(parentOf(record2))) { if ("unknown" in step) return false; if (!("conversation" in step)) @@ -21633,8 +21633,8 @@ var TaskScheduler = class { if (cascade) this.#cascadePending = false; const marks = await this.#marks(); - for (const { record, reason } of marks.values()) - tx.setTask(withAbortMark(record, reason)); + for (const { record: record2, reason } of marks.values()) + tx.setTask(withAbortMark(record2, reason)); const withdrawn = await this.#region(() => queued.filter((id) => this.#belowCancelled({ conversation: id })), this.#reloadScopes); for (const id of withdrawn) await this.#withdrawInputs(tx, id); @@ -21681,13 +21681,13 @@ var TaskScheduler = class { #planMarks(ended) { const marks = /* @__PURE__ */ new Map(); const mark = (found, reason) => { - const record = this.#liveRecord(found.id); - const staged = marks.get(record.id); + const record2 = this.#liveRecord(found.id); + const staged = marks.get(record2.id); if (staged !== void 0 && staged.reason === void 0) return; - if (record.abortRequested && (record.abortReason === void 0 || reason !== void 0)) + if (record2.abortRequested && (record2.abortReason === void 0 || reason !== void 0)) return; - marks.set(record.id, { record, reason }); + marks.set(record2.id, { record: record2, reason }); }; this.#record(INTENT_KEY); for (const id of this.#intent) { @@ -21706,18 +21706,18 @@ var TaskScheduler = class { continue; let failed = false; for (const member of waiter.state.on) { - const record = this.#liveRecord(member) ?? ended.get(member); - if (record === void 0) + const record2 = this.#liveRecord(member) ?? ended.get(member); + if (record2 === void 0) missing.push(member); - else if (record !== null && failedOutcome(record)) + else if (record2 !== null && failedOutcome(record2)) failed = true; } if (!failed) continue; for (const member of waiter.state.on) { - const record = this.#liveRecord(member); - if (record !== void 0 && !failedOutcome(record)) - mark(record, void 0); + const record2 = this.#liveRecord(member); + if (record2 !== void 0 && !failedOutcome(record2)) + mark(record2, void 0); } } return missing.length > 0 ? { missing } : { marks, checked }; @@ -21742,13 +21742,13 @@ var TaskScheduler = class { const plan = await this.#region(() => this.#planRound(ids ?? [...this.#finalizeChecks], overlay, done), this.#reloadScopes); if (ids === void 0) this.#finalizeChecks.clear(); - for (const { record, terminal } of plan.finalized) { + for (const { record: record2, terminal } of plan.finalized) { tx.setTask(terminal); - overlay.tasks.set(record.id, terminal); - done.add(record.id); - const outcome = record.state.outcome; + overlay.tasks.set(record2.id, terminal); + done.add(record2.id); + const outcome = record2.state.outcome; if (outcome.status === "faulted" || outcome.status === "orphaned") { - await this.#settleOutcome(tx, record, outcome); + await this.#settleOutcome(tx, record2, outcome); } } if (plan.next.length === 0) @@ -21771,25 +21771,25 @@ var TaskScheduler = class { try { for (const id of ids) { const staged = tasks.get(id); - const record = staged ?? this.#liveRecord(id); - if (record?.state.status !== "completing" || done.has(id)) + const record2 = staged ?? this.#liveRecord(id); + if (record2?.state.status !== "completing" || done.has(id)) continue; if (this.#hasOwnedLive(id, overlay, done)) continue; - const terminal = withState(record, { status: "terminal", outcome: record.state.outcome }); + const terminal = withState(record2, { status: "terminal", outcome: record2.state.outcome }); tasks.set(id, terminal); round.add(id); - ended.push({ record, terminal, staged: staged !== void 0 }); - const above = this.#nearestLiveAbove(record, overlay); + ended.push({ record: record2, terminal, staged: staged !== void 0 }); + const above = this.#nearestLiveAbove(record2, overlay); if (above?.state.status === "completing") next.add(above.id); } } finally { - for (const { record, staged } of ended) { + for (const { record: record2, staged } of ended) { if (staged) - tasks.set(record.id, record); + tasks.set(record2.id, record2); else - tasks.delete(record.id); + tasks.delete(record2.id); } } return { finalized: ended, next: [...next] }; @@ -21832,11 +21832,11 @@ var TaskScheduler = class { if (node !== void 0 && !this.#unloaded.has(id)) break; if (node === void 0) { - const record = await this.#session.lineStorage.task(id, this.#context); - if (record === void 0) + const record2 = await this.#session.lineStorage.task(id, this.#context); + if (record2 === void 0) return; - node = nodeOf(record); - if (record.state.status === "terminal") { + node = nodeOf(record2); + if (record2.state.status === "terminal") { this.#settle(id, node); this.#pin(id); installed = true; @@ -21848,8 +21848,8 @@ var TaskScheduler = class { } else { let edge = this.#edge(at.conversation, overlay); if (edge === void 0) { - const record = await this.#session.lineStorage.conversation(at.conversation, this.#context); - edge = record?.owner?.taskId ?? null; + const record2 = await this.#session.lineStorage.conversation(at.conversation, this.#context); + edge = record2?.owner?.taskId ?? null; this.#setEdge(at.conversation, edge); this.#pin(conversationNode(at.conversation)); installed = true; @@ -21994,23 +21994,23 @@ var TaskScheduler = class { * Bring the scheduler's sets in line with a task's committed record. A task that ended may free the nearest live task * above it: one holding its outcome finalizes, an abort-marked one runs its abort handler; its waiters may run. */ - #track(previous, record) { - const id = record.id; + #track(previous, record2) { + const id = record2.id; const invocation = this.#invocations.get(id); if (invocation !== void 0) - invocation.record = record; - if (record.state.status === "terminal") { + invocation.record = record2; + if (record2.state.status === "terminal") { if (previous !== void 0 && !this.#closing) - this.#countActive(-1, record.kind); + this.#countActive(-1, record2.kind); this.#resumed.delete(id); } else if (previous === void 0 && !this.#closing) { - this.#countActive(1, record.kind); + this.#countActive(1, record2.kind); } if (previous?.state.status === "waiting") for (const on of previous.state.on) deleteFrom(this.#waiters, on, id); - if (record.state.status === "terminal") { - const node = nodeOf(record); + if (record2.state.status === "terminal") { + const node = nodeOf(record2); this.#live.delete(id); this.#sleeping.delete(id); this.#intent.delete(id); @@ -22024,26 +22024,26 @@ var TaskScheduler = class { this.#dropIfUnneeded(id); return; } - this.#live.set(id, record); + this.#live.set(id, record2); if (previous === void 0) { - if (!this.#known(parentNode(record))) + if (!this.#known(parentNode(record2))) this.#unloaded.add(id); this.#relist(id); } - if (cancellationIntent(record)) + if (cancellationIntent(record2)) this.#intent.add(id); else this.#intent.delete(id); - if (record.state.status === "completing") { + if (record2.state.status === "completing") { this.#runnable.delete(id); this.#finalizeChecks.add(id); } else { this.#runnable.add(id); } - if (record.state.status === "waiting") - for (const on of record.state.on) + if (record2.state.status === "waiting") + for (const on of record2.state.on) addTo(this.#waiters, on, id); - if (record.state.status === "sleeping") + if (record2.state.status === "sleeping") this.#sleeping.add(id); else this.#sleeping.delete(id); @@ -22112,10 +22112,10 @@ var TaskScheduler = class { const owner = current.owner ?? this.#edge(current.conversationId, overlay); if (owner === void 0 || owner === null) return void 0; - const record = overlay?.tasks.get(owner) ?? this.#liveRecord(owner); - if (record !== void 0 && record.state.status !== "terminal") - return record; - const above = record ?? this.#settled.get(owner); + const record2 = overlay?.tasks.get(owner) ?? this.#liveRecord(owner); + if (record2 !== void 0 && record2.state.status !== "terminal") + return record2; + const above = record2 ?? this.#settled.get(owner); if (above === void 0) return void 0; current = above; @@ -22180,11 +22180,11 @@ var TaskScheduler = class { pending.push(child); continue; } - const record = overlay?.tasks.get(child) ?? this.#live.get(child); - const node = record ?? this.#settled.get(child); + const record2 = overlay?.tasks.get(child) ?? this.#live.get(child); + const node = record2 ?? this.#settled.get(child); if (node === void 0) continue; - const live = record !== void 0 && record.state.status !== "terminal" ? record : void 0; + const live = record2 !== void 0 && record2.state.status !== "terminal" ? record2 : void 0; yield { id: child, node, live }; if ((crossBackground || !node.background) && (enter === void 0 || enter(child))) pending.push(child); @@ -22200,11 +22200,11 @@ var TaskScheduler = class { if (overlay.links !== void 0) return overlay.links; const links = /* @__PURE__ */ new Map(); - for (const record of overlay.tasks.values()) { - if (this.#live.has(record.id) || this.#settled.has(record.id)) + for (const record2 of overlay.tasks.values()) { + if (this.#live.has(record2.id) || this.#settled.has(record2.id)) continue; - let child = record.id; - let parent = parentNode(record); + let child = record2.id; + let parent = parentNode(record2); while (parent !== void 0 && parent !== null && links.get(parent)?.has(child) !== true) { addTo(links, parent, child); if (typeof parent !== "string" && (this.#live.has(parent) || overlay.tasks.has(parent))) @@ -22316,8 +22316,8 @@ var TaskScheduler = class { } /** Close listener: runs synchronously once admission is sealed, before `join()`, or when the Session fails. */ #seal() { - for (const record of this.#live.values()) - this.#countActive(-1, record.kind); + for (const record2 of this.#live.values()) + this.#countActive(-1, record2.kind); this.#resumed.clear(); this.#closing = true; try { @@ -22385,8 +22385,8 @@ var TaskScheduler = class { /** Make every sleeping task whose time has come a reservation candidate. */ #wakeDue() { for (const id of this.#sleeping) { - const record = this.#live.get(id); - if (record !== void 0 && !this.#notDue(record)) + const record2 = this.#live.get(id); + if (record2 !== void 0 && !this.#notDue(record2)) this.#runnable.add(id); } } @@ -22427,9 +22427,9 @@ var TaskScheduler = class { } } /** A sleep whose `until` the clock has not reached; an abort mark ends a sleep. */ - #notDue(record) { - const state = record.state; - return state.status === "sleeping" && !record.abortRequested && state.until > this.#now(); + #notDue(record2) { + const state = record2.state; + return state.status === "sleeping" && !record2.abortRequested && state.until > this.#now(); } #kick() { this.#scheduleWake(); @@ -22468,9 +22468,9 @@ var TaskScheduler = class { return; if (this.#abandoned.size > 0) { const ids = [...this.#abandoned]; - const unmarked = await this.#region(() => ids.map((id) => this.#liveRecord(id)).filter((record) => record !== void 0 && !record.abortRequested)); - for (const record of unmarked) - tx.setTask(withAbortMark(record, "restart")); + const unmarked = await this.#region(() => ids.map((id) => this.#liveRecord(id)).filter((record2) => record2 !== void 0 && !record2.abortRequested)); + for (const record2 of unmarked) + tx.setTask(withAbortMark(record2, "restart")); abandoned = ids; return; } @@ -22484,37 +22484,37 @@ var TaskScheduler = class { this.#scheduleReconcile(); } let snapshot; - for (const record of plan.ready) { - const id = record.id; - const mode = record.abortRequested ? "abort" : "run"; + for (const record2 of plan.ready) { + const id = record2.id; + const mode = record2.abortRequested ? "abort" : "run"; if (mode === "run" && this.#held) continue; - const cutOff = record.restarts !== void 0 && record.restarts >= this.#restartLimit(); - if (cutOff && (mode === "abort" || !this.#limitsRestarts(record.kind))) { - const message = mode === "abort" ? `Abort handler of task ${id} (${record.kind}) was cut off ${record.restarts} times` : `Task ${id} (${record.kind}) was cut off ${record.restarts} times without progress`; - await this.#terminate(tx, record, { + const cutOff = record2.restarts !== void 0 && record2.restarts >= this.#restartLimit(); + if (cutOff && (mode === "abort" || !this.#limitsRestarts(record2.kind))) { + const message = mode === "abort" ? `Abort handler of task ${id} (${record2.kind}) was cut off ${record2.restarts} times` : `Task ${id} (${record2.kind}) was cut off ${record2.restarts} times without progress`; + await this.#terminate(tx, record2, { status: "faulted", error: { message, detail: { reason: "restart_limit" } } }); - faulted.push({ record, message }); + faulted.push({ record: record2, message }); continue; } snapshot ??= this.#registry.snapshot(); - const resolution = this.#resolve(record, snapshot); + const resolution = this.#resolve(record2, snapshot); if (resolution.kind === "blocked") { - if (mode === "abort" && record.abortReason === void 0) { - await this.#terminate(tx, record, { status: "orphaned", reason: resolution.reason }); + if (mode === "abort" && record2.abortReason === void 0) { + await this.#terminate(tx, record2, { status: "orphaned", reason: resolution.reason }); } continue; } - if (resolution.record !== record || record.state.status !== "running") { + if (resolution.record !== record2 || record2.state.status !== "running") { tx.setTask(withState(resolution.record, { status: "running", checkpoint: resolution.record.state.checkpoint })); } this.#runnable.delete(id); - const invocation = this.#createInvocation(record, mode, installerOf(snapshot, resolution.task)); + const invocation = this.#createInvocation(record2, mode, installerOf(snapshot, resolution.task)); reservations.push({ invocation, task: resolution.task, snapshot }); } }, this.#context); @@ -22526,10 +22526,10 @@ var TaskScheduler = class { } throw error; } - for (const { record, message } of faulted) { + for (const { record: record2, message } of faulted) { this.#report(new Error(message), { kind: "task_faulted", - ...this.#aboutTask(recordTask(record)), + ...this.#aboutTask(recordTask(record2)), reason: "restart_limit" }); } @@ -22554,16 +22554,16 @@ var TaskScheduler = class { dropped.push(id); continue; } - const record = this.#liveRecord(id); - if (record === void 0 || record.state.status === "completing" || this.#waitingOn(record, true).length > 0 || this.#notDue(record)) { + const record2 = this.#liveRecord(id); + if (record2 === void 0 || record2.state.status === "completing" || this.#waitingOn(record2, true).length > 0 || this.#notDue(record2)) { dropped.push(id); continue; } - if (!record.abortRequested && !record.background && this.#belowCancelled(parentOf(record))) { + if (!record2.abortRequested && !record2.background && this.#belowCancelled(parentOf(record2))) { cascade = true; continue; } - ready.push(record); + ready.push(record2); } return { dropped, cascade, ready }; } @@ -22571,49 +22571,49 @@ var TaskScheduler = class { * Live tasks a task waits for before its next invocation: its live ordinary owned work when abort-marked, since * abort runs bottom-up, otherwise the live part of the `on` of a wait. */ - #waitingOn(record, first = false) { - if (record.abortRequested) - return this.#ownedLive(record.id, { first }); - if (record.state.status !== "waiting") + #waitingOn(record2, first = false) { + if (record2.abortRequested) + return this.#ownedLive(record2.id, { first }); + if (record2.state.status !== "waiting") return []; if (!first) - return record.state.on.filter((id) => this.#live.has(id)); - const live = record.state.on.find((id) => this.#live.has(id)); + return record2.state.on.filter((id) => this.#live.has(id)); + const live = record2.state.on.find((id) => this.#live.has(id)); return live === void 0 ? [] : [live]; } /** Resolve the record's definition by kind, migrating an older stored version. */ - #resolve(record, snapshot) { - const fit = this.#fit(record, snapshot.task(record.kind)); + #resolve(record2, snapshot) { + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return { kind: "blocked", reason: fit.reason }; if (!fit.migrates) - return { kind: "ready", task: fit.task, record }; + return { kind: "ready", task: fit.task, record: record2 }; const definition = erased(fit.task); try { if (definition.migrate === void 0) - throw missingMigration(record, definition); - const migrated = definition.migrate(record.input, record.state.checkpoint, record.version); - const state = { ...record.state, checkpoint: copyJson(migrated.checkpoint) }; - const migratedRecord = { ...record, version: definition.version, input: copyJson(migrated.input), state }; + throw missingMigration(record2, definition); + const migrated = definition.migrate(record2.input, record2.state.checkpoint, record2.version); + const state = { ...record2.state, checkpoint: copyJson(migrated.checkpoint) }; + const migratedRecord = { ...record2, version: definition.version, input: copyJson(migrated.input), state }; return { kind: "ready", task: fit.task, record: migratedRecord }; } catch (error) { - this.#failedMigrations.set(record.id, { task: fit.task, error }); + this.#failedMigrations.set(record2.id, { task: fit.task, error }); queueMicrotask(() => this.#notifyBlocked()); this.#scheduleWake(); const extension = installerOf(snapshot, fit.task); - this.#report(error, { kind: "migration_failed", ...this.#aboutTask({ ...recordTask(record), extension }) }); + this.#report(error, { kind: "migration_failed", ...this.#aboutTask({ ...recordTask(record2), extension }) }); return { kind: "blocked", reason: "migration_failed" }; } } - #fit(record, task) { + #fit(record2, task) { if (task === void 0) return { reason: "missing_task" }; const version = task.definition.version; - if (version === record.version) + if (version === record2.version) return { task, migrates: false }; - if (version < record.version) + if (version < record2.version) return { reason: "task_too_old" }; - const failed = this.#failedMigrations.get(record.id); + const failed = this.#failedMigrations.get(record2.id); if (failed?.task === task) return { reason: "migration_failed", error: failed.error }; return { task, migrates: true }; @@ -22672,49 +22672,49 @@ var TaskScheduler = class { await this.#published(); await this.#loadScopes(false); const tasks = []; - for (const record of this.#live.values()) - tasks.push({ record, state: this.#inspectTask(record, snapshot) }); + for (const record2 of this.#live.values()) + tasks.push({ record: record2, state: this.#inspectTask(record2, snapshot) }); const scheduling = this.#closing ? "closing" : this.#enabled && !this.#held ? "running" : "paused"; return { scheduling, tasks }; } - #inspectTask(record, snapshot) { - if (this.#invocations.has(record.id)) + #inspectTask(record2, snapshot) { + if (this.#invocations.has(record2.id)) return { kind: "running" }; - if (record.state.status === "completing") + if (record2.state.status === "completing") return { kind: "completing" }; - const on = this.#waitingOn(record); + const on = this.#waitingOn(record2); if (on.length > 0) return { kind: "waiting", on }; - if (this.#notDue(record)) - return { kind: "sleeping", until: record.state.until }; - const fit = this.#fit(record, snapshot.task(record.kind)); + if (this.#notDue(record2)) + return { kind: "sleeping", until: record2.state.until }; + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return { kind: "blocked", ...fit }; if (fit.migrates && fit.task.definition.migrate === void 0) { - return { kind: "blocked", reason: "migration_failed", error: missingMigration(record, erased(fit.task)) }; + return { kind: "blocked", reason: "migration_failed", error: missingMigration(record2, erased(fit.task)) }; } return { kind: "pending", migrates: fit.migrates }; } - #createInvocation(record, mode, extension) { + #createInvocation(record2, mode, extension) { const controller = new AbortController(); const { promise: done, resolve: finish2 } = Promise.withResolvers(); const invocation = { - taskId: record.id, - kind: record.kind, + taskId: record2.id, + kind: record2.kind, extension, - conversationId: record.conversationId, + conversationId: record2.conversationId, mode, controller, - links: record.startedAt === void 0 ? record.links : void 0, + links: record2.startedAt === void 0 ? record2.links : void 0, context: withAbortSignal(controller.signal, this.#context), - record, + record: record2, watches: /* @__PURE__ */ new Set(), timers: /* @__PURE__ */ new Set(), ended: false, done, finish: () => finish2() }; - this.#invocations.set(record.id, invocation); + this.#invocations.set(record2.id, invocation); return invocation; } #start(reservation) { @@ -22925,14 +22925,14 @@ var TaskScheduler = class { * Write an outcome the scheduler decided. While the task's ordinary owned work is live it holds as `completing` and * its Harness cleanup waits for the final commit (spec §5.5, rule 4); otherwise it is terminal with its cleanup. */ - async #terminate(tx, record, outcome) { + async #terminate(tx, record2, outcome) { await this.#loadScopes(false); - if (await this.#region(() => this.#hasOwnedLive(record.id, overlayOf(tx)), this.#reloadScopes)) { - tx.setTask(withState(record, { status: "completing", outcome })); + if (await this.#region(() => this.#hasOwnedLive(record2.id, overlayOf(tx)), this.#reloadScopes)) { + tx.setTask(withState(record2, { status: "completing", outcome })); return; } - tx.setTask(withState(record, { status: "terminal", outcome })); - await this.#settleOutcome(tx, record, outcome); + tx.setTask(withState(record2, { status: "terminal", outcome })); + await this.#settleOutcome(tx, record2, outcome); } /** * Replace a running task's state with what it committed. A terminal state holds as `completing` while ordinary owned @@ -22947,8 +22947,8 @@ var TaskScheduler = class { if (next.status === "terminal") { const overlay = overlayOf(tx); await this.#loadScopes(false); - for (const record of overlay.tasks.values()) - await this.#loadChain(parentOf(record), overlay); + for (const record2 of overlay.tasks.values()) + await this.#loadChain(parentOf(record2), overlay); if (await this.#region(() => this.#hasOwnedLive(current.id, overlayOf(tx)), this.#reloadScopes)) { tx.setTask(withState(progressed(current), { status: "completing", outcome: next.outcome })); return; @@ -23042,11 +23042,11 @@ var TaskScheduler = class { for (let id = pending.pop(); id !== void 0; id = pending.pop()) { if (id === task) return path(id); - const record = overlay.tasks.get(id) ?? this.#liveRecord(id); - if (record === void 0 || record.state.status === "terminal") + const record2 = overlay.tasks.get(id) ?? this.#liveRecord(id); + if (record2 === void 0 || record2.state.status === "terminal") continue; - if (record.state.status === "waiting" && !record.abortRequested) { - for (const member of record.state.on) + if (record2.state.status === "waiting" && !record2.abortRequested) { + for (const member of record2.state.on) reach(member, id); } if (walked.has(id)) @@ -23088,8 +23088,8 @@ var TaskScheduler = class { if (conversationId === void 0) this.#record(ROOTS_KEY); for (const id of this.#unloaded) { - const record = this.#live.get(id); - if (record !== void 0 && !record.background && this.#inScope(parentOf(record), scope) !== false) + const record2 = this.#live.get(id); + if (record2 !== void 0 && !record2.background && this.#inScope(parentOf(record2), scope) !== false) return false; } const starts = conversationId === void 0 ? this.#roots : [conversationId]; @@ -23210,21 +23210,21 @@ var TaskScheduler = class { this.#record(ownedKey(invocation.taskId)); const records = []; for (const node of this.#below.get(invocation.taskId) ?? []) { - const record = typeof node === "string" ? void 0 : this.#liveRecord(node); - if (record?.owner === invocation.taskId) - records.push(record); + const record2 = typeof node === "string" ? void 0 : this.#liveRecord(node); + if (record2?.owner === invocation.taskId) + records.push(record2); } return records; }); - return owned.sort((a, b) => a.id - b.id).map((record) => copyJson(record)); + return owned.sort((a, b) => a.id - b.id).map((record2) => copyJson(record2)); })), abortOwned: (id, context) => this.#read(invocation, async () => { const bound = withAbortSignal(invocation.controller.signal, context); - const record = await this.#session.readOnLine((storage2) => storage2.task(id, bound)); - if (record?.owner !== invocation.taskId) { + const record2 = await this.#session.readOnLine((storage2) => storage2.task(id, bound)); + if (record2?.owner !== invocation.taskId) { throw new Error(`Task ${id} is not owned by task ${invocation.taskId}`); } - if (record.state.status === "terminal") + if (record2.state.status === "terminal") return; await this.abort(id, bound, true); await this.waitForTask(id, bound); @@ -23370,8 +23370,8 @@ function installerOf(snapshot, task) { function reportTask(task) { return { id: task.taskId, kind: task.kind, conversationId: task.conversationId }; } -function recordTask(record) { - return { taskId: record.id, kind: record.kind, conversationId: record.conversationId }; +function recordTask(record2) { + return { taskId: record2.id, kind: record2.kind, conversationId: record2.conversationId }; } var endedByCrashesStates = /* @__PURE__ */ new WeakMap(); function endedByCrashes(state, message) { @@ -23384,33 +23384,33 @@ function interruptionOf(error) { return void 0; return detail.reason === "restart_limit" || detail.reason === "interrupted" ? detail.reason : void 0; } -function withAbortMark(record, reason) { - const { abortReason: _reason, restarts: _restarts, ...rest } = record; +function withAbortMark(record2, reason) { + const { abortReason: _reason, restarts: _restarts, ...rest } = record2; return { ...rest, abortRequested: true, ...reason === void 0 ? {} : { abortReason: reason } }; } -function cancellationIntent(record) { - return record.state.status !== "terminal" && (record.abortRequested || failedOutcome(record)); +function cancellationIntent(record2) { + return record2.state.status !== "terminal" && (record2.abortRequested || failedOutcome(record2)); } function intentChanges(previous, next) { return (previous?.abortRequested ?? false) !== next.abortRequested || previous?.abortReason !== next.abortReason || (previous !== void 0 && failedOutcome(previous)) !== failedOutcome(next) || (previous !== void 0 && cancellationIntent(previous)) !== cancellationIntent(next); } -function failedOutcome(record) { - const state = record.state; +function failedOutcome(record2) { + const state = record2.state; return (state.status === "completing" || state.status === "terminal") && state.outcome.status !== "completed"; } function parentOf(node) { return node.owner !== void 0 ? { task: node.owner } : { conversation: node.conversationId }; } -function nodeOf(record) { +function nodeOf(record2) { return { - conversationId: record.conversationId, - ...record.owner === void 0 ? {} : { owner: record.owner }, - background: record.background + conversationId: record2.conversationId, + ...record2.owner === void 0 ? {} : { owner: record2.owner }, + background: record2.background }; } function overlayOf(tx) { - const tasks = new Map(tx.stagedTasks().map((record) => [record.id, record])); - const edges = new Map(tx.stagedConversations().map((record) => [record.id, record.owner?.taskId ?? null])); + const tasks = new Map(tx.stagedTasks().map((record2) => [record2.id, record2])); + const edges = new Map(tx.stagedConversations().map((record2) => [record2.id, record2.owner?.taskId ?? null])); return { tasks, edges }; } function addTo(map2, key, value2) { @@ -23428,15 +23428,15 @@ function deleteFrom(map2, key, value2) { if (values.size === 0) map2.delete(key); } -function memoOf(record, name) { - const memos = record?.memos; +function memoOf(record2, name) { + const memos = record2?.memos; return memos !== void 0 && Object.hasOwn(memos, name) ? memos[name] : void 0; } function sessionMethod(session, name) { return session[name].bind(session); } -function missingMigration(record, definition) { - return new Error(`Task ${record.kind} version ${definition.version} has no migration from ${record.version}`); +function missingMigration(record2, definition) { + return new Error(`Task ${record2.kind} version ${definition.version} has no migration from ${record2.version}`); } function erased(task) { return task.definition; @@ -23444,26 +23444,26 @@ function erased(task) { function endedError(invocation) { return new Error(`Task ${invocation.taskId} invocation has ended`); } -function progressed(record) { - if (record.restarts === void 0) - return record; - const { restarts: _restarts, ...rest } = record; +function progressed(record2) { + if (record2.restarts === void 0) + return record2; + const { restarts: _restarts, ...rest } = record2; return rest; } -function withState(record, state) { +function withState(record2, state) { if (state.status !== "terminal" && state.status !== "completing") - return { ...record, state }; - const { memos: _memos, restarts: _restarts, ...rest } = record; + return { ...record2, state }; + const { memos: _memos, restarts: _restarts, ...rest } = record2; return { ...rest, state }; } -function outdatedReason(task, record) { +function outdatedReason(task, record2) { if (task === void 0) return "missing_task"; - return task.definition.version < record.version ? "task_too_old" : "migration_failed"; + return task.definition.version < record2.version ? "task_too_old" : "migration_failed"; } -function canReserve(task, record) { +function canReserve(task, record2) { const definition = task.definition; - return definition.version === record.version || definition.version > record.version && definition.migrate !== void 0; + return definition.version === record2.version || definition.version > record2.version && definition.migrate !== void 0; } function validateSleep(invocation, task, until2) { if (invocation.mode === "abort") @@ -24962,7 +24962,7 @@ function structuredOutputError(tool, result) { return `Tool ${tool.name} returned structuredOutput that does not match its schema: ${path}: ${first?.message ?? "invalid"}`; } async function settle2(runtime, input, call, ending, result, context, durationMs) { - const nestedIds = (await runtime.ownedTasks(context)).filter((record) => record.kind === ToolTask.definition.name && record.input.kind === "nested").map((record) => record.id); + const nestedIds = (await runtime.ownedTasks(context)).filter((record2) => record2.kind === ToolTask.definition.name && record2.input.kind === "nested").map((record2) => record2.id); const madeNested = await runtime.memo(MADE_NESTED_CALLS, context) === true; await Promise.all(nestedIds.map((id) => runtime.abortOwned(id, context))); await runtime.commit(async (tx) => { @@ -26097,33 +26097,33 @@ function clearProgress(slot) { delete slot.details; delete slot.diagnostics; } -async function settleSchedulerOutcome(tx, record, outcome) { - if (record.kind === TOOL_TASK_KIND) { - const live2 = await tx.doc(LiveDoc, record.conversationId); - const slot = toolSlot(live2, record.id); +async function settleSchedulerOutcome(tx, record2, outcome) { + if (record2.kind === TOOL_TASK_KIND) { + const live2 = await tx.doc(LiveDoc, record2.conversationId); + const slot = toolSlot(live2, record2.id); if (slot !== void 0) finishSlot(slot); - removeNestedSlots(live2, record.id); + removeNestedSlots(live2, record2.id); return; } const detail = outcome.status === "faulted" ? outcome.error.detail : void 0; const cutOff = typeof detail === "object" && detail !== null && "reason" in detail && detail.reason === "restart_limit"; - if (record.kind === COMPACTION_TASK_KIND) { - const live2 = await tx.doc(LiveDoc, record.conversationId); + if (record2.kind === COMPACTION_TASK_KIND) { + const live2 = await tx.doc(LiveDoc, record2.conversationId); if (cutOff) - removeCompactionStatus(live2, record.id); + removeCompactionStatus(live2, record2.id); else - failCompactionStatus(live2, record.id, outcome.status === "faulted" ? outcome.error.message : outcome.reason); + failCompactionStatus(live2, record2.id, outcome.status === "faulted" ? outcome.error.message : outcome.reason); return; } - if (!RUN_TASK_KINDS.has(record.kind)) + if (!RUN_TASK_KINDS.has(record2.kind)) return; - const live = await tx.doc(LiveDoc, record.conversationId); - if (live.run?.taskId !== record.id) + const live = await tx.doc(LiveDoc, record2.conversationId); + if (live.run?.taskId !== record2.id) return; if (!cutOff) - await convertPartial(tx, live, record.conversationId); - endRun(tx, live, record.id, outcome.status === "faulted" ? { status: "unanswered", reason: cutOff ? "aborted" : "faulted", detail: outcome.error.message } : { + await convertPartial(tx, live, record2.conversationId); + endRun(tx, live, record2.id, outcome.status === "faulted" ? { status: "unanswered", reason: cutOff ? "aborted" : "faulted", detail: outcome.error.message } : { status: "unanswered", reason: "orphaned", detail: outcome.reason, @@ -26194,23 +26194,23 @@ var Submissions = class { } /** Handle for an existing submission, or `undefined`. */ async get(id, context) { - const record = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); - return record === void 0 ? void 0 : new SubmissionHandle(record.id, this); + const record2 = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); + return record2 === void 0 ? void 0 : new SubmissionHandle(record2.id, this); } async status(id, context) { - const record = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); - if (record === void 0) + const record2 = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); + if (record2 === void 0) throw new Error(`Submission ${id} does not exist`); - return record; + return record2; } async wait(id, context) { this.#resume(); const found = await this.#session.readOnLine(async (storage2) => { - const record = await storage2.submission(id, context); - if (record === void 0) + const record2 = await storage2.submission(id, context); + if (record2 === void 0) throw new Error(`Submission ${id} does not exist`); - if (isSettled(record)) - return { promise: Promise.resolve(record) }; + if (isSettled(record2)) + return { promise: Promise.resolve(record2) }; if (this.#closed) throw closedError(this.#session); return { promise: this.#waiters.add(id, context) }; @@ -26220,16 +26220,16 @@ var Submissions = class { /** Withdraw a queued submission and remove its inbox item; placed inputs and settled submissions are reported. */ abort(id, context, conversationId) { return this.#session.commitWith(async (tx) => { - const record = await tx.submission(id); - if (record === void 0 || conversationId !== void 0 && record.conversationId !== conversationId) { + const record2 = await tx.submission(id); + if (record2 === void 0 || conversationId !== void 0 && record2.conversationId !== conversationId) { return "not_found"; } - if (record.status === "queued") { + if (record2.status === "queued") { tx.settleSubmission(id, { status: "unanswered", reason: "aborted" }); - await removeInboxItem(tx, record.conversationId, id); + await removeInboxItem(tx, record2.conversationId, id); return "aborted"; } - return record.status === "placed" ? "already_placed" : "settled"; + return record2.status === "placed" ? "already_placed" : "settled"; }, context); } #observe(publication) { @@ -26262,8 +26262,8 @@ var SubmissionHandle = class { return result; } }; -function isSettled(record) { - return record.status === "done" || record.status === "unanswered"; +function isSettled(record2) { + return record2.status === "done" || record2.status === "unanswered"; } function checkInputEntry(entry2) { const { kind } = entry2; @@ -26918,8 +26918,8 @@ var TaskGraphView = class { if (mount === void 0) return; const ops2 = []; - for (const [key, record] of mount.records) { - const blocked = this.#blocking.blocked(record); + for (const [key, record2] of mount.records) { + const blocked = this.#blocking.blocked(record2); if (blocked === mount.value.tasks[key]?.blocked) continue; ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]); @@ -26937,12 +26937,12 @@ var TaskGraphView = class { records.push(...await scanAll((cursor) => storage2.scanTasks({ status }, SCAN_PAGE_SIZE4, cursor, context))); } records.sort((a, b) => a.id - b.id); - for (const record of records) { - const owned = await scanAll((cursor) => storage2.scanConversations({ ownerTaskId: record.id }, SCAN_PAGE_SIZE4, cursor, context)); + for (const record2 of records) { + const owned = await scanAll((cursor) => storage2.scanConversations({ ownerTaskId: record2.id }, SCAN_PAGE_SIZE4, cursor, context)); const conversations = owned.map((conversation) => conversation.id).sort((a, b) => a - b); - const subject = blockedSubject(record); - tasks[String(record.id)] = nodeOf2(record, conversations, this.#blocking.blocked(subject)); - live.set(String(record.id), subject); + const subject = blockedSubject(record2); + tasks[String(record2.id)] = nodeOf2(record2, conversations, this.#blocking.blocked(subject)); + live.set(String(record2.id), subject); } return { value: { tasks }, records: live }; } @@ -26954,10 +26954,10 @@ function advance(mount, publication, context, blocked, report2) { for (const change of publication.changes) { if (change.type !== "task") continue; - const record = change.value; - const key = String(record.id); + const record2 = change.value; + const key = String(record2.id); const previous = node(key); - if (record.state.status === "terminal") { + if (record2.state.status === "terminal") { mount.records.delete(key); if (previous === void 0) continue; @@ -26965,9 +26965,9 @@ function advance(mount, publication, context, blocked, report2) { changed.set(key, void 0); continue; } - const subject = blockedSubject(record); + const subject = blockedSubject(record2); mount.records.set(key, subject); - const next = nodeOf2(record, previous?.conversations ?? [], blocked(subject)); + const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject)); if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous)) continue; ops2.push(["s", ["tasks", key], next]); @@ -26996,21 +26996,21 @@ function publish(mount, ops2, context, report2) { for (const observer of [...mount.observers]) contained(() => observer.advance(call, frameContext), report2); } -function nodeOf2(record, conversations, blocked) { +function nodeOf2(record2, conversations, blocked) { return { - id: record.id, - kind: record.kind, - conversationId: record.conversationId, - ...record.owner === void 0 ? {} : { owner: record.owner }, - background: record.background, - abortRequested: record.abortRequested, - state: stateOf(record), + id: record2.id, + kind: record2.kind, + conversationId: record2.conversationId, + ...record2.owner === void 0 ? {} : { owner: record2.owner }, + background: record2.background, + abortRequested: record2.abortRequested, + state: stateOf(record2), conversations, ...blocked === void 0 ? {} : { blocked } }; } -function stateOf(record) { - const state = record.state; +function stateOf(record2) { + const state = record2.state; switch (state.status) { case "pending": case "running": @@ -27293,8 +27293,8 @@ var ConversationImpl = class { return readContext(this.#host.harness, this.#host.storage, this.id, context, options?.at); } entries(query, limit, cursor, context) { - const scan = conversationEntries(this.id, query); - return this.#host.harness.readOnLine((storage2) => storage2.scanEntries(scan, limit, cursor, context)); + const scan2 = conversationEntries(this.id, query); + return this.#host.harness.readOnLine((storage2) => storage2.scanEntries(scan2, limit, cursor, context)); } fork(at, options, context) { return this.#host.create({ kind: "fork", parentId: this.id, at, ownership: options.ownership }, options, context); @@ -27358,15 +27358,15 @@ var HarnessImpl = class extends SessionImpl { settleOutcome: settleSchedulerOutcome, withdrawInputs: withdrawQueuedInputs, conversation: async (id, binding, callContext) => { - const record = await this.readOnLine((line) => line.conversation(id, callContext)); - return record === void 0 ? void 0 : boundConversation(id, binding, this.#submissions, this.#tasks); + const record2 = await this.readOnLine((line) => line.conversation(id, callContext)); + return record2 === void 0 ? void 0 : boundConversation(id, binding, this.#submissions, this.#tasks); }, context: withoutAbortSignal(context), readOnly: options.readOnly === true }); this.#submissions = new Submissions(this, now, settings, () => this.#tasks.resume(), options.telemetry); this.#taskGraph = new TaskGraphView(this, { - blocked: (record) => this.#tasks.blockedReason(record), + blocked: (record2) => this.#tasks.blockedReason(record2), subscribeBlocked: (listener) => this.#tasks.subscribeBlocked(listener) }); this.#host = { @@ -27434,8 +27434,8 @@ var HarnessImpl = class extends SessionImpl { inspect(context) { return this.readOnLine(async (storage2) => { const { scheduling, tasks } = await this.#tasks.inspect(this.#options.registry.snapshot()); - const scan = (status) => scanAll((cursor) => storage2.scanSubmissions({ status }, SCAN_PAGE_SIZE6, cursor, context)); - const submissions = [...await scan("queued"), ...await scan("placed")].sort((a, b) => a.id - b.id); + const scan2 = (status) => scanAll((cursor) => storage2.scanSubmissions({ status }, SCAN_PAGE_SIZE6, cursor, context)); + const submissions = [...await scan2("queued"), ...await scan2("placed")].sort((a, b) => a.id - b.id); return { scheduling, tasks, submissions }; }); } @@ -27483,8 +27483,8 @@ var HarnessImpl = class extends SessionImpl { } async conversation(id, context) { this.assertUsable(); - const record = await this.readOnLine((storage2) => storage2.conversation(id, context)); - return record === void 0 ? void 0 : new ConversationImpl(record.id, this.#host); + const record2 = await this.readOnLine((storage2) => storage2.conversation(id, context)); + return record2 === void 0 ? void 0 : new ConversationImpl(record2.id, this.#host); } createConversation(options, context) { return this.#create({ kind: "independent", ownership: options.ownership }, options, context); @@ -27506,12 +27506,12 @@ var HarnessImpl = class extends SessionImpl { if (target.kind === "root" && await tx.conversation(ROOT_CONVERSATION_ID) !== void 0) { return ROOT_CONVERSATION_ID; } - const record = target.kind === "root" ? await tx.createRootConversation() : target.kind === "fork" ? await tx.forkConversation(target.parentId, target.at, { ownership: target.ownership }) : await tx.createConversation({ ownership: target.ownership }); + const record2 = target.kind === "root" ? await tx.createRootConversation() : target.kind === "fork" ? await tx.forkConversation(target.parentId, target.at, { ownership: target.ownership }) : await tx.createConversation({ ownership: target.ownership }); if (options.agent !== void 0) - await configure(tx, record.id, options.agent); + await configure(tx, record2.id, options.agent); if (options.init !== void 0) - await options.init(tx, record.id); - return record.id; + await options.init(tx, record2.id); + return record2.id; }, context); return new ConversationImpl(id, this.#host); } @@ -27520,13 +27520,13 @@ var HarnessImpl = class extends SessionImpl { * `pi.usage`, a fresh `pi.provider`, the conversation's `pi.agent` (see `createAgent()`), then * `HarnessOptions.conversationCreated`. */ - async conversationCreated(tx, record) { - await tx.doc(LiveDoc, record.id); - await tx.doc(InboxDoc, record.id); - await tx.doc(UsageDoc, record.id); - await tx.doc(ProviderDoc, record.id); - await createAgent(tx, record); - await this.#options.conversationCreated?.(tx, record); + async conversationCreated(tx, record2) { + await tx.doc(LiveDoc, record2.id); + await tx.doc(InboxDoc, record2.id); + await tx.doc(UsageDoc, record2.id); + await tx.doc(ProviderDoc, record2.id); + await createAgent(tx, record2); + await this.#options.conversationCreated?.(tx, record2); } }; function safeReport(onReport, telemetry) { @@ -28013,13 +28013,13 @@ var addressKey = (address) => JSON.stringify([ scopeKey2(address.scope), address.key === void 0 ? ["singleton"] : ["family", address.key] ]); -var recordAddressKey = (record) => addressKey({ kind: record.kind, scope: record.scope, key: record.key }); -var isAliveAt = (record, at) => { +var recordAddressKey = (record2) => addressKey({ kind: record2.kind, scope: record2.scope, key: record2.key }); +var isAliveAt = (record2, at) => { if (at === "current") - return record.retiredAt === void 0; - return record.createdAt <= at && (record.retiredAt === void 0 || at < record.retiredAt); + return record2.retiredAt === void 0; + return record2.createdAt <= at && (record2.retiredAt === void 0 || at < record2.retiredAt); }; -var isCurrentOnly = (record) => record.scope.kind !== "conversation" || record.history === "latest"; +var isCurrentOnly = (record2) => record2.scope.kind !== "conversation" || record2.history === "latest"; var tableContaining = (state, id) => state.recordTypes.get(id); var page = (values, limit, order = "ascending") => { const items = values.slice(0, limit); @@ -28382,9 +28382,9 @@ var MemoryStorage = class { return index?.currentId === void 0 ? void 0 : clone(this.state.documents.get(index.currentId).record); } for (const id of index?.ids ?? []) { - const record = this.state.documents.get(id).record; - if (isAliveAt(record, at)) - return clone(record); + const record2 = this.state.documents.get(id).record; + if (isAliveAt(record2, at)) + return clone(record2); } return void 0; } @@ -28405,11 +28405,11 @@ var MemoryStorage = class { const start = after === void 0 ? 0 : upperBound(ids, after); const values = []; for (let index = start; index < ids.length && values.length <= limit; index++) { - const record = this.state.documents.get(ids[index]).record; - if (query.kind !== void 0 && record.kind !== query.kind) + const record2 = this.state.documents.get(ids[index]).record; + if (query.kind !== void 0 && record2.kind !== query.kind) continue; - if (isAliveAt(record, query.at)) - values.push(record); + if (isAliveAt(record2, query.at)) + values.push(record2); } return page(values, limit); } @@ -28583,25 +28583,25 @@ var MemoryStorage = class { for (const [id, action] of actions) { let stored = this.state.documents.get(id); if (action.create !== void 0) { - const record = { + const record2 = { ...action.create, createdAt: seq, ...action.retire ? { retiredAt: seq } : {} }; - stored = { record, revisions: [{ ...action.content, seq }] }; + stored = { record: record2, revisions: [{ ...action.content, seq }] }; this.state.recordTypes.set(id, "document"); this.state.documents.set(id, stored); - const key = recordAddressKey(record); + const key = recordAddressKey(record2); let address = this.state.documentAddresses.get(key); if (address === void 0) { address = { ids: [] }; this.state.documentAddresses.set(key, address); } insertSorted(address.ids, id); - let scopeIds = this.state.documentIdsByScope.get(scopeKey2(record.scope)); + let scopeIds = this.state.documentIdsByScope.get(scopeKey2(record2.scope)); if (scopeIds === void 0) { scopeIds = []; - this.state.documentIdsByScope.set(scopeKey2(record.scope), scopeIds); + this.state.documentIdsByScope.set(scopeKey2(record2.scope), scopeIds); } insertSorted(scopeIds, id); this.nextId = Math.max(this.nextId, id + 1); @@ -28871,12 +28871,12 @@ var addressKey2 = (address) => { const parts = addressParts(address); return JSON.stringify([parts.kind, parts.scopeKind, parts.ownerId, parts.family, parts.keyValue]); }; -var isAliveAt2 = (record, at) => { +var isAliveAt2 = (record2, at) => { if (at === "current") - return record.retiredAt === void 0; - return record.createdAt <= at && (record.retiredAt === void 0 || at < record.retiredAt); + return record2.retiredAt === void 0; + return record2.createdAt <= at && (record2.retiredAt === void 0 || at < record2.retiredAt); }; -var isCurrentOnly2 = (record) => record.scope.kind !== "conversation" || record.history === "latest"; +var isCurrentOnly2 = (record2) => record2.scope.kind !== "conversation" || record2.history === "latest"; var writeId = (write) => { switch (write.type) { case "conversation": @@ -29014,9 +29014,9 @@ var SqliteStorage = class _SqliteStorage { async scanConversations(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.ownerConversationId !== void 0) { clauses.push("owner_conversation_id = ?"); params.push(query.ownerConversationId); @@ -29026,7 +29026,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.ownerTaskId); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.conversations} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.conversations} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } entry(idOrConversationId, idOrContext, context) { @@ -29162,9 +29162,9 @@ var SqliteStorage = class _SqliteStorage { async scanTasks(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.conversationId !== void 0) { clauses.push("conversation_id = ?"); params.push(query.conversationId); @@ -29186,7 +29186,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.background ? 1 : 0); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.tasks} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.tasks} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } async submission(id, _context) { @@ -29197,9 +29197,9 @@ var SqliteStorage = class _SqliteStorage { async scanSubmissions(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.conversationId !== void 0) { clauses.push("conversation_id = ?"); params.push(query.conversationId); @@ -29209,7 +29209,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.status); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.submissions} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.submissions} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } async submissionByRequest(conversationId, requestId, _context) { @@ -29292,11 +29292,11 @@ var SqliteStorage = class _SqliteStorage { const row = await executor.get(`SELECT record FROM ${this.schema.tables.documents} WHERE id = ?`, id); if (row === void 0) return void 0; - const record = parseJson(row.record); - if (at !== "current" && isCurrentOnly2(record)) { + const record2 = parseJson(row.record); + if (at !== "current" && isCurrentOnly2(record2)) { throw new StorageRequestError(`Document ${id} does not retain historical content`); } - if (!isAliveAt2(record, at)) + if (!isAliveAt2(record2, at)) return void 0; const upper = at === "current" ? Number.MAX_SAFE_INTEGER : at; const base = await executor.get(`SELECT seq, kind, version, content FROM ${this.schema.tables.documentRevisions} @@ -29312,7 +29312,7 @@ var SqliteStorage = class _SqliteStorage { } value2 = apply(value2, parseJson(revision.content)); } - return { record, version: base.version, value: value2, deltasSinceBase: tail.length }; + return { record: record2, version: base.version, value: value2, deltasSinceBase: tail.length }; } candidateNextId(writes) { let nextId = this.nextId; @@ -29411,11 +29411,11 @@ var SqliteStorage = class _SqliteStorage { throw new Error(`Document ${id} version transition requires a base`); } } - const record = action.create ?? existing; - const key = addressKey2(record); + const record2 = action.create ?? existing; + const key = addressKey2(record2); let live = liveCounts.get(key); if (live === void 0) - live = await this.currentDocumentId(executor, record) === void 0 ? 0 : 1; + live = await this.currentDocumentId(executor, record2) === void 0 ? 0 : 1; if (action.retire && existing !== void 0) live--; if (action.create !== void 0 && !action.retire) @@ -29490,24 +29490,24 @@ var SqliteStorage = class _SqliteStorage { async applyDocumentActions(executor, actions, seq) { for (const [id, action] of actions) { const content = action.content; - let record; + let record2; if (action.create !== void 0) { - record = { + record2 = { ...action.create, createdAt: seq, ...action.retire ? { retiredAt: seq } : {} }; - const parts = addressParts(record); + const parts = addressParts(record2); await this.claimId(executor, id, "document"); await executor.run(`INSERT INTO ${this.schema.tables.documents} (id, kind, family, key_value, scope_kind, owner_id, created_at, retired_at, record) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, parts.kind, parts.family, parts.keyValue, parts.scopeKind, parts.ownerId, seq, action.retire ? seq : null, encodeJson(record)); + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, parts.kind, parts.family, parts.keyValue, parts.scopeKind, parts.ownerId, seq, action.retire ? seq : null, encodeJson(record2)); } else { const row = await executor.get(`SELECT record FROM ${this.schema.tables.documents} WHERE id = ?`, id); - record = parseJson(row.record); + record2 = parseJson(row.record); } if (content !== void 0) { - if (content.kind === "base" && isCurrentOnly2(record)) { + if (content.kind === "base" && isCurrentOnly2(record2)) { await executor.run(`DELETE FROM ${this.schema.tables.documentRevisions} WHERE document_id = ?`, id); } const encodedContent = content.kind === "base" ? encodeJson(content.value) : encodeJson(content.ops); @@ -29515,10 +29515,10 @@ var SqliteStorage = class _SqliteStorage { } if (action.retire) { if (action.create === void 0) { - record = { ...record, retiredAt: seq }; - await executor.run(`UPDATE ${this.schema.tables.documents} SET retired_at = ?, record = ? WHERE id = ?`, seq, encodeJson(record), id); + record2 = { ...record2, retiredAt: seq }; + await executor.run(`UPDATE ${this.schema.tables.documents} SET retired_at = ?, record = ? WHERE id = ?`, seq, encodeJson(record2), id); } - if (isCurrentOnly2(record)) { + if (isCurrentOnly2(record2)) { await executor.run(`DELETE FROM ${this.schema.tables.documentRevisions} WHERE document_id = ?`, id); } } @@ -30941,8 +30941,8 @@ async function readText(reader, path, offset, limit, context) { const requestedEnd = limit === void 0 ? void 0 : Math.max(scanStart2 + 1, sliceIndex(startLine + limit)); const scanEnd = requestedEnd !== void 0 && Number.isSafeInteger(requestedEnd) ? requestedEnd : void 0; const scanOf = async (endLine) => getOrThrow(await reader.scanLines({ startLine: scanStart2, ...endLine === void 0 ? {} : { endLine } }, context)); - let scan = await scanOf(scanEnd); - const totalFileLines = scan.newlines + 1; + let scan2 = await scanOf(scanEnd); + const totalFileLines = scan2.newlines + 1; if (startLine >= totalFileLines) { throw new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`); } @@ -30955,16 +30955,16 @@ async function readText(reader, path, offset, limit, context) { const sliceEnd = relativeEnd < 0 ? Math.max(totalFileLines + relativeEnd, 0) : relativeEnd; selectedLineCount = Math.max(0, sliceEnd - sliceStart); if (selectedLineCount > 0 && relativeEnd < 0) - scan = await scanOf(sliceEnd); + scan2 = await scanOf(sliceEnd); } const empty = selectedLineCount === 0; - const endsWithNewline = !empty && scan.lastLineStart === scan.end && scan.lastLineStart > scan.start; + const endsWithNewline = !empty && scan2.lastLineStart === scan2.end && scan2.lastLineStart > scan2.start; const totals = { - lines: empty || scan.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0), - bytes: empty ? 0 : scan.selectedBytes + lines: empty || scan2.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0), + bytes: empty ? 0 : scan2.selectedBytes }; const firstBytes = getOrThrow(await reader.read(0, 3, context)); - const head = empty ? "" : await readHead(reader, scan.start, scan.end, startsWithBom(firstBytes), context); + const head = empty ? "" : await readHead(reader, scan2.start, scan2.end, startsWithBom(firstBytes), context); const { content: headText, ...truncation } = truncateHeadOf(head, totals); const diagnostics = []; let outputText = headText; @@ -30972,7 +30972,7 @@ async function readText(reader, path, offset, limit, context) { if (truncation.firstLineExceedsLimit) { const integral = Number.isInteger(startLine); const lineBytes = new TextEncoder().encode(integral ? head.split("\n")[0] ?? "" : ""); - const lineSize = integral ? scan.firstLineBytes : 0; + const lineSize = integral ? scan2.firstLineBytes : 0; const end = characterEnd(lineBytes, DEFAULT_MAX_BYTES); outputText = new TextDecoder().decode(lineBytes.subarray(0, end)); diagnostics.push({ @@ -31198,6 +31198,176 @@ function StringEnum(values, options) { }; } +// events.ts +var events_exports = {}; +__export(events_exports, { + EVENT_FEED: () => EVENT_FEED, + EVENT_FEED_SIZE: () => EVENT_FEED_SIZE, + EventFeed: () => EventFeed, + EventLog: () => EventLog, + NoticeEvent: () => NoticeEvent, + defineEvent: () => defineEvent, + emit: () => emit, + eventLog: () => eventLog +}); +function defineEvent(name, options = {}) { + const kind = `event.${name}`; + return { name, kind, version: options.version ?? 1, is: (event) => event?.kind === kind }; +} +var NoticeEvent = defineEvent("sausage.notice"); +var EVENT_FEED = "sausage.event-feed"; +var EVENT_FEED_SIZE = 100; +var EventLog = defineDoc({ + kind: "sausage.events", + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ cursors: {} }) +}); +var EventFeed = defineDoc({ + kind: EVENT_FEED, + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ events: [] }) +}); +var record = (entry2) => structuredClone({ id: entry2.id, name: entry2.kind.slice(6), kind: entry2.kind, ...entry2.data }); +async function emit(tx, event, data) { + const index = await tx.doc(EventLog, ROOT_CONVERSATION_ID); + if (index.conversation === void 0) index.conversation = (await tx.createConversation({ ownership: { kind: "ownerless" } })).id; + const entry2 = await tx.appendEntry(index.conversation, { kind: event.kind, data: { v: event.version, at: (/* @__PURE__ */ new Date()).toISOString(), data } }); + const emitted = record(entry2); + const feed = (await tx.doc(EventFeed, ROOT_CONVERSATION_ID)).events; + feed.push(emitted); + if (feed.length > EVENT_FEED_SIZE) feed.splice(0, feed.length - EVENT_FEED_SIZE); + return emitted; +} +async function scan(tx, log, after, visit) { + let cursor; + do { + const page3 = await tx.scanEntries({ conversationId: log, minEntryId: after + 1, order: "ascending" }, 256, cursor); + for (const entry2 of page3.items) if (visit(entry2)) return; + cursor = page3.next; + } while (cursor); +} +function eventLog(harness2) { + const reactors = /* @__PURE__ */ new Map(); + function drain2(name) { + const state = reactors.get(name); + state.chain = state.chain.then(async () => { + for (; ; ) { + const active = state.active; + if (!active) return; + let attempted; + try { + const handled = await harness2().commit(async (tx) => { + const index = await tx.doc(EventLog, ROOT_CONVERSATION_ID); + if (index.cursors[name] === void 0 && active.from === "now") { + const newest = index.conversation === void 0 ? void 0 : (await tx.scanEntries({ conversationId: index.conversation }, 1)).items[0]; + index.cursors[name] = newest?.id ?? 0; + return false; + } + if (index.conversation === void 0) return false; + const after = index.cursors[name] ?? 0; + let pending, last = after; + await scan(tx, index.conversation, after, (entry2) => { + last = entry2.id; + if (active.kinds.has(entry2.kind)) pending = entry2; + return pending !== void 0; + }); + if (pending) { + attempted = pending.id; + await active.handler(tx, record(pending)); + } + if (last !== after) index.cursors[name] = last; + return pending !== void 0; + }, BACKGROUND_CONTEXT); + state.failure = void 0; + if (!handled) return; + } catch (error) { + state.failure = { error: String(error), at: (/* @__PURE__ */ new Date()).toISOString(), entryId: attempted }; + return; + } + } + }); + } + return { + emit, + reactor(name, events, handler, options = {}) { + const active = { kinds: new Set(events.map((event) => event.kind)), handler, from: options.from ?? "start" }; + if (!reactors.has(name)) reactors.set(name, { kinds: active.kinds, chain: Promise.resolve() }); + const state = reactors.get(name); + let detach; + const stop2 = () => { + detach?.(); + detach = void 0; + if (state.active === active) state.active = void 0; + return state.chain; + }; + return { + start: async () => { + await stop2(); + state.active = active; + state.kinds = active.kinds; + const observation2 = harness2().observeCommits({ advance: (publication) => { + if (publication.changes.some((change) => change.type === "entry" && active.kinds.has(change.value.kind))) drain2(name); + }, end: () => { + } }); + detach = () => observation2.detach(); + drain2(name); + }, + stop: stop2 + }; + }, + eventLog: { recent, reactors: statuses } + }; + async function recent(options = {}) { + const log = (await harness2().snapshot(EventLog, ROOT_CONVERSATION_ID, BACKGROUND_CONTEXT))?.conversation; + if (log === void 0) return []; + const kinds = options.events && new Set(options.events.map((event) => event.kind)); + const limit = options.limit ?? 20; + return harness2().commit(async (tx) => { + const found = []; + let cursor; + do { + const page3 = await tx.scanEntries({ + conversationId: log, + ...options.since === void 0 ? {} : { minEntryId: options.since + 1 }, + ...options.before === void 0 ? {} : { maxEntryId: options.before - 1 }, + order: options.order ?? "descending" + }, 256, cursor); + for (const entry2 of page3.items) { + if (!(kinds ? kinds.has(entry2.kind) : entry2.kind.startsWith("event."))) continue; + found.push(record(entry2)); + if (found.length >= limit) return found; + } + cursor = page3.next; + } while (cursor); + return found; + }, BACKGROUND_CONTEXT); + } + async function statuses() { + const index = await harness2().snapshot(EventLog, ROOT_CONVERSATION_ID, BACKGROUND_CONTEXT); + return harness2().commit(async (tx) => { + const list = []; + for (const [name, state] of reactors) { + const cursor = index?.cursors[name] ?? 0; + let lag = 0; + if (index?.conversation !== void 0) await scan(tx, index.conversation, cursor, (entry2) => { + if (state.kinds.has(entry2.kind)) lag++; + return false; + }); + list.push({ name, events: [...state.kinds].map((kind) => kind.slice(6)), cursor, lag, running: state.active !== void 0, ...state.failure ? { failure: state.failure } : {} }); + } + return list; + }, BACKGROUND_CONTEXT); + } +} + // extensions.ts var ops = Deno.core.ops; var encoder4 = new TextEncoder(); @@ -31254,12 +31424,13 @@ async function git(cwd, ...args) { return result.stdout.trim(); } function registerModules() { - const modules = { "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports }; + const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports }; globalThis.__sausage = { modules }; ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)]))); } async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) { registerModules(); + const eventService = eventLog(() => harness2); const repo = "/src/sausage"; let providers = /* @__PURE__ */ new Map(); const resources = /* @__PURE__ */ new Map(); @@ -31284,7 +31455,13 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b } }; const scope = (name, staged) => { - const open = [], close = []; + let opened = false; + const notices = /* @__PURE__ */ new Set(); + const open = [async () => { + opened = true; + }], close = [async () => { + opened = false; + }]; const handle = (api) => new Proxy({}, { get: (_, key) => { if (typeof key === "symbol" || key === "then") return void 0; const value2 = providers.get(api.name); @@ -31315,6 +31492,14 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b if (!resources.has(id)) resources.set(id, create()); return resources.get(id); }, + emit: eventService.emit, + eventLog: eventService.eventLog, + react: (key, tokens, handler, options) => { + const reactor = eventService.reactor(`${name}/${key}`, tokens, handler, options); + open.push(reactor.start); + close.push(reactor.stop); + if (opened) void reactor.start(); + }, onOpen: (cb) => open.push(cb), onClose: (cb) => close.push(cb), notice: (message) => { @@ -31322,6 +31507,11 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b ops.op_extension_status(JSON.stringify(state)); ops.op_output(`${name}: ${message} `); + if (!notices.has(message)) { + notices.add(message); + void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)} +`)); + } } }; return { host, open, close }; diff --git a/host/js/package.json b/host/js/package.json index 76d364a..09470cd 100644 --- a/host/js/package.json +++ b/host/js/package.json @@ -1,7 +1,7 @@ { "private": true, "type": "module", - "scripts": { "bundle": "nu apply-patch.nu && esbuild main.ts --bundle --format=esm --platform=browser --outfile=bundle.js && esbuild test.ts --bundle --format=esm --platform=browser --outfile=test-bundle.js && esbuild durable-test.ts --bundle --format=esm --platform=browser --outfile=durable-test-bundle.js && esbuild foundation-test.ts --bundle --format=esm --platform=browser --outfile=foundation-test-bundle.js && esbuild extensions-test.ts --bundle --format=esm --platform=browser --outfile=extensions-test-bundle.js && esbuild events-test.ts --bundle --format=esm --platform=browser --outfile=events-test-bundle.js && esbuild subagents-test.ts --bundle --format=esm --platform=browser --outfile=subagents-test-bundle.js && esbuild core-ext-test.ts --bundle --format=esm --platform=browser --outfile=core-ext-test-bundle.js && esbuild plan-todos-test.ts --bundle --format=esm --platform=browser --outfile=plan-todos-test-bundle.js" }, + "scripts": { "bundle": "nu apply-patch.nu && esbuild main.ts --bundle --format=esm --platform=browser --outfile=bundle.js && esbuild test.ts --bundle --format=esm --platform=browser --outfile=test-bundle.js && esbuild durable-test.ts --bundle --format=esm --platform=browser --outfile=durable-test-bundle.js && esbuild foundation-test.ts --bundle --format=esm --platform=browser --outfile=foundation-test-bundle.js && esbuild extensions-test.ts --bundle --format=esm --platform=browser --outfile=extensions-test-bundle.js && esbuild events-test.ts --bundle --format=esm --platform=browser --outfile=events-test-bundle.js && esbuild subagents-test.ts --bundle --format=esm --platform=browser --outfile=subagents-test-bundle.js && esbuild core-ext-test.ts --bundle --format=esm --platform=browser --outfile=core-ext-test-bundle.js && esbuild plan-todos-test.ts --bundle --format=esm --platform=browser --outfile=plan-todos-test-bundle.js && esbuild p2-test.ts --bundle --format=esm --platform=browser --outfile=p2-test-bundle.js && esbuild chain-test.ts --bundle --format=esm --platform=browser --outfile=chain-test-bundle.js" }, "dependencies": { "@earendil-works/pi-durable": "1.5.0", "@earendil-works/pi-ai": "1.5.0", diff --git a/host/js/plan-todos-test-bundle.js b/host/js/plan-todos-test-bundle.js index 4e72bb9..4bdab3d 100644 --- a/host/js/plan-todos-test-bundle.js +++ b/host/js/plan-todos-test-bundle.js @@ -5781,13 +5781,13 @@ function cleanFrom(schema, value2) { } if (!isPlainObject2(value2)) return value2; - const record = recordOf(schema); + const record2 = recordOf(schema); const properties = isSchemaObject2(schema.properties) ? schema.properties : void 0; - if (properties === void 0 && record === void 0 && !isSchemaObject2(schema.additionalProperties)) + if (properties === void 0 && record2 === void 0 && !isSchemaObject2(schema.additionalProperties)) return value2; for (const key of Object.keys(value2)) { const known = properties !== void 0 && Object.hasOwn(properties, key) ? properties[key] : void 0; - const matched = known ?? (record?.[0].test(key) ? record[1] : void 0); + const matched = known ?? (record2?.[0].test(key) ? record2[1] : void 0); if (matched !== void 0) value2[key] = cleanFrom(asSchema(matched), value2[key]); else if (keepsUnknown(schema.additionalProperties, value2[key])) @@ -5827,7 +5827,7 @@ function withDefaults(schema, value2) { if (!isPlainObject2(defaulted)) return defaulted; const properties = isSchemaObject2(schema.properties) ? schema.properties : void 0; - const record = recordOf(schema); + const record2 = recordOf(schema); if (properties !== void 0) { const required = schema.required ?? []; for (const [key, property] of Object.entries(properties)) { @@ -5836,17 +5836,17 @@ function withDefaults(schema, value2) { continue; defaulted[key] = item; } - } else if (record !== void 0 && isSchemaObject2(record[1]) && "default" in record[1]) { + } else if (record2 !== void 0 && isSchemaObject2(record2[1]) && "default" in record2[1]) { for (const key of Object.keys(defaulted)) { - if (record[0].test(key)) - defaulted[key] = withDefaults(record[1], defaulted[key]); + if (record2[0].test(key)) + defaulted[key] = withDefaults(record2[1], defaulted[key]); } } if (!isSchemaObject2(schema.additionalProperties)) return defaulted; for (const key of Object.keys(defaulted)) { const known = properties !== void 0 && Object.hasOwn(properties, key); - if (!known && !record?.[0].test(key)) + if (!known && !record2?.[0].test(key)) defaulted[key] = withDefaults(schema.additionalProperties, defaulted[key]); } return defaulted; @@ -9057,10 +9057,10 @@ function merge2(previous, next) { // node_modules/@earendil-works/chord/dist/delta/index.js var isObj = (value2) => value2 !== null && typeof value2 === "object"; -function overlap(a, b, scan, probe = 64, maxCandidates = 8) { - if (a.length === 0 || b.length === 0 || scan === 0) +function overlap(a, b, scan2, probe = 64, maxCandidates = 8) { + if (a.length === 0 || b.length === 0 || scan2 === 0) return 0; - const tail = a.length > scan ? a.slice(a.length - scan) : a; + const tail = a.length > scan2 ? a.slice(a.length - scan2) : a; for (const h of [Math.min(probe, b.length), 1]) { const head = b.slice(0, h); let tried = 0; @@ -16026,25 +16026,25 @@ function documentCreate(definition, address, id) { }; } } -function checkRecordScope(definition, record) { - if (record.scope.kind !== definition.scope || record.scope.kind === "conversation" && (record.history !== definition.history || record.fork !== definition.fork)) { - throw new TypeError(`Document ${record.id} (${record.kind}) does not match the supplied definition semantics`); +function checkRecordScope(definition, record2) { + if (record2.scope.kind !== definition.scope || record2.scope.kind === "conversation" && (record2.history !== definition.history || record2.fork !== definition.fork)) { + throw new TypeError(`Document ${record2.id} (${record2.kind}) does not match the supplied definition semantics`); } } -function checkRecordVersion(definition, record, version) { +function checkRecordVersion(definition, record2, version) { if (version > definition.version) { - throw new Error(`Document ${record.id} (${record.kind}) has newer version ${version} than ${definition.version}`); + throw new Error(`Document ${record2.id} (${record2.kind}) has newer version ${version} than ${definition.version}`); } if (version < definition.version && definition.migrate === void 0) { - throw new Error(`Document ${record.id} (${record.kind}) requires migration from version ${version}`); + throw new Error(`Document ${record2.id} (${record2.kind}) requires migration from version ${version}`); } } function materializeDocument(definition, stored) { return materializeDocumentValue(definition, stored.record, stored.version, stored.value); } -function materializeDocumentValue(definition, record, version, value2) { - checkRecordScope(definition, record); - checkRecordVersion(definition, record, version); +function materializeDocumentValue(definition, record2, version, value2) { + checkRecordScope(definition, record2); + checkRecordVersion(definition, record2, version); if (version === definition.version) return value2; return copyJson(definition.migrate(value2, version)); @@ -16234,7 +16234,7 @@ var CachedStorage = class { const cached = this.#conversations.get(id); if (cached !== void 0) return Promise.resolve(cached); - return this.#keep(id, () => this.#storage.conversation(id, context), (record) => this.#conversations.set(id, record)); + return this.#keep(id, () => this.#storage.conversation(id, context), (record2) => this.#conversations.set(id, record2)); } scanConversations(query, limit, cursor, context) { return this.#storage.scanConversations(query, limit, cursor, context); @@ -16254,7 +16254,7 @@ var CachedStorage = class { const cached = this.#liveTasks.get(id) ?? this.#awaited.get(id)?.record ?? this.#tasks.get(id); if (cached !== void 0) return Promise.resolve(cached); - return this.#keep(id, () => this.#storage.task(id, context), (record) => this.#keepTask(record)); + return this.#keep(id, () => this.#storage.task(id, context), (record2) => this.#keepTask(record2)); } scanTasks(query, limit, cursor, context) { return this.#storage.scanTasks(query, limit, cursor, context); @@ -16294,30 +16294,30 @@ var CachedStorage = class { }; } /** Keep a frozen task record: a live one until its task ends, an ended one in the bounded table. */ - #keepTask(record) { - if (record.state.status === "terminal") { - this.#liveTasks.delete(record.id); - const awaited = this.#awaited.get(record.id); + #keepTask(record2) { + if (record2.state.status === "terminal") { + this.#liveTasks.delete(record2.id); + const awaited = this.#awaited.get(record2.id); if (awaited !== void 0) - awaited.record = record; + awaited.record = record2; else - this.#tasks.set(record.id, record); + this.#tasks.set(record2.id, record2); return; } - this.#tasks.delete(record.id); - this.#liveTasks.set(record.id, record); + this.#tasks.delete(record2.id); + this.#liveTasks.set(record2.id, record2); } /** * Follow a task's waits from its written record: a wait holds the ended records of its `on` until the task, after * leaving the wait, changes again (or ends), as the phase that resumes reads their outcomes before its next commit. */ - #followWait(record) { - const wait = this.#waits.get(record.id); - if (record.state.status === "waiting") { + #followWait(record2) { + const wait = this.#waits.get(record2.id); + if (record2.state.status === "waiting") { if (wait !== void 0) this.#release(wait.on); - const on = record.state.on; - this.#waits.set(record.id, { on, left: false }); + const on = record2.state.on; + this.#waits.set(record2.id, { on, left: false }); for (const id of on) { let awaited = this.#awaited.get(id); if (awaited === void 0) { @@ -16332,11 +16332,11 @@ var CachedStorage = class { } if (wait === void 0) return; - if (!wait.left && record.state.status !== "terminal") { + if (!wait.left && record2.state.status !== "terminal") { wait.left = true; return; } - this.#waits.delete(record.id); + this.#waits.delete(record2.id); this.#release(wait.on); } /** A wait on `on` is over: what no other wait names goes back to the bounded table. */ @@ -16676,14 +16676,14 @@ async function collectCopies(storage2, scope, at, policy, childConversationId, c ...source.key === void 0 ? {} : { key: source.key }, scope: { kind: "conversation", conversationId: childConversationId } }; - const record = source.history === "latest" ? { ...identity, history: "latest", fork: source.fork } : { ...identity, history: "rewindable", fork: source.fork }; - const copyAddress = addressId(record); + const record2 = source.history === "latest" ? { ...identity, history: "latest", fork: source.fork } : { ...identity, history: "rewindable", fork: source.fork }; + const copyAddress = addressId(record2); if (copiedAddresses.has(copyAddress)) { - const member = record.key === void 0 ? record.kind : `${record.kind}/${record.key}`; + const member = record2.key === void 0 ? record2.kind : `${record2.kind}/${record2.key}`; throw new Error(`Fork selects multiple source documents for ${member}`); } copiedAddresses.add(copyAddress); - copies.push({ record, source: { id: source.id, at } }); + copies.push({ record: record2, source: { id: source.id, at } }); } cursor = page3.next; } while (cursor !== void 0); @@ -16812,7 +16812,7 @@ var Transaction = class { throw new Error(`Conversation owner task ${ownerTaskId} does not exist`); owner = { conversationId: task.conversationId, taskId: ownerTaskId }; } - const record = { + const record2 = { id, ...parent === void 0 ? {} : { parent }, ...owner === void 0 ? {} : { owner } @@ -16833,10 +16833,10 @@ var Transaction = class { if (parent !== void 0) this.#forkSourceConversationIds.add(parent.conversationId); this.#createdConversationIds.add(id); - this.#writes.push({ type: "conversation", value: record }); - await this.#host.conversationCreated(this, record); + this.#writes.push({ type: "conversation", value: record2 }); + await this.#host.conversationCreated(this, record2); this.#assertOpen(); - return record; + return record2; } beforeInput(conversationId, input) { return this.#read("beforeInput", () => this.#host.beforeInput(this, conversationId, input, this.#context)); @@ -16853,15 +16853,15 @@ var Transaction = class { const id = await this.#host.storage.mintId(); this.#assertOpen(); const { head, ...rest } = value2; - const record = copyJson({ + const record2 = copyJson({ ...rest, id, conversationId, head: head === "self" ? id : head, byTaskId: this.#scope.taskId }, TABLE_JSON_COPY_OPTIONS); - this.#writes.push({ type: "entry", value: record }); - return record; + this.#writes.push({ type: "entry", value: record2 }); + return record2; }); } createTask(task, input, options) { @@ -16903,7 +16903,7 @@ var Transaction = class { const checkpoint = definition.initial(input); const id = await this.#host.storage.mintId(); this.#assertOpen(); - const record = copyJson({ + const record2 = copyJson({ id, conversationId, kind: definition.name, @@ -16917,7 +16917,7 @@ var Transaction = class { ...options.abandonOnRestart === true ? { abandonOnRestart: true } : {}, state: { status: "pending", checkpoint } }, TABLE_JSON_COPY_OPTIONS); - this.#tasksById.set(id, { write: { kind: "create", record } }); + this.#tasksById.set(id, { write: { kind: "create", record: record2 } }); return id; }); } @@ -16928,9 +16928,9 @@ var Transaction = class { this.#assertOpen(); const id = await this.#host.storage.mintId(); this.#assertOpen(); - const record = copyJson({ ...create, id }, TABLE_JSON_COPY_OPTIONS); - this.#submissions.set(id, record); - return record; + const record2 = copyJson({ ...create, id }, TABLE_JSON_COPY_OPTIONS); + this.#submissions.set(id, record2); + return record2; }); } /** @@ -17104,12 +17104,12 @@ var Transaction = class { const gate = this.#host.touch(documentKey(entry2.addressId)); if (gate !== void 0) await gate; - const record = this.#host.cached(entry2.addressId)?.record ?? await this.#host.storage.findDocument(entry2.address, "current", this.#context); + const record2 = this.#host.cached(entry2.addressId)?.record ?? await this.#host.storage.findDocument(entry2.address, "current", this.#context); this.#assertOpen(); - if (record === void 0) + if (record2 === void 0) return; - checkRecordScope(entry2.definition, record); - entry2.target = { kind: "retire-only", record }; + checkRecordScope(entry2.definition, record2); + entry2.target = { kind: "retire-only", record: record2 }; } // ─── Settlement ───────────────────────────────────────────────────────── /** Seal after callback failure: abort every change and observe every pending operation. */ @@ -17164,8 +17164,8 @@ var Transaction = class { if (plan.retire || change?.loaded === void 0 || content?.type !== "document.change") return void 0; const { loaded, prepared, version } = change; - const record = loaded.record; - if (record.scope.kind !== "conversation" || record.history !== "latest" || record.fork !== "initial") { + const record2 = loaded.record; + if (record2.scope.kind !== "conversation" || record2.history !== "latest" || record2.fork !== "initial") { return void 0; } if (loaded.storedVersion !== version) @@ -17187,12 +17187,12 @@ var Transaction = class { continue; } tracker.adopt(prepared); - const record = loaded.record; + const record2 = loaded.record; const conversationId = plan.conversationId; publications.push({ type: "document", kind: "change", - record, + record: record2, conversationId, version, value: prepared.value, @@ -17264,9 +17264,9 @@ var Transaction = class { let stored; for (const plan of this.#plans) { const committed = "createdAt" in plan.record; - let record = committed ? plan.record : { ...plan.record, createdAt: seq }; + let record2 = committed ? plan.record : { ...plan.record, createdAt: seq }; if (plan.retire) - record = { ...record, retiredAt: seq }; + record2 = { ...record2, retiredAt: seq }; const change = plan.change; if (change !== void 0) { const { tracker, prepared, loaded, version } = change; @@ -17286,7 +17286,7 @@ var Transaction = class { } else if (!plan.retire) { this.#host.install({ addressId: plan.addressId, - record, + record: record2, storedVersion: version, valueVersion: version, deltasSinceBase: 0, @@ -17295,8 +17295,8 @@ var Transaction = class { } } if (plan.retire && committed) - this.#host.evict(plan.addressId, record.id); - const publication = describe(plan, record); + this.#host.evict(plan.addressId, record2.id); + const publication = describe(plan, record2); if (publication === void 0) continue; const held2 = plan.held; @@ -17348,11 +17348,11 @@ var Transaction = class { plan.retire = true; retiring.add(plan.record.id); } - const retire = (record) => { - if (retiring.has(record.id)) + const retire = (record2) => { + if (retiring.has(record2.id)) return; - plans.push({ addressId: addressId(record), record, retire: true }); - retiring.add(record.id); + plans.push({ addressId: addressId(record2), record: record2, retire: true }); + retiring.add(record2.id); }; for (const taskId of terminalTaskIds) { if (this.#tasksById.get(taskId)?.write?.kind === "create") @@ -17362,15 +17362,15 @@ var Transaction = class { await gate; const known = this.#host.taskDocuments(taskId); if (known !== void 0) { - for (const record of known) - retire(record); + for (const record2 of known) + retire(record2); continue; } let cursor; do { const page3 = await storage2.scanDocuments({ scope: { kind: "task", taskId }, at: "current" }, INTERNAL_SCAN_PAGE_SIZE, cursor, this.#context); - for (const record of page3.items) - retire(record); + for (const record2 of page3.items) + retire(record2); cursor = page3.next; } while (cursor !== void 0); } @@ -17435,9 +17435,9 @@ var Transaction = class { owners.push({ what: "Conversation owner task", taskId: write.value.owner.taskId }); } for (const task of this.#tasksById.values()) { - const record = task.write?.kind === "create" ? task.write.record : void 0; - if (record?.owner !== void 0) - owners.push({ what: "Task owner", taskId: record.owner }); + const record2 = task.write?.kind === "create" ? task.write.record : void 0; + if (record2?.owner !== void 0) + owners.push({ what: "Task owner", taskId: record2.owner }); } for (const { what, taskId } of owners) { const task = await this.#currentTask(taskId); @@ -17454,12 +17454,12 @@ var Transaction = class { for (const plan of plans) { if (plan.content === void 0 && !plan.retire) continue; - const record = plan.record; - if (this.#forkSourceDocumentIds.has(record.id)) { - throw new Error(`Cannot change fork source document ${record.id} in the fork transaction`); + const record2 = plan.record; + if (this.#forkSourceDocumentIds.has(record2.id)) { + throw new Error(`Cannot change fork source document ${record2.id} in the fork transaction`); } const scope = plan.record.scope; - if (scope.kind === "conversation" && this.#forkSourceConversationIds.has(scope.conversationId) && record.scope.kind === "conversation" && record.fork === "current") { + if (scope.kind === "conversation" && this.#forkSourceConversationIds.has(scope.conversationId) && record2.scope.kind === "conversation" && record2.fork === "current") { throw new Error(`Cannot fork conversation ${scope.conversationId} while changing its current-policy documents`); } } @@ -17556,12 +17556,12 @@ function planDocument(document) { switch (target.kind) { case "created": { const prepared = document.prepared; - const { record, version, tracker } = target; + const { record: record2, version, tracker } = target; const content = { version, kind: "base", value: prepared.value }; return { ...plan, - record, - content: { type: "document.create", record, content }, + record: record2, + content: { type: "document.create", record: record2, content }, change: { tracker, prepared, version } }; } @@ -17591,12 +17591,12 @@ function planDocument(document) { } } } -function describe(plan, record) { +function describe(plan, record2) { const conversationId = plan.conversationId; if (plan.retire) - return { type: "document", kind: "retire", record, conversationId }; + return { type: "document", kind: "retire", record: record2, conversationId }; if (plan.content?.type === "document.copy") { - return { type: "document.copy", record, conversationId, source: plan.content.source }; + return { type: "document.copy", record: record2, conversationId, source: plan.content.source }; } const change = plan.change; if (change === void 0 || !publishes(plan)) @@ -17605,7 +17605,7 @@ function describe(plan, record) { return { type: "document", kind: "change", - record, + record: record2, conversationId, version: change.version, value: change.prepared.value, @@ -18029,7 +18029,7 @@ var SessionImpl = class { this.#dropDocument(id); }, beforeInput: (tx, id, input, context) => this.beforeInput(tx, id, input, context), - conversationCreated: (tx, record) => this.conversationCreated(tx, record), + conversationCreated: (tx, record2) => this.conversationCreated(tx, record2), taskDocuments: (taskId) => this.#taskDocuments.get(taskId)?.values(), latestEntryId: (conversationId, context) => this.latestEntryId(conversationId, context), latestHeadMarker: (conversationId, context) => this.latestHeadMarker(conversationId, void 0, context) @@ -18285,12 +18285,12 @@ var SessionImpl = class { ...resolved.address, scope: { kind: "conversation", conversationId: storedEntry.entry.conversationId } }; - const record = await this.#gated.findDocument(address, storedEntry.commitSeq, context); - if (record === void 0) + const record2 = await this.#gated.findDocument(address, storedEntry.commitSeq, context); + if (record2 === void 0) return { value: void 0 }; - const stored = await this.#gated.document(record.id, storedEntry.commitSeq, context); + const stored = await this.#gated.document(record2.id, storedEntry.commitSeq, context); if (stored === void 0) { - throw new Error(`Historical document ${record.id} (${record.kind}) cannot be read`); + throw new Error(`Historical document ${record2.id} (${record2.kind}) cannot be read`); } return { value: materializeDocument(definition, stored) }; }); @@ -18735,13 +18735,13 @@ var SessionImpl = class { const changes = []; const keys = []; for (const hold of holds) { - const { record, tracker, valueVersion: version } = hold.loaded; + const { record: record2, tracker, valueVersion: version } = hold.loaded; const value2 = tracker.value; const stored = storeHeld(hold, NO_OPERATIONS, value2, version); - writes.push({ type: "document.change", id: record.id, content: stored.content }); - keys.push(documentKey(hold.loaded.addressId), incarnationKey(record.id)); - const conversationId = record.scope.kind === "conversation" ? record.scope.conversationId : void 0; - changes.push({ type: "document", kind: "change", record, conversationId, version, value: value2, ops: stored.ops }); + writes.push({ type: "document.change", id: record2.id, content: stored.content }); + keys.push(documentKey(hold.loaded.addressId), incarnationKey(record2.id)); + const conversationId = record2.scope.kind === "conversation" ? record2.scope.conversationId : void 0; + changes.push({ type: "document", kind: "change", record: record2, conversationId, version, value: value2, ops: stored.ops }); } const publish2 = (seq) => { for (const [index2, hold] of holds.entries()) { @@ -19007,12 +19007,12 @@ var SessionImpl = class { }, context); this.#dropDocument(addressId2); } - const record = await this.#storage.findDocument(address, "current", context); - if (record === void 0) + const record2 = await this.#storage.findDocument(address, "current", context); + if (record2 === void 0) return void 0; - const stored = await this.#storage.document(record.id, "current", context); + const stored = await this.#storage.document(record2.id, "current", context); if (stored === void 0) - throw new Error(`Current document ${record.id} (${record.kind}) cannot be read`); + throw new Error(`Current document ${record2.id} (${record2.kind}) cannot be read`); const value2 = materializeDocument(definition, stored); const loaded = { addressId: addressId2, @@ -19250,16 +19250,16 @@ function endWatch(watch) { function endGuarded(guarded) { guarded.end(ENDED2); } -function atAddress(record, address) { - if (record.kind !== address.kind || record.key !== address.key) +function atAddress(record2, address) { + if (record2.kind !== address.kind || record2.key !== address.key) return false; - switch (record.scope.kind) { + switch (record2.scope.kind) { case "session": return address.scope.kind === "session"; case "conversation": - return address.scope.kind === "conversation" && address.scope.conversationId === record.scope.conversationId; + return address.scope.kind === "conversation" && address.scope.conversationId === record2.scope.conversationId; case "task": - return address.scope.kind === "task" && address.scope.taskId === record.scope.taskId; + return address.scope.kind === "task" && address.scope.taskId === record2.scope.taskId; } } function cancellationError(signal) { @@ -20832,11 +20832,11 @@ var Waiters = class { waiter.reject(error); } }; -async function scanAll(scan) { +async function scanAll(scan2) { const items = []; let cursor; do { - const page3 = await scan(cursor); + const page3 = await scan2(cursor); items.push(...page3.items); cursor = page3.next; } while (cursor !== void 0); @@ -20862,14 +20862,14 @@ function conversationEntries(conversationId, query) { var SCAN_PAGE_SIZE3 = 256; var NO_KEYS2 = []; var LIVE_STATUSES = ["pending", "running", "waiting", "sleeping", "completing"]; -function blockedSubject(record) { +function blockedSubject(record2) { return { - id: record.id, - kind: record.kind, - version: record.version, - abortRequested: record.abortRequested, - ...record.abortReason === void 0 ? {} : { abortReason: record.abortReason }, - status: record.state.status + id: record2.id, + kind: record2.kind, + version: record2.version, + abortRequested: record2.abortRequested, + ...record2.abortReason === void 0 ? {} : { abortReason: record2.abortReason }, + status: record2.state.status }; } var CONTINUE = { kind: "continue" }; @@ -21052,20 +21052,20 @@ var TaskScheduler = class { scans.push(await scanAll((cursor) => tx.scanTasks({ status }, SCAN_PAGE_SIZE3, cursor))); } for (const records of scans) { - for (const record of records) { - this.#track(void 0, record); - this.#resumed.add(record.id); + for (const record2 of records) { + this.#track(void 0, record2); + this.#resumed.add(record2.id); if (this.#readOnly) continue; - if (record.abandonOnRestart === true) - this.#abandoned.add(record.id); - if (record.state.status === "running") { - const restarts = (record.restarts ?? 0) + 1; - const pending = withState(record, { status: "pending", checkpoint: record.state.checkpoint }); + if (record2.abandonOnRestart === true) + this.#abandoned.add(record2.id); + if (record2.state.status === "running") { + const restarts = (record2.restarts ?? 0) + 1; + const pending = withState(record2, { status: "pending", checkpoint: record2.state.checkpoint }); tx.setTask({ ...pending, restarts }); } - if (record.state.status === "waiting" && record.state.policy === "failFast") { - this.#failFastChecks.add(record.id); + if (record2.state.status === "waiting" && record2.state.policy === "failFast") { + this.#failFastChecks.add(record2.id); } } } @@ -21091,11 +21091,11 @@ var TaskScheduler = class { * Why a live task cannot run here, if it cannot: its definition is missing, too old, or failed to migrate. A running * task, an abort-marked one, and one holding its outcome are never blocked. */ - blockedReason(record) { - const status = record.status; + blockedReason(record2) { + const status = record2.status; if (status === "running" || status === "completing" || status === "terminal") return void 0; - if (record.abortRequested && record.abortReason === void 0) + if (record2.abortRequested && record2.abortReason === void 0) return void 0; let snapshot; try { @@ -21106,7 +21106,7 @@ var TaskScheduler = class { this.#snapshotReported = true; return void 0; } - const fit = this.#fit(record, snapshot.task(record.kind)); + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return fit.reason; return fit.migrates && fit.task.definition.migrate === void 0 ? "migration_failed" : void 0; @@ -21127,9 +21127,9 @@ var TaskScheduler = class { const active = this.#invocations.size > 0 || this.#reconcilingWork > 0 || this.#reconcileWork() || this.#runsNow(now); let at; for (const id of this.#sleeping) { - const record = this.#live.get(id); - const state = record?.state; - if (state?.status === "sleeping" && !record.abortRequested && state.until > now) { + const record2 = this.#live.get(id); + const state = record2?.state; + if (state?.status === "sleeping" && !record2.abortRequested && state.until > now) { at = at === void 0 ? state.until : Math.min(at, state.until); } } @@ -21139,17 +21139,17 @@ var TaskScheduler = class { #runsNow(now) { let snapshot; let snapshotFailed = false; - for (const record of this.#live.values()) { - const state = record.state; + for (const record2 of this.#live.values()) { + const state = record2.state; if (state.status === "completing") continue; - if (state.status === "waiting" && !record.abortRequested && state.on.some((id) => this.#live.has(id))) + if (state.status === "waiting" && !record2.abortRequested && state.on.some((id) => this.#live.has(id))) continue; - if (state.status === "sleeping" && !record.abortRequested && state.until > now) + if (state.status === "sleeping" && !record2.abortRequested && state.until > now) continue; - if (record.abortRequested && this.#waitingOn(record, true).length > 0) + if (record2.abortRequested && this.#waitingOn(record2, true).length > 0) continue; - if (!record.abortRequested || record.abortReason !== void 0) { + if (!record2.abortRequested || record2.abortReason !== void 0) { if (snapshot === void 0 && !snapshotFailed) { try { snapshot = this.#registry.snapshot(); @@ -21158,7 +21158,7 @@ var TaskScheduler = class { this.#report(error, REGISTRY); } } - if (snapshot !== void 0 && "reason" in this.#fit(record, snapshot.task(record.kind))) + if (snapshot !== void 0 && "reason" in this.#fit(record2, snapshot.task(record2.kind))) continue; } return true; @@ -21178,10 +21178,10 @@ var TaskScheduler = class { if (stopped.length === 0) return; await this.#writeAtClose(() => stopped.flatMap((invocation) => { - const record = this.#live.get(invocation.taskId); - if (record?.state.status !== "running") + const record2 = this.#live.get(invocation.taskId); + if (record2?.state.status !== "running") return []; - const value2 = withState(record, { status: "pending", checkpoint: record.state.checkpoint }); + const value2 = withState(record2, { status: "pending", checkpoint: record2.state.checkpoint }); return [{ type: "task", value: value2 }]; })).catch(() => { }); @@ -21228,10 +21228,10 @@ var TaskScheduler = class { if (await this.#region(() => this.#liveRecord(id) !== void 0)) { return { promise: this.#taskWaiters.add(id, context) }; } - const record = await storage2.task(id, context); - if (record === void 0) + const record2 = await storage2.task(id, context); + if (record2 === void 0) throw new Error(`Task ${id} does not exist`); - return { promise: Promise.resolve(record) }; + return { promise: Promise.resolve(record2) }; }); return found.promise; } @@ -21288,7 +21288,7 @@ var TaskScheduler = class { membershipKeys(tx) { const tasks = tx.stagedTasks(); const conversations = tx.stagedConversations(); - if (tasks.length === 0 && conversations.every((record) => record.owner === void 0)) + if (tasks.length === 0 && conversations.every((record2) => record2.owner === void 0)) return NO_KEYS2; return this.#membershipKeys(tx, tasks, conversations); } @@ -21297,13 +21297,13 @@ var TaskScheduler = class { const overlay = overlayOf(tx); const created = new Set(tx.createdTaskIds()); const reached = /* @__PURE__ */ new Map(); - for (const record of tasks) { - if (intentChanges(this.#live.get(record.id), record)) + for (const record2 of tasks) { + if (intentChanges(this.#live.get(record2.id), record2)) keys.add(INTENT_KEY); - if (!created.has(record.id) && record.state.status !== "terminal") + if (!created.has(record2.id) && record2.state.status !== "terminal") continue; - await this.#loadChain(parentOf(record), overlay); - if (await this.#chainKeys(parentNode(record), overlay, keys, reached) !== void 0) + await this.#loadChain(parentOf(record2), overlay); + if (await this.#chainKeys(parentNode(record2), overlay, keys, reached) !== void 0) keys.add(ROOTS_KEY); } for (const conversation of conversations) { @@ -21340,18 +21340,18 @@ var TaskScheduler = class { root2 = conversation; continue; } - let record = overlay.tasks.get(node); - if (record === void 0) { + let record2 = overlay.tasks.get(node); + if (record2 === void 0) { const gate = this.#session.touch(taskKey(node)); if (gate !== void 0) { await gate; await this.#loadChain({ task: node }, overlay); } - record = this.#live.get(node); + record2 = this.#live.get(node); } - if (record !== void 0 && record.state.status !== "terminal") + if (record2 !== void 0 && record2.state.status !== "terminal") break; - const above = record ?? this.#settled.get(node); + const above = record2 ?? this.#settled.get(node); node = above === void 0 ? void 0 : parentNode(above); } for (const key of walked) @@ -21376,41 +21376,41 @@ var TaskScheduler = class { if (change.type !== "task") continue; changed = true; - const record = change.value; - const previous = this.#live.get(record.id); - if (failedOutcome(record) && (previous === void 0 || !failedOutcome(previous))) - failed.push(record.id); - this.#track(previous, record); - const liveness = previous === void 0 || record.state.status === "terminal"; - if (liveness && dirty !== void 0 && !this.#addScopesAbove(record, dirty)) + const record2 = change.value; + const previous = this.#live.get(record2.id); + if (failedOutcome(record2) && (previous === void 0 || !failedOutcome(previous))) + failed.push(record2.id); + this.#track(previous, record2); + const liveness = previous === void 0 || record2.state.status === "terminal"; + if (liveness && dirty !== void 0 && !this.#addScopesAbove(record2, dirty)) dirty = void 0; - if (record.state.status === "terminal") { - this.#failedMigrations.delete(record.id); - this.#failFastChecks.delete(record.id); - this.#taskWaiters.resolve(record.id, record); + if (record2.state.status === "terminal") { + this.#failedMigrations.delete(record2.id); + this.#failFastChecks.delete(record2.id); + this.#taskWaiters.resolve(record2.id, record2); this.#scheduleReconcile(); continue; } - if (record.abortRequested && previous?.abortRequested !== true) { + if (record2.abortRequested && previous?.abortRequested !== true) { this.#cascadePending = true; - const invocation = this.#invocations.get(record.id); + const invocation = this.#invocations.get(record2.id); if (invocation?.mode === "run") queueMicrotask(() => invocation.controller.abort()); } - if (previous?.abortReason !== void 0 && record.abortReason === void 0) + if (previous?.abortReason !== void 0 && record2.abortReason === void 0) this.#cascadePending = true; - const status = record.state.status; + const status = record2.state.status; if (status === "completing" && previous?.state.status !== "completing") { - if (cancellationIntent(record)) + if (cancellationIntent(record2)) this.#cascadePending = true; this.#scheduleReconcile(); } - if (status === "waiting" && record.state.policy === "failFast" && previous?.state.status !== "waiting") { - this.#failFastChecks.add(record.id); + if (status === "waiting" && record2.state.policy === "failFast" && previous?.state.status !== "waiting") { + this.#failFastChecks.add(record2.id); this.#scheduleReconcile(); } if (previous === void 0) - updated.push(record); + updated.push(record2); } for (const id of failed) { for (const waiter of this.#waiters.get(id) ?? []) { @@ -21431,10 +21431,10 @@ var TaskScheduler = class { this.#cascadePending = true; } } - for (const record of updated) { - if (this.#unloaded.has(record.id)) + for (const record2 of updated) { + if (this.#unloaded.has(record2.id)) this.#scheduleReconcile(); - else if (!record.background && !record.abortRequested && this.#belowCancelled(parentOf(record))) { + else if (!record2.background && !record2.abortRequested && this.#belowCancelled(parentOf(record2))) { this.#cascadePending = true; } } @@ -21450,8 +21450,8 @@ var TaskScheduler = class { * Add the conversations above `record` to `scopes`: those whose idleness a change of it can change. False when a node * on the way up is not known. A conversation already in `scopes` has everything above it there too. */ - #addScopesAbove(record, scopes) { - for (const step of this.#above(parentOf(record))) { + #addScopesAbove(record2, scopes) { + for (const step of this.#above(parentOf(record2))) { if ("unknown" in step) return false; if (!("conversation" in step)) @@ -21633,8 +21633,8 @@ var TaskScheduler = class { if (cascade) this.#cascadePending = false; const marks = await this.#marks(); - for (const { record, reason } of marks.values()) - tx.setTask(withAbortMark(record, reason)); + for (const { record: record2, reason } of marks.values()) + tx.setTask(withAbortMark(record2, reason)); const withdrawn = await this.#region(() => queued.filter((id) => this.#belowCancelled({ conversation: id })), this.#reloadScopes); for (const id of withdrawn) await this.#withdrawInputs(tx, id); @@ -21681,13 +21681,13 @@ var TaskScheduler = class { #planMarks(ended) { const marks = /* @__PURE__ */ new Map(); const mark2 = (found, reason) => { - const record = this.#liveRecord(found.id); - const staged = marks.get(record.id); + const record2 = this.#liveRecord(found.id); + const staged = marks.get(record2.id); if (staged !== void 0 && staged.reason === void 0) return; - if (record.abortRequested && (record.abortReason === void 0 || reason !== void 0)) + if (record2.abortRequested && (record2.abortReason === void 0 || reason !== void 0)) return; - marks.set(record.id, { record, reason }); + marks.set(record2.id, { record: record2, reason }); }; this.#record(INTENT_KEY); for (const id of this.#intent) { @@ -21706,18 +21706,18 @@ var TaskScheduler = class { continue; let failed = false; for (const member of waiter.state.on) { - const record = this.#liveRecord(member) ?? ended.get(member); - if (record === void 0) + const record2 = this.#liveRecord(member) ?? ended.get(member); + if (record2 === void 0) missing.push(member); - else if (record !== null && failedOutcome(record)) + else if (record2 !== null && failedOutcome(record2)) failed = true; } if (!failed) continue; for (const member of waiter.state.on) { - const record = this.#liveRecord(member); - if (record !== void 0 && !failedOutcome(record)) - mark2(record, void 0); + const record2 = this.#liveRecord(member); + if (record2 !== void 0 && !failedOutcome(record2)) + mark2(record2, void 0); } } return missing.length > 0 ? { missing } : { marks, checked }; @@ -21742,13 +21742,13 @@ var TaskScheduler = class { const plan = await this.#region(() => this.#planRound(ids ?? [...this.#finalizeChecks], overlay, done), this.#reloadScopes); if (ids === void 0) this.#finalizeChecks.clear(); - for (const { record, terminal } of plan.finalized) { + for (const { record: record2, terminal } of plan.finalized) { tx.setTask(terminal); - overlay.tasks.set(record.id, terminal); - done.add(record.id); - const outcome = record.state.outcome; + overlay.tasks.set(record2.id, terminal); + done.add(record2.id); + const outcome = record2.state.outcome; if (outcome.status === "faulted" || outcome.status === "orphaned") { - await this.#settleOutcome(tx, record, outcome); + await this.#settleOutcome(tx, record2, outcome); } } if (plan.next.length === 0) @@ -21771,25 +21771,25 @@ var TaskScheduler = class { try { for (const id of ids) { const staged = tasks.get(id); - const record = staged ?? this.#liveRecord(id); - if (record?.state.status !== "completing" || done.has(id)) + const record2 = staged ?? this.#liveRecord(id); + if (record2?.state.status !== "completing" || done.has(id)) continue; if (this.#hasOwnedLive(id, overlay, done)) continue; - const terminal = withState(record, { status: "terminal", outcome: record.state.outcome }); + const terminal = withState(record2, { status: "terminal", outcome: record2.state.outcome }); tasks.set(id, terminal); round.add(id); - ended.push({ record, terminal, staged: staged !== void 0 }); - const above = this.#nearestLiveAbove(record, overlay); + ended.push({ record: record2, terminal, staged: staged !== void 0 }); + const above = this.#nearestLiveAbove(record2, overlay); if (above?.state.status === "completing") next.add(above.id); } } finally { - for (const { record, staged } of ended) { + for (const { record: record2, staged } of ended) { if (staged) - tasks.set(record.id, record); + tasks.set(record2.id, record2); else - tasks.delete(record.id); + tasks.delete(record2.id); } } return { finalized: ended, next: [...next] }; @@ -21832,11 +21832,11 @@ var TaskScheduler = class { if (node !== void 0 && !this.#unloaded.has(id)) break; if (node === void 0) { - const record = await this.#session.lineStorage.task(id, this.#context); - if (record === void 0) + const record2 = await this.#session.lineStorage.task(id, this.#context); + if (record2 === void 0) return; - node = nodeOf(record); - if (record.state.status === "terminal") { + node = nodeOf(record2); + if (record2.state.status === "terminal") { this.#settle(id, node); this.#pin(id); installed = true; @@ -21848,8 +21848,8 @@ var TaskScheduler = class { } else { let edge = this.#edge(at.conversation, overlay); if (edge === void 0) { - const record = await this.#session.lineStorage.conversation(at.conversation, this.#context); - edge = record?.owner?.taskId ?? null; + const record2 = await this.#session.lineStorage.conversation(at.conversation, this.#context); + edge = record2?.owner?.taskId ?? null; this.#setEdge(at.conversation, edge); this.#pin(conversationNode(at.conversation)); installed = true; @@ -21994,23 +21994,23 @@ var TaskScheduler = class { * Bring the scheduler's sets in line with a task's committed record. A task that ended may free the nearest live task * above it: one holding its outcome finalizes, an abort-marked one runs its abort handler; its waiters may run. */ - #track(previous, record) { - const id = record.id; + #track(previous, record2) { + const id = record2.id; const invocation = this.#invocations.get(id); if (invocation !== void 0) - invocation.record = record; - if (record.state.status === "terminal") { + invocation.record = record2; + if (record2.state.status === "terminal") { if (previous !== void 0 && !this.#closing) - this.#countActive(-1, record.kind); + this.#countActive(-1, record2.kind); this.#resumed.delete(id); } else if (previous === void 0 && !this.#closing) { - this.#countActive(1, record.kind); + this.#countActive(1, record2.kind); } if (previous?.state.status === "waiting") for (const on of previous.state.on) deleteFrom(this.#waiters, on, id); - if (record.state.status === "terminal") { - const node = nodeOf(record); + if (record2.state.status === "terminal") { + const node = nodeOf(record2); this.#live.delete(id); this.#sleeping.delete(id); this.#intent.delete(id); @@ -22024,26 +22024,26 @@ var TaskScheduler = class { this.#dropIfUnneeded(id); return; } - this.#live.set(id, record); + this.#live.set(id, record2); if (previous === void 0) { - if (!this.#known(parentNode(record))) + if (!this.#known(parentNode(record2))) this.#unloaded.add(id); this.#relist(id); } - if (cancellationIntent(record)) + if (cancellationIntent(record2)) this.#intent.add(id); else this.#intent.delete(id); - if (record.state.status === "completing") { + if (record2.state.status === "completing") { this.#runnable.delete(id); this.#finalizeChecks.add(id); } else { this.#runnable.add(id); } - if (record.state.status === "waiting") - for (const on of record.state.on) + if (record2.state.status === "waiting") + for (const on of record2.state.on) addTo(this.#waiters, on, id); - if (record.state.status === "sleeping") + if (record2.state.status === "sleeping") this.#sleeping.add(id); else this.#sleeping.delete(id); @@ -22112,10 +22112,10 @@ var TaskScheduler = class { const owner = current.owner ?? this.#edge(current.conversationId, overlay); if (owner === void 0 || owner === null) return void 0; - const record = overlay?.tasks.get(owner) ?? this.#liveRecord(owner); - if (record !== void 0 && record.state.status !== "terminal") - return record; - const above = record ?? this.#settled.get(owner); + const record2 = overlay?.tasks.get(owner) ?? this.#liveRecord(owner); + if (record2 !== void 0 && record2.state.status !== "terminal") + return record2; + const above = record2 ?? this.#settled.get(owner); if (above === void 0) return void 0; current = above; @@ -22180,11 +22180,11 @@ var TaskScheduler = class { pending.push(child); continue; } - const record = overlay?.tasks.get(child) ?? this.#live.get(child); - const node = record ?? this.#settled.get(child); + const record2 = overlay?.tasks.get(child) ?? this.#live.get(child); + const node = record2 ?? this.#settled.get(child); if (node === void 0) continue; - const live = record !== void 0 && record.state.status !== "terminal" ? record : void 0; + const live = record2 !== void 0 && record2.state.status !== "terminal" ? record2 : void 0; yield { id: child, node, live }; if ((crossBackground || !node.background) && (enter === void 0 || enter(child))) pending.push(child); @@ -22200,11 +22200,11 @@ var TaskScheduler = class { if (overlay.links !== void 0) return overlay.links; const links = /* @__PURE__ */ new Map(); - for (const record of overlay.tasks.values()) { - if (this.#live.has(record.id) || this.#settled.has(record.id)) + for (const record2 of overlay.tasks.values()) { + if (this.#live.has(record2.id) || this.#settled.has(record2.id)) continue; - let child = record.id; - let parent = parentNode(record); + let child = record2.id; + let parent = parentNode(record2); while (parent !== void 0 && parent !== null && links.get(parent)?.has(child) !== true) { addTo(links, parent, child); if (typeof parent !== "string" && (this.#live.has(parent) || overlay.tasks.has(parent))) @@ -22316,8 +22316,8 @@ var TaskScheduler = class { } /** Close listener: runs synchronously once admission is sealed, before `join()`, or when the Session fails. */ #seal() { - for (const record of this.#live.values()) - this.#countActive(-1, record.kind); + for (const record2 of this.#live.values()) + this.#countActive(-1, record2.kind); this.#resumed.clear(); this.#closing = true; try { @@ -22385,8 +22385,8 @@ var TaskScheduler = class { /** Make every sleeping task whose time has come a reservation candidate. */ #wakeDue() { for (const id of this.#sleeping) { - const record = this.#live.get(id); - if (record !== void 0 && !this.#notDue(record)) + const record2 = this.#live.get(id); + if (record2 !== void 0 && !this.#notDue(record2)) this.#runnable.add(id); } } @@ -22427,9 +22427,9 @@ var TaskScheduler = class { } } /** A sleep whose `until` the clock has not reached; an abort mark ends a sleep. */ - #notDue(record) { - const state = record.state; - return state.status === "sleeping" && !record.abortRequested && state.until > this.#now(); + #notDue(record2) { + const state = record2.state; + return state.status === "sleeping" && !record2.abortRequested && state.until > this.#now(); } #kick() { this.#scheduleWake(); @@ -22468,9 +22468,9 @@ var TaskScheduler = class { return; if (this.#abandoned.size > 0) { const ids = [...this.#abandoned]; - const unmarked = await this.#region(() => ids.map((id) => this.#liveRecord(id)).filter((record) => record !== void 0 && !record.abortRequested)); - for (const record of unmarked) - tx.setTask(withAbortMark(record, "restart")); + const unmarked = await this.#region(() => ids.map((id) => this.#liveRecord(id)).filter((record2) => record2 !== void 0 && !record2.abortRequested)); + for (const record2 of unmarked) + tx.setTask(withAbortMark(record2, "restart")); abandoned = ids; return; } @@ -22484,37 +22484,37 @@ var TaskScheduler = class { this.#scheduleReconcile(); } let snapshot; - for (const record of plan.ready) { - const id = record.id; - const mode = record.abortRequested ? "abort" : "run"; + for (const record2 of plan.ready) { + const id = record2.id; + const mode = record2.abortRequested ? "abort" : "run"; if (mode === "run" && this.#held) continue; - const cutOff = record.restarts !== void 0 && record.restarts >= this.#restartLimit(); - if (cutOff && (mode === "abort" || !this.#limitsRestarts(record.kind))) { - const message = mode === "abort" ? `Abort handler of task ${id} (${record.kind}) was cut off ${record.restarts} times` : `Task ${id} (${record.kind}) was cut off ${record.restarts} times without progress`; - await this.#terminate(tx, record, { + const cutOff = record2.restarts !== void 0 && record2.restarts >= this.#restartLimit(); + if (cutOff && (mode === "abort" || !this.#limitsRestarts(record2.kind))) { + const message = mode === "abort" ? `Abort handler of task ${id} (${record2.kind}) was cut off ${record2.restarts} times` : `Task ${id} (${record2.kind}) was cut off ${record2.restarts} times without progress`; + await this.#terminate(tx, record2, { status: "faulted", error: { message, detail: { reason: "restart_limit" } } }); - faulted.push({ record, message }); + faulted.push({ record: record2, message }); continue; } snapshot ??= this.#registry.snapshot(); - const resolution = this.#resolve(record, snapshot); + const resolution = this.#resolve(record2, snapshot); if (resolution.kind === "blocked") { - if (mode === "abort" && record.abortReason === void 0) { - await this.#terminate(tx, record, { status: "orphaned", reason: resolution.reason }); + if (mode === "abort" && record2.abortReason === void 0) { + await this.#terminate(tx, record2, { status: "orphaned", reason: resolution.reason }); } continue; } - if (resolution.record !== record || record.state.status !== "running") { + if (resolution.record !== record2 || record2.state.status !== "running") { tx.setTask(withState(resolution.record, { status: "running", checkpoint: resolution.record.state.checkpoint })); } this.#runnable.delete(id); - const invocation = this.#createInvocation(record, mode, installerOf(snapshot, resolution.task)); + const invocation = this.#createInvocation(record2, mode, installerOf(snapshot, resolution.task)); reservations.push({ invocation, task: resolution.task, snapshot }); } }, this.#context); @@ -22526,10 +22526,10 @@ var TaskScheduler = class { } throw error; } - for (const { record, message } of faulted) { + for (const { record: record2, message } of faulted) { this.#report(new Error(message), { kind: "task_faulted", - ...this.#aboutTask(recordTask(record)), + ...this.#aboutTask(recordTask(record2)), reason: "restart_limit" }); } @@ -22554,16 +22554,16 @@ var TaskScheduler = class { dropped.push(id); continue; } - const record = this.#liveRecord(id); - if (record === void 0 || record.state.status === "completing" || this.#waitingOn(record, true).length > 0 || this.#notDue(record)) { + const record2 = this.#liveRecord(id); + if (record2 === void 0 || record2.state.status === "completing" || this.#waitingOn(record2, true).length > 0 || this.#notDue(record2)) { dropped.push(id); continue; } - if (!record.abortRequested && !record.background && this.#belowCancelled(parentOf(record))) { + if (!record2.abortRequested && !record2.background && this.#belowCancelled(parentOf(record2))) { cascade = true; continue; } - ready.push(record); + ready.push(record2); } return { dropped, cascade, ready }; } @@ -22571,49 +22571,49 @@ var TaskScheduler = class { * Live tasks a task waits for before its next invocation: its live ordinary owned work when abort-marked, since * abort runs bottom-up, otherwise the live part of the `on` of a wait. */ - #waitingOn(record, first = false) { - if (record.abortRequested) - return this.#ownedLive(record.id, { first }); - if (record.state.status !== "waiting") + #waitingOn(record2, first = false) { + if (record2.abortRequested) + return this.#ownedLive(record2.id, { first }); + if (record2.state.status !== "waiting") return []; if (!first) - return record.state.on.filter((id) => this.#live.has(id)); - const live = record.state.on.find((id) => this.#live.has(id)); + return record2.state.on.filter((id) => this.#live.has(id)); + const live = record2.state.on.find((id) => this.#live.has(id)); return live === void 0 ? [] : [live]; } /** Resolve the record's definition by kind, migrating an older stored version. */ - #resolve(record, snapshot) { - const fit = this.#fit(record, snapshot.task(record.kind)); + #resolve(record2, snapshot) { + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return { kind: "blocked", reason: fit.reason }; if (!fit.migrates) - return { kind: "ready", task: fit.task, record }; + return { kind: "ready", task: fit.task, record: record2 }; const definition = erased(fit.task); try { if (definition.migrate === void 0) - throw missingMigration(record, definition); - const migrated = definition.migrate(record.input, record.state.checkpoint, record.version); - const state = { ...record.state, checkpoint: copyJson(migrated.checkpoint) }; - const migratedRecord = { ...record, version: definition.version, input: copyJson(migrated.input), state }; + throw missingMigration(record2, definition); + const migrated = definition.migrate(record2.input, record2.state.checkpoint, record2.version); + const state = { ...record2.state, checkpoint: copyJson(migrated.checkpoint) }; + const migratedRecord = { ...record2, version: definition.version, input: copyJson(migrated.input), state }; return { kind: "ready", task: fit.task, record: migratedRecord }; } catch (error) { - this.#failedMigrations.set(record.id, { task: fit.task, error }); + this.#failedMigrations.set(record2.id, { task: fit.task, error }); queueMicrotask(() => this.#notifyBlocked()); this.#scheduleWake(); const extension = installerOf(snapshot, fit.task); - this.#report(error, { kind: "migration_failed", ...this.#aboutTask({ ...recordTask(record), extension }) }); + this.#report(error, { kind: "migration_failed", ...this.#aboutTask({ ...recordTask(record2), extension }) }); return { kind: "blocked", reason: "migration_failed" }; } } - #fit(record, task) { + #fit(record2, task) { if (task === void 0) return { reason: "missing_task" }; const version = task.definition.version; - if (version === record.version) + if (version === record2.version) return { task, migrates: false }; - if (version < record.version) + if (version < record2.version) return { reason: "task_too_old" }; - const failed = this.#failedMigrations.get(record.id); + const failed = this.#failedMigrations.get(record2.id); if (failed?.task === task) return { reason: "migration_failed", error: failed.error }; return { task, migrates: true }; @@ -22672,49 +22672,49 @@ var TaskScheduler = class { await this.#published(); await this.#loadScopes(false); const tasks = []; - for (const record of this.#live.values()) - tasks.push({ record, state: this.#inspectTask(record, snapshot) }); + for (const record2 of this.#live.values()) + tasks.push({ record: record2, state: this.#inspectTask(record2, snapshot) }); const scheduling = this.#closing ? "closing" : this.#enabled && !this.#held ? "running" : "paused"; return { scheduling, tasks }; } - #inspectTask(record, snapshot) { - if (this.#invocations.has(record.id)) + #inspectTask(record2, snapshot) { + if (this.#invocations.has(record2.id)) return { kind: "running" }; - if (record.state.status === "completing") + if (record2.state.status === "completing") return { kind: "completing" }; - const on = this.#waitingOn(record); + const on = this.#waitingOn(record2); if (on.length > 0) return { kind: "waiting", on }; - if (this.#notDue(record)) - return { kind: "sleeping", until: record.state.until }; - const fit = this.#fit(record, snapshot.task(record.kind)); + if (this.#notDue(record2)) + return { kind: "sleeping", until: record2.state.until }; + const fit = this.#fit(record2, snapshot.task(record2.kind)); if ("reason" in fit) return { kind: "blocked", ...fit }; if (fit.migrates && fit.task.definition.migrate === void 0) { - return { kind: "blocked", reason: "migration_failed", error: missingMigration(record, erased(fit.task)) }; + return { kind: "blocked", reason: "migration_failed", error: missingMigration(record2, erased(fit.task)) }; } return { kind: "pending", migrates: fit.migrates }; } - #createInvocation(record, mode, extension) { + #createInvocation(record2, mode, extension) { const controller = new AbortController(); const { promise: done, resolve: finish2 } = Promise.withResolvers(); const invocation = { - taskId: record.id, - kind: record.kind, + taskId: record2.id, + kind: record2.kind, extension, - conversationId: record.conversationId, + conversationId: record2.conversationId, mode, controller, - links: record.startedAt === void 0 ? record.links : void 0, + links: record2.startedAt === void 0 ? record2.links : void 0, context: withAbortSignal(controller.signal, this.#context), - record, + record: record2, watches: /* @__PURE__ */ new Set(), timers: /* @__PURE__ */ new Set(), ended: false, done, finish: () => finish2() }; - this.#invocations.set(record.id, invocation); + this.#invocations.set(record2.id, invocation); return invocation; } #start(reservation) { @@ -22925,14 +22925,14 @@ var TaskScheduler = class { * Write an outcome the scheduler decided. While the task's ordinary owned work is live it holds as `completing` and * its Harness cleanup waits for the final commit (spec §5.5, rule 4); otherwise it is terminal with its cleanup. */ - async #terminate(tx, record, outcome) { + async #terminate(tx, record2, outcome) { await this.#loadScopes(false); - if (await this.#region(() => this.#hasOwnedLive(record.id, overlayOf(tx)), this.#reloadScopes)) { - tx.setTask(withState(record, { status: "completing", outcome })); + if (await this.#region(() => this.#hasOwnedLive(record2.id, overlayOf(tx)), this.#reloadScopes)) { + tx.setTask(withState(record2, { status: "completing", outcome })); return; } - tx.setTask(withState(record, { status: "terminal", outcome })); - await this.#settleOutcome(tx, record, outcome); + tx.setTask(withState(record2, { status: "terminal", outcome })); + await this.#settleOutcome(tx, record2, outcome); } /** * Replace a running task's state with what it committed. A terminal state holds as `completing` while ordinary owned @@ -22947,8 +22947,8 @@ var TaskScheduler = class { if (next.status === "terminal") { const overlay = overlayOf(tx); await this.#loadScopes(false); - for (const record of overlay.tasks.values()) - await this.#loadChain(parentOf(record), overlay); + for (const record2 of overlay.tasks.values()) + await this.#loadChain(parentOf(record2), overlay); if (await this.#region(() => this.#hasOwnedLive(current.id, overlayOf(tx)), this.#reloadScopes)) { tx.setTask(withState(progressed(current), { status: "completing", outcome: next.outcome })); return; @@ -23042,11 +23042,11 @@ var TaskScheduler = class { for (let id = pending.pop(); id !== void 0; id = pending.pop()) { if (id === task) return path(id); - const record = overlay.tasks.get(id) ?? this.#liveRecord(id); - if (record === void 0 || record.state.status === "terminal") + const record2 = overlay.tasks.get(id) ?? this.#liveRecord(id); + if (record2 === void 0 || record2.state.status === "terminal") continue; - if (record.state.status === "waiting" && !record.abortRequested) { - for (const member of record.state.on) + if (record2.state.status === "waiting" && !record2.abortRequested) { + for (const member of record2.state.on) reach(member, id); } if (walked.has(id)) @@ -23088,8 +23088,8 @@ var TaskScheduler = class { if (conversationId === void 0) this.#record(ROOTS_KEY); for (const id of this.#unloaded) { - const record = this.#live.get(id); - if (record !== void 0 && !record.background && this.#inScope(parentOf(record), scope) !== false) + const record2 = this.#live.get(id); + if (record2 !== void 0 && !record2.background && this.#inScope(parentOf(record2), scope) !== false) return false; } const starts = conversationId === void 0 ? this.#roots : [conversationId]; @@ -23210,21 +23210,21 @@ var TaskScheduler = class { this.#record(ownedKey(invocation.taskId)); const records = []; for (const node of this.#below.get(invocation.taskId) ?? []) { - const record = typeof node === "string" ? void 0 : this.#liveRecord(node); - if (record?.owner === invocation.taskId) - records.push(record); + const record2 = typeof node === "string" ? void 0 : this.#liveRecord(node); + if (record2?.owner === invocation.taskId) + records.push(record2); } return records; }); - return owned.sort((a, b) => a.id - b.id).map((record) => copyJson(record)); + return owned.sort((a, b) => a.id - b.id).map((record2) => copyJson(record2)); })), abortOwned: (id, context) => this.#read(invocation, async () => { const bound = withAbortSignal(invocation.controller.signal, context); - const record = await this.#session.readOnLine((storage2) => storage2.task(id, bound)); - if (record?.owner !== invocation.taskId) { + const record2 = await this.#session.readOnLine((storage2) => storage2.task(id, bound)); + if (record2?.owner !== invocation.taskId) { throw new Error(`Task ${id} is not owned by task ${invocation.taskId}`); } - if (record.state.status === "terminal") + if (record2.state.status === "terminal") return; await this.abort(id, bound, true); await this.waitForTask(id, bound); @@ -23370,8 +23370,8 @@ function installerOf(snapshot, task) { function reportTask(task) { return { id: task.taskId, kind: task.kind, conversationId: task.conversationId }; } -function recordTask(record) { - return { taskId: record.id, kind: record.kind, conversationId: record.conversationId }; +function recordTask(record2) { + return { taskId: record2.id, kind: record2.kind, conversationId: record2.conversationId }; } var endedByCrashesStates = /* @__PURE__ */ new WeakMap(); function endedByCrashes(state, message) { @@ -23384,33 +23384,33 @@ function interruptionOf(error) { return void 0; return detail.reason === "restart_limit" || detail.reason === "interrupted" ? detail.reason : void 0; } -function withAbortMark(record, reason) { - const { abortReason: _reason, restarts: _restarts, ...rest } = record; +function withAbortMark(record2, reason) { + const { abortReason: _reason, restarts: _restarts, ...rest } = record2; return { ...rest, abortRequested: true, ...reason === void 0 ? {} : { abortReason: reason } }; } -function cancellationIntent(record) { - return record.state.status !== "terminal" && (record.abortRequested || failedOutcome(record)); +function cancellationIntent(record2) { + return record2.state.status !== "terminal" && (record2.abortRequested || failedOutcome(record2)); } function intentChanges(previous, next) { return (previous?.abortRequested ?? false) !== next.abortRequested || previous?.abortReason !== next.abortReason || (previous !== void 0 && failedOutcome(previous)) !== failedOutcome(next) || (previous !== void 0 && cancellationIntent(previous)) !== cancellationIntent(next); } -function failedOutcome(record) { - const state = record.state; +function failedOutcome(record2) { + const state = record2.state; return (state.status === "completing" || state.status === "terminal") && state.outcome.status !== "completed"; } function parentOf(node) { return node.owner !== void 0 ? { task: node.owner } : { conversation: node.conversationId }; } -function nodeOf(record) { +function nodeOf(record2) { return { - conversationId: record.conversationId, - ...record.owner === void 0 ? {} : { owner: record.owner }, - background: record.background + conversationId: record2.conversationId, + ...record2.owner === void 0 ? {} : { owner: record2.owner }, + background: record2.background }; } function overlayOf(tx) { - const tasks = new Map(tx.stagedTasks().map((record) => [record.id, record])); - const edges = new Map(tx.stagedConversations().map((record) => [record.id, record.owner?.taskId ?? null])); + const tasks = new Map(tx.stagedTasks().map((record2) => [record2.id, record2])); + const edges = new Map(tx.stagedConversations().map((record2) => [record2.id, record2.owner?.taskId ?? null])); return { tasks, edges }; } function addTo(map2, key, value2) { @@ -23428,15 +23428,15 @@ function deleteFrom(map2, key, value2) { if (values.size === 0) map2.delete(key); } -function memoOf(record, name) { - const memos = record?.memos; +function memoOf(record2, name) { + const memos = record2?.memos; return memos !== void 0 && Object.hasOwn(memos, name) ? memos[name] : void 0; } function sessionMethod(session, name) { return session[name].bind(session); } -function missingMigration(record, definition) { - return new Error(`Task ${record.kind} version ${definition.version} has no migration from ${record.version}`); +function missingMigration(record2, definition) { + return new Error(`Task ${record2.kind} version ${definition.version} has no migration from ${record2.version}`); } function erased(task) { return task.definition; @@ -23444,26 +23444,26 @@ function erased(task) { function endedError(invocation) { return new Error(`Task ${invocation.taskId} invocation has ended`); } -function progressed(record) { - if (record.restarts === void 0) - return record; - const { restarts: _restarts, ...rest } = record; +function progressed(record2) { + if (record2.restarts === void 0) + return record2; + const { restarts: _restarts, ...rest } = record2; return rest; } -function withState(record, state) { +function withState(record2, state) { if (state.status !== "terminal" && state.status !== "completing") - return { ...record, state }; - const { memos: _memos, restarts: _restarts, ...rest } = record; + return { ...record2, state }; + const { memos: _memos, restarts: _restarts, ...rest } = record2; return { ...rest, state }; } -function outdatedReason(task, record) { +function outdatedReason(task, record2) { if (task === void 0) return "missing_task"; - return task.definition.version < record.version ? "task_too_old" : "migration_failed"; + return task.definition.version < record2.version ? "task_too_old" : "migration_failed"; } -function canReserve(task, record) { +function canReserve(task, record2) { const definition = task.definition; - return definition.version === record.version || definition.version > record.version && definition.migrate !== void 0; + return definition.version === record2.version || definition.version > record2.version && definition.migrate !== void 0; } function validateSleep(invocation, task, until2) { if (invocation.mode === "abort") @@ -24962,7 +24962,7 @@ function structuredOutputError(tool, result) { return `Tool ${tool.name} returned structuredOutput that does not match its schema: ${path}: ${first?.message ?? "invalid"}`; } async function settle2(runtime, input, call2, ending, result, context, durationMs) { - const nestedIds = (await runtime.ownedTasks(context)).filter((record) => record.kind === ToolTask.definition.name && record.input.kind === "nested").map((record) => record.id); + const nestedIds = (await runtime.ownedTasks(context)).filter((record2) => record2.kind === ToolTask.definition.name && record2.input.kind === "nested").map((record2) => record2.id); const madeNested = await runtime.memo(MADE_NESTED_CALLS, context) === true; await Promise.all(nestedIds.map((id) => runtime.abortOwned(id, context))); await runtime.commit(async (tx) => { @@ -26097,33 +26097,33 @@ function clearProgress(slot) { delete slot.details; delete slot.diagnostics; } -async function settleSchedulerOutcome(tx, record, outcome) { - if (record.kind === TOOL_TASK_KIND) { - const live2 = await tx.doc(LiveDoc, record.conversationId); - const slot = toolSlot(live2, record.id); +async function settleSchedulerOutcome(tx, record2, outcome) { + if (record2.kind === TOOL_TASK_KIND) { + const live2 = await tx.doc(LiveDoc, record2.conversationId); + const slot = toolSlot(live2, record2.id); if (slot !== void 0) finishSlot(slot); - removeNestedSlots(live2, record.id); + removeNestedSlots(live2, record2.id); return; } const detail = outcome.status === "faulted" ? outcome.error.detail : void 0; const cutOff = typeof detail === "object" && detail !== null && "reason" in detail && detail.reason === "restart_limit"; - if (record.kind === COMPACTION_TASK_KIND) { - const live2 = await tx.doc(LiveDoc, record.conversationId); + if (record2.kind === COMPACTION_TASK_KIND) { + const live2 = await tx.doc(LiveDoc, record2.conversationId); if (cutOff) - removeCompactionStatus(live2, record.id); + removeCompactionStatus(live2, record2.id); else - failCompactionStatus(live2, record.id, outcome.status === "faulted" ? outcome.error.message : outcome.reason); + failCompactionStatus(live2, record2.id, outcome.status === "faulted" ? outcome.error.message : outcome.reason); return; } - if (!RUN_TASK_KINDS.has(record.kind)) + if (!RUN_TASK_KINDS.has(record2.kind)) return; - const live = await tx.doc(LiveDoc, record.conversationId); - if (live.run?.taskId !== record.id) + const live = await tx.doc(LiveDoc, record2.conversationId); + if (live.run?.taskId !== record2.id) return; if (!cutOff) - await convertPartial(tx, live, record.conversationId); - endRun(tx, live, record.id, outcome.status === "faulted" ? { status: "unanswered", reason: cutOff ? "aborted" : "faulted", detail: outcome.error.message } : { + await convertPartial(tx, live, record2.conversationId); + endRun(tx, live, record2.id, outcome.status === "faulted" ? { status: "unanswered", reason: cutOff ? "aborted" : "faulted", detail: outcome.error.message } : { status: "unanswered", reason: "orphaned", detail: outcome.reason, @@ -26194,23 +26194,23 @@ var Submissions = class { } /** Handle for an existing submission, or `undefined`. */ async get(id, context) { - const record = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); - return record === void 0 ? void 0 : new SubmissionHandle(record.id, this); + const record2 = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); + return record2 === void 0 ? void 0 : new SubmissionHandle(record2.id, this); } async status(id, context) { - const record = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); - if (record === void 0) + const record2 = await this.#session.readOnLine((storage2) => storage2.submission(id, context)); + if (record2 === void 0) throw new Error(`Submission ${id} does not exist`); - return record; + return record2; } async wait(id, context) { this.#resume(); const found = await this.#session.readOnLine(async (storage2) => { - const record = await storage2.submission(id, context); - if (record === void 0) + const record2 = await storage2.submission(id, context); + if (record2 === void 0) throw new Error(`Submission ${id} does not exist`); - if (isSettled(record)) - return { promise: Promise.resolve(record) }; + if (isSettled(record2)) + return { promise: Promise.resolve(record2) }; if (this.#closed) throw closedError(this.#session); return { promise: this.#waiters.add(id, context) }; @@ -26220,16 +26220,16 @@ var Submissions = class { /** Withdraw a queued submission and remove its inbox item; placed inputs and settled submissions are reported. */ abort(id, context, conversationId) { return this.#session.commitWith(async (tx) => { - const record = await tx.submission(id); - if (record === void 0 || conversationId !== void 0 && record.conversationId !== conversationId) { + const record2 = await tx.submission(id); + if (record2 === void 0 || conversationId !== void 0 && record2.conversationId !== conversationId) { return "not_found"; } - if (record.status === "queued") { + if (record2.status === "queued") { tx.settleSubmission(id, { status: "unanswered", reason: "aborted" }); - await removeInboxItem(tx, record.conversationId, id); + await removeInboxItem(tx, record2.conversationId, id); return "aborted"; } - return record.status === "placed" ? "already_placed" : "settled"; + return record2.status === "placed" ? "already_placed" : "settled"; }, context); } #observe(publication) { @@ -26262,8 +26262,8 @@ var SubmissionHandle = class { return result; } }; -function isSettled(record) { - return record.status === "done" || record.status === "unanswered"; +function isSettled(record2) { + return record2.status === "done" || record2.status === "unanswered"; } function checkInputEntry(entry2) { const { kind } = entry2; @@ -26918,8 +26918,8 @@ var TaskGraphView = class { if (mount === void 0) return; const ops2 = []; - for (const [key, record] of mount.records) { - const blocked = this.#blocking.blocked(record); + for (const [key, record2] of mount.records) { + const blocked = this.#blocking.blocked(record2); if (blocked === mount.value.tasks[key]?.blocked) continue; ops2.push(blocked === void 0 ? ["d", ["tasks", key, "blocked"]] : ["s", ["tasks", key, "blocked"], blocked]); @@ -26937,12 +26937,12 @@ var TaskGraphView = class { records.push(...await scanAll((cursor) => storage2.scanTasks({ status }, SCAN_PAGE_SIZE4, cursor, context))); } records.sort((a, b) => a.id - b.id); - for (const record of records) { - const owned = await scanAll((cursor) => storage2.scanConversations({ ownerTaskId: record.id }, SCAN_PAGE_SIZE4, cursor, context)); + for (const record2 of records) { + const owned = await scanAll((cursor) => storage2.scanConversations({ ownerTaskId: record2.id }, SCAN_PAGE_SIZE4, cursor, context)); const conversations = owned.map((conversation) => conversation.id).sort((a, b) => a - b); - const subject = blockedSubject(record); - tasks[String(record.id)] = nodeOf2(record, conversations, this.#blocking.blocked(subject)); - live.set(String(record.id), subject); + const subject = blockedSubject(record2); + tasks[String(record2.id)] = nodeOf2(record2, conversations, this.#blocking.blocked(subject)); + live.set(String(record2.id), subject); } return { value: { tasks }, records: live }; } @@ -26954,10 +26954,10 @@ function advance(mount, publication, context, blocked, report2) { for (const change of publication.changes) { if (change.type !== "task") continue; - const record = change.value; - const key = String(record.id); + const record2 = change.value; + const key = String(record2.id); const previous = node(key); - if (record.state.status === "terminal") { + if (record2.state.status === "terminal") { mount.records.delete(key); if (previous === void 0) continue; @@ -26965,9 +26965,9 @@ function advance(mount, publication, context, blocked, report2) { changed.set(key, void 0); continue; } - const subject = blockedSubject(record); + const subject = blockedSubject(record2); mount.records.set(key, subject); - const next = nodeOf2(record, previous?.conversations ?? [], blocked(subject)); + const next = nodeOf2(record2, previous?.conversations ?? [], blocked(subject)); if (previous !== void 0 && JSON.stringify(next) === JSON.stringify(previous)) continue; ops2.push(["s", ["tasks", key], next]); @@ -26996,21 +26996,21 @@ function publish(mount, ops2, context, report2) { for (const observer of [...mount.observers]) contained(() => observer.advance(call2, frameContext), report2); } -function nodeOf2(record, conversations, blocked) { +function nodeOf2(record2, conversations, blocked) { return { - id: record.id, - kind: record.kind, - conversationId: record.conversationId, - ...record.owner === void 0 ? {} : { owner: record.owner }, - background: record.background, - abortRequested: record.abortRequested, - state: stateOf(record), + id: record2.id, + kind: record2.kind, + conversationId: record2.conversationId, + ...record2.owner === void 0 ? {} : { owner: record2.owner }, + background: record2.background, + abortRequested: record2.abortRequested, + state: stateOf(record2), conversations, ...blocked === void 0 ? {} : { blocked } }; } -function stateOf(record) { - const state = record.state; +function stateOf(record2) { + const state = record2.state; switch (state.status) { case "pending": case "running": @@ -27293,8 +27293,8 @@ var ConversationImpl = class { return readContext(this.#host.harness, this.#host.storage, this.id, context, options?.at); } entries(query, limit, cursor, context) { - const scan = conversationEntries(this.id, query); - return this.#host.harness.readOnLine((storage2) => storage2.scanEntries(scan, limit, cursor, context)); + const scan2 = conversationEntries(this.id, query); + return this.#host.harness.readOnLine((storage2) => storage2.scanEntries(scan2, limit, cursor, context)); } fork(at, options, context) { return this.#host.create({ kind: "fork", parentId: this.id, at, ownership: options.ownership }, options, context); @@ -27358,15 +27358,15 @@ var HarnessImpl = class extends SessionImpl { settleOutcome: settleSchedulerOutcome, withdrawInputs: withdrawQueuedInputs, conversation: async (id, binding, callContext) => { - const record = await this.readOnLine((line) => line.conversation(id, callContext)); - return record === void 0 ? void 0 : boundConversation(id, binding, this.#submissions, this.#tasks); + const record2 = await this.readOnLine((line) => line.conversation(id, callContext)); + return record2 === void 0 ? void 0 : boundConversation(id, binding, this.#submissions, this.#tasks); }, context: withoutAbortSignal(context), readOnly: options.readOnly === true }); this.#submissions = new Submissions(this, now, settings, () => this.#tasks.resume(), options.telemetry); this.#taskGraph = new TaskGraphView(this, { - blocked: (record) => this.#tasks.blockedReason(record), + blocked: (record2) => this.#tasks.blockedReason(record2), subscribeBlocked: (listener) => this.#tasks.subscribeBlocked(listener) }); this.#host = { @@ -27434,8 +27434,8 @@ var HarnessImpl = class extends SessionImpl { inspect(context) { return this.readOnLine(async (storage2) => { const { scheduling, tasks } = await this.#tasks.inspect(this.#options.registry.snapshot()); - const scan = (status) => scanAll((cursor) => storage2.scanSubmissions({ status }, SCAN_PAGE_SIZE6, cursor, context)); - const submissions = [...await scan("queued"), ...await scan("placed")].sort((a, b) => a.id - b.id); + const scan2 = (status) => scanAll((cursor) => storage2.scanSubmissions({ status }, SCAN_PAGE_SIZE6, cursor, context)); + const submissions = [...await scan2("queued"), ...await scan2("placed")].sort((a, b) => a.id - b.id); return { scheduling, tasks, submissions }; }); } @@ -27483,8 +27483,8 @@ var HarnessImpl = class extends SessionImpl { } async conversation(id, context) { this.assertUsable(); - const record = await this.readOnLine((storage2) => storage2.conversation(id, context)); - return record === void 0 ? void 0 : new ConversationImpl(record.id, this.#host); + const record2 = await this.readOnLine((storage2) => storage2.conversation(id, context)); + return record2 === void 0 ? void 0 : new ConversationImpl(record2.id, this.#host); } createConversation(options, context) { return this.#create({ kind: "independent", ownership: options.ownership }, options, context); @@ -27506,12 +27506,12 @@ var HarnessImpl = class extends SessionImpl { if (target.kind === "root" && await tx.conversation(ROOT_CONVERSATION_ID) !== void 0) { return ROOT_CONVERSATION_ID; } - const record = target.kind === "root" ? await tx.createRootConversation() : target.kind === "fork" ? await tx.forkConversation(target.parentId, target.at, { ownership: target.ownership }) : await tx.createConversation({ ownership: target.ownership }); + const record2 = target.kind === "root" ? await tx.createRootConversation() : target.kind === "fork" ? await tx.forkConversation(target.parentId, target.at, { ownership: target.ownership }) : await tx.createConversation({ ownership: target.ownership }); if (options.agent !== void 0) - await configure(tx, record.id, options.agent); + await configure(tx, record2.id, options.agent); if (options.init !== void 0) - await options.init(tx, record.id); - return record.id; + await options.init(tx, record2.id); + return record2.id; }, context); return new ConversationImpl(id, this.#host); } @@ -27520,13 +27520,13 @@ var HarnessImpl = class extends SessionImpl { * `pi.usage`, a fresh `pi.provider`, the conversation's `pi.agent` (see `createAgent()`), then * `HarnessOptions.conversationCreated`. */ - async conversationCreated(tx, record) { - await tx.doc(LiveDoc, record.id); - await tx.doc(InboxDoc, record.id); - await tx.doc(UsageDoc, record.id); - await tx.doc(ProviderDoc, record.id); - await createAgent(tx, record); - await this.#options.conversationCreated?.(tx, record); + async conversationCreated(tx, record2) { + await tx.doc(LiveDoc, record2.id); + await tx.doc(InboxDoc, record2.id); + await tx.doc(UsageDoc, record2.id); + await tx.doc(ProviderDoc, record2.id); + await createAgent(tx, record2); + await this.#options.conversationCreated?.(tx, record2); } }; function safeReport(onReport, telemetry) { @@ -28013,13 +28013,13 @@ var addressKey = (address) => JSON.stringify([ scopeKey2(address.scope), address.key === void 0 ? ["singleton"] : ["family", address.key] ]); -var recordAddressKey = (record) => addressKey({ kind: record.kind, scope: record.scope, key: record.key }); -var isAliveAt = (record, at) => { +var recordAddressKey = (record2) => addressKey({ kind: record2.kind, scope: record2.scope, key: record2.key }); +var isAliveAt = (record2, at) => { if (at === "current") - return record.retiredAt === void 0; - return record.createdAt <= at && (record.retiredAt === void 0 || at < record.retiredAt); + return record2.retiredAt === void 0; + return record2.createdAt <= at && (record2.retiredAt === void 0 || at < record2.retiredAt); }; -var isCurrentOnly = (record) => record.scope.kind !== "conversation" || record.history === "latest"; +var isCurrentOnly = (record2) => record2.scope.kind !== "conversation" || record2.history === "latest"; var tableContaining = (state, id) => state.recordTypes.get(id); var page = (values, limit, order = "ascending") => { const items = values.slice(0, limit); @@ -28382,9 +28382,9 @@ var MemoryStorage = class { return index?.currentId === void 0 ? void 0 : clone(this.state.documents.get(index.currentId).record); } for (const id of index?.ids ?? []) { - const record = this.state.documents.get(id).record; - if (isAliveAt(record, at)) - return clone(record); + const record2 = this.state.documents.get(id).record; + if (isAliveAt(record2, at)) + return clone(record2); } return void 0; } @@ -28405,11 +28405,11 @@ var MemoryStorage = class { const start = after === void 0 ? 0 : upperBound(ids, after); const values = []; for (let index = start; index < ids.length && values.length <= limit; index++) { - const record = this.state.documents.get(ids[index]).record; - if (query.kind !== void 0 && record.kind !== query.kind) + const record2 = this.state.documents.get(ids[index]).record; + if (query.kind !== void 0 && record2.kind !== query.kind) continue; - if (isAliveAt(record, query.at)) - values.push(record); + if (isAliveAt(record2, query.at)) + values.push(record2); } return page(values, limit); } @@ -28583,25 +28583,25 @@ var MemoryStorage = class { for (const [id, action] of actions) { let stored = this.state.documents.get(id); if (action.create !== void 0) { - const record = { + const record2 = { ...action.create, createdAt: seq, ...action.retire ? { retiredAt: seq } : {} }; - stored = { record, revisions: [{ ...action.content, seq }] }; + stored = { record: record2, revisions: [{ ...action.content, seq }] }; this.state.recordTypes.set(id, "document"); this.state.documents.set(id, stored); - const key = recordAddressKey(record); + const key = recordAddressKey(record2); let address = this.state.documentAddresses.get(key); if (address === void 0) { address = { ids: [] }; this.state.documentAddresses.set(key, address); } insertSorted(address.ids, id); - let scopeIds = this.state.documentIdsByScope.get(scopeKey2(record.scope)); + let scopeIds = this.state.documentIdsByScope.get(scopeKey2(record2.scope)); if (scopeIds === void 0) { scopeIds = []; - this.state.documentIdsByScope.set(scopeKey2(record.scope), scopeIds); + this.state.documentIdsByScope.set(scopeKey2(record2.scope), scopeIds); } insertSorted(scopeIds, id); this.nextId = Math.max(this.nextId, id + 1); @@ -28871,12 +28871,12 @@ var addressKey2 = (address) => { const parts = addressParts(address); return JSON.stringify([parts.kind, parts.scopeKind, parts.ownerId, parts.family, parts.keyValue]); }; -var isAliveAt2 = (record, at) => { +var isAliveAt2 = (record2, at) => { if (at === "current") - return record.retiredAt === void 0; - return record.createdAt <= at && (record.retiredAt === void 0 || at < record.retiredAt); + return record2.retiredAt === void 0; + return record2.createdAt <= at && (record2.retiredAt === void 0 || at < record2.retiredAt); }; -var isCurrentOnly2 = (record) => record.scope.kind !== "conversation" || record.history === "latest"; +var isCurrentOnly2 = (record2) => record2.scope.kind !== "conversation" || record2.history === "latest"; var writeId = (write) => { switch (write.type) { case "conversation": @@ -29014,9 +29014,9 @@ var SqliteStorage = class _SqliteStorage { async scanConversations(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.ownerConversationId !== void 0) { clauses.push("owner_conversation_id = ?"); params.push(query.ownerConversationId); @@ -29026,7 +29026,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.ownerTaskId); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.conversations} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.conversations} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } entry(idOrConversationId, idOrContext, context) { @@ -29162,9 +29162,9 @@ var SqliteStorage = class _SqliteStorage { async scanTasks(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.conversationId !== void 0) { clauses.push("conversation_id = ?"); params.push(query.conversationId); @@ -29186,7 +29186,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.background ? 1 : 0); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.tasks} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.tasks} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } async submission(id, _context) { @@ -29197,9 +29197,9 @@ var SqliteStorage = class _SqliteStorage { async scanSubmissions(query, limit, cursor, _context) { this.assertOpen(); const start = scanStart(query.order, cursor, "ascending"); - const scan = scanSql(start); - const clauses = [scan.clause]; - const params = [scan.param]; + const scan2 = scanSql(start); + const clauses = [scan2.clause]; + const params = [scan2.param]; if (query.conversationId !== void 0) { clauses.push("conversation_id = ?"); params.push(query.conversationId); @@ -29209,7 +29209,7 @@ var SqliteStorage = class _SqliteStorage { params.push(query.status); } params.push(limit + 1); - const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.submissions} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan.direction} LIMIT ?`, ...params); + const rows = await this.db.all(`SELECT record FROM ${this.schema.tables.submissions} WHERE ${clauses.join(" AND ")} ORDER BY id ${scan2.direction} LIMIT ?`, ...params); return page2(rows.map((row) => parseJson(row.record)), limit, start.order); } async submissionByRequest(conversationId, requestId, _context) { @@ -29292,11 +29292,11 @@ var SqliteStorage = class _SqliteStorage { const row = await executor.get(`SELECT record FROM ${this.schema.tables.documents} WHERE id = ?`, id); if (row === void 0) return void 0; - const record = parseJson(row.record); - if (at !== "current" && isCurrentOnly2(record)) { + const record2 = parseJson(row.record); + if (at !== "current" && isCurrentOnly2(record2)) { throw new StorageRequestError(`Document ${id} does not retain historical content`); } - if (!isAliveAt2(record, at)) + if (!isAliveAt2(record2, at)) return void 0; const upper = at === "current" ? Number.MAX_SAFE_INTEGER : at; const base2 = await executor.get(`SELECT seq, kind, version, content FROM ${this.schema.tables.documentRevisions} @@ -29312,7 +29312,7 @@ var SqliteStorage = class _SqliteStorage { } value2 = apply(value2, parseJson(revision.content)); } - return { record, version: base2.version, value: value2, deltasSinceBase: tail.length }; + return { record: record2, version: base2.version, value: value2, deltasSinceBase: tail.length }; } candidateNextId(writes) { let nextId = this.nextId; @@ -29411,11 +29411,11 @@ var SqliteStorage = class _SqliteStorage { throw new Error(`Document ${id} version transition requires a base`); } } - const record = action.create ?? existing; - const key = addressKey2(record); + const record2 = action.create ?? existing; + const key = addressKey2(record2); let live = liveCounts.get(key); if (live === void 0) - live = await this.currentDocumentId(executor, record) === void 0 ? 0 : 1; + live = await this.currentDocumentId(executor, record2) === void 0 ? 0 : 1; if (action.retire && existing !== void 0) live--; if (action.create !== void 0 && !action.retire) @@ -29490,24 +29490,24 @@ var SqliteStorage = class _SqliteStorage { async applyDocumentActions(executor, actions, seq) { for (const [id, action] of actions) { const content = action.content; - let record; + let record2; if (action.create !== void 0) { - record = { + record2 = { ...action.create, createdAt: seq, ...action.retire ? { retiredAt: seq } : {} }; - const parts = addressParts(record); + const parts = addressParts(record2); await this.claimId(executor, id, "document"); await executor.run(`INSERT INTO ${this.schema.tables.documents} (id, kind, family, key_value, scope_kind, owner_id, created_at, retired_at, record) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, parts.kind, parts.family, parts.keyValue, parts.scopeKind, parts.ownerId, seq, action.retire ? seq : null, encodeJson(record)); + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, parts.kind, parts.family, parts.keyValue, parts.scopeKind, parts.ownerId, seq, action.retire ? seq : null, encodeJson(record2)); } else { const row = await executor.get(`SELECT record FROM ${this.schema.tables.documents} WHERE id = ?`, id); - record = parseJson(row.record); + record2 = parseJson(row.record); } if (content !== void 0) { - if (content.kind === "base" && isCurrentOnly2(record)) { + if (content.kind === "base" && isCurrentOnly2(record2)) { await executor.run(`DELETE FROM ${this.schema.tables.documentRevisions} WHERE document_id = ?`, id); } const encodedContent = content.kind === "base" ? encodeJson(content.value) : encodeJson(content.ops); @@ -29515,10 +29515,10 @@ var SqliteStorage = class _SqliteStorage { } if (action.retire) { if (action.create === void 0) { - record = { ...record, retiredAt: seq }; - await executor.run(`UPDATE ${this.schema.tables.documents} SET retired_at = ?, record = ? WHERE id = ?`, seq, encodeJson(record), id); + record2 = { ...record2, retiredAt: seq }; + await executor.run(`UPDATE ${this.schema.tables.documents} SET retired_at = ?, record = ? WHERE id = ?`, seq, encodeJson(record2), id); } - if (isCurrentOnly2(record)) { + if (isCurrentOnly2(record2)) { await executor.run(`DELETE FROM ${this.schema.tables.documentRevisions} WHERE document_id = ?`, id); } } @@ -30941,8 +30941,8 @@ async function readText(reader, path, offset, limit, context) { const requestedEnd = limit === void 0 ? void 0 : Math.max(scanStart2 + 1, sliceIndex(startLine + limit)); const scanEnd = requestedEnd !== void 0 && Number.isSafeInteger(requestedEnd) ? requestedEnd : void 0; const scanOf = async (endLine) => getOrThrow(await reader.scanLines({ startLine: scanStart2, ...endLine === void 0 ? {} : { endLine } }, context)); - let scan = await scanOf(scanEnd); - const totalFileLines = scan.newlines + 1; + let scan2 = await scanOf(scanEnd); + const totalFileLines = scan2.newlines + 1; if (startLine >= totalFileLines) { throw new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`); } @@ -30955,16 +30955,16 @@ async function readText(reader, path, offset, limit, context) { const sliceEnd = relativeEnd < 0 ? Math.max(totalFileLines + relativeEnd, 0) : relativeEnd; selectedLineCount = Math.max(0, sliceEnd - sliceStart); if (selectedLineCount > 0 && relativeEnd < 0) - scan = await scanOf(sliceEnd); + scan2 = await scanOf(sliceEnd); } const empty = selectedLineCount === 0; - const endsWithNewline = !empty && scan.lastLineStart === scan.end && scan.lastLineStart > scan.start; + const endsWithNewline = !empty && scan2.lastLineStart === scan2.end && scan2.lastLineStart > scan2.start; const totals = { - lines: empty || scan.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0), - bytes: empty ? 0 : scan.selectedBytes + lines: empty || scan2.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0), + bytes: empty ? 0 : scan2.selectedBytes }; const firstBytes = getOrThrow(await reader.read(0, 3, context)); - const head = empty ? "" : await readHead(reader, scan.start, scan.end, startsWithBom(firstBytes), context); + const head = empty ? "" : await readHead(reader, scan2.start, scan2.end, startsWithBom(firstBytes), context); const { content: headText, ...truncation } = truncateHeadOf(head, totals); const diagnostics = []; let outputText = headText; @@ -30972,7 +30972,7 @@ async function readText(reader, path, offset, limit, context) { if (truncation.firstLineExceedsLimit) { const integral = Number.isInteger(startLine); const lineBytes = new TextEncoder().encode(integral ? head.split("\n")[0] ?? "" : ""); - const lineSize = integral ? scan.firstLineBytes : 0; + const lineSize = integral ? scan2.firstLineBytes : 0; const end = characterEnd(lineBytes, DEFAULT_MAX_BYTES); outputText = new TextDecoder().decode(lineBytes.subarray(0, end)); diagnostics.push({ @@ -31198,6 +31198,176 @@ function StringEnum(values, options) { }; } +// events.ts +var events_exports = {}; +__export(events_exports, { + EVENT_FEED: () => EVENT_FEED, + EVENT_FEED_SIZE: () => EVENT_FEED_SIZE, + EventFeed: () => EventFeed, + EventLog: () => EventLog, + NoticeEvent: () => NoticeEvent, + defineEvent: () => defineEvent, + emit: () => emit, + eventLog: () => eventLog +}); +function defineEvent(name, options = {}) { + const kind = `event.${name}`; + return { name, kind, version: options.version ?? 1, is: (event) => event?.kind === kind }; +} +var NoticeEvent = defineEvent("sausage.notice"); +var EVENT_FEED = "sausage.event-feed"; +var EVENT_FEED_SIZE = 100; +var EventLog = defineDoc({ + kind: "sausage.events", + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ cursors: {} }) +}); +var EventFeed = defineDoc({ + kind: EVENT_FEED, + version: 1, + scope: "conversation", + history: "latest", + fork: "initial", + checkpointWhen: () => false, + initial: () => ({ events: [] }) +}); +var record = (entry2) => structuredClone({ id: entry2.id, name: entry2.kind.slice(6), kind: entry2.kind, ...entry2.data }); +async function emit(tx, event, data) { + const index = await tx.doc(EventLog, ROOT_CONVERSATION_ID); + if (index.conversation === void 0) index.conversation = (await tx.createConversation({ ownership: { kind: "ownerless" } })).id; + const entry2 = await tx.appendEntry(index.conversation, { kind: event.kind, data: { v: event.version, at: (/* @__PURE__ */ new Date()).toISOString(), data } }); + const emitted = record(entry2); + const feed = (await tx.doc(EventFeed, ROOT_CONVERSATION_ID)).events; + feed.push(emitted); + if (feed.length > EVENT_FEED_SIZE) feed.splice(0, feed.length - EVENT_FEED_SIZE); + return emitted; +} +async function scan(tx, log, after, visit) { + let cursor; + do { + const page3 = await tx.scanEntries({ conversationId: log, minEntryId: after + 1, order: "ascending" }, 256, cursor); + for (const entry2 of page3.items) if (visit(entry2)) return; + cursor = page3.next; + } while (cursor); +} +function eventLog(harness2) { + const reactors = /* @__PURE__ */ new Map(); + function drain2(name) { + const state = reactors.get(name); + state.chain = state.chain.then(async () => { + for (; ; ) { + const active = state.active; + if (!active) return; + let attempted; + try { + const handled = await harness2().commit(async (tx) => { + const index = await tx.doc(EventLog, ROOT_CONVERSATION_ID); + if (index.cursors[name] === void 0 && active.from === "now") { + const newest = index.conversation === void 0 ? void 0 : (await tx.scanEntries({ conversationId: index.conversation }, 1)).items[0]; + index.cursors[name] = newest?.id ?? 0; + return false; + } + if (index.conversation === void 0) return false; + const after = index.cursors[name] ?? 0; + let pending, last = after; + await scan(tx, index.conversation, after, (entry2) => { + last = entry2.id; + if (active.kinds.has(entry2.kind)) pending = entry2; + return pending !== void 0; + }); + if (pending) { + attempted = pending.id; + await active.handler(tx, record(pending)); + } + if (last !== after) index.cursors[name] = last; + return pending !== void 0; + }, BACKGROUND_CONTEXT); + state.failure = void 0; + if (!handled) return; + } catch (error) { + state.failure = { error: String(error), at: (/* @__PURE__ */ new Date()).toISOString(), entryId: attempted }; + return; + } + } + }); + } + return { + emit, + reactor(name, events, handler, options = {}) { + const active = { kinds: new Set(events.map((event) => event.kind)), handler, from: options.from ?? "start" }; + if (!reactors.has(name)) reactors.set(name, { kinds: active.kinds, chain: Promise.resolve() }); + const state = reactors.get(name); + let detach; + const stop2 = () => { + detach?.(); + detach = void 0; + if (state.active === active) state.active = void 0; + return state.chain; + }; + return { + start: async () => { + await stop2(); + state.active = active; + state.kinds = active.kinds; + const observation2 = harness2().observeCommits({ advance: (publication) => { + if (publication.changes.some((change) => change.type === "entry" && active.kinds.has(change.value.kind))) drain2(name); + }, end: () => { + } }); + detach = () => observation2.detach(); + drain2(name); + }, + stop: stop2 + }; + }, + eventLog: { recent, reactors: statuses } + }; + async function recent(options = {}) { + const log = (await harness2().snapshot(EventLog, ROOT_CONVERSATION_ID, BACKGROUND_CONTEXT))?.conversation; + if (log === void 0) return []; + const kinds = options.events && new Set(options.events.map((event) => event.kind)); + const limit = options.limit ?? 20; + return harness2().commit(async (tx) => { + const found = []; + let cursor; + do { + const page3 = await tx.scanEntries({ + conversationId: log, + ...options.since === void 0 ? {} : { minEntryId: options.since + 1 }, + ...options.before === void 0 ? {} : { maxEntryId: options.before - 1 }, + order: options.order ?? "descending" + }, 256, cursor); + for (const entry2 of page3.items) { + if (!(kinds ? kinds.has(entry2.kind) : entry2.kind.startsWith("event."))) continue; + found.push(record(entry2)); + if (found.length >= limit) return found; + } + cursor = page3.next; + } while (cursor); + return found; + }, BACKGROUND_CONTEXT); + } + async function statuses() { + const index = await harness2().snapshot(EventLog, ROOT_CONVERSATION_ID, BACKGROUND_CONTEXT); + return harness2().commit(async (tx) => { + const list = []; + for (const [name, state] of reactors) { + const cursor = index?.cursors[name] ?? 0; + let lag = 0; + if (index?.conversation !== void 0) await scan(tx, index.conversation, cursor, (entry2) => { + if (state.kinds.has(entry2.kind)) lag++; + return false; + }); + list.push({ name, events: [...state.kinds].map((kind) => kind.slice(6)), cursor, lag, running: state.active !== void 0, ...state.failure ? { failure: state.failure } : {} }); + } + return list; + }, BACKGROUND_CONTEXT); + } +} + // extensions.ts var ops = Deno.core.ops; var encoder4 = new TextEncoder(); @@ -31254,12 +31424,13 @@ async function git(cwd, ...args) { return result.stdout.trim(); } function registerModules() { - const modules = { "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports }; + const modules = { "@earendil-works/sausage-events": events_exports, "@earendil-works/pi-schema": dist_exports, "@earendil-works/pi-durable": dist_exports3, "@earendil-works/pi-durable/tools": tools_exports, "@earendil-works/pi-ai": dist_exports4, "@earendil-works/pi-ai/models": models_exports, "@earendil-works/chord": dist_exports2, "@earendil-works/chord/context": context_exports }; globalThis.__sausage = { modules }; ops.op_modules(Object.fromEntries(Object.entries(modules).map(([name, value2]) => [name, Object.keys(value2)]))); } async function loadExtensions(harness2, registry2, storage2, models2, context, builtins = []) { registerModules(); + const eventService = eventLog(() => harness2); const repo = "/src/sausage"; let providers = /* @__PURE__ */ new Map(); const resources = /* @__PURE__ */ new Map(); @@ -31284,7 +31455,13 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b } }; const scope = (name, staged) => { - const open = [], close = []; + let opened = false; + const notices = /* @__PURE__ */ new Set(); + const open = [async () => { + opened = true; + }], close = [async () => { + opened = false; + }]; const handle = (api) => new Proxy({}, { get: (_, key) => { if (typeof key === "symbol" || key === "then") return void 0; const value2 = providers.get(api.name); @@ -31315,6 +31492,14 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b if (!resources.has(id)) resources.set(id, create()); return resources.get(id); }, + emit: eventService.emit, + eventLog: eventService.eventLog, + react: (key, tokens, handler, options) => { + const reactor = eventService.reactor(`${name}/${key}`, tokens, handler, options); + open.push(reactor.start); + close.push(reactor.stop); + if (opened) void reactor.start(); + }, onOpen: (cb) => open.push(cb), onClose: (cb) => close.push(cb), notice: (message) => { @@ -31322,6 +31507,11 @@ async function loadExtensions(harness2, registry2, storage2, models2, context, b ops.op_extension_status(JSON.stringify(state)); ops.op_output(`${name}: ${message} `); + if (!notices.has(message)) { + notices.add(message); + void harness2.commit((tx) => eventService.emit(tx, NoticeEvent, { extension: name, message, level: "warning" }), context).catch((error) => ops.op_output(`Recording a notice failed: ${String(error)} +`)); + } } }; return { host, open, close }; @@ -31574,9 +31764,9 @@ try { equal(await title(second), "Build it well"); const at = await root.commit((tx) => tx.appendEntry(root.id, { kind: "branch.origin", data: {} }), BACKGROUND_CONTEXT); const branch = await root.commit(async (tx) => { - const record = await tx.forkConversation(root.id, at.id, { ownership: { kind: "ownerless" } }); - (await tx.doc(Branch, record.id)).branch = { from: root.id, at: at.id, intent: "Fresh" }; - return record; + const record2 = await tx.forkConversation(root.id, at.id, { ownership: { kind: "ownerless" } }); + (await tx.doc(Branch, record2.id)).branch = { from: root.id, at: at.id, intent: "Fresh" }; + return record2; }, BACKGROUND_CONTEXT); assert(Object.keys((await harness.snapshot(Plan, branch.id, BACKGROUND_CONTEXT)).epics).length === 1, "Fork inherited plan"); equal(JSON.parse(text(await call({ action: "show" }, branch.id))), Plan.definition.initial()); diff --git a/host/js/subagents-test-bundle.js b/host/js/subagents-test-bundle.js index 1ee6644..482b3f5 100644 --- a/host/js/subagents-test-bundle.js +++ b/host/js/subagents-test-bundle.js @@ -32,9 +32,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge mod )); -// host/js/node_modules/partial-json/dist/options.js +// node_modules/partial-json/dist/options.js var require_options = __commonJS({ - "host/js/node_modules/partial-json/dist/options.js"(exports) { + "node_modules/partial-json/dist/options.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Allow = exports.ALL = exports.COLLECTION = exports.ATOM = exports.SPECIAL = exports.INF = exports._INFINITY = exports.INFINITY = exports.NAN = exports.BOOL = exports.NULL = exports.OBJ = exports.ARR = exports.NUM = exports.STR = void 0; @@ -57,9 +57,9 @@ var require_options = __commonJS({ } }); -// host/js/node_modules/partial-json/dist/index.js +// node_modules/partial-json/dist/index.js var require_dist = __commonJS({ - "host/js/node_modules/partial-json/dist/index.js"(exports) { + "node_modules/partial-json/dist/index.js"(exports) { "use strict"; var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { if (k2 === void 0) k2 = k; @@ -268,7 +268,7 @@ var require_dist = __commonJS({ } }); -// host/js/node_modules/@earendil-works/chord/dist/context/index.js +// node_modules/@earendil-works/chord/dist/context/index.js var context_exports = {}; __export(context_exports, { BACKGROUND_CONTEXT: () => BACKGROUND_CONTEXT, @@ -283,7 +283,7 @@ __export(context_exports, { withoutAbortSignal: () => withoutAbortSignal }); -// host/js/node_modules/@earendil-works/pi-telemetry/dist/metrics.js +// node_modules/@earendil-works/pi-telemetry/dist/metrics.js function createTypedMetrics(metrics, schemas) { const definitions = /* @__PURE__ */ new Map(); for (const schema of schemas) { @@ -297,7 +297,7 @@ function createTypedMetrics(metrics, schemas) { }; } -// host/js/node_modules/@earendil-works/pi-telemetry/dist/noop.js +// node_modules/@earendil-works/pi-telemetry/dist/noop.js async function startNoopSpan(_options, callback) { return callback(noopTelemetrySpan); } @@ -332,7 +332,7 @@ var noopTelemetrySpan = { Object.freeze(noopTelemetrySpan); var NOOP_TELEMETRY_CONTEXT = noopTelemetrySpan; -// host/js/node_modules/@earendil-works/pi-telemetry/dist/index.js +// node_modules/@earendil-works/pi-telemetry/dist/index.js function defineTelemetrySchema(schema) { return schema; } @@ -344,7 +344,7 @@ function createTypedSpanStarter(telemetryContext, _schemas) { return bindTypedSpanStarter(telemetryContext); } -// host/js/node_modules/@earendil-works/chord/dist/context/index.js +// node_modules/@earendil-works/chord/dist/context/index.js var ABORT_SIGNAL_CONTEXT_KEY = Object.freeze({ token: /* @__PURE__ */ Symbol("chord.abortSignal") }); @@ -443,7 +443,7 @@ function abortError(signal) { return reason instanceof Error ? reason : new DOMException("The operation was aborted", "AbortError"); } -// host/js/node_modules/@earendil-works/pi-ai/dist/models.js +// node_modules/@earendil-works/pi-ai/dist/models.js var models_exports = {}; __export(models_exports, { AI_TELEMETRY_SCHEMA: () => AI_TELEMETRY_SCHEMA, @@ -459,7 +459,7 @@ __export(models_exports, { modelsAreEqual: () => modelsAreEqual }); -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js +// node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js var FifoQueue = class { incoming = []; outgoing = []; @@ -570,7 +570,7 @@ function createAssistantMessageEventStream() { return new AssistantMessageEventStream(); } -// host/js/node_modules/@earendil-works/pi-ai/dist/api/lazy.js +// node_modules/@earendil-works/pi-ai/dist/api/lazy.js function createSetupErrorMessage(model2, error, timestamp) { return { role: "assistant", @@ -653,7 +653,7 @@ function lazyApi(load, capabilities) { return api; } -// host/js/node_modules/@earendil-works/pi-ai/dist/auth/context.js +// node_modules/@earendil-works/pi-ai/dist/auth/context.js var __rewriteRelativeImportExtension = function(path, preserveJsx) { if (typeof path === "string" && /^\.\.?\//.test(path)) { return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) { @@ -690,7 +690,7 @@ function defaultProviderAuthContext() { }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/abort.js +// node_modules/@earendil-works/pi-ai/dist/utils/abort.js function abortReason(signal) { if (signal.reason !== void 0) return signal.reason; @@ -736,7 +736,7 @@ function raceWithAbortSignal(operation, signal) { }); } -// host/js/node_modules/@earendil-works/pi-ai/dist/auth/credential-store.js +// node_modules/@earendil-works/pi-ai/dist/auth/credential-store.js var InMemoryCredentialStore = class { credentials = /* @__PURE__ */ new Map(); chains = /* @__PURE__ */ new Map(); @@ -784,7 +784,7 @@ var InMemoryCredentialStore = class { } }; -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/diagnostics.js +// node_modules/@earendil-works/pi-ai/dist/utils/diagnostics.js function formatThrownValue(value2) { if (value2 instanceof Error) return value2.message || value2.name; @@ -810,7 +810,7 @@ function appendAssistantMessageDiagnostic(message, diagnostic) { message.diagnostics = [...message.diagnostics ?? [], diagnostic]; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/models-error.js +// node_modules/@earendil-works/pi-ai/dist/utils/models-error.js var ModelsError = class extends Error { code; constructor(code, message, options) { @@ -828,7 +828,7 @@ function withCauseDetail(message, cause) { return `${message}: ${detail}`; } -// host/js/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +// node_modules/@earendil-works/pi-ai/dist/auth/resolve.js function resolveProviderAuth(provider2, credentials, authContext, overrides) { const signal = operationSignal(overrides?.signal); return raceWithAbortSignal(resolveProviderAuthWithSignal(provider2, credentials, authContext, overrides, signal), signal); @@ -929,7 +929,7 @@ async function readCredential(credentials, providerId, signal) { } } -// host/js/node_modules/@earendil-works/pi-ai/dist/models-store.js +// node_modules/@earendil-works/pi-ai/dist/models-store.js var InMemoryModelsStore = class { entries = /* @__PURE__ */ new Map(); async read(providerId, options) { @@ -947,7 +947,7 @@ var InMemoryModelsStore = class { } }; -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/text.js +// node_modules/@earendil-works/pi-ai/dist/utils/text.js function contentText(content, separator = "\n") { if (typeof content === "string") return content; @@ -977,7 +977,7 @@ function ownedSlice(text2, start2, end = text2.length) { return `${text2.slice(start2, end)} `.slice(0, -1); } -// host/js/node_modules/@earendil-works/pi-ai/dist/telemetry.js +// node_modules/@earendil-works/pi-ai/dist/telemetry.js var provider = { type: "string", description: "Provider identifier.", required: true }; var model = { type: "string", description: "Requested model identifier.", required: true }; var stop = { @@ -1165,10 +1165,10 @@ function observeModelRequest(requestedModel, options, poll) { }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/version.js +// node_modules/@earendil-works/pi-ai/dist/utils/version.js var PI_AI_VERSION = "1.5.0"; -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/identity.js +// node_modules/@earendil-works/pi-ai/dist/utils/identity.js var PI_ATTRIBUTION = { name: "pi", url: "https://pi.dev", categories: ["cli-agent"] }; var NVIDIA_NIM_HOST = "integrate.api.nvidia.com"; var OPENCODE_HOST = "opencode.ai"; @@ -1292,7 +1292,7 @@ function withClientIdentity(models2, identity) { return mutable; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/model-operations.js +// node_modules/@earendil-works/pi-ai/dist/utils/model-operations.js function getModelType(model2) { return model2.type ?? "chat"; } @@ -1342,7 +1342,7 @@ function classifierErrorResult(model2, error, aborted = false) { }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/transcript.js +// node_modules/@earendil-works/pi-ai/dist/utils/transcript.js function createInitialSystemMessage(systemPrompt, tools) { const hasSystemPrompt = systemPrompt !== void 0 && systemPrompt.length > 0; const hasTools = tools !== void 0 && tools.length > 0; @@ -1501,7 +1501,7 @@ function resolveTranscriptTools(messages, support) { return { requestTools: getCurrentTools(systemMessages.slice(0, folded)), foldedSystemMessages: folded }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/models.js +// node_modules/@earendil-works/pi-ai/dist/models.js var KNOWN_MODEL_TYPES = { chat: true, image: true, classifier: true }; function hasKnownModelType(model2) { return Object.hasOwn(KNOWN_MODEL_TYPES, getModelType(model2)); @@ -2168,7 +2168,7 @@ function modelsAreEqual(a, b) { return getModelType(a) === getModelType(b) && a.id === b.id && a.provider === b.provider; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/json-parse.js +// node_modules/@earendil-works/pi-ai/dist/utils/json-parse.js var import_partial_json = __toESM(require_dist(), 1); var VALID_JSON_ESCAPES = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]); function isControlCharacter(char) { @@ -2285,7 +2285,7 @@ function parseStreamingJson(partialJson) { } } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/streaming-json.js +// node_modules/@earendil-works/pi-ai/dist/utils/streaming-json.js var MAX_REPORTED_DEPTH = 32; var ROOT_SLOT = { kind: "root" }; var CREDIT_PER_APPEND = 256; @@ -2724,7 +2724,7 @@ function setMember(members, key, value2) { } } -// host/js/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +// node_modules/@earendil-works/pi-ai/dist/providers/faux.js var DEFAULT_API = "faux"; var DEFAULT_PROVIDER = "faux"; var DEFAULT_MODEL_ID = "faux-1"; @@ -3199,7 +3199,7 @@ function fauxProvider(options = {}) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/index.js +// node_modules/@earendil-works/pi-durable/dist/index.js var dist_exports3 = {}; __export(dist_exports3, { AgentDoc: () => AgentDoc, @@ -3250,7 +3250,7 @@ __export(dist_exports3, { wrapTool: () => wrapTool }); -// host/js/node_modules/@earendil-works/chord/dist/index.js +// node_modules/@earendil-works/chord/dist/index.js var dist_exports2 = {}; __export(dist_exports2, { ChordError: () => ChordError, @@ -3282,7 +3282,7 @@ __export(dist_exports2, { webSocketLink: () => webSocketLink }); -// host/js/node_modules/@earendil-works/pi-schema/dist/index.js +// node_modules/@earendil-works/pi-schema/dist/index.js var dist_exports = {}; __export(dist_exports, { CoercingValidator: () => CoercingValidator, @@ -3307,7 +3307,7 @@ __export(dist_exports, { unknown: () => unknown }); -// host/js/node_modules/@earendil-works/pi-schema/dist/builders.js +// node_modules/@earendil-works/pi-schema/dist/builders.js function str(options = {}) { return { type: "string", ...options }; } @@ -3380,7 +3380,7 @@ function obj(properties, options = {}) { }; } -// host/js/node_modules/@earendil-works/pi-schema/dist/resolver.js +// node_modules/@earendil-works/pi-schema/dist/resolver.js var DEFAULT_BASE = "https://json-schema.invalid/root.json"; var Resolver = class { draft; @@ -3567,7 +3567,7 @@ function documentOf(uri) { return hash < 0 ? uri : uri.slice(0, hash); } -// host/js/node_modules/@earendil-works/pi-schema/dist/shared.js +// node_modules/@earendil-works/pi-schema/dist/shared.js function sharedRegex(source) { const hit = regexes.get(source)?.deref(); if (hit !== void 0) @@ -3588,7 +3588,7 @@ var regexCleanup = new FinalizationRegistry((source) => { regexes.delete(source); }); -// host/js/node_modules/@earendil-works/pi-schema/dist/coerce.js +// node_modules/@earendil-works/pi-schema/dist/coerce.js var Coercer = class { c; schema; @@ -4112,7 +4112,7 @@ function isPlainObject(value2) { return typeof value2 === "object" && value2 !== null && !Array.isArray(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/checks.js +// node_modules/@earendil-works/pi-schema/dist/checks.js var ALWAYS = () => true; var NEVER = () => false; var IS_STRING = (v) => typeof v === "string"; @@ -4515,7 +4515,7 @@ function deepEqual(a, b) { return true; } -// host/js/node_modules/@earendil-works/pi-schema/dist/annotations.js +// node_modules/@earendil-works/pi-schema/dist/annotations.js function newAnnotations() { return { props: null, allProps: false, items: 0, allItems: false, indexes: null }; } @@ -4692,7 +4692,7 @@ function unevaluatedItems(check2) { }; } -// host/js/node_modules/@earendil-works/pi-schema/dist/compiler.js +// node_modules/@earendil-works/pi-schema/dist/compiler.js var RESTART = /* @__PURE__ */ Symbol("restart"); var Compiler = class { r; @@ -5185,7 +5185,7 @@ function entries(value2) { return isSchemaObject(value2) ? Object.entries(value2) : []; } -// host/js/node_modules/@earendil-works/pi-schema/dist/explain.js +// node_modules/@earendil-works/pi-schema/dist/explain.js var Explainer = class { c; schema; @@ -5547,7 +5547,7 @@ function typeName(v) { return typeof v; } -// host/js/node_modules/@earendil-works/pi-schema/dist/validator.js +// node_modules/@earendil-works/pi-schema/dist/validator.js function compile(schema, options = NO_OPTIONS) { const check2 = compileCheck(schema, options); return options.coerce === true ? new CoercingValidator(schema, options, check2) : new Validator(schema, options, check2); @@ -5608,7 +5608,7 @@ function compiler(schema, options) { } } -// host/js/node_modules/@earendil-works/chord/dist/errors.js +// node_modules/@earendil-works/chord/dist/errors.js var CHORD_ERROR_CODES = [ "aborted", "contract_mismatch", @@ -5661,7 +5661,7 @@ async function runAll(steps, message) { throw new AggregateError(errors, message); } -// host/js/node_modules/@earendil-works/chord/dist/schema.js +// node_modules/@earendil-works/chord/dist/schema.js var asSchema = (schema) => schema !== null && typeof schema === "object" ? schema : schema === false ? { not: {} } : {}; function canonicalJson(value2) { if (value2 === null || typeof value2 !== "object") @@ -6021,7 +6021,7 @@ function functionFits(sub, sup) { return fits(sub.returnType, sup.returnType); } -// host/js/node_modules/@earendil-works/chord/dist/contract.js +// node_modules/@earendil-works/chord/dist/contract.js var MEMBER = /* @__PURE__ */ Symbol("chord.member"); function method(args, result) { return { [MEMBER]: "method", args, result }; @@ -6229,7 +6229,7 @@ function takesNoArgs(contract, name) { return false; } -// host/js/node_modules/@earendil-works/chord/dist/delta/diff.js +// node_modules/@earendil-works/chord/dist/delta/diff.js var DEFAULT_OVERLAP_SCAN = 65536; var MAX_DELTA_OPERATIONS = 4096; var overflowedBatches = /* @__PURE__ */ new WeakSet(); @@ -6650,7 +6650,7 @@ function diffRevisions(before, after) { return deltaCost >= snapshotCost ? [["r", after]] : operations; } -// host/js/node_modules/@earendil-works/chord/dist/json.js +// node_modules/@earendil-works/chord/dist/json.js var DATA_DESCRIPTOR = { value: void 0, writable: true, @@ -6766,7 +6766,7 @@ function check(value2, ancestors) { } } -// host/js/node_modules/@earendil-works/chord/dist/delta/apply-immutable-trusted.js +// node_modules/@earendil-works/chord/dist/delta/apply-immutable-trusted.js var DATA_DESCRIPTOR2 = { value: void 0, writable: true, @@ -6888,7 +6888,7 @@ function isContainer2(value2) { return value2 !== null && typeof value2 === "object"; } -// host/js/node_modules/@earendil-works/chord/dist/delta/tracker.js +// node_modules/@earendil-works/chord/dist/delta/tracker.js var NODE = /* @__PURE__ */ Symbol("chord.delta.overlay.node"); var PREPARED = /* @__PURE__ */ new WeakMap(); var RELEASED = {}; @@ -8937,7 +8937,7 @@ function isContainer3(value2) { return value2 !== null && typeof value2 === "object"; } -// host/js/node_modules/@earendil-works/chord/dist/delta/pending.js +// node_modules/@earendil-works/chord/dist/delta/pending.js var MAX_PENDING_OPS = 1e4; var EMPTY = { kind: "empty" }; var REPLACE = { kind: "replace" }; @@ -9055,7 +9055,7 @@ function merge2(previous, next) { return void 0; } -// host/js/node_modules/@earendil-works/chord/dist/delta/index.js +// node_modules/@earendil-works/chord/dist/delta/index.js var isObj = (value2) => value2 !== null && typeof value2 === "object"; function overlap(a, b, scan2, probe = 64, maxCandidates = 8) { if (a.length === 0 || b.length === 0 || scan2 === 0) @@ -9604,7 +9604,7 @@ function decoder() { }; } -// host/js/node_modules/@earendil-works/chord/dist/publish.js +// node_modules/@earendil-works/chord/dist/publish.js var queue = []; var held = 0; var draining = false; @@ -9633,7 +9633,7 @@ function drain() { } } -// host/js/node_modules/@earendil-works/chord/dist/cell.js +// node_modules/@earendil-works/chord/dist/cell.js var sequence = 0; var Fanout = class { #values = /* @__PURE__ */ new Set(); @@ -9961,7 +9961,7 @@ var Derived = class extends Source { var isSource = (value2) => value2 instanceof Source; var notReady = (what) => new ChordError("not_ready", `${what} is not ready`); -// host/js/node_modules/@earendil-works/chord/dist/object.js +// node_modules/@earendil-works/chord/dist/object.js var noop = () => { }; var makeService = (contract, factory, options, facet) => { @@ -11427,7 +11427,7 @@ var SelfImpl = class { } }; -// host/js/node_modules/@earendil-works/chord/dist/reload.js +// node_modules/@earendil-works/chord/dist/reload.js function dryRun(candidate) { const { run: run2, migrated, service, object } = candidate; const exposed = memberSchemas(service, run2.implementation); @@ -12027,7 +12027,7 @@ var Reload = class { } }; -// host/js/node_modules/@earendil-works/chord/dist/wire.js +// node_modules/@earendil-works/chord/dist/wire.js var PROTOCOL_VERSION = 4; var Id = int({ minimum: 1 }); var Name = str({ minLength: 1 }); @@ -12168,7 +12168,7 @@ function toWireError(error) { } var fromWireError = (error) => new ChordError(error.code, error.message.replace(/^[a-z_]+(\/[a-z_]+)?: /, ""), { reason: error.reason }); -// host/js/node_modules/@earendil-works/chord/dist/cluster.js +// node_modules/@earendil-works/chord/dist/cluster.js var reloadSeq = 0; var modulesFor = (remote2, hostName) => { let best; @@ -12360,7 +12360,7 @@ var ChildReloads = class { } }; -// host/js/node_modules/@earendil-works/chord/dist/link.js +// node_modules/@earendil-works/chord/dist/link.js var BaseLink = class { #handler; #queue = []; @@ -12668,7 +12668,7 @@ var WebSocketLink = class extends BaseLink { }; var webSocketLink = (socket, options) => new WebSocketLink(socket, options); -// host/js/node_modules/@earendil-works/chord/dist/connection.js +// node_modules/@earendil-works/chord/dist/connection.js var DEFAULT_WINDOW = 256 * 1024; var PENDING_WINDOWS = 4; var unavailable = (message) => new ChordError("unavailable", message); @@ -13804,7 +13804,7 @@ function clientError(error) { return error; } -// host/js/node_modules/@earendil-works/chord/dist/remote.js +// node_modules/@earendil-works/chord/dist/remote.js var noop2 = () => { }; function deferred() { @@ -14381,7 +14381,7 @@ var RemoteRef = class { } }; -// host/js/node_modules/@earendil-works/chord/dist/host.js +// node_modules/@earendil-works/chord/dist/host.js var defineFacet = (setup, options = {}) => ({ setup, uses: options.uses }); var isFacet = (ref) => typeof ref === "object" && !(ref instanceof URL); var EMPTY_TABLE = /* @__PURE__ */ new Map(); @@ -15328,13 +15328,13 @@ function reaches(from, to) { return false; } -// host/js/node_modules/@earendil-works/chord/dist/registry.js +// node_modules/@earendil-works/chord/dist/registry.js var defineRegistry = (name, options = {}) => ({ name, conflict: options.conflict ?? "error" }); -// host/js/node_modules/@earendil-works/chord/dist/state.js +// node_modules/@earendil-works/chord/dist/state.js var StateSubscriber = class { #listener; #reportError; @@ -15599,7 +15599,7 @@ function isReplicatedStateSource(value2) { return typeof value2.attach === "function"; } -// host/js/node_modules/@earendil-works/chord/dist/watch.js +// node_modules/@earendil-works/chord/dist/watch.js var STOPPED = { kind: "stopped" }; var ABORTED = { kind: "aborted" }; var ENDED = { kind: "ended" }; @@ -15945,7 +15945,7 @@ var NativeWatch = class extends WatchLifecycle { } }; -// host/js/node_modules/@earendil-works/chord/dist/websocket.js +// node_modules/@earendil-works/chord/dist/websocket.js function acceptWebSockets(server, host, options) { server.on("connection", (socket, request2) => { const link = webSocketLink(socket, { compress: true, acceptPacked: false }); @@ -15958,7 +15958,7 @@ function acceptWebSockets(server, host, options) { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/ids.js +// node_modules/@earendil-works/pi-durable/dist/ids.js function idFromNumber(value2) { return value2; } @@ -15966,7 +15966,7 @@ function seqFromNumber(value2) { return value2; } -// host/js/node_modules/@earendil-works/pi-durable/dist/documents.js +// node_modules/@earendil-works/pi-durable/dist/documents.js function defineDoc(definition) { validateDefinition(definition); return { definition }; @@ -16050,7 +16050,7 @@ function materializeDocumentValue(definition, record2, version, value2) { return copyJson(definition.migrate(value2, version)); } -// host/js/node_modules/@earendil-works/pi-durable/dist/entries.js +// node_modules/@earendil-works/pi-durable/dist/entries.js function defineEntry(kind) { if (typeof kind !== "string" || kind.length === 0) throw new TypeError("Entry kind must be a non-empty string"); @@ -16066,7 +16066,7 @@ var ToolResultEntry = defineEntry("pi.tool-result"); var ResetEntry = defineEntry("pi.reset"); var CompactionEntry = defineEntry("pi.compaction"); -// host/js/node_modules/@earendil-works/pi-durable/dist/errors.js +// node_modules/@earendil-works/pi-durable/dist/errors.js function errorMessage(error) { if (error instanceof Error) return error.message; @@ -16118,7 +16118,7 @@ var ConversationIdle = class extends Error { } }; -// host/js/node_modules/@earendil-works/pi-durable/dist/timers.js +// node_modules/@earendil-works/pi-durable/dist/timers.js var MAX_TIMER_DELAY = 2147483647; var inWorkers = typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers"; var nativeTimer = (callback, delayMs, options) => { @@ -16174,7 +16174,7 @@ function hostTimers(setTimer, report2) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/cached-storage.js +// node_modules/@earendil-works/pi-durable/dist/session/cached-storage.js var CachedStorage = class { #storage; #conversations = new Lru(4096); @@ -16424,7 +16424,7 @@ function freeze(value2) { return Object.freeze(value2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/conversation-tails.js +// node_modules/@earendil-works/pi-durable/dist/session/conversation-tails.js var LATEST_CAPACITY = 4096; var MARKER_CAPACITY = 256; var ConversationTails = class { @@ -16510,7 +16510,7 @@ function freeze2(value2) { return Object.freeze(value2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/keys.js +// node_modules/@earendil-works/pi-durable/dist/session/keys.js function taskKey(id) { return `task:${id}`; } @@ -16647,10 +16647,10 @@ var GatedStorage = class { } }; -// host/js/node_modules/@earendil-works/pi-durable/dist/types.js +// node_modules/@earendil-works/pi-durable/dist/types.js var ROOT_CONVERSATION_ID = 1; -// host/js/node_modules/@earendil-works/pi-durable/dist/session/forks.js +// node_modules/@earendil-works/pi-durable/dist/session/forks.js var SCAN_PAGE_SIZE = 256; async function prepareForkDocumentCopies(storage2, parentConversationId, at, childConversationId, context) { const entry2 = await storage2.entry(parentConversationId, at, context); @@ -16689,7 +16689,7 @@ async function collectCopies(storage2, scope, at, policy, childConversationId, c } while (cursor !== void 0); } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/transaction.js +// node_modules/@earendil-works/pi-durable/dist/session/transaction.js function applySubmissionChange(current, change) { if (current.status === "done" || current.status === "unanswered") return current; @@ -17625,7 +17625,7 @@ function publishes(plan) { return plan.retire || plan.change?.loaded === void 0 || plan.content !== void 0; } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/writer.js +// node_modules/@earendil-works/pi-durable/dist/session/writer.js var SESSION_FAILED = { kind: "failed" }; var Writer = class { #host; @@ -17874,7 +17874,7 @@ function hop() { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/session/session.js +// node_modules/@earendil-works/pi-durable/dist/session/session.js var NO_HOLDS = []; var NO_OPERATIONS = []; var NO_KEYS = []; @@ -19383,7 +19383,7 @@ var GuardedStorage = class { } }; -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/agent.js +// node_modules/@earendil-works/pi-durable/dist/harness/agent.js var DEFAULT_RETRY_POLICY = { enabled: true, maxRetries: 3, @@ -19645,7 +19645,7 @@ function applyWrap(items, target, wrap, nameOf, report2) { } } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +// node_modules/@earendil-works/pi-ai/dist/utils/estimate.js var CHARS_PER_TOKEN = 3.5; var ESTIMATED_IMAGE_CHARS = 4800; function calculateContextTokens(usage) { @@ -19698,7 +19698,7 @@ function estimateToolsTokens(tools) { return estimateTextTokens(safeJsonStringify(tools)); } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +// node_modules/@earendil-works/pi-ai/dist/utils/retry.js function buildProviderErrorPattern(patterns) { return new RegExp(patterns.join("|"), "i"); } @@ -19879,12 +19879,12 @@ function isRetryableAssistantError(message) { return RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/tasks.js +// node_modules/@earendil-works/pi-durable/dist/tasks.js function defineTask(definition) { return { definition }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/context.js +// node_modules/@earendil-works/pi-durable/dist/harness/context.js var SCAN_PAGE_SIZE2 = 256; var EXCLUDED_STOP_REASONS = /* @__PURE__ */ new Set(["aborted", "error", "deferred"]); var MISSING_RESULT_TEXT = "Tool result unavailable: history ends before this call completed."; @@ -20112,7 +20112,7 @@ function missingResult(call2, timestamp) { }); } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +// node_modules/@earendil-works/pi-ai/dist/utils/overflow.js var OVERFLOW_PATTERNS = [ /prompt (?:is )?too long/i, // Anthropic and z.ai token overflow @@ -20212,7 +20212,7 @@ function getOverflowPatterns() { return [...OVERFLOW_PATTERNS]; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/inbox.js +// node_modules/@earendil-works/pi-durable/dist/harness/inbox.js var InboxDoc = defineDoc({ kind: "pi.inbox", version: 1, @@ -20316,7 +20316,7 @@ async function withdrawQueuedInputs(tx, conversationId, reason = "aborted") { } } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/json.js +// node_modules/@earendil-works/pi-durable/dist/harness/json.js function assignJson(target, key, value2, plan) { if (plan?.kind === "keep") return; @@ -20352,7 +20352,7 @@ function isRecord(value2) { return typeof value2 === "object" && value2 !== null && !Array.isArray(value2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/partial-changes.js +// node_modules/@earendil-works/pi-durable/dist/harness/partial-changes.js var PartialChanges = class { #text = /* @__PURE__ */ new Map(); #toolCalls = /* @__PURE__ */ new Map(); @@ -20491,7 +20491,7 @@ function isRecord2(value2) { return typeof value2 === "object" && value2 !== null && !Array.isArray(value2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/prompt.js +// node_modules/@earendil-works/pi-durable/dist/harness/prompt.js function replaySections(messages) { const shown = /* @__PURE__ */ new Map(); for (const message of messages) { @@ -20597,7 +20597,7 @@ function systemEntry(sections, tools, timestamp) { return { model: [message] }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/uuid.js +// node_modules/@earendil-works/pi-ai/dist/utils/uuid.js var MAX_UUID_V7_TIMESTAMP = 281474976710655; var MAX_SEQUENCE = (1n << 41n) - 1n; var lastOrdinaryTimestamp = -1; @@ -20633,7 +20633,7 @@ function uuidv7(timestampMs) { return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/provider.js +// node_modules/@earendil-works/pi-durable/dist/harness/provider.js var ProviderDoc = defineDoc({ kind: "pi.provider", version: 1, @@ -20657,7 +20657,7 @@ async function ensureProviderSessionId(runtime, context) { return created; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/telemetry.js +// node_modules/@earendil-works/pi-durable/dist/harness/telemetry.js var entry = { description: "A transcript entry was committed; content remains in the transcript.", attributes: { @@ -20792,7 +20792,7 @@ var DURABLE_TELEMETRY_SCHEMA = defineTelemetrySchema({ } }); -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/util.js +// node_modules/@earendil-works/pi-durable/dist/harness/util.js var Waiters = class { #sets = /* @__PURE__ */ new Map(); add(key, context) { @@ -20858,7 +20858,7 @@ function conversationEntries(conversationId, query) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/scheduler.js +// node_modules/@earendil-works/pi-durable/dist/harness/scheduler.js var SCAN_PAGE_SIZE3 = 256; var NO_KEYS2 = []; var LIVE_STATUSES = ["pending", "running", "waiting", "sleeping", "completing"]; @@ -23488,7 +23488,7 @@ function jsonEqual(left, right) { return keys.every((key) => Object.hasOwn(right, key) && jsonEqual(left[key], right[key])); } -// host/js/node_modules/@earendil-works/chord/dist/output.js +// node_modules/@earendil-works/chord/dist/output.js var NEWLINE = 10; var INVALID_OUTPUT = /[\x00-\x08\x0b-\x1f\ufff9-\ufffb]/g; var ESCAPE_SEQUENCE = /\x1b[\]PX^_][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]|\x1b[\x20-\x2f]*[\x30-\x7e]/g; @@ -23807,7 +23807,7 @@ var Progress = class { } }; -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/date.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/date.js var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; function IsLeapYear(year) { @@ -23823,7 +23823,7 @@ function IsDate(value2) { return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/time.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/time.js var TIME = /^(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:([Zz])|([+-])(\d\d):(\d\d))?$/; function IsTime(value2, strictTimeZone = true) { const matches = TIME.exec(value2); @@ -23851,25 +23851,25 @@ function IsTime(value2, strictTimeZone = true) { return (totalUtcMin % 1440 + 1440) % 1440 === 1439; } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/date_time.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/date_time.js function IsDateTime(value2) { const dateTime = value2.split(/T/i); return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1]); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/duration.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/duration.js var Duration = /^P((\d+Y(\d+M(\d+D)?)?|\d+M(\d+D)?|\d+D)(T(\d+H(\d+M(\d+S)?)?|\d+M(\d+S)?|\d+S))?|T(\d+H(\d+M(\d+S)?)?|\d+M(\d+S)?|\d+S)|\d+W)$/; function IsDuration(value2) { return Duration.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/email.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/email.js var Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\[\x20-\x7e])*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i; function IsEmail(value2) { return Email.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/pattern/pattern.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/pattern/pattern.js var RE_RULE_HYPHEN_PLACEMENT = /^(?!-).*(? regexp.source).join("|"), "u"); -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/ascii.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/ascii.js function IsAsciiLabel(value2) { return RE_RULE_HYPHEN_PLACEMENT.test(value2) && RE_RULE_NOT_RESERVED_ACE.test(value2) && RE_ASCII_LDH.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/format/puny.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/format/puny.js var PUNYCODE_BASE = 36; var PUNYCODE_TMIN = 1; var PUNYCODE_TMAX = 26; @@ -24025,7 +24025,7 @@ function Encode(input) { return result.join(""); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/format/bidi.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/format/bidi.js var RE_RTL_ALLOWED = /^(?:R|AL|AN|EN|ES|CS|ET|ON|BN|NSM)$/; var RE_LTR_ALLOWED = /^(?:L|EN|ES|CS|ET|ON|BN|NSM)$/; var RE_RTL_CLASSES = /^(?:R|AL|AN)$/; @@ -24080,7 +24080,7 @@ function SatisfiesBidiRule(value2) { return true; } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/unicode.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/unicode.js function ExceedsMaxALabelLength(value2) { return RE_NON_ASCII.test(value2) && Encode(value2).length + 4 > 63; } @@ -24146,7 +24146,7 @@ function IsUnicodeLabel(value2) { return true; } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/puny.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/label/puny.js function IsPunyLabel(value2) { if (!IsAcePrefixed(value2)) return false; @@ -24163,7 +24163,7 @@ function IsPunyLabel(value2) { } } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/hostname.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/hostname.js function IsValidLabelLength(value2) { return value2.length > 0 && value2.length <= 63; } @@ -24178,7 +24178,7 @@ function IsHostname(value2) { return value2.split(".").every((label) => IsLabel(label)); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/idn-hostname.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idna/idn-hostname.js function IsValidLabelLength2(value2) { return value2.length > 0 && value2.length <= 63; } @@ -24199,35 +24199,35 @@ function IsIdnHostname(value2) { return labels.every((label) => IsLabel2(label) && (!hasBidiChars || SatisfiesBidiRule(label))); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/hostname.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/hostname.js function IsHostname2(value2) { return IsHostname(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/idn_email.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/idn_email.js var IdnEmail = /^(?:[A-Za-z0-9!#$%&'*+/=?^_`{|}~\u{0080}-\u{10FFFF}-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~\u{0080}-\u{10FFFF}-]+)*|"(?:[^"\\]|\\.)*")@[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,62})(?^`{|}\\]/; @@ -24243,26 +24243,26 @@ function IsIri(value2) { return URL.canParse(NarrowIpvFuture(value2)); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/iri_reference.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/iri_reference.js var InvalidIriChars2 = /[\x00-\x20\x7F\\]|%(?![0-9a-fA-F]{2})/; var MalformedScheme = /^[a-zA-Z][a-zA-Z0-9+\-.]*\/\//; function IsIriReference(value2) { return !InvalidIriChars2.test(value2) && !MalformedScheme.test(value2) && URL.canParse(value2, "http://example.com"); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/json_pointer.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/json_pointer.js var JsonPointer = /^(?:\/(?:[^~/]|~0|~1)*)*$/; function IsJsonPointer(value2) { return JsonPointer.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/json_pointer_uri_fragment.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/json_pointer_uri_fragment.js var JsonPointerUriFragment = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i; function IsJsonPointerUriFragment(value2) { return JsonPointerUriFragment.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/regex.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/regex.js function IsRegex(value2) { try { new RegExp(value2, "u"); @@ -24272,42 +24272,42 @@ function IsRegex(value2) { } } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/relative_json_pointer.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/relative_json_pointer.js var RelativeJsonPointer = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; function IsRelativeJsonPointer(value2) { return RelativeJsonPointer.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri.js var Uri = /^[a-z][a-z0-9+\-.]*:(?:\/\/(?:(?:[-a-z0-9._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:[\da-f]{1,4}:){6}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|::(?:[\da-f]{1,4}:){5}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:[\da-f]{1,4})?::(?:[\da-f]{1,4}:){4}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,1}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){3}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,2}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){2}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,3}[\da-f]{1,4})?::[\da-f]{1,4}:(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,4}[\da-f]{1,4})?::(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,5}[\da-f]{1,4})?::[\da-f]{1,4}|(?:(?:[\da-f]{1,4}:){0,6}[\da-f]{1,4})?::)|v[0-9a-f]+\.[-a-z0-9._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:[-a-z0-9._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[-a-z0-9._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[-a-z0-9._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; function IsUri(value2) { return Uri.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri_reference.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri_reference.js var UriReference = /^(?:[a-z][a-z0-9+\-.]*:(?:\/\/(?:(?:[-a-z0-9._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:[\da-f]{1,4}:){6}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|::(?:[\da-f]{1,4}:){5}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:[\da-f]{1,4})?::(?:[\da-f]{1,4}:){4}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,1}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){3}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,2}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){2}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,3}[\da-f]{1,4})?::[\da-f]{1,4}:(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,4}[\da-f]{1,4})?::(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,5}[\da-f]{1,4})?::[\da-f]{1,4}|(?:(?:[\da-f]{1,4}:){0,6}[\da-f]{1,4})?::)|v[0-9a-f]+\.[-a-z0-9._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:[-a-z0-9._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:\/\/(?:(?:[-a-z0-9._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:[\da-f]{1,4}:){6}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|::(?:[\da-f]{1,4}:){5}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:[\da-f]{1,4})?::(?:[\da-f]{1,4}:){4}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,1}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){3}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,2}[\da-f]{1,4})?::(?:[\da-f]{1,4}:){2}(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,3}[\da-f]{1,4})?::[\da-f]{1,4}:(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,4}[\da-f]{1,4})?::(?:[\da-f]{1,4}:[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d))|(?:(?:[\da-f]{1,4}:){0,5}[\da-f]{1,4})?::[\da-f]{1,4}|(?:(?:[\da-f]{1,4}:){0,6}[\da-f]{1,4})?::)|v[0-9a-f]+\.[-a-z0-9._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)|(?:[-a-z0-9._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[-a-z0-9._~!$&'()*+,;=@]|%[0-9a-f]{2})+(?:\/(?:[-a-z0-9._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?)(?:\?(?:[-a-z0-9._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[-a-z0-9._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; function IsUriReference(value2) { return UriReference.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri_template.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/uri_template.js var UriTemplate = /^(?:(?:[^\x00-\x20"<>%\\^`{|}\x7f]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\.(?:[a-z0-9_]|%[0-9a-f]{2})+)*(?::[1-9]\d{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?:\.(?:[a-z0-9_]|%[0-9a-f]{2})+)*(?::[1-9]\d{0,3}|\*)?)*\})*$/i; function IsUriTemplate(value2) { return UriTemplate.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/url.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/url.js function IsUrl(value2) { return URL.canParse(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/typebox/uuid.js +// node_modules/@earendil-works/pi-schema/dist/formats/typebox/uuid.js var Uuid = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i; function IsUuid(value2) { return Uuid.test(value2); } -// host/js/node_modules/@earendil-works/pi-schema/dist/formats/index.js +// node_modules/@earendil-works/pi-schema/dist/formats/index.js var formats = Object.freeze({ date: IsDate, "date-time": IsDateTime, @@ -24332,7 +24332,7 @@ var formats = Object.freeze({ uuid: IsUuid }); -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/validation.js +// node_modules/@earendil-works/pi-ai/dist/utils/validation.js var validatorCache = /* @__PURE__ */ new WeakMap(); function getValidator(schema) { const cached = validatorCache.get(schema); @@ -24372,7 +24372,7 @@ ${JSON.stringify(toolCall.arguments, null, 2)}`; throw new Error(errorMessage2); } -// host/js/node_modules/@earendil-works/pi-durable/dist/truncate.js +// node_modules/@earendil-works/pi-durable/dist/truncate.js var DEFAULT_MAX_LINES = 2e3; var DEFAULT_MAX_BYTES = 50 * 1024; function splitLinesForCounting(content) { @@ -24461,7 +24461,7 @@ function truncateHeadOf(prefix, totals, options = {}) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/usage.js +// node_modules/@earendil-works/pi-durable/dist/harness/usage.js var UsageDoc = defineDoc({ kind: "pi.usage", version: 1, @@ -24511,7 +24511,7 @@ function addUsageState(sum, state) { } } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/tool.js +// node_modules/@earendil-works/pi-durable/dist/harness/tool.js var MADE_NESTED_CALLS = "pi.tool.madeNestedCalls"; var NestedCallDoc = defineDocFamily({ kind: "pi.tool.nested-call", @@ -25190,7 +25190,7 @@ function jsonEqual2(a, b) { return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && jsonEqual2(a[key], b[key])); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/generation.js +// node_modules/@earendil-works/pi-durable/dist/harness/generation.js var DEFAULT_POLL_AFTER_MS = 5e3; var GenerationTask = defineTask({ name: "pi.generation", @@ -25953,7 +25953,7 @@ function handOver(live, from, to) { live.run.taskId = to; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/live.js +// node_modules/@earendil-works/pi-durable/dist/harness/live.js var LiveDoc = defineDoc({ kind: "pi.live", version: 3, @@ -26131,7 +26131,7 @@ async function settleSchedulerOutcome(tx, record2, outcome) { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/submissions.js +// node_modules/@earendil-works/pi-durable/dist/harness/submissions.js var Submissions = class { #telemetry; #session; @@ -26356,7 +26356,7 @@ async function admitSubmission(tx, conversationId, draft, now, queueModes, span) return { id, status: "placed" }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/compaction.js +// node_modules/@earendil-works/pi-durable/dist/harness/compaction.js var TOOL_RESULT_MAX_CHARS = 2e3; var SUMMARY_PREFIX = "The conversation history before this point was compacted into the following summary:\n\n\n"; var SUMMARY_SUFFIX = "\n"; @@ -26716,7 +26716,7 @@ async function failNoModel2(runtime, ref, context) { }, context); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/define.js +// node_modules/@earendil-works/pi-durable/dist/harness/define.js function defineExtension(extension) { return extension; } @@ -26739,7 +26739,7 @@ function wrapSection(key, wrapper) { return { section: key, wrap: wrapper }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/registry.js +// node_modules/@earendil-works/pi-durable/dist/harness/registry.js var SECTION_KEY = /^[a-z][a-z0-9_-]*$/; var BUILTIN_TASKS = [GenerationTask, ToolTask, CompactionTask]; var RegistryState = class { @@ -26833,7 +26833,7 @@ function createRegistry() { return new RegistryImpl(); } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/task-graph.js +// node_modules/@earendil-works/pi-durable/dist/harness/task-graph.js var LIVE_STATUSES2 = ["pending", "running", "waiting", "sleeping", "completing"]; var SCAN_PAGE_SIZE4 = 256; var TaskGraphView = class { @@ -27029,7 +27029,7 @@ function phaseOf(checkpoint) { return checkpoint.phase; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/view.js +// node_modules/@earendil-works/pi-durable/dist/harness/view.js var DEFAULT_WINDOW_MIN = 50; var MOUNTED = [ AgentDoc, @@ -27254,7 +27254,7 @@ function prefixed(op2, prefix) { } } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/harness.js +// node_modules/@earendil-works/pi-durable/dist/harness/harness.js var SCAN_PAGE_SIZE6 = 256; var SCHEDULER_INDEX_SIZES = /* @__PURE__ */ Symbol.for("pi-durable.schedulerIndexSizes"); var SCHEDULER_IDLE_STATE = /* @__PURE__ */ Symbol.for("pi-durable.schedulerIdleState"); @@ -27634,7 +27634,7 @@ var Harness = { } }; -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/registry-reader.js +// node_modules/@earendil-works/pi-durable/dist/harness/registry-reader.js function registryReader(value2, context) { return { snapshot: () => value2.value, @@ -27652,7 +27652,7 @@ function registryReader(value2, context) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/harness/transcript.js +// node_modules/@earendil-works/pi-durable/dist/harness/transcript.js var EARLIER = Object.freeze({ kind: "earlier" }); var ConversationTranscript = class { #load; @@ -27884,7 +27884,7 @@ function mergeById(left, right) { return merged; } -// host/js/node_modules/@earendil-works/pi-durable/dist/storage/scan.js +// node_modules/@earendil-works/pi-durable/dist/storage/scan.js function scanStart(requested, cursor, fallback) { if (requested !== void 0 && requested !== "ascending" && requested !== "descending") { throw new StorageRequestError(`Invalid scan order: ${String(requested)}`); @@ -27907,7 +27907,7 @@ function nextCursor(id, order) { return { after: id, order }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/storage/memory.js +// node_modules/@earendil-works/pi-durable/dist/storage/memory.js function* documentDeltaBatches(id, version, revisions, start2) { for (let index = start2; index < revisions.length; index++) { const revision = revisions[index]; @@ -28631,7 +28631,7 @@ var MemoryStorage = class { } }; -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/index.js +// node_modules/@earendil-works/pi-durable/dist/tools/index.js var tools_exports = {}; __export(tools_exports, { CodingTools: () => CodingTools, @@ -28644,14 +28644,14 @@ __export(tools_exports, { createWriteTool: () => createWriteTool }); -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/env.js +// node_modules/@earendil-works/pi-durable/dist/tools/env.js function requireEnv(api) { if (api.env === void 0) throw new Error("No execution environment is configured"); return api.env; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/bash.js +// node_modules/@earendil-works/pi-durable/dist/tools/bash.js var MAX_TIMEOUT_SECONDS = 2147483647 / 1e3; var bashSchema = obj({ command: str({ description: "Bash command to execute" }), @@ -28768,7 +28768,7 @@ ${execution.command}`; }); } -// host/js/node_modules/diff/libesm/diff/base.js +// node_modules/diff/libesm/diff/base.js var Diff = class { diff(oldStr, newStr, options = {}) { let callback; @@ -28970,7 +28970,7 @@ var Diff = class { } }; -// host/js/node_modules/diff/libesm/diff/line.js +// node_modules/diff/libesm/diff/line.js var LineDiff = class extends Diff { constructor() { super(...arguments); @@ -29018,7 +29018,7 @@ function tokenize(value2, options) { return retLines; } -// host/js/node_modules/diff/libesm/patch/create.js +// node_modules/diff/libesm/patch/create.js var INCLUDE_HEADERS = { includeIndex: true, includeUnderline: true, @@ -29203,7 +29203,7 @@ function splitLines(text2) { return result; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/edit-diff.js +// node_modules/@earendil-works/pi-durable/dist/tools/edit-diff.js function detectLineEnding(content) { const crlfIdx = content.indexOf("\r\n"); const lfIdx = content.indexOf("\n"); @@ -29512,7 +29512,7 @@ function generateDiffString(oldContent, newContent, contextLines = 4) { return { diff: output.join("\n"), firstChangedLine }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/env/decode.js +// node_modules/@earendil-works/pi-durable/dist/env/decode.js function rangeDecoder() { return new TextDecoder("utf-8", { ignoreBOM: true }); } @@ -29520,14 +29520,14 @@ function startsWithBom(firstBytes) { return firstBytes[0] === 239 && firstBytes[1] === 187 && firstBytes[2] === 191; } -// host/js/node_modules/@earendil-works/pi-durable/dist/env/index.js +// node_modules/@earendil-works/pi-durable/dist/env/index.js function getOrThrow(result) { if (!result.ok) throw result.error; return result.value; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/file-mutation-queue.js +// node_modules/@earendil-works/pi-durable/dist/tools/file-mutation-queue.js var queues = /* @__PURE__ */ new Map(); async function mutationKey(env, path, context) { const absolutePath = getOrThrow(await env.absolutePath(path, context)); @@ -29567,7 +29567,7 @@ async function withFileMutationQueue(env, path, fn, context) { } } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/path-utils.js +// node_modules/@earendil-works/pi-durable/dist/tools/path-utils.js var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g; var NARROW_NO_BREAK_SPACE = "\u202F"; function normalizeToolPath(path) { @@ -29593,7 +29593,7 @@ async function resolveReadToolPath(env, path, context) { return resolved; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/edit.js +// node_modules/@earendil-works/pi-durable/dist/tools/edit.js var replaceEditSchema = obj({ oldText: str({ description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call." @@ -29697,7 +29697,7 @@ function createEditTool() { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/image.js +// node_modules/@earendil-works/pi-durable/dist/tools/image.js var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10]; var HEADER_BYTES = 32; var BLOCK_BYTES = 64 * 1024; @@ -29857,7 +29857,7 @@ function startsWithAscii(buffer, offset, text2) { return true; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/image-processor.js +// node_modules/@earendil-works/pi-durable/dist/tools/image-processor.js var DEFAULT_IMAGE_LIMITS = { maxWidth: 2e3, maxHeight: 2e3, maxBytes: 4.5 * 1024 * 1024 }; var INLINE_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]); function toBase64(bytes2) { @@ -29871,7 +29871,7 @@ function base64Length(byteLength) { return Math.ceil(byteLength / 3) * 4; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/read.js +// node_modules/@earendil-works/pi-durable/dist/tools/read.js var readSchema = obj({ path: str({ description: "Path to the file to read (relative or absolute)" }), offset: opt(num({ description: "Line number to start reading from (1-indexed)" })), @@ -30067,7 +30067,7 @@ async function readText(reader, path, offset, limit, context) { }; } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/write.js +// node_modules/@earendil-works/pi-durable/dist/tools/write.js var writeSchema = obj({ path: str({ description: "Path to the file to write (relative or absolute)" }), content: str({ description: "Content to write to the file" }) @@ -30093,7 +30093,7 @@ function createWriteTool() { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/tools/index.js +// node_modules/@earendil-works/pi-durable/dist/tools/index.js function createCodingTools(options = {}) { return defineExtension({ name: "coding-tools", @@ -30102,7 +30102,7 @@ function createCodingTools(options = {}) { } var CodingTools = createCodingTools(); -// host/js/node_modules/@earendil-works/pi-ai/dist/index.js +// node_modules/@earendil-works/pi-ai/dist/index.js var dist_exports4 = {}; __export(dist_exports4, { AI_TELEMETRY_SCHEMA: () => AI_TELEMETRY_SCHEMA, @@ -30185,7 +30185,7 @@ __export(dist_exports4, { withoutInitialSystemMessage: () => withoutInitialSystemMessage }); -// host/js/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +// node_modules/@earendil-works/pi-ai/dist/auth/helpers.js function envApiKeyAuth(name, envVars) { return { name, @@ -30226,7 +30226,7 @@ function lazyOAuth(input) { }; } -// host/js/node_modules/@earendil-works/pi-ai/dist/session-resources.js +// node_modules/@earendil-works/pi-ai/dist/session-resources.js var sessionResourceCleanups = /* @__PURE__ */ new Set(); function registerSessionResourceCleanup(cleanup) { sessionResourceCleanups.add(cleanup); @@ -30248,7 +30248,7 @@ function cleanupSessionResources(sessionId) { } } -// host/js/node_modules/@earendil-works/pi-ai/dist/utils/string-enum.js +// node_modules/@earendil-works/pi-ai/dist/utils/string-enum.js function StringEnum(values, options) { return { type: "string", @@ -30258,7 +30258,7 @@ function StringEnum(values, options) { }; } -// host/js/events.ts +// events.ts var events_exports = {}; __export(events_exports, { EVENT_FEED: () => EVENT_FEED, @@ -30428,7 +30428,7 @@ function eventLog(harness2) { } } -// host/js/extensions.ts +// extensions.ts var ops = Deno.core.ops; var encoder4 = new TextEncoder(); var decoder4 = new TextDecoder(); @@ -30691,7 +30691,7 @@ Last good: ${fallback}` : failed; } }; } -// host/js/environment.ts +// environment.ts var Started = defineDoc({ kind: "board.started", version: 1, @@ -30719,7 +30719,7 @@ function conversationChains(harness2, context) { } var ops2 = Deno.core.ops; -// host/js/node_modules/@earendil-works/chord/dist/sqlite/schema.js +// node_modules/@earendil-works/chord/dist/sqlite/schema.js function validateSqliteSchemaPrefix(prefix) { if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) { throw new Error(`Invalid SQLite schema prefix ${JSON.stringify(prefix)}`); @@ -30729,7 +30729,7 @@ function validateSqliteSchemaPrefix(prefix) { } } -// host/js/node_modules/@earendil-works/chord/dist/sqlite/migrations.js +// node_modules/@earendil-works/chord/dist/sqlite/migrations.js async function applySqliteMigrations(database, migrations, options) { const { table } = options; validateSqliteSchemaPrefix(table); @@ -30761,7 +30761,7 @@ async function applySqliteMigrations(database, migrations, options) { }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/storage/sqlite/schema.js +// node_modules/@earendil-works/pi-durable/dist/storage/sqlite/schema.js var TABLE_NAMES = { durableSchema: "durable_schema", durableMetadata: "durable_metadata", @@ -30805,7 +30805,7 @@ function createSqliteSchema(prefix) { } var DEFAULT_SQLITE_SCHEMA = createSqliteSchema(); -// host/js/node_modules/@earendil-works/pi-durable/dist/storage/sqlite/migrations.js +// node_modules/@earendil-works/pi-durable/dist/storage/sqlite/migrations.js function createSqliteMigrations(schema) { const { tables, indexes } = schema; const initialSchema = [ @@ -30917,7 +30917,7 @@ async function applySqliteMigrationsForSchema(database, schema, migrations = cre await applySqliteMigrations(database, migrations, { table: schema.tables.durableSchema }); } -// host/js/node_modules/@earendil-works/pi-durable/dist/storage/sqlite/storage.js +// node_modules/@earendil-works/pi-durable/dist/storage/sqlite/storage.js var parseJson = (value2) => JSON.parse(value2); var encodeJson = (value2) => JSON.stringify(value2); var encodeIndexedString = (value2) => JSON.stringify(value2); @@ -31618,7 +31618,7 @@ var SqliteStorage = class _SqliteStorage { } }; -// host/js/sqlite.ts +// sqlite.ts var op = (kind, sql = "", params = []) => Deno.core.ops.op_sql(kind, sql, params.map((p) => p instanceof Uint8Array ? Array.from(p) : typeof p === "bigint" ? Number(p) : p)); async function openStorage(path) { op("open", path); @@ -31659,7 +31659,7 @@ async function openStorage(path) { return SqliteStorage.open(database); } -// host/js/subagents-test.ts +// subagents-test.ts var assert = (value2, message) => { if (!value2) throw new Error(message); }; @@ -31799,6 +31799,28 @@ try { assert(!(await harness.snapshot(LiveDoc, third.conversationId, BACKGROUND_CONTEXT))?.run, "Stop must end child"); assert((await reports()).length === 2, "Stop must suppress aborted report"); assert(text(await call({ action: "start", agent: "scout", task: "no", isolate: "worktree" })).includes("requires the workspaces extension"), "Worktree seam"); + await fs.write("/src/sausage/agents/worker.md", "---\nname: worker\ndescription: Worker\nfamily: fast\ntools: []\n---\nWorker body"); + await fs.mkdir("/src/sausage/.sausage"); + await fs.mkdir("/src/sausage/.sausage/agents"); + await fs.write("/src/sausage/.sausage/agents/worker.md", "---\nname: worker\naddTools: [hold]\n---\nOverlay body"); + await fs.write("/src/sausage/agents/models.json", JSON.stringify({ default: { model: "faux/faux-1" }, fast: { model: "faux/faux-1", thinking: "low" } })); + await call({ action: "start", agent: "worker", task: "family one" }); + const familyChild = (await children()).at(-1); + const firstAgent = await (await harness.conversation(familyChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT); + assert(firstAgent.model?.modelId === "faux-1" && firstAgent.thinkingLevel === "low", "Worker must resolve fast family"); + assert(firstAgent.tools.map((tool) => tool.name).join(",") === "hold", "Project overlay must add a tool"); + assert(firstAgent.instructions.includes("Worker body") && firstAgent.instructions.includes("Overlay body"), "Child must use overlaid instructions"); + const provider2 = models.getProvider("faux"), template = models.getModels("faux")[0]; + models.setProvider({ ...provider2, getModels: () => [template, { ...template, id: "second" }] }); + await fs.write("/src/sausage/agents/models.json", JSON.stringify({ default: { model: "faux/faux-1" }, fast: { model: "faux/second", thinking: "high" } })); + await call({ action: "start", agent: "worker", task: "family two" }); + const secondFamilyChild = (await children()).at(-1); + const secondAgent = await (await harness.conversation(secondFamilyChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT); + assert(secondAgent.model?.modelId === "second" && secondAgent.thinkingLevel === "high", "Next child must use edited family without restart"); + globalThis.childPin = { provider: "faux", modelId: "faux-1" }; + await call({ action: "start", agent: "worker", task: "pin" }); + const pinnedChild = (await children()).at(-1); + assert((await (await harness.conversation(pinnedChild.conversationId, BACKGROUND_CONTEXT)).agent(BACKGROUND_CONTEXT)).model?.modelId === "faux-1", "models.pin must apply to subagents"); } } finally { gate.resolve(); diff --git a/host/js/subagents-test.ts b/host/js/subagents-test.ts index 90e4659..7cff4c2 100644 --- a/host/js/subagents-test.ts +++ b/host/js/subagents-test.ts @@ -106,5 +106,26 @@ try { assert(!(await harness.snapshot(LiveDoc,third.conversationId,context))?.run,'Stop must end child'); assert((await reports()).length===2,'Stop must suppress aborted report'); assert(text(await call({action:'start',agent:'scout',task:'no',isolate:'worktree'})).includes('requires the workspaces extension'),'Worktree seam'); + await fs.write('/src/sausage/agents/worker.md','---\nname: worker\ndescription: Worker\nfamily: fast\ntools: []\n---\nWorker body'); + await fs.mkdir('/src/sausage/.sausage');await fs.mkdir('/src/sausage/.sausage/agents'); + await fs.write('/src/sausage/.sausage/agents/worker.md','---\nname: worker\naddTools: [hold]\n---\nOverlay body'); + await fs.write('/src/sausage/agents/models.json',JSON.stringify({default:{model:'faux/faux-1'},fast:{model:'faux/faux-1',thinking:'low'}})); + await call({action:'start',agent:'worker',task:'family one'}); + const familyChild=(await children()).at(-1)!; + const firstAgent=await (await harness.conversation(familyChild.conversationId,context))!.agent(context); + assert(firstAgent.model?.modelId==='faux-1'&&firstAgent.thinkingLevel==='low','Worker must resolve fast family'); + assert(firstAgent.tools.map((tool:any)=>tool.name).join(',')==='hold','Project overlay must add a tool'); + assert(firstAgent.instructions.includes('Worker body')&&firstAgent.instructions.includes('Overlay body'),'Child must use overlaid instructions'); + const provider=models.getProvider('faux')!, template=models.getModels('faux')[0]; + models.setProvider({...provider,getModels:()=>[template,{...template,id:'second'}]}); + await fs.write('/src/sausage/agents/models.json',JSON.stringify({default:{model:'faux/faux-1'},fast:{model:'faux/second',thinking:'high'}})); + await call({action:'start',agent:'worker',task:'family two'}); + const secondFamilyChild=(await children()).at(-1)!; + const secondAgent=await (await harness.conversation(secondFamilyChild.conversationId,context))!.agent(context); + assert(secondAgent.model?.modelId==='second'&&secondAgent.thinkingLevel==='high','Next child must use edited family without restart'); + globalThis.childPin={provider:'faux',modelId:'faux-1'}; + await call({action:'start',agent:'worker',task:'pin'}); + const pinnedChild=(await children()).at(-1)!; + assert((await (await harness.conversation(pinnedChild.conversationId,context))!.agent(context)).model?.modelId==='faux-1','models.pin must apply to subagents'); } } finally {gate.resolve();parentGate.resolve();await loader.close();await harness.close(context);} diff --git a/host/tests/subagents.rs b/host/tests/subagents.rs index feaf363..12a24f9 100644 --- a/host/tests/subagents.rs +++ b/host/tests/subagents.rs @@ -40,22 +40,31 @@ fn prepare(dir: &Path) -> anyhow::Result<()> { target.join("wasm32-wasip2/release/git.wasm"), disk.join("bin/git.wasm"), )?; - for directory in ["extensions/subagents", "extensions/core", "agents"] { + for directory in [ + "extensions/subagents", + "extensions/models", + "extensions/core", + "agents", + ] { std::fs::create_dir_all(repo.join(directory))?; } - for entry in std::fs::read_dir(source.join("extensions/subagents"))? { - let entry = entry?; - if entry.path().extension().is_some_and(|ext| ext == "ts") { - std::fs::copy( - entry.path(), - repo.join("extensions/subagents").join(entry.file_name()), - )?; + for extension in ["subagents", "models"] { + for entry in std::fs::read_dir(source.join("extensions").join(extension))? { + let entry = entry?; + if entry.path().extension().is_some_and(|ext| ext == "ts") { + std::fs::copy( + entry.path(), + repo.join("extensions") + .join(extension) + .join(entry.file_name()), + )?; + } } } std::fs::create_dir_all(repo.join("extensions/probe"))?; std::fs::write( repo.join("extensions/probe/server.ts"), - "export default host => { globalThis.subagentService = host.need({name:'subagents'}); return {name:'probe'}; }", + "export default host => { globalThis.subagentService = host.need({name:'subagents'}); host.implement({name:'models.pin'}, {pin:async()=>globalThis.childPin}); return {name:'probe'}; }", )?; std::fs::copy( source.join("extensions/core/agents.ts"),