actoviq-agent-sdk 0.3.1 → 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 +79 -8
- 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 +1 -1
- package/dist/src/config/anthropicEnvMapping.d.ts.map +1 -1
- package/dist/src/config/anthropicEnvMapping.js +17 -8
- package/dist/src/config/anthropicEnvMapping.js.map +1 -1
- 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 +44 -16
- package/dist/src/config/resolveRuntimeConfig.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -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/parity/actoviqCleanBridgeCompatSdk.d.ts.map +1 -1
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.js +6 -2
- package/dist/src/parity/actoviqCleanBridgeCompatSdk.js.map +1 -1
- package/dist/src/provider/client.d.ts +2 -0
- package/dist/src/provider/client.d.ts.map +1 -1
- package/dist/src/provider/client.js +5 -2
- package/dist/src/provider/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 +20 -2
- package/dist/src/runtime/actoviqAgents.d.ts.map +1 -1
- package/dist/src/runtime/actoviqAgents.js +143 -28
- package/dist/src/runtime/actoviqAgents.js.map +1 -1
- package/dist/src/runtime/actoviqBackgroundTasks.d.ts +14 -1
- package/dist/src/runtime/actoviqBackgroundTasks.d.ts.map +1 -1
- package/dist/src/runtime/actoviqBackgroundTasks.js +71 -10
- package/dist/src/runtime/actoviqBackgroundTasks.js.map +1 -1
- package/dist/src/runtime/actoviqCompact.d.ts +16 -0
- package/dist/src/runtime/actoviqCompact.d.ts.map +1 -1
- package/dist/src/runtime/actoviqCompact.js +112 -13
- 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 +83 -194
- 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 +26 -3
- package/dist/src/runtime/agentClient.d.ts.map +1 -1
- package/dist/src/runtime/agentClient.js +824 -110
- 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 +81 -2
- package/dist/src/runtime/conversationEngine.js.map +1 -1
- package/dist/src/runtime/defaultActoviqAgents.d.ts.map +1 -1
- package/dist/src/runtime/defaultActoviqAgents.js +41 -0
- package/dist/src/runtime/defaultActoviqAgents.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/bash/BashTool.d.ts.map +1 -1
- package/dist/src/tools/bash/BashTool.js +43 -10
- package/dist/src/tools/bash/BashTool.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 +118 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,8 +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';
|
|
2
5
|
import { z } from 'zod';
|
|
3
6
|
import { createActoviqBuddyApi } from '../buddy/actoviqBuddy.js';
|
|
4
7
|
import { createActoviqComputerUseMcpServer, createActoviqComputerUseTools, } from '../computer/actoviqComputerUse.js';
|
|
5
8
|
import { resolveRuntimeConfig } from '../config/resolveRuntimeConfig.js';
|
|
9
|
+
import { resolveActoviqModelReference } from '../config/modelTiers.js';
|
|
6
10
|
import { mergeActoviqHooks, normalizeActoviqHookMessages, resolveActoviqPostRunHooks, resolveActoviqSessionStartHooks, } from '../hooks/actoviqHooks.js';
|
|
7
11
|
import { createActoviqMemoryApi } from '../memory/actoviqMemory.js';
|
|
8
12
|
import { appendMessagesToTranscript } from '../memory/actoviqTranscriptLogger.js';
|
|
@@ -15,12 +19,15 @@ import { SessionStore } from '../storage/sessionStore.js';
|
|
|
15
19
|
import { TeammateStore } from '../storage/teammateStore.js';
|
|
16
20
|
import { ActoviqSwarmApi } from '../swarm/actoviqSwarm.js';
|
|
17
21
|
import { createActoviqFileTools } from '../tools/actoviqFileTools.js';
|
|
22
|
+
import { createGitWorktreeWorkspace, } from '../workspace/actoviqWorkspace.js';
|
|
18
23
|
import { ActoviqAgentsApi, createActoviqTaskTool, summarizeActoviqAgentDefinition, } from './actoviqAgents.js';
|
|
19
24
|
import { getDefaultActoviqAgents } from './defaultActoviqAgents.js';
|
|
25
|
+
import { loadActoviqAgentDefinitions } from './actoviqAgentDefinitions.js';
|
|
20
26
|
import { ActoviqSkillsApi, loadActoviqSkillDefinitions, resolveActoviqSkillPrompt, summarizeActoviqSkillDefinition, } from './actoviqSkills.js';
|
|
21
27
|
import { ActoviqBackgroundTaskManager, ActoviqBackgroundTasksApi, } from './actoviqBackgroundTasks.js';
|
|
22
28
|
import { ActoviqContextApi, ActoviqSlashCommandsApi, } from './actoviqSlashCommands.js';
|
|
23
|
-
import { compactActoviqSession, getPersistedActoviqCompactHistory, getPersistedActoviqCompactState, isActoviqPromptTooLongError, trackRecentFile, trackRecentSkill, } 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';
|
|
24
31
|
const RECENT_FILE_TOOL_NAMES = new Set(['Read', 'Write', 'Edit', 'NotebookEdit']);
|
|
25
32
|
import { ActoviqToolsApi, buildActoviqCleanToolCatalog, resolveActoviqCleanToolMetadata, } from './actoviqToolCatalog.js';
|
|
26
33
|
import { WorkflowApi } from '../workflow/workflowBuilder.js';
|
|
@@ -42,6 +49,7 @@ const RELEVANT_MEMORY_MAX_SESSION_BYTES = 60 * 1024;
|
|
|
42
49
|
const DEFAULT_SESSION_MEMORY_MAX_TOKENS = 4_096;
|
|
43
50
|
const DEFAULT_DREAM_MAX_TOKENS = 4_096;
|
|
44
51
|
const MAX_REACTIVE_COMPACT_ATTEMPTS = 3;
|
|
52
|
+
const execFile = promisify(execFileCallback);
|
|
45
53
|
const SESSION_MEMORY_SYSTEM_PROMPT = `You maintain the persistent session-memory markdown file for an ongoing engineering conversation.
|
|
46
54
|
|
|
47
55
|
Return only the full updated markdown document.
|
|
@@ -76,6 +84,13 @@ function cloneAgentDefinition(definition) {
|
|
|
76
84
|
hooks: cloneHooks(definition.hooks),
|
|
77
85
|
tools: definition.tools ? [...definition.tools] : undefined,
|
|
78
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,
|
|
79
94
|
};
|
|
80
95
|
}
|
|
81
96
|
function cloneSkillDefinition(definition) {
|
|
@@ -105,6 +120,17 @@ export class AgentSessionsApi {
|
|
|
105
120
|
get(sessionId) {
|
|
106
121
|
return this.resumeSession(sessionId);
|
|
107
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
|
+
}
|
|
108
134
|
delete(sessionId) {
|
|
109
135
|
return this.store.delete(sessionId);
|
|
110
136
|
}
|
|
@@ -178,7 +204,10 @@ function getAgentContinuityState(metadata) {
|
|
|
178
204
|
lastDescription: typeof record.lastDescription === 'string' ? record.lastDescription : undefined,
|
|
179
205
|
lastRunId: typeof record.lastRunId === 'string' ? record.lastRunId : undefined,
|
|
180
206
|
lastSessionId: typeof record.lastSessionId === 'string' ? record.lastSessionId : undefined,
|
|
181
|
-
lastStatus: record.lastStatus === 'completed' ||
|
|
207
|
+
lastStatus: record.lastStatus === 'completed' ||
|
|
208
|
+
record.lastStatus === 'async_launched' ||
|
|
209
|
+
record.lastStatus === 'failed' ||
|
|
210
|
+
record.lastStatus === 'cancelled'
|
|
182
211
|
? record.lastStatus
|
|
183
212
|
: undefined,
|
|
184
213
|
lastTaskId: typeof record.lastTaskId === 'string' ? record.lastTaskId : undefined,
|
|
@@ -186,6 +215,9 @@ function getAgentContinuityState(metadata) {
|
|
|
186
215
|
runIds: readStringArray(record.runIds),
|
|
187
216
|
sessionIds: readStringArray(record.sessionIds),
|
|
188
217
|
taskIds: readStringArray(record.taskIds),
|
|
218
|
+
totalRequestCount: typeof record.totalRequestCount === 'number'
|
|
219
|
+
? record.totalRequestCount
|
|
220
|
+
: undefined,
|
|
189
221
|
totalToolCallCount: typeof record.totalToolCallCount === 'number'
|
|
190
222
|
? record.totalToolCallCount
|
|
191
223
|
: undefined,
|
|
@@ -223,6 +255,7 @@ function mergeDelegatedAgents(existing, pending) {
|
|
|
223
255
|
runIds: record.runId ? [record.runId] : undefined,
|
|
224
256
|
sessionIds: record.sessionId ? [record.sessionId] : undefined,
|
|
225
257
|
taskIds: record.taskId ? [record.taskId] : undefined,
|
|
258
|
+
totalRequestCount: record.requestCount,
|
|
226
259
|
totalToolCallCount: record.toolCallCount,
|
|
227
260
|
totalToolErrorCount: record.toolErrorCount,
|
|
228
261
|
});
|
|
@@ -239,6 +272,7 @@ function mergeDelegatedAgents(existing, pending) {
|
|
|
239
272
|
current.runIds = appendUnique(current.runIds, record.runId);
|
|
240
273
|
current.sessionIds = appendUnique(current.sessionIds, record.sessionId);
|
|
241
274
|
current.taskIds = appendUnique(current.taskIds, record.taskId);
|
|
275
|
+
current.totalRequestCount = sumOptional(current.totalRequestCount, record.requestCount);
|
|
242
276
|
current.totalToolCallCount = sumOptional(current.totalToolCallCount, record.toolCallCount);
|
|
243
277
|
current.totalToolErrorCount = sumOptional(current.totalToolErrorCount, record.toolErrorCount);
|
|
244
278
|
}
|
|
@@ -320,6 +354,8 @@ export class ActoviqAgentClient {
|
|
|
320
354
|
defaultTools;
|
|
321
355
|
defaultMcpServers;
|
|
322
356
|
hooks;
|
|
357
|
+
maxSubagentDepth;
|
|
358
|
+
maxSubagentFanout;
|
|
323
359
|
sessions;
|
|
324
360
|
agents;
|
|
325
361
|
skills;
|
|
@@ -336,13 +372,15 @@ export class ActoviqAgentClient {
|
|
|
336
372
|
agentDefinitions;
|
|
337
373
|
skillDefinitions;
|
|
338
374
|
pendingDelegations = new Map();
|
|
375
|
+
pendingRuntimeNotifications = new Map();
|
|
376
|
+
subagentInputQueues = new Map();
|
|
339
377
|
sessionRuntimeOverrides = new Map();
|
|
340
378
|
backgroundTaskManager;
|
|
341
379
|
defaultPermissionMode;
|
|
342
380
|
defaultPermissions;
|
|
343
381
|
defaultClassifier;
|
|
344
382
|
defaultApprover;
|
|
345
|
-
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) {
|
|
346
384
|
this.config = config;
|
|
347
385
|
this.store = store;
|
|
348
386
|
this.backgroundTaskStore = backgroundTaskStore;
|
|
@@ -353,8 +391,10 @@ export class ActoviqAgentClient {
|
|
|
353
391
|
this.defaultTools = defaultTools;
|
|
354
392
|
this.defaultMcpServers = defaultMcpServers;
|
|
355
393
|
this.hooks = hooks;
|
|
394
|
+
this.maxSubagentDepth = maxSubagentDepth;
|
|
395
|
+
this.maxSubagentFanout = maxSubagentFanout;
|
|
356
396
|
this.sessionManager = new SessionManager(this.store, sessionManagerConfig);
|
|
357
|
-
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);
|
|
358
398
|
this.agentDefinitions = new Map(agentDefinitions.map(definition => [definition.name, cloneAgentDefinition(definition)]));
|
|
359
399
|
this.skillDefinitions = new Map(skillDefinitions.map(definition => [definition.name, cloneSkillDefinition(definition)]));
|
|
360
400
|
this.backgroundTaskManager = new ActoviqBackgroundTaskManager(this.backgroundTaskStore);
|
|
@@ -419,9 +459,16 @@ export class ActoviqAgentClient {
|
|
|
419
459
|
resumeSession: (sessionId) => this.resumeSession(sessionId),
|
|
420
460
|
getBackgroundTask: (taskId) => this.backgroundTaskManager.get(taskId),
|
|
421
461
|
}, this.teammateStore, this.mailboxStore);
|
|
422
|
-
|
|
462
|
+
const existingDelegationTool = this.defaultTools.find(tool => tool.name === 'Agent' || tool.name === 'Task');
|
|
463
|
+
if (!existingDelegationTool) {
|
|
423
464
|
this.defaultTools.unshift(this.createTaskTool());
|
|
424
465
|
}
|
|
466
|
+
else if (existingDelegationTool.name === 'Task') {
|
|
467
|
+
existingDelegationTool.aliases = [
|
|
468
|
+
...new Set([...(existingDelegationTool.aliases ?? []), 'Agent']),
|
|
469
|
+
];
|
|
470
|
+
}
|
|
471
|
+
this.replaceDefaultTool(this.createSendMessageTool());
|
|
425
472
|
this.replaceDefaultTool(this.createBackgroundTaskListTool());
|
|
426
473
|
this.replaceDefaultTool(this.createBackgroundTaskGetTool());
|
|
427
474
|
this.replaceDefaultTool(this.createBackgroundTaskStopTool());
|
|
@@ -443,7 +490,7 @@ export class ActoviqAgentClient {
|
|
|
443
490
|
}
|
|
444
491
|
/** Resolve a tool definition by name from the default tool registry. */
|
|
445
492
|
getTool(name) {
|
|
446
|
-
return this.defaultTools.find(t => t.name === name);
|
|
493
|
+
return this.defaultTools.find(t => t.name === name || t.aliases?.includes(name));
|
|
447
494
|
}
|
|
448
495
|
async getToolCatalog(options) {
|
|
449
496
|
return buildActoviqCleanToolCatalog(await this.listToolMetadata(options));
|
|
@@ -489,9 +536,13 @@ export class ActoviqAgentClient {
|
|
|
489
536
|
disableDefaultSkills: options.disableDefaultSkills,
|
|
490
537
|
loadDefaultSkillDirectories: options.loadDefaultSkillDirectories,
|
|
491
538
|
});
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
:
|
|
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 ?? []);
|
|
495
546
|
const defaultTools = [...(options.tools ?? [])];
|
|
496
547
|
const defaultMcpServers = [...(options.mcpServers ?? [])];
|
|
497
548
|
if (options.computerUse) {
|
|
@@ -503,7 +554,9 @@ export class ActoviqAgentClient {
|
|
|
503
554
|
defaultTools.push(...createActoviqComputerUseTools(computerUseOptions));
|
|
504
555
|
}
|
|
505
556
|
}
|
|
506
|
-
|
|
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;
|
|
507
560
|
}
|
|
508
561
|
async run(input, options = {}) {
|
|
509
562
|
const runId = createId();
|
|
@@ -564,24 +617,74 @@ export class ActoviqAgentClient {
|
|
|
564
617
|
return race(tasks, options);
|
|
565
618
|
}
|
|
566
619
|
async createSession(options = {}) {
|
|
620
|
+
const model = this.resolveModel(options.model);
|
|
567
621
|
const stored = await this.store.create({
|
|
568
622
|
id: options.id,
|
|
569
623
|
title: options.title,
|
|
570
624
|
systemPrompt: options.systemPrompt ?? this.config.systemPrompt,
|
|
571
|
-
model
|
|
625
|
+
model,
|
|
572
626
|
tags: options.tags,
|
|
573
627
|
metadata: {
|
|
574
|
-
__actoviqWorkDir: this.config.workDir,
|
|
575
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
|
+
: {}),
|
|
576
638
|
},
|
|
577
639
|
initialMessages: options.initialMessages,
|
|
578
640
|
});
|
|
579
641
|
return this.hydrateSession(stored);
|
|
580
642
|
}
|
|
581
|
-
async resumeSession(sessionId) {
|
|
582
|
-
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);
|
|
583
681
|
return this.hydrateSession(stored);
|
|
584
682
|
}
|
|
683
|
+
resolveModel(model) {
|
|
684
|
+
return model
|
|
685
|
+
? resolveActoviqModelReference(model, this.config.modelTiers).model
|
|
686
|
+
: this.config.model;
|
|
687
|
+
}
|
|
585
688
|
async compactSessionById(sessionId, options = {}) {
|
|
586
689
|
const session = await this.resumeSession(sessionId);
|
|
587
690
|
return this.compactSessionForSession(session, options);
|
|
@@ -603,8 +706,28 @@ export class ActoviqAgentClient {
|
|
|
603
706
|
return this.dream.maybeAutoDream(options);
|
|
604
707
|
}
|
|
605
708
|
async close() {
|
|
606
|
-
|
|
607
|
-
|
|
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
|
+
}
|
|
608
731
|
}
|
|
609
732
|
listAgentDefinitions() {
|
|
610
733
|
return [...this.agentDefinitions.values()].map(summarizeActoviqAgentDefinition);
|
|
@@ -628,6 +751,8 @@ export class ActoviqAgentClient {
|
|
|
628
751
|
...(definition.metadata ?? {}),
|
|
629
752
|
...(options.metadata ?? {}),
|
|
630
753
|
__actoviqAgentDefinition: definition.name,
|
|
754
|
+
__actoviqAgentMemory: definition.memory,
|
|
755
|
+
__actoviqAgentSource: definition.source,
|
|
631
756
|
[AGENT_CONTINUITY_STATE_KEY]: {
|
|
632
757
|
currentAgent: definition.name,
|
|
633
758
|
delegatedAgents: [],
|
|
@@ -665,8 +790,10 @@ export class ActoviqAgentClient {
|
|
|
665
790
|
...options,
|
|
666
791
|
listAgentDefinitions: () => this.listAgentDefinitions(),
|
|
667
792
|
getAgentDefinition: (agent) => this.getAgentDefinition(agent),
|
|
668
|
-
runAgent: (agent, prompt, runOptions) => this.
|
|
669
|
-
|
|
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, }) => {
|
|
670
797
|
this.recordPendingDelegation(parentSessionId ?? parentRunId, {
|
|
671
798
|
name: subagentType,
|
|
672
799
|
description,
|
|
@@ -675,12 +802,13 @@ export class ActoviqAgentClient {
|
|
|
675
802
|
sessionId,
|
|
676
803
|
status,
|
|
677
804
|
taskId,
|
|
805
|
+
requestCount,
|
|
678
806
|
toolCallCount,
|
|
679
807
|
toolErrorCount,
|
|
680
808
|
textSummary,
|
|
681
809
|
});
|
|
682
810
|
},
|
|
683
|
-
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),
|
|
684
812
|
});
|
|
685
813
|
}
|
|
686
814
|
replaceDefaultTool(replacement) {
|
|
@@ -691,6 +819,39 @@ export class ActoviqAgentClient {
|
|
|
691
819
|
}
|
|
692
820
|
this.defaultTools.push(replacement);
|
|
693
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
|
+
}
|
|
694
855
|
/**
|
|
695
856
|
* Model-facing Skill tool backed by the real skill registry. Loads the
|
|
696
857
|
* resolved skill content into the conversation as the tool result
|
|
@@ -897,12 +1058,30 @@ export class ActoviqAgentClient {
|
|
|
897
1058
|
return session.send(execution.prompt, execution.options);
|
|
898
1059
|
}
|
|
899
1060
|
async forwardStreamResult(stream, emit) {
|
|
1061
|
+
const errors = [];
|
|
900
1062
|
const pump = (async () => {
|
|
901
|
-
|
|
902
|
-
|
|
1063
|
+
try {
|
|
1064
|
+
for await (const event of stream) {
|
|
1065
|
+
emit(event);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
catch (error) {
|
|
1069
|
+
errors.push(error);
|
|
903
1070
|
}
|
|
904
1071
|
})();
|
|
905
|
-
|
|
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
|
+
}
|
|
906
1085
|
return result;
|
|
907
1086
|
}
|
|
908
1087
|
getSessionRuntimeOverrides(sessionId) {
|
|
@@ -949,14 +1128,19 @@ export class ActoviqAgentClient {
|
|
|
949
1128
|
}
|
|
950
1129
|
this.sessionRuntimeOverrides.set(sessionId, next);
|
|
951
1130
|
}
|
|
952
|
-
setSessionRuntimePermissionContext(
|
|
1131
|
+
async setSessionRuntimePermissionContext(session, context) {
|
|
1132
|
+
const sessionId = session.id;
|
|
953
1133
|
const current = this.sessionRuntimeOverrides.get(sessionId) ?? {};
|
|
1134
|
+
const stored = session.snapshot();
|
|
1135
|
+
const persisted = getPersistedActoviqSessionPermissionState(stored.metadata);
|
|
954
1136
|
const next = {
|
|
955
1137
|
...current,
|
|
956
|
-
permissionMode: context.mode,
|
|
957
|
-
permissions:
|
|
958
|
-
|
|
959
|
-
|
|
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,
|
|
960
1144
|
};
|
|
961
1145
|
if (isHooksEmpty(next.hooks) &&
|
|
962
1146
|
!next.permissionMode &&
|
|
@@ -964,27 +1148,42 @@ export class ActoviqAgentClient {
|
|
|
964
1148
|
!next.classifier &&
|
|
965
1149
|
!next.approver) {
|
|
966
1150
|
this.sessionRuntimeOverrides.delete(sessionId);
|
|
967
|
-
return;
|
|
968
1151
|
}
|
|
969
|
-
|
|
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;
|
|
970
1163
|
}
|
|
971
|
-
clearSessionRuntimePermissionContext(
|
|
1164
|
+
async clearSessionRuntimePermissionContext(session) {
|
|
1165
|
+
const sessionId = session.id;
|
|
972
1166
|
const current = this.sessionRuntimeOverrides.get(sessionId);
|
|
973
|
-
if (
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
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
|
+
}
|
|
986
1181
|
}
|
|
987
|
-
|
|
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;
|
|
988
1187
|
}
|
|
989
1188
|
applySessionRuntimeOverrides(sessionId, options) {
|
|
990
1189
|
const overrides = this.getSessionRuntimeOverrides(sessionId);
|
|
@@ -1001,6 +1200,16 @@ export class ActoviqAgentClient {
|
|
|
1001
1200
|
};
|
|
1002
1201
|
}
|
|
1003
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
|
+
}
|
|
1004
1213
|
return new AgentSession({
|
|
1005
1214
|
runSession: (session, input, options) => this.runOnSession(session, input, options),
|
|
1006
1215
|
streamSession: (session, input, options) => this.streamOnSession(session, input, options),
|
|
@@ -1021,8 +1230,9 @@ export class ActoviqAgentClient {
|
|
|
1021
1230
|
getAgentContinuity: (session) => this.getAgentContinuityForSession(session),
|
|
1022
1231
|
setRuntimeHooks: (session, hooks) => this.setSessionRuntimeHooks(session.id, hooks),
|
|
1023
1232
|
clearRuntimeHooks: (session) => this.clearSessionRuntimeHooks(session.id),
|
|
1024
|
-
|
|
1025
|
-
|
|
1233
|
+
setModel: (session, model) => this.setSessionModel(session, model),
|
|
1234
|
+
setRuntimePermissionContext: (session, context) => this.setSessionRuntimePermissionContext(session, context),
|
|
1235
|
+
clearRuntimePermissionContext: (session) => this.clearSessionRuntimePermissionContext(session),
|
|
1026
1236
|
hydrate: (next) => this.hydrateSession(next),
|
|
1027
1237
|
saveCheckpoint: (_session, label) => this.store.saveCheckpoint(stored.id, label),
|
|
1028
1238
|
restoreCheckpoint: (session, checkpointId) => this.restoreCheckpointToSession(session, checkpointId),
|
|
@@ -1030,6 +1240,13 @@ export class ActoviqAgentClient {
|
|
|
1030
1240
|
deleteCheckpoint: (_session, checkpointId) => this.store.deleteCheckpoint(stored.id, checkpointId),
|
|
1031
1241
|
}, this.store, stored);
|
|
1032
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
|
+
}
|
|
1033
1250
|
async restoreCheckpointToSession(session, checkpointId) {
|
|
1034
1251
|
const checkpoint = await this.store.loadCheckpoint(session.id, checkpointId);
|
|
1035
1252
|
session.replace(checkpoint.snapshot);
|
|
@@ -1132,19 +1349,30 @@ export class ActoviqAgentClient {
|
|
|
1132
1349
|
...(augmentations?.metadata ?? {}),
|
|
1133
1350
|
...(options.metadata ?? {}),
|
|
1134
1351
|
};
|
|
1135
|
-
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);
|
|
1136
1354
|
// Collect tool prompts for system prompt assembly
|
|
1137
1355
|
const toolPromptParts = await collectToolPrompts(mergedTools, {
|
|
1138
|
-
workDir
|
|
1356
|
+
workDir,
|
|
1139
1357
|
permissionMode: options.permissionMode ?? this.defaultPermissionMode,
|
|
1140
1358
|
});
|
|
1141
1359
|
const systemPrompt = await this.resolveSystemPrompt(options, session, [...(augmentations?.systemPromptParts ?? []), ...toolPromptParts]);
|
|
1142
|
-
const runtimeConfig = options.__actoviqMaxToolIterations
|
|
1360
|
+
const runtimeConfig = options.__actoviqMaxToolIterations || workDir !== this.config.workDir
|
|
1143
1361
|
? {
|
|
1144
1362
|
...this.config,
|
|
1145
|
-
|
|
1363
|
+
workDir,
|
|
1364
|
+
...(options.__actoviqMaxToolIterations
|
|
1365
|
+
? { maxToolIterations: options.__actoviqMaxToolIterations }
|
|
1366
|
+
: {}),
|
|
1146
1367
|
}
|
|
1147
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;
|
|
1148
1376
|
return executeConversation({
|
|
1149
1377
|
runId,
|
|
1150
1378
|
input,
|
|
@@ -1154,9 +1382,10 @@ export class ActoviqAgentClient {
|
|
|
1154
1382
|
systemPrompt,
|
|
1155
1383
|
tools: mergedTools,
|
|
1156
1384
|
mcpServers: mergeUniqueByName(options.__actoviqUseDefaultMcpServers === false ? [] : this.defaultMcpServers, options.mcpServers ?? []),
|
|
1157
|
-
model: options.model ?? session?.model
|
|
1385
|
+
model: this.resolveModel(options.model ?? session?.model),
|
|
1158
1386
|
maxTokens: options.maxTokens,
|
|
1159
1387
|
temperature: options.temperature,
|
|
1388
|
+
effort: options.effort,
|
|
1160
1389
|
toolChoice: options.toolChoice,
|
|
1161
1390
|
userId: options.userId ?? this.config.userId,
|
|
1162
1391
|
metadata,
|
|
@@ -1167,6 +1396,7 @@ export class ActoviqAgentClient {
|
|
|
1167
1396
|
approver: options.approver ?? this.defaultApprover,
|
|
1168
1397
|
canUseTool: options.canUseTool,
|
|
1169
1398
|
hooks: augmentations?.hooks,
|
|
1399
|
+
drainQueuedInputs,
|
|
1170
1400
|
streaming,
|
|
1171
1401
|
emit,
|
|
1172
1402
|
skipRunStartedEvent,
|
|
@@ -1231,11 +1461,32 @@ export class ActoviqAgentClient {
|
|
|
1231
1461
|
return promptParts.join('\n\n');
|
|
1232
1462
|
}
|
|
1233
1463
|
async prepareRunAugmentations(runId, input, options, session) {
|
|
1464
|
+
const internalOptions = options;
|
|
1234
1465
|
const promptText = typeof input === 'string' ? input : extractTextFromContent(input);
|
|
1235
1466
|
const memoryContext = await this.prepareRelevantMemoryContext(input, session);
|
|
1236
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);
|
|
1237
1484
|
const hooks = mergeActoviqHooks(this.hooks, options.hooks);
|
|
1238
1485
|
const prefixedMessages = [
|
|
1486
|
+
...notificationMessages,
|
|
1487
|
+
...initialPromptMessages,
|
|
1488
|
+
...agentMemoryMessages,
|
|
1489
|
+
...preloadedSkillMessages,
|
|
1239
1490
|
...invokedSkillContext.prefixedMessages,
|
|
1240
1491
|
...memoryContext.prefixedMessages,
|
|
1241
1492
|
];
|
|
@@ -1248,7 +1499,7 @@ export class ActoviqAgentClient {
|
|
|
1248
1499
|
promptText,
|
|
1249
1500
|
sessionId: session?.id,
|
|
1250
1501
|
session: session ? deepClone(session) : undefined,
|
|
1251
|
-
workDir: this.config.workDir,
|
|
1502
|
+
workDir: internalOptions.__actoviqWorkDir ?? internalOptions.workDir ?? this.config.workDir,
|
|
1252
1503
|
options,
|
|
1253
1504
|
});
|
|
1254
1505
|
if (!result) {
|
|
@@ -1271,6 +1522,70 @@ export class ActoviqAgentClient {
|
|
|
1271
1522
|
metadata,
|
|
1272
1523
|
};
|
|
1273
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
|
+
}
|
|
1274
1589
|
async applyPostRunHooks(runId, input, options, result, session) {
|
|
1275
1590
|
const promptText = typeof input === 'string' ? input : extractTextFromContent(input);
|
|
1276
1591
|
const hooks = mergeActoviqHooks(this.hooks, options.hooks);
|
|
@@ -1283,7 +1598,9 @@ export class ActoviqAgentClient {
|
|
|
1283
1598
|
promptText,
|
|
1284
1599
|
sessionId: session?.id,
|
|
1285
1600
|
session: session ? deepClone(session) : undefined,
|
|
1286
|
-
workDir:
|
|
1601
|
+
workDir: options.__actoviqWorkDir ??
|
|
1602
|
+
options.workDir ??
|
|
1603
|
+
this.config.workDir,
|
|
1287
1604
|
options,
|
|
1288
1605
|
result,
|
|
1289
1606
|
});
|
|
@@ -1309,6 +1626,27 @@ export class ActoviqAgentClient {
|
|
|
1309
1626
|
existing.push(record);
|
|
1310
1627
|
this.pendingDelegations.set(key, existing);
|
|
1311
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
|
+
}
|
|
1312
1650
|
consumePendingDelegations(key) {
|
|
1313
1651
|
if (!key) {
|
|
1314
1652
|
return [];
|
|
@@ -1409,6 +1747,9 @@ export class ActoviqAgentClient {
|
|
|
1409
1747
|
latestBoundary,
|
|
1410
1748
|
compactCount: Math.max(compactState.compactCount, persistedCompactState.compactCount),
|
|
1411
1749
|
microcompactCount: Math.max(compactState.microcompactCount, persistedCompactState.microcompactCount),
|
|
1750
|
+
consecutiveCompactFailures: persistedCompactState.consecutiveFailures,
|
|
1751
|
+
lastCompactFailureAt: persistedCompactState.lastFailureAt,
|
|
1752
|
+
lastCompactError: persistedCompactState.lastError,
|
|
1412
1753
|
hasCompacted: compactState.hasCompacted ||
|
|
1413
1754
|
persistedCompactState.compactCount + persistedCompactState.microcompactCount > 0,
|
|
1414
1755
|
summaryMessage: compactState.summaryMessage ?? persistedCompactState.lastSummaryMessage,
|
|
@@ -1437,12 +1778,16 @@ export class ActoviqAgentClient {
|
|
|
1437
1778
|
}, {
|
|
1438
1779
|
workDir: this.config.workDir,
|
|
1439
1780
|
systemPrompt: snapshot.systemPrompt ?? this.config.systemPrompt,
|
|
1440
|
-
model: options.model ?? snapshot.model
|
|
1781
|
+
model: this.resolveModel(options.model ?? snapshot.model),
|
|
1441
1782
|
modelApi: this.modelApi,
|
|
1442
1783
|
compactConfig: this.config.compact,
|
|
1443
1784
|
runtimeState: this.getSessionMemoryRuntimeState(snapshot),
|
|
1444
1785
|
});
|
|
1445
|
-
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
|
+
}
|
|
1446
1791
|
return undefined;
|
|
1447
1792
|
}
|
|
1448
1793
|
await this.store.save(reactive.session);
|
|
@@ -1460,49 +1805,239 @@ export class ActoviqAgentClient {
|
|
|
1460
1805
|
result: reactive.result,
|
|
1461
1806
|
};
|
|
1462
1807
|
}
|
|
1463
|
-
async
|
|
1808
|
+
async runDelegatedAgentTask(agent, prompt, runOptions = {}, delegation = { description: prompt }) {
|
|
1464
1809
|
const definition = this.requireAgentDefinition(agent);
|
|
1465
|
-
const
|
|
1466
|
-
|
|
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,
|
|
1467
1900
|
metadata: {
|
|
1468
|
-
|
|
1469
|
-
__actoviqBackgroundParentSessionId: options.parentSessionId,
|
|
1901
|
+
agent: definition.name,
|
|
1470
1902
|
},
|
|
1471
1903
|
});
|
|
1472
|
-
|
|
1473
|
-
|
|
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;
|
|
1923
|
+
}
|
|
1924
|
+
const dirty = await isGitWorkspaceDirty(workspace.path);
|
|
1925
|
+
if (!dirty) {
|
|
1926
|
+
await workspace.dispose();
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
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);
|
|
1474
1949
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
permissions: runOptions.permissions,
|
|
1482
|
-
classifier: runOptions.classifier,
|
|
1483
|
-
approver: runOptions.approver,
|
|
1484
|
-
});
|
|
1950
|
+
return notifications;
|
|
1951
|
+
}
|
|
1952
|
+
enqueueTaskNotification(task) {
|
|
1953
|
+
const notificationKey = task.parentSessionId ?? task.parentRunId;
|
|
1954
|
+
if (!notificationKey || task.notificationDeliveredAt) {
|
|
1955
|
+
return;
|
|
1485
1956
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
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(),
|
|
1503
1980
|
});
|
|
1504
1981
|
}
|
|
1505
|
-
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) {
|
|
1506
2041
|
const definition = this.requireAgentDefinition(agent);
|
|
1507
2042
|
if (runOptions.hooks) {
|
|
1508
2043
|
session.setHooks(runOptions.hooks);
|
|
@@ -1511,7 +2046,7 @@ export class ActoviqAgentClient {
|
|
|
1511
2046
|
runOptions.permissions ||
|
|
1512
2047
|
runOptions.classifier ||
|
|
1513
2048
|
runOptions.approver) {
|
|
1514
|
-
session.setPermissionContext({
|
|
2049
|
+
await session.setPermissionContext({
|
|
1515
2050
|
mode: runOptions.permissionMode,
|
|
1516
2051
|
permissions: runOptions.permissions,
|
|
1517
2052
|
classifier: runOptions.classifier,
|
|
@@ -1520,33 +2055,82 @@ export class ActoviqAgentClient {
|
|
|
1520
2055
|
}
|
|
1521
2056
|
return this.backgroundTaskManager.launch({
|
|
1522
2057
|
subagentType: definition.name,
|
|
1523
|
-
description:
|
|
1524
|
-
workDir: this.config.workDir,
|
|
2058
|
+
description: delegation.description,
|
|
2059
|
+
workDir: delegation.cwd ?? this.config.workDir,
|
|
1525
2060
|
parentRunId: options.parentRunId,
|
|
1526
2061
|
parentSessionId: options.parentSessionId ?? session.id,
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
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);
|
|
1537
2075
|
},
|
|
1538
2076
|
});
|
|
1539
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
|
+
}
|
|
1540
2123
|
async compactSessionForSession(session, options = {}) {
|
|
1541
2124
|
const snapshot = session.snapshot();
|
|
1542
2125
|
const { session: compactedSession, result } = await compactActoviqSession(snapshot, {
|
|
1543
2126
|
...options,
|
|
2127
|
+
model: options.model ? this.resolveModel(options.model) : undefined,
|
|
1544
2128
|
force: options.force ?? true,
|
|
1545
2129
|
trigger: 'manual',
|
|
1546
2130
|
}, {
|
|
1547
2131
|
workDir: this.config.workDir,
|
|
1548
2132
|
systemPrompt: snapshot.systemPrompt ?? this.config.systemPrompt,
|
|
1549
|
-
model:
|
|
2133
|
+
model: this.resolveModel(snapshot.model),
|
|
1550
2134
|
modelApi: this.modelApi,
|
|
1551
2135
|
compactConfig: this.config.compact,
|
|
1552
2136
|
runtimeState: this.getSessionMemoryRuntimeState(snapshot),
|
|
@@ -1567,7 +2151,7 @@ export class ActoviqAgentClient {
|
|
|
1567
2151
|
systemPrompt: await this.buildDreamSystemPrompt(),
|
|
1568
2152
|
tools: createActoviqFileTools({ cwd: this.config.workDir }),
|
|
1569
2153
|
mcpServers: [],
|
|
1570
|
-
model:
|
|
2154
|
+
model: this.resolveModel(request.model),
|
|
1571
2155
|
maxTokens: request.maxTokens ?? DEFAULT_DREAM_MAX_TOKENS,
|
|
1572
2156
|
userId: this.config.userId,
|
|
1573
2157
|
metadata: {
|
|
@@ -1781,7 +2365,7 @@ export class ActoviqAgentClient {
|
|
|
1781
2365
|
const stored = session.snapshot();
|
|
1782
2366
|
const extraction = await this.performSessionMemoryExtraction(stored, {
|
|
1783
2367
|
force: options.force ?? true,
|
|
1784
|
-
model: options.model ?? stored.model
|
|
2368
|
+
model: this.resolveModel(options.model ?? stored.model),
|
|
1785
2369
|
systemPrompt: stored.systemPrompt ?? this.config.systemPrompt,
|
|
1786
2370
|
trigger: 'manual',
|
|
1787
2371
|
maxTokens: options.maxTokens,
|
|
@@ -1803,6 +2387,9 @@ export class ActoviqAgentClient {
|
|
|
1803
2387
|
...(options.metadata ?? {}),
|
|
1804
2388
|
...(hookOutcome.sessionMetadata ?? {}),
|
|
1805
2389
|
};
|
|
2390
|
+
if (result.loopCompactions?.length) {
|
|
2391
|
+
recordActoviqLoopCompactionsOnSession(next, result.loopCompactions);
|
|
2392
|
+
}
|
|
1806
2393
|
const runtimeState = this.getSessionMemoryRuntimeState(next);
|
|
1807
2394
|
if (runtimeState.pendingPostCompaction) {
|
|
1808
2395
|
runtimeState.pendingPostCompaction = false;
|
|
@@ -1896,7 +2483,7 @@ export class ActoviqAgentClient {
|
|
|
1896
2483
|
await appendMessagesToTranscript(paths.projectStateDir, session.id, this.config.workDir, newMessages);
|
|
1897
2484
|
}
|
|
1898
2485
|
const extraction = await this.performSessionMemoryExtraction(next, {
|
|
1899
|
-
model: options.model ?? next.model
|
|
2486
|
+
model: this.resolveModel(options.model ?? next.model),
|
|
1900
2487
|
systemPrompt: next.systemPrompt ?? this.config.systemPrompt,
|
|
1901
2488
|
trigger: 'auto',
|
|
1902
2489
|
maxTokens: Math.min(options.maxTokens ?? this.config.maxTokens, DEFAULT_SESSION_MEMORY_MAX_TOKENS),
|
|
@@ -1906,7 +2493,7 @@ export class ActoviqAgentClient {
|
|
|
1906
2493
|
const compacted = await compactActoviqSession(next, { trigger: 'auto' }, {
|
|
1907
2494
|
workDir: this.config.workDir,
|
|
1908
2495
|
systemPrompt: next.systemPrompt ?? this.config.systemPrompt,
|
|
1909
|
-
model: options.model ?? next.model
|
|
2496
|
+
model: this.resolveModel(options.model ?? next.model),
|
|
1910
2497
|
modelApi: this.modelApi,
|
|
1911
2498
|
compactConfig: this.config.compact,
|
|
1912
2499
|
runtimeState: extraction.state,
|
|
@@ -1952,10 +2539,22 @@ export class ActoviqAgentClient {
|
|
|
1952
2539
|
return this.mergeAgentRunOptions(this.requireAgentDefinition(agentName), options);
|
|
1953
2540
|
}
|
|
1954
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'];
|
|
1955
2552
|
return {
|
|
1956
2553
|
...options,
|
|
1957
2554
|
systemPrompt: joinPromptParts(definition.systemPrompt, options.systemPrompt),
|
|
1958
2555
|
model: options.model ?? definition.model,
|
|
2556
|
+
effort: options.effort ?? definition.effort,
|
|
2557
|
+
permissionMode: options.permissionMode ?? definition.permissionMode,
|
|
1959
2558
|
metadata: {
|
|
1960
2559
|
...(definition.metadata ?? {}),
|
|
1961
2560
|
...(options.metadata ?? {}),
|
|
@@ -1966,7 +2565,19 @@ export class ActoviqAgentClient {
|
|
|
1966
2565
|
mcpServers: [...(definition.mcpServers ?? []), ...(options.mcpServers ?? [])],
|
|
1967
2566
|
__actoviqUseDefaultTools: definition.inheritDefaultTools !== false,
|
|
1968
2567
|
__actoviqUseDefaultMcpServers: definition.inheritDefaultMcpServers !== false,
|
|
1969
|
-
__actoviqMaxToolIterations: definition.maxToolIterations,
|
|
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,
|
|
1970
2581
|
};
|
|
1971
2582
|
}
|
|
1972
2583
|
mergeSkillRunOptions(definition, options) {
|
|
@@ -2008,13 +2619,23 @@ function serializeBackgroundTaskOutput(task) {
|
|
|
2008
2619
|
`Task id: ${task.id}`,
|
|
2009
2620
|
`Status: ${task.status}`,
|
|
2010
2621
|
`Subagent: ${task.subagentType}`,
|
|
2622
|
+
task.agentName ? `Agent name: ${task.agentName}` : undefined,
|
|
2011
2623
|
task.runId ? `Run id: ${task.runId}` : undefined,
|
|
2012
2624
|
task.sessionId ? `Session id: ${task.sessionId}` : undefined,
|
|
2013
2625
|
task.model ? `Model: ${task.model}` : undefined,
|
|
2014
2626
|
typeof task.toolCallCount === 'number' ? `Tool calls: ${task.toolCallCount}` : undefined,
|
|
2015
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,
|
|
2016
2633
|
task.error ? `Error:\n${task.error}` : undefined,
|
|
2017
|
-
task.text
|
|
2634
|
+
task.text
|
|
2635
|
+
? `Output:\n${task.text}`
|
|
2636
|
+
: task.partialText
|
|
2637
|
+
? `Partial output:\n${task.partialText}`
|
|
2638
|
+
: 'Output: <not available yet>',
|
|
2018
2639
|
].filter(Boolean).join('\n');
|
|
2019
2640
|
}
|
|
2020
2641
|
function joinPromptParts(...parts) {
|
|
@@ -2024,6 +2645,99 @@ function joinPromptParts(...parts) {
|
|
|
2024
2645
|
}
|
|
2025
2646
|
return normalized.join('\n\n');
|
|
2026
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
|
+
}
|
|
2027
2741
|
function mergeUniqueByName(defaults, overrides) {
|
|
2028
2742
|
const merged = new Map();
|
|
2029
2743
|
for (const item of defaults) {
|