@dxos/assistant 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.
- package/dist/lib/ExecutionGraph.mjs.map +1 -1
- package/dist/lib/chunk-Agent.mjs +202 -0
- package/dist/lib/chunk-Agent.mjs.map +1 -0
- package/dist/lib/chunk-AiContext.mjs +462 -0
- package/dist/lib/chunk-AiContext.mjs.map +1 -0
- package/dist/lib/chunk-AiRequest.mjs.map +1 -1
- package/dist/lib/chunk-AiSession.mjs +2 -2
- package/dist/lib/chunk-AiSession.mjs.map +1 -1
- package/dist/lib/chunk-Alarm.mjs +37 -0
- package/dist/lib/chunk-Alarm.mjs.map +1 -0
- package/dist/lib/chunk-Chat.mjs +258 -0
- package/dist/lib/chunk-Chat.mjs.map +1 -0
- package/dist/lib/chunk-Harness.mjs +4 -4
- package/dist/lib/chunk-Harness.mjs.map +1 -1
- package/dist/lib/chunk-SessionLink.mjs +20 -0
- package/dist/lib/chunk-SessionLink.mjs.map +1 -0
- package/dist/lib/chunk-SkillHooks.mjs +47 -0
- package/dist/lib/chunk-SkillHooks.mjs.map +1 -0
- package/dist/lib/extraction.mjs.map +1 -1
- package/dist/lib/index.mjs +6 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/request/AiRequest.mjs +1 -2
- package/dist/lib/session/AiContext.mjs +1 -462
- package/dist/lib/session/AiSession.mjs +1 -2
- package/dist/lib/session/Alarm.mjs +1 -37
- package/dist/lib/session/Harness.mjs +1 -2
- package/dist/lib/session/SessionLink.mjs +1 -20
- package/dist/lib/session/SkillHooks.mjs +1 -47
- package/dist/lib/types/Agent.mjs +1 -202
- package/dist/lib/types/Chat.mjs +1 -258
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/request/AiRequest.test.ts +1 -1
- package/src/types/Agent.ts +1 -1
- package/dist/lib/session/AiContext.mjs.map +0 -1
- package/dist/lib/session/Alarm.mjs.map +0 -1
- package/dist/lib/session/SessionLink.mjs.map +0 -1
- package/dist/lib/session/SkillHooks.mjs.map +0 -1
- package/dist/lib/types/Agent.mjs.map +0 -1
- package/dist/lib/types/Chat.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-AiRequest.mjs","names":[],"sources":["../../src/util/artifact.ts","../../src/tool-runtime/services.ts","../../src/request/artifact-diff.ts","../../src/request/format.ts","../../src/request/observer.ts","../../src/request/AiRequest.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport type * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\nimport * as SchemaTransformation from 'effect/SchemaTransformation';\n\nimport { Database, type Error as EchoError, Obj, Ref, type Type } from '@dxos/echo';\nimport { EncodedReference } from '@dxos/echo-protocol';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\nimport { EID, EntityId, SpaceId } from '@dxos/keys';\nimport { trim } from '@dxos/util';\n\n/**\n * @deprecated\n */\nexport const createArtifactElement = (id: EntityId) => `<artifact id=${id} />`;\n\n/**\n * A model-friendly way to reference an object.\n * Supports vairous formats that will be normalized to a DXN.\n *\n * @internal\n */\nconst ArtifactURI: Schema.Schema<string> & {\n toEchoURI: (reference: ArtifactURI, owningSpaceId?: SpaceId) => EID.EID;\n resolve: <S extends Type.AnyEntity>(\n schema: S,\n ref: ArtifactURI,\n ) => Effect.Effect<Type.InstanceType<S>, EchoError.EntityNotFoundError, Database.Service>;\n} = class extends Schema.String.annotate({\n // TODO(dmaretskyi): This section gets overriden.\n description: trim`\n The URI of the referenced object. Protocols accepted:\n - echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4. \n - space ID, object ID tuple (spaceID:objectID)\n - Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)\n - dxn:org.dxos.type.calendar:0.1.0 -- named entity from the registry. Absoutely make sure it actually exists in the registry.\n\n URIs can be prepended with an @ symbol for compatibility with in-text references.\n `,\n examples: [\n 'echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n '@echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n 'BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n '01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n 'dxn:org.dxos.type.calendar:0.1.0',\n '@dxn:org.dxos.type.calendar:0.1.0',\n ],\n}) {\n static toEchoURI(reference: ArtifactURI, owningSpaceId?: SpaceId): EID.EID {\n // Allow @ prefix for compatibility with in-text references.\n const stripped = reference.startsWith('@') ? reference.slice(1) : reference;\n if (stripped.startsWith('echo:') || stripped.startsWith('dxn:')) {\n return EID.parse(stripped);\n } else if (stripped.includes(':')) {\n const [spaceId, objectId] = stripped.split(':');\n if (!SpaceId.isValid(spaceId) || !EntityId.isValid(objectId)) {\n throw new Error(`Unable to parse object reference: ${reference}`);\n }\n // This is a workaround because the current Filter API doesn't work with fully qualified Echo URIs.\n // We check if the space ID is the same as the owning space and then use LOCAL_SPACE_TAG for local references.\n // TODO(dmaretskyi): Fix this in the Echo and Filter API to properly handle fully qualified URIs.\n return spaceId === owningSpaceId\n ? EID.make({ entityId: objectId })\n : EID.make({ spaceId: spaceId, entityId: objectId });\n } else if (EntityId.isValid(stripped)) {\n return EID.make({ entityId: stripped });\n } else {\n throw new Error(`Unable to parse object reference: ${reference}`);\n }\n }\n\n /**\n * Resolves an artifact ID to an object.\n */\n static resolve<S extends Type.AnyEntity>(\n schema: S,\n ref: ArtifactURI,\n ): Effect.Effect<Type.InstanceType<S>, EchoError.EntityNotFoundError, Database.Service> {\n const uri = ArtifactURI.toEchoURI(ref);\n return Database.resolve(Ref.fromURI(uri), schema);\n }\n};\n\ntype ArtifactURI = Schema.Schema.Type<typeof ArtifactURI>;\n\n/**\n * Schema that decodes ECHO reference object from an LLM-friendly input.\n */\nexport const RefFromLLM = ArtifactURI.pipe(\n Schema.decodeTo(\n Ref.Ref(Obj.Unknown),\n SchemaTransformation.transform({\n decode: (fromA) => {\n const eid = ArtifactURI.toEchoURI(fromA);\n // Normalize to local form: strip any space authority so the ref resolves within the current\n // space context. The AI commonly sends echo://SPACE/ENTITY format (mirroring what it sees in\n // the database context), but cross-space resolution is not yet supported and the space ID\n // encoded in the URI is always the current space anyway.\n return EncodedReference.fromURI(EID.toLocal(eid));\n },\n encode: (toI) => EncodedReference.toURI(toI) as never,\n }),\n ),\n).annotate({\n description: SchemaAST.getDescriptionAnnotation(ArtifactURI.ast) as string,\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Tool from 'effect/ai/Tool';\nimport type * as Toolkit from 'effect/ai/Toolkit';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as JsonSchema from 'effect/JsonSchema';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Record from 'effect/Record';\nimport * as Schema from 'effect/Schema';\nimport type * as SchemaRepresentation from 'effect/SchemaRepresentation';\nimport * as Stream from 'effect/Stream';\n\nimport { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from '@dxos/ai';\nimport { OpaqueToolkit } from '@dxos/ai';\nimport * as Operation from '@dxos/compute/Operation';\nimport { todo } from '@dxos/debug';\nimport { Filter, Ref, Registry } from '@dxos/echo';\nimport * as EchoJsonSchema from '@dxos/echo/JsonSchema';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\nimport * as SchemaEx from '@dxos/effect/SchemaEx';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport { RefFromLLM } from '../util/index.ts';\n\nexport const makeToolResolverFromOperations = <R = never>({\n toolkit: extraToolkit = OpaqueToolkit.empty,\n}: { toolkit?: OpaqueToolkit.OpaqueToolkit<never, never, R> } = {}): Layer.Layer<\n ToolResolverService,\n never,\n OpaqueToolkit.OpaqueToolkitProvider | Registry.Service | R\n> => {\n // `Layer.effect` runs construction in the layer's own scope, so the subscription below is released\n // with the layer.\n return Layer.effect(\n ToolResolverService,\n Effect.gen(function* () {\n const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;\n const registry = yield* Registry.Service;\n\n // Tool names derive from operation keys lossily (`Operation.toolNameFromKey`), so a record is\n // matched by re-deriving its name rather than by reconstructing a key — which rules out the\n // indexed key lookup and would otherwise mean scanning every operation per resolution.\n let index: Map<string, Operation.PersistentOperation[]> | undefined;\n // Dropped on any registry change rather than only on a lookup miss: a hit against a stale index\n // would resolve a name whose second claimant registered after the build, silently picking one\n // of two operations instead of reporting the ambiguity below. The counter carries that signal\n // across the query's own await, where clearing `index` alone would be undone by the assignment\n // of a snapshot the change is already missing from.\n let generation = 0;\n yield* Effect.acquireRelease(\n Effect.sync(() =>\n registry.changed.on(() => {\n index = undefined;\n generation++;\n }),\n ),\n (unsubscribe) => Effect.sync(() => unsubscribe()),\n );\n const buildIndex = Effect.fn('buildToolNameIndex')(function* () {\n // Retries rather than caching a snapshot the registry has already moved past; a change during\n // the query is rare and finite, so this settles on the first quiet pass.\n while (true) {\n const seen = generation;\n const records = yield* Effect.promise(() => registry.query(Filter.type(Operation.PersistentOperation)).run());\n const built = new Map<string, Operation.PersistentOperation[]>();\n for (const record of records) {\n const key = Operation.getKey(record);\n if (key == null) {\n continue;\n }\n // Non-throwing: a record's key is untrusted, and one that cannot derive a valid name must\n // cost only its own tool rather than every registry-backed tool in the index.\n const name = Operation.tryToolNameFromKey(key);\n if (name == null) {\n log.warn('operation key cannot derive a valid tool name; not indexed', { key });\n continue;\n }\n built.set(name, [...(built.get(name) ?? []), record]);\n }\n if (generation === seen) {\n index = built;\n return built;\n }\n }\n });\n\n return {\n resolve: (id): Effect.Effect<Tool.Any, AiToolNotFoundError> =>\n Effect.gen(function* () {\n const toolkit = OpaqueToolkit.merge(extraToolkit, yield* toolkitProvider.getToolkit());\n\n const tool = toolkit.toolkit.tools[id];\n if (tool) {\n return tool;\n }\n\n // A miss is conclusive: any registration since the build dropped the index above, so a\n // rebuild here would only re-scan the registry for every id that is not an operation.\n const matches = (index ?? (yield* buildIndex())).get(id);\n if (matches == null) {\n return yield* Effect.fail(new AiToolNotFoundError(id));\n }\n // Two keys deriving one name (`webSearch` vs `web-search`) is an authoring error. Picking\n // the first match would silently shadow the other, so it fails here instead — the only\n // place both keys are visible at once (a ToolId no longer distinguishes them).\n invariant(\n matches.length === 1,\n `Tool name \"${id}\" is claimed by ${matches.length} operations: ${matches\n .map((record) => Operation.getKey(record))\n .join(', ')}`,\n );\n // A registry record is untrusted input: its persisted schema may be one the projection\n // cannot express as tool parameters. Throwing here would surface as a defect and fail\n // the whole request, so one unprojectable operation is logged and dropped instead —\n // `resolveToolkit` filters a not-found tool out and the agent runs with the rest.\n try {\n return projectFunctionToTool(Operation.deserialize(matches[0]));\n } catch (err) {\n log.error('operation cannot be projected to a tool; excluded from context', {\n id,\n key: Operation.getKey(matches[0]),\n err,\n });\n return yield* Effect.fail(new AiToolNotFoundError(id));\n }\n }),\n } satisfies Context.Service.Shape<typeof ToolResolverService>;\n }),\n );\n};\n\nexport const makeToolExecutionService = <E, R>(opts: {\n invoke: (tool: Tool.Any, input: unknown) => Effect.Effect<unknown>;\n}): Layer.Layer<ToolExecutionService, never, OpaqueToolkit.OpaqueToolkitProvider> =>\n Layer.effect(\n ToolExecutionService,\n Effect.gen(function* () {\n const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;\n const toolkit = yield* toolkitProvider.getToolkit();\n\n const toolkitHandler = yield* toolkit.toolkit.pipe(Effect.provide(toolkit.layer));\n if (!isHandlerLike(toolkitHandler)) {\n return yield* Effect.die(new Error('Toolkit produced a handler-less toolkit instance'));\n }\n\n return {\n handlersFor: (toolkit) => {\n const makeHandler = (tool: Tool.Any): ((params: unknown) => Effect.Effect<unknown, any, any>) => {\n return Effect.fn(`toolFunctionHandler ${tool.name}`)(function* (input: unknown) {\n if (toolkitHandler.tools[tool.name]) {\n if (Tool.isProviderDefined(tool)) {\n throw new Error('Attempted to call a provider-defined tool');\n }\n\n // `handle` streams preliminary results before the final one; the toolkit\n // interrupts the handler fiber once its effect settles, so the final result is\n // always the last item emitted — `Stream.runLast` recovers exactly that value.\n const resultStream = yield* toolkitHandler.handle(tool.name, input);\n const lastResult = yield* Stream.runLast(resultStream);\n const handlerResult = yield* Option.match(lastResult, {\n onNone: () => Effect.die(new Error(`Tool \"${tool.name}\" handler produced no result`)),\n onSome: Effect.succeed,\n });\n return handlerResult.result;\n }\n\n // Every `invoke` implementation (in-process, or a child process spawned by the agent)\n // funnels through here, so the projected schema is applied once, at the boundary where\n // the model's raw arguments arrive. A malformed argument dies and is caught just below,\n // reaching the model as an error it can retry from.\n const decoded = yield* decodeToolParameters(tool, input).pipe(Effect.orDie);\n return yield* opts.invoke(tool, decoded).pipe(Effect.catchDefect((defect) => Effect.fail(defect)));\n });\n };\n\n return toolkit.of(\n // Operations project to dynamic tools (see `projectFunctionToTool`), which are handled here\n // just like user-defined ones; only provider-executed tools have no local handler.\n Record.map(toolkit.tools, (tool, _name) =>\n Tool.isUserDefined(tool) || Tool.isDynamic(tool) ? makeHandler(tool) : null,\n ) as any,\n ) as any;\n },\n };\n }),\n );\n\nexport const makeToolExecutionServiceFromOperationInvoker = (): Layer.Layer<\n ToolExecutionService,\n never,\n Operation.Service | OpaqueToolkit.OpaqueToolkitProvider\n> => {\n return Layer.unwrap(\n Effect.gen(function* () {\n const operationInvoker = yield* Operation.Service;\n\n return makeToolExecutionService({\n invoke: (tool, input) =>\n Effect.gen(function* () {\n const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);\n\n return yield* operationInvoker.invoke(operationDef, input).pipe(Effect.orDie);\n }),\n });\n }),\n );\n};\n\nexport const ToolExecutionServices = Layer.mergeAll(\n makeToolResolverFromOperations(),\n makeToolExecutionServiceFromOperationInvoker(),\n);\n\nclass FunctionToolAnnotation extends Context.Service<\n FunctionToolAnnotation,\n { definition: Operation.Definition.Any; parameters: Schema.Codec<unknown, unknown> }\n>()('@dxos/assistant/FunctionToolAnnotation') {}\n\nexport const getOperationFromTool = (tool: Tool.Any): Option.Option<Operation.Definition.Any> => {\n return Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option.map(({ definition }) => definition));\n};\n\n/**\n * Decodes a model's raw tool arguments against the projected parameter schema.\n *\n * A dynamic tool carries a JSON Schema, so `Toolkit.handle` passes the arguments through unvalidated\n * (that is what lets us state the schema the model is actually held to — see `projectFunctionToTool`).\n * Decoding here keeps the coercions the projection introduces, above all a ref supplied as a URI\n * string becoming a `Ref`, and surfaces a malformed argument to the model as a tool error.\n */\nexport const decodeToolParameters = (tool: Tool.Any, input: unknown): Effect.Effect<unknown, Schema.SchemaError> =>\n Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(\n Option.match({\n onNone: () => Effect.succeed(input),\n onSome: ({ parameters }) => Schema.decodeUnknownEffect(parameters)(input),\n }),\n );\n\nconst toolCache = new WeakMap<Operation.Definition.Any, Tool.Any>();\n\n/**\n * Parameter schema for an operation that takes no input.\n *\n * Spelled as a record with an uninhabited value type rather than `Schema.Struct({})` because v4\n * emits an empty struct as `{not: {type: 'null'}}` — the bare `object` keyword, which states\n * neither `type` nor `additionalProperties`. A provider's strict tool mode rejects that outright\n * (\"For 'object' type, 'additionalProperties' must be explicitly set to false\"), and one bad tool\n * fails the whole request. A `Never` value type admits no keys, so this emits\n * `{type: 'object', additionalProperties: false}` — the same shape, and valid under strict.\n * A JSON-schema annotation cannot express this: v4 honours such an annotation only on a check.\n */\nconst EMPTY_PARAMETERS_SCHEMA = Schema.Record(Schema.String, Schema.Never);\n\n/**\n * Projects an `Operation.Definition` into an `AiTool`.\n * Exported for testing.\n */\nexport const projectFunctionToTool = (fn: Operation.Definition.Any): Tool.Any => {\n if (toolCache.has(fn)) {\n return toolCache.get(fn)!;\n }\n\n const fields = createStructFieldsFromSchema(fn.input);\n const parametersSchema = Object.keys(fields).length === 0 ? EMPTY_PARAMETERS_SCHEMA : Schema.Struct(fields);\n const tool = Tool.dynamic(Operation.toolName(fn), {\n description: fn.meta.description,\n // A dynamic tool's JSON Schema is passed to the provider verbatim (`Tool.getJsonSchema` returns\n // it before any transformer runs), which is the only way to keep what the model is told and what\n // we validate in agreement. Handed an Effect schema instead, rc.108 describes the tool through\n // the provider's structured-output codec while `Toolkit.handle` validates against the\n // untransformed schema, so a compliant model is always rejected: a record is advertised as an\n // array of `[key, value]` pairs but validated as an object, and an optional key is advertised as\n // nullable-and-required but validated as absent-or-`T`.\n parameters: toModelJsonSchema(parametersSchema),\n failure: Schema.Any,\n })\n .annotate(FunctionToolAnnotation, { definition: fn, parameters: parametersSchema })\n // The schema above states open records and omissible optional keys, which a provider's strict tool\n // mode forbids (it demands `additionalProperties: false` and every key in `required`). Operations\n // accept arbitrary input, so strict is off for all of them rather than per-operation — one\n // non-conforming tool rejects the whole request.\n .annotate(Tool.Strict, false);\n toolCache.set(fn, tool);\n return tool;\n};\n\n/**\n * Keeps refs inline rather than hoisting them into `$defs`.\n *\n * The default policy extracts anything carrying an `identifier`, and `Ref` carries one so its\n * rejection messages can name the target type. Without this a ref parameter reaches the model as a\n * `$ref` into `$defs` instead of the described URI string it used to be. Only refs are declined, so\n * a recursive parameter keeps the name it was recorded under.\n */\nconst REFERENCES_INLINE = {\n referencePolicy: ({ identifier }: SchemaRepresentation.ReferencePolicyInput) =>\n Ref.isRefIdentifier(identifier) ? undefined : identifier,\n} as const;\n\n/**\n * Emits the JSON Schema the model is shown for a tool's parameters.\n *\n * v4 renders `Schema.optional(T)` as `anyOf: [T, null]` while its decoder accepts an absent key but\n * rejects `null`, so a model that takes the schema at its word is rejected. The null branch of an\n * optional property is therefore dropped, stating the property as the bare type and leaving it out of\n * `required` — the same contract ECHO's own emitter keeps (`stripUndefinedMember`) and the one the\n * pre-migration corpus was recorded against.\n */\nconst toModelJsonSchema = (schema: Schema.Codec<unknown, unknown>): JsonSchema.JsonSchema => {\n // A recursive parameter renders as `$ref: '#/$defs/…'` with the bodies in a separate `definitions`\n // record; keeping only the root would advertise a dangling reference to the model.\n // Closed structs, as the recorded corpus states them; Effect's default leaves them open.\n const { schema: root, definitions } = Schema.toJsonSchemaDocument(schema, {\n ...REFERENCES_INLINE,\n onExcessProperty: 'error',\n });\n const document = Object.keys(definitions).length > 0 ? { ...root, $defs: definitions } : root;\n // Folded first: once openness sets `additionalProperties`, the fold skips the node.\n return statePropertyOpenness(\n EchoJsonSchema.foldRestSignatures(dropNullBranches(document, new Set(asStringArray(schema)))),\n );\n};\n\n/**\n * States openness on every object node that leaves it implied.\n *\n * v4 omits `additionalProperties` when a record's value type is unconstrained, so an open property bag\n * reads as \"some object\". Absent, it also fails a provider's strict tool mode, which requires the key\n * to be present. `true` says what the record means and what the pre-migration corpus stated.\n */\nconst statePropertyOpenness = (node: JsonSchema.JsonSchema): JsonSchema.JsonSchema => {\n if (Array.isArray(node)) {\n // `JsonSchema.JsonSchema` (effect's own type) is a bare index signature that structurally\n // excludes arrays, even though a JSON Schema document legitimately nests them (e.g. `anyOf`).\n return node.map(statePropertyOpenness) as unknown as JsonSchema.JsonSchema;\n }\n if (typeof node !== 'object' || node === null) {\n return node;\n }\n const mapped = Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n typeof value === 'object' && value !== null ? statePropertyOpenness(value as JsonSchema.JsonSchema) : value,\n ]),\n );\n return mapped.type === 'object' && !('additionalProperties' in mapped)\n ? { ...mapped, additionalProperties: true }\n : mapped;\n};\n\n/** Property names the schema marks required; only optional properties carry the spurious null branch. */\nconst asStringArray = (schema: Schema.Codec<unknown, unknown>): readonly string[] => {\n const { required } = Schema.toJsonSchemaDocument(schema, REFERENCES_INLINE).schema;\n return Array.isArray(required) ? required.map(String) : [];\n};\n\nconst dropNullBranches = (node: JsonSchema.JsonSchema, required: ReadonlySet<string>): JsonSchema.JsonSchema => {\n const properties = node.properties;\n if (typeof properties !== 'object' || properties === null) {\n return node;\n }\n return {\n ...node,\n properties: Object.fromEntries(\n Object.entries(properties).map(([name, value]) => [name, required.has(name) ? value : withoutNull(value)]),\n ),\n };\n};\n\n/** Collapses `anyOf: [T, null]` (nested arbitrarily deep by a non-idempotent `optional`) down to `T`. */\nconst withoutNull = (value: unknown): unknown => {\n if (typeof value !== 'object' || value === null || !('anyOf' in value)) {\n return value;\n }\n const { anyOf, ...rest } = value as { anyOf: unknown[] };\n const branches = anyOf.filter(\n (branch) => !(typeof branch === 'object' && branch !== null && (branch as { type?: unknown }).type === 'null'),\n );\n if (branches.length !== 1) {\n return { ...rest, anyOf: branches.map(withoutNull) };\n }\n const only = withoutNull(branches[0]);\n return typeof only === 'object' && only !== null ? { ...rest, ...only } : only;\n};\n\n// TODO(dmaretskyi): Factor out.\n/**\n * Projects an operation input struct into the LLM-facing tool parameter fields, mapping each\n * property through {@link mapSchemaTypeForLLM} (so refs become the model-friendly `RefFromLLM`).\n * Exported for testing.\n */\nexport const createStructFieldsFromSchema = (\n schema: Schema.Codec<unknown, unknown>,\n): Record<string, Schema.Codec<unknown, unknown>> => {\n switch (schema.ast._tag) {\n case 'Objects': {\n // One cache for the whole struct: sibling properties may reach the same recursive body.\n const expansions = new Map<SchemaAST.AST, SchemaAST.AST>();\n return Object.fromEntries(\n schema.ast.propertySignatures.map((prop) => [\n prop.name,\n Schema.make(mapSchemaTypeForLLM(prop.type, expansions)),\n ]),\n );\n }\n // All three spell \"this operation takes no input\". `Void` is the authored form; the other two\n // are what it degrades to across `Operation.serialize`/`deserialize`, which routes the schema\n // through JSON Schema: `Schema.Void` emits `{type: 'null'}` and reads back as `Null`, and an\n // operation persisted with no `inputSchema` at all reads back as `Schema.Unknown`.\n case 'Void':\n case 'Null':\n case 'Unknown':\n return {};\n default:\n return todo(`Unsupported schema AST: ${schema.ast._tag}`);\n }\n};\n\n/**\n * Picks an LLM-friendly schema type for the given schema AST.\n * The picked schema type decodes to the original schema type.\n */\nconst mapSchemaTypeForLLM = (\n ast: SchemaAST.AST,\n expansions: Map<SchemaAST.AST, SchemaAST.AST> = new Map(),\n): SchemaAST.AST => {\n if (Ref.isRefType(ast)) {\n // v4's element and property nodes carry their own modifiers, so the wrapper types are gone and\n // the annotations record is optional.\n const own = SchemaAST.resolveAnnotations(ast)?.description;\n const fallback = SchemaAST.resolveAnnotations(RefFromLLM.ast)?.description as string;\n const description = own ? `${own}\\n${fallback}` : fallback;\n // `optionalKey` puts the modifier on the ref node itself, so the rewrite must carry it across.\n return SchemaEx.retainContext(ast, RefFromLLM.annotate({ description }).ast);\n }\n\n // `mapAst` evaluates a suspended body eagerly, so a recursive schema would walk its own cycle\n // forever. Memoizing by body identity and rebuilding the suspend lazily closes the cycle: the\n // re-entrant call returns this very node instead of expanding the body again.\n if (SchemaAST.isSuspend(ast)) {\n const body = ast.thunk();\n let mapped = expansions.get(body);\n if (!mapped) {\n let expanded: SchemaAST.AST | undefined;\n mapped = new SchemaAST.Suspend(\n () => (expanded ??= mapSchemaTypeForLLM(body, expansions)),\n ast.annotations,\n undefined,\n ast.encoding,\n ast.context,\n );\n expansions.set(body, mapped);\n }\n return mapped;\n }\n\n // `mapAst` carries annotations, checks, encoding and `context` across every rebuilt node; in v4\n // optionality rides on `context`, so rebuilding by hand silently makes an optional key required.\n return SchemaEx.mapAst(ast, (child) => mapSchemaTypeForLLM(child, expansions));\n};\n\n/** Narrows `value` to {@link Toolkit.WithHandler} when it carries a non-null `tools` object and a `handle` function. */\nexport const isHandlerLike = (value: unknown): value is Toolkit.WithHandler<Record<string, Tool.Any>> => {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'tools' in value &&\n typeof value.tools === 'object' &&\n value.tools !== null &&\n 'handle' in value &&\n typeof value.handle === 'function'\n );\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\n\nimport { type Obj } from '@dxos/echo';\nimport { type EntityId } from '@dxos/keys';\n\n/**\n * Resolves artifact ids to their versions.\n * Used to give the model a sense of the changes to the artifacts made by users during the conversation.\n * The artifacts versions are pinned in the history, and whenever the artifact changes in-between assistant's steps,\n * a diff is inserted into the conversation.\n *\n * Can be optionally provided to the session run call.\n */\n// TODO(dmaretskyi): Convert to Context.Reference\nexport class ArtifactDiffResolver extends Context.Service<ArtifactDiffResolver, ArtifactDiffResolver.Service>()(\n '@dxos/assistant/ArtifactDiffResolver',\n) {}\n\nexport namespace ArtifactDiffResolver {\n export type Service = {\n resolve: (\n artifacts: {\n id: EntityId;\n lastVersion: Obj.Version;\n }[],\n ) => Promise<\n Map<\n EntityId,\n {\n version: Obj.Version;\n diff?: string;\n }\n >\n >;\n };\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Option from 'effect/Option';\n\nimport type * as Operation from '@dxos/compute/Operation';\nimport * as Template from '@dxos/compute/Template';\nimport { Database, type Error, Obj, type Registry } from '@dxos/echo';\nimport { type EntityId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { type ContentBlock, Message } from '@dxos/types';\nimport { trim } from '@dxos/util';\n\nimport { AiAssistantError } from '../util/index.ts';\nimport type * as AiRequest from './AiRequest.ts';\nimport { ArtifactDiffResolver } from './artifact-diff.ts';\n\n/**\n * Formats the system prompt.\n */\n// TODO(burdon): Move to AiPreprocessor.\nexport const formatSystemPrompt = ({\n system,\n skills = [],\n objects = [],\n instructions = [],\n}: Pick<AiRequest.RunProps, 'system' | 'skills' | 'objects' | 'instructions'>): Effect.Effect<\n string,\n Operation.FunctionNotFoundError | Error.EntityNotFoundError,\n Database.Service | Registry.Service | Operation.Service\n> =>\n Effect.gen(function* () {\n const skillDefs = yield* Function.pipe(\n skills,\n Effect.forEach((skill) => Effect.succeed(skill.instructions)),\n Effect.flatMap(\n Effect.forEach((template) =>\n Effect.gen(function* () {\n return trim`\n <skill>\n ${yield* Template.processTemplate(template)}\n </skill>\n `;\n }),\n ),\n ),\n Effect.map((skills) => (skills.length > 0 ? ['## Skills Definitions', ...skills].join('\\n\\n') : undefined)),\n );\n\n // Instructions steer the session, so their text (and sentinel commands) belongs in the system\n // prompt itself — a context-object stub would force the model to tool-load them and follow nothing.\n const instructionDefs = yield* Function.pipe(\n instructions,\n Effect.forEach((instructions) =>\n Effect.gen(function* () {\n // A broken text ref degrades to commands-only rather than failing the whole prompt.\n const text = yield* Database.load(instructions.text).pipe(\n Effect.map((doc) => doc.content.trim()),\n Effect.catchTag('EntityNotFoundError', () => Effect.succeed('')),\n );\n const commands = (instructions.commands ?? []).map(\n ({ sentinel, description, prompt }) =>\n `- \\`${sentinel}\\`${description ? ` (${description})` : ''}: ${prompt}`,\n );\n const parts = [text];\n if (commands.length > 0) {\n parts.push(\n ['When the user message contains one of these sentinel commands, follow its prompt:', ...commands].join(\n '\\n',\n ),\n );\n }\n return `<instructions>\\n${parts.filter(Boolean).join('\\n\\n')}\\n</instructions>`;\n }),\n ),\n Effect.map((defs) => (defs.length > 0 ? ['## Instructions', ...defs].join('\\n\\n') : undefined)),\n );\n\n const objectDefs = yield* Function.pipe(\n objects,\n Effect.forEach((object) => {\n // Carry the label so the model only tool-loads an object when it needs the contents,\n // not just to learn what the reference is.\n const label = Obj.getLabel(object);\n return Effect.succeed(\n trim`\n <object>\n <dxn>${Obj.getURI(object)}</dxn>\n <typename>${Obj.getTypename(object)}</typename>${label ? `\\n <label>${label}</label>` : ''}\n </object>\n `,\n );\n }),\n Effect.map((objects) => (objects.length > 0 ? ['## Context Objects', ...objects].join('\\n\\n') : undefined)),\n );\n\n return yield* Function.pipe(\n Effect.succeed(\n [system, instructionDefs, skillDefs, objectDefs].filter((def): def is string => def !== undefined),\n ),\n Effect.map((parts) => parts.join('\\n\\n')),\n );\n }).pipe(Effect.withSpan('formatSystemPrompt'));\n\n/**\n * Formats the user prompt.\n */\n// TODO(burdon): Move to AiPreprocessor.\n// TODO(burdon): Convert util below to `Effect.fn` (to preserve stack info)\nexport const formatUserPrompt = ({\n prompt,\n history = [],\n sender,\n}: Pick<AiRequest.RunProps, 'prompt' | 'history' | 'sender'>): Effect.Effect<Message.Message, AiRequest.RunError> =>\n Effect.gen(function* () {\n const blocks: ContentBlock.Any[] = [];\n\n // TODO(dmaretskyi): Evaluate other approaches as `serviceOption` isn't represented in the type system.\n const artifactDiffResolver = yield* Effect.serviceOption(ArtifactDiffResolver);\n if (Option.isSome(artifactDiffResolver)) {\n const versions = gatherObjectVersions(history);\n const artifactDiff = yield* Effect.tryPromise({\n try: () =>\n artifactDiffResolver.value.resolve(\n [...versions.entries()].map(([id, version]) => ({ id, lastVersion: version })),\n ),\n catch: AiAssistantError.wrap({ message: 'Artifact diff resolution error' }),\n });\n\n log('version', { artifactDiff, versions });\n for (const [id, { version }] of [...artifactDiff.entries()]) {\n const lastVersion = versions.get(id);\n if (lastVersion && Obj.compareVersions(version, lastVersion) === 'equal') {\n artifactDiff.delete(id);\n continue;\n }\n\n blocks.push({ _tag: 'anchor', objectId: id, version });\n }\n\n if (artifactDiff.size > 0) {\n blocks.push(createArtifactUpdateBlock(artifactDiff));\n }\n }\n\n return Obj.make(Message.Message, {\n created: new Date().toISOString(),\n // A named sender is what lets the model tell speakers apart (see `AiPreprocessor`).\n sender: { ...sender, role: 'user' },\n blocks: typeof prompt === 'string' ? [...blocks, { _tag: 'text', text: prompt }] : [...blocks, ...prompt],\n });\n }).pipe(Effect.withSpan('formatUserPrompt'));\n\nconst gatherObjectVersions = (messages: Message.Message[]): Map<EntityId, Obj.Version> => {\n const artifactIds = new Map<EntityId, Obj.Version>();\n for (const message of messages) {\n for (const block of message.blocks) {\n // An anchor's `version` is persisted as `Schema.Unknown`, so narrow rather than assume the shape.\n if (block._tag === 'anchor' && Obj.isVersion(block.version)) {\n artifactIds.set(block.objectId, block.version);\n }\n }\n }\n\n return artifactIds;\n};\n\nconst createArtifactUpdateBlock = (\n artifactDiff: Map<EntityId, { version: Obj.Version; diff?: string }>,\n): ContentBlock.Any => {\n return {\n _tag: 'text',\n // TODO(dmaretskyi): Does this need to be a special content-block?\n disposition: 'synthetic',\n text: trim`\n The following artifacts have been updated since the last message:\n ${[...artifactDiff.entries()]\n .map(([id, { diff }]) => `<changed-artifact id=\"${id}\">${diff ? `\\n${diff}` : ''}</changed-artifact>`)\n .join('\\n')}\n `,\n };\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport type * as Tool from 'effect/ai/Tool';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\n\nimport { type AiParser, type ConsolePrinter } from '@dxos/ai';\nimport { type Message } from '@dxos/types';\n\n/**\n * Live observer of the generation process.\n */\nexport interface GenerationObserver<\n Tools extends Record<string, Tool.Any> = any,\n> extends AiParser.ParseResponseCallbacks<Tools> {\n /**\n * Complete messages fired during the session, both from the model and from the user.\n * This message will contain all message blocks emitted through the `onBlock` callback.\n */\n onMessage: (message: Message.Message) => Effect.Effect<void>;\n}\n\nexport const GenerationObserver = Object.freeze({\n make: ({\n onBegin = Function.constant(Effect.void),\n onPart = Function.constant(Effect.void),\n onBlock = Function.constant(Effect.void),\n onEnd = Function.constant(Effect.void),\n onMessage = Function.constant(Effect.void),\n }: Partial<GenerationObserver> = {}): GenerationObserver => ({\n onBegin,\n onPart,\n onBlock,\n onEnd,\n onMessage,\n }),\n\n noop: () => GenerationObserver.make(),\n\n /**\n * Debug printer to be used in unit-tests and browser devtools.\n */\n fromPrinter: (printer: ConsolePrinter) =>\n GenerationObserver.make({\n onBlock: (block) =>\n Effect.sync(() => {\n if (block.pending) {\n return; // Only prints full blocks (better for unit-tests and browser devtools).\n }\n printer.printContentBlock(block);\n }),\n onMessage: (message) =>\n Effect.sync(() => {\n if (message.sender.role === 'assistant') {\n return; // Skip assistant messages since they are printed in the `onBlock` callback.\n }\n printer.printMessage(message);\n }),\n }),\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as AiError from 'effect/ai/AiError';\nimport * as LanguageModel from 'effect/ai/LanguageModel';\nimport type * as Toolkit from 'effect/ai/Toolkit';\nimport * as Array from 'effect/Array';\nimport * as Effect from 'effect/Effect';\nimport { pipe } from 'effect/Function';\nimport * as Option from 'effect/Option';\nimport * as Result from 'effect/Result';\nimport * as Schedule from 'effect/Schedule';\nimport * as Semaphore from 'effect/Semaphore';\nimport * as Stream from 'effect/Stream';\n\nimport {\n AiParser,\n AiPreprocessor,\n AiSummarizer,\n type AiToolNotFoundError,\n type OpaqueToolkit,\n type PromptPreprocessingError,\n type ToolExecutionService,\n type ToolResolverService,\n callTool,\n withoutToolCallParsing,\n} from '@dxos/ai';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport * as Operation from '@dxos/compute/Operation';\nimport type * as Skill from '@dxos/compute/Skill';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Database, Obj, Registry } from '@dxos/echo';\nimport { log } from '@dxos/log';\nimport { ContentBlock, Message } from '@dxos/types';\nimport { markWork } from '@dxos/util';\n\nimport { getOperationFromTool } from '../tool-runtime/services.ts';\nimport { type AiAssistantError } from '../util/index.ts';\nimport { formatSystemPrompt, formatUserPrompt } from './format.ts';\nimport { GenerationObserver } from './observer.ts';\n\nexport type RunError = AiError.AiError | PromptPreprocessingError | AiToolNotFoundError | AiAssistantError;\n\n/**\n * An {@link AiError.AiError} raised because the model called a tool the toolkit does not contain.\n * Narrowed with a guard rather than a cast: the recovery reads the reason's fields.\n */\ntype ToolNotFoundError = AiError.AiError & { readonly reason: AiError.ToolNotFoundError };\n\nconst isToolNotFound = (error: unknown): error is ToolNotFoundError =>\n AiError.isAiError(error) && error.reason._tag === 'ToolNotFoundError';\n\n/**\n * How many turns of one request may be spent reporting unresolvable tool calls. A model that keeps\n * calling the same absent tool would otherwise loop until the token budget is gone; past this the\n * error is raised, so a persistent fault still surfaces.\n */\nconst MAX_UNRESOLVED_TOOL_TURNS = 2;\n\n/**\n * An {@link AiError.AiError} the provider raised while authenticating the request.\n * Narrowed with a guard rather than a cast: the retry predicate reads the reason's `kind`.\n */\ntype AuthenticationError = AiError.AiError & { readonly reason: AiError.AuthenticationError };\n\nconst isAuthenticationError = (error: unknown): error is AuthenticationError =>\n AiError.isAiError(error) && error.reason._tag === 'AuthenticationError';\n\n/**\n * Whether the provider rejected the request for permissions that have not yet propagated to the\n * key. Unlike the other authentication kinds (a missing, expired, or invalid key, which need a\n * credential change and never recover on their own), this one clears by itself, so it is the only\n * one worth re-issuing — `AuthenticationError.isRetryable` is `false` for all of them.\n */\nconst isInsufficientPermissions = (error: unknown): boolean =>\n isAuthenticationError(error) && error.reason.kind === 'InsufficientPermissions';\n\n/** Attempts spent re-issuing a request rejected for insufficient permissions, beyond the first. */\nconst INSUFFICIENT_PERMISSIONS_RETRIES = 10;\n\n/** Spaced rather than exponential: a propagation delay is bounded, and ten backed-off waits are not. */\nconst INSUFFICIENT_PERMISSIONS_RETRY_DELAY = '2 seconds';\n\nexport type RunRequirements =\n | LanguageModel.LanguageModel\n | ToolExecutionService\n | ToolResolverService\n | Database.Service\n | Operation.Service\n | Registry.Service\n | Trace.TraceService;\n\nexport type Options = {\n /**\n * Summarize before executing the prompt if the existing history exceeds this threshold.\n */\n summarizationThreshold?: number;\n\n // TODO(dmaretskyi): Plan to phase out in favor of TracingService and the return type being a stream.\n observer?: GenerationObserver;\n /**\n * Callback for when a message is received from the user, model, or tool.\n * This is useful for streaming the output to a queue.\n */\n onOutput?: (message: Message.Message) => Effect.Effect<void, never, never>;\n\n /**\n * When false, turn messages are not appended to the feed or written as persisted trace blocks.\n *\n * @default true\n */\n persist?: boolean;\n};\n\nexport type RunProps<R = never> = {\n prompt: string | ContentBlock.Any[];\n /** Who the prompt is from, when not the session's reader (e.g. one of several people in a shared agent chat). */\n sender?: Message.Message['sender'];\n // TODO(wittjosiah): Rename to systemPrompt.\n system?: string;\n history?: Message.Message[];\n objects?: Obj.Unknown[];\n skills?: readonly Skill.Skill[];\n /** Rendered inline into the system prompt; passed explicitly rather than inferred from `objects`. */\n instructions?: readonly Instructions.Instructions[];\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n};\n\nexport type BeginProps = {\n prompt: string | ContentBlock.Any[];\n sender?: Message.Message['sender'];\n system?: string;\n /** The system prompt already formatted from `system` and the bindings, so it is not formatted twice. */\n systemPrompt?: string;\n history?: Message.Message[];\n objects?: Obj.Unknown[];\n skills?: readonly Skill.Skill[];\n instructions?: readonly Instructions.Instructions[];\n};\n\nexport type TurnProps<R = never> = {\n system: string;\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n};\n\nexport type TurnResult = {\n messages: Message.Message[];\n done: boolean;\n /**\n * Provider finish reason for this turn. `pause` means the provider paused a server-tool turn\n * mid-execution (e.g. Anthropic `pause_turn`) and the turn must be resumed by issuing another\n * request without mutating the trailing assistant content.\n */\n finishReason?: ContentBlock.FinishReason;\n};\n\n/**\n * Contains message history, tools, current context.\n * Current context means the state of the app, time of day, and other contextual information.\n * It makes requests to the model, its a state machine.\n * It keeps track of the current goal.\n * It manages the context window.\n * Tracks the success criteria of reaching the goal, exposing metrics (stretch).\n * Could be run locally in the app or remotely.\n * Could be personal or shared.\n */\nexport class Request {\n /** Prevents concurrent execution of session. */\n private readonly _semaphore = Effect.runSync(Semaphore.make(1));\n\n private readonly _observer: GenerationObserver;\n private readonly _onOutput: (message: Message.Message) => Effect.Effect<void, never, never>;\n\n /** Message history from queue. */\n // TODO(burdon): Evolve into supporting a git-like graph of messages.\n private _history: Message.Message[] = [];\n\n /** Pending messages for this session (incl. the current prompt). */\n private _pending: Message.Message[] = [];\n\n private _started = 0;\n private _ended = 0;\n private _toolCalls = 0;\n /** Turns of this request spent reporting a tool call the toolkit could not resolve. */\n #unresolvedTools = 0;\n\n /** The turn's prompt, sent on the ephemeral channel once the next model call is under way. */\n #announcement: Message.Message | undefined;\n\n constructor(private readonly _options: Options = {}) {\n this._observer = _options.observer ?? GenerationObserver.noop();\n this._onOutput = _options.onOutput ?? (() => Effect.void);\n }\n\n get duration(): number {\n return this._ended - this._started;\n }\n\n get toolCalls(): number {\n return this._toolCalls;\n }\n\n get pending(): readonly Message.Message[] {\n return this._pending;\n }\n\n private _submitMessage = (message: Message.Message): Effect.Effect<Message.Message, never, Trace.TraceService> =>\n Effect.gen({ self: this }, function* () {\n this._pending.push(message);\n yield* this._observer.onMessage(message);\n if (this._options.persist === false) {\n return message;\n }\n for (const block of message.blocks) {\n log('write complete block', {\n messageId: message.id,\n role: message.sender.role!,\n block: JSON.stringify(block),\n });\n yield* Trace.write(Trace.CompleteBlock, {\n messageId: message.id,\n role: message.sender.role!,\n block,\n });\n }\n yield* this._onOutput(message);\n return message;\n });\n\n /**\n * Appends a system-generated note to the turn, addressed to the model: synthetic, so it renders as\n * its own panel rather than as words the reader typed. Used to report a fault the model can act on\n * (an unresolvable tool call) without failing the request.\n */\n submitNotice = (text: string): Effect.Effect<Message.Message, never, Trace.TraceService> =>\n this._submitMessage(\n Obj.make(Message.Message, {\n created: new Date().toISOString(),\n sender: { role: 'user' },\n blocks: [ContentBlock.Text.make({ text, disposition: 'synthetic' })],\n }),\n );\n\n getToolCalls = () =>\n pipe(\n [...this._history, ...this._pending],\n Array.reverse,\n Array.takeWhile((_) => _.sender.role === 'assistant'),\n Array.flatMap((_) => _.blocks.filter(ContentBlock.is('toolCall')).map((block) => ({ block, message: _ }))),\n Array.filter((_) => !_.block.providerExecuted),\n Array.reverse,\n );\n\n /**\n * Initialize a session: set up history, perform summarization if needed, and submit the user prompt.\n * Must be called before `runTurn()`.\n */\n begin = ({\n prompt,\n sender,\n system,\n systemPrompt: formatted,\n history = [],\n skills = [],\n objects = [],\n instructions = [],\n }: BeginProps): Effect.Effect<void, RunError, RunRequirements> =>\n Effect.gen({ self: this }, function* () {\n this._started = Date.now();\n this._history = [...history];\n this._pending = [];\n // Per-run allowance: a reused Request must not inherit a spent budget from the previous run.\n this.#unresolvedTools = 0;\n\n const systemPrompt =\n formatted ?? (yield* formatSystemPrompt({ system, skills, objects, instructions }).pipe(Effect.orDie));\n\n if (this._options.summarizationThreshold !== undefined) {\n const tokenCount = yield* AiPreprocessor.estimateTokens(\n yield* AiPreprocessor.preprocessPrompt([...this._history], {\n system: systemPrompt,\n }),\n );\n if (tokenCount > this._options.summarizationThreshold) {\n // A summarization pass is itself a model round-trip, so it can dominate the wait before\n // the turn the reader asked for even starts.\n yield* Trace.emitRequestPhase('summarizing');\n const summary = yield* AiSummarizer.summarize([...this._history]);\n yield* this._submitMessage(summary);\n }\n }\n\n const userMessage = yield* formatUserPrompt({ prompt, history, sender });\n // Also sent on the ephemeral channel, as the reply's blocks are: the feed shows the prompt only\n // once its index catches up, which can be after the reply has started streaming in.\n this.#announcement = userMessage;\n yield* this._submitMessage(userMessage);\n }).pipe(Effect.withSpan('AiRequest.begin'));\n\n /**\n * Reports a tool call the toolkit could not resolve back to the model, as a turn it can correct.\n *\n * The provider raises this while DECODING its own response — it needs the tool's schema to decode\n * the arguments — so the failure arrives before any tool call reaches the loop, and it kills the\n * whole turn. The usual causes are a skill whose instructions name a tool whose handler this host\n * never contributed, and a model inventing a name; both leave the reader with no reply at all.\n *\n * The tool call itself is lost: the provider discards the event batch it failed in, including the\n * `tool-params-end` the parser needs to complete the block, so nothing dangles in history that\n * would need a matching tool result — a plain note is enough.\n */\n #reportUnresolvedTool = (error: ToolNotFoundError): Effect.Effect<TurnResult, RunError, Trace.TraceService> =>\n Effect.gen({ self: this }, function* () {\n if (++this.#unresolvedTools > MAX_UNRESOLVED_TOOL_TURNS) {\n return yield* Effect.fail(error);\n }\n\n const { toolName, availableTools } = error.reason;\n log.warn('tool not found; reporting to the model', { tool: toolName, available: availableTools });\n yield* this.submitNotice(\n `The tool '${toolName}' does not exist, so nothing was called. ` +\n `The tools you can call are: ${availableTools.join(', ')}. ` +\n 'Continue with those, and say so plainly if the task needs one that is missing.',\n );\n\n return { messages: [], done: false };\n });\n\n /**\n * Execute a single turn: one LLM generation followed by tool execution.\n * The toolkit and system prompt can be updated between turns to reflect context changes (e.g. dynamically enabled skills).\n */\n runAgentTurn = <const R = never>({\n system,\n toolkit: opaqueToolkit,\n }: TurnProps<R>): Effect.Effect<TurnResult, RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n log('request', {\n system: { snippet: createSnippet(system), length: system.length },\n pending: this._pending.length,\n history: this._history.length,\n });\n\n yield* Trace.emitRequestPhase('encoding-prompt');\n const prompt = yield* AiPreprocessor.preprocessPrompt([...this._history, ...this._pending], {\n system,\n cacheControl: 'ephemeral',\n });\n\n const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : undefined;\n markWork('request.prompt-encoded');\n\n const observer = this._observer;\n let currentMessageId: Obj.ID | null = null;\n let finishReason: ContentBlock.FinishReason | undefined;\n\n // v4 overloads `streamText` on the presence of `toolkit`, so the two cases branch explicitly\n // rather than passing a possibly-undefined key.\n const openStream = () =>\n toolkit\n ? LanguageModel.streamText({ prompt, toolkit, disableToolCallResolution: true })\n : LanguageModel.streamText({ prompt, disableToolCallResolution: true });\n\n // Counts attempts at the provider rather than turns: the retry below re-runs the whole\n // collect, so `Stream.unwrap` re-evaluates this on each attempt and the reader sees the\n // request being re-issued instead of an unexplained stall.\n // Forked to run once the call has gone out rather than written before it: rendering the prompt\n // is the reader's page work, so it runs while the provider answers instead of delaying the request.\n // Detached, since the effect that opens the stream returns at once and would take a child with it.\n const announce = Effect.suspend(() => {\n const announcement = this.#announcement;\n this.#announcement = undefined;\n return announcement === undefined\n ? Effect.void\n : Effect.forEach(\n announcement.blocks,\n (block) => Trace.write(Trace.PartialBlock, { messageId: announcement.id, role: 'user', block }),\n { discard: true },\n );\n });\n\n let attempt = 0;\n const stream = Stream.unwrap(\n Effect.gen(function* () {\n yield* Trace.emitRequestPhase('contacting-provider', { attempt: ++attempt });\n yield* Effect.yieldNow.pipe(Effect.andThen(announce), Effect.forkDetach);\n return openStream();\n }),\n );\n\n // Set once any block of this attempt has been submitted, after which the request cannot be\n // re-issued: the messages are already in `_pending` and a second attempt would duplicate them.\n let emitted = false;\n\n const messages = yield* stream.pipe(\n withoutToolCallParsing,\n AiParser.parseResponse({\n emitPartial: true,\n // Tagged chain-of-thought (<cot>/<think>/<reasoning>) becomes reasoning blocks, which the\n // UI renders per view type; flattened to prose it can neither be shown nor hidden.\n parseReasoningTags: true,\n onBegin: () => observer.onBegin(),\n onBlock: (block) => observer.onBlock(block),\n onPart: (part) => observer.onPart(part as any),\n onEnd: (summary) => observer.onEnd(summary),\n }),\n Stream.map((block) => enrichToolCallBlock(block, toolkit)),\n Stream.mapEffect(\n (block) =>\n Effect.gen({ self: this }, function* () {\n // A model that answers before the forked announcement runs must not show its reply\n // ahead of the prompt; a no-op once the prompt has gone out.\n yield* announce;\n if (block._tag === 'stats' && block.finishReason !== undefined) {\n finishReason = block.finishReason;\n }\n if (block.pending) {\n currentMessageId ??= Obj.ID.random();\n log('emit ephemeral message', { id: currentMessageId, type: block._tag });\n yield* Trace.write(Trace.PartialBlock, {\n messageId: currentMessageId,\n role: 'assistant',\n block,\n });\n return Option.none();\n } else {\n currentMessageId ??= Obj.ID.random();\n const id = currentMessageId;\n currentMessageId = null;\n log('emit complete message', { id, type: block._tag });\n emitted = true;\n const message = Obj.make(Message.Message, {\n id,\n created: new Date().toISOString(),\n sender: { role: 'assistant' },\n blocks: [block],\n });\n return Option.some(yield* this._submitMessage(message));\n }\n }),\n { concurrency: 1, unordered: false },\n ),\n Stream.filterMap((value) => (Option.isSome(value) ? Result.succeed(value.value) : Result.failVoid)),\n Stream.runCollect,\n Effect.retry({\n schedule: Schedule.spaced(INSUFFICIENT_PERMISSIONS_RETRY_DELAY).pipe(\n Schedule.jittered,\n Schedule.upTo({ times: INSUFFICIENT_PERMISSIONS_RETRIES }),\n ),\n while: (error) => {\n if (emitted || !isInsufficientPermissions(error)) {\n return false;\n }\n log.warn('insufficient permissions; retrying request', { error });\n return true;\n },\n }),\n );\n log('messages', { messages });\n\n // A paused server-tool turn (e.g. Anthropic `pause_turn`) is not complete: the provider\n // expects the assistant content to be resent so it can finish executing the server tool.\n // No local tool execution is needed — just another request.\n if (finishReason === 'pause') {\n return { messages, done: false, finishReason };\n }\n\n const toolCalls = this.getToolCalls();\n\n if (toolCalls.length === 0) {\n this._ended = Date.now();\n return { messages, done: true, finishReason };\n } else if (!toolkit) {\n throw new Error('No toolkit provided');\n }\n\n return { messages, done: false, finishReason };\n }).pipe(\n Effect.catchIf(isToolNotFound, (error) => this.#reportUnresolvedTool(error)),\n Effect.withSpan('AiRequest.runAgentTurn'),\n );\n\n runTools = <const R = never>({\n toolkit: opaqueToolkit,\n }: {\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n }): Effect.Effect<void, RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : undefined;\n markWork('request.tools-begin');\n const toolCalls = this.getToolCalls();\n // A turn can end with no calls to run — a turn recovered from an unresolvable tool call leaves\n // none. Submitting anyway would append a tool message with no blocks, which the provider\n // rejects as empty content.\n if (toolCalls.length === 0) {\n return;\n }\n const toolResults = yield* Effect.forEach(toolCalls, ({ block, message }) =>\n Effect.gen(function* () {\n if (!toolkit) {\n throw new Error('No toolkit provided');\n }\n // Tool execution is where an agentic turn spends most of its time, and it produces no\n // streamed content, so the tool's name is the only progress the reader has.\n yield* Trace.emitRequestPhase('calling-tool', { detail: block.name });\n return yield* callTool(toolkit, block);\n }),\n );\n markWork('request.tools-called');\n\n yield* this._submitMessage(\n Obj.make(Message.Message, {\n created: new Date().toISOString(),\n sender: { role: 'tool' },\n blocks: toolResults,\n }),\n );\n\n this._toolCalls += toolResults.length;\n }).pipe(Effect.withSpan('AiRequest.runTools'));\n\n /**\n * Run a full conversation turn loop. Equivalent to calling `begin()` then `runTurn()` in a loop.\n */\n run = <const R = never>({\n prompt,\n sender,\n system: systemTemplate,\n history = [],\n objects = [],\n skills = [],\n instructions = [],\n toolkit,\n }: RunProps<R>): Effect.Effect<Message.Message[], RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n const system = yield* formatSystemPrompt({ system: systemTemplate, skills, objects, instructions }).pipe(\n Effect.orDie,\n );\n yield* this.begin({\n prompt,\n sender,\n system: systemTemplate,\n systemPrompt: system,\n history,\n objects,\n skills,\n instructions,\n });\n\n do {\n const { done, finishReason } = yield* this.runAgentTurn({ system, toolkit });\n if (done) {\n break;\n }\n // A paused server-tool turn resumes with another request and no local tool execution.\n if (finishReason === 'pause') {\n continue;\n }\n yield* this.runTools({ toolkit });\n } while (true);\n\n log('done', { pending: this._pending.length, duration: this.duration, tools: this._toolCalls });\n return this._pending;\n }).pipe(this._semaphore.withPermits(1), Effect.withSpan('AiRequest.run'));\n}\n\n/**\n * Annotates `toolCall` blocks with metadata about the backing Operation, when one exists.\n * Tool calls that resolve to a toolkit handler (no Operation) are left unchanged so callers can\n * distinguish operation invocations from inline tool calls.\n */\nconst enrichToolCallBlock = (\n block: ContentBlock.Any,\n toolkit: Toolkit.WithHandler<any> | undefined,\n): ContentBlock.Any => {\n if (block._tag !== 'toolCall' || !toolkit) {\n return block;\n }\n const tool = toolkit.tools[block.name];\n if (!tool) {\n return block;\n }\n // Some tools (provider-defined, raw MCP) don't carry an Effect `Context` for annotations and\n // `getOperationFromTool` throws. Be defensive: treat any failure as \"no operation\".\n let operationOpt: Option.Option<Operation.Definition.Any>;\n try {\n operationOpt = getOperationFromTool(tool);\n } catch {\n return block;\n }\n if (Option.isNone(operationOpt)) {\n return block;\n }\n const { meta } = operationOpt.value;\n return {\n ...block,\n operationKey: meta.key,\n operationName: meta.name,\n operationIcon: meta.icon,\n } satisfies ContentBlock.ToolCall;\n};\n\nconst createSnippet = (text: string, len = 32) =>\n text.length <= len * 2 ? text : [text.slice(0, len), '...', text.slice(-len)].join('');\n\n// TODO(dmaretskyi): Extract as a general util.\nconst logDump = (message: string, data: unknown) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { writeFileSync } = require('node:fs');\n const path = `/tmp/log-data-${Date.now()}.json`;\n writeFileSync(path, JSON.stringify(data, null, 2));\n log.info(message, { path });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-AiRequest.mjs","names":["EID","__dxlog_file","__dxlog_file","Array"],"sources":["../../src/util/artifact.ts","../../src/tool-runtime/services.ts","../../src/request/artifact-diff.ts","../../src/request/format.ts","../../src/request/observer.ts","../../src/request/AiRequest.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport type * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\nimport * as SchemaTransformation from 'effect/SchemaTransformation';\n\nimport { Database, type Error as EchoError, Obj, Ref, type Type } from '@dxos/echo';\nimport { EncodedReference } from '@dxos/echo-protocol';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\nimport { EID, EntityId, SpaceId } from '@dxos/keys';\nimport { trim } from '@dxos/util';\n\n/**\n * @deprecated\n */\nexport const createArtifactElement = (id: EntityId) => `<artifact id=${id} />`;\n\n/**\n * A model-friendly way to reference an object.\n * Supports vairous formats that will be normalized to a DXN.\n *\n * @internal\n */\nconst ArtifactURI: Schema.Schema<string> & {\n toEchoURI: (reference: ArtifactURI, owningSpaceId?: SpaceId) => EID.EID;\n resolve: <S extends Type.AnyEntity>(\n schema: S,\n ref: ArtifactURI,\n ) => Effect.Effect<Type.InstanceType<S>, EchoError.EntityNotFoundError, Database.Service>;\n} = class extends Schema.String.annotate({\n // TODO(dmaretskyi): This section gets overriden.\n description: trim`\n The URI of the referenced object. Protocols accepted:\n - echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4. \n - space ID, object ID tuple (spaceID:objectID)\n - Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)\n - dxn:org.dxos.type.calendar:0.1.0 -- named entity from the registry. Absoutely make sure it actually exists in the registry.\n\n URIs can be prepended with an @ symbol for compatibility with in-text references.\n `,\n examples: [\n 'echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n '@echo://01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n 'BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n '01KG7R1ZXWFMWQ4DA1Q6TN1DG4',\n 'dxn:org.dxos.type.calendar:0.1.0',\n '@dxn:org.dxos.type.calendar:0.1.0',\n ],\n}) {\n static toEchoURI(reference: ArtifactURI, owningSpaceId?: SpaceId): EID.EID {\n // Allow @ prefix for compatibility with in-text references.\n const stripped = reference.startsWith('@') ? reference.slice(1) : reference;\n if (stripped.startsWith('echo:') || stripped.startsWith('dxn:')) {\n return EID.parse(stripped);\n } else if (stripped.includes(':')) {\n const [spaceId, objectId] = stripped.split(':');\n if (!SpaceId.isValid(spaceId) || !EntityId.isValid(objectId)) {\n throw new Error(`Unable to parse object reference: ${reference}`);\n }\n // This is a workaround because the current Filter API doesn't work with fully qualified Echo URIs.\n // We check if the space ID is the same as the owning space and then use LOCAL_SPACE_TAG for local references.\n // TODO(dmaretskyi): Fix this in the Echo and Filter API to properly handle fully qualified URIs.\n return spaceId === owningSpaceId\n ? EID.make({ entityId: objectId })\n : EID.make({ spaceId: spaceId, entityId: objectId });\n } else if (EntityId.isValid(stripped)) {\n return EID.make({ entityId: stripped });\n } else {\n throw new Error(`Unable to parse object reference: ${reference}`);\n }\n }\n\n /**\n * Resolves an artifact ID to an object.\n */\n static resolve<S extends Type.AnyEntity>(\n schema: S,\n ref: ArtifactURI,\n ): Effect.Effect<Type.InstanceType<S>, EchoError.EntityNotFoundError, Database.Service> {\n const uri = ArtifactURI.toEchoURI(ref);\n return Database.resolve(Ref.fromURI(uri), schema);\n }\n};\n\ntype ArtifactURI = Schema.Schema.Type<typeof ArtifactURI>;\n\n/**\n * Schema that decodes ECHO reference object from an LLM-friendly input.\n */\nexport const RefFromLLM = ArtifactURI.pipe(\n Schema.decodeTo(\n Ref.Ref(Obj.Unknown),\n SchemaTransformation.transform({\n decode: (fromA) => {\n const eid = ArtifactURI.toEchoURI(fromA);\n // Normalize to local form: strip any space authority so the ref resolves within the current\n // space context. The AI commonly sends echo://SPACE/ENTITY format (mirroring what it sees in\n // the database context), but cross-space resolution is not yet supported and the space ID\n // encoded in the URI is always the current space anyway.\n return EncodedReference.fromURI(EID.toLocal(eid));\n },\n encode: (toI) => EncodedReference.toURI(toI) as never,\n }),\n ),\n).annotate({\n description: SchemaAST.getDescriptionAnnotation(ArtifactURI.ast) as string,\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Tool from 'effect/ai/Tool';\nimport type * as Toolkit from 'effect/ai/Toolkit';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport type * as JsonSchema from 'effect/JsonSchema';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Record from 'effect/Record';\nimport * as Schema from 'effect/Schema';\nimport type * as SchemaRepresentation from 'effect/SchemaRepresentation';\nimport * as Stream from 'effect/Stream';\n\nimport { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from '@dxos/ai';\nimport { OpaqueToolkit } from '@dxos/ai';\nimport * as Operation from '@dxos/compute/Operation';\nimport { todo } from '@dxos/debug';\nimport { Filter, Ref, Registry } from '@dxos/echo';\nimport * as EchoJsonSchema from '@dxos/echo/JsonSchema';\nimport * as SchemaAST from '@dxos/effect/SchemaAST';\nimport * as SchemaEx from '@dxos/effect/SchemaEx';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\n\nimport { RefFromLLM } from '../util/index.ts';\n\nexport const makeToolResolverFromOperations = <R = never>({\n toolkit: extraToolkit = OpaqueToolkit.empty,\n}: { toolkit?: OpaqueToolkit.OpaqueToolkit<never, never, R> } = {}): Layer.Layer<\n ToolResolverService,\n never,\n OpaqueToolkit.OpaqueToolkitProvider | Registry.Service | R\n> => {\n // `Layer.effect` runs construction in the layer's own scope, so the subscription below is released\n // with the layer.\n return Layer.effect(\n ToolResolverService,\n Effect.gen(function* () {\n const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;\n const registry = yield* Registry.Service;\n\n // Tool names derive from operation keys lossily (`Operation.toolNameFromKey`), so a record is\n // matched by re-deriving its name rather than by reconstructing a key — which rules out the\n // indexed key lookup and would otherwise mean scanning every operation per resolution.\n let index: Map<string, Operation.PersistentOperation[]> | undefined;\n // Dropped on any registry change rather than only on a lookup miss: a hit against a stale index\n // would resolve a name whose second claimant registered after the build, silently picking one\n // of two operations instead of reporting the ambiguity below. The counter carries that signal\n // across the query's own await, where clearing `index` alone would be undone by the assignment\n // of a snapshot the change is already missing from.\n let generation = 0;\n yield* Effect.acquireRelease(\n Effect.sync(() =>\n registry.changed.on(() => {\n index = undefined;\n generation++;\n }),\n ),\n (unsubscribe) => Effect.sync(() => unsubscribe()),\n );\n const buildIndex = Effect.fn('buildToolNameIndex')(function* () {\n // Retries rather than caching a snapshot the registry has already moved past; a change during\n // the query is rare and finite, so this settles on the first quiet pass.\n while (true) {\n const seen = generation;\n const records = yield* Effect.promise(() => registry.query(Filter.type(Operation.PersistentOperation)).run());\n const built = new Map<string, Operation.PersistentOperation[]>();\n for (const record of records) {\n const key = Operation.getKey(record);\n if (key == null) {\n continue;\n }\n // Non-throwing: a record's key is untrusted, and one that cannot derive a valid name must\n // cost only its own tool rather than every registry-backed tool in the index.\n const name = Operation.tryToolNameFromKey(key);\n if (name == null) {\n log.warn('operation key cannot derive a valid tool name; not indexed', { key });\n continue;\n }\n built.set(name, [...(built.get(name) ?? []), record]);\n }\n if (generation === seen) {\n index = built;\n return built;\n }\n }\n });\n\n return {\n resolve: (id): Effect.Effect<Tool.Any, AiToolNotFoundError> =>\n Effect.gen(function* () {\n const toolkit = OpaqueToolkit.merge(extraToolkit, yield* toolkitProvider.getToolkit());\n\n const tool = toolkit.toolkit.tools[id];\n if (tool) {\n return tool;\n }\n\n // A miss is conclusive: any registration since the build dropped the index above, so a\n // rebuild here would only re-scan the registry for every id that is not an operation.\n const matches = (index ?? (yield* buildIndex())).get(id);\n if (matches == null) {\n return yield* Effect.fail(new AiToolNotFoundError(id));\n }\n // Two keys deriving one name (`webSearch` vs `web-search`) is an authoring error. Picking\n // the first match would silently shadow the other, so it fails here instead — the only\n // place both keys are visible at once (a ToolId no longer distinguishes them).\n invariant(\n matches.length === 1,\n `Tool name \"${id}\" is claimed by ${matches.length} operations: ${matches\n .map((record) => Operation.getKey(record))\n .join(', ')}`,\n );\n // A registry record is untrusted input: its persisted schema may be one the projection\n // cannot express as tool parameters. Throwing here would surface as a defect and fail\n // the whole request, so one unprojectable operation is logged and dropped instead —\n // `resolveToolkit` filters a not-found tool out and the agent runs with the rest.\n try {\n return projectFunctionToTool(Operation.deserialize(matches[0]));\n } catch (err) {\n log.error('operation cannot be projected to a tool; excluded from context', {\n id,\n key: Operation.getKey(matches[0]),\n err,\n });\n return yield* Effect.fail(new AiToolNotFoundError(id));\n }\n }),\n } satisfies Context.Service.Shape<typeof ToolResolverService>;\n }),\n );\n};\n\nexport const makeToolExecutionService = <E, R>(opts: {\n invoke: (tool: Tool.Any, input: unknown) => Effect.Effect<unknown>;\n}): Layer.Layer<ToolExecutionService, never, OpaqueToolkit.OpaqueToolkitProvider> =>\n Layer.effect(\n ToolExecutionService,\n Effect.gen(function* () {\n const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;\n const toolkit = yield* toolkitProvider.getToolkit();\n\n const toolkitHandler = yield* toolkit.toolkit.pipe(Effect.provide(toolkit.layer));\n if (!isHandlerLike(toolkitHandler)) {\n return yield* Effect.die(new Error('Toolkit produced a handler-less toolkit instance'));\n }\n\n return {\n handlersFor: (toolkit) => {\n const makeHandler = (tool: Tool.Any): ((params: unknown) => Effect.Effect<unknown, any, any>) => {\n return Effect.fn(`toolFunctionHandler ${tool.name}`)(function* (input: unknown) {\n if (toolkitHandler.tools[tool.name]) {\n if (Tool.isProviderDefined(tool)) {\n throw new Error('Attempted to call a provider-defined tool');\n }\n\n // `handle` streams preliminary results before the final one; the toolkit\n // interrupts the handler fiber once its effect settles, so the final result is\n // always the last item emitted — `Stream.runLast` recovers exactly that value.\n const resultStream = yield* toolkitHandler.handle(tool.name, input);\n const lastResult = yield* Stream.runLast(resultStream);\n const handlerResult = yield* Option.match(lastResult, {\n onNone: () => Effect.die(new Error(`Tool \"${tool.name}\" handler produced no result`)),\n onSome: Effect.succeed,\n });\n return handlerResult.result;\n }\n\n // Every `invoke` implementation (in-process, or a child process spawned by the agent)\n // funnels through here, so the projected schema is applied once, at the boundary where\n // the model's raw arguments arrive. A malformed argument dies and is caught just below,\n // reaching the model as an error it can retry from.\n const decoded = yield* decodeToolParameters(tool, input).pipe(Effect.orDie);\n return yield* opts.invoke(tool, decoded).pipe(Effect.catchDefect((defect) => Effect.fail(defect)));\n });\n };\n\n return toolkit.of(\n // Operations project to dynamic tools (see `projectFunctionToTool`), which are handled here\n // just like user-defined ones; only provider-executed tools have no local handler.\n Record.map(toolkit.tools, (tool, _name) =>\n Tool.isUserDefined(tool) || Tool.isDynamic(tool) ? makeHandler(tool) : null,\n ) as any,\n ) as any;\n },\n };\n }),\n );\n\nexport const makeToolExecutionServiceFromOperationInvoker = (): Layer.Layer<\n ToolExecutionService,\n never,\n Operation.Service | OpaqueToolkit.OpaqueToolkitProvider\n> => {\n return Layer.unwrap(\n Effect.gen(function* () {\n const operationInvoker = yield* Operation.Service;\n\n return makeToolExecutionService({\n invoke: (tool, input) =>\n Effect.gen(function* () {\n const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);\n\n return yield* operationInvoker.invoke(operationDef, input).pipe(Effect.orDie);\n }),\n });\n }),\n );\n};\n\nexport const ToolExecutionServices = Layer.mergeAll(\n makeToolResolverFromOperations(),\n makeToolExecutionServiceFromOperationInvoker(),\n);\n\nclass FunctionToolAnnotation extends Context.Service<\n FunctionToolAnnotation,\n { definition: Operation.Definition.Any; parameters: Schema.Codec<unknown, unknown> }\n>()('@dxos/assistant/FunctionToolAnnotation') {}\n\nexport const getOperationFromTool = (tool: Tool.Any): Option.Option<Operation.Definition.Any> => {\n return Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option.map(({ definition }) => definition));\n};\n\n/**\n * Decodes a model's raw tool arguments against the projected parameter schema.\n *\n * A dynamic tool carries a JSON Schema, so `Toolkit.handle` passes the arguments through unvalidated\n * (that is what lets us state the schema the model is actually held to — see `projectFunctionToTool`).\n * Decoding here keeps the coercions the projection introduces, above all a ref supplied as a URI\n * string becoming a `Ref`, and surfaces a malformed argument to the model as a tool error.\n */\nexport const decodeToolParameters = (tool: Tool.Any, input: unknown): Effect.Effect<unknown, Schema.SchemaError> =>\n Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(\n Option.match({\n onNone: () => Effect.succeed(input),\n onSome: ({ parameters }) => Schema.decodeUnknownEffect(parameters)(input),\n }),\n );\n\nconst toolCache = new WeakMap<Operation.Definition.Any, Tool.Any>();\n\n/**\n * Parameter schema for an operation that takes no input.\n *\n * Spelled as a record with an uninhabited value type rather than `Schema.Struct({})` because v4\n * emits an empty struct as `{not: {type: 'null'}}` — the bare `object` keyword, which states\n * neither `type` nor `additionalProperties`. A provider's strict tool mode rejects that outright\n * (\"For 'object' type, 'additionalProperties' must be explicitly set to false\"), and one bad tool\n * fails the whole request. A `Never` value type admits no keys, so this emits\n * `{type: 'object', additionalProperties: false}` — the same shape, and valid under strict.\n * A JSON-schema annotation cannot express this: v4 honours such an annotation only on a check.\n */\nconst EMPTY_PARAMETERS_SCHEMA = Schema.Record(Schema.String, Schema.Never);\n\n/**\n * Projects an `Operation.Definition` into an `AiTool`.\n * Exported for testing.\n */\nexport const projectFunctionToTool = (fn: Operation.Definition.Any): Tool.Any => {\n if (toolCache.has(fn)) {\n return toolCache.get(fn)!;\n }\n\n const fields = createStructFieldsFromSchema(fn.input);\n const parametersSchema = Object.keys(fields).length === 0 ? EMPTY_PARAMETERS_SCHEMA : Schema.Struct(fields);\n const tool = Tool.dynamic(Operation.toolName(fn), {\n description: fn.meta.description,\n // A dynamic tool's JSON Schema is passed to the provider verbatim (`Tool.getJsonSchema` returns\n // it before any transformer runs), which is the only way to keep what the model is told and what\n // we validate in agreement. Handed an Effect schema instead, rc.108 describes the tool through\n // the provider's structured-output codec while `Toolkit.handle` validates against the\n // untransformed schema, so a compliant model is always rejected: a record is advertised as an\n // array of `[key, value]` pairs but validated as an object, and an optional key is advertised as\n // nullable-and-required but validated as absent-or-`T`.\n parameters: toModelJsonSchema(parametersSchema),\n failure: Schema.Any,\n })\n .annotate(FunctionToolAnnotation, { definition: fn, parameters: parametersSchema })\n // The schema above states open records and omissible optional keys, which a provider's strict tool\n // mode forbids (it demands `additionalProperties: false` and every key in `required`). Operations\n // accept arbitrary input, so strict is off for all of them rather than per-operation — one\n // non-conforming tool rejects the whole request.\n .annotate(Tool.Strict, false);\n toolCache.set(fn, tool);\n return tool;\n};\n\n/**\n * Keeps refs inline rather than hoisting them into `$defs`.\n *\n * The default policy extracts anything carrying an `identifier`, and `Ref` carries one so its\n * rejection messages can name the target type. Without this a ref parameter reaches the model as a\n * `$ref` into `$defs` instead of the described URI string it used to be. Only refs are declined, so\n * a recursive parameter keeps the name it was recorded under.\n */\nconst REFERENCES_INLINE = {\n referencePolicy: ({ identifier }: SchemaRepresentation.ReferencePolicyInput) =>\n Ref.isRefIdentifier(identifier) ? undefined : identifier,\n} as const;\n\n/**\n * Emits the JSON Schema the model is shown for a tool's parameters.\n *\n * v4 renders `Schema.optional(T)` as `anyOf: [T, null]` while its decoder accepts an absent key but\n * rejects `null`, so a model that takes the schema at its word is rejected. The null branch of an\n * optional property is therefore dropped, stating the property as the bare type and leaving it out of\n * `required` — the same contract ECHO's own emitter keeps (`stripUndefinedMember`) and the one the\n * pre-migration corpus was recorded against.\n */\nconst toModelJsonSchema = (schema: Schema.Codec<unknown, unknown>): JsonSchema.JsonSchema => {\n // A recursive parameter renders as `$ref: '#/$defs/…'` with the bodies in a separate `definitions`\n // record; keeping only the root would advertise a dangling reference to the model.\n // Closed structs, as the recorded corpus states them; Effect's default leaves them open.\n const { schema: root, definitions } = Schema.toJsonSchemaDocument(schema, {\n ...REFERENCES_INLINE,\n onExcessProperty: 'error',\n });\n const document = Object.keys(definitions).length > 0 ? { ...root, $defs: definitions } : root;\n // Folded first: once openness sets `additionalProperties`, the fold skips the node.\n return statePropertyOpenness(\n EchoJsonSchema.foldRestSignatures(dropNullBranches(document, new Set(asStringArray(schema)))),\n );\n};\n\n/**\n * States openness on every object node that leaves it implied.\n *\n * v4 omits `additionalProperties` when a record's value type is unconstrained, so an open property bag\n * reads as \"some object\". Absent, it also fails a provider's strict tool mode, which requires the key\n * to be present. `true` says what the record means and what the pre-migration corpus stated.\n */\nconst statePropertyOpenness = (node: JsonSchema.JsonSchema): JsonSchema.JsonSchema => {\n if (Array.isArray(node)) {\n // `JsonSchema.JsonSchema` (effect's own type) is a bare index signature that structurally\n // excludes arrays, even though a JSON Schema document legitimately nests them (e.g. `anyOf`).\n return node.map(statePropertyOpenness) as unknown as JsonSchema.JsonSchema;\n }\n if (typeof node !== 'object' || node === null) {\n return node;\n }\n const mapped = Object.fromEntries(\n Object.entries(node).map(([key, value]) => [\n key,\n typeof value === 'object' && value !== null ? statePropertyOpenness(value as JsonSchema.JsonSchema) : value,\n ]),\n );\n return mapped.type === 'object' && !('additionalProperties' in mapped)\n ? { ...mapped, additionalProperties: true }\n : mapped;\n};\n\n/** Property names the schema marks required; only optional properties carry the spurious null branch. */\nconst asStringArray = (schema: Schema.Codec<unknown, unknown>): readonly string[] => {\n const { required } = Schema.toJsonSchemaDocument(schema, REFERENCES_INLINE).schema;\n return Array.isArray(required) ? required.map(String) : [];\n};\n\nconst dropNullBranches = (node: JsonSchema.JsonSchema, required: ReadonlySet<string>): JsonSchema.JsonSchema => {\n const properties = node.properties;\n if (typeof properties !== 'object' || properties === null) {\n return node;\n }\n return {\n ...node,\n properties: Object.fromEntries(\n Object.entries(properties).map(([name, value]) => [name, required.has(name) ? value : withoutNull(value)]),\n ),\n };\n};\n\n/** Collapses `anyOf: [T, null]` (nested arbitrarily deep by a non-idempotent `optional`) down to `T`. */\nconst withoutNull = (value: unknown): unknown => {\n if (typeof value !== 'object' || value === null || !('anyOf' in value)) {\n return value;\n }\n const { anyOf, ...rest } = value as { anyOf: unknown[] };\n const branches = anyOf.filter(\n (branch) => !(typeof branch === 'object' && branch !== null && (branch as { type?: unknown }).type === 'null'),\n );\n if (branches.length !== 1) {\n return { ...rest, anyOf: branches.map(withoutNull) };\n }\n const only = withoutNull(branches[0]);\n return typeof only === 'object' && only !== null ? { ...rest, ...only } : only;\n};\n\n// TODO(dmaretskyi): Factor out.\n/**\n * Projects an operation input struct into the LLM-facing tool parameter fields, mapping each\n * property through {@link mapSchemaTypeForLLM} (so refs become the model-friendly `RefFromLLM`).\n * Exported for testing.\n */\nexport const createStructFieldsFromSchema = (\n schema: Schema.Codec<unknown, unknown>,\n): Record<string, Schema.Codec<unknown, unknown>> => {\n switch (schema.ast._tag) {\n case 'Objects': {\n // One cache for the whole struct: sibling properties may reach the same recursive body.\n const expansions = new Map<SchemaAST.AST, SchemaAST.AST>();\n return Object.fromEntries(\n schema.ast.propertySignatures.map((prop) => [\n prop.name,\n Schema.make(mapSchemaTypeForLLM(prop.type, expansions)),\n ]),\n );\n }\n // All three spell \"this operation takes no input\". `Void` is the authored form; the other two\n // are what it degrades to across `Operation.serialize`/`deserialize`, which routes the schema\n // through JSON Schema: `Schema.Void` emits `{type: 'null'}` and reads back as `Null`, and an\n // operation persisted with no `inputSchema` at all reads back as `Schema.Unknown`.\n case 'Void':\n case 'Null':\n case 'Unknown':\n return {};\n default:\n return todo(`Unsupported schema AST: ${schema.ast._tag}`);\n }\n};\n\n/**\n * Picks an LLM-friendly schema type for the given schema AST.\n * The picked schema type decodes to the original schema type.\n */\nconst mapSchemaTypeForLLM = (\n ast: SchemaAST.AST,\n expansions: Map<SchemaAST.AST, SchemaAST.AST> = new Map(),\n): SchemaAST.AST => {\n if (Ref.isRefType(ast)) {\n // v4's element and property nodes carry their own modifiers, so the wrapper types are gone and\n // the annotations record is optional.\n const own = SchemaAST.resolveAnnotations(ast)?.description;\n const fallback = SchemaAST.resolveAnnotations(RefFromLLM.ast)?.description as string;\n const description = own ? `${own}\\n${fallback}` : fallback;\n // `optionalKey` puts the modifier on the ref node itself, so the rewrite must carry it across.\n return SchemaEx.retainContext(ast, RefFromLLM.annotate({ description }).ast);\n }\n\n // `mapAst` evaluates a suspended body eagerly, so a recursive schema would walk its own cycle\n // forever. Memoizing by body identity and rebuilding the suspend lazily closes the cycle: the\n // re-entrant call returns this very node instead of expanding the body again.\n if (SchemaAST.isSuspend(ast)) {\n const body = ast.thunk();\n let mapped = expansions.get(body);\n if (!mapped) {\n let expanded: SchemaAST.AST | undefined;\n mapped = new SchemaAST.Suspend(\n () => (expanded ??= mapSchemaTypeForLLM(body, expansions)),\n ast.annotations,\n undefined,\n ast.encoding,\n ast.context,\n );\n expansions.set(body, mapped);\n }\n return mapped;\n }\n\n // `mapAst` carries annotations, checks, encoding and `context` across every rebuilt node; in v4\n // optionality rides on `context`, so rebuilding by hand silently makes an optional key required.\n return SchemaEx.mapAst(ast, (child) => mapSchemaTypeForLLM(child, expansions));\n};\n\n/** Narrows `value` to {@link Toolkit.WithHandler} when it carries a non-null `tools` object and a `handle` function. */\nexport const isHandlerLike = (value: unknown): value is Toolkit.WithHandler<Record<string, Tool.Any>> => {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'tools' in value &&\n typeof value.tools === 'object' &&\n value.tools !== null &&\n 'handle' in value &&\n typeof value.handle === 'function'\n );\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Context from 'effect/Context';\n\nimport { type Obj } from '@dxos/echo';\nimport { type EntityId } from '@dxos/keys';\n\n/**\n * Resolves artifact ids to their versions.\n * Used to give the model a sense of the changes to the artifacts made by users during the conversation.\n * The artifacts versions are pinned in the history, and whenever the artifact changes in-between assistant's steps,\n * a diff is inserted into the conversation.\n *\n * Can be optionally provided to the session run call.\n */\n// TODO(dmaretskyi): Convert to Context.Reference\nexport class ArtifactDiffResolver extends Context.Service<ArtifactDiffResolver, ArtifactDiffResolver.Service>()(\n '@dxos/assistant/ArtifactDiffResolver',\n) {}\n\nexport namespace ArtifactDiffResolver {\n export type Service = {\n resolve: (\n artifacts: {\n id: EntityId;\n lastVersion: Obj.Version;\n }[],\n ) => Promise<\n Map<\n EntityId,\n {\n version: Obj.Version;\n diff?: string;\n }\n >\n >;\n };\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\nimport * as Option from 'effect/Option';\n\nimport type * as Operation from '@dxos/compute/Operation';\nimport * as Template from '@dxos/compute/Template';\nimport { Database, type Error, Obj, type Registry } from '@dxos/echo';\nimport { type EntityId } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { type ContentBlock, Message } from '@dxos/types';\nimport { trim } from '@dxos/util';\n\nimport { AiAssistantError } from '../util/index.ts';\nimport type * as AiRequest from './AiRequest.ts';\nimport { ArtifactDiffResolver } from './artifact-diff.ts';\n\n/**\n * Formats the system prompt.\n */\n// TODO(burdon): Move to AiPreprocessor.\nexport const formatSystemPrompt = ({\n system,\n skills = [],\n objects = [],\n instructions = [],\n}: Pick<AiRequest.RunProps, 'system' | 'skills' | 'objects' | 'instructions'>): Effect.Effect<\n string,\n Operation.FunctionNotFoundError | Error.EntityNotFoundError,\n Database.Service | Registry.Service | Operation.Service\n> =>\n Effect.gen(function* () {\n const skillDefs = yield* Function.pipe(\n skills,\n Effect.forEach((skill) => Effect.succeed(skill.instructions)),\n Effect.flatMap(\n Effect.forEach((template) =>\n Effect.gen(function* () {\n return trim`\n <skill>\n ${yield* Template.processTemplate(template)}\n </skill>\n `;\n }),\n ),\n ),\n Effect.map((skills) => (skills.length > 0 ? ['## Skills Definitions', ...skills].join('\\n\\n') : undefined)),\n );\n\n // Instructions steer the session, so their text (and sentinel commands) belongs in the system\n // prompt itself — a context-object stub would force the model to tool-load them and follow nothing.\n const instructionDefs = yield* Function.pipe(\n instructions,\n Effect.forEach((instructions) =>\n Effect.gen(function* () {\n // A broken text ref degrades to commands-only rather than failing the whole prompt.\n const text = yield* Database.load(instructions.text).pipe(\n Effect.map((doc) => doc.content.trim()),\n Effect.catchTag('EntityNotFoundError', () => Effect.succeed('')),\n );\n const commands = (instructions.commands ?? []).map(\n ({ sentinel, description, prompt }) =>\n `- \\`${sentinel}\\`${description ? ` (${description})` : ''}: ${prompt}`,\n );\n const parts = [text];\n if (commands.length > 0) {\n parts.push(\n ['When the user message contains one of these sentinel commands, follow its prompt:', ...commands].join(\n '\\n',\n ),\n );\n }\n return `<instructions>\\n${parts.filter(Boolean).join('\\n\\n')}\\n</instructions>`;\n }),\n ),\n Effect.map((defs) => (defs.length > 0 ? ['## Instructions', ...defs].join('\\n\\n') : undefined)),\n );\n\n const objectDefs = yield* Function.pipe(\n objects,\n Effect.forEach((object) => {\n // Carry the label so the model only tool-loads an object when it needs the contents,\n // not just to learn what the reference is.\n const label = Obj.getLabel(object);\n return Effect.succeed(\n trim`\n <object>\n <dxn>${Obj.getURI(object)}</dxn>\n <typename>${Obj.getTypename(object)}</typename>${label ? `\\n <label>${label}</label>` : ''}\n </object>\n `,\n );\n }),\n Effect.map((objects) => (objects.length > 0 ? ['## Context Objects', ...objects].join('\\n\\n') : undefined)),\n );\n\n return yield* Function.pipe(\n Effect.succeed(\n [system, instructionDefs, skillDefs, objectDefs].filter((def): def is string => def !== undefined),\n ),\n Effect.map((parts) => parts.join('\\n\\n')),\n );\n }).pipe(Effect.withSpan('formatSystemPrompt'));\n\n/**\n * Formats the user prompt.\n */\n// TODO(burdon): Move to AiPreprocessor.\n// TODO(burdon): Convert util below to `Effect.fn` (to preserve stack info)\nexport const formatUserPrompt = ({\n prompt,\n history = [],\n sender,\n}: Pick<AiRequest.RunProps, 'prompt' | 'history' | 'sender'>): Effect.Effect<Message.Message, AiRequest.RunError> =>\n Effect.gen(function* () {\n const blocks: ContentBlock.Any[] = [];\n\n // TODO(dmaretskyi): Evaluate other approaches as `serviceOption` isn't represented in the type system.\n const artifactDiffResolver = yield* Effect.serviceOption(ArtifactDiffResolver);\n if (Option.isSome(artifactDiffResolver)) {\n const versions = gatherObjectVersions(history);\n const artifactDiff = yield* Effect.tryPromise({\n try: () =>\n artifactDiffResolver.value.resolve(\n [...versions.entries()].map(([id, version]) => ({ id, lastVersion: version })),\n ),\n catch: AiAssistantError.wrap({ message: 'Artifact diff resolution error' }),\n });\n\n log('version', { artifactDiff, versions });\n for (const [id, { version }] of [...artifactDiff.entries()]) {\n const lastVersion = versions.get(id);\n if (lastVersion && Obj.compareVersions(version, lastVersion) === 'equal') {\n artifactDiff.delete(id);\n continue;\n }\n\n blocks.push({ _tag: 'anchor', objectId: id, version });\n }\n\n if (artifactDiff.size > 0) {\n blocks.push(createArtifactUpdateBlock(artifactDiff));\n }\n }\n\n return Obj.make(Message.Message, {\n created: new Date().toISOString(),\n // A named sender is what lets the model tell speakers apart (see `AiPreprocessor`).\n sender: { ...sender, role: 'user' },\n blocks: typeof prompt === 'string' ? [...blocks, { _tag: 'text', text: prompt }] : [...blocks, ...prompt],\n });\n }).pipe(Effect.withSpan('formatUserPrompt'));\n\nconst gatherObjectVersions = (messages: Message.Message[]): Map<EntityId, Obj.Version> => {\n const artifactIds = new Map<EntityId, Obj.Version>();\n for (const message of messages) {\n for (const block of message.blocks) {\n // An anchor's `version` is persisted as `Schema.Unknown`, so narrow rather than assume the shape.\n if (block._tag === 'anchor' && Obj.isVersion(block.version)) {\n artifactIds.set(block.objectId, block.version);\n }\n }\n }\n\n return artifactIds;\n};\n\nconst createArtifactUpdateBlock = (\n artifactDiff: Map<EntityId, { version: Obj.Version; diff?: string }>,\n): ContentBlock.Any => {\n return {\n _tag: 'text',\n // TODO(dmaretskyi): Does this need to be a special content-block?\n disposition: 'synthetic',\n text: trim`\n The following artifacts have been updated since the last message:\n ${[...artifactDiff.entries()]\n .map(([id, { diff }]) => `<changed-artifact id=\"${id}\">${diff ? `\\n${diff}` : ''}</changed-artifact>`)\n .join('\\n')}\n `,\n };\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport type * as Tool from 'effect/ai/Tool';\nimport * as Effect from 'effect/Effect';\nimport * as Function from 'effect/Function';\n\nimport { type AiParser, type ConsolePrinter } from '@dxos/ai';\nimport { type Message } from '@dxos/types';\n\n/**\n * Live observer of the generation process.\n */\nexport interface GenerationObserver<\n Tools extends Record<string, Tool.Any> = any,\n> extends AiParser.ParseResponseCallbacks<Tools> {\n /**\n * Complete messages fired during the session, both from the model and from the user.\n * This message will contain all message blocks emitted through the `onBlock` callback.\n */\n onMessage: (message: Message.Message) => Effect.Effect<void>;\n}\n\nexport const GenerationObserver = Object.freeze({\n make: ({\n onBegin = Function.constant(Effect.void),\n onPart = Function.constant(Effect.void),\n onBlock = Function.constant(Effect.void),\n onEnd = Function.constant(Effect.void),\n onMessage = Function.constant(Effect.void),\n }: Partial<GenerationObserver> = {}): GenerationObserver => ({\n onBegin,\n onPart,\n onBlock,\n onEnd,\n onMessage,\n }),\n\n noop: () => GenerationObserver.make(),\n\n /**\n * Debug printer to be used in unit-tests and browser devtools.\n */\n fromPrinter: (printer: ConsolePrinter) =>\n GenerationObserver.make({\n onBlock: (block) =>\n Effect.sync(() => {\n if (block.pending) {\n return; // Only prints full blocks (better for unit-tests and browser devtools).\n }\n printer.printContentBlock(block);\n }),\n onMessage: (message) =>\n Effect.sync(() => {\n if (message.sender.role === 'assistant') {\n return; // Skip assistant messages since they are printed in the `onBlock` callback.\n }\n printer.printMessage(message);\n }),\n }),\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as AiError from 'effect/ai/AiError';\nimport * as LanguageModel from 'effect/ai/LanguageModel';\nimport type * as Toolkit from 'effect/ai/Toolkit';\nimport * as Array from 'effect/Array';\nimport * as Effect from 'effect/Effect';\nimport { pipe } from 'effect/Function';\nimport * as Option from 'effect/Option';\nimport * as Result from 'effect/Result';\nimport * as Schedule from 'effect/Schedule';\nimport * as Semaphore from 'effect/Semaphore';\nimport * as Stream from 'effect/Stream';\n\nimport {\n AiParser,\n AiPreprocessor,\n AiSummarizer,\n type AiToolNotFoundError,\n type OpaqueToolkit,\n type PromptPreprocessingError,\n type ToolExecutionService,\n type ToolResolverService,\n callTool,\n withoutToolCallParsing,\n} from '@dxos/ai';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport * as Operation from '@dxos/compute/Operation';\nimport type * as Skill from '@dxos/compute/Skill';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Database, Obj, Registry } from '@dxos/echo';\nimport { log } from '@dxos/log';\nimport { ContentBlock, Message } from '@dxos/types';\nimport { markWork } from '@dxos/util';\n\nimport { getOperationFromTool } from '../tool-runtime/services.ts';\nimport { type AiAssistantError } from '../util/index.ts';\nimport { formatSystemPrompt, formatUserPrompt } from './format.ts';\nimport { GenerationObserver } from './observer.ts';\n\nexport type RunError = AiError.AiError | PromptPreprocessingError | AiToolNotFoundError | AiAssistantError;\n\n/**\n * An {@link AiError.AiError} raised because the model called a tool the toolkit does not contain.\n * Narrowed with a guard rather than a cast: the recovery reads the reason's fields.\n */\ntype ToolNotFoundError = AiError.AiError & { readonly reason: AiError.ToolNotFoundError };\n\nconst isToolNotFound = (error: unknown): error is ToolNotFoundError =>\n AiError.isAiError(error) && error.reason._tag === 'ToolNotFoundError';\n\n/**\n * How many turns of one request may be spent reporting unresolvable tool calls. A model that keeps\n * calling the same absent tool would otherwise loop until the token budget is gone; past this the\n * error is raised, so a persistent fault still surfaces.\n */\nconst MAX_UNRESOLVED_TOOL_TURNS = 2;\n\n/**\n * An {@link AiError.AiError} the provider raised while authenticating the request.\n * Narrowed with a guard rather than a cast: the retry predicate reads the reason's `kind`.\n */\ntype AuthenticationError = AiError.AiError & { readonly reason: AiError.AuthenticationError };\n\nconst isAuthenticationError = (error: unknown): error is AuthenticationError =>\n AiError.isAiError(error) && error.reason._tag === 'AuthenticationError';\n\n/**\n * Whether the provider rejected the request for permissions that have not yet propagated to the\n * key. Unlike the other authentication kinds (a missing, expired, or invalid key, which need a\n * credential change and never recover on their own), this one clears by itself, so it is the only\n * one worth re-issuing — `AuthenticationError.isRetryable` is `false` for all of them.\n */\nconst isInsufficientPermissions = (error: unknown): boolean =>\n isAuthenticationError(error) && error.reason.kind === 'InsufficientPermissions';\n\n/** Attempts spent re-issuing a request rejected for insufficient permissions, beyond the first. */\nconst INSUFFICIENT_PERMISSIONS_RETRIES = 10;\n\n/** Spaced rather than exponential: a propagation delay is bounded, and ten backed-off waits are not. */\nconst INSUFFICIENT_PERMISSIONS_RETRY_DELAY = '2 seconds';\n\nexport type RunRequirements =\n | LanguageModel.LanguageModel\n | ToolExecutionService\n | ToolResolverService\n | Database.Service\n | Operation.Service\n | Registry.Service\n | Trace.TraceService;\n\nexport type Options = {\n /**\n * Summarize before executing the prompt if the existing history exceeds this threshold.\n */\n summarizationThreshold?: number;\n\n // TODO(dmaretskyi): Plan to phase out in favor of TracingService and the return type being a stream.\n observer?: GenerationObserver;\n /**\n * Callback for when a message is received from the user, model, or tool.\n * This is useful for streaming the output to a queue.\n */\n onOutput?: (message: Message.Message) => Effect.Effect<void, never, never>;\n\n /**\n * When false, turn messages are not appended to the feed or written as persisted trace blocks.\n *\n * @default true\n */\n persist?: boolean;\n};\n\nexport type RunProps<R = never> = {\n prompt: string | ContentBlock.Any[];\n /** Who the prompt is from, when not the session's reader (e.g. one of several people in a shared agent chat). */\n sender?: Message.Message['sender'];\n // TODO(wittjosiah): Rename to systemPrompt.\n system?: string;\n history?: Message.Message[];\n objects?: Obj.Unknown[];\n skills?: readonly Skill.Skill[];\n /** Rendered inline into the system prompt; passed explicitly rather than inferred from `objects`. */\n instructions?: readonly Instructions.Instructions[];\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n};\n\nexport type BeginProps = {\n prompt: string | ContentBlock.Any[];\n sender?: Message.Message['sender'];\n system?: string;\n /** The system prompt already formatted from `system` and the bindings, so it is not formatted twice. */\n systemPrompt?: string;\n history?: Message.Message[];\n objects?: Obj.Unknown[];\n skills?: readonly Skill.Skill[];\n instructions?: readonly Instructions.Instructions[];\n};\n\nexport type TurnProps<R = never> = {\n system: string;\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n};\n\nexport type TurnResult = {\n messages: Message.Message[];\n done: boolean;\n /**\n * Provider finish reason for this turn. `pause` means the provider paused a server-tool turn\n * mid-execution (e.g. Anthropic `pause_turn`) and the turn must be resumed by issuing another\n * request without mutating the trailing assistant content.\n */\n finishReason?: ContentBlock.FinishReason;\n};\n\n/**\n * Contains message history, tools, current context.\n * Current context means the state of the app, time of day, and other contextual information.\n * It makes requests to the model, its a state machine.\n * It keeps track of the current goal.\n * It manages the context window.\n * Tracks the success criteria of reaching the goal, exposing metrics (stretch).\n * Could be run locally in the app or remotely.\n * Could be personal or shared.\n */\nexport class Request {\n /** Prevents concurrent execution of session. */\n private readonly _semaphore = Effect.runSync(Semaphore.make(1));\n\n private readonly _observer: GenerationObserver;\n private readonly _onOutput: (message: Message.Message) => Effect.Effect<void, never, never>;\n\n /** Message history from queue. */\n // TODO(burdon): Evolve into supporting a git-like graph of messages.\n private _history: Message.Message[] = [];\n\n /** Pending messages for this session (incl. the current prompt). */\n private _pending: Message.Message[] = [];\n\n private _started = 0;\n private _ended = 0;\n private _toolCalls = 0;\n /** Turns of this request spent reporting a tool call the toolkit could not resolve. */\n #unresolvedTools = 0;\n\n /** The turn's prompt, sent on the ephemeral channel once the next model call is under way. */\n #announcement: Message.Message | undefined;\n\n constructor(private readonly _options: Options = {}) {\n this._observer = _options.observer ?? GenerationObserver.noop();\n this._onOutput = _options.onOutput ?? (() => Effect.void);\n }\n\n get duration(): number {\n return this._ended - this._started;\n }\n\n get toolCalls(): number {\n return this._toolCalls;\n }\n\n get pending(): readonly Message.Message[] {\n return this._pending;\n }\n\n private _submitMessage = (message: Message.Message): Effect.Effect<Message.Message, never, Trace.TraceService> =>\n Effect.gen({ self: this }, function* () {\n this._pending.push(message);\n yield* this._observer.onMessage(message);\n if (this._options.persist === false) {\n return message;\n }\n for (const block of message.blocks) {\n log('write complete block', {\n messageId: message.id,\n role: message.sender.role!,\n block: JSON.stringify(block),\n });\n yield* Trace.write(Trace.CompleteBlock, {\n messageId: message.id,\n role: message.sender.role!,\n block,\n });\n }\n yield* this._onOutput(message);\n return message;\n });\n\n /**\n * Appends a system-generated note to the turn, addressed to the model: synthetic, so it renders as\n * its own panel rather than as words the reader typed. Used to report a fault the model can act on\n * (an unresolvable tool call) without failing the request.\n */\n submitNotice = (text: string): Effect.Effect<Message.Message, never, Trace.TraceService> =>\n this._submitMessage(\n Obj.make(Message.Message, {\n created: new Date().toISOString(),\n sender: { role: 'user' },\n blocks: [ContentBlock.Text.make({ text, disposition: 'synthetic' })],\n }),\n );\n\n getToolCalls = () =>\n pipe(\n [...this._history, ...this._pending],\n Array.reverse,\n Array.takeWhile((_) => _.sender.role === 'assistant'),\n Array.flatMap((_) => _.blocks.filter(ContentBlock.is('toolCall')).map((block) => ({ block, message: _ }))),\n Array.filter((_) => !_.block.providerExecuted),\n Array.reverse,\n );\n\n /**\n * Initialize a session: set up history, perform summarization if needed, and submit the user prompt.\n * Must be called before `runTurn()`.\n */\n begin = ({\n prompt,\n sender,\n system,\n systemPrompt: formatted,\n history = [],\n skills = [],\n objects = [],\n instructions = [],\n }: BeginProps): Effect.Effect<void, RunError, RunRequirements> =>\n Effect.gen({ self: this }, function* () {\n this._started = Date.now();\n this._history = [...history];\n this._pending = [];\n // Per-run allowance: a reused Request must not inherit a spent budget from the previous run.\n this.#unresolvedTools = 0;\n\n const systemPrompt =\n formatted ?? (yield* formatSystemPrompt({ system, skills, objects, instructions }).pipe(Effect.orDie));\n\n if (this._options.summarizationThreshold !== undefined) {\n const tokenCount = yield* AiPreprocessor.estimateTokens(\n yield* AiPreprocessor.preprocessPrompt([...this._history], {\n system: systemPrompt,\n }),\n );\n if (tokenCount > this._options.summarizationThreshold) {\n // A summarization pass is itself a model round-trip, so it can dominate the wait before\n // the turn the reader asked for even starts.\n yield* Trace.emitRequestPhase('summarizing');\n const summary = yield* AiSummarizer.summarize([...this._history]);\n yield* this._submitMessage(summary);\n }\n }\n\n const userMessage = yield* formatUserPrompt({ prompt, history, sender });\n // Also sent on the ephemeral channel, as the reply's blocks are: the feed shows the prompt only\n // once its index catches up, which can be after the reply has started streaming in.\n this.#announcement = userMessage;\n yield* this._submitMessage(userMessage);\n }).pipe(Effect.withSpan('AiRequest.begin'));\n\n /**\n * Reports a tool call the toolkit could not resolve back to the model, as a turn it can correct.\n *\n * The provider raises this while DECODING its own response — it needs the tool's schema to decode\n * the arguments — so the failure arrives before any tool call reaches the loop, and it kills the\n * whole turn. The usual causes are a skill whose instructions name a tool whose handler this host\n * never contributed, and a model inventing a name; both leave the reader with no reply at all.\n *\n * The tool call itself is lost: the provider discards the event batch it failed in, including the\n * `tool-params-end` the parser needs to complete the block, so nothing dangles in history that\n * would need a matching tool result — a plain note is enough.\n */\n #reportUnresolvedTool = (error: ToolNotFoundError): Effect.Effect<TurnResult, RunError, Trace.TraceService> =>\n Effect.gen({ self: this }, function* () {\n if (++this.#unresolvedTools > MAX_UNRESOLVED_TOOL_TURNS) {\n return yield* Effect.fail(error);\n }\n\n const { toolName, availableTools } = error.reason;\n log.warn('tool not found; reporting to the model', { tool: toolName, available: availableTools });\n yield* this.submitNotice(\n `The tool '${toolName}' does not exist, so nothing was called. ` +\n `The tools you can call are: ${availableTools.join(', ')}. ` +\n 'Continue with those, and say so plainly if the task needs one that is missing.',\n );\n\n return { messages: [], done: false };\n });\n\n /**\n * Execute a single turn: one LLM generation followed by tool execution.\n * The toolkit and system prompt can be updated between turns to reflect context changes (e.g. dynamically enabled skills).\n */\n runAgentTurn = <const R = never>({\n system,\n toolkit: opaqueToolkit,\n }: TurnProps<R>): Effect.Effect<TurnResult, RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n log('request', {\n system: { snippet: createSnippet(system), length: system.length },\n pending: this._pending.length,\n history: this._history.length,\n });\n\n yield* Trace.emitRequestPhase('encoding-prompt');\n const prompt = yield* AiPreprocessor.preprocessPrompt([...this._history, ...this._pending], {\n system,\n cacheControl: 'ephemeral',\n });\n\n const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : undefined;\n markWork('request.prompt-encoded');\n\n const observer = this._observer;\n let currentMessageId: Obj.ID | null = null;\n let finishReason: ContentBlock.FinishReason | undefined;\n\n // v4 overloads `streamText` on the presence of `toolkit`, so the two cases branch explicitly\n // rather than passing a possibly-undefined key.\n const openStream = () =>\n toolkit\n ? LanguageModel.streamText({ prompt, toolkit, disableToolCallResolution: true })\n : LanguageModel.streamText({ prompt, disableToolCallResolution: true });\n\n // Counts attempts at the provider rather than turns: the retry below re-runs the whole\n // collect, so `Stream.unwrap` re-evaluates this on each attempt and the reader sees the\n // request being re-issued instead of an unexplained stall.\n // Forked to run once the call has gone out rather than written before it: rendering the prompt\n // is the reader's page work, so it runs while the provider answers instead of delaying the request.\n // Detached, since the effect that opens the stream returns at once and would take a child with it.\n const announce = Effect.suspend(() => {\n const announcement = this.#announcement;\n this.#announcement = undefined;\n return announcement === undefined\n ? Effect.void\n : Effect.forEach(\n announcement.blocks,\n (block) => Trace.write(Trace.PartialBlock, { messageId: announcement.id, role: 'user', block }),\n { discard: true },\n );\n });\n\n let attempt = 0;\n const stream = Stream.unwrap(\n Effect.gen(function* () {\n yield* Trace.emitRequestPhase('contacting-provider', { attempt: ++attempt });\n yield* Effect.yieldNow.pipe(Effect.andThen(announce), Effect.forkDetach);\n return openStream();\n }),\n );\n\n // Set once any block of this attempt has been submitted, after which the request cannot be\n // re-issued: the messages are already in `_pending` and a second attempt would duplicate them.\n let emitted = false;\n\n const messages = yield* stream.pipe(\n withoutToolCallParsing,\n AiParser.parseResponse({\n emitPartial: true,\n // Tagged chain-of-thought (<cot>/<think>/<reasoning>) becomes reasoning blocks, which the\n // UI renders per view type; flattened to prose it can neither be shown nor hidden.\n parseReasoningTags: true,\n onBegin: () => observer.onBegin(),\n onBlock: (block) => observer.onBlock(block),\n onPart: (part) => observer.onPart(part as any),\n onEnd: (summary) => observer.onEnd(summary),\n }),\n Stream.map((block) => enrichToolCallBlock(block, toolkit)),\n Stream.mapEffect(\n (block) =>\n Effect.gen({ self: this }, function* () {\n // A model that answers before the forked announcement runs must not show its reply\n // ahead of the prompt; a no-op once the prompt has gone out.\n yield* announce;\n if (block._tag === 'stats' && block.finishReason !== undefined) {\n finishReason = block.finishReason;\n }\n if (block.pending) {\n currentMessageId ??= Obj.ID.random();\n log('emit ephemeral message', { id: currentMessageId, type: block._tag });\n yield* Trace.write(Trace.PartialBlock, {\n messageId: currentMessageId,\n role: 'assistant',\n block,\n });\n return Option.none();\n } else {\n currentMessageId ??= Obj.ID.random();\n const id = currentMessageId;\n currentMessageId = null;\n log('emit complete message', { id, type: block._tag });\n emitted = true;\n const message = Obj.make(Message.Message, {\n id,\n created: new Date().toISOString(),\n sender: { role: 'assistant' },\n blocks: [block],\n });\n return Option.some(yield* this._submitMessage(message));\n }\n }),\n { concurrency: 1, unordered: false },\n ),\n Stream.filterMap((value) => (Option.isSome(value) ? Result.succeed(value.value) : Result.failVoid)),\n Stream.runCollect,\n Effect.retry({\n schedule: Schedule.spaced(INSUFFICIENT_PERMISSIONS_RETRY_DELAY).pipe(\n Schedule.jittered,\n Schedule.upTo({ times: INSUFFICIENT_PERMISSIONS_RETRIES }),\n ),\n while: (error) => {\n if (emitted || !isInsufficientPermissions(error)) {\n return false;\n }\n log.warn('insufficient permissions; retrying request', { error });\n return true;\n },\n }),\n );\n log('messages', { messages });\n\n // A paused server-tool turn (e.g. Anthropic `pause_turn`) is not complete: the provider\n // expects the assistant content to be resent so it can finish executing the server tool.\n // No local tool execution is needed — just another request.\n if (finishReason === 'pause') {\n return { messages, done: false, finishReason };\n }\n\n const toolCalls = this.getToolCalls();\n\n if (toolCalls.length === 0) {\n this._ended = Date.now();\n return { messages, done: true, finishReason };\n } else if (!toolkit) {\n throw new Error('No toolkit provided');\n }\n\n return { messages, done: false, finishReason };\n }).pipe(\n Effect.catchIf(isToolNotFound, (error) => this.#reportUnresolvedTool(error)),\n Effect.withSpan('AiRequest.runAgentTurn'),\n );\n\n runTools = <const R = never>({\n toolkit: opaqueToolkit,\n }: {\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n }): Effect.Effect<void, RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : undefined;\n markWork('request.tools-begin');\n const toolCalls = this.getToolCalls();\n // A turn can end with no calls to run — a turn recovered from an unresolvable tool call leaves\n // none. Submitting anyway would append a tool message with no blocks, which the provider\n // rejects as empty content.\n if (toolCalls.length === 0) {\n return;\n }\n const toolResults = yield* Effect.forEach(toolCalls, ({ block, message }) =>\n Effect.gen(function* () {\n if (!toolkit) {\n throw new Error('No toolkit provided');\n }\n // Tool execution is where an agentic turn spends most of its time, and it produces no\n // streamed content, so the tool's name is the only progress the reader has.\n yield* Trace.emitRequestPhase('calling-tool', { detail: block.name });\n return yield* callTool(toolkit, block);\n }),\n );\n markWork('request.tools-called');\n\n yield* this._submitMessage(\n Obj.make(Message.Message, {\n created: new Date().toISOString(),\n sender: { role: 'tool' },\n blocks: toolResults,\n }),\n );\n\n this._toolCalls += toolResults.length;\n }).pipe(Effect.withSpan('AiRequest.runTools'));\n\n /**\n * Run a full conversation turn loop. Equivalent to calling `begin()` then `runTurn()` in a loop.\n */\n run = <const R = never>({\n prompt,\n sender,\n system: systemTemplate,\n history = [],\n objects = [],\n skills = [],\n instructions = [],\n toolkit,\n }: RunProps<R>): Effect.Effect<Message.Message[], RunError, RunRequirements | R> =>\n Effect.gen({ self: this }, function* () {\n const system = yield* formatSystemPrompt({ system: systemTemplate, skills, objects, instructions }).pipe(\n Effect.orDie,\n );\n yield* this.begin({\n prompt,\n sender,\n system: systemTemplate,\n systemPrompt: system,\n history,\n objects,\n skills,\n instructions,\n });\n\n do {\n const { done, finishReason } = yield* this.runAgentTurn({ system, toolkit });\n if (done) {\n break;\n }\n // A paused server-tool turn resumes with another request and no local tool execution.\n if (finishReason === 'pause') {\n continue;\n }\n yield* this.runTools({ toolkit });\n } while (true);\n\n log('done', { pending: this._pending.length, duration: this.duration, tools: this._toolCalls });\n return this._pending;\n }).pipe(this._semaphore.withPermits(1), Effect.withSpan('AiRequest.run'));\n}\n\n/**\n * Annotates `toolCall` blocks with metadata about the backing Operation, when one exists.\n * Tool calls that resolve to a toolkit handler (no Operation) are left unchanged so callers can\n * distinguish operation invocations from inline tool calls.\n */\nconst enrichToolCallBlock = (\n block: ContentBlock.Any,\n toolkit: Toolkit.WithHandler<any> | undefined,\n): ContentBlock.Any => {\n if (block._tag !== 'toolCall' || !toolkit) {\n return block;\n }\n const tool = toolkit.tools[block.name];\n if (!tool) {\n return block;\n }\n // Some tools (provider-defined, raw MCP) don't carry an Effect `Context` for annotations and\n // `getOperationFromTool` throws. Be defensive: treat any failure as \"no operation\".\n let operationOpt: Option.Option<Operation.Definition.Any>;\n try {\n operationOpt = getOperationFromTool(tool);\n } catch {\n return block;\n }\n if (Option.isNone(operationOpt)) {\n return block;\n }\n const { meta } = operationOpt.value;\n return {\n ...block,\n operationKey: meta.key,\n operationName: meta.name,\n operationIcon: meta.icon,\n } satisfies ContentBlock.ToolCall;\n};\n\nconst createSnippet = (text: string, len = 32) =>\n text.length <= len * 2 ? text : [text.slice(0, len), '...', text.slice(-len)].join('');\n\n// TODO(dmaretskyi): Extract as a general util.\nconst logDump = (message: string, data: unknown) => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { writeFileSync } = require('node:fs');\n const path = `/tmp/log-data-${Date.now()}.json`;\n writeFileSync(path, JSON.stringify(data, null, 2));\n log.info(message, { path });\n};\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
2
|
import { i as formatSystemPrompt, n as Request, o as ToolExecutionServices } from "./chunk-AiRequest.mjs";
|
|
3
|
-
import { Binder } from "./
|
|
3
|
+
import { n as Binder } from "./chunk-AiContext.mjs";
|
|
4
4
|
import { _ as SessionStore, b as isQueued, s as fromBinder, t as HarnessService } from "./chunk-Harness.mjs";
|
|
5
|
-
import { runHooks } from "./
|
|
5
|
+
import { n as runHooks } from "./chunk-SkillHooks.mjs";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
7
|
import * as Layer from "effect/Layer";
|
|
8
8
|
import * as Record from "effect/Record";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-AiSession.mjs","names":[],"sources":["../../src/session/toolkit.ts","../../src/session/AiSession.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Toolkit from 'effect/ai/Toolkit';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { type AiToolNotFoundError, OpaqueToolkit, ToolExecutionService, ToolResolverService } from '@dxos/ai';\nimport type * as Skill from '@dxos/compute/Skill';\nimport { invariant } from '@dxos/invariant';\nimport { isTruthy } from '@dxos/util';\n\nexport type CreateToolkitProps<E = never, R = never> = {\n toolkit?: OpaqueToolkit.Any<E, R>;\n skills?: readonly Skill.Skill[];\n /**\n * Self-contained with handlers toolkits.\n */\n opaqueToolkits?: readonly OpaqueToolkit.Any<E, R>[];\n};\n\n/**\n * Build a combined toolkit from the skill tools and the provided toolkit.\n */\nexport const createToolkit = <E = never, R = never>({\n toolkit: toolkitProp,\n skills = [],\n opaqueToolkits = [],\n}: CreateToolkitProps<E, R>): Effect.Effect<\n OpaqueToolkit.OpaqueToolkit<never, E, R>,\n AiToolNotFoundError,\n ToolResolverService | ToolExecutionService\n> =>\n Effect.gen(function* () {\n // Dedupe: two skills binding the same operation share one tool name and one tool.\n const toolIds = [...new Set(skills.flatMap(({ tools }) => tools))];\n const skillToolkit = yield* ToolResolverService.resolveToolkit(toolIds);\n const skillToolHandler = yield* skillToolkit.toHandlers(ToolExecutionService.handlersFor(skillToolkit));\n const opaqueToolkit = OpaqueToolkit.merge(...opaqueToolkits);\n\n const toolkitDefs = [toolkitProp?.toolkit, skillToolkit, opaqueToolkit.toolkit].filter(isTruthy);\n // Tool names are key-derived and registry-unique, so a duplicate here is a distinct tool being\n // silently shadowed by the merge below — fail loudly instead.\n const toolNames = toolkitDefs.flatMap((def) => Object.keys(def.tools));\n const duplicates = toolNames.filter((name, index) => toolNames.indexOf(name) !== index);\n invariant(duplicates.length === 0, `Duplicate tool names in session toolkit: ${duplicates.join(', ')}`);\n const mergedToolkit = Toolkit.merge(...toolkitDefs);\n const combinedHandlerLayer = Layer.succeedContext(skillToolHandler).pipe(\n Layer.provideMerge(toolkitProp?.layer ?? OpaqueToolkit.empty.layer),\n Layer.provideMerge(opaqueToolkit.layer),\n );\n return OpaqueToolkit.make(mergedToolkit, combinedHandlerLayer);\n });\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Tool from 'effect/ai/Tool';\nimport * as Array from 'effect/Array';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport { pipe } from 'effect/Function';\nimport * as Layer from 'effect/Layer';\nimport * as Order from 'effect/Order';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\nimport * as Record from 'effect/Record';\nimport type * as Scope from 'effect/Scope';\n\nimport { AiTelemetry, type OpaqueToolkit, type ToolExecutionService, type ToolResolverService } from '@dxos/ai';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport * as Operation from '@dxos/compute/Operation';\nimport type * as Skill from '@dxos/compute/Skill';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Resource } from '@dxos/context';\nimport { Database, Feed, Filter, Obj, Registry } from '@dxos/echo';\nimport * as RuntimeProvider from '@dxos/effect/RuntimeProvider';\nimport { invariant } from '@dxos/invariant';\nimport { EID } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { McpToolkit } from '@dxos/mcp-client';\nimport { FeedProtocol } from '@dxos/protocols';\nimport { type ContentBlock, Message } from '@dxos/types';\nimport { markWork } from '@dxos/util';\n\nimport { AiRequest, type GenerationObserver, formatSystemPrompt } from '../request/index.ts';\nimport { ToolExecutionServices } from '../tool-runtime/index.ts';\nimport * as AiContext from './AiContext.ts';\nimport * as Harness from './Harness.ts';\nimport { SessionStore, isQueued } from './SessionStore.ts';\nimport * as SkillHooks from './SkillHooks.ts';\nimport { createToolkit } from './toolkit.ts';\n\nexport type RunProps<R = never> = {\n prompt: string | ContentBlock.Any[];\n /** Who the prompt is from, when not the session's reader (e.g. one of several people in a shared agent chat). */\n sender?: Message.Message['sender'];\n system?: string;\n observer?: GenerationObserver;\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n\n /**\n * Space-level MCP servers to connect alongside skill-defined ones.\n */\n mcpServers?: readonly McpToolkit.Options[];\n\n /**\n * When false, messages from this request are not appended to the feed or persisted to trace.\n *\n * @default true\n */\n persist?: boolean;\n};\n\nexport type Options = {\n feed: Feed.Feed;\n runtime: Context.Context<Database.Service>;\n /** @effect/atom-react Registry for reactive state. */\n registry?: AtomRegistry.AtomRegistry;\n /**\n * Instructions steering the conversation (typically the owning `Chat`'s), rendered into the system\n * prompt on every turn. The session is feed-centric and cannot reach its chat, so these are passed in.\n */\n instructions?: readonly Instructions.Instructions[];\n};\n\n/**\n * Summarization threshold in tokens.\n */\n// TODO(dmaretskyi): Take from model characteristics. opus has 200k max tokens.\nconst SUMMARY_THRESHOLD = 80_000;\n\n/**\n * Durable conversation state (initiated by users and agents) backed by a Feed.\n * Executes tools based on AI responses and supports cancellation of in-progress requests.\n */\nexport class Session extends Resource {\n private readonly _feed: Feed.Feed;\n private readonly _runtime: Context.Context<Database.Service>;\n readonly #instructions: readonly Instructions.Instructions[];\n\n /**\n * Skills and objects bound to the session.\n */\n private readonly _binder: AiContext.Binder;\n\n private readonly _sessionStore = new SessionStore();\n\n public constructor(options: Options) {\n super();\n this._feed = options.feed;\n this._runtime = options.runtime;\n this.#instructions = options.instructions ?? [];\n invariant(this._feed);\n invariant(this._runtime);\n this._binder = new AiContext.Binder({\n feed: this._feed,\n runtime: this._runtime,\n registry: options.registry,\n });\n }\n\n protected override async _open(): Promise<void> {\n await this._binder.open(this._ctx);\n }\n\n public get feed() {\n return this._feed;\n }\n\n public get context() {\n return this._binder;\n }\n\n /**\n * The conversation as the model should see it: only the messages reachable from the feed's current\n * head, so a soft fork's abandoned turns are excluded.\n */\n public async getHistory(): Promise<Message.Message[]> {\n const { items: reachable } = Feed.history(await this.#messagesInAppendOrder());\n return RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n this._sessionStore.reifyHistory(this._feed, reachable),\n );\n }\n\n /**\n * Every message in the feed, in append order — what `Feed.history` needs, since it walks lineage\n * positionally rather than by `created`, a wall clock peers do not agree on.\n */\n async #messagesInAppendOrder(): Promise<Message.Message[]> {\n const queryResult = await RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n Feed.query(this._feed, Filter.type(Message.Message)),\n );\n const items = await queryResult.run();\n return Array.sort(items.filter(Obj.instanceOf(Message.Message)), byFeedPosition);\n }\n\n getTools(): Effect.Effect<Record<string, Tool.Any>, never, ToolExecutionService | ToolResolverService> {\n return Effect.gen({ self: this }, function* () {\n const toolkit = yield* createToolkit({ skills: this.context.getSkills() });\n return toolkit.toolkit.tools;\n }).pipe(Effect.orDie);\n }\n\n /**\n * Not provided by default, since users might want to override them.\n */\n makeToolExecutionServices(): Layer.Layer<\n ToolExecutionService | ToolResolverService,\n never,\n OpaqueToolkit.OpaqueToolkitProvider | Operation.Service | Registry.Service\n > {\n return ToolExecutionServices.pipe(\n Layer.provide(Operation.withInvocationOptions({ conversation: Obj.getURI(this._feed) })),\n );\n }\n\n /**\n * Appends one of this turn's messages to the feed, consuming a pending rewind if there is one.\n *\n * The rewind decision is made in the UI, but the continuation is appended by the agent's process,\n * which resolves the feed and never sees the chat — so the feed carries the intent and this is where\n * it becomes lineage. `rewindFrom` names the earliest discarded message, so the new parent is whatever\n * precedes it; nothing preceding means the continuation starts a fresh line. Only the first message of\n * a turn finds it set, since the append clears it, so the rest of the turn chains implicitly. Clearing\n * after the append (rather than before) leaves the rewind pending if the turn fails.\n */\n public async appendTurnMessage(message: Message.Message): Promise<void> {\n const rewindFrom = this._feed.rewindFrom;\n const parent = rewindFrom !== undefined ? await this.#parentForRewind(rewindFrom) : undefined;\n\n await RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n Feed.append(this._feed, [message], parent !== undefined ? { parent } : undefined),\n );\n if (rewindFrom !== undefined) {\n // Cleared only once feed queries see the continuation: the thread truncates on the pointer, and\n // clearing it before the index has the continuation shows the abandoned turn again until it does.\n await this.#messagesInAppendOrder();\n Obj.update(this._feed, (feed) => {\n feed.rewindFrom = undefined;\n });\n }\n }\n\n /**\n * The message preceding `rewindFrom` in append order, which the continuation parents to.\n *\n * Queue entries are skipped: a turn's own user message is appended right after the entry it was\n * taken from, and an entry never joins the thread, so parenting to one cuts the lineage off.\n */\n async #parentForRewind(rewindFrom: string): Promise<string | undefined> {\n const messages = (await this.#messagesInAppendOrder()).filter((message) => !isQueued(message));\n const index = messages.findIndex((message) => message.id === rewindFrom);\n return index > 0 ? messages[index - 1].id : undefined;\n }\n\n /**\n * Creates a new cancelable request effect.\n */\n public createRequest<R = never>(\n params: RunProps<R>,\n ): Effect.Effect<Message.Message[], AiRequest.RunError, AiRequest.RunRequirements | R> {\n return Effect.gen({ self: this }, function* () {\n yield* AiTelemetry.annotateKind(AiTelemetry.KIND.turn);\n const inputTruncated = yield* AiTelemetry.annotateContent(AiTelemetry.ATTRIBUTES.input, () =>\n serializePrompt(params.prompt),\n );\n\n yield* Trace.emitRequestPhase('loading-history');\n const history = yield* Effect.promise(() => this.getHistory());\n markWork('session.history-loaded');\n const skills = this.context.getSkills();\n const objects = this.context.getObjects();\n\n log('run', {\n history: history.length,\n skills: skills.length,\n objects: objects.length,\n });\n\n // Formatted once per binding set and source text rather than per model call: formatting loads\n // every skill's template, each a wait behind the page's other work, and an unchanged prompt is\n // also what lets the provider's prompt cache hit across turns. The atoms keep their arrays'\n // identity until a binding changes them; a source not loaded here cannot be compared, so it\n // formats again.\n const sources = (currentSkills: Skill.Skill[]): (string | undefined)[] => [\n ...this.#instructions.map((instructions) => instructions.text.target?.content),\n ...currentSkills.map((skill) => skill.instructions.source.target?.content),\n ];\n let formatted:\n | { skills: Skill.Skill[]; objects: Obj.Unknown[]; sources: (string | undefined)[]; text: string }\n | undefined;\n const formatSystem = (currentSkills: Skill.Skill[], currentObjects: Obj.Unknown[]) =>\n Effect.gen({ self: this }, function* () {\n const currentSources = sources(currentSkills);\n if (\n formatted?.skills === currentSkills &&\n formatted.objects === currentObjects &&\n currentSources.every((source, index) => source !== undefined && source === formatted?.sources[index])\n ) {\n return formatted.text;\n }\n const text = yield* formatSystemPrompt({\n system: params.system,\n skills: currentSkills,\n objects: currentObjects,\n instructions: this.#instructions,\n }).pipe(Effect.orDie);\n formatted = { skills: currentSkills, objects: currentObjects, sources: currentSources, text };\n return text;\n });\n\n const request = new AiRequest.Request({\n summarizationThreshold: SUMMARY_THRESHOLD,\n observer: params.observer,\n persist: params.persist,\n onOutput: (message) => Effect.promise(() => this.appendTurnMessage(message)),\n });\n\n yield* request.begin({\n history,\n skills,\n objects,\n instructions: this.#instructions,\n prompt: params.prompt,\n sender: params.sender,\n system: params.system,\n systemPrompt: yield* formatSystem(skills, objects),\n });\n\n markWork('session.request-begun');\n\n // Fire begin-request hooks declared by the bound skills. These run in the agent's turn\n // fiber (Tier A only), so they cannot reach the live host (Tier B) — that is the end hook's job.\n yield* SkillHooks.runHooks({\n skills,\n phase: 'begin-request',\n invoke: (operation, input) => Operation.invoke(operation, input).pipe(Effect.asVoid, Effect.orDie),\n });\n\n // Turn loop: recompute toolkit and system prompt between turns to pick up dynamically enabled skills.\n // Each iteration is scoped so the MCP connections it opens are closed before the next opens its own.\n const runIteration = Effect.gen({ self: this }, function* () {\n markWork('session.iteration');\n yield* Effect.promise(() => this.context.sync());\n markWork('session.context-synced');\n const currentSkills = this.context.getSkills();\n const mcps = yield* connectMcpServers(currentSkills, params.mcpServers);\n yield* Trace.emitRequestPhase('building-toolkit');\n const toolkit = yield* createToolkit({\n toolkit: params.toolkit,\n skills: currentSkills,\n opaqueToolkits: mcps,\n });\n\n markWork('session.toolkit-built');\n log('toolkit', { tools: Record.keys(toolkit.toolkit.tools) });\n const system = yield* formatSystem(currentSkills, this.context.getObjects());\n markWork('session.system-prompt-built');\n\n const { done, finishReason } = yield* request.runAgentTurn({ system, toolkit });\n if (done) {\n return 'done' as const;\n }\n // A paused server-tool turn (e.g. Anthropic `pause_turn`) resumes with another request and\n // no local tool execution; the trailing server tool call must be left intact for the provider.\n if (finishReason === 'pause') {\n return 'continue' as const;\n }\n\n yield* request.runTools({ toolkit });\n markWork('session.tools-done');\n return 'continue' as const;\n }).pipe(Effect.scoped);\n\n while ((yield* runIteration) !== 'done') {}\n\n log('result', {\n messages: request.pending.length,\n duration: request.duration,\n toolCalls: request.toolCalls,\n });\n\n const output = [...request.pending];\n const outputTruncated = yield* AiTelemetry.annotateContent(AiTelemetry.ATTRIBUTES.output, () =>\n output.map(serializeMessage),\n );\n if (inputTruncated || outputTruncated) {\n yield* Effect.annotateCurrentSpan(AiTelemetry.ATTRIBUTES.truncated, true);\n }\n return output;\n }).pipe(\n Effect.provide(\n Layer.mergeAll(\n // Tier A only: the agent's own turn fiber has no `ProcessManager.Service` in scope, so\n // Tier B (setAlarm/enqueueMessage) is reachable only by child operations through the\n // process-affinity HarnessService LayerSpec — not from here.\n Layer.succeed(\n Harness.HarnessService,\n Harness.fromBinder({ feed: this._feed, runtime: this._runtime, binder: this.context }),\n ),\n Operation.withInvocationOptions({ conversation: Obj.getURI(this._feed) }),\n ),\n ),\n Effect.withSpan('AiSession.createRequest'),\n Effect.annotateSpans(sessionAnnotations(this._feed)),\n );\n }\n}\n\nconst connectMcpServers = (\n skills: readonly Skill.Skill[],\n spaceServers: readonly McpToolkit.Options[] = [],\n): Effect.Effect<OpaqueToolkit.OpaqueToolkit[], never, Trace.TraceService | Scope.Scope> => {\n const skillServers: McpToolkit.Options[] = pipe(\n skills,\n Array.flatMap((_) => _.mcpServers ?? []),\n Array.map(({ url, protocol, apiKey }) => ({ url, protocol, apiKey })),\n );\n const allServers = [...skillServers, ...spaceServers];\n if (allServers.length === 0) {\n // Naming a phase that has nothing to do would misreport where the wait actually is.\n return Effect.succeed([]);\n }\n\n const connect = pipe(\n allServers,\n Effect.forEach((options) =>\n McpToolkit.make(options).pipe(\n // NOTE: Type-inference fails here without explicit void return.\n Effect.tap((toolkit) =>\n Effect.sync(() =>\n log.info('Connected to MCP server', { url: options.url, tools: Object.keys(toolkit.toolkit.tools).length }),\n ),\n ),\n // Surface typed connection failures via ephemeral trace + warn log, then drop the server.\n Effect.tapError((error) =>\n Effect.gen(function* () {\n log.warn('Failed to connect to MCP server', {\n url: error.url,\n protocol: error.protocol,\n message: error.message,\n });\n yield* Trace.write(Trace.McpServerError, {\n url: error.url,\n protocol: error.protocol,\n message: error.message,\n unauthorized: error.unauthorized,\n });\n }),\n ),\n // Catch unexpected defects too (e.g. malformed tool schemas) so a single broken\n // server can never abort the whole turn — surface them through the same channel.\n Effect.catchDefect((defect) =>\n Effect.gen(function* () {\n const message = defect instanceof Error ? defect.message : String(defect);\n log.warn('Unexpected MCP defect', { url: options.url, message });\n yield* Trace.write(Trace.McpServerError, {\n url: options.url,\n protocol: options.protocol,\n message: `Unexpected MCP failure: ${message}`,\n });\n return yield* Effect.fail(\n new McpToolkit.McpConnectionError({\n url: options.url,\n protocol: options.protocol,\n message,\n }),\n );\n }),\n ),\n Effect.result,\n ),\n ),\n Effect.map((results) => Array.filterMap(results, (result) => result)),\n );\n\n return Effect.gen(function* () {\n // Reported before the connections are opened, since opening them is the wait being reported.\n yield* Trace.emitRequestPhase('connecting-mcp', { detail: String(allServers.length) });\n return yield* connect;\n });\n};\n\n/**\n * Orders feed items by the position the server assigned them. Unpositioned blocks (written locally and\n * not yet acknowledged) sort last, which is what we want: a message just written is the newest.\n */\nconst byFeedPosition = Order.make<Message.Message>((a, b) => {\n const positionA = feedPosition(a);\n const positionB = feedPosition(b);\n return positionA === positionB ? 0 : positionA < positionB ? -1 : 1;\n});\n\nconst feedPosition = (message: Message.Message): number => {\n const key = Obj.getKeys(message, FeedProtocol.KEY_QUEUE_POSITION).at(0)?.id;\n const position = key !== undefined ? Number(key) : Number.NaN;\n return Number.isNaN(position) ? Number.POSITIVE_INFINITY : position;\n};\n\n/** The prompt as the turn span carries it: text as is, blocks reduced to what identifies them. */\nexport const serializePrompt = (prompt: string | readonly ContentBlock.Any[]): unknown =>\n typeof prompt === 'string' ? prompt : prompt.map(serializeBlock);\n\n/** A turn's message as its span carries it. */\nexport const serializeMessage = (message: Message.Message): unknown => ({\n role: message.sender.role,\n blocks: message.blocks.map(serializeBlock),\n});\n\nconst serializeBlock = (block: ContentBlock.Any): unknown => {\n switch (block._tag) {\n case 'text':\n return { type: 'text', text: block.text };\n case 'toolCall':\n return { type: 'toolCall', name: block.name, input: block.input };\n case 'toolResult':\n return { type: 'toolResult', name: block.name, result: block.result, error: block.error };\n default:\n return { type: block._tag };\n }\n};\n\n/**\n * Span annotations identifying the conversation a model call belongs to. The space is read off the\n * feed's URI (`echo://<spaceId>/<objectId>`) rather than passed in, so it cannot go missing.\n */\nexport const sessionAnnotations = (feed: Feed.Feed): Record<string, string> => {\n const uri = Obj.getURI(feed, { prefer: 'absolute' });\n const eid = EID.tryParse(uri);\n const spaceId = eid && EID.getSpaceId(eid);\n return {\n [AiTelemetry.ATTRIBUTES.sessionId]: uri,\n ...(spaceId ? { [AiTelemetry.ATTRIBUTES.spaceId]: spaceId } : {}),\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-AiSession.mjs","names":["__dxlog_file","AiContext.Binder","AiRequest.Request","SkillHooks.runHooks","Harness.HarnessService","Harness.fromBinder","EID"],"sources":["../../src/session/toolkit.ts","../../src/session/AiSession.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Toolkit from 'effect/ai/Toolkit';\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { type AiToolNotFoundError, OpaqueToolkit, ToolExecutionService, ToolResolverService } from '@dxos/ai';\nimport type * as Skill from '@dxos/compute/Skill';\nimport { invariant } from '@dxos/invariant';\nimport { isTruthy } from '@dxos/util';\n\nexport type CreateToolkitProps<E = never, R = never> = {\n toolkit?: OpaqueToolkit.Any<E, R>;\n skills?: readonly Skill.Skill[];\n /**\n * Self-contained with handlers toolkits.\n */\n opaqueToolkits?: readonly OpaqueToolkit.Any<E, R>[];\n};\n\n/**\n * Build a combined toolkit from the skill tools and the provided toolkit.\n */\nexport const createToolkit = <E = never, R = never>({\n toolkit: toolkitProp,\n skills = [],\n opaqueToolkits = [],\n}: CreateToolkitProps<E, R>): Effect.Effect<\n OpaqueToolkit.OpaqueToolkit<never, E, R>,\n AiToolNotFoundError,\n ToolResolverService | ToolExecutionService\n> =>\n Effect.gen(function* () {\n // Dedupe: two skills binding the same operation share one tool name and one tool.\n const toolIds = [...new Set(skills.flatMap(({ tools }) => tools))];\n const skillToolkit = yield* ToolResolverService.resolveToolkit(toolIds);\n const skillToolHandler = yield* skillToolkit.toHandlers(ToolExecutionService.handlersFor(skillToolkit));\n const opaqueToolkit = OpaqueToolkit.merge(...opaqueToolkits);\n\n const toolkitDefs = [toolkitProp?.toolkit, skillToolkit, opaqueToolkit.toolkit].filter(isTruthy);\n // Tool names are key-derived and registry-unique, so a duplicate here is a distinct tool being\n // silently shadowed by the merge below — fail loudly instead.\n const toolNames = toolkitDefs.flatMap((def) => Object.keys(def.tools));\n const duplicates = toolNames.filter((name, index) => toolNames.indexOf(name) !== index);\n invariant(duplicates.length === 0, `Duplicate tool names in session toolkit: ${duplicates.join(', ')}`);\n const mergedToolkit = Toolkit.merge(...toolkitDefs);\n const combinedHandlerLayer = Layer.succeedContext(skillToolHandler).pipe(\n Layer.provideMerge(toolkitProp?.layer ?? OpaqueToolkit.empty.layer),\n Layer.provideMerge(opaqueToolkit.layer),\n );\n return OpaqueToolkit.make(mergedToolkit, combinedHandlerLayer);\n });\n","//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Tool from 'effect/ai/Tool';\nimport * as Array from 'effect/Array';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport { pipe } from 'effect/Function';\nimport * as Layer from 'effect/Layer';\nimport * as Order from 'effect/Order';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\nimport * as Record from 'effect/Record';\nimport type * as Scope from 'effect/Scope';\n\nimport { AiTelemetry, type OpaqueToolkit, type ToolExecutionService, type ToolResolverService } from '@dxos/ai';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport * as Operation from '@dxos/compute/Operation';\nimport type * as Skill from '@dxos/compute/Skill';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Resource } from '@dxos/context';\nimport { Database, Feed, Filter, Obj, Registry } from '@dxos/echo';\nimport * as RuntimeProvider from '@dxos/effect/RuntimeProvider';\nimport { invariant } from '@dxos/invariant';\nimport { EID } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { McpToolkit } from '@dxos/mcp-client';\nimport { FeedProtocol } from '@dxos/protocols';\nimport { type ContentBlock, Message } from '@dxos/types';\nimport { markWork } from '@dxos/util';\n\nimport { AiRequest, type GenerationObserver, formatSystemPrompt } from '../request/index.ts';\nimport { ToolExecutionServices } from '../tool-runtime/index.ts';\nimport * as AiContext from './AiContext.ts';\nimport * as Harness from './Harness.ts';\nimport { SessionStore, isQueued } from './SessionStore.ts';\nimport * as SkillHooks from './SkillHooks.ts';\nimport { createToolkit } from './toolkit.ts';\n\nexport type RunProps<R = never> = {\n prompt: string | ContentBlock.Any[];\n /** Who the prompt is from, when not the session's reader (e.g. one of several people in a shared agent chat). */\n sender?: Message.Message['sender'];\n system?: string;\n observer?: GenerationObserver;\n toolkit?: OpaqueToolkit.OpaqueToolkit<R>;\n\n /**\n * Space-level MCP servers to connect alongside skill-defined ones.\n */\n mcpServers?: readonly McpToolkit.Options[];\n\n /**\n * When false, messages from this request are not appended to the feed or persisted to trace.\n *\n * @default true\n */\n persist?: boolean;\n};\n\nexport type Options = {\n feed: Feed.Feed;\n runtime: Context.Context<Database.Service>;\n /** @effect/atom-react Registry for reactive state. */\n registry?: AtomRegistry.AtomRegistry;\n /**\n * Instructions steering the conversation (typically the owning `Chat`'s), rendered into the system\n * prompt on every turn. The session is feed-centric and cannot reach its chat, so these are passed in.\n */\n instructions?: readonly Instructions.Instructions[];\n};\n\n/**\n * Summarization threshold in tokens.\n */\n// TODO(dmaretskyi): Take from model characteristics. opus has 200k max tokens.\nconst SUMMARY_THRESHOLD = 80_000;\n\n/**\n * Durable conversation state (initiated by users and agents) backed by a Feed.\n * Executes tools based on AI responses and supports cancellation of in-progress requests.\n */\nexport class Session extends Resource {\n private readonly _feed: Feed.Feed;\n private readonly _runtime: Context.Context<Database.Service>;\n readonly #instructions: readonly Instructions.Instructions[];\n\n /**\n * Skills and objects bound to the session.\n */\n private readonly _binder: AiContext.Binder;\n\n private readonly _sessionStore = new SessionStore();\n\n public constructor(options: Options) {\n super();\n this._feed = options.feed;\n this._runtime = options.runtime;\n this.#instructions = options.instructions ?? [];\n invariant(this._feed);\n invariant(this._runtime);\n this._binder = new AiContext.Binder({\n feed: this._feed,\n runtime: this._runtime,\n registry: options.registry,\n });\n }\n\n protected override async _open(): Promise<void> {\n await this._binder.open(this._ctx);\n }\n\n public get feed() {\n return this._feed;\n }\n\n public get context() {\n return this._binder;\n }\n\n /**\n * The conversation as the model should see it: only the messages reachable from the feed's current\n * head, so a soft fork's abandoned turns are excluded.\n */\n public async getHistory(): Promise<Message.Message[]> {\n const { items: reachable } = Feed.history(await this.#messagesInAppendOrder());\n return RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n this._sessionStore.reifyHistory(this._feed, reachable),\n );\n }\n\n /**\n * Every message in the feed, in append order — what `Feed.history` needs, since it walks lineage\n * positionally rather than by `created`, a wall clock peers do not agree on.\n */\n async #messagesInAppendOrder(): Promise<Message.Message[]> {\n const queryResult = await RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n Feed.query(this._feed, Filter.type(Message.Message)),\n );\n const items = await queryResult.run();\n return Array.sort(items.filter(Obj.instanceOf(Message.Message)), byFeedPosition);\n }\n\n getTools(): Effect.Effect<Record<string, Tool.Any>, never, ToolExecutionService | ToolResolverService> {\n return Effect.gen({ self: this }, function* () {\n const toolkit = yield* createToolkit({ skills: this.context.getSkills() });\n return toolkit.toolkit.tools;\n }).pipe(Effect.orDie);\n }\n\n /**\n * Not provided by default, since users might want to override them.\n */\n makeToolExecutionServices(): Layer.Layer<\n ToolExecutionService | ToolResolverService,\n never,\n OpaqueToolkit.OpaqueToolkitProvider | Operation.Service | Registry.Service\n > {\n return ToolExecutionServices.pipe(\n Layer.provide(Operation.withInvocationOptions({ conversation: Obj.getURI(this._feed) })),\n );\n }\n\n /**\n * Appends one of this turn's messages to the feed, consuming a pending rewind if there is one.\n *\n * The rewind decision is made in the UI, but the continuation is appended by the agent's process,\n * which resolves the feed and never sees the chat — so the feed carries the intent and this is where\n * it becomes lineage. `rewindFrom` names the earliest discarded message, so the new parent is whatever\n * precedes it; nothing preceding means the continuation starts a fresh line. Only the first message of\n * a turn finds it set, since the append clears it, so the rest of the turn chains implicitly. Clearing\n * after the append (rather than before) leaves the rewind pending if the turn fails.\n */\n public async appendTurnMessage(message: Message.Message): Promise<void> {\n const rewindFrom = this._feed.rewindFrom;\n const parent = rewindFrom !== undefined ? await this.#parentForRewind(rewindFrom) : undefined;\n\n await RuntimeProvider.runPromise(Effect.succeed(this._runtime))(\n Feed.append(this._feed, [message], parent !== undefined ? { parent } : undefined),\n );\n if (rewindFrom !== undefined) {\n // Cleared only once feed queries see the continuation: the thread truncates on the pointer, and\n // clearing it before the index has the continuation shows the abandoned turn again until it does.\n await this.#messagesInAppendOrder();\n Obj.update(this._feed, (feed) => {\n feed.rewindFrom = undefined;\n });\n }\n }\n\n /**\n * The message preceding `rewindFrom` in append order, which the continuation parents to.\n *\n * Queue entries are skipped: a turn's own user message is appended right after the entry it was\n * taken from, and an entry never joins the thread, so parenting to one cuts the lineage off.\n */\n async #parentForRewind(rewindFrom: string): Promise<string | undefined> {\n const messages = (await this.#messagesInAppendOrder()).filter((message) => !isQueued(message));\n const index = messages.findIndex((message) => message.id === rewindFrom);\n return index > 0 ? messages[index - 1].id : undefined;\n }\n\n /**\n * Creates a new cancelable request effect.\n */\n public createRequest<R = never>(\n params: RunProps<R>,\n ): Effect.Effect<Message.Message[], AiRequest.RunError, AiRequest.RunRequirements | R> {\n return Effect.gen({ self: this }, function* () {\n yield* AiTelemetry.annotateKind(AiTelemetry.KIND.turn);\n const inputTruncated = yield* AiTelemetry.annotateContent(AiTelemetry.ATTRIBUTES.input, () =>\n serializePrompt(params.prompt),\n );\n\n yield* Trace.emitRequestPhase('loading-history');\n const history = yield* Effect.promise(() => this.getHistory());\n markWork('session.history-loaded');\n const skills = this.context.getSkills();\n const objects = this.context.getObjects();\n\n log('run', {\n history: history.length,\n skills: skills.length,\n objects: objects.length,\n });\n\n // Formatted once per binding set and source text rather than per model call: formatting loads\n // every skill's template, each a wait behind the page's other work, and an unchanged prompt is\n // also what lets the provider's prompt cache hit across turns. The atoms keep their arrays'\n // identity until a binding changes them; a source not loaded here cannot be compared, so it\n // formats again.\n const sources = (currentSkills: Skill.Skill[]): (string | undefined)[] => [\n ...this.#instructions.map((instructions) => instructions.text.target?.content),\n ...currentSkills.map((skill) => skill.instructions.source.target?.content),\n ];\n let formatted:\n | { skills: Skill.Skill[]; objects: Obj.Unknown[]; sources: (string | undefined)[]; text: string }\n | undefined;\n const formatSystem = (currentSkills: Skill.Skill[], currentObjects: Obj.Unknown[]) =>\n Effect.gen({ self: this }, function* () {\n const currentSources = sources(currentSkills);\n if (\n formatted?.skills === currentSkills &&\n formatted.objects === currentObjects &&\n currentSources.every((source, index) => source !== undefined && source === formatted?.sources[index])\n ) {\n return formatted.text;\n }\n const text = yield* formatSystemPrompt({\n system: params.system,\n skills: currentSkills,\n objects: currentObjects,\n instructions: this.#instructions,\n }).pipe(Effect.orDie);\n formatted = { skills: currentSkills, objects: currentObjects, sources: currentSources, text };\n return text;\n });\n\n const request = new AiRequest.Request({\n summarizationThreshold: SUMMARY_THRESHOLD,\n observer: params.observer,\n persist: params.persist,\n onOutput: (message) => Effect.promise(() => this.appendTurnMessage(message)),\n });\n\n yield* request.begin({\n history,\n skills,\n objects,\n instructions: this.#instructions,\n prompt: params.prompt,\n sender: params.sender,\n system: params.system,\n systemPrompt: yield* formatSystem(skills, objects),\n });\n\n markWork('session.request-begun');\n\n // Fire begin-request hooks declared by the bound skills. These run in the agent's turn\n // fiber (Tier A only), so they cannot reach the live host (Tier B) — that is the end hook's job.\n yield* SkillHooks.runHooks({\n skills,\n phase: 'begin-request',\n invoke: (operation, input) => Operation.invoke(operation, input).pipe(Effect.asVoid, Effect.orDie),\n });\n\n // Turn loop: recompute toolkit and system prompt between turns to pick up dynamically enabled skills.\n // Each iteration is scoped so the MCP connections it opens are closed before the next opens its own.\n const runIteration = Effect.gen({ self: this }, function* () {\n markWork('session.iteration');\n yield* Effect.promise(() => this.context.sync());\n markWork('session.context-synced');\n const currentSkills = this.context.getSkills();\n const mcps = yield* connectMcpServers(currentSkills, params.mcpServers);\n yield* Trace.emitRequestPhase('building-toolkit');\n const toolkit = yield* createToolkit({\n toolkit: params.toolkit,\n skills: currentSkills,\n opaqueToolkits: mcps,\n });\n\n markWork('session.toolkit-built');\n log('toolkit', { tools: Record.keys(toolkit.toolkit.tools) });\n const system = yield* formatSystem(currentSkills, this.context.getObjects());\n markWork('session.system-prompt-built');\n\n const { done, finishReason } = yield* request.runAgentTurn({ system, toolkit });\n if (done) {\n return 'done' as const;\n }\n // A paused server-tool turn (e.g. Anthropic `pause_turn`) resumes with another request and\n // no local tool execution; the trailing server tool call must be left intact for the provider.\n if (finishReason === 'pause') {\n return 'continue' as const;\n }\n\n yield* request.runTools({ toolkit });\n markWork('session.tools-done');\n return 'continue' as const;\n }).pipe(Effect.scoped);\n\n while ((yield* runIteration) !== 'done') {}\n\n log('result', {\n messages: request.pending.length,\n duration: request.duration,\n toolCalls: request.toolCalls,\n });\n\n const output = [...request.pending];\n const outputTruncated = yield* AiTelemetry.annotateContent(AiTelemetry.ATTRIBUTES.output, () =>\n output.map(serializeMessage),\n );\n if (inputTruncated || outputTruncated) {\n yield* Effect.annotateCurrentSpan(AiTelemetry.ATTRIBUTES.truncated, true);\n }\n return output;\n }).pipe(\n Effect.provide(\n Layer.mergeAll(\n // Tier A only: the agent's own turn fiber has no `ProcessManager.Service` in scope, so\n // Tier B (setAlarm/enqueueMessage) is reachable only by child operations through the\n // process-affinity HarnessService LayerSpec — not from here.\n Layer.succeed(\n Harness.HarnessService,\n Harness.fromBinder({ feed: this._feed, runtime: this._runtime, binder: this.context }),\n ),\n Operation.withInvocationOptions({ conversation: Obj.getURI(this._feed) }),\n ),\n ),\n Effect.withSpan('AiSession.createRequest'),\n Effect.annotateSpans(sessionAnnotations(this._feed)),\n );\n }\n}\n\nconst connectMcpServers = (\n skills: readonly Skill.Skill[],\n spaceServers: readonly McpToolkit.Options[] = [],\n): Effect.Effect<OpaqueToolkit.OpaqueToolkit[], never, Trace.TraceService | Scope.Scope> => {\n const skillServers: McpToolkit.Options[] = pipe(\n skills,\n Array.flatMap((_) => _.mcpServers ?? []),\n Array.map(({ url, protocol, apiKey }) => ({ url, protocol, apiKey })),\n );\n const allServers = [...skillServers, ...spaceServers];\n if (allServers.length === 0) {\n // Naming a phase that has nothing to do would misreport where the wait actually is.\n return Effect.succeed([]);\n }\n\n const connect = pipe(\n allServers,\n Effect.forEach((options) =>\n McpToolkit.make(options).pipe(\n // NOTE: Type-inference fails here without explicit void return.\n Effect.tap((toolkit) =>\n Effect.sync(() =>\n log.info('Connected to MCP server', { url: options.url, tools: Object.keys(toolkit.toolkit.tools).length }),\n ),\n ),\n // Surface typed connection failures via ephemeral trace + warn log, then drop the server.\n Effect.tapError((error) =>\n Effect.gen(function* () {\n log.warn('Failed to connect to MCP server', {\n url: error.url,\n protocol: error.protocol,\n message: error.message,\n });\n yield* Trace.write(Trace.McpServerError, {\n url: error.url,\n protocol: error.protocol,\n message: error.message,\n unauthorized: error.unauthorized,\n });\n }),\n ),\n // Catch unexpected defects too (e.g. malformed tool schemas) so a single broken\n // server can never abort the whole turn — surface them through the same channel.\n Effect.catchDefect((defect) =>\n Effect.gen(function* () {\n const message = defect instanceof Error ? defect.message : String(defect);\n log.warn('Unexpected MCP defect', { url: options.url, message });\n yield* Trace.write(Trace.McpServerError, {\n url: options.url,\n protocol: options.protocol,\n message: `Unexpected MCP failure: ${message}`,\n });\n return yield* Effect.fail(\n new McpToolkit.McpConnectionError({\n url: options.url,\n protocol: options.protocol,\n message,\n }),\n );\n }),\n ),\n Effect.result,\n ),\n ),\n Effect.map((results) => Array.filterMap(results, (result) => result)),\n );\n\n return Effect.gen(function* () {\n // Reported before the connections are opened, since opening them is the wait being reported.\n yield* Trace.emitRequestPhase('connecting-mcp', { detail: String(allServers.length) });\n return yield* connect;\n });\n};\n\n/**\n * Orders feed items by the position the server assigned them. Unpositioned blocks (written locally and\n * not yet acknowledged) sort last, which is what we want: a message just written is the newest.\n */\nconst byFeedPosition = Order.make<Message.Message>((a, b) => {\n const positionA = feedPosition(a);\n const positionB = feedPosition(b);\n return positionA === positionB ? 0 : positionA < positionB ? -1 : 1;\n});\n\nconst feedPosition = (message: Message.Message): number => {\n const key = Obj.getKeys(message, FeedProtocol.KEY_QUEUE_POSITION).at(0)?.id;\n const position = key !== undefined ? Number(key) : Number.NaN;\n return Number.isNaN(position) ? Number.POSITIVE_INFINITY : position;\n};\n\n/** The prompt as the turn span carries it: text as is, blocks reduced to what identifies them. */\nexport const serializePrompt = (prompt: string | readonly ContentBlock.Any[]): unknown =>\n typeof prompt === 'string' ? prompt : prompt.map(serializeBlock);\n\n/** A turn's message as its span carries it. */\nexport const serializeMessage = (message: Message.Message): unknown => ({\n role: message.sender.role,\n blocks: message.blocks.map(serializeBlock),\n});\n\nconst serializeBlock = (block: ContentBlock.Any): unknown => {\n switch (block._tag) {\n case 'text':\n return { type: 'text', text: block.text };\n case 'toolCall':\n return { type: 'toolCall', name: block.name, input: block.input };\n case 'toolResult':\n return { type: 'toolResult', name: block.name, result: block.result, error: block.error };\n default:\n return { type: block._tag };\n }\n};\n\n/**\n * Span annotations identifying the conversation a model call belongs to. The space is read off the\n * feed's URI (`echo://<spaceId>/<objectId>`) rather than passed in, so it cannot go missing.\n */\nexport const sessionAnnotations = (feed: Feed.Feed): Record<string, string> => {\n const uri = Obj.getURI(feed, { prefer: 'absolute' });\n const eid = EID.tryParse(uri);\n const spaceId = eid && EID.getSpaceId(eid);\n return {\n [AiTelemetry.ATTRIBUTES.sessionId]: uri,\n ...(spaceId ? { [AiTelemetry.ATTRIBUTES.spaceId]: spaceId } : {}),\n };\n};\n"],"mappings":"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"}
|