@dxos/agent-runtime 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-AgentService.mjs","names":[],"sources":["../../src/agent-service/mcp-servers.ts","../../src/agent-service/turn-producer.ts","../../src/agent-service/agent-process.ts","../../src/agent-service/AgentService.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport * as Credential from '@dxos/compute/Credential';\nimport * as McpServer from '@dxos/compute/McpServer';\nimport { Database, Filter, type Ref } from '@dxos/echo';\nimport { type AccessToken } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport { McpOAuth, type McpToolkit } from '@dxos/mcp-client';\n\n/**\n * The space's enabled MCP servers as connection options, read at the start of every turn so a server\n * added, toggled or signed in to from the chat's options applies from the next turn on.\n */\nexport const loadSpaceMcpServers = (): Effect.Effect<\n McpToolkit.Options[],\n never,\n Database.Service | Credential.CredentialsService\n> =>\n Effect.gen(function* () {\n const servers = yield* Database.query(Filter.type(McpServer.McpServer)).run;\n return yield* Effect.forEach(\n servers.filter((server) => server.enabled !== false),\n toOptions,\n );\n });\n\nconst toOptions = (\n server: McpServer.McpServer,\n): Effect.Effect<McpToolkit.Options, never, Database.Service | Credential.CredentialsService> =>\n Effect.gen(function* () {\n const options: McpToolkit.Options = { url: server.url, protocol: server.protocol, apiKey: server.apiKey };\n if (server.oauth?.tokens) {\n // No `onAuthorize`: the agent uses and refreshes the stored tokens but cannot start a sign-in.\n return { ...options, authProvider: McpOAuth.makeProvider({ store: McpServer.oauthStore(server) }) };\n }\n if (server.accessToken) {\n const apiKey = yield* resolveAccessToken(server.accessToken);\n return Option.isSome(apiKey) ? { ...options, apiKey: apiKey.value } : options;\n }\n return options;\n });\n\n/**\n * Resolves through {@link Credential.CredentialsService} rather than reading `token` directly, so a\n * token EDGE custodies (a managed one) resolves the same way a pasted one does.\n */\nconst resolveAccessToken = (ref: Ref.Ref<AccessToken.AccessToken>) =>\n Effect.gen(function* () {\n const accessToken = yield* Database.load(ref);\n const credentials = yield* Credential.CredentialsService;\n const credential = yield* Effect.tryPromise(() => credentials.getCredential({ accessTokenId: accessToken.id }));\n return Option.fromNullishOr(credential.apiKey);\n }).pipe(\n // The connection then goes out without a key, and the server's 401 reports what is wrong.\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to resolve MCP server credential', { error }))),\n Effect.orElseSucceed(() => Option.none<string>()),\n );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as Scope from 'effect/Scope';\n\nimport { type AiRequest, AiSession, type Chat } from '@dxos/assistant';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport type * as Skill from '@dxos/compute/Skill';\nimport type { Database, Feed } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport type { Message } from '@dxos/types';\n\n/**\n * Produces one turn of a conversation.\n *\n * The agent process owns everything around a turn — the input queue, alarms, tool-call redelivery,\n * delegation, skill hooks, tracing, hydration — and none of that is specific to how the turn is\n * generated. This is the one thing that is, so an alternative engine (e.g. a Claude Agent SDK host)\n * can be substituted without touching the process.\n *\n * A producer MUST append each message it produces to the conversation's feed as it goes: the chat\n * thread renders from a reactive query over that feed, so a turn that is only returned never\n * appears.\n */\nexport interface TurnProducer {\n /**\n * Runs a turn, appending messages to the feed and returning what it produced.\n *\n * Must be interruptible — the process wraps the call and cancels it on interrupt.\n */\n runTurn(params: TurnRequest): Effect.Effect<Message.Message[], AiRequest.RunError, AiRequest.RunRequirements>;\n\n /** Skills bound to this conversation, which the process fires end-request hooks against. */\n getSkills(): Skill.Skill[];\n}\n\nexport type TurnRequest = {\n prompt: Parameters<AiSession.Session['createRequest']>[0]['prompt'];\n /** Who the queued prompt is from; carried onto the turn's user message. */\n sender?: Parameters<AiSession.Session['createRequest']>[0]['sender'];\n system?: string;\n mcpServers?: Parameters<AiSession.Session['createRequest']>[0]['mcpServers'];\n};\n\nexport type MakeTurnProducerOptions = {\n /** The conversation the process serves; its session config says which engine runs it and how. */\n chat: Chat.Chat;\n feed: Feed.Feed;\n runtime: Context.Context<Database.Service>;\n instructions: Instructions.Instructions[];\n};\n\n/**\n * Builds the producer for a conversation as a scoped effect — teardown registers with the scope\n * (`Effect.acquireRelease`/`addFinalizer`), so the process's scope owns the producer's lifetime.\n * The default drives DXOS's own `AiSession`.\n */\nexport type MakeTurnProducer = (options: MakeTurnProducerOptions) => Effect.Effect<TurnProducer, never, Scope.Scope>;\n\n/**\n * The default producer: `AiSession.Session`, which resolves the model, binds skills and context\n * objects as tools, and appends each message to the feed via its `onOutput` hook.\n */\nexport const makeAiSessionTurnProducer: MakeTurnProducer = ({ feed, runtime, instructions }) =>\n EffectEx.acquireReleaseResource(() => new AiSession.Session({ feed, runtime, instructions })).pipe(\n Effect.map((session) => ({\n runTurn: (params: TurnRequest) => session.createRequest(params),\n getSkills: () => session.context.getSkills(),\n })),\n );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Tool from 'effect/ai/Tool';\nimport * as Toolkit from 'effect/ai/Toolkit';\nimport * as Cause from 'effect/Cause';\nimport * as Clock from 'effect/Clock';\nimport * as DateTime from 'effect/DateTime';\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport { AiService, Model, OpaqueToolkit } from '@dxos/ai';\nimport {\n AiContext,\n Alarm,\n HarnessControl,\n type HarnessControlRpcs,\n type PendingState,\n SessionStore,\n SkillHooks,\n getOperationFromTool,\n makeToolExecutionService,\n makeToolResolverFromOperations,\n} from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport { OperationProcess } from '@dxos/compute-runtime';\nimport * as Credential from '@dxos/compute/Credential';\nimport * as McpServer from '@dxos/compute/McpServer';\nimport * as Operation from '@dxos/compute/Operation';\nimport * as OperationHandlerSet from '@dxos/compute/OperationHandlerSet';\nimport * as Process from '@dxos/compute/Process';\nimport * as Skill from '@dxos/compute/Skill';\nimport * as StorageService from '@dxos/compute/StorageService';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Annotation, Database, Feed, Obj, Ref, Registry } from '@dxos/echo';\nimport { DXN } from '@dxos/keys';\nimport { AccessToken } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport { Actor, ContentBlock, Message } from '@dxos/types';\nimport { markWork, trim } from '@dxos/util';\n\nimport { type DelegationStrategy } from './delegation-strategy.ts';\nimport { loadSpaceMcpServers } from './mcp-servers.ts';\nimport { type MakeTurnProducer, makeAiSessionTurnProducer } from './turn-producer.ts';\n\nexport interface AgentProcessOptions {\n // TODO(burdon): Instructions?\n systemPrompt?: string;\n\n /**\n * Produces each turn. Defaults to {@link makeAiSessionTurnProducer}; substituting it swaps the\n * engine (e.g. a Claude Agent SDK host) while leaving the queue, alarms, redelivery, delegation\n * and hydration around it untouched.\n */\n makeTurnProducer?: MakeTurnProducer;\n\n /** Model used when the chat the process is bound to has not selected one. */\n defaultModel?: DXN.DXN;\n\n /**\n * Stay resident (IDLE) once the work is done rather than succeeding, so the next prompt lands on this\n * process instead of a new one. For a host where a spawn is expensive — on EDGE it is a fresh Durable\n * Object that opens the space and loads the toolkit before the turn can start.\n * @default false\n */\n resident?: boolean;\n\n /**\n * The catalog's shared model ids are served by several providers, so resolution needs the provider\n * alongside the id; without it a local model id cannot be claimed by any resolver.\n */\n provider?: DXN.DXN;\n\n /**\n * If true, long-running tool calls are moved to the background after `backgroundThreshold`\n * and the agent is notified asynchronously when they complete.\n *\n * Currently unstable — disabled by default.\n * @default false\n */\n enableToolBackgrounding?: boolean;\n\n /**\n * When provided, the agent acts as a supervisor: after each turn it delegates outstanding work to\n * linked child processes and folds their results back into the conversation on completion. Absent\n * (the default) the process behaves as a plain conversational agent.\n */\n delegationStrategy?: DelegationStrategy;\n}\n\nexport const AGENT_PROCESS_KEY = 'org.dxos.testing.process.agent';\n\nconst AgentPrompt = Schema.Union([Schema.String, Schema.Array(ContentBlock.Any)]);\n\n/**\n * Who sent a prompt: the plain-data subset of `Actor`, since the input crosses a JSON boundary\n * (EDGE decodes it with the schema's type side) where a `Ref` cannot be supplied.\n */\nconst AgentInputSender = Actor.Actor.mapFields(Struct.pick(['role', 'name', 'identityDid', 'email']));\n\n/**\n * Input accepted by {@link AgentProcess}: a bare prompt, or a prompt attributed to a sender (e.g. a\n * relayed Discord author) whose identity the appended `Message` records.\n */\nexport const AgentInput = Schema.Union([\n AgentPrompt,\n Schema.Struct({\n prompt: AgentPrompt,\n sender: Schema.optional(AgentInputSender),\n /** Foreign identity and other source metadata copied onto the message (e.g. `{ discord: { userId } }`). */\n properties: Schema.optional(Schema.Record(Schema.String, Schema.Any)),\n }),\n]);\n\nexport type AgentInput = Schema.Schema.Type<typeof AgentInput>;\n\n/** Builds the feed message for an input; the sender defaults to the plain user role. */\nexport const makeInputMessage = (input: AgentInput): Message.Message => {\n const { prompt, sender, properties } =\n typeof input === 'object' && 'prompt' in input\n ? input\n : { prompt: input, sender: undefined, properties: undefined };\n const blocks = typeof prompt === 'string' ? [ContentBlock.Text.make({ text: prompt })] : [...prompt];\n return Message.make({ sender: { role: 'user', ...sender }, blocks, properties });\n};\n\n/**\n * A process that can run a chat in place of {@link AgentProcess}: it takes the same input and serves\n * the same `HarnessControl` RPCs, so a session drives it without knowing which one it is. Its\n * requirements are its own; the host resolves them when it spawns the process.\n */\nexport type AgentProcessDefinition = Operation.Durable<AgentInput, void, any, HarnessControlRpcs>;\n\n/**\n * How long to wait before re-reading a queue that contradicts a write this process just made, and\n * how many times. A hosted runtime serves the read from an eventually-consistent index, and the lag\n * measured on EDGE is single-digit milliseconds — the cap is what keeps a write that never lands\n * from waking the agent forever.\n */\nconst UNSEEN_WRITE_RETRY_MS = 250;\n\n/** How much of a sub-agent's result the trace keeps: enough to read, never the whole payload. */\nconst RESULT_PREVIEW_LENGTH = 200;\n\n/**\n * A short, safe rendering of a sub-agent's result for the trace.\n *\n * Guarded because this sits between removing the child from `delegations` and running the strategy's\n * `onComplete`: a `BigInt` or a cycle would throw here, become a defect, and take the whole return\n * path with it — the work item would never be updated and work waiting on it never reconciled.\n * Losing the preview is survivable; losing the return is not.\n */\nconst resultPreview = (value: unknown): string | undefined => {\n let text: string | undefined;\n try {\n text = typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return undefined;\n }\n if (text === undefined) {\n return undefined;\n }\n return text.length > RESULT_PREVIEW_LENGTH ? `${text.slice(0, RESULT_PREVIEW_LENGTH)}…` : text;\n};\n\nconst MAX_UNSEEN_WRITE_WAKES = 20;\n\n/**\n * Hosts a persistent, suspendible AiAgent that can process a number of prompts.\n * The process target is a queue DXN string.\n */\nexport const AgentProcess = (options: AgentProcessOptions) =>\n Operation.makeDurable({\n key: AGENT_PROCESS_KEY,\n input: AgentInput,\n output: Schema.Void,\n // The conversation's own data model. `SessionStore` reads the queue with a TYPED query\n // (`Filter.type(Message)`/`Filter.type(Alarm)`), so without these registered every read comes\n // back empty on a host that did not happen to register them itself: the prompt appends fine and\n // the agent then finds nothing to do. `Chat`/`Feed` are resolved by DXN at startup.\n // `AiContext.Binding` and `Skill` belong here for the same reason the rest do: the host\n // registers exactly these with the process's database, and a typed query for a type it does\n // not know matches nothing. Without them a hosted agent reads its own skill bindings back\n // empty and runs every turn with an EMPTY TOOLKIT — the model can only answer in prose.\n // `McpServer` and `AccessToken` are read each turn to connect the space's MCP servers.\n types: [\n Chat.Chat,\n Feed.Feed,\n Message.Message,\n Alarm.Alarm,\n AiContext.Binding,\n Skill.Skill,\n McpServer.McpServer,\n AccessToken.AccessToken,\n ],\n services: [\n Database.Service,\n OpaqueToolkit.OpaqueToolkitProvider,\n Operation.Service,\n Registry.Service,\n StorageService.StorageService,\n Process.ManagerService,\n OperationHandlerSet.OperationHandlerProvider,\n AiService.AiService,\n // Needed in the fiber's context — `Header.byokLayer`'s per-request callback reads it.\n Credential.CredentialsService,\n ],\n rpcs: HarnessControl,\n }).pipe(\n Operation.withDurableHandler((ctx) =>\n Effect.gen(function* () {\n const chatDxn = Annotation.getDictionary(ctx.params.annotations, Process.TargetAnnotation).pipe(\n Option.getOrUndefined,\n );\n if (chatDxn == null) {\n return yield* Effect.die(new Error('Agent executable requires spawn options.target set to a Chat DXN.'));\n }\n // The process is bound to the chat, not to its feed: the queue, the steering instructions\n // and the checklist are all read from the chat, and a rehydrated process recovers them by\n // re-reading it rather than from spawn annotations.\n const chat = yield* Database.resolve(chatDxn, Chat.Chat).pipe(Effect.orDie);\n const feed = yield* Database.load(chat.feed).pipe(Effect.orDie);\n // A broken instructions ref degrades to an unsteered session rather than failing the process.\n const instructions = chat.instructions\n ? yield* Database.load(chat.instructions).pipe(Effect.orElseSucceed(() => undefined))\n : undefined;\n const runtime = yield* Effect.context<Database.Service>();\n const makeTurnProducer = options.makeTurnProducer ?? makeAiSessionTurnProducer;\n // Scoped acquisition: the producer's teardown registers with this process's scope.\n const session = yield* makeTurnProducer({\n chat,\n feed,\n runtime,\n instructions: instructions ? [instructions] : [],\n });\n const sessionStore = new SessionStore();\n // KV holds only undelivered tool results; queued prompts and alarms live in the feed via\n // `sessionStore`.\n let toolResults: ToolResultEvent[] = [...(yield* ToolResultsCell.get)];\n let ackedEntries: string[] = [...(yield* AckedEntriesCell.get)];\n let selfWakes = yield* SelfWakesCell.get;\n const storageService = yield* StorageService.StorageService;\n const toolCallManager = new ToolCallManager(storageService);\n yield* toolCallManager.load();\n\n // Read time from the ambient Effect Clock so alarm scheduling and the due-check stay\n // consistent (and both honor a TestClock under tests).\n const clock = yield* Clock.Clock;\n const now = () => clock.currentTimeMillisUnsafe();\n\n // Queued tool results were never consumed by onAlarm — reported flags from the synchronous\n // execution path are stale after reload and would cause onAlarm to drop them.\n yield* toolCallManager.reconcileWithInputQueue(toolResults);\n\n // Alarms written by this incarnation, keyed by id, until a read confirms the index has them.\n // `wakes` counts reads that did not show the alarm, which is what distinguishes an index\n // still catching up from one that is gone — a due time cannot: an alarm set for an instant\n // already past reads as \"due\" on the very first look, before the index has had any chance.\n const unseenAlarms = new Map<string, { wakeAt: number; wakes: number }>();\n\n // Schedules the process alarm from durable state: immediately when work is queued, at the\n // earliest pending alarm otherwise, not at all when idle.\n const reconcileAlarmWith = (state: PendingState): Effect.Effect<void> => {\n // An alarm this incarnation wrote leaves the unseen set once the read can see it; until\n // then it is the only record of a wake, so arming from the read alone would arm nothing.\n for (const alarm of state.pendingAlarms) {\n unseenAlarms.delete(alarm.id);\n }\n const observed = state.pendingAlarms[0]?.wakeAt;\n const wakeAts = [\n ...(observed != null ? [observed] : []),\n ...[...unseenAlarms.values()].map(({ wakeAt }) => wakeAt),\n ];\n const delay = computeAlarmDelay({\n hasPendingWork: toolResults.length > 0 || state.pendingMessages.length > 0,\n wakeAt: wakeAts.length > 0 ? Math.min(...wakeAts) : null,\n now: now(),\n });\n return delay != null ? ctx.setAlarm(delay) : Effect.void;\n };\n const reconcileAlarm = Effect.flatMap(sessionStore.loadPending(feed), reconcileAlarmWith);\n\n // Hydration: the work is durable (queued prompts and alarms on the feed, undelivered tool\n // results in KV) but the process alarm is not, and a rehydrated process is not handed its\n // pending input events again — so it must re-arm from that durable state here or sit idle\n // with work waiting. What was already pending is also what `onSpawn` decides the fate of.\n const startupPending = yield* sessionStore.loadPending(feed);\n yield* reconcileAlarmWith(startupPending);\n\n // Optional supervisor behaviour: when a strategy is provided, the agent reconciles\n // outstanding work into linked child processes after each turn and folds their results back\n // into the conversation on completion. Absent (the default), the process behaves as a plain\n // conversational agent.\n const strategy = Option.fromNullishOr(options.delegationStrategy);\n let delegations: Delegation[] = [...(yield* DelegationsCell.get)];\n // Background end-request hooks still running as children; persisted so a rehydrated process\n // still waits for them, and so it knows the request they belong to has already ended.\n let asyncHooks: Process.ID[] = [...(yield* AsyncHooksCell.get)];\n // Exits of children this process did not recognise yet: child events run concurrently with\n // the handler, so a background hook can exit before its pid is registered below.\n const untrackedExits = new Set<Process.ID>();\n\n // The chat's own selection wins: the process is bound to the chat, so the model it runs on is\n // recovered from the chat on rehydration like the instructions are.\n const model = chat.session?.model ?? options.defaultModel;\n const requestModelLayer = AiService.languageModel(DXN.getName(model ?? Model.claudeSonnet5.id), {\n provider: options.provider,\n });\n\n const childServices = yield* Effect.context<\n Process.ManagerService | OperationHandlerSet.OperationHandlerProvider\n >();\n\n // Fire end-request hooks declared by the bound skills (e.g. the planning plan-reminder).\n // Each hook runs as a child operation with `conversation` set, so it resolves the full\n // HarnessService (Tier B) and can enqueue a continuation back onto this host's queue — which\n // keeps the process alive past this turn.\n const runEndRequestHooks = Effect.gen(function* () {\n yield* SkillHooks.runHooks({\n skills: session.getSkills(),\n phase: 'end-request',\n invoke: (operation, input, hook) =>\n Effect.gen(function* () {\n const handle = yield* Process.spawn(OperationProcess.make(operation), input, {\n environment: { conversation: Obj.getURI(feed) },\n traceMeta: { conversation: Ref.make(feed) },\n });\n if (hook.async) {\n if (untrackedExits.delete(handle.pid)) {\n return;\n }\n // Not awaited, so the request settles now; the child is linked, so its exit wakes\n // `onChildEvent`, which completes the process once every background hook is done.\n asyncHooks.push(handle.pid);\n yield* AsyncHooksCell.set(asyncHooks);\n return;\n }\n // A child failure surfaces in the Effect channel, so the outer `Effect.orDie` (and the\n // hook runner's `catchAllCause`) handle it instead of it being silently discarded.\n yield* Process.awaitOutput(handle);\n }).pipe(Effect.provide(childServices), Effect.asVoid, Effect.orDie),\n });\n });\n\n // Whether this incarnation has actually run a turn. An empty queue means two different things:\n // after a turn it means the work drained and the process should finish, but on a fresh spawn\n // it is simply a conversation nobody has spoken to yet — `onSpawn` discards what it inherits,\n // so a new process ALWAYS starts empty. Completing on the latter ends the agent before the\n // prompt that spawned it arrives, and `submitInput` then drops that prompt on a finished\n // handle, leaving the reader with no reply and no error.\n let turnRan = false;\n\n // Whether this request's end-request hooks have fired; reset by every turn, so a request\n // continued by a hook gets its own hooks when it ends. A rehydrated process with background\n // hooks still running has already fired them.\n let endHooksFired = asyncHooks.length > 0;\n\n // Queue entries this incarnation wrote but has not yet read back. A hosted process's queue\n // read is served by the space INDEX, which is eventually consistent: the agent appends a\n // prompt and, milliseconds later, its own read returns empty — the index catches up a beat\n // afterwards, but by then the agent has already concluded there is no work and gone idle,\n // and nothing looks again. Locally the same read is served from the resident feed handle, so\n // this never happens off a hosted runtime. Counted rather than flagged so a burst of prompts\n // is not mistaken for one.\n // Held by id, not counted: a resumed process can dequeue an OLDER entry it did not write,\n // and a bare count would treat that as its own write becoming visible — zeroing the budget\n // while this incarnation's prompt is still unread, so the next empty read completes the\n // process and drops it. In memory because it describes this incarnation's writes only; a\n // later one re-reads the feed from scratch.\n const unseenWriteIds = new Set<string>();\n let unseenWriteWakes = 0;\n\n const pendingWork = (state: PendingState): boolean =>\n isAgentWorkPending({\n toolResults,\n pendingMessages: state.pendingMessages,\n pendingAlarms: state.pendingAlarms,\n delegations,\n toolCallManager,\n });\n\n // A turn that ends with an alarm pending leaves the process resident, and the reader is then\n // waiting on the wake rather than on the turn — so the last stage of that turn is no longer\n // what is happening.\n const reportSleeping = (state: PendingState): Effect.Effect<void, never, Trace.TraceService> =>\n state.pendingAlarms.length > 0 ? Trace.emitRequestPhase('sleeping') : Effect.void;\n\n const maybeCompleteWith = (state: PendingState) =>\n Effect.gen(function* () {\n // A result reported inside the turn it belongs to is still sitting in the queue; it is\n // not outstanding work, and counting it as such keeps the agent from ever completing —\n // the head-drop at the top of `onAlarm` cannot help, because nothing arms another wake.\n for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) {\n log('drop tool result reported within its turn', { pid });\n }\n if (pendingWork(state)) {\n yield* reportSleeping(state);\n return;\n }\n\n if (!turnRan && !endHooksFired) {\n // Idle, not done: stay resident so the prompt this process was spawned for can still\n // land. Ahead of the hooks below, which are end-of-REQUEST hooks — there has been no\n // request to end. Nothing is scheduled; the next `onInput` arms the alarm.\n log('agent idle before its first turn, staying resident');\n return;\n }\n\n if (!endHooksFired) {\n endHooksFired = true;\n // The hook may enqueue work (e.g. a plan continuation reminder) via HarnessService Tier B,\n // which appends to the feed queue; re-check before succeeding so the turn is not dropped.\n yield* runEndRequestHooks;\n const after = yield* sessionStore.loadPending(feed);\n if (pendingWork(after)) {\n log('agent work enqueued by end-request hook, continuing');\n yield* reconcileAlarmWith(after);\n yield* reportSleeping(after);\n return;\n }\n }\n\n if (asyncHooks.length > 0) {\n // Resident rather than done: the linked children keep the process HYBERNATING, which\n // `runUntilSettled` treats as settled, so the reader is not held up by them.\n log('awaiting background end-request hooks', { count: asyncHooks.length });\n return;\n }\n\n if (options.resident) {\n log('agent work complete, staying resident');\n return;\n }\n\n log('agent work complete, succeeding');\n ctx.succeed();\n });\n\n const maybeComplete = Effect.flatMap(sessionStore.loadPending(feed), maybeCompleteWith);\n\n return {\n // Runs on a fresh spawn only — never on a resume, which is what a hibernated process gets.\n // So anything already pending here was left by a process that is gone for good: stopped by\n // the user, or dead without being rehydrated. Redelivering it would re-run a prompt the\n // reader stopped, and the next thing they type would queue behind it.\n onSpawn: Effect.fnUntraced(function* () {\n if (startupPending.pendingMessages.length > 0) {\n log.info('discarding queue entries left by a previous process', {\n count: startupPending.pendingMessages.length,\n });\n yield* Effect.forEach(startupPending.pendingMessages, (message) => sessionStore.ack(feed, message), {\n discard: true,\n });\n }\n }),\n // Control plane (§4.3): handlers run on the host process's server fiber, writing their\n // durable effect to the feed inline.\n rpcHandlers: yield* HarnessControl.toHandlers({\n setAlarm: Effect.fn(function* ({ at, message }) {\n const alarm = yield* sessionStore.setAlarm(feed, {\n wakeAt: DateTime.toEpochMillis(at),\n message: message ?? undefined,\n });\n // Reconciling reads the feed back through the eventually-consistent index, so a wake\n // whose record has not landed yet arms nothing and nothing looks again.\n unseenAlarms.set(alarm.id, { wakeAt: alarm.wakeAt, wakes: 0 });\n yield* reconcileAlarm;\n }),\n enqueueMessage: Effect.fn(function* ({ content }) {\n const message = Message.make({ sender: { role: 'user' }, blocks: [...content] });\n yield* sessionStore.enqueueMessage(feed, message);\n unseenWriteIds.add(message.id);\n yield* ctx.setAlarm(0);\n }),\n }),\n onInput: Effect.fnUntraced(function* (input: AgentInput) {\n log('agent onInput received', { backlog: toolResults.length });\n const message = makeInputMessage(input);\n yield* sessionStore.enqueueMessage(feed, message);\n markWork('agent.prompt-queued');\n unseenWriteIds.add(message.id);\n yield* ctx.setAlarm(0);\n log('agent onInput enqueued to feed');\n }),\n onAlarm: Effect.fnUntraced(\n function* () {\n log('agent onAlarm fired', { backlog: toolResults.length });\n markWork('agent.wake');\n\n // Earliest point the agent can report to a reader who is already waiting: draining the\n // queue below reads the feed, which is itself part of the wait. Only for a wake with known\n // work — the re-check after every turn finds none, and on a hosted agent that read takes\n // long enough for the line to read as another request starting after the reply.\n const announced = toolResults.length > 0 || unseenWriteIds.size > 0;\n if (announced) {\n yield* Trace.emitRequestPhase('preparing');\n }\n\n for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) {\n log.info('skip tool result that was reported synchronously', { pid });\n }\n\n // Undelivered tool results drain first; then the feed queue, then a due alarm.\n const toolResult = toolResults.shift();\n let prompt: ContentBlock.Any[];\n let dequeued: Message.Message | Alarm.Alarm | undefined;\n if (toolResult !== undefined) {\n log('agent onAlarm handling', { tag: toolResult._tag });\n prompt = toolResultPrompt(toolResult);\n } else {\n const state = yield* sessionStore.loadPending(feed);\n markWork('agent.pending-loaded');\n // An id still in the pending set has not caught up yet; one that has left it is\n // durably acked and no longer needs remembering.\n const stillPending = new Set([\n ...state.pendingMessages.map((message) => message.id),\n ...state.pendingAlarms.map((alarm) => alarm.id),\n ]);\n if (ackedEntries.some((id) => !stillPending.has(id))) {\n ackedEntries = ackedEntries.filter((id) => stillPending.has(id));\n yield* AckedEntriesCell.set(ackedEntries);\n }\n const acked = new Set(ackedEntries);\n const message = state.pendingMessages.find((candidate) => !acked.has(candidate.id));\n const dueAlarm = state.pendingAlarms.find((alarm) => alarm.wakeAt <= now() && !acked.has(alarm.id));\n // An alarm this incarnation wrote that enough reads have failed to show is gone —\n // cancelled, or delivered on an earlier wake — and must stop contributing a due\n // time, or reconciling would re-arm for it forever. Bounded by reads rather than by\n // the due time, which would drop an already-due alarm before the index caught up.\n for (const [id, entry] of unseenAlarms) {\n if (state.pendingAlarms.some((alarm) => alarm.id === id)) {\n continue;\n }\n entry.wakes++;\n if (entry.wakes >= MAX_UNSEEN_WRITE_WAKES) {\n unseenAlarms.delete(id);\n }\n }\n if (message !== undefined) {\n log('agent onAlarm handling', { tag: 'message', id: message.id });\n unseenWriteIds.delete(message.id);\n unseenWriteWakes = 0;\n if (isUserPrompt(message) && selfWakes > 0) {\n selfWakes = 0;\n yield* SelfWakesCell.set(selfWakes);\n }\n dequeued = message;\n prompt = [...message.blocks];\n } else if (dueAlarm !== undefined) {\n unseenAlarms.delete(dueAlarm.id);\n if (selfWakes >= Alarm.MAX_SELF_WAKES) {\n // Spent: acked without a turn, so the loop ends here until the user prompts again.\n log.warn('agent self-wake budget spent, dropping alarm', { wakes: selfWakes });\n yield* sessionStore.ack(feed, dueAlarm);\n ackedEntries = [...ackedEntries, dueAlarm.id];\n yield* AckedEntriesCell.set(ackedEntries);\n const after = yield* sessionStore.loadPending(feed);\n yield* reconcileAlarmWith(after);\n yield* maybeCompleteWith(after);\n return;\n }\n selfWakes++;\n yield* SelfWakesCell.set(selfWakes);\n log('agent onAlarm self-wake', { firedAt: dueAlarm.wakeAt, wakes: selfWakes });\n dequeued = dueAlarm;\n prompt = [\n ContentBlock.Text.make({\n text: wakeUpPrompt(dueAlarm.wakeAt, dueAlarm.message ?? null, {\n wake: selfWakes,\n max: Alarm.MAX_SELF_WAKES,\n }),\n disposition: 'synthetic',\n }),\n ];\n } else if (unseenWriteIds.size > 0 && unseenWriteWakes < MAX_UNSEEN_WRITE_WAKES) {\n // Read-your-writes over an eventually-consistent read: this incarnation appended an\n // entry the query has not caught up to, so look again shortly rather than conclude\n // the queue is drained. Bounded, so a write that never materialises degrades to the\n // idle path instead of waking forever.\n unseenWriteWakes++;\n markWork('agent.unseen-write-retry');\n log('agent onAlarm empty queue with an unread write, waking again', {\n unseenWrites: unseenWriteIds.size,\n attempt: unseenWriteWakes,\n });\n yield* ctx.setAlarm(UNSEEN_WRITE_RETRY_MS);\n return;\n } else if (unseenWriteIds.size > 0) {\n // Retry budget spent and a write this incarnation made is still unread. The bound\n // stops the waking, but completing here would drop that prompt for good — there is\n // no guaranteed upper bound on index lag — so the process stays resident instead,\n // to be woken by the next input or alarm.\n log.warn('agent giving up on an unread write, staying resident', {\n unseenWrites: unseenWriteIds.size,\n wakes: unseenWriteWakes,\n });\n yield* reconcileAlarmWith(state);\n return;\n } else {\n log('agent onAlarm empty queue', {});\n yield* reconcileAlarmWith(state);\n yield* maybeCompleteWith(state);\n return;\n }\n }\n\n if (!announced) {\n yield* Trace.emitRequestPhase('preparing');\n }\n\n // The MCP servers are read concurrently with the writes below: neither depends on the\n // other, and each is a round trip to the database that the turn would otherwise wait on in series.\n const [mcpServers] = yield* Effect.all(\n [\n loadSpaceMcpServers(),\n Effect.gen(function* () {\n // The turn appends its own user message built from `prompt`, so the queue entry that\n // supplied it must leave the queue view now or the same content shows in both places\n // until the late ack below.\n if (dequeued !== undefined) {\n yield* sessionStore.markInFlight(feed, dequeued);\n }\n log('begin request', { prompt });\n log('trace agent request begin');\n yield* Trace.write(Trace.AgentRequestBegin, {});\n }),\n ],\n { concurrency: 'unbounded' },\n );\n markWork('agent.turn-begin');\n yield* session\n .runTurn({\n prompt,\n // The turn rewrites the queued message as its own, so the sender has to travel with it.\n sender:\n dequeued !== undefined && Obj.instanceOf(Message.Message, dequeued) ? dequeued.sender : undefined,\n // TODO(dmaretskyi): Polling currently broken, agent relies on completion notifications being delivered.\n // toolkit: AsynchronousExectionToolkit,\n system: options.systemPrompt,\n mcpServers,\n })\n .pipe(\n Effect.onExit((exit) =>\n Effect.gen(function* () {\n yield* Trace.write(Trace.AgentRequestEnd, {\n status: Exit.isSuccess(exit) ? 'success' : Exit.hasInterrupts(exit) ? 'interrupted' : 'error',\n error: Exit.isFailure(exit) ? Cause.pretty(exit.cause) : undefined,\n });\n // The failure ends the process below, skipping the reconcile, so the strategy\n // hears of it here or not at all. A stop the reader asked for is not a failure.\n const onTurnFailed = Option.isSome(strategy) ? strategy.value.onTurnFailed : undefined;\n if (onTurnFailed && Exit.isFailure(exit) && !Cause.hasInterrupts(exit.cause)) {\n yield* onTurnFailed(chat, exit.cause);\n }\n }),\n ),\n );\n log('end request');\n turnRan = true;\n endHooksFired = false;\n yield* ToolResultsCell.set(toolResults);\n\n // Ack only now: the turn is what the queue entry was for, so a process that dies before\n // this point must find the entry still pending and redeliver it.\n if (dequeued !== undefined) {\n yield* sessionStore.ack(feed, dequeued);\n ackedEntries = [...ackedEntries, dequeued.id];\n yield* AckedEntriesCell.set(ackedEntries);\n }\n const after = yield* sessionStore.loadPending(feed);\n\n // Reconcile outstanding work into linked child processes (supervisor behaviour). The\n // children are linked, so their exits wake `onChildEvent` below.\n if (Option.isSome(strategy)) {\n const activeIds = new Set(delegations.map((delegation) => delegation.id));\n const pending = yield* strategy.value.reconcile(chat, activeIds);\n for (const delegation of pending) {\n const pid = yield* delegation.spawn;\n delegations.push({ pid, id: delegation.id });\n log('delegated work', { pid, id: delegation.id });\n yield* Trace.write(Trace.DelegationSpawned, { taskId: delegation.id, pid: String(pid) });\n }\n if (pending.length > 0) {\n yield* DelegationsCell.set(delegations);\n }\n }\n\n // Reconcile so a pending alarm (or remaining queue work) is rescheduled.\n yield* reconcileAlarmWith(after);\n yield* maybeCompleteWith(after);\n },\n Effect.orDie,\n Effect.provide(\n Layer.mergeAll(\n makeToolResolverFromOperations(),\n ToolExecutionService({\n toolCallManager,\n feed,\n enableBackgrounding: options.enableToolBackgrounding ?? false,\n }),\n AsynchronousExectionToolkitLayer,\n requestModelLayer,\n ).pipe(Layer.orDie),\n ),\n ),\n onChildEvent: Effect.fnUntraced(function* (event) {\n log('childEvent', { event });\n if (event._tag === 'exited' && asyncHooks.includes(event.pid)) {\n asyncHooks = asyncHooks.filter((pid) => pid !== event.pid);\n yield* AsyncHooksCell.set(asyncHooks);\n const manager = yield* Process.ManagerService;\n const exit = yield* manager.attach(event.pid).pipe(Effect.flatMap(Process.awaitOutput), Effect.exit);\n // A failed background hook is reported and dropped, like a failed inline hook.\n if (Exit.isFailure(exit)) {\n log.warn('background end-request hook failed', { pid: event.pid });\n }\n yield* maybeComplete;\n return;\n }\n if (event._tag === 'exited') {\n // A delegated sub-agent finished: read its result and hand it to the strategy (which\n // updates the work item and notifies the user). Unlike tool results, this does not\n // re-enter the turn — the supervisor folds it in out of band.\n const delegation = delegations.find((delegation) => delegation.pid === event.pid);\n if (delegation) {\n delegations = delegations.filter((other) => other.pid !== event.pid);\n yield* DelegationsCell.set(delegations);\n const manager = yield* Process.ManagerService;\n const child = yield* manager.attach(event.pid);\n const exit = yield* Process.awaitOutput(child).pipe(Effect.exit);\n // Written beside `DelegationSpawned`, and for the same reason: the return is only\n // observable here. The child's own trace ends with its operation and says nothing\n // about reporting back, so this is what pairs an exit with the task it answers.\n const result = Exit.isSuccess(exit) ? resultPreview(exit.value) : undefined;\n yield* Trace.write(Trace.DelegationCompleted, {\n taskId: delegation.id,\n pid: String(event.pid),\n status: Exit.isSuccess(exit) ? 'success' : 'failure',\n ...(result === undefined ? {} : { result }),\n });\n if (Option.isSome(strategy)) {\n yield* strategy.value.onComplete(chat, delegation.id, exit);\n // Re-reconcile: work that was waiting on this delegation (e.g. a dependent task)\n // spawns now rather than on the next conversational turn — this is what lets a\n // batch of delegated tasks drain without further prompting.\n const activeIds = new Set(delegations.map((delegation) => delegation.id));\n const pending = yield* strategy.value.reconcile(chat, activeIds);\n for (const next of pending) {\n const pid = yield* next.spawn;\n delegations.push({ pid, id: next.id });\n log('delegated work', { pid, id: next.id });\n yield* Trace.write(Trace.DelegationSpawned, { taskId: next.id, pid: String(pid) });\n }\n if (pending.length > 0) {\n yield* DelegationsCell.set(delegations);\n }\n }\n log('delegated work completed', { pid: event.pid, id: delegation.id, success: Exit.isSuccess(exit) });\n yield* maybeComplete;\n } else if (toolCallManager.isToolCall(event.pid)) {\n const manager = yield* Process.ManagerService;\n const attachExit = yield* manager.attach(event.pid).pipe(Effect.exit);\n if (\n Exit.isFailure(attachExit) &&\n isToolResultHandled(toolResults, event.pid, (pid) => toolCallManager.isReported(pid))\n ) {\n // Completed tool children are not rehydrated on reload; this result is already queued or\n // was delivered before the interrupted turn.\n log.verbose('childEvent skipped (process gone, result already handled)', { pid: event.pid });\n return;\n }\n const result = yield* (\n Exit.isFailure(attachExit)\n ? // Unreported and unreachable: answered as an error so the call does not stay pending forever.\n Effect.failCause(attachExit.cause)\n : Process.awaitOutput(attachExit.value)\n ).pipe(\n Effect.exit,\n Effect.map(\n Exit.match({\n onSuccess: (value): ToolResultEvent => ({\n _tag: 'tool_result',\n pid: event.pid,\n result: value,\n isError: false,\n }),\n onFailure: (cause): ToolResultEvent => ({\n _tag: 'tool_result',\n pid: event.pid,\n result: Cause.pretty(cause),\n isError: true,\n }),\n }),\n ),\n );\n toolResults.push(result);\n log('agent onChildEvent persisted tool result', { depth: toolResults.length, childPid: event.pid });\n yield* ToolResultsCell.set(toolResults);\n yield* ctx.setAlarm(0);\n log('agent onChildEvent alarm scheduled', { depth: toolResults.length });\n } else {\n log.verbose('childEvent ignored non-tool call and not a delegation', { pid: event.pid });\n untrackedExits.add(event.pid);\n }\n }\n }),\n };\n }),\n ),\n );\n\ninterface ToolExecutionServiceOptions {\n /**\n * If true, tool calls that exceed `backgroundThreshold` are detached and the agent is told\n * the call is running in the background. If false, the executor always blocks on the call.\n */\n enableBackgrounding: boolean;\n\n /**\n * Threshold after which the tool execution is placed in the background.\n * Ignored when `enableBackgrounding` is false.\n */\n // TODO(dmaretskyi): Tool annotation to never run in background.\n backgroundThreshold?: Duration.Duration;\n\n toolCallManager: ToolCallManager;\n\n feed: Feed.Feed;\n}\n\nconst ToolResultEvent = Schema.TaggedStruct('tool_result', {\n pid: Process.ID,\n result: Schema.Unknown,\n isError: Schema.Boolean,\n});\n/** Exported so the pure queue/prompt helpers below can be exercised without spawning an agent. */\nexport type ToolResultEvent = Schema.Schema.Type<typeof ToolResultEvent>;\n\nconst ToolResultsCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(ToolResultEvent).pipe(Schema.mutable)),\n 'toolResults',\n).pipe(StorageService.withDefault(() => []));\n\n/**\n * Tracks delegated sub-agent child processes (pid -> correlation id) so that, after a hibernation,\n * a delegated child's exit can be matched back to the work it was fulfilling.\n */\nconst Delegation = Schema.Struct({ pid: Process.ID, id: Schema.String }).mapFields(Struct.map(Schema.mutableKey));\ntype Delegation = Schema.Schema.Type<typeof Delegation>;\n\nconst DelegationsCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Delegation).pipe(Schema.mutable)),\n 'delegations',\n).pipe(StorageService.withDefault(() => []));\n\n/** Pids of background ({@link Skill.Hook.async}) end-request hooks the process is waiting on. */\nconst AsyncHooksCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Process.ID).pipe(Schema.mutable)),\n 'asyncHooks',\n).pipe(StorageService.withDefault(() => []));\n\n/**\n * Ids of queue entries this process has ACKED, held until the ack is visible in the feed read.\n *\n * The ack is a feed append and a hosted process reads the feed back through the eventually\n * consistent space index, so `loadPending` keeps returning an entry whose ack has not landed yet —\n * and `loadPending` deliberately includes in-flight entries, so that an interrupted turn is\n * redelivered. Without this the two combine into a redelivery loop: the agent re-runs the same turn\n * on every wake, forever. Durable rather than in-memory because the isolate does not survive\n * between turns, and pruned as soon as the entry leaves the pending set, so it cannot grow\n * unbounded.\n *\n * This does NOT weaken at-least-once delivery: an entry is recorded only after its turn ran, which\n * is the same point the ack is written, so a crash before that still redelivers.\n */\nconst AckedEntriesCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Schema.String).pipe(Schema.mutable)),\n 'ackedEntries',\n).pipe(StorageService.withDefault(() => []));\n\n/** Alarms that have woken the agent since the last user prompt; bounded by {@link Alarm.MAX_SELF_WAKES}. */\nconst SelfWakesCell = StorageService.cell(Schema.fromJsonString(Schema.Number), 'selfWakes').pipe(\n StorageService.withDefault(() => 0),\n);\n\n/** A prompt someone typed, as opposed to one the system wrote (a report, a tool result). */\nconst isUserPrompt = (message: Message.Message): boolean =>\n message.sender.role === 'user' &&\n message.blocks.some((block) => block._tag === 'text' && block.disposition !== 'synthetic');\n\nconst ToolCallState = Schema.Struct({\n activeCalls: Schema.Array(\n Schema.Struct({\n pid: Process.ID,\n // Whether the result was reported to the agent.\n reported: Schema.Boolean,\n }).mapFields(Struct.map(Schema.mutableKey)),\n ).pipe(Schema.mutable),\n});\ninterface ToolCallState extends Schema.Schema.Type<typeof ToolCallState> {}\n\n// Id's of processes who's results were already submitted to the agent.\nconst ToolCallStateCell = StorageService.cell(\n Schema.fromJsonString(ToolCallState.mapFields(Struct.map(Schema.mutableKey))),\n 'toolCallState',\n).pipe(StorageService.withDefault(() => ({ activeCalls: [] })));\n\nclass ToolCallManager {\n #storageService: StorageService.Service;\n #state: ToolCallState = { activeCalls: [] };\n\n constructor(storageService: StorageService.Service) {\n this.#storageService = storageService;\n }\n\n load() {\n return Effect.gen({ self: this }, function* () {\n this.#state = yield* ToolCallStateCell.get;\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n beginCall(pid: Process.ID) {\n return Effect.gen({ self: this }, function* () {\n this.#state.activeCalls.push({ pid, reported: false });\n yield* ToolCallStateCell.set(this.#state);\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n markAsReported(pid: Process.ID) {\n return Effect.gen({ self: this }, function* () {\n const call = this.#state.activeCalls.find((call) => call.pid === pid);\n if (!call) {\n return;\n }\n call.reported = true;\n yield* ToolCallStateCell.set(this.#state);\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n isToolCall(pid: Process.ID): boolean {\n return this.#state.activeCalls.some((call) => call.pid === pid);\n }\n\n isReported(pid: Process.ID) {\n return this.#state.activeCalls.some((call) => call.pid === pid && call.reported);\n }\n\n /** True while a tool-call result has not yet been delivered back to the agent turn. */\n hasPendingToolResults(): boolean {\n return this.#state.activeCalls.some((call) => !call.reported);\n }\n\n /**\n * Clears reported flags for tool calls that still have a pending queue entry.\n * After reload the in-flight createRequest is gone, so those results must be redelivered via onAlarm.\n */\n reconcileWithInputQueue(queue: readonly ToolResultEvent[]) {\n return Effect.gen({ self: this }, function* () {\n let changed = false;\n for (const item of queue) {\n const call = this.#state.activeCalls.find((entry) => entry.pid === item.pid);\n if (call?.reported) {\n call.reported = false;\n changed = true;\n log('reconcile queued tool result', { pid: item.pid });\n }\n }\n if (changed) {\n yield* ToolCallStateCell.set(this.#state);\n }\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n}\n\nexport type AgentIdleSnapshot = {\n toolResults: readonly ToolResultEvent[];\n pendingMessages: readonly Message.Message[];\n // A future alarm counts as pending work: the process must stay alive to fire it.\n pendingAlarms: readonly Alarm.Alarm[];\n delegations: readonly Delegation[];\n // Only the pending-results check is consulted, so the predicate stays decoupled from the rest of\n // the ToolCallManager surface (and is trivially stubbable in tests).\n toolCallManager: Pick<ToolCallManager, 'hasPendingToolResults'>;\n};\n\n/** True while the agent still has queued work, a pending alarm, subprocesses, or undelivered tool results. */\nexport const isAgentWorkPending = ({\n toolResults,\n pendingMessages,\n pendingAlarms,\n delegations,\n toolCallManager,\n}: AgentIdleSnapshot): boolean =>\n toolResults.length > 0 ||\n pendingMessages.length > 0 ||\n pendingAlarms.length > 0 ||\n delegations.length > 0 ||\n toolCallManager.hasPendingToolResults();\n\n/**\n * Whether a tool call's result is already accounted for — queued for the next turn or delivered to the\n * agent — so a finished child that can no longer be reattached needs no answer of its own.\n */\nexport const isToolResultHandled = (\n queue: readonly ToolResultEvent[],\n pid: Process.ID,\n isReported: (pid: Process.ID) => boolean,\n): boolean => queue.some((item) => item.pid === pid) || isReported(pid);\n\n/**\n * Discards tool results at the head of the queue whose values already reached the agent.\n *\n * A tool that returned inside its turn is reported synchronously AND left queued; after a reload the\n * queue is replayed, so without this the model would be handed a result it has already seen. Only the\n * head is examined: a result further back belongs to a turn that has not run yet.\n *\n * Mutates `queue` and returns the pids dropped, so the caller owns the logging.\n */\nexport const dropReportedToolResults = (\n queue: ToolResultEvent[],\n isReported: (pid: Process.ID) => boolean,\n): readonly Process.ID[] => {\n const dropped: Process.ID[] = [];\n while (queue.length > 0) {\n const head = queue[0];\n if (!isReported(head.pid)) {\n break;\n }\n queue.shift();\n dropped.push(head.pid);\n }\n return dropped;\n};\n\n/**\n * Renders a recovered tool result as the next turn's prompt: a synthetic `<result pid=N>` TEXT block\n * rather than a tool-result part, because the request it belonged to is gone and its tool-call id\n * cannot be answered. `disposition: 'synthetic'` keeps it out of the user-visible transcript.\n */\nexport const toolResultPrompt = (event: ToolResultEvent): ContentBlock.Any[] => [\n ContentBlock.Text.make({\n text: event.isError\n ? toolErrorResponse(event.pid, String(event.result))\n : toolResultResponse(event.pid, event.result),\n disposition: 'synthetic',\n }),\n];\n\n//\n// Alarms.\n//\n\n/**\n * Computes the timeout to pass to `ctx.setAlarm`, reconciling pending queue work with the earliest\n * pending feed alarm. Returns `null` when no alarm should be scheduled (process can go idle).\n */\nexport const computeAlarmDelay = ({\n hasPendingWork,\n wakeAt,\n now,\n}: {\n hasPendingWork: boolean;\n wakeAt: number | null;\n now: number;\n}): number | null => {\n if (hasPendingWork) {\n return 0;\n }\n if (wakeAt != null) {\n return Math.max(0, wakeAt - now);\n }\n return null;\n};\n\n/**\n * Prompt delivered to the agent when a self-scheduled alarm fires. When the alarm carried a\n * reminder message it is surfaced verbatim, otherwise a generic continuation prompt is used.\n * Exported so the prompt shape stays pinned by tests without spawning an agent.\n */\nexport const wakeUpPrompt = (\n firedAt: number,\n message: string | null,\n budget?: { wake: number; max: number },\n): string => {\n const fired = `Scheduled alarm fired (it was set for ${new Date(firedAt).toISOString()}).`;\n const body = message ?? 'Continue with whatever you intended to do when you scheduled this wake-up.';\n const limit =\n budget == null\n ? undefined\n : budget.wake >= budget.max\n ? `This is self-wake ${budget.wake} of ${budget.max}: further alarms will not wake you until the user writes again, so finish or report where you are now.`\n : `This is self-wake ${budget.wake} of ${budget.max} before the user must write again.`;\n return [fired, body, limit].filter((line) => line != null).join('\\n');\n};\n\nconst ToolExecutionService = ({\n enableBackgrounding,\n backgroundThreshold = Duration.seconds(1),\n toolCallManager,\n feed,\n}: ToolExecutionServiceOptions) =>\n Layer.unwrap(\n Effect.gen(function* () {\n const childServices = yield* Effect.context<\n Process.ManagerService | OperationHandlerSet.OperationHandlerProvider\n >();\n return makeToolExecutionService({\n invoke: (tool, input) =>\n Effect.gen(function* () {\n const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);\n log('invoking operation', { operationDef, input });\n const handle = yield* Process.spawn(OperationProcess.make(operationDef), input, {\n environment: {\n conversation: Obj.getURI(feed),\n },\n traceMeta: {\n conversation: Ref.make(feed),\n },\n });\n markWork('tool.spawned');\n yield* toolCallManager.beginCall(handle.pid);\n markWork('tool.call-recorded');\n log('invoked operation', { operationDef, input, pid: handle.pid });\n\n const awaitWithReport = Process.awaitOutput(handle).pipe(\n Effect.exit,\n Effect.tap(() => toolCallManager.markAsReported(handle.pid)),\n );\n const result = enableBackgrounding\n ? yield* awaitWithReport.pipe(\n Effect.timeout(backgroundThreshold),\n Effect.catchTag('TimeoutError', () =>\n Effect.succeed(Exit.succeed(toolIsRunningInBackgroundResponse(handle.pid))),\n ),\n )\n : yield* awaitWithReport;\n markWork('tool.settled');\n log('result', { result });\n return yield* result;\n }).pipe(Effect.provide(childServices)),\n });\n }),\n );\n\nclass AsynchronousExectionToolkit extends Toolkit.make(\n Tool.make('poll-tools', {\n description: trim`\n Poll tool calls running in the background.\n Set wait to true to wait for the tool call to complete before returning.\n Only set wait to true if you dont have other tasks to perform in parallel.\n Set an appropriate timeout to avoid waiting forever.\n You will also be notified about the job completion separatelly, so you do not always need to inspect the job if you dont need the result right now.\n `,\n parameters: Schema.Struct({\n ids: Schema.Array(Schema.String).annotate({\n description: 'The IDs of the jobs to inspect.',\n }),\n wait: Schema.optional(Schema.Boolean).annotate({\n description: 'Whether to wait for the tool call to complete before returning.',\n default: false,\n }),\n timeout: Schema.optional(Schema.Number).annotate({\n description:\n 'Maximum time to wait for the job to complete. If the job does not complete within the timeout, the current state is returned.',\n default: 10_000,\n }),\n }),\n }),\n) {}\n\n// TODO(dmaretskyi): Currently broken: polling a completed process returns interruped error.\nconst AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(\n Effect.gen(function* () {\n const manager = yield* Process.ManagerService;\n return {\n 'poll-tools': ({ ids, wait, timeout = 10_000 }) =>\n Effect.gen(function* () {\n return yield* Effect.forEach(ids, (pid) =>\n Effect.gen(function* () {\n const attached = yield* manager.attach<unknown, unknown>(Process.ID.make(pid)).pipe(Effect.exit);\n if (Exit.isFailure(attached)) {\n return `Process not found: ${pid}`;\n }\n return yield* Process.awaitOutput(attached.value).pipe(\n Effect.exit,\n Effect.timeout(Duration.millis(timeout)),\n Effect.map(\n Exit.match({\n onSuccess: (value) => toolResultResponse(pid, value),\n onFailure: (cause) => toolErrorResponse(pid, Cause.pretty(cause)),\n }),\n ),\n Effect.catchTag('TimeoutError', () => Effect.succeed(`Process still running: ${pid}`)),\n );\n }),\n );\n }),\n };\n }),\n);\n\n/**\n * Instructs model that the tool is running in the background.\n */\nconst toolIsRunningInBackgroundResponse = (pid: Process.ID) =>\n `Tool is running in the background (id=${pid}); wait for the completion notification to get the result.`;\n// `Tool is running in the background (id=${pid}); use ${AsynchronousExectionToolkit.tools['poll-tools'].name} to get the result.`;\n\nconst toolResultResponse = (pid: string, value: unknown) => `<result pid=${pid}>${JSON.stringify(value)}</result>`;\n\nconst toolErrorResponse = (pid: string, cause: string) => `<error pid=${pid}>${cause}</error>`;\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Cause from 'effect/Cause';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Semaphore from 'effect/Semaphore';\n\nimport { AiContext, type HarnessControlRpcs } from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as Process from '@dxos/compute/Process';\nimport * as Skill from '@dxos/compute/Skill';\nimport { Annotation, Database, Feed, Obj, Ref, Registry } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { DXN, EID, type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport type { ContentBlock } from '@dxos/types';\n\nimport { AGENT_PROCESS_KEY, type AgentInput, AgentProcess, type AgentProcessDefinition } from './agent-process.ts';\nimport { type DelegationStrategy } from './delegation-strategy.ts';\nimport { type MakeTurnProducer } from './turn-producer.ts';\n\n/** Live handle to a spawned agent process, carrying its `HarnessControl` RPC surface. */\ntype AgentHandle = Process.Process<AgentInput, void, HarnessControlRpcs>;\n\n// TODO(burdon): Agent identity?\nexport interface CreateSessionOptions {\n readonly skills?: Skill.Skill[];\n readonly context?: Ref.Ref<Obj.Unknown>[];\n readonly model?: DXN.DXN;\n readonly provider?: DXN.DXN;\n readonly systemPrompt?: string;\n}\n\n/**\n * Creates a session on a fresh feed and chat, with `opts.skills` and `opts.context` bound to it.\n */\nexport const createSession: (\n opts?: CreateSessionOptions,\n) => Effect.Effect<AgentService.Session, never, Database.Service | Registry.Service | AgentService.AgentService> =\n Effect.fn('createSession')(function* (opts) {\n // By registry URI unless the skill is a space copy, so the registry stays the one pristine copy.\n const skills = (opts?.skills ?? []).map(Skill.makeRef);\n\n const feed = yield* Database.add(Feed.make());\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n\n yield* Effect.promise(() =>\n binder.bind({\n skills,\n objects: opts?.context ?? [],\n }),\n );\n\n // The agent process runs on a chat, so the conversation gets one even when the caller only\n // wanted a bare session; the model is the chat's own, which is where the process reads it from.\n const chat = yield* Database.add(\n Chat.make({ feed: Ref.make(feed), ...(opts?.model ? { session: { model: opts.model } } : {}) }),\n );\n return yield* AgentService.getSession(chat, { provider: opts?.provider });\n }, Effect.scoped);\n\nexport interface Options {\n systemPrompt?: string;\n\n /**\n * Produces each turn. Defaults to DXOS's own `AiSession`; substituting it swaps the engine while\n * the process keeps ownership of the queue, alarms, redelivery, delegation and hydration.\n */\n makeTurnProducer?: MakeTurnProducer;\n\n /**\n * Model for a chat that has not selected one (`Chat.session.model` unset).\n */\n defaultModel?: DXN.DXN;\n\n /**\n * Default provider used to resolve the model for sessions that don't specify one explicitly.\n */\n provider?: DXN.DXN;\n\n /**\n * If true, long-running tool calls are moved to the background and the agent is notified\n * asynchronously when they complete. Currently unstable — disabled by default.\n *\n * @default false\n */\n enableToolBackgrounding?: boolean;\n\n /**\n * When provided, sessions act as supervisors: the agent delegates outstanding work to sub-agent\n * child processes and folds their results back into the conversation. Absent — a plain agent.\n */\n delegationStrategy?: DelegationStrategy;\n\n /**\n * Process definitions a chat can name in `chat.session.process` to run on instead of\n * {@link AgentProcess}. Read per session, so a definition contributed after the layer was built\n * is still found.\n */\n processes?: () => readonly AgentProcessDefinition[];\n /** Keep each session's process resident between turns (see `AgentProcessOptions.resident`). */\n resident?: boolean;\n}\n\n/**\n * The `AgentService` layer.\n *\n * Requires {@link Process.ManagerService}, which runs a session locally or, for `location: 'edge'`,\n * on the remote runtime. A host without EDGE satisfies its remote half with\n * `RemoteProcessManager.layerNoop`, so an edge session fails at spawn rather than silently running\n * locally.\n */\nexport const layer = (opts?: Options): Layer.Layer<AgentService.AgentService, never, Process.ManagerService> =>\n Layer.effect(\n AgentService.AgentService,\n Effect.gen(function* () {\n const processManager = yield* Process.ManagerService;\n\n // Spaces an edge session has been opened on this run. One remote runtime spans them all and\n // cannot enumerate them, so this is what `hydrate` has to walk.\n const remoteSpaces = new Set<SpaceId>();\n\n /**\n * Where a session's agent runs. `edge` needs the space, since one remote runtime spans them,\n * and a chat with no space cannot name where its agent would run.\n */\n const locationFor = (\n location: AgentService.AgentLocation | undefined,\n spaceId: SpaceId | undefined,\n ): Process.Location => {\n if (location !== 'edge') {\n return { kind: 'local' };\n }\n if (!spaceId) {\n throw new Error('Agent requested on edge, but its conversation has no space.');\n }\n remoteSpaces.add(spaceId);\n return { kind: 'edge', space: spaceId };\n };\n\n // The agent's model and steering instructions are bound to its process at spawn time, so the\n // cache tracks what each session was created with. A chat repointed at a different model or\n // instructions ref tears down the old process and spawns a fresh one (see below).\n const sessionCache = new Map<\n string,\n {\n model: string | undefined;\n provider: DXN.DXN | undefined;\n instructions: string | undefined;\n location: AgentService.AgentLocation;\n process: string;\n handle: AgentHandle;\n session: AgentService.Session;\n }\n >();\n\n // Serializes `getSession` per chat: discovery and spawn sit several suspensions before the\n // cache is written, so concurrent callers resolving the same conversation would each spawn a\n // process for it. Kept for the lifetime of the layer — one entry per chat, no bigger than the\n // session cache beside it.\n const sessionLocks = new Map<string, Semaphore.Semaphore>();\n const lockFor = (chatId: string): Semaphore.Semaphore => {\n const existing = sessionLocks.get(chatId);\n if (existing) {\n return existing;\n }\n const lock = Effect.runSync(Semaphore.make(1));\n sessionLocks.set(chatId, lock);\n return lock;\n };\n\n const makeExecutable = (provider?: DXN.DXN) =>\n AgentProcess({\n systemPrompt: opts?.systemPrompt,\n makeTurnProducer: opts?.makeTurnProducer,\n defaultModel: opts?.defaultModel,\n provider: provider ?? opts?.provider,\n enableToolBackgrounding: opts?.enableToolBackgrounding,\n delegationStrategy: opts?.delegationStrategy,\n resident: opts?.resident,\n });\n\n /**\n * The process a chat runs on: the one its session names, or ours. A name nothing contributed\n * (its plugin was removed) falls back to ours rather than leaving the chat unable to run.\n */\n const executableFor = (chat: AgentService.Conversation, provider?: DXN.DXN): AgentProcessDefinition => {\n const key = chat.session?.process;\n if (key === undefined || key === AGENT_PROCESS_KEY) {\n return makeExecutable(provider);\n }\n const definition = opts?.processes?.().find((candidate) => candidate.key === key);\n if (!definition) {\n log.warn('agent process not contributed; running the default', { key });\n return makeExecutable(provider);\n }\n return definition;\n };\n\n const hydrateAgents = Effect.fnUntraced(function* () {\n // Handles cached before shutdown are suspended and no longer registered with the manager.\n sessionCache.clear();\n\n // Contributed processes run only locally (see `getSession`), so only ours is looked for on edge.\n const contributed = (opts?.processes?.() ?? []).filter(({ key }) => key !== AGENT_PROCESS_KEY);\n const executable = makeExecutable();\n // Local, plus every space a session has already been opened on this run. A fresh client knows\n // no edge spaces yet and cannot enumerate them (one manager spans them all), but an edge agent\n // does not need the pre-warm: `getSession` reattaches to a process still running for its\n // chat, which is the path opening one takes.\n const agents = [\n ...(yield* processManager.handles({ key: AGENT_PROCESS_KEY })).map((agent) => ({ agent, executable })),\n ...(yield* Effect.forEach(contributed, (definition) =>\n processManager\n .handles({ key: definition.key })\n .pipe(Effect.map((agents) => agents.map((agent) => ({ agent, executable: definition })))),\n )).flat(),\n ...(yield* Effect.forEach([...remoteSpaces], (space) =>\n processManager.handles({ key: AGENT_PROCESS_KEY, location: { kind: 'edge', space } }),\n ))\n .flat()\n .map((agent) => ({ agent, executable })),\n ];\n log('agent hydrate', { count: agents.length });\n for (const { agent, executable } of agents) {\n yield* agent\n .hydrate(executable)\n .pipe(\n Effect.catchCause((cause) =>\n Effect.sync(() => log.warn('agent hydrate skipped', { pid: agent.pid, cause: Cause.pretty(cause) })),\n ),\n );\n }\n });\n\n const service: AgentService.Service = {\n getSession: (chat: AgentService.Conversation, options?: AgentService.GetSessionOptions) =>\n Effect.suspend(() =>\n lockFor(chat.id).withPermits(1)(\n Effect.gen(function* () {\n const provider = options?.provider ?? opts?.provider;\n // Read off the chat rather than passed in: the process is bound to the chat, so its\n // model and steering are whatever the chat points at when the process is spawned.\n const model = chat.session?.model;\n const instructions = chat.instructions?.uri;\n const location: AgentService.AgentLocation = options?.location ?? 'local';\n const executable = executableFor(chat, provider);\n if (location === 'edge' && executable.key !== AGENT_PROCESS_KEY) {\n // EDGE spawns by key from its own registry, which holds no process a plugin contributed.\n return yield* Effect.die(\n new Error(`Agent process ${executable.key} runs only locally, but the chat asked for edge.`),\n );\n }\n const cached = sessionCache.get(chat.id);\n if (cached) {\n if (\n cached.model === model &&\n cached.provider === provider &&\n cached.instructions === instructions &&\n cached.location === location &&\n cached.process === executable.key &&\n !Process.isTerminal(cached.handle.status.state)\n ) {\n return cached.session;\n }\n\n if (!Process.isTerminal(cached.handle.status.state)) {\n // Model, provider, steering instructions, location or process changed (e.g. the user\n // toggled online/offline, or moved the chat to the cloud): terminate the\n // existing process so the conversation continues on a fresh process bound to the new\n // configuration. Conversation history is preserved via the feed, which the new\n // process replays.\n yield* cached.handle.terminate();\n }\n sessionCache.delete(chat.id);\n }\n\n const feed = yield* Database.load(chat.feed).pipe(Effect.orDie);\n const target = Obj.getURI(chat);\n const parsedEchoUri = EID.tryParse(target);\n const spaceId = parsedEchoUri ? EID.getSpaceId(parsedEchoUri) : undefined;\n const processLocation = locationFor(options?.location, spaceId);\n\n // Reuse a still-running process for this feed only when there was no cached session\n // (e.g. after the UI remounted). A process adopted this way re-reads the chat when it\n // hydrates, so it picks up a model selected while this client was away.\n const processes = yield* processManager.handles({\n target,\n key: executable.key,\n location: processLocation,\n });\n let activeProcess = processes.find((process) => !Process.isTerminal(process.status.state));\n\n let handle: AgentHandle;\n if (activeProcess) {\n // `hydrate` returns the live handle; the listed one may be a dormant view whose\n // methods die with \"Process not hydrated\" (see ProcessManager's DormantHandle).\n handle = yield* activeProcess.hydrate(executable);\n } else {\n handle = yield* processManager.spawn(executable, {\n location: processLocation,\n name: 'Agent',\n target,\n // Stamp the host marker so the harness control surface is discoverable by annotation\n // lookup (set once at spawn, immutable — the identity plane).\n annotations: Annotation.buildDictionary((dictionary) => {\n Annotation.setDictionary(dictionary, Process.HarnessHostAnnotation, true);\n }),\n environment: {\n ...(spaceId !== undefined ? { space: spaceId } : {}),\n conversation: Obj.getURI(feed),\n },\n traceMeta: {\n conversation: Ref.make(feed),\n },\n });\n }\n\n const releaseSession = () => {\n sessionCache.delete(chat.id);\n };\n // A process that finished its turn releases its host, so the NEXT prompt on this\n // session has nowhere to land — it is dropped as \"input dropped (already finished)\"\n // and the conversation silently stops accepting turns. Re-entering `getSession`\n // spawns a fresh process for the same feed (history is replayed from it), which is\n // the path an app already takes when it re-reads the session per prompt.\n const databaseContext = yield* Effect.context<Database.Service>();\n // A REMOTE handle's status is a snapshot the client polls, so a process that finished\n // moments ago still reads as running here — and the host then drops the prompt. What\n // the host actually knows is the manager's `list`. A local handle is the live process\n // itself, so its status is authoritative, and the local `list` reads every persisted\n // process record — a storage round trip per record before every prompt.\n const isFinished: Effect.Effect<boolean> = Effect.suspend(() =>\n Process.isTerminal(handle.status.state) || location !== 'edge'\n ? Effect.succeed(Process.isTerminal(handle.status.state))\n : processManager.handles({ target, key: executable.key, location: processLocation }).pipe(\n Effect.map((live) => {\n const current = live.find((process) => process.pid === handle.pid);\n return current === undefined || Process.isTerminal(current.status.state);\n }),\n Effect.orElseSucceed(() => false),\n ),\n );\n // Releasing the cache first is what keeps this from recursing: `getSession` then\n // takes its spawn path and returns a NEW session whose process is live, so that\n // session's own `submitPrompt` submits directly.\n const resubmit = (\n prompt: string | ContentBlock.Any[],\n submitOptions?: AgentService.SubmitPromptOptions,\n ): Effect.Effect<void> =>\n Effect.sync(releaseSession).pipe(\n Effect.andThen(service.getSession(chat, options)),\n Effect.flatMap((next) => next.submitPrompt(prompt, submitOptions)),\n Effect.provide(databaseContext),\n );\n const session = makeSession(handle, chat, feed, releaseSession, isFinished, resubmit);\n sessionCache.set(chat.id, {\n model,\n provider,\n instructions,\n location,\n process: executable.key,\n handle,\n session,\n });\n return session;\n }),\n ),\n ),\n hydrate: hydrateAgents,\n };\n\n return service;\n }),\n );\n\nconst makeSession = (\n process: AgentHandle,\n chat: AgentService.Conversation,\n feed: Feed.Feed,\n releaseSession: () => void,\n isFinished: Effect.Effect<boolean>,\n resubmit: (prompt: string | ContentBlock.Any[], options?: AgentService.SubmitPromptOptions) => Effect.Effect<void>,\n): AgentService.Session => ({\n chat,\n feed,\n getContext: () =>\n Effect.gen(function* () {\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n return binder.getObjects().map((object) => Ref.make(object));\n }).pipe(Effect.scoped),\n addContext: (context: Ref.Ref<Obj.Unknown>[]) =>\n Effect.gen(function* () {\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n yield* Effect.promise(() =>\n binder.bind({\n skills: [],\n objects: context,\n }),\n );\n }).pipe(Effect.scoped),\n // Suspended so the state is read per call: a session outlives the process that served its last\n // turn, and submitting to a finished one drops the prompt.\n submitPrompt: (prompt: string | ContentBlock.Any[], options?: AgentService.SubmitPromptOptions) =>\n Effect.flatMap(isFinished, (finished) =>\n finished\n ? resubmit(prompt, options)\n : process.submitInput(options?.sender ? { prompt, sender: options.sender } : prompt),\n ),\n // Derived from the process's status atom, written on the app-wide registry the UI reads.\n running: Atom.make(\n (get) =>\n get(process.statusAtom).state === Process.State.RUNNING ||\n get(process.statusAtom).state === Process.State.HYBERNATING,\n ),\n // Settle when the turn's reply is complete; do NOT block on background sub-agents\n // (a supervisor delegates work that runs after the turn and reports back out of band).\n waitForCompletion: () => process.runUntilSettled(),\n // The stopped turn's queue entry is NOT discarded here: `terminate` blocks while a tool holds the\n // turn open, so anything it did afterwards would land after the reader's next prompt. The next\n // process to spawn on this feed discards what it inherits instead (see `onSpawn` in agent-process).\n terminate: () => process.terminate().pipe(Effect.tap(() => Effect.sync(releaseSession))),\n subscribeEphemeral: (options) => process.subscribeEphemeral(options),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"chunk-AgentService.mjs","names":["__dxlog_file","__dxlog_file"],"sources":["../../src/agent-service/mcp-servers.ts","../../src/agent-service/turn-producer.ts","../../src/agent-service/agent-process.ts","../../src/agent-service/AgentService.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport * as Credential from '@dxos/compute/Credential';\nimport * as McpServer from '@dxos/compute/McpServer';\nimport { Database, Filter, type Ref } from '@dxos/echo';\nimport { type AccessToken } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport { McpOAuth, type McpToolkit } from '@dxos/mcp-client';\n\n/**\n * The space's enabled MCP servers as connection options, read at the start of every turn so a server\n * added, toggled or signed in to from the chat's options applies from the next turn on.\n */\nexport const loadSpaceMcpServers = (): Effect.Effect<\n McpToolkit.Options[],\n never,\n Database.Service | Credential.CredentialsService\n> =>\n Effect.gen(function* () {\n const servers = yield* Database.query(Filter.type(McpServer.McpServer)).run;\n return yield* Effect.forEach(\n servers.filter((server) => server.enabled !== false),\n toOptions,\n );\n });\n\nconst toOptions = (\n server: McpServer.McpServer,\n): Effect.Effect<McpToolkit.Options, never, Database.Service | Credential.CredentialsService> =>\n Effect.gen(function* () {\n const options: McpToolkit.Options = { url: server.url, protocol: server.protocol, apiKey: server.apiKey };\n if (server.oauth?.tokens) {\n // No `onAuthorize`: the agent uses and refreshes the stored tokens but cannot start a sign-in.\n return { ...options, authProvider: McpOAuth.makeProvider({ store: McpServer.oauthStore(server) }) };\n }\n if (server.accessToken) {\n const apiKey = yield* resolveAccessToken(server.accessToken);\n return Option.isSome(apiKey) ? { ...options, apiKey: apiKey.value } : options;\n }\n return options;\n });\n\n/**\n * Resolves through {@link Credential.CredentialsService} rather than reading `token` directly, so a\n * token EDGE custodies (a managed one) resolves the same way a pasted one does.\n */\nconst resolveAccessToken = (ref: Ref.Ref<AccessToken.AccessToken>) =>\n Effect.gen(function* () {\n const accessToken = yield* Database.load(ref);\n const credentials = yield* Credential.CredentialsService;\n const credential = yield* Effect.tryPromise(() => credentials.getCredential({ accessTokenId: accessToken.id }));\n return Option.fromNullishOr(credential.apiKey);\n }).pipe(\n // The connection then goes out without a key, and the server's 401 reports what is wrong.\n Effect.tapError((error) => Effect.sync(() => log.warn('failed to resolve MCP server credential', { error }))),\n Effect.orElseSucceed(() => Option.none<string>()),\n );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as Scope from 'effect/Scope';\n\nimport { type AiRequest, AiSession, type Chat } from '@dxos/assistant';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport type * as Skill from '@dxos/compute/Skill';\nimport type { Database, Feed } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport type { Message } from '@dxos/types';\n\n/**\n * Produces one turn of a conversation.\n *\n * The agent process owns everything around a turn — the input queue, alarms, tool-call redelivery,\n * delegation, skill hooks, tracing, hydration — and none of that is specific to how the turn is\n * generated. This is the one thing that is, so an alternative engine (e.g. a Claude Agent SDK host)\n * can be substituted without touching the process.\n *\n * A producer MUST append each message it produces to the conversation's feed as it goes: the chat\n * thread renders from a reactive query over that feed, so a turn that is only returned never\n * appears.\n */\nexport interface TurnProducer {\n /**\n * Runs a turn, appending messages to the feed and returning what it produced.\n *\n * Must be interruptible — the process wraps the call and cancels it on interrupt.\n */\n runTurn(params: TurnRequest): Effect.Effect<Message.Message[], AiRequest.RunError, AiRequest.RunRequirements>;\n\n /** Skills bound to this conversation, which the process fires end-request hooks against. */\n getSkills(): Skill.Skill[];\n}\n\nexport type TurnRequest = {\n prompt: Parameters<AiSession.Session['createRequest']>[0]['prompt'];\n /** Who the queued prompt is from; carried onto the turn's user message. */\n sender?: Parameters<AiSession.Session['createRequest']>[0]['sender'];\n system?: string;\n mcpServers?: Parameters<AiSession.Session['createRequest']>[0]['mcpServers'];\n};\n\nexport type MakeTurnProducerOptions = {\n /** The conversation the process serves; its session config says which engine runs it and how. */\n chat: Chat.Chat;\n feed: Feed.Feed;\n runtime: Context.Context<Database.Service>;\n instructions: Instructions.Instructions[];\n};\n\n/**\n * Builds the producer for a conversation as a scoped effect — teardown registers with the scope\n * (`Effect.acquireRelease`/`addFinalizer`), so the process's scope owns the producer's lifetime.\n * The default drives DXOS's own `AiSession`.\n */\nexport type MakeTurnProducer = (options: MakeTurnProducerOptions) => Effect.Effect<TurnProducer, never, Scope.Scope>;\n\n/**\n * The default producer: `AiSession.Session`, which resolves the model, binds skills and context\n * objects as tools, and appends each message to the feed via its `onOutput` hook.\n */\nexport const makeAiSessionTurnProducer: MakeTurnProducer = ({ feed, runtime, instructions }) =>\n EffectEx.acquireReleaseResource(() => new AiSession.Session({ feed, runtime, instructions })).pipe(\n Effect.map((session) => ({\n runTurn: (params: TurnRequest) => session.createRequest(params),\n getSkills: () => session.context.getSkills(),\n })),\n );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Tool from 'effect/ai/Tool';\nimport * as Toolkit from 'effect/ai/Toolkit';\nimport * as Cause from 'effect/Cause';\nimport * as Clock from 'effect/Clock';\nimport * as DateTime from 'effect/DateTime';\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport { AiService, Model, OpaqueToolkit } from '@dxos/ai';\nimport {\n AiContext,\n Alarm,\n HarnessControl,\n type HarnessControlRpcs,\n type PendingState,\n SessionStore,\n SkillHooks,\n getOperationFromTool,\n makeToolExecutionService,\n makeToolResolverFromOperations,\n} from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport { OperationProcess } from '@dxos/compute-runtime';\nimport * as Credential from '@dxos/compute/Credential';\nimport * as McpServer from '@dxos/compute/McpServer';\nimport * as Operation from '@dxos/compute/Operation';\nimport * as OperationHandlerSet from '@dxos/compute/OperationHandlerSet';\nimport * as Process from '@dxos/compute/Process';\nimport * as Skill from '@dxos/compute/Skill';\nimport * as StorageService from '@dxos/compute/StorageService';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Annotation, Database, Feed, Obj, Ref, Registry } from '@dxos/echo';\nimport { DXN } from '@dxos/keys';\nimport { AccessToken } from '@dxos/link';\nimport { log } from '@dxos/log';\nimport { Actor, ContentBlock, Message } from '@dxos/types';\nimport { markWork, trim } from '@dxos/util';\n\nimport { type DelegationStrategy } from './delegation-strategy.ts';\nimport { loadSpaceMcpServers } from './mcp-servers.ts';\nimport { type MakeTurnProducer, makeAiSessionTurnProducer } from './turn-producer.ts';\n\nexport interface AgentProcessOptions {\n // TODO(burdon): Instructions?\n systemPrompt?: string;\n\n /**\n * Produces each turn. Defaults to {@link makeAiSessionTurnProducer}; substituting it swaps the\n * engine (e.g. a Claude Agent SDK host) while leaving the queue, alarms, redelivery, delegation\n * and hydration around it untouched.\n */\n makeTurnProducer?: MakeTurnProducer;\n\n /** Model used when the chat the process is bound to has not selected one. */\n defaultModel?: DXN.DXN;\n\n /**\n * Stay resident (IDLE) once the work is done rather than succeeding, so the next prompt lands on this\n * process instead of a new one. For a host where a spawn is expensive — on EDGE it is a fresh Durable\n * Object that opens the space and loads the toolkit before the turn can start.\n * @default false\n */\n resident?: boolean;\n\n /**\n * The catalog's shared model ids are served by several providers, so resolution needs the provider\n * alongside the id; without it a local model id cannot be claimed by any resolver.\n */\n provider?: DXN.DXN;\n\n /**\n * If true, long-running tool calls are moved to the background after `backgroundThreshold`\n * and the agent is notified asynchronously when they complete.\n *\n * Currently unstable — disabled by default.\n * @default false\n */\n enableToolBackgrounding?: boolean;\n\n /**\n * When provided, the agent acts as a supervisor: after each turn it delegates outstanding work to\n * linked child processes and folds their results back into the conversation on completion. Absent\n * (the default) the process behaves as a plain conversational agent.\n */\n delegationStrategy?: DelegationStrategy;\n}\n\nexport const AGENT_PROCESS_KEY = 'org.dxos.testing.process.agent';\n\nconst AgentPrompt = Schema.Union([Schema.String, Schema.Array(ContentBlock.Any)]);\n\n/**\n * Who sent a prompt: the plain-data subset of `Actor`, since the input crosses a JSON boundary\n * (EDGE decodes it with the schema's type side) where a `Ref` cannot be supplied.\n */\nconst AgentInputSender = Actor.Actor.mapFields(Struct.pick(['role', 'name', 'identityDid', 'email']));\n\n/**\n * Input accepted by {@link AgentProcess}: a bare prompt, or a prompt attributed to a sender (e.g. a\n * relayed Discord author) whose identity the appended `Message` records.\n */\nexport const AgentInput = Schema.Union([\n AgentPrompt,\n Schema.Struct({\n prompt: AgentPrompt,\n sender: Schema.optional(AgentInputSender),\n /** Foreign identity and other source metadata copied onto the message (e.g. `{ discord: { userId } }`). */\n properties: Schema.optional(Schema.Record(Schema.String, Schema.Any)),\n }),\n]);\n\nexport type AgentInput = Schema.Schema.Type<typeof AgentInput>;\n\n/** Builds the feed message for an input; the sender defaults to the plain user role. */\nexport const makeInputMessage = (input: AgentInput): Message.Message => {\n const { prompt, sender, properties } =\n typeof input === 'object' && 'prompt' in input\n ? input\n : { prompt: input, sender: undefined, properties: undefined };\n const blocks = typeof prompt === 'string' ? [ContentBlock.Text.make({ text: prompt })] : [...prompt];\n return Message.make({ sender: { role: 'user', ...sender }, blocks, properties });\n};\n\n/**\n * A process that can run a chat in place of {@link AgentProcess}: it takes the same input and serves\n * the same `HarnessControl` RPCs, so a session drives it without knowing which one it is. Its\n * requirements are its own; the host resolves them when it spawns the process.\n */\nexport type AgentProcessDefinition = Operation.Durable<AgentInput, void, any, HarnessControlRpcs>;\n\n/**\n * How long to wait before re-reading a queue that contradicts a write this process just made, and\n * how many times. A hosted runtime serves the read from an eventually-consistent index, and the lag\n * measured on EDGE is single-digit milliseconds — the cap is what keeps a write that never lands\n * from waking the agent forever.\n */\nconst UNSEEN_WRITE_RETRY_MS = 250;\n\n/** How much of a sub-agent's result the trace keeps: enough to read, never the whole payload. */\nconst RESULT_PREVIEW_LENGTH = 200;\n\n/**\n * A short, safe rendering of a sub-agent's result for the trace.\n *\n * Guarded because this sits between removing the child from `delegations` and running the strategy's\n * `onComplete`: a `BigInt` or a cycle would throw here, become a defect, and take the whole return\n * path with it — the work item would never be updated and work waiting on it never reconciled.\n * Losing the preview is survivable; losing the return is not.\n */\nconst resultPreview = (value: unknown): string | undefined => {\n let text: string | undefined;\n try {\n text = typeof value === 'string' ? value : JSON.stringify(value);\n } catch {\n return undefined;\n }\n if (text === undefined) {\n return undefined;\n }\n return text.length > RESULT_PREVIEW_LENGTH ? `${text.slice(0, RESULT_PREVIEW_LENGTH)}…` : text;\n};\n\nconst MAX_UNSEEN_WRITE_WAKES = 20;\n\n/**\n * Hosts a persistent, suspendible AiAgent that can process a number of prompts.\n * The process target is a queue DXN string.\n */\nexport const AgentProcess = (options: AgentProcessOptions) =>\n Operation.makeDurable({\n key: AGENT_PROCESS_KEY,\n input: AgentInput,\n output: Schema.Void,\n // The conversation's own data model. `SessionStore` reads the queue with a TYPED query\n // (`Filter.type(Message)`/`Filter.type(Alarm)`), so without these registered every read comes\n // back empty on a host that did not happen to register them itself: the prompt appends fine and\n // the agent then finds nothing to do. `Chat`/`Feed` are resolved by DXN at startup.\n // `AiContext.Binding` and `Skill` belong here for the same reason the rest do: the host\n // registers exactly these with the process's database, and a typed query for a type it does\n // not know matches nothing. Without them a hosted agent reads its own skill bindings back\n // empty and runs every turn with an EMPTY TOOLKIT — the model can only answer in prose.\n // `McpServer` and `AccessToken` are read each turn to connect the space's MCP servers.\n types: [\n Chat.Chat,\n Feed.Feed,\n Message.Message,\n Alarm.Alarm,\n AiContext.Binding,\n Skill.Skill,\n McpServer.McpServer,\n AccessToken.AccessToken,\n ],\n services: [\n Database.Service,\n OpaqueToolkit.OpaqueToolkitProvider,\n Operation.Service,\n Registry.Service,\n StorageService.StorageService,\n Process.ManagerService,\n OperationHandlerSet.OperationHandlerProvider,\n AiService.AiService,\n // Needed in the fiber's context — `Header.byokLayer`'s per-request callback reads it.\n Credential.CredentialsService,\n ],\n rpcs: HarnessControl,\n }).pipe(\n Operation.withDurableHandler((ctx) =>\n Effect.gen(function* () {\n const chatDxn = Annotation.getDictionary(ctx.params.annotations, Process.TargetAnnotation).pipe(\n Option.getOrUndefined,\n );\n if (chatDxn == null) {\n return yield* Effect.die(new Error('Agent executable requires spawn options.target set to a Chat DXN.'));\n }\n // The process is bound to the chat, not to its feed: the queue, the steering instructions\n // and the checklist are all read from the chat, and a rehydrated process recovers them by\n // re-reading it rather than from spawn annotations.\n const chat = yield* Database.resolve(chatDxn, Chat.Chat).pipe(Effect.orDie);\n const feed = yield* Database.load(chat.feed).pipe(Effect.orDie);\n // A broken instructions ref degrades to an unsteered session rather than failing the process.\n const instructions = chat.instructions\n ? yield* Database.load(chat.instructions).pipe(Effect.orElseSucceed(() => undefined))\n : undefined;\n const runtime = yield* Effect.context<Database.Service>();\n const makeTurnProducer = options.makeTurnProducer ?? makeAiSessionTurnProducer;\n // Scoped acquisition: the producer's teardown registers with this process's scope.\n const session = yield* makeTurnProducer({\n chat,\n feed,\n runtime,\n instructions: instructions ? [instructions] : [],\n });\n const sessionStore = new SessionStore();\n // KV holds only undelivered tool results; queued prompts and alarms live in the feed via\n // `sessionStore`.\n let toolResults: ToolResultEvent[] = [...(yield* ToolResultsCell.get)];\n let ackedEntries: string[] = [...(yield* AckedEntriesCell.get)];\n let selfWakes = yield* SelfWakesCell.get;\n const storageService = yield* StorageService.StorageService;\n const toolCallManager = new ToolCallManager(storageService);\n yield* toolCallManager.load();\n\n // Read time from the ambient Effect Clock so alarm scheduling and the due-check stay\n // consistent (and both honor a TestClock under tests).\n const clock = yield* Clock.Clock;\n const now = () => clock.currentTimeMillisUnsafe();\n\n // Queued tool results were never consumed by onAlarm — reported flags from the synchronous\n // execution path are stale after reload and would cause onAlarm to drop them.\n yield* toolCallManager.reconcileWithInputQueue(toolResults);\n\n // Alarms written by this incarnation, keyed by id, until a read confirms the index has them.\n // `wakes` counts reads that did not show the alarm, which is what distinguishes an index\n // still catching up from one that is gone — a due time cannot: an alarm set for an instant\n // already past reads as \"due\" on the very first look, before the index has had any chance.\n const unseenAlarms = new Map<string, { wakeAt: number; wakes: number }>();\n\n // Schedules the process alarm from durable state: immediately when work is queued, at the\n // earliest pending alarm otherwise, not at all when idle.\n const reconcileAlarmWith = (state: PendingState): Effect.Effect<void> => {\n // An alarm this incarnation wrote leaves the unseen set once the read can see it; until\n // then it is the only record of a wake, so arming from the read alone would arm nothing.\n for (const alarm of state.pendingAlarms) {\n unseenAlarms.delete(alarm.id);\n }\n const observed = state.pendingAlarms[0]?.wakeAt;\n const wakeAts = [\n ...(observed != null ? [observed] : []),\n ...[...unseenAlarms.values()].map(({ wakeAt }) => wakeAt),\n ];\n const delay = computeAlarmDelay({\n hasPendingWork: toolResults.length > 0 || state.pendingMessages.length > 0,\n wakeAt: wakeAts.length > 0 ? Math.min(...wakeAts) : null,\n now: now(),\n });\n return delay != null ? ctx.setAlarm(delay) : Effect.void;\n };\n const reconcileAlarm = Effect.flatMap(sessionStore.loadPending(feed), reconcileAlarmWith);\n\n // Hydration: the work is durable (queued prompts and alarms on the feed, undelivered tool\n // results in KV) but the process alarm is not, and a rehydrated process is not handed its\n // pending input events again — so it must re-arm from that durable state here or sit idle\n // with work waiting. What was already pending is also what `onSpawn` decides the fate of.\n const startupPending = yield* sessionStore.loadPending(feed);\n yield* reconcileAlarmWith(startupPending);\n\n // Optional supervisor behaviour: when a strategy is provided, the agent reconciles\n // outstanding work into linked child processes after each turn and folds their results back\n // into the conversation on completion. Absent (the default), the process behaves as a plain\n // conversational agent.\n const strategy = Option.fromNullishOr(options.delegationStrategy);\n let delegations: Delegation[] = [...(yield* DelegationsCell.get)];\n // Background end-request hooks still running as children; persisted so a rehydrated process\n // still waits for them, and so it knows the request they belong to has already ended.\n let asyncHooks: Process.ID[] = [...(yield* AsyncHooksCell.get)];\n // Exits of children this process did not recognise yet: child events run concurrently with\n // the handler, so a background hook can exit before its pid is registered below.\n const untrackedExits = new Set<Process.ID>();\n\n // The chat's own selection wins: the process is bound to the chat, so the model it runs on is\n // recovered from the chat on rehydration like the instructions are.\n const model = chat.session?.model ?? options.defaultModel;\n const requestModelLayer = AiService.languageModel(DXN.getName(model ?? Model.claudeSonnet5.id), {\n provider: options.provider,\n });\n\n const childServices = yield* Effect.context<\n Process.ManagerService | OperationHandlerSet.OperationHandlerProvider\n >();\n\n // Fire end-request hooks declared by the bound skills (e.g. the planning plan-reminder).\n // Each hook runs as a child operation with `conversation` set, so it resolves the full\n // HarnessService (Tier B) and can enqueue a continuation back onto this host's queue — which\n // keeps the process alive past this turn.\n const runEndRequestHooks = Effect.gen(function* () {\n yield* SkillHooks.runHooks({\n skills: session.getSkills(),\n phase: 'end-request',\n invoke: (operation, input, hook) =>\n Effect.gen(function* () {\n const handle = yield* Process.spawn(OperationProcess.make(operation), input, {\n environment: { conversation: Obj.getURI(feed) },\n traceMeta: { conversation: Ref.make(feed) },\n });\n if (hook.async) {\n if (untrackedExits.delete(handle.pid)) {\n return;\n }\n // Not awaited, so the request settles now; the child is linked, so its exit wakes\n // `onChildEvent`, which completes the process once every background hook is done.\n asyncHooks.push(handle.pid);\n yield* AsyncHooksCell.set(asyncHooks);\n return;\n }\n // A child failure surfaces in the Effect channel, so the outer `Effect.orDie` (and the\n // hook runner's `catchAllCause`) handle it instead of it being silently discarded.\n yield* Process.awaitOutput(handle);\n }).pipe(Effect.provide(childServices), Effect.asVoid, Effect.orDie),\n });\n });\n\n // Whether this incarnation has actually run a turn. An empty queue means two different things:\n // after a turn it means the work drained and the process should finish, but on a fresh spawn\n // it is simply a conversation nobody has spoken to yet — `onSpawn` discards what it inherits,\n // so a new process ALWAYS starts empty. Completing on the latter ends the agent before the\n // prompt that spawned it arrives, and `submitInput` then drops that prompt on a finished\n // handle, leaving the reader with no reply and no error.\n let turnRan = false;\n\n // Whether this request's end-request hooks have fired; reset by every turn, so a request\n // continued by a hook gets its own hooks when it ends. A rehydrated process with background\n // hooks still running has already fired them.\n let endHooksFired = asyncHooks.length > 0;\n\n // Queue entries this incarnation wrote but has not yet read back. A hosted process's queue\n // read is served by the space INDEX, which is eventually consistent: the agent appends a\n // prompt and, milliseconds later, its own read returns empty — the index catches up a beat\n // afterwards, but by then the agent has already concluded there is no work and gone idle,\n // and nothing looks again. Locally the same read is served from the resident feed handle, so\n // this never happens off a hosted runtime. Counted rather than flagged so a burst of prompts\n // is not mistaken for one.\n // Held by id, not counted: a resumed process can dequeue an OLDER entry it did not write,\n // and a bare count would treat that as its own write becoming visible — zeroing the budget\n // while this incarnation's prompt is still unread, so the next empty read completes the\n // process and drops it. In memory because it describes this incarnation's writes only; a\n // later one re-reads the feed from scratch.\n const unseenWriteIds = new Set<string>();\n let unseenWriteWakes = 0;\n\n const pendingWork = (state: PendingState): boolean =>\n isAgentWorkPending({\n toolResults,\n pendingMessages: state.pendingMessages,\n pendingAlarms: state.pendingAlarms,\n delegations,\n toolCallManager,\n });\n\n // A turn that ends with an alarm pending leaves the process resident, and the reader is then\n // waiting on the wake rather than on the turn — so the last stage of that turn is no longer\n // what is happening.\n const reportSleeping = (state: PendingState): Effect.Effect<void, never, Trace.TraceService> =>\n state.pendingAlarms.length > 0 ? Trace.emitRequestPhase('sleeping') : Effect.void;\n\n const maybeCompleteWith = (state: PendingState) =>\n Effect.gen(function* () {\n // A result reported inside the turn it belongs to is still sitting in the queue; it is\n // not outstanding work, and counting it as such keeps the agent from ever completing —\n // the head-drop at the top of `onAlarm` cannot help, because nothing arms another wake.\n for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) {\n log('drop tool result reported within its turn', { pid });\n }\n if (pendingWork(state)) {\n yield* reportSleeping(state);\n return;\n }\n\n if (!turnRan && !endHooksFired) {\n // Idle, not done: stay resident so the prompt this process was spawned for can still\n // land. Ahead of the hooks below, which are end-of-REQUEST hooks — there has been no\n // request to end. Nothing is scheduled; the next `onInput` arms the alarm.\n log('agent idle before its first turn, staying resident');\n return;\n }\n\n if (!endHooksFired) {\n endHooksFired = true;\n // The hook may enqueue work (e.g. a plan continuation reminder) via HarnessService Tier B,\n // which appends to the feed queue; re-check before succeeding so the turn is not dropped.\n yield* runEndRequestHooks;\n const after = yield* sessionStore.loadPending(feed);\n if (pendingWork(after)) {\n log('agent work enqueued by end-request hook, continuing');\n yield* reconcileAlarmWith(after);\n yield* reportSleeping(after);\n return;\n }\n }\n\n if (asyncHooks.length > 0) {\n // Resident rather than done: the linked children keep the process HYBERNATING, which\n // `runUntilSettled` treats as settled, so the reader is not held up by them.\n log('awaiting background end-request hooks', { count: asyncHooks.length });\n return;\n }\n\n if (options.resident) {\n log('agent work complete, staying resident');\n return;\n }\n\n log('agent work complete, succeeding');\n ctx.succeed();\n });\n\n const maybeComplete = Effect.flatMap(sessionStore.loadPending(feed), maybeCompleteWith);\n\n return {\n // Runs on a fresh spawn only — never on a resume, which is what a hibernated process gets.\n // So anything already pending here was left by a process that is gone for good: stopped by\n // the user, or dead without being rehydrated. Redelivering it would re-run a prompt the\n // reader stopped, and the next thing they type would queue behind it.\n onSpawn: Effect.fnUntraced(function* () {\n if (startupPending.pendingMessages.length > 0) {\n log.info('discarding queue entries left by a previous process', {\n count: startupPending.pendingMessages.length,\n });\n yield* Effect.forEach(startupPending.pendingMessages, (message) => sessionStore.ack(feed, message), {\n discard: true,\n });\n }\n }),\n // Control plane (§4.3): handlers run on the host process's server fiber, writing their\n // durable effect to the feed inline.\n rpcHandlers: yield* HarnessControl.toHandlers({\n setAlarm: Effect.fn(function* ({ at, message }) {\n const alarm = yield* sessionStore.setAlarm(feed, {\n wakeAt: DateTime.toEpochMillis(at),\n message: message ?? undefined,\n });\n // Reconciling reads the feed back through the eventually-consistent index, so a wake\n // whose record has not landed yet arms nothing and nothing looks again.\n unseenAlarms.set(alarm.id, { wakeAt: alarm.wakeAt, wakes: 0 });\n yield* reconcileAlarm;\n }),\n enqueueMessage: Effect.fn(function* ({ content }) {\n const message = Message.make({ sender: { role: 'user' }, blocks: [...content] });\n yield* sessionStore.enqueueMessage(feed, message);\n unseenWriteIds.add(message.id);\n yield* ctx.setAlarm(0);\n }),\n }),\n onInput: Effect.fnUntraced(function* (input: AgentInput) {\n log('agent onInput received', { backlog: toolResults.length });\n const message = makeInputMessage(input);\n yield* sessionStore.enqueueMessage(feed, message);\n markWork('agent.prompt-queued');\n unseenWriteIds.add(message.id);\n yield* ctx.setAlarm(0);\n log('agent onInput enqueued to feed');\n }),\n onAlarm: Effect.fnUntraced(\n function* () {\n log('agent onAlarm fired', { backlog: toolResults.length });\n markWork('agent.wake');\n\n // Earliest point the agent can report to a reader who is already waiting: draining the\n // queue below reads the feed, which is itself part of the wait. Only for a wake with known\n // work — the re-check after every turn finds none, and on a hosted agent that read takes\n // long enough for the line to read as another request starting after the reply.\n const announced = toolResults.length > 0 || unseenWriteIds.size > 0;\n if (announced) {\n yield* Trace.emitRequestPhase('preparing');\n }\n\n for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) {\n log.info('skip tool result that was reported synchronously', { pid });\n }\n\n // Undelivered tool results drain first; then the feed queue, then a due alarm.\n const toolResult = toolResults.shift();\n let prompt: ContentBlock.Any[];\n let dequeued: Message.Message | Alarm.Alarm | undefined;\n if (toolResult !== undefined) {\n log('agent onAlarm handling', { tag: toolResult._tag });\n prompt = toolResultPrompt(toolResult);\n } else {\n const state = yield* sessionStore.loadPending(feed);\n markWork('agent.pending-loaded');\n // An id still in the pending set has not caught up yet; one that has left it is\n // durably acked and no longer needs remembering.\n const stillPending = new Set([\n ...state.pendingMessages.map((message) => message.id),\n ...state.pendingAlarms.map((alarm) => alarm.id),\n ]);\n if (ackedEntries.some((id) => !stillPending.has(id))) {\n ackedEntries = ackedEntries.filter((id) => stillPending.has(id));\n yield* AckedEntriesCell.set(ackedEntries);\n }\n const acked = new Set(ackedEntries);\n const message = state.pendingMessages.find((candidate) => !acked.has(candidate.id));\n const dueAlarm = state.pendingAlarms.find((alarm) => alarm.wakeAt <= now() && !acked.has(alarm.id));\n // An alarm this incarnation wrote that enough reads have failed to show is gone —\n // cancelled, or delivered on an earlier wake — and must stop contributing a due\n // time, or reconciling would re-arm for it forever. Bounded by reads rather than by\n // the due time, which would drop an already-due alarm before the index caught up.\n for (const [id, entry] of unseenAlarms) {\n if (state.pendingAlarms.some((alarm) => alarm.id === id)) {\n continue;\n }\n entry.wakes++;\n if (entry.wakes >= MAX_UNSEEN_WRITE_WAKES) {\n unseenAlarms.delete(id);\n }\n }\n if (message !== undefined) {\n log('agent onAlarm handling', { tag: 'message', id: message.id });\n unseenWriteIds.delete(message.id);\n unseenWriteWakes = 0;\n if (isUserPrompt(message) && selfWakes > 0) {\n selfWakes = 0;\n yield* SelfWakesCell.set(selfWakes);\n }\n dequeued = message;\n prompt = [...message.blocks];\n } else if (dueAlarm !== undefined) {\n unseenAlarms.delete(dueAlarm.id);\n if (selfWakes >= Alarm.MAX_SELF_WAKES) {\n // Spent: acked without a turn, so the loop ends here until the user prompts again.\n log.warn('agent self-wake budget spent, dropping alarm', { wakes: selfWakes });\n yield* sessionStore.ack(feed, dueAlarm);\n ackedEntries = [...ackedEntries, dueAlarm.id];\n yield* AckedEntriesCell.set(ackedEntries);\n const after = yield* sessionStore.loadPending(feed);\n yield* reconcileAlarmWith(after);\n yield* maybeCompleteWith(after);\n return;\n }\n selfWakes++;\n yield* SelfWakesCell.set(selfWakes);\n log('agent onAlarm self-wake', { firedAt: dueAlarm.wakeAt, wakes: selfWakes });\n dequeued = dueAlarm;\n prompt = [\n ContentBlock.Text.make({\n text: wakeUpPrompt(dueAlarm.wakeAt, dueAlarm.message ?? null, {\n wake: selfWakes,\n max: Alarm.MAX_SELF_WAKES,\n }),\n disposition: 'synthetic',\n }),\n ];\n } else if (unseenWriteIds.size > 0 && unseenWriteWakes < MAX_UNSEEN_WRITE_WAKES) {\n // Read-your-writes over an eventually-consistent read: this incarnation appended an\n // entry the query has not caught up to, so look again shortly rather than conclude\n // the queue is drained. Bounded, so a write that never materialises degrades to the\n // idle path instead of waking forever.\n unseenWriteWakes++;\n markWork('agent.unseen-write-retry');\n log('agent onAlarm empty queue with an unread write, waking again', {\n unseenWrites: unseenWriteIds.size,\n attempt: unseenWriteWakes,\n });\n yield* ctx.setAlarm(UNSEEN_WRITE_RETRY_MS);\n return;\n } else if (unseenWriteIds.size > 0) {\n // Retry budget spent and a write this incarnation made is still unread. The bound\n // stops the waking, but completing here would drop that prompt for good — there is\n // no guaranteed upper bound on index lag — so the process stays resident instead,\n // to be woken by the next input or alarm.\n log.warn('agent giving up on an unread write, staying resident', {\n unseenWrites: unseenWriteIds.size,\n wakes: unseenWriteWakes,\n });\n yield* reconcileAlarmWith(state);\n return;\n } else {\n log('agent onAlarm empty queue', {});\n yield* reconcileAlarmWith(state);\n yield* maybeCompleteWith(state);\n return;\n }\n }\n\n if (!announced) {\n yield* Trace.emitRequestPhase('preparing');\n }\n\n // The MCP servers are read concurrently with the writes below: neither depends on the\n // other, and each is a round trip to the database that the turn would otherwise wait on in series.\n const [mcpServers] = yield* Effect.all(\n [\n loadSpaceMcpServers(),\n Effect.gen(function* () {\n // The turn appends its own user message built from `prompt`, so the queue entry that\n // supplied it must leave the queue view now or the same content shows in both places\n // until the late ack below.\n if (dequeued !== undefined) {\n yield* sessionStore.markInFlight(feed, dequeued);\n }\n log('begin request', { prompt });\n log('trace agent request begin');\n yield* Trace.write(Trace.AgentRequestBegin, {});\n }),\n ],\n { concurrency: 'unbounded' },\n );\n markWork('agent.turn-begin');\n yield* session\n .runTurn({\n prompt,\n // The turn rewrites the queued message as its own, so the sender has to travel with it.\n sender:\n dequeued !== undefined && Obj.instanceOf(Message.Message, dequeued) ? dequeued.sender : undefined,\n // TODO(dmaretskyi): Polling currently broken, agent relies on completion notifications being delivered.\n // toolkit: AsynchronousExectionToolkit,\n system: options.systemPrompt,\n mcpServers,\n })\n .pipe(\n Effect.onExit((exit) =>\n Effect.gen(function* () {\n yield* Trace.write(Trace.AgentRequestEnd, {\n status: Exit.isSuccess(exit) ? 'success' : Exit.hasInterrupts(exit) ? 'interrupted' : 'error',\n error: Exit.isFailure(exit) ? Cause.pretty(exit.cause) : undefined,\n });\n // The failure ends the process below, skipping the reconcile, so the strategy\n // hears of it here or not at all. A stop the reader asked for is not a failure.\n const onTurnFailed = Option.isSome(strategy) ? strategy.value.onTurnFailed : undefined;\n if (onTurnFailed && Exit.isFailure(exit) && !Cause.hasInterrupts(exit.cause)) {\n yield* onTurnFailed(chat, exit.cause);\n }\n }),\n ),\n );\n log('end request');\n turnRan = true;\n endHooksFired = false;\n yield* ToolResultsCell.set(toolResults);\n\n // Ack only now: the turn is what the queue entry was for, so a process that dies before\n // this point must find the entry still pending and redeliver it.\n if (dequeued !== undefined) {\n yield* sessionStore.ack(feed, dequeued);\n ackedEntries = [...ackedEntries, dequeued.id];\n yield* AckedEntriesCell.set(ackedEntries);\n }\n const after = yield* sessionStore.loadPending(feed);\n\n // Reconcile outstanding work into linked child processes (supervisor behaviour). The\n // children are linked, so their exits wake `onChildEvent` below.\n if (Option.isSome(strategy)) {\n const activeIds = new Set(delegations.map((delegation) => delegation.id));\n const pending = yield* strategy.value.reconcile(chat, activeIds);\n for (const delegation of pending) {\n const pid = yield* delegation.spawn;\n delegations.push({ pid, id: delegation.id });\n log('delegated work', { pid, id: delegation.id });\n yield* Trace.write(Trace.DelegationSpawned, { taskId: delegation.id, pid: String(pid) });\n }\n if (pending.length > 0) {\n yield* DelegationsCell.set(delegations);\n }\n }\n\n // Reconcile so a pending alarm (or remaining queue work) is rescheduled.\n yield* reconcileAlarmWith(after);\n yield* maybeCompleteWith(after);\n },\n Effect.orDie,\n Effect.provide(\n Layer.mergeAll(\n makeToolResolverFromOperations(),\n ToolExecutionService({\n toolCallManager,\n feed,\n enableBackgrounding: options.enableToolBackgrounding ?? false,\n }),\n AsynchronousExectionToolkitLayer,\n requestModelLayer,\n ).pipe(Layer.orDie),\n ),\n ),\n onChildEvent: Effect.fnUntraced(function* (event) {\n log('childEvent', { event });\n if (event._tag === 'exited' && asyncHooks.includes(event.pid)) {\n asyncHooks = asyncHooks.filter((pid) => pid !== event.pid);\n yield* AsyncHooksCell.set(asyncHooks);\n const manager = yield* Process.ManagerService;\n const exit = yield* manager.attach(event.pid).pipe(Effect.flatMap(Process.awaitOutput), Effect.exit);\n // A failed background hook is reported and dropped, like a failed inline hook.\n if (Exit.isFailure(exit)) {\n log.warn('background end-request hook failed', { pid: event.pid });\n }\n yield* maybeComplete;\n return;\n }\n if (event._tag === 'exited') {\n // A delegated sub-agent finished: read its result and hand it to the strategy (which\n // updates the work item and notifies the user). Unlike tool results, this does not\n // re-enter the turn — the supervisor folds it in out of band.\n const delegation = delegations.find((delegation) => delegation.pid === event.pid);\n if (delegation) {\n delegations = delegations.filter((other) => other.pid !== event.pid);\n yield* DelegationsCell.set(delegations);\n const manager = yield* Process.ManagerService;\n const child = yield* manager.attach(event.pid);\n const exit = yield* Process.awaitOutput(child).pipe(Effect.exit);\n // Written beside `DelegationSpawned`, and for the same reason: the return is only\n // observable here. The child's own trace ends with its operation and says nothing\n // about reporting back, so this is what pairs an exit with the task it answers.\n const result = Exit.isSuccess(exit) ? resultPreview(exit.value) : undefined;\n yield* Trace.write(Trace.DelegationCompleted, {\n taskId: delegation.id,\n pid: String(event.pid),\n status: Exit.isSuccess(exit) ? 'success' : 'failure',\n ...(result === undefined ? {} : { result }),\n });\n if (Option.isSome(strategy)) {\n yield* strategy.value.onComplete(chat, delegation.id, exit);\n // Re-reconcile: work that was waiting on this delegation (e.g. a dependent task)\n // spawns now rather than on the next conversational turn — this is what lets a\n // batch of delegated tasks drain without further prompting.\n const activeIds = new Set(delegations.map((delegation) => delegation.id));\n const pending = yield* strategy.value.reconcile(chat, activeIds);\n for (const next of pending) {\n const pid = yield* next.spawn;\n delegations.push({ pid, id: next.id });\n log('delegated work', { pid, id: next.id });\n yield* Trace.write(Trace.DelegationSpawned, { taskId: next.id, pid: String(pid) });\n }\n if (pending.length > 0) {\n yield* DelegationsCell.set(delegations);\n }\n }\n log('delegated work completed', { pid: event.pid, id: delegation.id, success: Exit.isSuccess(exit) });\n yield* maybeComplete;\n } else if (toolCallManager.isToolCall(event.pid)) {\n const manager = yield* Process.ManagerService;\n const attachExit = yield* manager.attach(event.pid).pipe(Effect.exit);\n if (\n Exit.isFailure(attachExit) &&\n isToolResultHandled(toolResults, event.pid, (pid) => toolCallManager.isReported(pid))\n ) {\n // Completed tool children are not rehydrated on reload; this result is already queued or\n // was delivered before the interrupted turn.\n log.verbose('childEvent skipped (process gone, result already handled)', { pid: event.pid });\n return;\n }\n const result = yield* (\n Exit.isFailure(attachExit)\n ? // Unreported and unreachable: answered as an error so the call does not stay pending forever.\n Effect.failCause(attachExit.cause)\n : Process.awaitOutput(attachExit.value)\n ).pipe(\n Effect.exit,\n Effect.map(\n Exit.match({\n onSuccess: (value): ToolResultEvent => ({\n _tag: 'tool_result',\n pid: event.pid,\n result: value,\n isError: false,\n }),\n onFailure: (cause): ToolResultEvent => ({\n _tag: 'tool_result',\n pid: event.pid,\n result: Cause.pretty(cause),\n isError: true,\n }),\n }),\n ),\n );\n toolResults.push(result);\n log('agent onChildEvent persisted tool result', { depth: toolResults.length, childPid: event.pid });\n yield* ToolResultsCell.set(toolResults);\n yield* ctx.setAlarm(0);\n log('agent onChildEvent alarm scheduled', { depth: toolResults.length });\n } else {\n log.verbose('childEvent ignored non-tool call and not a delegation', { pid: event.pid });\n untrackedExits.add(event.pid);\n }\n }\n }),\n };\n }),\n ),\n );\n\ninterface ToolExecutionServiceOptions {\n /**\n * If true, tool calls that exceed `backgroundThreshold` are detached and the agent is told\n * the call is running in the background. If false, the executor always blocks on the call.\n */\n enableBackgrounding: boolean;\n\n /**\n * Threshold after which the tool execution is placed in the background.\n * Ignored when `enableBackgrounding` is false.\n */\n // TODO(dmaretskyi): Tool annotation to never run in background.\n backgroundThreshold?: Duration.Duration;\n\n toolCallManager: ToolCallManager;\n\n feed: Feed.Feed;\n}\n\nconst ToolResultEvent = Schema.TaggedStruct('tool_result', {\n pid: Process.ID,\n result: Schema.Unknown,\n isError: Schema.Boolean,\n});\n/** Exported so the pure queue/prompt helpers below can be exercised without spawning an agent. */\nexport type ToolResultEvent = Schema.Schema.Type<typeof ToolResultEvent>;\n\nconst ToolResultsCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(ToolResultEvent).pipe(Schema.mutable)),\n 'toolResults',\n).pipe(StorageService.withDefault(() => []));\n\n/**\n * Tracks delegated sub-agent child processes (pid -> correlation id) so that, after a hibernation,\n * a delegated child's exit can be matched back to the work it was fulfilling.\n */\nconst Delegation = Schema.Struct({ pid: Process.ID, id: Schema.String }).mapFields(Struct.map(Schema.mutableKey));\ntype Delegation = Schema.Schema.Type<typeof Delegation>;\n\nconst DelegationsCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Delegation).pipe(Schema.mutable)),\n 'delegations',\n).pipe(StorageService.withDefault(() => []));\n\n/** Pids of background ({@link Skill.Hook.async}) end-request hooks the process is waiting on. */\nconst AsyncHooksCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Process.ID).pipe(Schema.mutable)),\n 'asyncHooks',\n).pipe(StorageService.withDefault(() => []));\n\n/**\n * Ids of queue entries this process has ACKED, held until the ack is visible in the feed read.\n *\n * The ack is a feed append and a hosted process reads the feed back through the eventually\n * consistent space index, so `loadPending` keeps returning an entry whose ack has not landed yet —\n * and `loadPending` deliberately includes in-flight entries, so that an interrupted turn is\n * redelivered. Without this the two combine into a redelivery loop: the agent re-runs the same turn\n * on every wake, forever. Durable rather than in-memory because the isolate does not survive\n * between turns, and pruned as soon as the entry leaves the pending set, so it cannot grow\n * unbounded.\n *\n * This does NOT weaken at-least-once delivery: an entry is recorded only after its turn ran, which\n * is the same point the ack is written, so a crash before that still redelivers.\n */\nconst AckedEntriesCell = StorageService.cell(\n Schema.fromJsonString(Schema.Array(Schema.String).pipe(Schema.mutable)),\n 'ackedEntries',\n).pipe(StorageService.withDefault(() => []));\n\n/** Alarms that have woken the agent since the last user prompt; bounded by {@link Alarm.MAX_SELF_WAKES}. */\nconst SelfWakesCell = StorageService.cell(Schema.fromJsonString(Schema.Number), 'selfWakes').pipe(\n StorageService.withDefault(() => 0),\n);\n\n/** A prompt someone typed, as opposed to one the system wrote (a report, a tool result). */\nconst isUserPrompt = (message: Message.Message): boolean =>\n message.sender.role === 'user' &&\n message.blocks.some((block) => block._tag === 'text' && block.disposition !== 'synthetic');\n\nconst ToolCallState = Schema.Struct({\n activeCalls: Schema.Array(\n Schema.Struct({\n pid: Process.ID,\n // Whether the result was reported to the agent.\n reported: Schema.Boolean,\n }).mapFields(Struct.map(Schema.mutableKey)),\n ).pipe(Schema.mutable),\n});\ninterface ToolCallState extends Schema.Schema.Type<typeof ToolCallState> {}\n\n// Id's of processes who's results were already submitted to the agent.\nconst ToolCallStateCell = StorageService.cell(\n Schema.fromJsonString(ToolCallState.mapFields(Struct.map(Schema.mutableKey))),\n 'toolCallState',\n).pipe(StorageService.withDefault(() => ({ activeCalls: [] })));\n\nclass ToolCallManager {\n #storageService: StorageService.Service;\n #state: ToolCallState = { activeCalls: [] };\n\n constructor(storageService: StorageService.Service) {\n this.#storageService = storageService;\n }\n\n load() {\n return Effect.gen({ self: this }, function* () {\n this.#state = yield* ToolCallStateCell.get;\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n beginCall(pid: Process.ID) {\n return Effect.gen({ self: this }, function* () {\n this.#state.activeCalls.push({ pid, reported: false });\n yield* ToolCallStateCell.set(this.#state);\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n markAsReported(pid: Process.ID) {\n return Effect.gen({ self: this }, function* () {\n const call = this.#state.activeCalls.find((call) => call.pid === pid);\n if (!call) {\n return;\n }\n call.reported = true;\n yield* ToolCallStateCell.set(this.#state);\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n\n isToolCall(pid: Process.ID): boolean {\n return this.#state.activeCalls.some((call) => call.pid === pid);\n }\n\n isReported(pid: Process.ID) {\n return this.#state.activeCalls.some((call) => call.pid === pid && call.reported);\n }\n\n /** True while a tool-call result has not yet been delivered back to the agent turn. */\n hasPendingToolResults(): boolean {\n return this.#state.activeCalls.some((call) => !call.reported);\n }\n\n /**\n * Clears reported flags for tool calls that still have a pending queue entry.\n * After reload the in-flight createRequest is gone, so those results must be redelivered via onAlarm.\n */\n reconcileWithInputQueue(queue: readonly ToolResultEvent[]) {\n return Effect.gen({ self: this }, function* () {\n let changed = false;\n for (const item of queue) {\n const call = this.#state.activeCalls.find((entry) => entry.pid === item.pid);\n if (call?.reported) {\n call.reported = false;\n changed = true;\n log('reconcile queued tool result', { pid: item.pid });\n }\n }\n if (changed) {\n yield* ToolCallStateCell.set(this.#state);\n }\n }).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));\n }\n}\n\nexport type AgentIdleSnapshot = {\n toolResults: readonly ToolResultEvent[];\n pendingMessages: readonly Message.Message[];\n // A future alarm counts as pending work: the process must stay alive to fire it.\n pendingAlarms: readonly Alarm.Alarm[];\n delegations: readonly Delegation[];\n // Only the pending-results check is consulted, so the predicate stays decoupled from the rest of\n // the ToolCallManager surface (and is trivially stubbable in tests).\n toolCallManager: Pick<ToolCallManager, 'hasPendingToolResults'>;\n};\n\n/** True while the agent still has queued work, a pending alarm, subprocesses, or undelivered tool results. */\nexport const isAgentWorkPending = ({\n toolResults,\n pendingMessages,\n pendingAlarms,\n delegations,\n toolCallManager,\n}: AgentIdleSnapshot): boolean =>\n toolResults.length > 0 ||\n pendingMessages.length > 0 ||\n pendingAlarms.length > 0 ||\n delegations.length > 0 ||\n toolCallManager.hasPendingToolResults();\n\n/**\n * Whether a tool call's result is already accounted for — queued for the next turn or delivered to the\n * agent — so a finished child that can no longer be reattached needs no answer of its own.\n */\nexport const isToolResultHandled = (\n queue: readonly ToolResultEvent[],\n pid: Process.ID,\n isReported: (pid: Process.ID) => boolean,\n): boolean => queue.some((item) => item.pid === pid) || isReported(pid);\n\n/**\n * Discards tool results at the head of the queue whose values already reached the agent.\n *\n * A tool that returned inside its turn is reported synchronously AND left queued; after a reload the\n * queue is replayed, so without this the model would be handed a result it has already seen. Only the\n * head is examined: a result further back belongs to a turn that has not run yet.\n *\n * Mutates `queue` and returns the pids dropped, so the caller owns the logging.\n */\nexport const dropReportedToolResults = (\n queue: ToolResultEvent[],\n isReported: (pid: Process.ID) => boolean,\n): readonly Process.ID[] => {\n const dropped: Process.ID[] = [];\n while (queue.length > 0) {\n const head = queue[0];\n if (!isReported(head.pid)) {\n break;\n }\n queue.shift();\n dropped.push(head.pid);\n }\n return dropped;\n};\n\n/**\n * Renders a recovered tool result as the next turn's prompt: a synthetic `<result pid=N>` TEXT block\n * rather than a tool-result part, because the request it belonged to is gone and its tool-call id\n * cannot be answered. `disposition: 'synthetic'` keeps it out of the user-visible transcript.\n */\nexport const toolResultPrompt = (event: ToolResultEvent): ContentBlock.Any[] => [\n ContentBlock.Text.make({\n text: event.isError\n ? toolErrorResponse(event.pid, String(event.result))\n : toolResultResponse(event.pid, event.result),\n disposition: 'synthetic',\n }),\n];\n\n//\n// Alarms.\n//\n\n/**\n * Computes the timeout to pass to `ctx.setAlarm`, reconciling pending queue work with the earliest\n * pending feed alarm. Returns `null` when no alarm should be scheduled (process can go idle).\n */\nexport const computeAlarmDelay = ({\n hasPendingWork,\n wakeAt,\n now,\n}: {\n hasPendingWork: boolean;\n wakeAt: number | null;\n now: number;\n}): number | null => {\n if (hasPendingWork) {\n return 0;\n }\n if (wakeAt != null) {\n return Math.max(0, wakeAt - now);\n }\n return null;\n};\n\n/**\n * Prompt delivered to the agent when a self-scheduled alarm fires. When the alarm carried a\n * reminder message it is surfaced verbatim, otherwise a generic continuation prompt is used.\n * Exported so the prompt shape stays pinned by tests without spawning an agent.\n */\nexport const wakeUpPrompt = (\n firedAt: number,\n message: string | null,\n budget?: { wake: number; max: number },\n): string => {\n const fired = `Scheduled alarm fired (it was set for ${new Date(firedAt).toISOString()}).`;\n const body = message ?? 'Continue with whatever you intended to do when you scheduled this wake-up.';\n const limit =\n budget == null\n ? undefined\n : budget.wake >= budget.max\n ? `This is self-wake ${budget.wake} of ${budget.max}: further alarms will not wake you until the user writes again, so finish or report where you are now.`\n : `This is self-wake ${budget.wake} of ${budget.max} before the user must write again.`;\n return [fired, body, limit].filter((line) => line != null).join('\\n');\n};\n\nconst ToolExecutionService = ({\n enableBackgrounding,\n backgroundThreshold = Duration.seconds(1),\n toolCallManager,\n feed,\n}: ToolExecutionServiceOptions) =>\n Layer.unwrap(\n Effect.gen(function* () {\n const childServices = yield* Effect.context<\n Process.ManagerService | OperationHandlerSet.OperationHandlerProvider\n >();\n return makeToolExecutionService({\n invoke: (tool, input) =>\n Effect.gen(function* () {\n const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);\n log('invoking operation', { operationDef, input });\n const handle = yield* Process.spawn(OperationProcess.make(operationDef), input, {\n environment: {\n conversation: Obj.getURI(feed),\n },\n traceMeta: {\n conversation: Ref.make(feed),\n },\n });\n markWork('tool.spawned');\n yield* toolCallManager.beginCall(handle.pid);\n markWork('tool.call-recorded');\n log('invoked operation', { operationDef, input, pid: handle.pid });\n\n const awaitWithReport = Process.awaitOutput(handle).pipe(\n Effect.exit,\n Effect.tap(() => toolCallManager.markAsReported(handle.pid)),\n );\n const result = enableBackgrounding\n ? yield* awaitWithReport.pipe(\n Effect.timeout(backgroundThreshold),\n Effect.catchTag('TimeoutError', () =>\n Effect.succeed(Exit.succeed(toolIsRunningInBackgroundResponse(handle.pid))),\n ),\n )\n : yield* awaitWithReport;\n markWork('tool.settled');\n log('result', { result });\n return yield* result;\n }).pipe(Effect.provide(childServices)),\n });\n }),\n );\n\nclass AsynchronousExectionToolkit extends Toolkit.make(\n Tool.make('poll-tools', {\n description: trim`\n Poll tool calls running in the background.\n Set wait to true to wait for the tool call to complete before returning.\n Only set wait to true if you dont have other tasks to perform in parallel.\n Set an appropriate timeout to avoid waiting forever.\n You will also be notified about the job completion separatelly, so you do not always need to inspect the job if you dont need the result right now.\n `,\n parameters: Schema.Struct({\n ids: Schema.Array(Schema.String).annotate({\n description: 'The IDs of the jobs to inspect.',\n }),\n wait: Schema.optional(Schema.Boolean).annotate({\n description: 'Whether to wait for the tool call to complete before returning.',\n default: false,\n }),\n timeout: Schema.optional(Schema.Number).annotate({\n description:\n 'Maximum time to wait for the job to complete. If the job does not complete within the timeout, the current state is returned.',\n default: 10_000,\n }),\n }),\n }),\n) {}\n\n// TODO(dmaretskyi): Currently broken: polling a completed process returns interruped error.\nconst AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(\n Effect.gen(function* () {\n const manager = yield* Process.ManagerService;\n return {\n 'poll-tools': ({ ids, wait, timeout = 10_000 }) =>\n Effect.gen(function* () {\n return yield* Effect.forEach(ids, (pid) =>\n Effect.gen(function* () {\n const attached = yield* manager.attach<unknown, unknown>(Process.ID.make(pid)).pipe(Effect.exit);\n if (Exit.isFailure(attached)) {\n return `Process not found: ${pid}`;\n }\n return yield* Process.awaitOutput(attached.value).pipe(\n Effect.exit,\n Effect.timeout(Duration.millis(timeout)),\n Effect.map(\n Exit.match({\n onSuccess: (value) => toolResultResponse(pid, value),\n onFailure: (cause) => toolErrorResponse(pid, Cause.pretty(cause)),\n }),\n ),\n Effect.catchTag('TimeoutError', () => Effect.succeed(`Process still running: ${pid}`)),\n );\n }),\n );\n }),\n };\n }),\n);\n\n/**\n * Instructs model that the tool is running in the background.\n */\nconst toolIsRunningInBackgroundResponse = (pid: Process.ID) =>\n `Tool is running in the background (id=${pid}); wait for the completion notification to get the result.`;\n// `Tool is running in the background (id=${pid}); use ${AsynchronousExectionToolkit.tools['poll-tools'].name} to get the result.`;\n\nconst toolResultResponse = (pid: string, value: unknown) => `<result pid=${pid}>${JSON.stringify(value)}</result>`;\n\nconst toolErrorResponse = (pid: string, cause: string) => `<error pid=${pid}>${cause}</error>`;\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Cause from 'effect/Cause';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Semaphore from 'effect/Semaphore';\n\nimport { SessionConfig } from '@dxos/ai';\nimport { AiContext, type HarnessControlRpcs } from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as Process from '@dxos/compute/Process';\nimport * as Skill from '@dxos/compute/Skill';\nimport { Annotation, Database, Feed, Obj, Ref, Registry } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { DXN, EID, type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport type { ContentBlock } from '@dxos/types';\n\nimport { AGENT_PROCESS_KEY, type AgentInput, AgentProcess, type AgentProcessDefinition } from './agent-process.ts';\nimport { type DelegationStrategy } from './delegation-strategy.ts';\nimport { type MakeTurnProducer } from './turn-producer.ts';\n\n/** Live handle to a spawned agent process, carrying its `HarnessControl` RPC surface. */\ntype AgentHandle = Process.Process<AgentInput, void, HarnessControlRpcs>;\n\n// TODO(burdon): Agent identity?\nexport interface CreateSessionOptions {\n readonly skills?: Skill.Skill[];\n readonly context?: Ref.Ref<Obj.Unknown>[];\n readonly model?: DXN.DXN;\n readonly provider?: DXN.DXN;\n readonly systemPrompt?: string;\n}\n\n/**\n * Creates a session on a fresh feed and chat, with `opts.skills` and `opts.context` bound to it.\n */\nexport const createSession: (\n opts?: CreateSessionOptions,\n) => Effect.Effect<AgentService.Session, never, Database.Service | Registry.Service | AgentService.AgentService> =\n Effect.fn('createSession')(function* (opts) {\n // By registry URI unless the skill is a space copy, so the registry stays the one pristine copy.\n const skills = (opts?.skills ?? []).map(Skill.makeRef);\n\n const feed = yield* Database.add(Feed.make());\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n\n yield* Effect.promise(() =>\n binder.bind({\n skills,\n objects: opts?.context ?? [],\n }),\n );\n\n // The agent process runs on a chat, so the conversation gets one even when the caller only\n // wanted a bare session; the model is the chat's own, which is where the process reads it from.\n const chat = yield* Database.add(\n Chat.make({ feed: Ref.make(feed), ...(opts?.model ? { session: { model: opts.model } } : {}) }),\n );\n return yield* AgentService.getSession(chat, { provider: opts?.provider });\n }, Effect.scoped);\n\nexport interface Options {\n systemPrompt?: string;\n\n /**\n * Produces each turn. Defaults to DXOS's own `AiSession`; substituting it swaps the engine while\n * the process keeps ownership of the queue, alarms, redelivery, delegation and hydration.\n */\n makeTurnProducer?: MakeTurnProducer;\n\n /**\n * Model for a chat that has not selected one (`Chat.session.model` unset).\n */\n defaultModel?: DXN.DXN;\n\n /**\n * Default provider used to resolve the model for sessions that don't specify one explicitly.\n */\n provider?: DXN.DXN;\n\n /**\n * If true, long-running tool calls are moved to the background and the agent is notified\n * asynchronously when they complete. Currently unstable — disabled by default.\n *\n * @default false\n */\n enableToolBackgrounding?: boolean;\n\n /**\n * When provided, sessions act as supervisors: the agent delegates outstanding work to sub-agent\n * child processes and folds their results back into the conversation. Absent — a plain agent.\n */\n delegationStrategy?: DelegationStrategy;\n\n /**\n * Process definitions a chat can name in `chat.session.process` to run on instead of\n * {@link AgentProcess}. Read per session, so a definition contributed after the layer was built\n * is still found.\n */\n processes?: () => readonly AgentProcessDefinition[];\n /** Keep each session's process resident between turns (see `AgentProcessOptions.resident`). */\n resident?: boolean;\n}\n\n/**\n * The `AgentService` layer.\n *\n * Requires {@link Process.ManagerService}, which runs a session locally or, for `location: 'edge'`,\n * on the remote runtime. A host without EDGE satisfies its remote half with\n * `RemoteProcessManager.layerNoop`, so an edge session fails at spawn rather than silently running\n * locally.\n */\nexport const layer = (opts?: Options): Layer.Layer<AgentService.AgentService, never, Process.ManagerService> =>\n Layer.effect(\n AgentService.AgentService,\n Effect.gen(function* () {\n const processManager = yield* Process.ManagerService;\n\n // Spaces an edge session has been opened on this run. One remote runtime spans them all and\n // cannot enumerate them, so this is what `hydrate` has to walk.\n const remoteSpaces = new Set<SpaceId>();\n\n /**\n * Where a session's agent runs. `edge` needs the space, since one remote runtime spans them,\n * and a chat with no space cannot name where its agent would run.\n */\n const locationFor = (\n location: AgentService.AgentLocation | undefined,\n spaceId: SpaceId | undefined,\n ): Process.Location => {\n if (location !== 'edge') {\n return { kind: 'local' };\n }\n if (!spaceId) {\n throw new Error('Agent requested on edge, but its conversation has no space.');\n }\n remoteSpaces.add(spaceId);\n return { kind: 'edge', space: spaceId };\n };\n\n // The agent's model, harness and steering instructions are bound to its process at spawn time, so\n // the cache tracks what each session was created with. A chat repointed at a different model,\n // harness or instructions ref tears down the old process and spawns a fresh one (see below).\n const sessionCache = new Map<\n string,\n {\n model: string | undefined;\n harness: string;\n provider: DXN.DXN | undefined;\n instructions: string | undefined;\n location: AgentService.AgentLocation;\n process: string;\n handle: AgentHandle;\n session: AgentService.Session;\n }\n >();\n\n // Serializes `getSession` per chat: discovery and spawn sit several suspensions before the\n // cache is written, so concurrent callers resolving the same conversation would each spawn a\n // process for it. Kept for the lifetime of the layer — one entry per chat, no bigger than the\n // session cache beside it.\n const sessionLocks = new Map<string, Semaphore.Semaphore>();\n const lockFor = (chatId: string): Semaphore.Semaphore => {\n const existing = sessionLocks.get(chatId);\n if (existing) {\n return existing;\n }\n const lock = Effect.runSync(Semaphore.make(1));\n sessionLocks.set(chatId, lock);\n return lock;\n };\n\n const makeExecutable = (provider?: DXN.DXN) =>\n AgentProcess({\n systemPrompt: opts?.systemPrompt,\n makeTurnProducer: opts?.makeTurnProducer,\n defaultModel: opts?.defaultModel,\n provider: provider ?? opts?.provider,\n enableToolBackgrounding: opts?.enableToolBackgrounding,\n delegationStrategy: opts?.delegationStrategy,\n resident: opts?.resident,\n });\n\n /**\n * The process a chat runs on: the one its session names, or ours. A name nothing contributed\n * (its plugin was removed) falls back to ours rather than leaving the chat unable to run.\n */\n const executableFor = (chat: AgentService.Conversation, provider?: DXN.DXN): AgentProcessDefinition => {\n const key = chat.session?.process;\n if (key === undefined || key === AGENT_PROCESS_KEY) {\n return makeExecutable(provider);\n }\n const definition = opts?.processes?.().find((candidate) => candidate.key === key);\n if (!definition) {\n log.warn('agent process not contributed; running the default', { key });\n return makeExecutable(provider);\n }\n return definition;\n };\n\n const hydrateAgents = Effect.fnUntraced(function* () {\n // Handles cached before shutdown are suspended and no longer registered with the manager.\n sessionCache.clear();\n\n // Contributed processes run only locally (see `getSession`), so only ours is looked for on edge.\n const contributed = (opts?.processes?.() ?? []).filter(({ key }) => key !== AGENT_PROCESS_KEY);\n const executable = makeExecutable();\n // Local, plus every space a session has already been opened on this run. A fresh client knows\n // no edge spaces yet and cannot enumerate them (one manager spans them all), but an edge agent\n // does not need the pre-warm: `getSession` reattaches to a process still running for its\n // chat, which is the path opening one takes.\n const agents = [\n ...(yield* processManager.handles({ key: AGENT_PROCESS_KEY })).map((agent) => ({ agent, executable })),\n ...(yield* Effect.forEach(contributed, (definition) =>\n processManager\n .handles({ key: definition.key })\n .pipe(Effect.map((agents) => agents.map((agent) => ({ agent, executable: definition })))),\n )).flat(),\n ...(yield* Effect.forEach([...remoteSpaces], (space) =>\n processManager.handles({ key: AGENT_PROCESS_KEY, location: { kind: 'edge', space } }),\n ))\n .flat()\n .map((agent) => ({ agent, executable })),\n ];\n log('agent hydrate', { count: agents.length });\n for (const { agent, executable } of agents) {\n yield* agent\n .hydrate(executable)\n .pipe(\n Effect.catchCause((cause) =>\n Effect.sync(() => log.warn('agent hydrate skipped', { pid: agent.pid, cause: Cause.pretty(cause) })),\n ),\n );\n }\n });\n\n const service: AgentService.Service = {\n getSession: (chat: AgentService.Conversation, options?: AgentService.GetSessionOptions) =>\n Effect.suspend(() =>\n lockFor(chat.id).withPermits(1)(\n Effect.gen(function* () {\n const provider = options?.provider ?? opts?.provider;\n // Read off the chat rather than passed in: the process is bound to the chat, so its\n // model and steering are whatever the chat points at when the process is spawned.\n const model = chat.session?.model;\n const harness = SessionConfig.harnessOf(chat.session);\n const instructions = chat.instructions?.uri;\n const location: AgentService.AgentLocation = options?.location ?? 'local';\n const executable = executableFor(chat, provider);\n if (location === 'edge' && executable.key !== AGENT_PROCESS_KEY) {\n // EDGE spawns by key from its own registry, which holds no process a plugin contributed.\n return yield* Effect.die(\n new Error(`Agent process ${executable.key} runs only locally, but the chat asked for edge.`),\n );\n }\n const cached = sessionCache.get(chat.id);\n if (cached) {\n if (\n cached.model === model &&\n cached.harness === harness &&\n cached.provider === provider &&\n cached.instructions === instructions &&\n cached.location === location &&\n cached.process === executable.key &&\n !Process.isTerminal(cached.handle.status.state)\n ) {\n return cached.session;\n }\n\n if (!Process.isTerminal(cached.handle.status.state)) {\n // Model, harness, provider, steering instructions, location or process changed (e.g. the user\n // toggled online/offline, or moved the chat to the cloud): terminate the\n // existing process so the conversation continues on a fresh process bound to the new\n // configuration. Conversation history is preserved via the feed, which the new\n // process replays.\n yield* cached.handle.terminate();\n }\n sessionCache.delete(chat.id);\n }\n\n const feed = yield* Database.load(chat.feed).pipe(Effect.orDie);\n const target = Obj.getURI(chat);\n const parsedEchoUri = EID.tryParse(target);\n const spaceId = parsedEchoUri ? EID.getSpaceId(parsedEchoUri) : undefined;\n const processLocation = locationFor(options?.location, spaceId);\n\n // Reuse a still-running process for this feed only when there was no cached session\n // (e.g. after the UI remounted). A process adopted this way re-reads the chat when it\n // hydrates, so it picks up a model selected while this client was away.\n const processes = yield* processManager.handles({\n target,\n key: executable.key,\n location: processLocation,\n });\n let activeProcess = processes.find((process) => !Process.isTerminal(process.status.state));\n\n let handle: AgentHandle;\n if (activeProcess) {\n // `hydrate` returns the live handle; the listed one may be a dormant view whose\n // methods die with \"Process not hydrated\" (see ProcessManager's DormantHandle).\n handle = yield* activeProcess.hydrate(executable);\n } else {\n handle = yield* processManager.spawn(executable, {\n location: processLocation,\n name: 'Agent',\n target,\n // Stamp the host marker so the harness control surface is discoverable by annotation\n // lookup (set once at spawn, immutable — the identity plane).\n annotations: Annotation.buildDictionary((dictionary) => {\n Annotation.setDictionary(dictionary, Process.HarnessHostAnnotation, true);\n }),\n environment: {\n ...(spaceId !== undefined ? { space: spaceId } : {}),\n conversation: Obj.getURI(feed),\n },\n traceMeta: {\n conversation: Ref.make(feed),\n },\n });\n }\n\n const releaseSession = () => {\n sessionCache.delete(chat.id);\n };\n // A process that finished its turn releases its host, so the NEXT prompt on this\n // session has nowhere to land — it is dropped as \"input dropped (already finished)\"\n // and the conversation silently stops accepting turns. Re-entering `getSession`\n // spawns a fresh process for the same feed (history is replayed from it), which is\n // the path an app already takes when it re-reads the session per prompt.\n const databaseContext = yield* Effect.context<Database.Service>();\n // A REMOTE handle's status is a snapshot the client polls, so a process that finished\n // moments ago still reads as running here — and the host then drops the prompt. What\n // the host actually knows is the manager's `list`. A local handle is the live process\n // itself, so its status is authoritative, and the local `list` reads every persisted\n // process record — a storage round trip per record before every prompt.\n const isFinished: Effect.Effect<boolean> = Effect.suspend(() =>\n Process.isTerminal(handle.status.state) || location !== 'edge'\n ? Effect.succeed(Process.isTerminal(handle.status.state))\n : processManager.handles({ target, key: executable.key, location: processLocation }).pipe(\n Effect.map((live) => {\n const current = live.find((process) => process.pid === handle.pid);\n return current === undefined || Process.isTerminal(current.status.state);\n }),\n Effect.orElseSucceed(() => false),\n ),\n );\n // Releasing the cache first is what keeps this from recursing: `getSession` then\n // takes its spawn path and returns a NEW session whose process is live, so that\n // session's own `submitPrompt` submits directly.\n const resubmit = (\n prompt: string | ContentBlock.Any[],\n submitOptions?: AgentService.SubmitPromptOptions,\n ): Effect.Effect<void> =>\n Effect.sync(releaseSession).pipe(\n Effect.andThen(service.getSession(chat, options)),\n Effect.flatMap((next) => next.submitPrompt(prompt, submitOptions)),\n Effect.provide(databaseContext),\n );\n const session = makeSession(handle, chat, feed, releaseSession, isFinished, resubmit);\n sessionCache.set(chat.id, {\n model,\n harness,\n provider,\n instructions,\n location,\n process: executable.key,\n handle,\n session,\n });\n return session;\n }),\n ),\n ),\n hydrate: hydrateAgents,\n };\n\n return service;\n }),\n );\n\nconst makeSession = (\n process: AgentHandle,\n chat: AgentService.Conversation,\n feed: Feed.Feed,\n releaseSession: () => void,\n isFinished: Effect.Effect<boolean>,\n resubmit: (prompt: string | ContentBlock.Any[], options?: AgentService.SubmitPromptOptions) => Effect.Effect<void>,\n): AgentService.Session => ({\n chat,\n feed,\n getContext: () =>\n Effect.gen(function* () {\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n return binder.getObjects().map((object) => Ref.make(object));\n }).pipe(Effect.scoped),\n addContext: (context: Ref.Ref<Obj.Unknown>[]) =>\n Effect.gen(function* () {\n const runtime = yield* Effect.context<Database.Service>();\n const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({ feed, runtime }));\n yield* Effect.promise(() =>\n binder.bind({\n skills: [],\n objects: context,\n }),\n );\n }).pipe(Effect.scoped),\n // Suspended so the state is read per call: a session outlives the process that served its last\n // turn, and submitting to a finished one drops the prompt.\n submitPrompt: (prompt: string | ContentBlock.Any[], options?: AgentService.SubmitPromptOptions) =>\n Effect.flatMap(isFinished, (finished) =>\n finished\n ? resubmit(prompt, options)\n : process.submitInput(options?.sender ? { prompt, sender: options.sender } : prompt),\n ),\n // Derived from the process's status atom, written on the app-wide registry the UI reads.\n running: Atom.make(\n (get) =>\n get(process.statusAtom).state === Process.State.RUNNING ||\n get(process.statusAtom).state === Process.State.HYBERNATING,\n ),\n // Settle when the turn's reply is complete; do NOT block on background sub-agents\n // (a supervisor delegates work that runs after the turn and reports back out of band).\n waitForCompletion: () => process.runUntilSettled(),\n // The stopped turn's queue entry is NOT discarded here: `terminate` blocks while a tool holds the\n // turn open, so anything it did afterwards would land after the reader's next prompt. The next\n // process to spawn on this feed discards what it inherits instead (see `onSpawn` in agent-process).\n terminate: () => process.terminate().pipe(Effect.tap(() => Effect.sync(releaseSession))),\n subscribeEphemeral: (options) => process.subscribeEphemeral(options),\n});\n"],"mappings":"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"}
@@ -1,2 +1 @@
1
- import { a as AgentProcess, i as AgentInput, o as makeInputMessage, r as AGENT_PROCESS_KEY, s as makeAiSessionTurnProducer, t as AgentService_exports } from "./chunk-AgentService.mjs";
2
- export { AGENT_PROCESS_KEY, AgentInput, AgentProcess, AgentService_exports as AgentService, makeAiSessionTurnProducer, makeInputMessage };
1
+ export { r as AGENT_PROCESS_KEY, i as AgentInput, a as AgentProcess, t as AgentService, s as makeAiSessionTurnProducer, o as makeInputMessage } from "./chunk-AgentService.mjs";
@@ -1,6 +1,7 @@
1
1
  import { c as __exportAll, n as layer$1 } from "./chunk-AgentService.mjs";
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Layer from "effect/Layer";
4
+ import { AiService, ConsolePrinter, OpaqueToolkit, Provider } from "@dxos/ai";
4
5
  import { Alarm, Harness } from "@dxos/assistant";
5
6
  import * as Chat from "@dxos/assistant/Chat";
6
7
  import * as AgentService from "@dxos/compute/AgentService";
@@ -9,7 +10,6 @@ import * as Skill from "@dxos/compute/Skill";
9
10
  import { Database, Feed, Filter, Hypergraph, Obj, Query, Registry, Tag, Type } from "@dxos/echo";
10
11
  import { DXN } from "@dxos/keys";
11
12
  import { log } from "@dxos/log";
12
- import { AiService, ConsolePrinter, OpaqueToolkit, Provider } from "@dxos/ai";
13
13
  import { FeedTraceSink, ProcessManager, ProcessOperationInvoker, RemoteProcessManager, RemoteTraceMonitor, TriggerDispatcher, TriggerStateStore, UnifiedProcessManager, configuredCredentialsLayer } from "@dxos/compute-runtime";
14
14
  import * as Credential from "@dxos/compute/Credential";
15
15
  import * as Operation from "@dxos/compute/Operation";