actoviq-agent-sdk 0.3.0 → 0.4.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/README-zh.md +37 -2
- package/README.md +37 -2
- package/bin/actoviq-tui.js +5 -0
- package/dist/src/cli/actoviq-react.js +93 -13
- package/dist/src/cli/actoviq-react.js.map +1 -1
- package/dist/src/cli/actoviq-tui.d.ts +3 -0
- package/dist/src/cli/actoviq-tui.d.ts.map +1 -0
- package/dist/src/cli/actoviq-tui.js +76 -0
- package/dist/src/cli/actoviq-tui.js.map +1 -0
- package/dist/src/config/actoviqSettingsStore.d.ts.map +1 -1
- package/dist/src/config/actoviqSettingsStore.js +8 -2
- package/dist/src/config/actoviqSettingsStore.js.map +1 -1
- package/dist/src/config/anthropicEnvMapping.d.ts +18 -0
- package/dist/src/config/anthropicEnvMapping.d.ts.map +1 -0
- package/dist/src/config/anthropicEnvMapping.js +49 -0
- package/dist/src/config/anthropicEnvMapping.js.map +1 -0
- package/dist/src/config/modelTiers.d.ts +12 -0
- package/dist/src/config/modelTiers.d.ts.map +1 -0
- package/dist/src/config/modelTiers.js +31 -0
- package/dist/src/config/modelTiers.js.map +1 -0
- package/dist/src/config/projectSessionDirectory.d.ts +8 -0
- package/dist/src/config/projectSessionDirectory.d.ts.map +1 -0
- package/dist/src/config/projectSessionDirectory.js +83 -0
- package/dist/src/config/projectSessionDirectory.js.map +1 -0
- package/dist/src/config/resolveRuntimeConfig.d.ts.map +1 -1
- package/dist/src/config/resolveRuntimeConfig.js +73 -21
- package/dist/src/config/resolveRuntimeConfig.js.map +1 -1
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/connectionManager.d.ts.map +1 -1
- package/dist/src/mcp/connectionManager.js +10 -1
- package/dist/src/mcp/connectionManager.js.map +1 -1
- package/dist/src/memory/actoviqMemory.d.ts.map +1 -1
- package/dist/src/memory/actoviqMemory.js +10 -6
- package/dist/src/memory/actoviqMemory.js.map +1 -1
- package/dist/src/memory/actoviqTranscriptLogger.d.ts.map +1 -1
- package/dist/src/memory/actoviqTranscriptLogger.js +2 -2
- package/dist/src/memory/actoviqTranscriptLogger.js.map +1 -1
- package/dist/src/parity/actoviqBridgeEvents.d.ts.map +1 -1
- package/dist/src/parity/actoviqBridgeEvents.js +4 -2
- package/dist/src/parity/actoviqBridgeEvents.js.map +1 -1
- package/dist/src/parity/actoviqBridgeSdk.d.ts.map +1 -1
- package/dist/src/parity/actoviqBridgeSdk.js +9 -1
- package/dist/src/parity/actoviqBridgeSdk.js.map +1 -1
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.d.ts +277 -0
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.d.ts.map +1 -0
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.js +1395 -0
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.js.map +1 -0
- package/dist/src/parity/portableSessions.d.ts.map +1 -1
- package/dist/src/parity/portableSessions.js +6 -2
- package/dist/src/parity/portableSessions.js.map +1 -1
- package/dist/src/provider/client.d.ts +5 -0
- package/dist/src/provider/client.d.ts.map +1 -1
- package/dist/src/provider/client.js +82 -8
- package/dist/src/provider/client.js.map +1 -1
- package/dist/src/provider/openai-client.d.ts.map +1 -1
- package/dist/src/provider/openai-client.js +43 -4
- package/dist/src/provider/openai-client.js.map +1 -1
- package/dist/src/provider/openai-model-api.d.ts.map +1 -1
- package/dist/src/provider/openai-model-api.js +3 -0
- package/dist/src/provider/openai-model-api.js.map +1 -1
- package/dist/src/provider/openai-types.d.ts +1 -0
- package/dist/src/provider/openai-types.d.ts.map +1 -1
- package/dist/src/runtime/actoviqAgentDefinitions.d.ts +8 -0
- package/dist/src/runtime/actoviqAgentDefinitions.d.ts.map +1 -0
- package/dist/src/runtime/actoviqAgentDefinitions.js +165 -0
- package/dist/src/runtime/actoviqAgentDefinitions.js.map +1 -0
- package/dist/src/runtime/actoviqAgents.d.ts +26 -2
- package/dist/src/runtime/actoviqAgents.d.ts.map +1 -1
- package/dist/src/runtime/actoviqAgents.js +237 -26
- package/dist/src/runtime/actoviqAgents.js.map +1 -1
- package/dist/src/runtime/actoviqApiMicrocompact.d.ts +14 -0
- package/dist/src/runtime/actoviqApiMicrocompact.d.ts.map +1 -1
- package/dist/src/runtime/actoviqApiMicrocompact.js +102 -0
- package/dist/src/runtime/actoviqApiMicrocompact.js.map +1 -1
- package/dist/src/runtime/actoviqBackgroundTasks.d.ts +15 -1
- package/dist/src/runtime/actoviqBackgroundTasks.d.ts.map +1 -1
- package/dist/src/runtime/actoviqBackgroundTasks.js +72 -10
- package/dist/src/runtime/actoviqBackgroundTasks.js.map +1 -1
- package/dist/src/runtime/actoviqCompact.d.ts +59 -0
- package/dist/src/runtime/actoviqCompact.d.ts.map +1 -1
- package/dist/src/runtime/actoviqCompact.js +344 -15
- package/dist/src/runtime/actoviqCompact.js.map +1 -1
- package/dist/src/runtime/actoviqModelApi.d.ts.map +1 -1
- package/dist/src/runtime/actoviqModelApi.js +20 -4
- package/dist/src/runtime/actoviqModelApi.js.map +1 -1
- package/dist/src/runtime/actoviqPermissions.d.ts +5 -2
- package/dist/src/runtime/actoviqPermissions.d.ts.map +1 -1
- package/dist/src/runtime/actoviqPermissions.js +91 -202
- package/dist/src/runtime/actoviqPermissions.js.map +1 -1
- package/dist/src/runtime/actoviqSessionPermissions.d.ts +5 -0
- package/dist/src/runtime/actoviqSessionPermissions.d.ts.map +1 -0
- package/dist/src/runtime/actoviqSessionPermissions.js +44 -0
- package/dist/src/runtime/actoviqSessionPermissions.js.map +1 -0
- package/dist/src/runtime/agentClient.d.ts +37 -3
- package/dist/src/runtime/agentClient.d.ts.map +1 -1
- package/dist/src/runtime/agentClient.js +1112 -100
- package/dist/src/runtime/agentClient.js.map +1 -1
- package/dist/src/runtime/agentSession.d.ts +8 -5
- package/dist/src/runtime/agentSession.d.ts.map +1 -1
- package/dist/src/runtime/agentSession.js +11 -4
- package/dist/src/runtime/agentSession.js.map +1 -1
- package/dist/src/runtime/conversationEngine.d.ts +2 -0
- package/dist/src/runtime/conversationEngine.d.ts.map +1 -1
- package/dist/src/runtime/conversationEngine.js +575 -36
- package/dist/src/runtime/conversationEngine.js.map +1 -1
- package/dist/src/runtime/defaultActoviqAgents.d.ts +3 -0
- package/dist/src/runtime/defaultActoviqAgents.d.ts.map +1 -0
- package/dist/src/runtime/defaultActoviqAgents.js +85 -0
- package/dist/src/runtime/defaultActoviqAgents.js.map +1 -0
- package/dist/src/runtime/safetyChecks.d.ts.map +1 -1
- package/dist/src/runtime/safetyChecks.js +15 -9
- package/dist/src/runtime/safetyChecks.js.map +1 -1
- package/dist/src/runtime/sessionManager.d.ts.map +1 -1
- package/dist/src/runtime/sessionManager.js +10 -5
- package/dist/src/runtime/sessionManager.js.map +1 -1
- package/dist/src/runtime/tools.d.ts.map +1 -1
- package/dist/src/runtime/tools.js +1 -0
- package/dist/src/runtime/tools.js.map +1 -1
- package/dist/src/scheduling/cron.d.ts.map +1 -1
- package/dist/src/scheduling/cron.js +3 -1
- package/dist/src/scheduling/cron.js.map +1 -1
- package/dist/src/storage/backgroundTaskStore.d.ts.map +1 -1
- package/dist/src/storage/backgroundTaskStore.js +3 -2
- package/dist/src/storage/backgroundTaskStore.js.map +1 -1
- package/dist/src/storage/mailboxStore.d.ts.map +1 -1
- package/dist/src/storage/mailboxStore.js +3 -3
- package/dist/src/storage/mailboxStore.js.map +1 -1
- package/dist/src/storage/pathSafety.d.ts +4 -0
- package/dist/src/storage/pathSafety.d.ts.map +1 -0
- package/dist/src/storage/pathSafety.js +28 -0
- package/dist/src/storage/pathSafety.js.map +1 -0
- package/dist/src/storage/sessionStore.d.ts +2 -2
- package/dist/src/storage/sessionStore.d.ts.map +1 -1
- package/dist/src/storage/sessionStore.js +23 -8
- package/dist/src/storage/sessionStore.js.map +1 -1
- package/dist/src/storage/teammateStore.d.ts.map +1 -1
- package/dist/src/storage/teammateStore.js +3 -2
- package/dist/src/storage/teammateStore.js.map +1 -1
- package/dist/src/swarm/actoviqSwarm.d.ts.map +1 -1
- package/dist/src/swarm/actoviqSwarm.js +6 -6
- package/dist/src/swarm/actoviqSwarm.js.map +1 -1
- package/dist/src/tools/actoviqCoreTools.d.ts +7 -2
- package/dist/src/tools/actoviqCoreTools.d.ts.map +1 -1
- package/dist/src/tools/actoviqCoreTools.js +2 -2
- package/dist/src/tools/actoviqCoreTools.js.map +1 -1
- package/dist/src/tools/actoviqFileTools.d.ts.map +1 -1
- package/dist/src/tools/actoviqFileTools.js +16 -5
- package/dist/src/tools/actoviqFileTools.js.map +1 -1
- package/dist/src/tools/actoviqNotebookEdit.d.ts.map +1 -1
- package/dist/src/tools/actoviqNotebookEdit.js +121 -4
- package/dist/src/tools/actoviqNotebookEdit.js.map +1 -1
- package/dist/src/tools/actoviqShellTools.d.ts.map +1 -1
- package/dist/src/tools/actoviqShellTools.js +38 -9
- package/dist/src/tools/actoviqShellTools.js.map +1 -1
- package/dist/src/tools/actoviqWebTools.d.ts.map +1 -1
- package/dist/src/tools/actoviqWebTools.js +50 -18
- package/dist/src/tools/actoviqWebTools.js.map +1 -1
- package/dist/src/tools/bash/BashTool.d.ts.map +1 -1
- package/dist/src/tools/bash/BashTool.js +47 -14
- package/dist/src/tools/bash/BashTool.js.map +1 -1
- package/dist/src/tools/todo/TodoWriteTool.d.ts +14 -0
- package/dist/src/tools/todo/TodoWriteTool.d.ts.map +1 -1
- package/dist/src/tools/todo/TodoWriteTool.js +45 -2
- package/dist/src/tools/todo/TodoWriteTool.js.map +1 -1
- package/dist/src/tui/actoviqTui.d.ts +13 -0
- package/dist/src/tui/actoviqTui.d.ts.map +1 -0
- package/dist/src/tui/actoviqTui.js +1594 -0
- package/dist/src/tui/actoviqTui.js.map +1 -0
- package/dist/src/tui/ansi.d.ts +33 -0
- package/dist/src/tui/ansi.d.ts.map +1 -0
- package/dist/src/tui/ansi.js +127 -0
- package/dist/src/tui/ansi.js.map +1 -0
- package/dist/src/tui/editor.d.ts +56 -0
- package/dist/src/tui/editor.d.ts.map +1 -0
- package/dist/src/tui/editor.js +253 -0
- package/dist/src/tui/editor.js.map +1 -0
- package/dist/src/tui/pluginCatalog.d.ts +13 -0
- package/dist/src/tui/pluginCatalog.d.ts.map +1 -0
- package/dist/src/tui/pluginCatalog.js +78 -0
- package/dist/src/tui/pluginCatalog.js.map +1 -0
- package/dist/src/tui/screen.d.ts +28 -0
- package/dist/src/tui/screen.d.ts.map +1 -0
- package/dist/src/tui/screen.js +119 -0
- package/dist/src/tui/screen.js.map +1 -0
- package/dist/src/tui/selection.d.ts +9 -0
- package/dist/src/tui/selection.d.ts.map +1 -0
- package/dist/src/tui/selection.js +16 -0
- package/dist/src/tui/selection.js.map +1 -0
- package/dist/src/tui/transcript.d.ts +41 -0
- package/dist/src/tui/transcript.d.ts.map +1 -0
- package/dist/src/tui/transcript.js +147 -0
- package/dist/src/tui/transcript.js.map +1 -0
- package/dist/src/types.d.ts +233 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/workflow/workflowEngine.js +26 -3
- package/dist/src/workflow/workflowEngine.js.map +1 -1
- package/dist/src/workspace/actoviqWorkspace.d.ts.map +1 -1
- package/dist/src/workspace/actoviqWorkspace.js +25 -2
- package/dist/src/workspace/actoviqWorkspace.js.map +1 -1
- package/package.json +26 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { execFile as execFileCallback } from 'node:child_process';
|
|
3
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { z } from 'zod';
|
|
2
6
|
import { createActoviqBuddyApi } from '../buddy/actoviqBuddy.js';
|
|
3
7
|
import { createActoviqComputerUseMcpServer, createActoviqComputerUseTools, } from '../computer/actoviqComputerUse.js';
|
|
4
8
|
import { resolveRuntimeConfig } from '../config/resolveRuntimeConfig.js';
|
|
9
|
+
import { resolveActoviqModelReference } from '../config/modelTiers.js';
|
|
5
10
|
import { mergeActoviqHooks, normalizeActoviqHookMessages, resolveActoviqPostRunHooks, resolveActoviqSessionStartHooks, } from '../hooks/actoviqHooks.js';
|
|
6
11
|
import { createActoviqMemoryApi } from '../memory/actoviqMemory.js';
|
|
7
12
|
import { appendMessagesToTranscript } from '../memory/actoviqTranscriptLogger.js';
|
|
@@ -14,11 +19,16 @@ import { SessionStore } from '../storage/sessionStore.js';
|
|
|
14
19
|
import { TeammateStore } from '../storage/teammateStore.js';
|
|
15
20
|
import { ActoviqSwarmApi } from '../swarm/actoviqSwarm.js';
|
|
16
21
|
import { createActoviqFileTools } from '../tools/actoviqFileTools.js';
|
|
22
|
+
import { createGitWorktreeWorkspace, } from '../workspace/actoviqWorkspace.js';
|
|
17
23
|
import { ActoviqAgentsApi, createActoviqTaskTool, summarizeActoviqAgentDefinition, } from './actoviqAgents.js';
|
|
24
|
+
import { getDefaultActoviqAgents } from './defaultActoviqAgents.js';
|
|
25
|
+
import { loadActoviqAgentDefinitions } from './actoviqAgentDefinitions.js';
|
|
18
26
|
import { ActoviqSkillsApi, loadActoviqSkillDefinitions, resolveActoviqSkillPrompt, summarizeActoviqSkillDefinition, } from './actoviqSkills.js';
|
|
19
27
|
import { ActoviqBackgroundTaskManager, ActoviqBackgroundTasksApi, } from './actoviqBackgroundTasks.js';
|
|
20
28
|
import { ActoviqContextApi, ActoviqSlashCommandsApi, } from './actoviqSlashCommands.js';
|
|
21
|
-
import { compactActoviqSession, getPersistedActoviqCompactHistory, getPersistedActoviqCompactState, isActoviqPromptTooLongError, } from './actoviqCompact.js';
|
|
29
|
+
import { compactActoviqSession, getPersistedActoviqCompactHistory, getPersistedActoviqCompactState, isActoviqPromptTooLongError, recordActoviqLoopCompactionsOnSession, trackRecentFile, trackRecentSkill, } from './actoviqCompact.js';
|
|
30
|
+
import { ACTOVIQ_SESSION_PERMISSION_STATE_KEY, getPersistedActoviqSessionPermissionState, serializeActoviqSessionPermissionState, } from './actoviqSessionPermissions.js';
|
|
31
|
+
const RECENT_FILE_TOOL_NAMES = new Set(['Read', 'Write', 'Edit', 'NotebookEdit']);
|
|
22
32
|
import { ActoviqToolsApi, buildActoviqCleanToolCatalog, resolveActoviqCleanToolMetadata, } from './actoviqToolCatalog.js';
|
|
23
33
|
import { WorkflowApi } from '../workflow/workflowBuilder.js';
|
|
24
34
|
import { SessionManager } from './sessionManager.js';
|
|
@@ -29,6 +39,7 @@ import { createOpenaiModelApi } from '../provider/openai-model-api.js';
|
|
|
29
39
|
import { AgentRunStream } from './asyncQueue.js';
|
|
30
40
|
import { executeConversation } from './conversationEngine.js';
|
|
31
41
|
import { asError, createId, deepClone, isRecord, nowIso, truncateText } from './helpers.js';
|
|
42
|
+
import { tool } from './tools.js';
|
|
32
43
|
import { buildInvokedSkillMessages, buildRelevantMemoryMessages, extractTextFromContent, } from './messageUtils.js';
|
|
33
44
|
import { AgentSession } from './agentSession.js';
|
|
34
45
|
const RELEVANT_MEMORY_SESSION_STATE_KEY = '__actoviqRelevantMemoryState';
|
|
@@ -38,6 +49,7 @@ const RELEVANT_MEMORY_MAX_SESSION_BYTES = 60 * 1024;
|
|
|
38
49
|
const DEFAULT_SESSION_MEMORY_MAX_TOKENS = 4_096;
|
|
39
50
|
const DEFAULT_DREAM_MAX_TOKENS = 4_096;
|
|
40
51
|
const MAX_REACTIVE_COMPACT_ATTEMPTS = 3;
|
|
52
|
+
const execFile = promisify(execFileCallback);
|
|
41
53
|
const SESSION_MEMORY_SYSTEM_PROMPT = `You maintain the persistent session-memory markdown file for an ongoing engineering conversation.
|
|
42
54
|
|
|
43
55
|
Return only the full updated markdown document.
|
|
@@ -72,6 +84,13 @@ function cloneAgentDefinition(definition) {
|
|
|
72
84
|
hooks: cloneHooks(definition.hooks),
|
|
73
85
|
tools: definition.tools ? [...definition.tools] : undefined,
|
|
74
86
|
mcpServers: definition.mcpServers ? deepClone(definition.mcpServers) : undefined,
|
|
87
|
+
allowedTools: definition.allowedTools ? [...definition.allowedTools] : undefined,
|
|
88
|
+
disallowedTools: definition.disallowedTools ? [...definition.disallowedTools] : undefined,
|
|
89
|
+
allowedAgents: definition.allowedAgents ? [...definition.allowedAgents] : undefined,
|
|
90
|
+
skills: definition.skills ? [...definition.skills] : undefined,
|
|
91
|
+
requiredMcpServers: definition.requiredMcpServers
|
|
92
|
+
? [...definition.requiredMcpServers]
|
|
93
|
+
: undefined,
|
|
75
94
|
};
|
|
76
95
|
}
|
|
77
96
|
function cloneSkillDefinition(definition) {
|
|
@@ -101,6 +120,17 @@ export class AgentSessionsApi {
|
|
|
101
120
|
get(sessionId) {
|
|
102
121
|
return this.resumeSession(sessionId);
|
|
103
122
|
}
|
|
123
|
+
resume(sessionId, options = {}) {
|
|
124
|
+
return this.resumeSession(sessionId, options);
|
|
125
|
+
}
|
|
126
|
+
async continueMostRecent(options = {}) {
|
|
127
|
+
const sessions = await this.store.list();
|
|
128
|
+
const mostRecent = sessions.find(session => session.status !== 'closed') ?? sessions[0];
|
|
129
|
+
if (!mostRecent) {
|
|
130
|
+
throw new Error('No stored sessions are available to resume.');
|
|
131
|
+
}
|
|
132
|
+
return this.resumeSession(mostRecent.id, options);
|
|
133
|
+
}
|
|
104
134
|
delete(sessionId) {
|
|
105
135
|
return this.store.delete(sessionId);
|
|
106
136
|
}
|
|
@@ -172,12 +202,41 @@ function getAgentContinuityState(metadata) {
|
|
|
172
202
|
count: typeof record.count === 'number' ? record.count : 1,
|
|
173
203
|
lastInvokedAt: record.lastInvokedAt,
|
|
174
204
|
lastDescription: typeof record.lastDescription === 'string' ? record.lastDescription : undefined,
|
|
205
|
+
lastRunId: typeof record.lastRunId === 'string' ? record.lastRunId : undefined,
|
|
206
|
+
lastSessionId: typeof record.lastSessionId === 'string' ? record.lastSessionId : undefined,
|
|
207
|
+
lastStatus: record.lastStatus === 'completed' ||
|
|
208
|
+
record.lastStatus === 'async_launched' ||
|
|
209
|
+
record.lastStatus === 'failed' ||
|
|
210
|
+
record.lastStatus === 'cancelled'
|
|
211
|
+
? record.lastStatus
|
|
212
|
+
: undefined,
|
|
213
|
+
lastTaskId: typeof record.lastTaskId === 'string' ? record.lastTaskId : undefined,
|
|
214
|
+
lastTextSummary: typeof record.lastTextSummary === 'string' ? record.lastTextSummary : undefined,
|
|
215
|
+
runIds: readStringArray(record.runIds),
|
|
216
|
+
sessionIds: readStringArray(record.sessionIds),
|
|
217
|
+
taskIds: readStringArray(record.taskIds),
|
|
218
|
+
totalRequestCount: typeof record.totalRequestCount === 'number'
|
|
219
|
+
? record.totalRequestCount
|
|
220
|
+
: undefined,
|
|
221
|
+
totalToolCallCount: typeof record.totalToolCallCount === 'number'
|
|
222
|
+
? record.totalToolCallCount
|
|
223
|
+
: undefined,
|
|
224
|
+
totalToolErrorCount: typeof record.totalToolErrorCount === 'number'
|
|
225
|
+
? record.totalToolErrorCount
|
|
226
|
+
: undefined,
|
|
175
227
|
},
|
|
176
228
|
];
|
|
177
229
|
})
|
|
178
230
|
: [],
|
|
179
231
|
};
|
|
180
232
|
}
|
|
233
|
+
function readStringArray(value) {
|
|
234
|
+
if (!Array.isArray(value)) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
const values = value.filter((entry) => typeof entry === 'string');
|
|
238
|
+
return values.length > 0 ? values : undefined;
|
|
239
|
+
}
|
|
181
240
|
function mergeDelegatedAgents(existing, pending) {
|
|
182
241
|
const merged = new Map(existing.map(record => [record.name, { ...record }]));
|
|
183
242
|
for (const record of pending) {
|
|
@@ -188,15 +247,53 @@ function mergeDelegatedAgents(existing, pending) {
|
|
|
188
247
|
count: 1,
|
|
189
248
|
lastInvokedAt: record.invokedAt,
|
|
190
249
|
lastDescription: record.description,
|
|
250
|
+
lastRunId: record.runId,
|
|
251
|
+
lastSessionId: record.sessionId,
|
|
252
|
+
lastStatus: record.status,
|
|
253
|
+
lastTaskId: record.taskId,
|
|
254
|
+
lastTextSummary: record.textSummary,
|
|
255
|
+
runIds: record.runId ? [record.runId] : undefined,
|
|
256
|
+
sessionIds: record.sessionId ? [record.sessionId] : undefined,
|
|
257
|
+
taskIds: record.taskId ? [record.taskId] : undefined,
|
|
258
|
+
totalRequestCount: record.requestCount,
|
|
259
|
+
totalToolCallCount: record.toolCallCount,
|
|
260
|
+
totalToolErrorCount: record.toolErrorCount,
|
|
191
261
|
});
|
|
192
262
|
continue;
|
|
193
263
|
}
|
|
194
264
|
current.count += 1;
|
|
195
265
|
current.lastInvokedAt = record.invokedAt;
|
|
196
266
|
current.lastDescription = record.description ?? current.lastDescription;
|
|
267
|
+
current.lastRunId = record.runId ?? current.lastRunId;
|
|
268
|
+
current.lastSessionId = record.sessionId ?? current.lastSessionId;
|
|
269
|
+
current.lastStatus = record.status ?? current.lastStatus;
|
|
270
|
+
current.lastTaskId = record.taskId ?? current.lastTaskId;
|
|
271
|
+
current.lastTextSummary = record.textSummary ?? current.lastTextSummary;
|
|
272
|
+
current.runIds = appendUnique(current.runIds, record.runId);
|
|
273
|
+
current.sessionIds = appendUnique(current.sessionIds, record.sessionId);
|
|
274
|
+
current.taskIds = appendUnique(current.taskIds, record.taskId);
|
|
275
|
+
current.totalRequestCount = sumOptional(current.totalRequestCount, record.requestCount);
|
|
276
|
+
current.totalToolCallCount = sumOptional(current.totalToolCallCount, record.toolCallCount);
|
|
277
|
+
current.totalToolErrorCount = sumOptional(current.totalToolErrorCount, record.toolErrorCount);
|
|
197
278
|
}
|
|
198
279
|
return [...merged.values()].sort((left, right) => right.lastInvokedAt.localeCompare(left.lastInvokedAt));
|
|
199
280
|
}
|
|
281
|
+
function appendUnique(existing, value) {
|
|
282
|
+
if (!value) {
|
|
283
|
+
return existing;
|
|
284
|
+
}
|
|
285
|
+
const next = existing ? [...existing] : [];
|
|
286
|
+
if (!next.includes(value)) {
|
|
287
|
+
next.push(value);
|
|
288
|
+
}
|
|
289
|
+
return next;
|
|
290
|
+
}
|
|
291
|
+
function sumOptional(current, next) {
|
|
292
|
+
if (next === undefined) {
|
|
293
|
+
return current;
|
|
294
|
+
}
|
|
295
|
+
return (current ?? 0) + next;
|
|
296
|
+
}
|
|
200
297
|
function getInvokedSkillState(metadata) {
|
|
201
298
|
const raw = metadata?.[INVOKED_SKILLS_STATE_KEY];
|
|
202
299
|
if (!Array.isArray(raw)) {
|
|
@@ -257,6 +354,8 @@ export class ActoviqAgentClient {
|
|
|
257
354
|
defaultTools;
|
|
258
355
|
defaultMcpServers;
|
|
259
356
|
hooks;
|
|
357
|
+
maxSubagentDepth;
|
|
358
|
+
maxSubagentFanout;
|
|
260
359
|
sessions;
|
|
261
360
|
agents;
|
|
262
361
|
skills;
|
|
@@ -273,13 +372,15 @@ export class ActoviqAgentClient {
|
|
|
273
372
|
agentDefinitions;
|
|
274
373
|
skillDefinitions;
|
|
275
374
|
pendingDelegations = new Map();
|
|
375
|
+
pendingRuntimeNotifications = new Map();
|
|
376
|
+
subagentInputQueues = new Map();
|
|
276
377
|
sessionRuntimeOverrides = new Map();
|
|
277
378
|
backgroundTaskManager;
|
|
278
379
|
defaultPermissionMode;
|
|
279
380
|
defaultPermissions;
|
|
280
381
|
defaultClassifier;
|
|
281
382
|
defaultApprover;
|
|
282
|
-
constructor(config, store, backgroundTaskStore, mailboxStore, teammateStore, modelApi, mcpManager, defaultTools, defaultMcpServers, hooks, agentDefinitions = [], skillDefinitions = [], defaultPermissionMode, defaultPermissions, defaultClassifier, defaultApprover, sessionManagerConfig) {
|
|
383
|
+
constructor(config, store, backgroundTaskStore, mailboxStore, teammateStore, modelApi, mcpManager, defaultTools, defaultMcpServers, hooks, agentDefinitions = [], skillDefinitions = [], defaultPermissionMode, defaultPermissions, defaultClassifier, defaultApprover, sessionManagerConfig, maxSubagentDepth = 1, maxSubagentFanout = 8) {
|
|
283
384
|
this.config = config;
|
|
284
385
|
this.store = store;
|
|
285
386
|
this.backgroundTaskStore = backgroundTaskStore;
|
|
@@ -290,8 +391,10 @@ export class ActoviqAgentClient {
|
|
|
290
391
|
this.defaultTools = defaultTools;
|
|
291
392
|
this.defaultMcpServers = defaultMcpServers;
|
|
292
393
|
this.hooks = hooks;
|
|
394
|
+
this.maxSubagentDepth = maxSubagentDepth;
|
|
395
|
+
this.maxSubagentFanout = maxSubagentFanout;
|
|
293
396
|
this.sessionManager = new SessionManager(this.store, sessionManagerConfig);
|
|
294
|
-
this.sessions = new AgentSessionsApi(this.store, (sessionId) => this.resumeSession(sessionId), this.sessionManager);
|
|
397
|
+
this.sessions = new AgentSessionsApi(this.store, (sessionId, options) => this.resumeSession(sessionId, options), this.sessionManager);
|
|
295
398
|
this.agentDefinitions = new Map(agentDefinitions.map(definition => [definition.name, cloneAgentDefinition(definition)]));
|
|
296
399
|
this.skillDefinitions = new Map(skillDefinitions.map(definition => [definition.name, cloneSkillDefinition(definition)]));
|
|
297
400
|
this.backgroundTaskManager = new ActoviqBackgroundTaskManager(this.backgroundTaskStore);
|
|
@@ -356,9 +459,23 @@ export class ActoviqAgentClient {
|
|
|
356
459
|
resumeSession: (sessionId) => this.resumeSession(sessionId),
|
|
357
460
|
getBackgroundTask: (taskId) => this.backgroundTaskManager.get(taskId),
|
|
358
461
|
}, this.teammateStore, this.mailboxStore);
|
|
359
|
-
|
|
462
|
+
const existingDelegationTool = this.defaultTools.find(tool => tool.name === 'Agent' || tool.name === 'Task');
|
|
463
|
+
if (!existingDelegationTool) {
|
|
360
464
|
this.defaultTools.unshift(this.createTaskTool());
|
|
361
465
|
}
|
|
466
|
+
else if (existingDelegationTool.name === 'Task') {
|
|
467
|
+
existingDelegationTool.aliases = [
|
|
468
|
+
...new Set([...(existingDelegationTool.aliases ?? []), 'Agent']),
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
this.replaceDefaultTool(this.createSendMessageTool());
|
|
472
|
+
this.replaceDefaultTool(this.createBackgroundTaskListTool());
|
|
473
|
+
this.replaceDefaultTool(this.createBackgroundTaskGetTool());
|
|
474
|
+
this.replaceDefaultTool(this.createBackgroundTaskStopTool());
|
|
475
|
+
this.replaceDefaultTool(this.createBackgroundTaskOutputTool());
|
|
476
|
+
if (this.listSkillDefinitions().length > 0) {
|
|
477
|
+
this.replaceDefaultTool(this.createSkillRegistryTool());
|
|
478
|
+
}
|
|
362
479
|
}
|
|
363
480
|
async listToolMetadata(options) {
|
|
364
481
|
return resolveActoviqCleanToolMetadata({
|
|
@@ -373,7 +490,7 @@ export class ActoviqAgentClient {
|
|
|
373
490
|
}
|
|
374
491
|
/** Resolve a tool definition by name from the default tool registry. */
|
|
375
492
|
getTool(name) {
|
|
376
|
-
return this.defaultTools.find(t => t.name === name);
|
|
493
|
+
return this.defaultTools.find(t => t.name === name || t.aliases?.includes(name));
|
|
377
494
|
}
|
|
378
495
|
async getToolCatalog(options) {
|
|
379
496
|
return buildActoviqCleanToolCatalog(await this.listToolMetadata(options));
|
|
@@ -419,6 +536,13 @@ export class ActoviqAgentClient {
|
|
|
419
536
|
disableDefaultSkills: options.disableDefaultSkills,
|
|
420
537
|
loadDefaultSkillDirectories: options.loadDefaultSkillDirectories,
|
|
421
538
|
});
|
|
539
|
+
const loadedAgents = await loadActoviqAgentDefinitions({
|
|
540
|
+
homeDir: config.homeDir,
|
|
541
|
+
workDir: config.workDir,
|
|
542
|
+
agentDirectories: options.agentDirectories,
|
|
543
|
+
loadDefaultAgentDirectories: options.loadDefaultAgentDirectories,
|
|
544
|
+
});
|
|
545
|
+
const agentDefinitions = mergeAgentDefinitions(options.disableDefaultAgents === true ? [] : getDefaultActoviqAgents(), loadedAgents, options.agents ?? []);
|
|
422
546
|
const defaultTools = [...(options.tools ?? [])];
|
|
423
547
|
const defaultMcpServers = [...(options.mcpServers ?? [])];
|
|
424
548
|
if (options.computerUse) {
|
|
@@ -430,7 +554,9 @@ export class ActoviqAgentClient {
|
|
|
430
554
|
defaultTools.push(...createActoviqComputerUseTools(computerUseOptions));
|
|
431
555
|
}
|
|
432
556
|
}
|
|
433
|
-
|
|
557
|
+
const client = new ActoviqAgentClient(config, store, backgroundTaskStore, mailboxStore, teammateStore, modelApi, mcpManager, defaultTools, defaultMcpServers, options.hooks, agentDefinitions, [...loadedSkills, ...(options.skills ?? [])], options.permissionMode, options.permissions, options.classifier, options.approver, options.sessionManager, options.maxSubagentDepth, options.maxSubagentFanout);
|
|
558
|
+
await client.backgroundTaskManager.reconcileInterruptedTasks();
|
|
559
|
+
return client;
|
|
434
560
|
}
|
|
435
561
|
async run(input, options = {}) {
|
|
436
562
|
const runId = createId();
|
|
@@ -491,23 +617,74 @@ export class ActoviqAgentClient {
|
|
|
491
617
|
return race(tasks, options);
|
|
492
618
|
}
|
|
493
619
|
async createSession(options = {}) {
|
|
620
|
+
const model = this.resolveModel(options.model);
|
|
494
621
|
const stored = await this.store.create({
|
|
622
|
+
id: options.id,
|
|
495
623
|
title: options.title,
|
|
496
624
|
systemPrompt: options.systemPrompt ?? this.config.systemPrompt,
|
|
497
|
-
model
|
|
625
|
+
model,
|
|
498
626
|
tags: options.tags,
|
|
499
627
|
metadata: {
|
|
500
|
-
__actoviqWorkDir: this.config.workDir,
|
|
501
628
|
...(options.metadata ?? {}),
|
|
629
|
+
__actoviqWorkDir: this.config.workDir,
|
|
630
|
+
...(options.permissionMode || options.permissions
|
|
631
|
+
? {
|
|
632
|
+
[ACTOVIQ_SESSION_PERMISSION_STATE_KEY]: serializeActoviqSessionPermissionState({
|
|
633
|
+
mode: options.permissionMode,
|
|
634
|
+
permissions: clonePermissionRules(options.permissions) ?? [],
|
|
635
|
+
}),
|
|
636
|
+
}
|
|
637
|
+
: {}),
|
|
502
638
|
},
|
|
503
639
|
initialMessages: options.initialMessages,
|
|
504
640
|
});
|
|
505
641
|
return this.hydrateSession(stored);
|
|
506
642
|
}
|
|
507
|
-
async resumeSession(sessionId) {
|
|
508
|
-
const
|
|
643
|
+
async resumeSession(sessionId, options = {}) {
|
|
644
|
+
const loaded = options.fork
|
|
645
|
+
? await this.store.fork(sessionId, {
|
|
646
|
+
title: options.title,
|
|
647
|
+
tags: options.tags,
|
|
648
|
+
metadata: options.metadata,
|
|
649
|
+
})
|
|
650
|
+
: await this.store.load(sessionId);
|
|
651
|
+
const stored = deepClone(loaded);
|
|
652
|
+
if (options.model) {
|
|
653
|
+
stored.model = this.resolveModel(options.model);
|
|
654
|
+
}
|
|
655
|
+
if (options.permissionMode !== undefined || options.permissions !== undefined) {
|
|
656
|
+
const currentPermissionState = getPersistedActoviqSessionPermissionState(stored.metadata);
|
|
657
|
+
stored.metadata[ACTOVIQ_SESSION_PERMISSION_STATE_KEY] =
|
|
658
|
+
serializeActoviqSessionPermissionState({
|
|
659
|
+
mode: options.permissionMode ?? currentPermissionState.mode,
|
|
660
|
+
permissions: options.permissions !== undefined
|
|
661
|
+
? clonePermissionRules(options.permissions) ?? []
|
|
662
|
+
: currentPermissionState.permissions,
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (!options.fork) {
|
|
666
|
+
if (options.title?.trim()) {
|
|
667
|
+
stored.title = options.title.trim();
|
|
668
|
+
stored.titleSource = 'manual';
|
|
669
|
+
}
|
|
670
|
+
if (options.tags) {
|
|
671
|
+
stored.tags = [...options.tags];
|
|
672
|
+
}
|
|
673
|
+
if (options.metadata) {
|
|
674
|
+
stored.metadata = { ...stored.metadata, ...options.metadata };
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
stored.status = 'active';
|
|
678
|
+
stored.lastActiveAt = nowIso();
|
|
679
|
+
stored.updatedAt = stored.lastActiveAt;
|
|
680
|
+
await this.store.save(stored);
|
|
509
681
|
return this.hydrateSession(stored);
|
|
510
682
|
}
|
|
683
|
+
resolveModel(model) {
|
|
684
|
+
return model
|
|
685
|
+
? resolveActoviqModelReference(model, this.config.modelTiers).model
|
|
686
|
+
: this.config.model;
|
|
687
|
+
}
|
|
511
688
|
async compactSessionById(sessionId, options = {}) {
|
|
512
689
|
const session = await this.resumeSession(sessionId);
|
|
513
690
|
return this.compactSessionForSession(session, options);
|
|
@@ -529,7 +706,28 @@ export class ActoviqAgentClient {
|
|
|
529
706
|
return this.dream.maybeAutoDream(options);
|
|
530
707
|
}
|
|
531
708
|
async close() {
|
|
532
|
-
|
|
709
|
+
const errors = [];
|
|
710
|
+
try {
|
|
711
|
+
await this.backgroundTaskManager.cancelAll();
|
|
712
|
+
}
|
|
713
|
+
catch (error) {
|
|
714
|
+
errors.push(error);
|
|
715
|
+
}
|
|
716
|
+
try {
|
|
717
|
+
this.sessionManager.dispose();
|
|
718
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
errors.push(error);
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
await this.mcpManager.closeAll();
|
|
724
|
+
}
|
|
725
|
+
catch (error) {
|
|
726
|
+
errors.push(error);
|
|
727
|
+
}
|
|
728
|
+
if (errors.length > 0) {
|
|
729
|
+
throw new AggregateError(errors, 'Errors occurred while closing the agent SDK.');
|
|
730
|
+
}
|
|
533
731
|
}
|
|
534
732
|
listAgentDefinitions() {
|
|
535
733
|
return [...this.agentDefinitions.values()].map(summarizeActoviqAgentDefinition);
|
|
@@ -553,6 +751,8 @@ export class ActoviqAgentClient {
|
|
|
553
751
|
...(definition.metadata ?? {}),
|
|
554
752
|
...(options.metadata ?? {}),
|
|
555
753
|
__actoviqAgentDefinition: definition.name,
|
|
754
|
+
__actoviqAgentMemory: definition.memory,
|
|
755
|
+
__actoviqAgentSource: definition.source,
|
|
556
756
|
[AGENT_CONTINUITY_STATE_KEY]: {
|
|
557
757
|
currentAgent: definition.name,
|
|
558
758
|
delegatedAgents: [],
|
|
@@ -588,16 +788,223 @@ export class ActoviqAgentClient {
|
|
|
588
788
|
createTaskTool(options = {}) {
|
|
589
789
|
return createActoviqTaskTool({
|
|
590
790
|
...options,
|
|
791
|
+
listAgentDefinitions: () => this.listAgentDefinitions(),
|
|
591
792
|
getAgentDefinition: (agent) => this.getAgentDefinition(agent),
|
|
592
|
-
runAgent: (agent, prompt, runOptions) => this.
|
|
593
|
-
|
|
793
|
+
runAgent: (agent, prompt, runOptions, delegation) => this.runDelegatedAgentTask(agent, prompt, runOptions, delegation),
|
|
794
|
+
maxDepth: this.maxSubagentDepth,
|
|
795
|
+
maxFanout: this.maxSubagentFanout,
|
|
796
|
+
onDelegated: ({ subagentType, description, parentSessionId, parentRunId, runId, sessionId, status, taskId, requestCount, toolCallCount, toolErrorCount, textSummary, }) => {
|
|
594
797
|
this.recordPendingDelegation(parentSessionId ?? parentRunId, {
|
|
595
798
|
name: subagentType,
|
|
596
799
|
description,
|
|
597
800
|
invokedAt: nowIso(),
|
|
801
|
+
runId,
|
|
802
|
+
sessionId,
|
|
803
|
+
status,
|
|
804
|
+
taskId,
|
|
805
|
+
requestCount,
|
|
806
|
+
toolCallCount,
|
|
807
|
+
toolErrorCount,
|
|
808
|
+
textSummary,
|
|
598
809
|
});
|
|
599
810
|
},
|
|
600
|
-
launchBackgroundAgent: (agent, prompt, backgroundOptions, runOptions) => this.launchBackgroundAgentTask(agent, prompt, backgroundOptions, runOptions),
|
|
811
|
+
launchBackgroundAgent: (agent, prompt, backgroundOptions, runOptions, delegation) => this.launchBackgroundAgentTask(agent, prompt, backgroundOptions, runOptions, delegation),
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
replaceDefaultTool(replacement) {
|
|
815
|
+
const existingIndex = this.defaultTools.findIndex(tool => tool.name === replacement.name);
|
|
816
|
+
if (existingIndex >= 0) {
|
|
817
|
+
this.defaultTools.splice(existingIndex, 1, replacement);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
this.defaultTools.push(replacement);
|
|
821
|
+
}
|
|
822
|
+
createSendMessageTool() {
|
|
823
|
+
return tool({
|
|
824
|
+
name: 'SendMessage',
|
|
825
|
+
description: 'Send a follow-up message to a running or previously completed agent by agent id, task id, session id, or assigned name. Running agents receive it at the next tool boundary; stopped agents resume in the background with full session context.',
|
|
826
|
+
inputSchema: z.strictObject({
|
|
827
|
+
to: z.string().min(1).describe('Agent id, task id, session id, or assigned name'),
|
|
828
|
+
message: z.union([z.string(), z.record(z.string(), z.unknown())]),
|
|
829
|
+
summary: z.string().optional(),
|
|
830
|
+
}),
|
|
831
|
+
isConcurrencySafe: () => true,
|
|
832
|
+
}, async ({ to, message, summary }, context) => {
|
|
833
|
+
const text = typeof message === 'string'
|
|
834
|
+
? message
|
|
835
|
+
: JSON.stringify(message);
|
|
836
|
+
const routed = await this.routeMessageToAgent(to.trim(), text, {
|
|
837
|
+
parentRunId: context.runId,
|
|
838
|
+
parentSessionId: context.sessionId,
|
|
839
|
+
runOptions: {
|
|
840
|
+
permissionMode: context.permissionMode,
|
|
841
|
+
permissions: context.permissions,
|
|
842
|
+
classifier: context.classifier,
|
|
843
|
+
approver: context.approver,
|
|
844
|
+
hooks: context.hooks,
|
|
845
|
+
effort: context.effort,
|
|
846
|
+
metadata: context.metadata,
|
|
847
|
+
},
|
|
848
|
+
});
|
|
849
|
+
return {
|
|
850
|
+
...routed,
|
|
851
|
+
summary,
|
|
852
|
+
};
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Model-facing Skill tool backed by the real skill registry. Loads the
|
|
857
|
+
* resolved skill content into the conversation as the tool result
|
|
858
|
+
* (progressive disclosure), instead of the former no-op stub.
|
|
859
|
+
*/
|
|
860
|
+
createSkillRegistryTool() {
|
|
861
|
+
return tool({
|
|
862
|
+
name: 'Skill',
|
|
863
|
+
description: 'Load a registered skill by name. The skill content (workflow, checklist, or domain knowledge) is returned so you can follow it for the current task.',
|
|
864
|
+
inputSchema: z.strictObject({
|
|
865
|
+
skill: z.string().describe('The name of the skill to load'),
|
|
866
|
+
args: z.string().optional().describe('Optional arguments for the skill'),
|
|
867
|
+
}),
|
|
868
|
+
isReadOnly: () => true,
|
|
869
|
+
prompt: () => {
|
|
870
|
+
const names = this.listSkillDefinitions()
|
|
871
|
+
.filter((definition) => definition.disableModelInvocation !== true)
|
|
872
|
+
.map((definition) => definition.description
|
|
873
|
+
? `- ${definition.name}: ${definition.description}`
|
|
874
|
+
: `- ${definition.name}`);
|
|
875
|
+
if (names.length === 0) {
|
|
876
|
+
return '';
|
|
877
|
+
}
|
|
878
|
+
return [
|
|
879
|
+
'Use the Skill tool to load a registered skill when the task matches its description.',
|
|
880
|
+
'Available skills:',
|
|
881
|
+
...names,
|
|
882
|
+
].join('\n');
|
|
883
|
+
},
|
|
884
|
+
}, async ({ skill, args }, context) => {
|
|
885
|
+
const definition = this.getSkillDefinition(skill);
|
|
886
|
+
if (!definition || definition.disableModelInvocation === true) {
|
|
887
|
+
const available = this.listSkillDefinitions()
|
|
888
|
+
.filter((entry) => entry.disableModelInvocation !== true)
|
|
889
|
+
.map((entry) => entry.name)
|
|
890
|
+
.join(', ');
|
|
891
|
+
throw new Error(`No skill named "${skill}" is registered.${available ? ` Available skills: ${available}.` : ''}`);
|
|
892
|
+
}
|
|
893
|
+
const resolved = await resolveActoviqSkillPrompt(definition, args ?? '', {
|
|
894
|
+
args: args ?? '',
|
|
895
|
+
workDir: this.config.workDir,
|
|
896
|
+
homeDir: this.config.homeDir,
|
|
897
|
+
sessionId: context.sessionId,
|
|
898
|
+
userId: this.config.userId,
|
|
899
|
+
});
|
|
900
|
+
const content = extractTextFromContent(resolved.content);
|
|
901
|
+
return [
|
|
902
|
+
`Loaded skill "${definition.name}".`,
|
|
903
|
+
'',
|
|
904
|
+
'<skill-content>',
|
|
905
|
+
content,
|
|
906
|
+
'</skill-content>',
|
|
907
|
+
'',
|
|
908
|
+
'Apply this skill to the current task now.',
|
|
909
|
+
].join('\n');
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
createBackgroundTaskListTool() {
|
|
913
|
+
return tool({
|
|
914
|
+
name: 'TaskList',
|
|
915
|
+
description: 'List background subagent tasks for the current SDK runtime.',
|
|
916
|
+
inputSchema: z.strictObject({}),
|
|
917
|
+
isReadOnly: () => true,
|
|
918
|
+
serialize: (output) => {
|
|
919
|
+
if (output.tasks.length === 0) {
|
|
920
|
+
return 'No background tasks.';
|
|
921
|
+
}
|
|
922
|
+
return output.tasks
|
|
923
|
+
.map(task => [
|
|
924
|
+
`Task id: ${task.id}`,
|
|
925
|
+
`Status: ${task.status}`,
|
|
926
|
+
`Subagent: ${task.subagentType}`,
|
|
927
|
+
`Description: ${task.description}`,
|
|
928
|
+
task.runId ? `Run id: ${task.runId}` : undefined,
|
|
929
|
+
task.sessionId ? `Session id: ${task.sessionId}` : undefined,
|
|
930
|
+
task.completedAt ? `Completed at: ${task.completedAt}` : undefined,
|
|
931
|
+
].filter(Boolean).join('\n'))
|
|
932
|
+
.join('\n\n');
|
|
933
|
+
},
|
|
934
|
+
}, async () => ({
|
|
935
|
+
tasks: await this.backgroundTaskManager.list(),
|
|
936
|
+
}));
|
|
937
|
+
}
|
|
938
|
+
createBackgroundTaskGetTool() {
|
|
939
|
+
return tool({
|
|
940
|
+
name: 'TaskGet',
|
|
941
|
+
description: 'Retrieve background subagent task status by ID.',
|
|
942
|
+
inputSchema: z.strictObject({
|
|
943
|
+
taskId: z.string().optional().describe('The ID of the task to get'),
|
|
944
|
+
task_id: z.string().optional().describe('The ID of the task to get'),
|
|
945
|
+
}).superRefine((input, ctx) => {
|
|
946
|
+
if (!resolveTaskId(input)) {
|
|
947
|
+
ctx.addIssue({
|
|
948
|
+
code: 'custom',
|
|
949
|
+
path: ['task_id'],
|
|
950
|
+
message: 'Provide `task_id` or `taskId`.',
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}),
|
|
954
|
+
isReadOnly: () => true,
|
|
955
|
+
serialize: serializeBackgroundTaskOutput,
|
|
956
|
+
}, async (input) => {
|
|
957
|
+
const taskId = resolveTaskId(input);
|
|
958
|
+
if (!taskId) {
|
|
959
|
+
throw new Error('TaskGet requires `task_id` or `taskId`.');
|
|
960
|
+
}
|
|
961
|
+
const task = await this.backgroundTaskManager.get(taskId);
|
|
962
|
+
if (!task) {
|
|
963
|
+
throw new Error(`No background task with id "${taskId}" exists.`);
|
|
964
|
+
}
|
|
965
|
+
return task;
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
createBackgroundTaskStopTool() {
|
|
969
|
+
return tool({
|
|
970
|
+
name: 'TaskStop',
|
|
971
|
+
description: 'Stop a running background subagent task.',
|
|
972
|
+
inputSchema: z.strictObject({
|
|
973
|
+
task_id: z.string().describe('The ID of the task to stop'),
|
|
974
|
+
}),
|
|
975
|
+
isConcurrencySafe: () => true,
|
|
976
|
+
serialize: serializeBackgroundTaskOutput,
|
|
977
|
+
}, async ({ task_id }) => {
|
|
978
|
+
const task = await this.backgroundTaskManager.cancel(task_id);
|
|
979
|
+
if (!task) {
|
|
980
|
+
throw new Error(`No background task with id "${task_id}" exists.`);
|
|
981
|
+
}
|
|
982
|
+
return task;
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
createBackgroundTaskOutputTool() {
|
|
986
|
+
return tool({
|
|
987
|
+
name: 'TaskOutput',
|
|
988
|
+
description: 'Retrieve output from a completed or running background subagent task.',
|
|
989
|
+
inputSchema: z.strictObject({
|
|
990
|
+
task_id: z.string().describe('The ID of the task to get output for'),
|
|
991
|
+
block: z.boolean().optional().describe('Wait for task to complete before returning'),
|
|
992
|
+
timeout: z.number().int().positive().optional().describe('Max wait time in milliseconds'),
|
|
993
|
+
}),
|
|
994
|
+
isReadOnly: () => true,
|
|
995
|
+
serialize: serializeBackgroundTaskOutput,
|
|
996
|
+
}, async ({ task_id, block, timeout }, context) => {
|
|
997
|
+
if (block) {
|
|
998
|
+
return this.backgroundTaskManager.wait(task_id, {
|
|
999
|
+
timeoutMs: timeout,
|
|
1000
|
+
signal: context.signal,
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
const task = await this.backgroundTaskManager.get(task_id);
|
|
1004
|
+
if (!task) {
|
|
1005
|
+
throw new Error(`No background task with id "${task_id}" exists.`);
|
|
1006
|
+
}
|
|
1007
|
+
return task;
|
|
601
1008
|
});
|
|
602
1009
|
}
|
|
603
1010
|
async prepareSkillExecution(skillName, args, options, sessionId) {
|
|
@@ -651,12 +1058,30 @@ export class ActoviqAgentClient {
|
|
|
651
1058
|
return session.send(execution.prompt, execution.options);
|
|
652
1059
|
}
|
|
653
1060
|
async forwardStreamResult(stream, emit) {
|
|
1061
|
+
const errors = [];
|
|
654
1062
|
const pump = (async () => {
|
|
655
|
-
|
|
656
|
-
|
|
1063
|
+
try {
|
|
1064
|
+
for await (const event of stream) {
|
|
1065
|
+
emit(event);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
catch (error) {
|
|
1069
|
+
errors.push(error);
|
|
657
1070
|
}
|
|
658
1071
|
})();
|
|
659
|
-
|
|
1072
|
+
let result;
|
|
1073
|
+
try {
|
|
1074
|
+
[result] = await Promise.all([stream.result, pump]);
|
|
1075
|
+
}
|
|
1076
|
+
catch (error) {
|
|
1077
|
+
if (errors.length > 0) {
|
|
1078
|
+
throw new AggregateError([error, ...errors], 'Stream result and event pump both failed.');
|
|
1079
|
+
}
|
|
1080
|
+
throw error;
|
|
1081
|
+
}
|
|
1082
|
+
if (errors.length > 0) {
|
|
1083
|
+
throw errors[0];
|
|
1084
|
+
}
|
|
660
1085
|
return result;
|
|
661
1086
|
}
|
|
662
1087
|
getSessionRuntimeOverrides(sessionId) {
|
|
@@ -703,14 +1128,19 @@ export class ActoviqAgentClient {
|
|
|
703
1128
|
}
|
|
704
1129
|
this.sessionRuntimeOverrides.set(sessionId, next);
|
|
705
1130
|
}
|
|
706
|
-
setSessionRuntimePermissionContext(
|
|
1131
|
+
async setSessionRuntimePermissionContext(session, context) {
|
|
1132
|
+
const sessionId = session.id;
|
|
707
1133
|
const current = this.sessionRuntimeOverrides.get(sessionId) ?? {};
|
|
1134
|
+
const stored = session.snapshot();
|
|
1135
|
+
const persisted = getPersistedActoviqSessionPermissionState(stored.metadata);
|
|
708
1136
|
const next = {
|
|
709
1137
|
...current,
|
|
710
|
-
permissionMode: context.mode,
|
|
711
|
-
permissions:
|
|
712
|
-
|
|
713
|
-
|
|
1138
|
+
permissionMode: context.mode ?? current.permissionMode ?? persisted.mode,
|
|
1139
|
+
permissions: context.permissions !== undefined
|
|
1140
|
+
? clonePermissionRules(context.permissions)
|
|
1141
|
+
: current.permissions ?? persisted.permissions,
|
|
1142
|
+
classifier: context.classifier ?? current.classifier,
|
|
1143
|
+
approver: context.approver ?? current.approver,
|
|
714
1144
|
};
|
|
715
1145
|
if (isHooksEmpty(next.hooks) &&
|
|
716
1146
|
!next.permissionMode &&
|
|
@@ -718,27 +1148,42 @@ export class ActoviqAgentClient {
|
|
|
718
1148
|
!next.classifier &&
|
|
719
1149
|
!next.approver) {
|
|
720
1150
|
this.sessionRuntimeOverrides.delete(sessionId);
|
|
721
|
-
return;
|
|
722
1151
|
}
|
|
723
|
-
|
|
1152
|
+
else {
|
|
1153
|
+
this.sessionRuntimeOverrides.set(sessionId, next);
|
|
1154
|
+
}
|
|
1155
|
+
stored.metadata[ACTOVIQ_SESSION_PERMISSION_STATE_KEY] =
|
|
1156
|
+
serializeActoviqSessionPermissionState({
|
|
1157
|
+
mode: next.permissionMode,
|
|
1158
|
+
permissions: clonePermissionRules(next.permissions) ?? [],
|
|
1159
|
+
});
|
|
1160
|
+
stored.updatedAt = nowIso();
|
|
1161
|
+
await this.store.save(stored);
|
|
1162
|
+
return stored;
|
|
724
1163
|
}
|
|
725
|
-
clearSessionRuntimePermissionContext(
|
|
1164
|
+
async clearSessionRuntimePermissionContext(session) {
|
|
1165
|
+
const sessionId = session.id;
|
|
726
1166
|
const current = this.sessionRuntimeOverrides.get(sessionId);
|
|
727
|
-
if (
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1167
|
+
if (current) {
|
|
1168
|
+
const next = {
|
|
1169
|
+
...current,
|
|
1170
|
+
permissionMode: undefined,
|
|
1171
|
+
permissions: undefined,
|
|
1172
|
+
classifier: undefined,
|
|
1173
|
+
approver: undefined,
|
|
1174
|
+
};
|
|
1175
|
+
if (isHooksEmpty(next.hooks)) {
|
|
1176
|
+
this.sessionRuntimeOverrides.delete(sessionId);
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
this.sessionRuntimeOverrides.set(sessionId, next);
|
|
1180
|
+
}
|
|
740
1181
|
}
|
|
741
|
-
|
|
1182
|
+
const stored = session.snapshot();
|
|
1183
|
+
delete stored.metadata[ACTOVIQ_SESSION_PERMISSION_STATE_KEY];
|
|
1184
|
+
stored.updatedAt = nowIso();
|
|
1185
|
+
await this.store.save(stored);
|
|
1186
|
+
return stored;
|
|
742
1187
|
}
|
|
743
1188
|
applySessionRuntimeOverrides(sessionId, options) {
|
|
744
1189
|
const overrides = this.getSessionRuntimeOverrides(sessionId);
|
|
@@ -755,6 +1200,16 @@ export class ActoviqAgentClient {
|
|
|
755
1200
|
};
|
|
756
1201
|
}
|
|
757
1202
|
hydrateSession(stored) {
|
|
1203
|
+
const persistedPermissionState = getPersistedActoviqSessionPermissionState(stored.metadata);
|
|
1204
|
+
if (persistedPermissionState.mode ||
|
|
1205
|
+
persistedPermissionState.permissions.length > 0) {
|
|
1206
|
+
const current = this.sessionRuntimeOverrides.get(stored.id) ?? {};
|
|
1207
|
+
this.sessionRuntimeOverrides.set(stored.id, {
|
|
1208
|
+
...current,
|
|
1209
|
+
permissionMode: current.permissionMode ?? persistedPermissionState.mode,
|
|
1210
|
+
permissions: current.permissions ?? persistedPermissionState.permissions,
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
758
1213
|
return new AgentSession({
|
|
759
1214
|
runSession: (session, input, options) => this.runOnSession(session, input, options),
|
|
760
1215
|
streamSession: (session, input, options) => this.streamOnSession(session, input, options),
|
|
@@ -775,8 +1230,9 @@ export class ActoviqAgentClient {
|
|
|
775
1230
|
getAgentContinuity: (session) => this.getAgentContinuityForSession(session),
|
|
776
1231
|
setRuntimeHooks: (session, hooks) => this.setSessionRuntimeHooks(session.id, hooks),
|
|
777
1232
|
clearRuntimeHooks: (session) => this.clearSessionRuntimeHooks(session.id),
|
|
778
|
-
|
|
779
|
-
|
|
1233
|
+
setModel: (session, model) => this.setSessionModel(session, model),
|
|
1234
|
+
setRuntimePermissionContext: (session, context) => this.setSessionRuntimePermissionContext(session, context),
|
|
1235
|
+
clearRuntimePermissionContext: (session) => this.clearSessionRuntimePermissionContext(session),
|
|
780
1236
|
hydrate: (next) => this.hydrateSession(next),
|
|
781
1237
|
saveCheckpoint: (_session, label) => this.store.saveCheckpoint(stored.id, label),
|
|
782
1238
|
restoreCheckpoint: (session, checkpointId) => this.restoreCheckpointToSession(session, checkpointId),
|
|
@@ -784,6 +1240,13 @@ export class ActoviqAgentClient {
|
|
|
784
1240
|
deleteCheckpoint: (_session, checkpointId) => this.store.deleteCheckpoint(stored.id, checkpointId),
|
|
785
1241
|
}, this.store, stored);
|
|
786
1242
|
}
|
|
1243
|
+
async setSessionModel(session, model) {
|
|
1244
|
+
const next = session.snapshot();
|
|
1245
|
+
next.model = this.resolveModel(model);
|
|
1246
|
+
next.updatedAt = nowIso();
|
|
1247
|
+
await this.store.save(next);
|
|
1248
|
+
return next;
|
|
1249
|
+
}
|
|
787
1250
|
async restoreCheckpointToSession(session, checkpointId) {
|
|
788
1251
|
const checkpoint = await this.store.loadCheckpoint(session.id, checkpointId);
|
|
789
1252
|
session.replace(checkpoint.snapshot);
|
|
@@ -886,13 +1349,30 @@ export class ActoviqAgentClient {
|
|
|
886
1349
|
...(augmentations?.metadata ?? {}),
|
|
887
1350
|
...(options.metadata ?? {}),
|
|
888
1351
|
};
|
|
889
|
-
const
|
|
1352
|
+
const workDir = options.__actoviqWorkDir ?? options.workDir ?? this.config.workDir;
|
|
1353
|
+
const mergedTools = filterAgentTools(mergeUniqueByName(options.__actoviqUseDefaultTools === false ? [] : this.defaultTools, options.tools ?? []), options.__actoviqAllowedTools, options.__actoviqDisallowedTools);
|
|
890
1354
|
// Collect tool prompts for system prompt assembly
|
|
891
1355
|
const toolPromptParts = await collectToolPrompts(mergedTools, {
|
|
892
|
-
workDir
|
|
1356
|
+
workDir,
|
|
893
1357
|
permissionMode: options.permissionMode ?? this.defaultPermissionMode,
|
|
894
1358
|
});
|
|
895
1359
|
const systemPrompt = await this.resolveSystemPrompt(options, session, [...(augmentations?.systemPromptParts ?? []), ...toolPromptParts]);
|
|
1360
|
+
const runtimeConfig = options.__actoviqMaxToolIterations || workDir !== this.config.workDir
|
|
1361
|
+
? {
|
|
1362
|
+
...this.config,
|
|
1363
|
+
workDir,
|
|
1364
|
+
...(options.__actoviqMaxToolIterations
|
|
1365
|
+
? { maxToolIterations: options.__actoviqMaxToolIterations }
|
|
1366
|
+
: {}),
|
|
1367
|
+
}
|
|
1368
|
+
: this.config;
|
|
1369
|
+
const notificationKey = session?.id ?? runId;
|
|
1370
|
+
const drainQueuedInputs = notificationKey || options.drainQueuedInputs
|
|
1371
|
+
? () => [
|
|
1372
|
+
...(options.drainQueuedInputs?.() ?? []),
|
|
1373
|
+
...this.drainRuntimeNotifications(notificationKey),
|
|
1374
|
+
]
|
|
1375
|
+
: undefined;
|
|
896
1376
|
return executeConversation({
|
|
897
1377
|
runId,
|
|
898
1378
|
input,
|
|
@@ -902,9 +1382,10 @@ export class ActoviqAgentClient {
|
|
|
902
1382
|
systemPrompt,
|
|
903
1383
|
tools: mergedTools,
|
|
904
1384
|
mcpServers: mergeUniqueByName(options.__actoviqUseDefaultMcpServers === false ? [] : this.defaultMcpServers, options.mcpServers ?? []),
|
|
905
|
-
model: options.model ?? session?.model
|
|
1385
|
+
model: this.resolveModel(options.model ?? session?.model),
|
|
906
1386
|
maxTokens: options.maxTokens,
|
|
907
1387
|
temperature: options.temperature,
|
|
1388
|
+
effort: options.effort,
|
|
908
1389
|
toolChoice: options.toolChoice,
|
|
909
1390
|
userId: options.userId ?? this.config.userId,
|
|
910
1391
|
metadata,
|
|
@@ -915,11 +1396,12 @@ export class ActoviqAgentClient {
|
|
|
915
1396
|
approver: options.approver ?? this.defaultApprover,
|
|
916
1397
|
canUseTool: options.canUseTool,
|
|
917
1398
|
hooks: augmentations?.hooks,
|
|
1399
|
+
drainQueuedInputs,
|
|
918
1400
|
streaming,
|
|
919
1401
|
emit,
|
|
920
1402
|
skipRunStartedEvent,
|
|
921
1403
|
modelApi: this.modelApi,
|
|
922
|
-
config:
|
|
1404
|
+
config: runtimeConfig,
|
|
923
1405
|
mcpManager: this.mcpManager,
|
|
924
1406
|
}).then(result => ({
|
|
925
1407
|
...result,
|
|
@@ -979,11 +1461,32 @@ export class ActoviqAgentClient {
|
|
|
979
1461
|
return promptParts.join('\n\n');
|
|
980
1462
|
}
|
|
981
1463
|
async prepareRunAugmentations(runId, input, options, session) {
|
|
1464
|
+
const internalOptions = options;
|
|
982
1465
|
const promptText = typeof input === 'string' ? input : extractTextFromContent(input);
|
|
983
1466
|
const memoryContext = await this.prepareRelevantMemoryContext(input, session);
|
|
984
1467
|
const invokedSkillContext = this.prepareInvokedSkillContext(session);
|
|
1468
|
+
const notificationMessages = session
|
|
1469
|
+
? (await this.collectPendingTaskNotifications(session.id)).map(text => ({
|
|
1470
|
+
role: 'user',
|
|
1471
|
+
content: text,
|
|
1472
|
+
}))
|
|
1473
|
+
: [];
|
|
1474
|
+
const initialPromptMessages = internalOptions.__actoviqInitialPrompt
|
|
1475
|
+
? [{
|
|
1476
|
+
role: 'user',
|
|
1477
|
+
content: internalOptions.__actoviqInitialPrompt,
|
|
1478
|
+
}]
|
|
1479
|
+
: [];
|
|
1480
|
+
const agentMemoryMessages = session
|
|
1481
|
+
? await this.prepareAgentMemoryMessages(session, internalOptions)
|
|
1482
|
+
: [];
|
|
1483
|
+
const preloadedSkillMessages = await this.preparePreloadedAgentSkillMessages(internalOptions.__actoviqPreloadedSkills, session?.id, internalOptions.__actoviqWorkDir ?? internalOptions.workDir ?? this.config.workDir);
|
|
985
1484
|
const hooks = mergeActoviqHooks(this.hooks, options.hooks);
|
|
986
1485
|
const prefixedMessages = [
|
|
1486
|
+
...notificationMessages,
|
|
1487
|
+
...initialPromptMessages,
|
|
1488
|
+
...agentMemoryMessages,
|
|
1489
|
+
...preloadedSkillMessages,
|
|
987
1490
|
...invokedSkillContext.prefixedMessages,
|
|
988
1491
|
...memoryContext.prefixedMessages,
|
|
989
1492
|
];
|
|
@@ -996,7 +1499,7 @@ export class ActoviqAgentClient {
|
|
|
996
1499
|
promptText,
|
|
997
1500
|
sessionId: session?.id,
|
|
998
1501
|
session: session ? deepClone(session) : undefined,
|
|
999
|
-
workDir: this.config.workDir,
|
|
1502
|
+
workDir: internalOptions.__actoviqWorkDir ?? internalOptions.workDir ?? this.config.workDir,
|
|
1000
1503
|
options,
|
|
1001
1504
|
});
|
|
1002
1505
|
if (!result) {
|
|
@@ -1019,6 +1522,70 @@ export class ActoviqAgentClient {
|
|
|
1019
1522
|
metadata,
|
|
1020
1523
|
};
|
|
1021
1524
|
}
|
|
1525
|
+
async preparePreloadedAgentSkillMessages(skillNames, sessionId, workDir) {
|
|
1526
|
+
if (!skillNames?.length) {
|
|
1527
|
+
return [];
|
|
1528
|
+
}
|
|
1529
|
+
const messages = [];
|
|
1530
|
+
for (const skillName of skillNames) {
|
|
1531
|
+
const definition = this.getSkillDefinition(skillName);
|
|
1532
|
+
if (!definition) {
|
|
1533
|
+
messages.push({
|
|
1534
|
+
role: 'user',
|
|
1535
|
+
content: `<agent_skill_warning>Skill "${skillName}" was requested by the agent definition but is not registered.</agent_skill_warning>`,
|
|
1536
|
+
});
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const resolved = await resolveActoviqSkillPrompt(definition, '', {
|
|
1540
|
+
args: '',
|
|
1541
|
+
workDir,
|
|
1542
|
+
homeDir: this.config.homeDir,
|
|
1543
|
+
sessionId,
|
|
1544
|
+
userId: this.config.userId,
|
|
1545
|
+
});
|
|
1546
|
+
messages.push({
|
|
1547
|
+
role: 'user',
|
|
1548
|
+
content: [
|
|
1549
|
+
`<agent_skill name="${skillName}">`,
|
|
1550
|
+
extractTextFromContent(resolved.content),
|
|
1551
|
+
'</agent_skill>',
|
|
1552
|
+
].join('\n'),
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
return messages;
|
|
1556
|
+
}
|
|
1557
|
+
async prepareAgentMemoryMessages(session, options) {
|
|
1558
|
+
const scope = session.metadata.__actoviqAgentMemory;
|
|
1559
|
+
const agentName = session.metadata.__actoviqAgentDefinition;
|
|
1560
|
+
if ((scope !== 'user' && scope !== 'project' && scope !== 'local') ||
|
|
1561
|
+
typeof agentName !== 'string') {
|
|
1562
|
+
return [];
|
|
1563
|
+
}
|
|
1564
|
+
const workDir = options.__actoviqWorkDir ?? options.workDir ?? this.config.workDir;
|
|
1565
|
+
const root = scope === 'user'
|
|
1566
|
+
? path.join(this.config.homeDir, 'agent-memory', agentName)
|
|
1567
|
+
: scope === 'project'
|
|
1568
|
+
? path.join(workDir, '.actoviq', 'agent-memory', agentName)
|
|
1569
|
+
: path.join(workDir, '.actoviq', 'agent-memory-local', agentName);
|
|
1570
|
+
const memoryPath = path.join(root, 'MEMORY.md');
|
|
1571
|
+
await mkdir(root, { recursive: true });
|
|
1572
|
+
let content = '';
|
|
1573
|
+
try {
|
|
1574
|
+
content = await readFile(memoryPath, 'utf8');
|
|
1575
|
+
}
|
|
1576
|
+
catch {
|
|
1577
|
+
// A new agent memory starts empty and can be updated by normal file tools.
|
|
1578
|
+
}
|
|
1579
|
+
return [{
|
|
1580
|
+
role: 'user',
|
|
1581
|
+
content: [
|
|
1582
|
+
`<agent_memory scope="${scope}" path="${memoryPath}">`,
|
|
1583
|
+
content.trim() || '(empty)',
|
|
1584
|
+
'</agent_memory>',
|
|
1585
|
+
`Persist durable lessons for future ${agentName} runs by updating ${memoryPath}.`,
|
|
1586
|
+
].join('\n'),
|
|
1587
|
+
}];
|
|
1588
|
+
}
|
|
1022
1589
|
async applyPostRunHooks(runId, input, options, result, session) {
|
|
1023
1590
|
const promptText = typeof input === 'string' ? input : extractTextFromContent(input);
|
|
1024
1591
|
const hooks = mergeActoviqHooks(this.hooks, options.hooks);
|
|
@@ -1031,7 +1598,9 @@ export class ActoviqAgentClient {
|
|
|
1031
1598
|
promptText,
|
|
1032
1599
|
sessionId: session?.id,
|
|
1033
1600
|
session: session ? deepClone(session) : undefined,
|
|
1034
|
-
workDir:
|
|
1601
|
+
workDir: options.__actoviqWorkDir ??
|
|
1602
|
+
options.workDir ??
|
|
1603
|
+
this.config.workDir,
|
|
1035
1604
|
options,
|
|
1036
1605
|
result,
|
|
1037
1606
|
});
|
|
@@ -1057,6 +1626,27 @@ export class ActoviqAgentClient {
|
|
|
1057
1626
|
existing.push(record);
|
|
1058
1627
|
this.pendingDelegations.set(key, existing);
|
|
1059
1628
|
}
|
|
1629
|
+
updatePendingDelegation(key, task) {
|
|
1630
|
+
const records = this.pendingDelegations.get(key);
|
|
1631
|
+
const record = records?.find(candidate => candidate.taskId === task.id);
|
|
1632
|
+
if (!record) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
record.runId = task.runId ?? record.runId;
|
|
1636
|
+
record.sessionId = task.sessionId ?? record.sessionId;
|
|
1637
|
+
record.status =
|
|
1638
|
+
task.status === 'completed'
|
|
1639
|
+
? 'completed'
|
|
1640
|
+
: task.status === 'failed'
|
|
1641
|
+
? 'failed'
|
|
1642
|
+
: task.status === 'cancelled'
|
|
1643
|
+
? 'cancelled'
|
|
1644
|
+
: record.status;
|
|
1645
|
+
record.requestCount = task.requestCount ?? record.requestCount;
|
|
1646
|
+
record.toolCallCount = task.toolCallCount ?? record.toolCallCount;
|
|
1647
|
+
record.toolErrorCount = task.toolErrorCount ?? record.toolErrorCount;
|
|
1648
|
+
record.textSummary = task.text ?? task.partialText ?? record.textSummary;
|
|
1649
|
+
}
|
|
1060
1650
|
consumePendingDelegations(key) {
|
|
1061
1651
|
if (!key) {
|
|
1062
1652
|
return [];
|
|
@@ -1157,6 +1747,9 @@ export class ActoviqAgentClient {
|
|
|
1157
1747
|
latestBoundary,
|
|
1158
1748
|
compactCount: Math.max(compactState.compactCount, persistedCompactState.compactCount),
|
|
1159
1749
|
microcompactCount: Math.max(compactState.microcompactCount, persistedCompactState.microcompactCount),
|
|
1750
|
+
consecutiveCompactFailures: persistedCompactState.consecutiveFailures,
|
|
1751
|
+
lastCompactFailureAt: persistedCompactState.lastFailureAt,
|
|
1752
|
+
lastCompactError: persistedCompactState.lastError,
|
|
1160
1753
|
hasCompacted: compactState.hasCompacted ||
|
|
1161
1754
|
persistedCompactState.compactCount + persistedCompactState.microcompactCount > 0,
|
|
1162
1755
|
summaryMessage: compactState.summaryMessage ?? persistedCompactState.lastSummaryMessage,
|
|
@@ -1185,12 +1778,16 @@ export class ActoviqAgentClient {
|
|
|
1185
1778
|
}, {
|
|
1186
1779
|
workDir: this.config.workDir,
|
|
1187
1780
|
systemPrompt: snapshot.systemPrompt ?? this.config.systemPrompt,
|
|
1188
|
-
model: options.model ?? snapshot.model
|
|
1781
|
+
model: this.resolveModel(options.model ?? snapshot.model),
|
|
1189
1782
|
modelApi: this.modelApi,
|
|
1190
1783
|
compactConfig: this.config.compact,
|
|
1191
1784
|
runtimeState: this.getSessionMemoryRuntimeState(snapshot),
|
|
1192
1785
|
});
|
|
1193
|
-
if (reactive.
|
|
1786
|
+
if (!reactive.result.compacted) {
|
|
1787
|
+
if (reactive.session !== snapshot) {
|
|
1788
|
+
await this.store.save(reactive.session);
|
|
1789
|
+
session.replace(reactive.session);
|
|
1790
|
+
}
|
|
1194
1791
|
return undefined;
|
|
1195
1792
|
}
|
|
1196
1793
|
await this.store.save(reactive.session);
|
|
@@ -1208,48 +1805,239 @@ export class ActoviqAgentClient {
|
|
|
1208
1805
|
result: reactive.result,
|
|
1209
1806
|
};
|
|
1210
1807
|
}
|
|
1211
|
-
async
|
|
1808
|
+
async runDelegatedAgentTask(agent, prompt, runOptions = {}, delegation = { description: prompt }) {
|
|
1212
1809
|
const definition = this.requireAgentDefinition(agent);
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1810
|
+
const prepared = await this.prepareDelegatedWorkspace(definition, delegation);
|
|
1811
|
+
try {
|
|
1812
|
+
const session = await this.createAgentSession(agent, {
|
|
1813
|
+
title: `${delegation.name ?? definition.name}: ${truncateText(delegation.description, 80)}`,
|
|
1814
|
+
metadata: {
|
|
1815
|
+
__actoviqAgentName: delegation.name,
|
|
1816
|
+
__actoviqAgentWorkDir: prepared.workDir,
|
|
1817
|
+
__actoviqAgentWorktreePath: prepared.workspace?.path,
|
|
1818
|
+
__actoviqAgentWorktreeBranch: prepared.workspace?.metadata.branch,
|
|
1819
|
+
},
|
|
1820
|
+
});
|
|
1821
|
+
const result = await session.send(prompt, this.prepareDelegatedRunOptions(runOptions, prepared.workDir, session.id));
|
|
1822
|
+
const retained = await this.finalizeDelegatedWorkspace(prepared.workspace);
|
|
1823
|
+
return {
|
|
1824
|
+
result,
|
|
1825
|
+
sessionId: session.id,
|
|
1826
|
+
worktreePath: retained ? prepared.workspace?.path : undefined,
|
|
1827
|
+
worktreeBranch: retained ? prepared.workspace?.metadata.branch : undefined,
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
catch (error) {
|
|
1831
|
+
await this.finalizeDelegatedWorkspace(prepared.workspace);
|
|
1832
|
+
throw error;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
async routeMessageToAgent(address, message, context) {
|
|
1836
|
+
const tasks = await this.backgroundTaskManager.list();
|
|
1837
|
+
const task = tasks.find(candidate => candidate.id === address ||
|
|
1838
|
+
candidate.sessionId === address ||
|
|
1839
|
+
candidate.agentName === address);
|
|
1840
|
+
if (task?.sessionId && (task.status === 'queued' || task.status === 'running')) {
|
|
1841
|
+
const queue = this.subagentInputQueues.get(task.sessionId) ?? [];
|
|
1842
|
+
queue.push(message);
|
|
1843
|
+
this.subagentInputQueues.set(task.sessionId, queue);
|
|
1844
|
+
await this.backgroundTaskManager.updateProgress(task.id, {
|
|
1845
|
+
queuedMessageCount: queue.length,
|
|
1846
|
+
progressSummary: `Queued follow-up message for ${task.agentName ?? task.subagentType}.`,
|
|
1847
|
+
});
|
|
1848
|
+
return {
|
|
1849
|
+
status: 'queued',
|
|
1850
|
+
taskId: task.id,
|
|
1851
|
+
agentId: task.sessionId,
|
|
1852
|
+
agentName: task.agentName,
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
const sessionId = task?.sessionId ?? address;
|
|
1856
|
+
let session;
|
|
1857
|
+
try {
|
|
1858
|
+
session = await this.resumeSession(sessionId);
|
|
1859
|
+
}
|
|
1860
|
+
catch {
|
|
1861
|
+
throw new Error(`No addressable agent found for "${address}".`);
|
|
1862
|
+
}
|
|
1863
|
+
const agentName = typeof session.metadata.__actoviqAgentDefinition === 'string'
|
|
1864
|
+
? session.metadata.__actoviqAgentDefinition
|
|
1865
|
+
: task?.subagentType;
|
|
1866
|
+
if (!agentName) {
|
|
1867
|
+
throw new Error(`Session "${sessionId}" is not an agent session.`);
|
|
1868
|
+
}
|
|
1869
|
+
const resumed = await this.launchBackgroundOnSession(session, agentName, message, {
|
|
1870
|
+
parentRunId: context.parentRunId,
|
|
1871
|
+
parentSessionId: context.parentSessionId,
|
|
1872
|
+
}, context.runOptions, {
|
|
1873
|
+
description: `Continue ${task?.agentName ?? agentName}`,
|
|
1874
|
+
name: task?.agentName,
|
|
1875
|
+
cwd: typeof session.metadata.__actoviqAgentWorkDir === 'string'
|
|
1876
|
+
? session.metadata.__actoviqAgentWorkDir
|
|
1877
|
+
: undefined,
|
|
1878
|
+
}, task?.id);
|
|
1879
|
+
return {
|
|
1880
|
+
status: 'resumed',
|
|
1881
|
+
taskId: resumed.id,
|
|
1882
|
+
agentId: session.id,
|
|
1883
|
+
agentName: task?.agentName,
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
async prepareDelegatedWorkspace(definition, delegation) {
|
|
1887
|
+
if (delegation.cwd) {
|
|
1888
|
+
return { workDir: path.resolve(delegation.cwd) };
|
|
1889
|
+
}
|
|
1890
|
+
if ((delegation.isolation ?? definition.isolation) !== 'worktree') {
|
|
1891
|
+
return { workDir: path.resolve(definition.cwd ?? this.config.workDir) };
|
|
1892
|
+
}
|
|
1893
|
+
const branch = `actoviq-agent-${createId().slice(0, 8)}`;
|
|
1894
|
+
const workspace = await createGitWorktreeWorkspace({
|
|
1895
|
+
repositoryPath: this.config.workDir,
|
|
1896
|
+
name: delegation.name
|
|
1897
|
+
? `actoviq-${sanitizeWorkspaceName(delegation.name)}-${createId().slice(0, 6)}`
|
|
1898
|
+
: undefined,
|
|
1899
|
+
branch,
|
|
1215
1900
|
metadata: {
|
|
1216
|
-
|
|
1217
|
-
__actoviqBackgroundParentSessionId: options.parentSessionId,
|
|
1901
|
+
agent: definition.name,
|
|
1218
1902
|
},
|
|
1219
1903
|
});
|
|
1220
|
-
|
|
1221
|
-
|
|
1904
|
+
return { workDir: workspace.path, workspace };
|
|
1905
|
+
}
|
|
1906
|
+
prepareDelegatedRunOptions(runOptions, workDir, sessionId) {
|
|
1907
|
+
return {
|
|
1908
|
+
...runOptions,
|
|
1909
|
+
workDir,
|
|
1910
|
+
tools: [
|
|
1911
|
+
...createActoviqFileTools({ cwd: workDir }),
|
|
1912
|
+
...(runOptions.tools ?? []),
|
|
1913
|
+
],
|
|
1914
|
+
drainQueuedInputs: () => [
|
|
1915
|
+
...(runOptions.drainQueuedInputs?.() ?? []),
|
|
1916
|
+
...this.drainSubagentInputs(sessionId),
|
|
1917
|
+
],
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
async finalizeDelegatedWorkspace(workspace) {
|
|
1921
|
+
if (!workspace) {
|
|
1922
|
+
return false;
|
|
1222
1923
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
session.setPermissionContext({
|
|
1228
|
-
mode: runOptions.permissionMode,
|
|
1229
|
-
permissions: runOptions.permissions,
|
|
1230
|
-
classifier: runOptions.classifier,
|
|
1231
|
-
approver: runOptions.approver,
|
|
1232
|
-
});
|
|
1924
|
+
const dirty = await isGitWorkspaceDirty(workspace.path);
|
|
1925
|
+
if (!dirty) {
|
|
1926
|
+
await workspace.dispose();
|
|
1927
|
+
return false;
|
|
1233
1928
|
}
|
|
1234
|
-
return
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1929
|
+
return true;
|
|
1930
|
+
}
|
|
1931
|
+
drainSubagentInputs(sessionId) {
|
|
1932
|
+
const queued = this.subagentInputQueues.get(sessionId) ?? [];
|
|
1933
|
+
this.subagentInputQueues.delete(sessionId);
|
|
1934
|
+
return queued;
|
|
1935
|
+
}
|
|
1936
|
+
async collectPendingTaskNotifications(parentSessionId) {
|
|
1937
|
+
this.pendingRuntimeNotifications.delete(parentSessionId);
|
|
1938
|
+
const notifications = [];
|
|
1939
|
+
for (const task of await this.backgroundTaskManager.list()) {
|
|
1940
|
+
if (task.parentSessionId !== parentSessionId ||
|
|
1941
|
+
task.notificationDeliveredAt ||
|
|
1942
|
+
(task.status !== 'completed' &&
|
|
1943
|
+
task.status !== 'failed' &&
|
|
1944
|
+
task.status !== 'cancelled')) {
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
notifications.push(formatTaskNotification(task));
|
|
1948
|
+
await this.markTaskNotificationDelivered(task.id);
|
|
1949
|
+
}
|
|
1950
|
+
return notifications;
|
|
1951
|
+
}
|
|
1952
|
+
enqueueTaskNotification(task) {
|
|
1953
|
+
const notificationKey = task.parentSessionId ?? task.parentRunId;
|
|
1954
|
+
if (!notificationKey || task.notificationDeliveredAt) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
const queue = this.pendingRuntimeNotifications.get(notificationKey) ?? [];
|
|
1958
|
+
if (!queue.some(entry => entry.taskId === task.id)) {
|
|
1959
|
+
queue.push({ taskId: task.id, text: formatTaskNotification(task) });
|
|
1960
|
+
this.pendingRuntimeNotifications.set(notificationKey, queue);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
drainRuntimeNotifications(sessionId) {
|
|
1964
|
+
const queued = this.pendingRuntimeNotifications.get(sessionId) ?? [];
|
|
1965
|
+
this.pendingRuntimeNotifications.delete(sessionId);
|
|
1966
|
+
for (const entry of queued) {
|
|
1967
|
+
void this.markTaskNotificationDelivered(entry.taskId);
|
|
1968
|
+
}
|
|
1969
|
+
return queued.map(entry => entry.text);
|
|
1970
|
+
}
|
|
1971
|
+
async markTaskNotificationDelivered(taskId) {
|
|
1972
|
+
const task = await this.backgroundTaskManager.get(taskId);
|
|
1973
|
+
if (!task || task.notificationDeliveredAt) {
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
await this.backgroundTaskStore.save({
|
|
1977
|
+
...task,
|
|
1978
|
+
notificationDeliveredAt: nowIso(),
|
|
1979
|
+
updatedAt: nowIso(),
|
|
1250
1980
|
});
|
|
1251
1981
|
}
|
|
1252
|
-
async
|
|
1982
|
+
async launchBackgroundAgentTask(agent, prompt, options, runOptions = {}, delegation = { description: prompt }) {
|
|
1983
|
+
const definition = this.requireAgentDefinition(agent);
|
|
1984
|
+
const prepared = await this.prepareDelegatedWorkspace(definition, delegation);
|
|
1985
|
+
try {
|
|
1986
|
+
const session = await this.createAgentSession(agent, {
|
|
1987
|
+
title: `${delegation.name ?? definition.name}: ${truncateText(delegation.description, 80)}`,
|
|
1988
|
+
metadata: {
|
|
1989
|
+
__actoviqBackgroundParentRunId: options.parentRunId,
|
|
1990
|
+
__actoviqBackgroundParentSessionId: options.parentSessionId,
|
|
1991
|
+
__actoviqAgentName: delegation.name,
|
|
1992
|
+
__actoviqAgentWorkDir: prepared.workDir,
|
|
1993
|
+
__actoviqAgentWorktreePath: prepared.workspace?.path,
|
|
1994
|
+
__actoviqAgentWorktreeBranch: prepared.workspace?.metadata.branch,
|
|
1995
|
+
},
|
|
1996
|
+
});
|
|
1997
|
+
if (runOptions.hooks) {
|
|
1998
|
+
session.setHooks(runOptions.hooks);
|
|
1999
|
+
}
|
|
2000
|
+
if (runOptions.permissionMode ||
|
|
2001
|
+
runOptions.permissions ||
|
|
2002
|
+
runOptions.classifier ||
|
|
2003
|
+
runOptions.approver) {
|
|
2004
|
+
await session.setPermissionContext({
|
|
2005
|
+
mode: runOptions.permissionMode,
|
|
2006
|
+
permissions: runOptions.permissions,
|
|
2007
|
+
classifier: runOptions.classifier,
|
|
2008
|
+
approver: runOptions.approver,
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
return this.backgroundTaskManager.launch({
|
|
2012
|
+
subagentType: definition.name,
|
|
2013
|
+
description: delegation.description,
|
|
2014
|
+
workDir: prepared.workDir,
|
|
2015
|
+
parentRunId: options.parentRunId,
|
|
2016
|
+
parentSessionId: options.parentSessionId,
|
|
2017
|
+
sessionId: session.id,
|
|
2018
|
+
agentName: delegation.name,
|
|
2019
|
+
worktreePath: prepared.workspace?.path,
|
|
2020
|
+
worktreeBranch: prepared.workspace?.metadata.branch,
|
|
2021
|
+
onRun: (signal, updateProgress) => this.runBackgroundAgentSession({
|
|
2022
|
+
session,
|
|
2023
|
+
prompt,
|
|
2024
|
+
signal,
|
|
2025
|
+
updateProgress,
|
|
2026
|
+
runOptions: this.prepareDelegatedRunOptions(runOptions, prepared.workDir, session.id),
|
|
2027
|
+
workspace: prepared.workspace,
|
|
2028
|
+
}),
|
|
2029
|
+
onSettled: task => {
|
|
2030
|
+
this.updatePendingDelegation(options.parentSessionId ?? options.parentRunId, task);
|
|
2031
|
+
this.enqueueTaskNotification(task);
|
|
2032
|
+
},
|
|
2033
|
+
});
|
|
2034
|
+
}
|
|
2035
|
+
catch (error) {
|
|
2036
|
+
await this.finalizeDelegatedWorkspace(prepared.workspace);
|
|
2037
|
+
throw error;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
async launchBackgroundOnSession(session, agent, prompt, options, runOptions = {}, delegation = { description: prompt }, resumedFromTaskId) {
|
|
1253
2041
|
const definition = this.requireAgentDefinition(agent);
|
|
1254
2042
|
if (runOptions.hooks) {
|
|
1255
2043
|
session.setHooks(runOptions.hooks);
|
|
@@ -1258,7 +2046,7 @@ export class ActoviqAgentClient {
|
|
|
1258
2046
|
runOptions.permissions ||
|
|
1259
2047
|
runOptions.classifier ||
|
|
1260
2048
|
runOptions.approver) {
|
|
1261
|
-
session.setPermissionContext({
|
|
2049
|
+
await session.setPermissionContext({
|
|
1262
2050
|
mode: runOptions.permissionMode,
|
|
1263
2051
|
permissions: runOptions.permissions,
|
|
1264
2052
|
classifier: runOptions.classifier,
|
|
@@ -1267,32 +2055,82 @@ export class ActoviqAgentClient {
|
|
|
1267
2055
|
}
|
|
1268
2056
|
return this.backgroundTaskManager.launch({
|
|
1269
2057
|
subagentType: definition.name,
|
|
1270
|
-
description:
|
|
1271
|
-
workDir: this.config.workDir,
|
|
2058
|
+
description: delegation.description,
|
|
2059
|
+
workDir: delegation.cwd ?? this.config.workDir,
|
|
1272
2060
|
parentRunId: options.parentRunId,
|
|
1273
2061
|
parentSessionId: options.parentSessionId ?? session.id,
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
2062
|
+
sessionId: session.id,
|
|
2063
|
+
agentName: delegation.name,
|
|
2064
|
+
resumedFromTaskId,
|
|
2065
|
+
onRun: (signal, updateProgress) => this.runBackgroundAgentSession({
|
|
2066
|
+
session,
|
|
2067
|
+
prompt,
|
|
2068
|
+
signal,
|
|
2069
|
+
updateProgress,
|
|
2070
|
+
runOptions: this.prepareDelegatedRunOptions(runOptions, delegation.cwd ?? this.config.workDir, session.id),
|
|
2071
|
+
}),
|
|
2072
|
+
onSettled: task => {
|
|
2073
|
+
this.updatePendingDelegation(options.parentSessionId ?? options.parentRunId, task);
|
|
2074
|
+
this.enqueueTaskNotification(task);
|
|
1283
2075
|
},
|
|
1284
2076
|
});
|
|
1285
2077
|
}
|
|
2078
|
+
async runBackgroundAgentSession(args) {
|
|
2079
|
+
try {
|
|
2080
|
+
await args.updateProgress({
|
|
2081
|
+
progressSummary: 'Agent is running.',
|
|
2082
|
+
});
|
|
2083
|
+
const result = await args.session.send(args.prompt, {
|
|
2084
|
+
...args.runOptions,
|
|
2085
|
+
signal: args.signal,
|
|
2086
|
+
});
|
|
2087
|
+
await args.updateProgress({
|
|
2088
|
+
partialText: result.text,
|
|
2089
|
+
requestCount: result.requests.length,
|
|
2090
|
+
toolCallCount: result.toolCalls.length,
|
|
2091
|
+
toolErrorCount: result.toolCalls.filter(call => call.isError).length,
|
|
2092
|
+
currentIteration: result.requests.at(-1)?.iteration,
|
|
2093
|
+
currentToolName: undefined,
|
|
2094
|
+
progressSummary: 'Agent completed.',
|
|
2095
|
+
});
|
|
2096
|
+
const retainedWorktree = await this.finalizeDelegatedWorkspace(args.workspace);
|
|
2097
|
+
return {
|
|
2098
|
+
runId: result.runId,
|
|
2099
|
+
sessionId: args.session.id,
|
|
2100
|
+
model: result.model,
|
|
2101
|
+
text: result.text,
|
|
2102
|
+
toolCallCount: result.toolCalls.length,
|
|
2103
|
+
toolErrorCount: result.toolCalls.filter(call => call.isError).length,
|
|
2104
|
+
requestCount: result.requests.length,
|
|
2105
|
+
retainedWorktree,
|
|
2106
|
+
worktreePath: retainedWorktree ? args.workspace?.path : undefined,
|
|
2107
|
+
worktreeBranch: retainedWorktree ? args.workspace?.metadata.branch : undefined,
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
catch (error) {
|
|
2111
|
+
await args.updateProgress({
|
|
2112
|
+
progressSummary: args.signal.aborted
|
|
2113
|
+
? 'Agent stopped before completion.'
|
|
2114
|
+
: 'Agent failed before completion.',
|
|
2115
|
+
});
|
|
2116
|
+
await this.finalizeDelegatedWorkspace(args.workspace);
|
|
2117
|
+
throw error;
|
|
2118
|
+
}
|
|
2119
|
+
finally {
|
|
2120
|
+
this.subagentInputQueues.delete(args.session.id);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
1286
2123
|
async compactSessionForSession(session, options = {}) {
|
|
1287
2124
|
const snapshot = session.snapshot();
|
|
1288
2125
|
const { session: compactedSession, result } = await compactActoviqSession(snapshot, {
|
|
1289
2126
|
...options,
|
|
2127
|
+
model: options.model ? this.resolveModel(options.model) : undefined,
|
|
1290
2128
|
force: options.force ?? true,
|
|
1291
2129
|
trigger: 'manual',
|
|
1292
2130
|
}, {
|
|
1293
2131
|
workDir: this.config.workDir,
|
|
1294
2132
|
systemPrompt: snapshot.systemPrompt ?? this.config.systemPrompt,
|
|
1295
|
-
model:
|
|
2133
|
+
model: this.resolveModel(snapshot.model),
|
|
1296
2134
|
modelApi: this.modelApi,
|
|
1297
2135
|
compactConfig: this.config.compact,
|
|
1298
2136
|
runtimeState: this.getSessionMemoryRuntimeState(snapshot),
|
|
@@ -1313,7 +2151,7 @@ export class ActoviqAgentClient {
|
|
|
1313
2151
|
systemPrompt: await this.buildDreamSystemPrompt(),
|
|
1314
2152
|
tools: createActoviqFileTools({ cwd: this.config.workDir }),
|
|
1315
2153
|
mcpServers: [],
|
|
1316
|
-
model:
|
|
2154
|
+
model: this.resolveModel(request.model),
|
|
1317
2155
|
maxTokens: request.maxTokens ?? DEFAULT_DREAM_MAX_TOKENS,
|
|
1318
2156
|
userId: this.config.userId,
|
|
1319
2157
|
metadata: {
|
|
@@ -1527,7 +2365,7 @@ export class ActoviqAgentClient {
|
|
|
1527
2365
|
const stored = session.snapshot();
|
|
1528
2366
|
const extraction = await this.performSessionMemoryExtraction(stored, {
|
|
1529
2367
|
force: options.force ?? true,
|
|
1530
|
-
model: options.model ?? stored.model
|
|
2368
|
+
model: this.resolveModel(options.model ?? stored.model),
|
|
1531
2369
|
systemPrompt: stored.systemPrompt ?? this.config.systemPrompt,
|
|
1532
2370
|
trigger: 'manual',
|
|
1533
2371
|
maxTokens: options.maxTokens,
|
|
@@ -1549,6 +2387,9 @@ export class ActoviqAgentClient {
|
|
|
1549
2387
|
...(options.metadata ?? {}),
|
|
1550
2388
|
...(hookOutcome.sessionMetadata ?? {}),
|
|
1551
2389
|
};
|
|
2390
|
+
if (result.loopCompactions?.length) {
|
|
2391
|
+
recordActoviqLoopCompactionsOnSession(next, result.loopCompactions);
|
|
2392
|
+
}
|
|
1552
2393
|
const runtimeState = this.getSessionMemoryRuntimeState(next);
|
|
1553
2394
|
if (runtimeState.pendingPostCompaction) {
|
|
1554
2395
|
runtimeState.pendingPostCompaction = false;
|
|
@@ -1575,6 +2416,30 @@ export class ActoviqAgentClient {
|
|
|
1575
2416
|
next.metadata[INVOKED_SKILLS_STATE_KEY] = invokedSkills;
|
|
1576
2417
|
result.invokedSkills = invokedSkills;
|
|
1577
2418
|
}
|
|
2419
|
+
// Track recently touched files and skills so post-compact context
|
|
2420
|
+
// reminders can point the model back to its working set.
|
|
2421
|
+
for (const call of result.toolCalls) {
|
|
2422
|
+
if (call.isError) {
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
const callInput = call.input;
|
|
2426
|
+
if (RECENT_FILE_TOOL_NAMES.has(call.publicName)) {
|
|
2427
|
+
const filePath = typeof callInput?.file_path === 'string'
|
|
2428
|
+
? callInput.file_path
|
|
2429
|
+
: typeof callInput?.notebook_path === 'string'
|
|
2430
|
+
? callInput.notebook_path
|
|
2431
|
+
: undefined;
|
|
2432
|
+
if (filePath) {
|
|
2433
|
+
trackRecentFile(next, filePath);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
if (call.publicName === 'Skill' && typeof callInput?.skill === 'string') {
|
|
2437
|
+
trackRecentSkill(next, callInput.skill);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
for (const record of result.invokedSkills ?? []) {
|
|
2441
|
+
trackRecentSkill(next, record.name);
|
|
2442
|
+
}
|
|
1578
2443
|
const previousRelevantMemoryState = getRelevantMemorySessionState(next.metadata);
|
|
1579
2444
|
const surfacedPaths = new Set(previousRelevantMemoryState.surfacedPaths);
|
|
1580
2445
|
let totalBytes = previousRelevantMemoryState.totalBytes;
|
|
@@ -1618,7 +2483,7 @@ export class ActoviqAgentClient {
|
|
|
1618
2483
|
await appendMessagesToTranscript(paths.projectStateDir, session.id, this.config.workDir, newMessages);
|
|
1619
2484
|
}
|
|
1620
2485
|
const extraction = await this.performSessionMemoryExtraction(next, {
|
|
1621
|
-
model: options.model ?? next.model
|
|
2486
|
+
model: this.resolveModel(options.model ?? next.model),
|
|
1622
2487
|
systemPrompt: next.systemPrompt ?? this.config.systemPrompt,
|
|
1623
2488
|
trigger: 'auto',
|
|
1624
2489
|
maxTokens: Math.min(options.maxTokens ?? this.config.maxTokens, DEFAULT_SESSION_MEMORY_MAX_TOKENS),
|
|
@@ -1628,7 +2493,7 @@ export class ActoviqAgentClient {
|
|
|
1628
2493
|
const compacted = await compactActoviqSession(next, { trigger: 'auto' }, {
|
|
1629
2494
|
workDir: this.config.workDir,
|
|
1630
2495
|
systemPrompt: next.systemPrompt ?? this.config.systemPrompt,
|
|
1631
|
-
model: options.model ?? next.model
|
|
2496
|
+
model: this.resolveModel(options.model ?? next.model),
|
|
1632
2497
|
modelApi: this.modelApi,
|
|
1633
2498
|
compactConfig: this.config.compact,
|
|
1634
2499
|
runtimeState: extraction.state,
|
|
@@ -1674,10 +2539,22 @@ export class ActoviqAgentClient {
|
|
|
1674
2539
|
return this.mergeAgentRunOptions(this.requireAgentDefinition(agentName), options);
|
|
1675
2540
|
}
|
|
1676
2541
|
mergeAgentRunOptions(definition, options) {
|
|
2542
|
+
const availableMcpServers = new Set([
|
|
2543
|
+
...this.defaultMcpServers.map(server => server.name),
|
|
2544
|
+
...(definition.mcpServers ?? []).map(server => server.name),
|
|
2545
|
+
...(options.mcpServers ?? []).map(server => server.name),
|
|
2546
|
+
]);
|
|
2547
|
+
const missingMcpServers = (definition.requiredMcpServers ?? []).filter(server => !availableMcpServers.has(server));
|
|
2548
|
+
if (missingMcpServers.length > 0) {
|
|
2549
|
+
throw new Error(`Agent "${definition.name}" requires unavailable MCP servers: ${missingMcpServers.join(', ')}.`);
|
|
2550
|
+
}
|
|
2551
|
+
const nestedAgentDenylist = definition.allowNestedAgents === true ? [] : ['Agent', 'Task'];
|
|
1677
2552
|
return {
|
|
1678
2553
|
...options,
|
|
1679
2554
|
systemPrompt: joinPromptParts(definition.systemPrompt, options.systemPrompt),
|
|
1680
2555
|
model: options.model ?? definition.model,
|
|
2556
|
+
effort: options.effort ?? definition.effort,
|
|
2557
|
+
permissionMode: options.permissionMode ?? definition.permissionMode,
|
|
1681
2558
|
metadata: {
|
|
1682
2559
|
...(definition.metadata ?? {}),
|
|
1683
2560
|
...(options.metadata ?? {}),
|
|
@@ -1688,6 +2565,19 @@ export class ActoviqAgentClient {
|
|
|
1688
2565
|
mcpServers: [...(definition.mcpServers ?? []), ...(options.mcpServers ?? [])],
|
|
1689
2566
|
__actoviqUseDefaultTools: definition.inheritDefaultTools !== false,
|
|
1690
2567
|
__actoviqUseDefaultMcpServers: definition.inheritDefaultMcpServers !== false,
|
|
2568
|
+
__actoviqMaxToolIterations: definition.maxToolIterations ?? definition.maxTurns,
|
|
2569
|
+
__actoviqAllowedTools: definition.allowedTools
|
|
2570
|
+
? [...definition.allowedTools]
|
|
2571
|
+
: undefined,
|
|
2572
|
+
__actoviqDisallowedTools: [
|
|
2573
|
+
...(definition.disallowedTools ?? []),
|
|
2574
|
+
...nestedAgentDenylist,
|
|
2575
|
+
],
|
|
2576
|
+
__actoviqPreloadedSkills: definition.skills
|
|
2577
|
+
? [...definition.skills]
|
|
2578
|
+
: undefined,
|
|
2579
|
+
__actoviqWorkDir: options.workDir ?? definition.cwd,
|
|
2580
|
+
__actoviqInitialPrompt: definition.initialPrompt,
|
|
1691
2581
|
};
|
|
1692
2582
|
}
|
|
1693
2583
|
mergeSkillRunOptions(definition, options) {
|
|
@@ -1719,6 +2609,35 @@ export class ActoviqAgentClient {
|
|
|
1719
2609
|
export async function createAgentSdk(options = {}) {
|
|
1720
2610
|
return ActoviqAgentClient.create(options);
|
|
1721
2611
|
}
|
|
2612
|
+
function resolveTaskId(input) {
|
|
2613
|
+
return [input.task_id, input.taskId]
|
|
2614
|
+
.map(value => value?.trim())
|
|
2615
|
+
.find((value) => Boolean(value));
|
|
2616
|
+
}
|
|
2617
|
+
function serializeBackgroundTaskOutput(task) {
|
|
2618
|
+
return [
|
|
2619
|
+
`Task id: ${task.id}`,
|
|
2620
|
+
`Status: ${task.status}`,
|
|
2621
|
+
`Subagent: ${task.subagentType}`,
|
|
2622
|
+
task.agentName ? `Agent name: ${task.agentName}` : undefined,
|
|
2623
|
+
task.runId ? `Run id: ${task.runId}` : undefined,
|
|
2624
|
+
task.sessionId ? `Session id: ${task.sessionId}` : undefined,
|
|
2625
|
+
task.model ? `Model: ${task.model}` : undefined,
|
|
2626
|
+
typeof task.toolCallCount === 'number' ? `Tool calls: ${task.toolCallCount}` : undefined,
|
|
2627
|
+
typeof task.toolErrorCount === 'number' ? `Tool errors: ${task.toolErrorCount}` : undefined,
|
|
2628
|
+
typeof task.requestCount === 'number' ? `Requests: ${task.requestCount}` : undefined,
|
|
2629
|
+
task.currentToolName ? `Current tool: ${task.currentToolName}` : undefined,
|
|
2630
|
+
task.progressSummary ? `Progress: ${task.progressSummary}` : undefined,
|
|
2631
|
+
task.worktreePath ? `Worktree: ${task.worktreePath}` : undefined,
|
|
2632
|
+
task.worktreeBranch ? `Branch: ${task.worktreeBranch}` : undefined,
|
|
2633
|
+
task.error ? `Error:\n${task.error}` : undefined,
|
|
2634
|
+
task.text
|
|
2635
|
+
? `Output:\n${task.text}`
|
|
2636
|
+
: task.partialText
|
|
2637
|
+
? `Partial output:\n${task.partialText}`
|
|
2638
|
+
: 'Output: <not available yet>',
|
|
2639
|
+
].filter(Boolean).join('\n');
|
|
2640
|
+
}
|
|
1722
2641
|
function joinPromptParts(...parts) {
|
|
1723
2642
|
const normalized = parts.filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
1724
2643
|
if (normalized.length === 0) {
|
|
@@ -1726,6 +2645,99 @@ function joinPromptParts(...parts) {
|
|
|
1726
2645
|
}
|
|
1727
2646
|
return normalized.join('\n\n');
|
|
1728
2647
|
}
|
|
2648
|
+
function mergeAgentDefinitions(...groups) {
|
|
2649
|
+
const merged = new Map();
|
|
2650
|
+
for (const group of groups) {
|
|
2651
|
+
for (const definition of group) {
|
|
2652
|
+
merged.set(definition.name, cloneAgentDefinition(definition));
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
return [...merged.values()];
|
|
2656
|
+
}
|
|
2657
|
+
function filterAgentTools(tools, allowedTools, disallowedTools) {
|
|
2658
|
+
const allowed = allowedTools?.length ? new Set(allowedTools) : undefined;
|
|
2659
|
+
const denied = new Set(disallowedTools ?? []);
|
|
2660
|
+
return tools.filter(toolDefinition => {
|
|
2661
|
+
const names = [toolDefinition.name, ...(toolDefinition.aliases ?? [])];
|
|
2662
|
+
if (names.some(name => denied.has(name))) {
|
|
2663
|
+
return false;
|
|
2664
|
+
}
|
|
2665
|
+
return !allowed || names.some(name => allowed.has(name));
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
function formatTaskNotification(task) {
|
|
2669
|
+
const result = task.status === 'completed'
|
|
2670
|
+
? task.text ?? task.partialText ?? ''
|
|
2671
|
+
: task.partialText ?? '';
|
|
2672
|
+
return [
|
|
2673
|
+
'<task_notification>',
|
|
2674
|
+
`<task_id>${escapeXml(task.id)}</task_id>`,
|
|
2675
|
+
task.sessionId ? `<agent_id>${escapeXml(task.sessionId)}</agent_id>` : undefined,
|
|
2676
|
+
task.agentName ? `<agent_name>${escapeXml(task.agentName)}</agent_name>` : undefined,
|
|
2677
|
+
`<status>${task.status}</status>`,
|
|
2678
|
+
`<summary>${escapeXml(task.status === 'completed'
|
|
2679
|
+
? `Agent "${task.agentName ?? task.subagentType}" completed.`
|
|
2680
|
+
: `Agent "${task.agentName ?? task.subagentType}" ${task.status}.`)}</summary>`,
|
|
2681
|
+
result ? `<result>${escapeXml(result)}</result>` : undefined,
|
|
2682
|
+
task.error ? `<error>${escapeXml(task.error)}</error>` : undefined,
|
|
2683
|
+
`<usage><requests>${task.requestCount ?? 0}</requests><tool_uses>${task.toolCallCount ?? 0}</tool_uses><tool_errors>${task.toolErrorCount ?? 0}</tool_errors></usage>`,
|
|
2684
|
+
task.retainedWorktree && task.worktreePath
|
|
2685
|
+
? `<worktree><path>${escapeXml(task.worktreePath)}</path>${task.worktreeBranch ? `<branch>${escapeXml(task.worktreeBranch)}</branch>` : ''}</worktree>`
|
|
2686
|
+
: undefined,
|
|
2687
|
+
'</task_notification>',
|
|
2688
|
+
]
|
|
2689
|
+
.filter((line) => Boolean(line))
|
|
2690
|
+
.join('\n');
|
|
2691
|
+
}
|
|
2692
|
+
function escapeXml(value) {
|
|
2693
|
+
return value
|
|
2694
|
+
.replace(/&/gu, '&')
|
|
2695
|
+
.replace(/</gu, '<')
|
|
2696
|
+
.replace(/>/gu, '>');
|
|
2697
|
+
}
|
|
2698
|
+
function sanitizeWorkspaceName(value) {
|
|
2699
|
+
return value
|
|
2700
|
+
.trim()
|
|
2701
|
+
.replace(/[^A-Za-z0-9_-]+/gu, '-')
|
|
2702
|
+
.replace(/^-+|-+$/gu, '')
|
|
2703
|
+
.slice(0, 40) || 'agent';
|
|
2704
|
+
}
|
|
2705
|
+
async function isGitWorkspaceDirty(workDir) {
|
|
2706
|
+
try {
|
|
2707
|
+
const controller = new AbortController();
|
|
2708
|
+
const timeout = setTimeout(() => controller.abort(), 10_000);
|
|
2709
|
+
try {
|
|
2710
|
+
const { stdout } = await new Promise((resolve, reject) => {
|
|
2711
|
+
const child = execFileCallback('git', ['-C', workDir, 'status', '--porcelain'], {
|
|
2712
|
+
windowsHide: true,
|
|
2713
|
+
signal: controller.signal,
|
|
2714
|
+
});
|
|
2715
|
+
child.on('error', reject);
|
|
2716
|
+
let stdout = '';
|
|
2717
|
+
let stderr = '';
|
|
2718
|
+
child.stdout?.on('data', (data) => { stdout += data.toString(); });
|
|
2719
|
+
child.stderr?.on('data', (data) => { stderr += data.toString(); });
|
|
2720
|
+
child.on('close', (code) => {
|
|
2721
|
+
if (code === 0) {
|
|
2722
|
+
resolve({ stdout, stderr });
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
reject(new Error(`git status exited with code ${code}: ${stderr}`));
|
|
2726
|
+
}
|
|
2727
|
+
});
|
|
2728
|
+
});
|
|
2729
|
+
return stdout.trim().length > 0;
|
|
2730
|
+
}
|
|
2731
|
+
finally {
|
|
2732
|
+
clearTimeout(timeout);
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
catch {
|
|
2736
|
+
// If git is unavailable, timed out, or the repo is broken,
|
|
2737
|
+
// conservatively treat as not dirty to avoid leaking worktrees.
|
|
2738
|
+
return false;
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
1729
2741
|
function mergeUniqueByName(defaults, overrides) {
|
|
1730
2742
|
const merged = new Map();
|
|
1731
2743
|
for (const item of defaults) {
|