@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":"ExecutionGraph.mjs","names":[],"sources":["../../src/util/execution-graph.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { AgentStatus } from '@dxos/ai';\nimport { MESSAGE_PROPERTY_TOOL_CALL_ID } from '@dxos/compute-runtime';\nimport { type Entity, Obj, type Ref } from '@dxos/echo';\nimport { EID, type EntityId } from '@dxos/keys';\nimport { LogLevel } from '@dxos/log';\nimport { ContentBlock, Message } from '@dxos/types';\nimport { isNonNullable } from '@dxos/util';\n\nconst SKIP_BLOCKS: ContentBlock.Any['_tag'][] = ['text'];\n\n/**\n * Mercurial-style Commit.\n */\n// TODO(wittjosiah): Reconcile with @dxos/react-ui-components.\nexport type Commit = {\n id: string;\n parents: string[];\n branch: string;\n icon?: string;\n level?: LogLevel;\n message: string;\n timestamp?: Date;\n tags?: string[];\n};\n\n// TODO(burdon): Add colors?\nenum IconType {\n // General status.\n SUCCESS = 'ph--check-circle--regular',\n WARNING = 'ph--warning-circle--regular',\n ERROR = 'ph--x-circle--regular',\n FLAG = 'ph--flag--regular',\n ROCKET = 'ph--rocket--regular',\n TIMER = 'ph--timer--regular',\n\n // Interactions.\n USER = 'ph--user--regular',\n USER_INTERACTION = 'ph--user-sound--regular',\n AGENT = 'ph--drone--regular',\n THINKING = 'ph--brain--regular',\n LINK = 'ph--link--regular',\n TOOL = 'ph--wrench--regular',\n}\n\n/**\n * Models the execution graph based on a stream of events.\n */\nexport class ExecutionGraph {\n // TODO(dmaretskyi): Evolve the internal state to support chats, functions, circuit workflows, etc.\n private _commits: Commit[] = [];\n private _branchNames = new Set<string>();\n private _lastBlockId: string | undefined;\n\n // Tool call tracking.\n private _toolCallCommitIds = new Map<string, string>(); // toolCallId -> commitId\n private _lastCommitByBranch = new Map<string, string>(); // branch -> last commitId\n private _pendingToolResults = new Map<string, string>(); // toolCallId -> toolResultCommitId\n\n constructor(private readonly _skipBlocks: ContentBlock.Any['_tag'][] = SKIP_BLOCKS) {}\n\n /**\n * Adds events to the graph.\n */\n addEvents(events: Entity.Unknown[]) {\n const sortedEvents = this.sortObjectsByCreated(events);\n for (const event of sortedEvents) {\n if (Obj.instanceOf(Message.Message, event)) {\n this._processMessage(event);\n } else if (Obj.instanceOf(AgentStatus, event)) {\n this._processAgentStatus(event);\n }\n }\n }\n\n /**\n * Processes a message event and creates commits for its blocks.\n */\n private _processMessage(message: Message.Message) {\n const messageCommits = messageToCommits(\n message,\n this._lastBlockId,\n this._toolCallCommitIds,\n this._lastCommitByBranch,\n this._skipBlocks,\n );\n this._commits.push(...messageCommits);\n\n // Track branches.\n messageCommits.forEach((commit) => this._branchNames.add(commit.branch));\n\n // Track tool calls and update pending tool results.\n this._trackToolCalls(messageCommits);\n\n // Update last block ID for sequential chaining.\n if (messageCommits.length > 0) {\n this._lastBlockId = messageCommits[messageCommits.length - 1].id;\n }\n }\n\n /**\n * Processes an AgentStatus event.\n */\n private _processAgentStatus(event: AgentStatus) {\n const branch = this._getToolCallBranch(event.parentMessage, event.toolCallId);\n this._branchNames.add(branch);\n\n const agentStatusCommit: Commit = {\n id: event.id,\n branch,\n message: event.message,\n icon: IconType.FLAG,\n parents: this._getAgentStatusParents(event),\n timestamp: new Date(event.created),\n };\n\n this._commits.push(agentStatusCommit);\n\n // Update last block ID for sequential chaining.\n this._lastBlockId = event.id;\n\n // Update pending tool results to point to this AgentStatus.\n if (event.toolCallId) {\n this._lastCommitByBranch.set(branch, event.id);\n this._updatePendingToolResults(event.toolCallId, event.id);\n }\n }\n\n /**\n * Stable sort: objects with 'created' field are sorted by it, others remain in place.\n */\n private sortObjectsByCreated(objects: any[]) {\n const sortedObjects = objects.slice();\n // Find indices of objects with 'created'\n const createdObjects: { idx: number; obj: any }[] = [];\n sortedObjects.forEach((obj, idx) => {\n if (obj && typeof obj.created === 'string') {\n createdObjects.push({ idx, obj });\n }\n });\n // Sort only the objects with 'created' by their date, stable\n createdObjects.sort((a, b) => {\n const aDate = new Date(a.obj.created).getTime();\n const bDate = new Date(b.obj.created).getTime();\n return aDate - bDate;\n });\n // Place sorted createdObjects back into their original positions\n let createdIdx = 0;\n return sortedObjects.map((obj, _idx) => {\n if (obj && typeof obj.created === 'string') {\n return createdObjects[createdIdx++].obj;\n }\n return obj;\n });\n }\n\n /**\n * Returns the current state of the graph.\n */\n getGraph(lastRequest = false): { branches: string[]; commits: Commit[] } {\n const idx = lastRequest ? this._commits.findLastIndex((c) => c.tags?.includes('user')) : -1;\n const commits = idx === -1 ? this._commits : this._commits.slice(idx);\n\n return {\n branches: Array.from(this._branchNames),\n commits,\n };\n }\n\n /**\n * Tracks tool calls and handles pending tool result updates.\n */\n private _trackToolCalls(commits: Commit[]) {\n commits.forEach((commit) => {\n if (this._isToolCallCommit(commit)) {\n const toolCallId = this._extractToolCallId(commit.id);\n if (toolCallId) {\n this._toolCallCommitIds.set(toolCallId, commit.id);\n }\n }\n\n if (this._isToolResultCommit(commit)) {\n const toolCallId = this._extractToolCallId(commit.id);\n if (toolCallId) {\n this._pendingToolResults.set(toolCallId, commit.id);\n this._handleLateToolResultUpdate(toolCallId, commit.id);\n }\n }\n\n this._lastCommitByBranch.set(commit.branch, commit.id);\n });\n }\n\n /**\n * Updates pending tool results to point to an AgentStatus event.\n */\n private _updatePendingToolResults(toolCallId: string, agentStatusId: string) {\n const toolResultId = this._pendingToolResults.get(toolCallId);\n if (!toolResultId) {\n return;\n }\n\n const toolResultCommit = this._commits.find((c) => c.id === toolResultId);\n if (!toolResultCommit) {\n return;\n }\n\n const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);\n if (!toolCallCommitId) {\n return;\n }\n\n // Replace tool call commit with AgentStatus commit.\n const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);\n if (toolCallIndex !== -1) {\n toolResultCommit.parents[toolCallIndex] = agentStatusId;\n }\n\n this._pendingToolResults.delete(toolCallId);\n }\n\n /**\n * Handles late updates when AgentStatus is processed before tool result.\n */\n private _handleLateToolResultUpdate(toolCallId: string, toolResultId: string) {\n const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);\n if (!toolCallCommitId) {\n return;\n }\n\n const messageId = toolCallCommitId.split('_toolCall_')[0];\n const toolCallBranch = `${messageId}_${toolCallId}`;\n const agentStatusCommitId = this._lastCommitByBranch.get(toolCallBranch);\n\n if (agentStatusCommitId && agentStatusCommitId !== toolCallCommitId) {\n const toolResultCommit = this._commits.find((c) => c.id === toolResultId);\n if (toolResultCommit) {\n const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);\n if (toolCallIndex !== -1) {\n toolResultCommit.parents[toolCallIndex] = agentStatusCommitId;\n }\n }\n this._pendingToolResults.delete(toolCallId);\n }\n }\n\n /**\n * Gets the branch name for a tool call.\n */\n private _getToolCallBranch(parentMessage?: EntityId, toolCallId?: string): string {\n if (toolCallId) {\n return toolCallId;\n }\n return parentMessage || 'main';\n }\n\n /**\n * Gets the parents for an AgentStatus commit.\n */\n private _getAgentStatusParents(event: AgentStatus): string[] {\n if (event.parentMessage && event.toolCallId) {\n return [getToolCallId(event.parentMessage, event.toolCallId)];\n }\n return [];\n }\n\n /**\n * Checks if a commit is a tool call commit.\n */\n private _isToolCallCommit(commit: Commit): boolean {\n return commit.message.includes('Calling tool');\n }\n\n /**\n * Checks if a commit is a tool result commit.\n */\n private _isToolResultCommit(commit: Commit): boolean {\n return commit.message.includes('Tool call succeeded') || commit.message.includes('Tool error:');\n }\n\n /**\n * Extracts tool call ID from a commit ID.\n */\n private _extractToolCallId(commitId: string): string | undefined {\n const toolCallMatch = commitId.match(/_toolCall_(.+)$/);\n const toolResultMatch = commitId.match(/_toolResult_(.+)$/);\n return toolCallMatch?.[1] || toolResultMatch?.[1];\n }\n}\n\n/**\n * Creates commits for all blocks in a message.\n */\n// TODO(burdon): Pass in AiToolProvider.\nconst messageToCommits = (\n message: Message.Message,\n lastBlockId?: string,\n toolCallIds?: Map<string, string>,\n lastCommitByBranch?: Map<string, string>,\n skipBlocks?: ContentBlock.Any['_tag'][],\n): Commit[] => {\n let previousBlockId: string | undefined = lastBlockId;\n\n return message.blocks\n .map((block, idx) => {\n const branch = getMessageBranch(message);\n const parents = getBlockParents(block, previousBlockId, message, toolCallIds, lastCommitByBranch);\n if (skipBlocks?.includes(block._tag)) {\n return null;\n }\n\n const commit = createBlockCommit(block, message, branch, parents, idx);\n if (commit) {\n previousBlockId = commit.id;\n }\n\n return commit;\n })\n .filter(isNonNullable);\n};\n\n/**\n * Determines the parent commits for a block based on its type and context.\n */\nconst getBlockParents = (\n block: ContentBlock.Any,\n previousBlockId: string | undefined,\n message: Message.Message,\n toolCallIds?: Map<string, string>,\n lastCommitByBranch?: Map<string, string>,\n): string[] => {\n const parents: string[] = [];\n\n if (block._tag === 'toolResult') {\n // Tool results have two parents: previous block and last block from tool call branch.\n if (previousBlockId) {\n parents.push(previousBlockId);\n }\n\n const toolCallCommitId = toolCallIds?.get(block.toolCallId);\n if (toolCallCommitId) {\n const toolCallBranch = getToolCallBranchFromCommitId(toolCallCommitId, block.toolCallId);\n const lastBlockFromToolCallBranch = lastCommitByBranch?.get(toolCallBranch);\n\n if (lastBlockFromToolCallBranch) {\n parents.push(lastBlockFromToolCallBranch);\n } else {\n // Fallback to tool call commit if no AgentStatus exists yet.\n parents.push(toolCallCommitId);\n }\n }\n } else {\n // For other blocks, use previous block as parent (or tool call parent for first block).\n if (previousBlockId) {\n parents.push(previousBlockId);\n } else {\n const toolCallParent = getParentId(message);\n if (toolCallParent) {\n parents.push(toolCallParent);\n }\n }\n }\n\n return parents;\n};\n\n/**\n * Creates a commit for a specific block.\n */\nconst createBlockCommit = (\n block: ContentBlock.Any,\n message: Message.Message,\n branch: string,\n parents: string[],\n idx: number,\n): Commit | null => {\n const timestamp = new Date(message.created);\n switch (block._tag) {\n case 'text':\n if (!block.text.trim().length) {\n return null;\n }\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n ...(message.sender.role === 'user'\n ? {\n icon: IconType.USER,\n tags: ['user'],\n message: 'Processing request...',\n }\n : {\n icon: IconType.AGENT,\n message: `Response (${block.text.split(' ').length} words)`,\n }),\n } satisfies Commit;\n\n case 'toolCall':\n return {\n id: getToolCallId(message.id, block.toolCallId),\n branch,\n parents,\n timestamp,\n icon: IconType.TOOL,\n level: LogLevel.INFO,\n message: `Calling tool (${block.name})`,\n } satisfies Commit;\n\n case 'toolResult':\n return {\n id: getToolResultId(message.id, block.toolCallId),\n branch,\n parents,\n timestamp,\n icon: block.error ? IconType.ERROR : IconType.SUCCESS,\n level: block.error ? LogLevel.ERROR : LogLevel.INFO,\n message: block.error ? 'Tool error: ' + block.error : 'Tool call succeeded',\n } satisfies Commit;\n\n case 'stats':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n icon: IconType.ROCKET,\n level: LogLevel.INFO,\n message: ContentBlock.createStatsMessage(block),\n } satisfies Commit;\n\n case 'status':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n message: block.statusText,\n level: LogLevel.INFO,\n icon: IconType.FLAG,\n } satisfies Commit;\n\n case 'reasoning':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n message: block.reasoningText ?? 'Thinking...',\n icon: IconType.THINKING,\n } satisfies Commit;\n\n case 'reference':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n icon: IconType.LINK,\n message: stringifyRef(block.reference),\n } satisfies Commit;\n\n default:\n return null;\n }\n};\n\n/**\n * Gets the tool call branch name from a tool call commit ID.\n */\nconst getToolCallBranchFromCommitId = (toolCallCommitId: string, toolCallId: string): string => {\n const messageId = toolCallCommitId.split('_toolCall_')[0];\n return `${messageId}_${toolCallId}`;\n};\n\nconst getToolCallId = (messageId: EntityId, toolCallId: string) => `${messageId}_toolCall_${toolCallId}`;\nconst getToolResultId = (messageId: EntityId, toolCallId: string) => `${messageId}_toolResult_${toolCallId}`;\nconst getGenericBlockId = (messageId: EntityId, idx: number) => `${messageId}_block_${idx}`;\n\nconst getBranchName = (options: { parentMessage?: EntityId; toolCallId?: string }) => {\n if (options.toolCallId) {\n return options.toolCallId;\n } else if (options.parentMessage) {\n return options.parentMessage;\n } else {\n return 'main';\n }\n};\n\nconst getMessageBranch = (message: Message.Message) => {\n return getBranchName({\n parentMessage: message.parentMessage,\n toolCallId: message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID],\n });\n};\n\nconst getParentId = (message: Message.Message) => {\n if (message.parentMessage && message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]) {\n return getToolCallId(message.parentMessage, message.properties[MESSAGE_PROPERTY_TOOL_CALL_ID]);\n } else {\n return undefined;\n }\n};\n\nconst stringifyRef = (ref: Ref.Unknown) => {\n if (ref.target) {\n return stringifyObject(ref.target);\n }\n\n const echoUri = EID.tryParse(ref.uri);\n return (echoUri ? EID.getEntityId(echoUri) : undefined) ?? '';\n};\n\nconst stringifyObject = (obj: Obj.Unknown) => {\n return Obj.getLabel(obj) ?? Obj.getTypename(obj) ?? obj.id;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExecutionGraph.mjs","names":["EID"],"sources":["../../src/util/execution-graph.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { AgentStatus } from '@dxos/ai';\nimport { MESSAGE_PROPERTY_TOOL_CALL_ID } from '@dxos/compute-runtime';\nimport { type Entity, Obj, type Ref } from '@dxos/echo';\nimport { EID, type EntityId } from '@dxos/keys';\nimport { LogLevel } from '@dxos/log';\nimport { ContentBlock, Message } from '@dxos/types';\nimport { isNonNullable } from '@dxos/util';\n\nconst SKIP_BLOCKS: ContentBlock.Any['_tag'][] = ['text'];\n\n/**\n * Mercurial-style Commit.\n */\n// TODO(wittjosiah): Reconcile with @dxos/react-ui-components.\nexport type Commit = {\n id: string;\n parents: string[];\n branch: string;\n icon?: string;\n level?: LogLevel;\n message: string;\n timestamp?: Date;\n tags?: string[];\n};\n\n// TODO(burdon): Add colors?\nenum IconType {\n // General status.\n SUCCESS = 'ph--check-circle--regular',\n WARNING = 'ph--warning-circle--regular',\n ERROR = 'ph--x-circle--regular',\n FLAG = 'ph--flag--regular',\n ROCKET = 'ph--rocket--regular',\n TIMER = 'ph--timer--regular',\n\n // Interactions.\n USER = 'ph--user--regular',\n USER_INTERACTION = 'ph--user-sound--regular',\n AGENT = 'ph--drone--regular',\n THINKING = 'ph--brain--regular',\n LINK = 'ph--link--regular',\n TOOL = 'ph--wrench--regular',\n}\n\n/**\n * Models the execution graph based on a stream of events.\n */\nexport class ExecutionGraph {\n // TODO(dmaretskyi): Evolve the internal state to support chats, functions, circuit workflows, etc.\n private _commits: Commit[] = [];\n private _branchNames = new Set<string>();\n private _lastBlockId: string | undefined;\n\n // Tool call tracking.\n private _toolCallCommitIds = new Map<string, string>(); // toolCallId -> commitId\n private _lastCommitByBranch = new Map<string, string>(); // branch -> last commitId\n private _pendingToolResults = new Map<string, string>(); // toolCallId -> toolResultCommitId\n\n constructor(private readonly _skipBlocks: ContentBlock.Any['_tag'][] = SKIP_BLOCKS) {}\n\n /**\n * Adds events to the graph.\n */\n addEvents(events: Entity.Unknown[]) {\n const sortedEvents = this.sortObjectsByCreated(events);\n for (const event of sortedEvents) {\n if (Obj.instanceOf(Message.Message, event)) {\n this._processMessage(event);\n } else if (Obj.instanceOf(AgentStatus, event)) {\n this._processAgentStatus(event);\n }\n }\n }\n\n /**\n * Processes a message event and creates commits for its blocks.\n */\n private _processMessage(message: Message.Message) {\n const messageCommits = messageToCommits(\n message,\n this._lastBlockId,\n this._toolCallCommitIds,\n this._lastCommitByBranch,\n this._skipBlocks,\n );\n this._commits.push(...messageCommits);\n\n // Track branches.\n messageCommits.forEach((commit) => this._branchNames.add(commit.branch));\n\n // Track tool calls and update pending tool results.\n this._trackToolCalls(messageCommits);\n\n // Update last block ID for sequential chaining.\n if (messageCommits.length > 0) {\n this._lastBlockId = messageCommits[messageCommits.length - 1].id;\n }\n }\n\n /**\n * Processes an AgentStatus event.\n */\n private _processAgentStatus(event: AgentStatus) {\n const branch = this._getToolCallBranch(event.parentMessage, event.toolCallId);\n this._branchNames.add(branch);\n\n const agentStatusCommit: Commit = {\n id: event.id,\n branch,\n message: event.message,\n icon: IconType.FLAG,\n parents: this._getAgentStatusParents(event),\n timestamp: new Date(event.created),\n };\n\n this._commits.push(agentStatusCommit);\n\n // Update last block ID for sequential chaining.\n this._lastBlockId = event.id;\n\n // Update pending tool results to point to this AgentStatus.\n if (event.toolCallId) {\n this._lastCommitByBranch.set(branch, event.id);\n this._updatePendingToolResults(event.toolCallId, event.id);\n }\n }\n\n /**\n * Stable sort: objects with 'created' field are sorted by it, others remain in place.\n */\n private sortObjectsByCreated(objects: any[]) {\n const sortedObjects = objects.slice();\n // Find indices of objects with 'created'\n const createdObjects: { idx: number; obj: any }[] = [];\n sortedObjects.forEach((obj, idx) => {\n if (obj && typeof obj.created === 'string') {\n createdObjects.push({ idx, obj });\n }\n });\n // Sort only the objects with 'created' by their date, stable\n createdObjects.sort((a, b) => {\n const aDate = new Date(a.obj.created).getTime();\n const bDate = new Date(b.obj.created).getTime();\n return aDate - bDate;\n });\n // Place sorted createdObjects back into their original positions\n let createdIdx = 0;\n return sortedObjects.map((obj, _idx) => {\n if (obj && typeof obj.created === 'string') {\n return createdObjects[createdIdx++].obj;\n }\n return obj;\n });\n }\n\n /**\n * Returns the current state of the graph.\n */\n getGraph(lastRequest = false): { branches: string[]; commits: Commit[] } {\n const idx = lastRequest ? this._commits.findLastIndex((c) => c.tags?.includes('user')) : -1;\n const commits = idx === -1 ? this._commits : this._commits.slice(idx);\n\n return {\n branches: Array.from(this._branchNames),\n commits,\n };\n }\n\n /**\n * Tracks tool calls and handles pending tool result updates.\n */\n private _trackToolCalls(commits: Commit[]) {\n commits.forEach((commit) => {\n if (this._isToolCallCommit(commit)) {\n const toolCallId = this._extractToolCallId(commit.id);\n if (toolCallId) {\n this._toolCallCommitIds.set(toolCallId, commit.id);\n }\n }\n\n if (this._isToolResultCommit(commit)) {\n const toolCallId = this._extractToolCallId(commit.id);\n if (toolCallId) {\n this._pendingToolResults.set(toolCallId, commit.id);\n this._handleLateToolResultUpdate(toolCallId, commit.id);\n }\n }\n\n this._lastCommitByBranch.set(commit.branch, commit.id);\n });\n }\n\n /**\n * Updates pending tool results to point to an AgentStatus event.\n */\n private _updatePendingToolResults(toolCallId: string, agentStatusId: string) {\n const toolResultId = this._pendingToolResults.get(toolCallId);\n if (!toolResultId) {\n return;\n }\n\n const toolResultCommit = this._commits.find((c) => c.id === toolResultId);\n if (!toolResultCommit) {\n return;\n }\n\n const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);\n if (!toolCallCommitId) {\n return;\n }\n\n // Replace tool call commit with AgentStatus commit.\n const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);\n if (toolCallIndex !== -1) {\n toolResultCommit.parents[toolCallIndex] = agentStatusId;\n }\n\n this._pendingToolResults.delete(toolCallId);\n }\n\n /**\n * Handles late updates when AgentStatus is processed before tool result.\n */\n private _handleLateToolResultUpdate(toolCallId: string, toolResultId: string) {\n const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);\n if (!toolCallCommitId) {\n return;\n }\n\n const messageId = toolCallCommitId.split('_toolCall_')[0];\n const toolCallBranch = `${messageId}_${toolCallId}`;\n const agentStatusCommitId = this._lastCommitByBranch.get(toolCallBranch);\n\n if (agentStatusCommitId && agentStatusCommitId !== toolCallCommitId) {\n const toolResultCommit = this._commits.find((c) => c.id === toolResultId);\n if (toolResultCommit) {\n const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);\n if (toolCallIndex !== -1) {\n toolResultCommit.parents[toolCallIndex] = agentStatusCommitId;\n }\n }\n this._pendingToolResults.delete(toolCallId);\n }\n }\n\n /**\n * Gets the branch name for a tool call.\n */\n private _getToolCallBranch(parentMessage?: EntityId, toolCallId?: string): string {\n if (toolCallId) {\n return toolCallId;\n }\n return parentMessage || 'main';\n }\n\n /**\n * Gets the parents for an AgentStatus commit.\n */\n private _getAgentStatusParents(event: AgentStatus): string[] {\n if (event.parentMessage && event.toolCallId) {\n return [getToolCallId(event.parentMessage, event.toolCallId)];\n }\n return [];\n }\n\n /**\n * Checks if a commit is a tool call commit.\n */\n private _isToolCallCommit(commit: Commit): boolean {\n return commit.message.includes('Calling tool');\n }\n\n /**\n * Checks if a commit is a tool result commit.\n */\n private _isToolResultCommit(commit: Commit): boolean {\n return commit.message.includes('Tool call succeeded') || commit.message.includes('Tool error:');\n }\n\n /**\n * Extracts tool call ID from a commit ID.\n */\n private _extractToolCallId(commitId: string): string | undefined {\n const toolCallMatch = commitId.match(/_toolCall_(.+)$/);\n const toolResultMatch = commitId.match(/_toolResult_(.+)$/);\n return toolCallMatch?.[1] || toolResultMatch?.[1];\n }\n}\n\n/**\n * Creates commits for all blocks in a message.\n */\n// TODO(burdon): Pass in AiToolProvider.\nconst messageToCommits = (\n message: Message.Message,\n lastBlockId?: string,\n toolCallIds?: Map<string, string>,\n lastCommitByBranch?: Map<string, string>,\n skipBlocks?: ContentBlock.Any['_tag'][],\n): Commit[] => {\n let previousBlockId: string | undefined = lastBlockId;\n\n return message.blocks\n .map((block, idx) => {\n const branch = getMessageBranch(message);\n const parents = getBlockParents(block, previousBlockId, message, toolCallIds, lastCommitByBranch);\n if (skipBlocks?.includes(block._tag)) {\n return null;\n }\n\n const commit = createBlockCommit(block, message, branch, parents, idx);\n if (commit) {\n previousBlockId = commit.id;\n }\n\n return commit;\n })\n .filter(isNonNullable);\n};\n\n/**\n * Determines the parent commits for a block based on its type and context.\n */\nconst getBlockParents = (\n block: ContentBlock.Any,\n previousBlockId: string | undefined,\n message: Message.Message,\n toolCallIds?: Map<string, string>,\n lastCommitByBranch?: Map<string, string>,\n): string[] => {\n const parents: string[] = [];\n\n if (block._tag === 'toolResult') {\n // Tool results have two parents: previous block and last block from tool call branch.\n if (previousBlockId) {\n parents.push(previousBlockId);\n }\n\n const toolCallCommitId = toolCallIds?.get(block.toolCallId);\n if (toolCallCommitId) {\n const toolCallBranch = getToolCallBranchFromCommitId(toolCallCommitId, block.toolCallId);\n const lastBlockFromToolCallBranch = lastCommitByBranch?.get(toolCallBranch);\n\n if (lastBlockFromToolCallBranch) {\n parents.push(lastBlockFromToolCallBranch);\n } else {\n // Fallback to tool call commit if no AgentStatus exists yet.\n parents.push(toolCallCommitId);\n }\n }\n } else {\n // For other blocks, use previous block as parent (or tool call parent for first block).\n if (previousBlockId) {\n parents.push(previousBlockId);\n } else {\n const toolCallParent = getParentId(message);\n if (toolCallParent) {\n parents.push(toolCallParent);\n }\n }\n }\n\n return parents;\n};\n\n/**\n * Creates a commit for a specific block.\n */\nconst createBlockCommit = (\n block: ContentBlock.Any,\n message: Message.Message,\n branch: string,\n parents: string[],\n idx: number,\n): Commit | null => {\n const timestamp = new Date(message.created);\n switch (block._tag) {\n case 'text':\n if (!block.text.trim().length) {\n return null;\n }\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n ...(message.sender.role === 'user'\n ? {\n icon: IconType.USER,\n tags: ['user'],\n message: 'Processing request...',\n }\n : {\n icon: IconType.AGENT,\n message: `Response (${block.text.split(' ').length} words)`,\n }),\n } satisfies Commit;\n\n case 'toolCall':\n return {\n id: getToolCallId(message.id, block.toolCallId),\n branch,\n parents,\n timestamp,\n icon: IconType.TOOL,\n level: LogLevel.INFO,\n message: `Calling tool (${block.name})`,\n } satisfies Commit;\n\n case 'toolResult':\n return {\n id: getToolResultId(message.id, block.toolCallId),\n branch,\n parents,\n timestamp,\n icon: block.error ? IconType.ERROR : IconType.SUCCESS,\n level: block.error ? LogLevel.ERROR : LogLevel.INFO,\n message: block.error ? 'Tool error: ' + block.error : 'Tool call succeeded',\n } satisfies Commit;\n\n case 'stats':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n icon: IconType.ROCKET,\n level: LogLevel.INFO,\n message: ContentBlock.createStatsMessage(block),\n } satisfies Commit;\n\n case 'status':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n message: block.statusText,\n level: LogLevel.INFO,\n icon: IconType.FLAG,\n } satisfies Commit;\n\n case 'reasoning':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n message: block.reasoningText ?? 'Thinking...',\n icon: IconType.THINKING,\n } satisfies Commit;\n\n case 'reference':\n return {\n id: getGenericBlockId(message.id, idx),\n branch,\n parents,\n timestamp,\n icon: IconType.LINK,\n message: stringifyRef(block.reference),\n } satisfies Commit;\n\n default:\n return null;\n }\n};\n\n/**\n * Gets the tool call branch name from a tool call commit ID.\n */\nconst getToolCallBranchFromCommitId = (toolCallCommitId: string, toolCallId: string): string => {\n const messageId = toolCallCommitId.split('_toolCall_')[0];\n return `${messageId}_${toolCallId}`;\n};\n\nconst getToolCallId = (messageId: EntityId, toolCallId: string) => `${messageId}_toolCall_${toolCallId}`;\nconst getToolResultId = (messageId: EntityId, toolCallId: string) => `${messageId}_toolResult_${toolCallId}`;\nconst getGenericBlockId = (messageId: EntityId, idx: number) => `${messageId}_block_${idx}`;\n\nconst getBranchName = (options: { parentMessage?: EntityId; toolCallId?: string }) => {\n if (options.toolCallId) {\n return options.toolCallId;\n } else if (options.parentMessage) {\n return options.parentMessage;\n } else {\n return 'main';\n }\n};\n\nconst getMessageBranch = (message: Message.Message) => {\n return getBranchName({\n parentMessage: message.parentMessage,\n toolCallId: message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID],\n });\n};\n\nconst getParentId = (message: Message.Message) => {\n if (message.parentMessage && message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]) {\n return getToolCallId(message.parentMessage, message.properties[MESSAGE_PROPERTY_TOOL_CALL_ID]);\n } else {\n return undefined;\n }\n};\n\nconst stringifyRef = (ref: Ref.Unknown) => {\n if (ref.target) {\n return stringifyObject(ref.target);\n }\n\n const echoUri = EID.tryParse(ref.uri);\n return (echoUri ? EID.getEntityId(echoUri) : undefined) ?? '';\n};\n\nconst stringifyObject = (obj: Obj.Unknown) => {\n return Obj.getLabel(obj) ?? Obj.getTypename(obj) ?? obj.id;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
import { r as HarnessContextError } from "./chunk-errors.mjs";
|
|
3
|
+
import { d as linkCompanion, m as make, t as Chat } from "./chunk-Chat.mjs";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Schema from "effect/Schema";
|
|
6
|
+
import { Annotation, DXN, Database, Feed, Filter, Obj, Ref, Type } from "@dxos/echo";
|
|
7
|
+
import { IdentityDid } from "@dxos/keys";
|
|
8
|
+
import * as Skill from "@dxos/compute/Skill";
|
|
9
|
+
import * as EffectEx from "@dxos/effect/EffectEx";
|
|
10
|
+
import * as Instructions from "@dxos/compute/Instructions";
|
|
11
|
+
//#region src/types/Agent.ts
|
|
12
|
+
var Agent_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
Agent: () => Agent,
|
|
14
|
+
chatLocation: () => chatLocation,
|
|
15
|
+
getFromChatContext: () => getFromChatContext,
|
|
16
|
+
isAgentChat: () => isAgentChat,
|
|
17
|
+
loadChat: () => loadChat,
|
|
18
|
+
loadForChat: () => loadForChat,
|
|
19
|
+
loadInstructions: () => loadInstructions,
|
|
20
|
+
makeInitialized: () => makeInitialized,
|
|
21
|
+
resetChatHistory: () => resetChatHistory
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* An agent identity: a personality (attribution DID) plus its preset payload (instructions with
|
|
25
|
+
* text, skills, objects, and commands). Owns no conversation state — a chat is linked to the agent
|
|
26
|
+
* it runs as by the ECHO parent edge (the agent parents its chats), and durable work products live on a Project
|
|
27
|
+
* (plugin-projects DESIGN.md, "Agent ↔ Project convergence").
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* The AI session runtime (MCP SDK, Anthropic client, ~280 KB) is loaded lazily: this module holds
|
|
31
|
+
* the Agent *schema*, which core plugins reference for operation definitions, so the runtime loads
|
|
32
|
+
* only when an agent operation actually runs.
|
|
33
|
+
*/
|
|
34
|
+
var aiContextRuntime = () => import("./session/AiContext.mjs");
|
|
35
|
+
var harnessRuntime = () => import("./session/Harness.mjs");
|
|
36
|
+
var Agent = class extends Type.makeObject(DXN.make("org.dxos.type.agent", "0.2.0"))(Schema.Struct({
|
|
37
|
+
name: Schema.optional(Schema.String),
|
|
38
|
+
/**
|
|
39
|
+
* Stable identity DID for the agent, used to attribute content it authors (e.g. suggestion
|
|
40
|
+
* branches) as its own author — distinct from any human or other agent. Provisioned
|
|
41
|
+
* externally, never minted by {@link makeInitialized} (see the note there); the slot a real
|
|
42
|
+
* HALO identity DID takes once agents get first-class identities. Optional because nothing
|
|
43
|
+
* populates it yet.
|
|
44
|
+
*/
|
|
45
|
+
did: Schema.optional(IdentityDid.annotate({
|
|
46
|
+
title: "DID",
|
|
47
|
+
description: "The agent's identity DID; attributes content the agent authors."
|
|
48
|
+
})).annotate({
|
|
49
|
+
title: "DID",
|
|
50
|
+
description: "The agent's DID."
|
|
51
|
+
}),
|
|
52
|
+
/**
|
|
53
|
+
* Master switch for the agent's automation (propagated onto its compiled routine triggers).
|
|
54
|
+
*/
|
|
55
|
+
enabled: Schema.optional(Schema.Boolean).annotate({
|
|
56
|
+
title: "Enabled",
|
|
57
|
+
description: "Master switch for agent automation; propagated to all triggers on sync."
|
|
58
|
+
}),
|
|
59
|
+
/**
|
|
60
|
+
* Instructions for the agent — the preset payload (text, skills, objects, commands) a chat
|
|
61
|
+
* receives when the agent is applied to it. Owned: `SetParent` cascades it with the agent.
|
|
62
|
+
*/
|
|
63
|
+
instructions: Ref.Ref(Instructions.Instructions).pipe(Annotation.SetParent.set(), Annotation.FormInlineAnnotation.set(true), Schema.annotate({ title: "Instructions" }))
|
|
64
|
+
}).pipe(Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
65
|
+
icon: "ph--drone--regular",
|
|
66
|
+
hue: "amber"
|
|
67
|
+
}))) {};
|
|
68
|
+
/**
|
|
69
|
+
* Resolves the agent's instructions and their markdown text.
|
|
70
|
+
*/
|
|
71
|
+
var loadInstructions = (agent) => Effect.gen(function* () {
|
|
72
|
+
const instructions = yield* Database.load(agent.instructions);
|
|
73
|
+
return {
|
|
74
|
+
text: yield* Database.load(instructions.text).pipe(Effect.map((doc) => doc.content), Effect.catchTag("EntityNotFoundError", () => Effect.succeed(""))),
|
|
75
|
+
instructions
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Resolves the agent's primary chat (the agent holds no chat ref): the latest chat parented to the
|
|
80
|
+
* agent. Entity ids are ULIDs, so the id order is creation order — `resetChatHistory` makes the
|
|
81
|
+
* replacement chat primary simply by creating it later.
|
|
82
|
+
*/
|
|
83
|
+
var loadChat = (agent) => Effect.gen(function* () {
|
|
84
|
+
return (yield* Database.query(Filter.and(Filter.type(Chat), Filter.childOf(agent))).run).filter((chat) => Obj.getMeta(chat).keys.length === 0).sort((left, right) => left.id.localeCompare(right.id)).at(-1);
|
|
85
|
+
}).pipe(Effect.orDie);
|
|
86
|
+
/**
|
|
87
|
+
* Resolves the agent a chat runs as, if any. Plain (agentless) chats yield `undefined`.
|
|
88
|
+
* The parent is loaded with the object, so this is synchronous — no query.
|
|
89
|
+
*/
|
|
90
|
+
var loadForChat = (chat) => Effect.sync(() => {
|
|
91
|
+
const parent = Obj.getParent(chat);
|
|
92
|
+
return parent && Obj.instanceOf(Agent, parent) ? parent : void 0;
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Where a chat's turns run: as its `remote` flag says, else on EDGE for an agent's chat, since its chats share
|
|
96
|
+
* the agent's one brain, which only EDGE holds. An explicit `remote: false` keeps an agent's chats local, for
|
|
97
|
+
* a runtime with no EDGE to reach (a story or test), where every chat shares the client's brain instead.
|
|
98
|
+
*/
|
|
99
|
+
var chatLocation = (chat) => chat.remote ?? isAgentChat(chat) ? "edge" : "local";
|
|
100
|
+
/** Whether the chat runs as an agent (the agent parents it); synchronous, like {@link loadForChat}. */
|
|
101
|
+
var isAgentChat = (chat) => {
|
|
102
|
+
const parent = Obj.getParent(chat);
|
|
103
|
+
return parent !== void 0 && Obj.instanceOf(Agent, parent);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Creates a fully initialized Agent with its first chat and context bindings.
|
|
107
|
+
*
|
|
108
|
+
* @param props - Agent properties including spec, skills, and context objects.
|
|
109
|
+
* @param skill - The skill to use for the agent context: a skill object is cloned into the space, a ref
|
|
110
|
+
* (e.g. a registry URI) is bound as-is so the agent follows the compiled skill until customized.
|
|
111
|
+
* @returns An Effect that yields the initialized Agent.
|
|
112
|
+
*/
|
|
113
|
+
var makeInitialized = (props, skill) => Effect.gen(function* () {
|
|
114
|
+
const { skills: propsSkills, contextObjects, ...agentProps } = props;
|
|
115
|
+
const AiContextRuntime = yield* Effect.promise(aiContextRuntime);
|
|
116
|
+
const persistedPropsSkills = yield* Effect.all((propsSkills ?? []).map((ref) => {
|
|
117
|
+
const target = ref.peek();
|
|
118
|
+
return target !== void 0 ? Database.add(target).pipe(Effect.map((persisted) => Ref.make(persisted))) : Effect.succeed(ref);
|
|
119
|
+
}));
|
|
120
|
+
const instructions = yield* Database.add(Instructions.make({
|
|
121
|
+
text: props.instructions,
|
|
122
|
+
skills: persistedPropsSkills
|
|
123
|
+
}));
|
|
124
|
+
const agent = yield* Database.add(Obj.make(Agent, {
|
|
125
|
+
...agentProps,
|
|
126
|
+
instructions: Ref.make(instructions),
|
|
127
|
+
enabled: props.enabled ?? true
|
|
128
|
+
}));
|
|
129
|
+
const feed = yield* Database.add(Feed.make());
|
|
130
|
+
const runtime = yield* Effect.context();
|
|
131
|
+
const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
132
|
+
feed,
|
|
133
|
+
runtime
|
|
134
|
+
}));
|
|
135
|
+
const agentSkill = Ref.isRef(skill) ? skill : Ref.make(yield* Database.add(Obj.clone(skill, { deep: "all" })));
|
|
136
|
+
const chat = yield* Database.add(make({
|
|
137
|
+
feed: Ref.make(feed),
|
|
138
|
+
instructions: Ref.make(instructions)
|
|
139
|
+
}));
|
|
140
|
+
linkCompanion({
|
|
141
|
+
chat,
|
|
142
|
+
subject: agent
|
|
143
|
+
});
|
|
144
|
+
yield* Effect.promise(() => contextBinder.bind({
|
|
145
|
+
skills: [agentSkill, ...persistedPropsSkills],
|
|
146
|
+
objects: [
|
|
147
|
+
Ref.make(agent),
|
|
148
|
+
Ref.make(chat),
|
|
149
|
+
...contextObjects ?? []
|
|
150
|
+
]
|
|
151
|
+
}));
|
|
152
|
+
return agent;
|
|
153
|
+
}).pipe(Effect.scoped);
|
|
154
|
+
/**
|
|
155
|
+
* Resets the agent's chat history by rebuilding the chat context.
|
|
156
|
+
* Preserves the existing skills and objects from the current chat context.
|
|
157
|
+
*
|
|
158
|
+
* @param agent - The agent whose chat history should be reset. Must have an existing chat.
|
|
159
|
+
* @returns An Effect that resets the chat history.
|
|
160
|
+
*/
|
|
161
|
+
var resetChatHistory = (agent) => Effect.gen(function* () {
|
|
162
|
+
const existingChat = yield* loadChat(agent);
|
|
163
|
+
if (!existingChat) return yield* Effect.die(/* @__PURE__ */ new Error("Agent must have an existing chat to reset."));
|
|
164
|
+
const existingFeed = yield* Database.load(existingChat.feed);
|
|
165
|
+
const runtime = yield* Effect.context();
|
|
166
|
+
const AiContextRuntime = yield* Effect.promise(aiContextRuntime);
|
|
167
|
+
const existingContextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
168
|
+
feed: existingFeed,
|
|
169
|
+
runtime
|
|
170
|
+
}));
|
|
171
|
+
const skills = existingContextBinder.getSkills().map(Skill.makeRef);
|
|
172
|
+
const objects = existingContextBinder.getObjects().filter((object) => !Obj.instanceOf(Chat, object)).map((object) => Ref.make(object));
|
|
173
|
+
const feed = yield* Database.add(Feed.make());
|
|
174
|
+
const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
175
|
+
feed,
|
|
176
|
+
runtime
|
|
177
|
+
}));
|
|
178
|
+
const chat = yield* Database.add(make({
|
|
179
|
+
feed: Ref.make(feed),
|
|
180
|
+
instructions: agent.instructions
|
|
181
|
+
}));
|
|
182
|
+
linkCompanion({
|
|
183
|
+
chat,
|
|
184
|
+
subject: agent
|
|
185
|
+
});
|
|
186
|
+
yield* Effect.promise(() => contextBinder.bind({
|
|
187
|
+
skills,
|
|
188
|
+
objects: [...objects, Ref.make(chat)]
|
|
189
|
+
}));
|
|
190
|
+
}).pipe(Effect.scoped);
|
|
191
|
+
var getFromChatContext = Effect.gen(function* () {
|
|
192
|
+
const agents = yield* (yield* Effect.promise(harnessRuntime)).queryContext(Filter.type(Agent));
|
|
193
|
+
if (agents.length !== 1) return yield* Effect.fail(new HarnessContextError({
|
|
194
|
+
type: "agent",
|
|
195
|
+
count: agents.length
|
|
196
|
+
}));
|
|
197
|
+
return agents[0];
|
|
198
|
+
});
|
|
199
|
+
//#endregion
|
|
200
|
+
export { isAgentChat as a, loadInstructions as c, getFromChatContext as i, makeInitialized as l, Agent_exports as n, loadChat as o, chatLocation as r, loadForChat as s, Agent as t, resetChatHistory as u };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=chunk-Agent.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-Agent.mjs","names":["Chat.Chat","Chat.make"],"sources":["../../src/types/Agent.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport type * as AgentService from '@dxos/compute/AgentService';\nimport * as Instructions from '@dxos/compute/Instructions';\nimport * as Skill from '@dxos/compute/Skill';\nimport { Annotation, Database, DXN, type Error as EchoError, Feed, Filter, Obj, Ref, Type } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { IdentityDid } from '@dxos/keys';\n\nimport type * as Harness from '../session/Harness.ts';\nimport { HarnessContextError } from '../util/errors.ts';\nimport * as Chat from './Chat.ts';\n\n/**\n * An agent identity: a personality (attribution DID) plus its preset payload (instructions with\n * text, skills, objects, and commands). Owns no conversation state — a chat is linked to the agent\n * it runs as by the ECHO parent edge (the agent parents its chats), and durable work products live on a Project\n * (plugin-projects DESIGN.md, \"Agent ↔ Project convergence\").\n */\n\n/**\n * The AI session runtime (MCP SDK, Anthropic client, ~280 KB) is loaded lazily: this module holds\n * the Agent *schema*, which core plugins reference for operation definitions, so the runtime loads\n * only when an agent operation actually runs.\n */\nconst aiContextRuntime = () => import('../session/AiContext.ts');\nconst harnessRuntime = () => import('../session/Harness.ts');\n\nexport class Agent extends Type.makeObject<Agent>(DXN.make('org.dxos.type.agent', '0.2.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n\n /**\n * Stable identity DID for the agent, used to attribute content it authors (e.g. suggestion\n * branches) as its own author — distinct from any human or other agent. Provisioned\n * externally, never minted by {@link makeInitialized} (see the note there); the slot a real\n * HALO identity DID takes once agents get first-class identities. Optional because nothing\n * populates it yet.\n */\n did: Schema.optional(\n // Annotated inside `optional`: the form reads the title from the value schema, not the wrapper.\n IdentityDid.annotate({\n title: 'DID',\n description: \"The agent's identity DID; attributes content the agent authors.\",\n }),\n ).annotate({\n title: 'DID',\n description: \"The agent's DID.\",\n }),\n\n /**\n * Master switch for the agent's automation (propagated onto its compiled routine triggers).\n */\n enabled: Schema.optional(Schema.Boolean).annotate({\n title: 'Enabled',\n description: 'Master switch for agent automation; propagated to all triggers on sync.',\n }),\n\n /**\n * Instructions for the agent — the preset payload (text, skills, objects, commands) a chat\n * receives when the agent is applied to it. Owned: `SetParent` cascades it with the agent.\n */\n instructions: Ref.Ref(Instructions.Instructions).pipe(\n Annotation.SetParent.set(),\n Annotation.FormInlineAnnotation.set(true),\n Schema.annotate({ title: 'Instructions' }),\n ),\n // No `UserType`: agents are managed in space settings, not listed with the space's content.\n }).pipe(\n Annotation.LabelAnnotation.set(['name']),\n Annotation.IconAnnotation.set({ icon: 'ph--drone--regular', hue: 'amber' }),\n ),\n) {}\n\n/**\n * Resolves the agent's instructions and their markdown text.\n */\nexport const loadInstructions = (\n agent: Agent,\n): Effect.Effect<\n { text: string; instructions: Instructions.Instructions },\n EchoError.EntityNotFoundError,\n Database.Service\n> =>\n Effect.gen(function* () {\n const instructions = yield* Database.load(agent.instructions);\n const text = yield* Database.load(instructions.text).pipe(\n Effect.map((doc) => doc.content),\n Effect.catchTag('EntityNotFoundError', () => Effect.succeed('')),\n );\n return { text, instructions };\n });\n\n/**\n * Resolves the agent's primary chat (the agent holds no chat ref): the latest chat parented to the\n * agent. Entity ids are ULIDs, so the id order is creation order — `resetChatHistory` makes the\n * replacement chat primary simply by creating it later.\n */\nexport const loadChat = (agent: Agent): Effect.Effect<Chat.Chat | undefined, never, Database.Service> =>\n Effect.gen(function* () {\n // A child-of filter rather than a `.children()` traversal: EDGE's query planner has no hierarchy\n // traversal, so an agent hosted there could not resolve its chat.\n const chats = yield* Database.query(Filter.and(Filter.type(Chat.Chat), Filter.childOf(agent))).run;\n return (\n chats\n // Chats bridged from an external conversation (a Discord thread) carry its foreign key; they are\n // the agent's too, but never its own primary chat.\n .filter((chat) => Obj.getMeta(chat).keys.length === 0)\n .sort((left, right) => left.id.localeCompare(right.id))\n .at(-1)\n );\n }).pipe(Effect.orDie);\n\n/**\n * Resolves the agent a chat runs as, if any. Plain (agentless) chats yield `undefined`.\n * The parent is loaded with the object, so this is synchronous — no query.\n */\nexport const loadForChat = (chat: Chat.Chat): Effect.Effect<Agent | undefined> =>\n Effect.sync(() => {\n const parent = Obj.getParent(chat);\n return parent && Obj.instanceOf(Agent, parent) ? parent : undefined;\n });\n\n/**\n * Where a chat's turns run: as its `remote` flag says, else on EDGE for an agent's chat, since its chats share\n * the agent's one brain, which only EDGE holds. An explicit `remote: false` keeps an agent's chats local, for\n * a runtime with no EDGE to reach (a story or test), where every chat shares the client's brain instead.\n */\nexport const chatLocation = (chat: Chat.Chat): AgentService.AgentLocation =>\n (chat.remote ?? isAgentChat(chat)) ? 'edge' : 'local';\n\n/** Whether the chat runs as an agent (the agent parents it); synchronous, like {@link loadForChat}. */\nexport const isAgentChat = (chat: Chat.Chat): boolean => {\n const parent = Obj.getParent(chat);\n return parent !== undefined && Obj.instanceOf(Agent, parent);\n};\n\nexport type MakeProps = Omit<Obj.MakeProps<typeof Agent>, 'instructions'> & {\n instructions: string;\n skills?: Ref.Ref<Skill.Skill>[];\n contextObjects?: Ref.Ref<Obj.Any>[];\n};\n\n/**\n * Creates a fully initialized Agent with its first chat and context bindings.\n *\n * @param props - Agent properties including spec, skills, and context objects.\n * @param skill - The skill to use for the agent context: a skill object is cloned into the space, a ref\n * (e.g. a registry URI) is bound as-is so the agent follows the compiled skill until customized.\n * @returns An Effect that yields the initialized Agent.\n */\nexport const makeInitialized = (\n props: MakeProps,\n // TODO(burdon): Reconcile with props.skills.\n skill: Skill.Skill | Ref.Ref<Skill.Skill>,\n): Effect.Effect<Agent, never, Database.Service> =>\n Effect.gen(function* () {\n const { skills: propsSkills, contextObjects, ...agentProps } = props;\n\n // Loaded before anything is persisted: a rejected chunk after the first `Database.add` would\n // leave a half-built agent graph behind with nothing to clean it up.\n const AiContextRuntime = yield* Effect.promise(aiContextRuntime);\n\n // Persist any inline (transient) skills so their refs are resolvable from feed bindings later.\n // Refs created with Ref.make(obj) carry an inline target, but when stored in ECHO and read back\n // by a new AiSession, the target is lost and must be found in the DB via tryLoad().\n // `peek` rather than `target`: a registry-URI ref has no inline target and no resolver yet.\n const persistedPropsSkills = yield* Effect.all(\n (propsSkills ?? []).map((ref) => {\n const target = ref.peek();\n return target !== undefined\n ? Database.add(target).pipe(Effect.map((persisted) => Ref.make(persisted)))\n : Effect.succeed(ref);\n }),\n );\n\n // The typed Instructions is the agent's preset payload: text plus skill set.\n const instructions = yield* Database.add(\n Instructions.make({ text: props.instructions, skills: persistedPropsSkills }),\n );\n const agent = yield* Database.add(\n Obj.make(Agent, {\n // `did` (if provided) flows through via agentProps. Not auto-minted here: `IdentityDid.random()`\n // uses uncontrolled `randomBytes`, which would make agent creation non-deterministic and break\n // memoized-LLM tests. Minting is deferred to the runtime-identity provision (see agent-identity\n // spec), where it can be deterministic or a real HALO DID.\n ...agentProps,\n instructions: Ref.make(instructions),\n enabled: props.enabled ?? true,\n }),\n );\n const feed = yield* Database.add(Feed.make());\n const runtime = yield* Effect.context<Database.Service>();\n const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({ feed, runtime }));\n const agentSkill = Ref.isRef(skill) ? skill : Ref.make(yield* Database.add(Obj.clone(skill, { deep: 'all' })));\n\n const chat = yield* Database.add(\n Chat.make({\n feed: Ref.make(feed),\n // Steered through the chat's own channel: instructions render into the system prompt.\n // Identity/attribution comes from the companion link (ref on the agent + parent edge).\n instructions: Ref.make(instructions),\n }),\n );\n Chat.linkCompanion({ chat, subject: agent });\n yield* Effect.promise(() =>\n contextBinder.bind({\n skills: [agentSkill, ...persistedPropsSkills],\n objects: [Ref.make(agent), Ref.make(chat), ...(contextObjects ?? [])],\n }),\n );\n return agent;\n }).pipe(Effect.scoped);\n\n/**\n * Resets the agent's chat history by rebuilding the chat context.\n * Preserves the existing skills and objects from the current chat context.\n *\n * @param agent - The agent whose chat history should be reset. Must have an existing chat.\n * @returns An Effect that resets the chat history.\n */\nexport const resetChatHistory = (agent: Agent): Effect.Effect<void, EchoError.EntityNotFoundError, Database.Service> =>\n Effect.gen(function* () {\n const existingChat = yield* loadChat(agent);\n if (!existingChat) {\n return yield* Effect.die(new Error('Agent must have an existing chat to reset.'));\n }\n\n const existingFeed = yield* Database.load(existingChat.feed);\n const runtime = yield* Effect.context<Database.Service>();\n const AiContextRuntime = yield* Effect.promise(aiContextRuntime);\n const existingContextBinder = yield* EffectEx.acquireReleaseResource(\n () =>\n new AiContextRuntime.Binder({\n feed: existingFeed,\n runtime,\n }),\n );\n const skills = existingContextBinder.getSkills().map(Skill.makeRef);\n const objects = existingContextBinder\n .getObjects()\n .filter((object) => !Obj.instanceOf(Chat.Chat, object))\n .map((object) => Ref.make(object));\n\n const feed = yield* Database.add(Feed.make());\n const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({ feed, runtime }));\n\n const chat = yield* Database.add(\n Chat.make({\n feed: Ref.make(feed),\n instructions: agent.instructions,\n }),\n );\n Chat.linkCompanion({ chat, subject: agent });\n yield* Effect.promise(() =>\n contextBinder.bind({\n skills,\n objects: [...objects, Ref.make(chat)],\n }),\n );\n\n // The old chat stays parented (history is retired, not deleted); the new chat is primary\n // because `loadChat` resolves the latest child by creation order.\n }).pipe(Effect.scoped);\n\nexport const getFromChatContext: Effect.Effect<\n Agent,\n HarnessContextError | Harness.NotSupportedError,\n Harness.HarnessService\n> = Effect.gen(function* () {\n const HarnessRuntime = yield* Effect.promise(harnessRuntime);\n const agents = yield* HarnessRuntime.queryContext(Filter.type(Agent));\n if (agents.length !== 1) {\n return yield* Effect.fail(new HarnessContextError({ type: 'agent', count: agents.length }));\n }\n\n const agent = agents[0];\n return agent;\n});\n"],"mappings":"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"}
|