@borgee/agents-host 0.2.44 → 0.2.62
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.md +24 -27
- package/dist/agents-host.d.ts +27 -5
- package/dist/agents-host.js +266 -201
- package/dist/chat/chat-control-plane.d.ts +3 -0
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
- package/dist/chat/sdk-chat-control-plane.js +3 -0
- package/dist/cli.js +1 -5
- package/dist/compatibility-gates.d.ts +4 -0
- package/dist/compatibility-gates.js +20 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +21 -0
- package/dist/context/claude-file-brief.d.ts +2 -0
- package/dist/context/claude-file-brief.js +83 -0
- package/dist/context/compaction.d.ts +20 -0
- package/dist/context/compaction.js +59 -0
- package/dist/context/injection.d.ts +58 -7
- package/dist/context/injection.js +370 -36
- package/dist/context/main-session-delegation.d.ts +1 -1
- package/dist/context/projection-strategy.d.ts +24 -0
- package/dist/context/projection-strategy.js +90 -0
- package/dist/context/prompt.d.ts +16 -1
- package/dist/context/prompt.js +464 -26
- package/dist/context/resolved-workspace.d.ts +3 -0
- package/dist/context/resolved-workspace.js +106 -0
- package/dist/context/skill-manual.d.ts +1 -0
- package/dist/context/skill-manual.js +4 -1
- package/dist/context/turn-preparation.d.ts +8 -2
- package/dist/context/turn-preparation.js +64 -14
- package/dist/gateway/localhost-gateway.js +4 -5
- package/dist/local-config.js +11 -1
- package/dist/managed-daemon.js +127 -9
- package/dist/plugin-sdk.js +264 -364
- package/dist/plugin-sdk.js.map +4 -4
- package/dist/policy/copilot-permission.d.ts +1 -0
- package/dist/policy/copilot-permission.js +18 -0
- package/dist/policy/gateway-authorization.js +2 -2
- package/dist/progress-to-activity.d.ts +16 -0
- package/dist/progress-to-activity.js +24 -0
- package/dist/projection-strategy-values.d.ts +4 -0
- package/dist/projection-strategy-values.js +28 -0
- package/dist/providers/acp-progress-collector.d.ts +44 -0
- package/dist/providers/acp-progress-collector.js +130 -0
- package/dist/providers/awaiting-user.d.ts +2 -3
- package/dist/providers/awaiting-user.js +5 -7
- package/dist/providers/claude/activity-metadata.d.ts +14 -0
- package/dist/providers/claude/activity-metadata.js +81 -0
- package/dist/providers/claude/cli-client.d.ts +2 -3
- package/dist/providers/claude/cli-client.js +220 -120
- package/dist/providers/codex/cli-client.d.ts +2 -0
- package/dist/providers/codex/cli-client.js +28 -104
- package/dist/providers/codex/project-doc.js +12 -11
- package/dist/providers/copilot/cli-client.d.ts +1 -1
- package/dist/providers/copilot/cli-client.js +12 -86
- package/dist/providers/create-provider.d.ts +2 -0
- package/dist/providers/create-provider.js +22 -4
- package/dist/state-paths.d.ts +1 -1
- package/dist/state-paths.js +3 -3
- package/dist/task-thread-resolution.d.ts +3 -2
- package/dist/types.d.ts +143 -6
- package/package.json +2 -2
- package/skills/borgee-agent/SKILL.md +12 -4
- package/skills/borgee-agent/references/errors.md +2 -2
- package/skills/borgee-agent/references/task-properties.md +6 -2
- package/dist/durable-cursor-store.d.ts +0 -5
- package/dist/durable-cursor-store.js +0 -7
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
1
|
+
import { constants as fsConstants, promises as fs } from 'node:fs';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
6
|
+
import { findTaskForThread } from '../task-thread-resolution.js';
|
|
7
|
+
import { buildClaudeFileBrief } from './claude-file-brief.js';
|
|
5
8
|
const CHANNEL_CONTEXT_ROOT_DIRNAME = 'channel-context';
|
|
6
9
|
const CHANNEL_CONTEXT_PAYLOAD_FILENAME = 'context.json';
|
|
10
|
+
const TASK_ASSIGNMENT_STATE_ROOT_DIRNAME = 'task-assignment-state';
|
|
11
|
+
const TASK_ASSIGNMENT_STATE_FILENAME = 'context.json';
|
|
7
12
|
const CHANNEL_GATEWAY_CREDENTIAL_FILENAME = '.borgee-agent-gateway.json';
|
|
8
|
-
const
|
|
13
|
+
const CLAUDE_PROJECTED_BRIEF_FILENAME = 'CLAUDE.md';
|
|
14
|
+
const DEFAULT_HOME_BORGEE_DIRNAME = '.borgee';
|
|
15
|
+
const CHANNEL_WORKSPACE_COLLECTION_DIRNAME = 'channels';
|
|
16
|
+
const CHANNEL_WORKSPACE_DIRNAME = 'workspace';
|
|
17
|
+
const TASK_ISOLATED_WORKSPACE_COLLECTION_DIRNAME = 'tasks';
|
|
18
|
+
const TASK_THREAD_SCRATCH_WORKSPACE_DIRNAME = 'scratch';
|
|
19
|
+
const TASK_EXECUTION_LOCAL_DIRECTORY_PROPERTY_KEY = 'execution.local_directory';
|
|
20
|
+
const DEFAULT_TASK_WORKSPACE_RESOLUTION_TIMEOUT_MS = 1_500;
|
|
9
21
|
export class ChannelContextPreparationError extends Error {
|
|
10
22
|
partialContext;
|
|
11
23
|
constructor(message, partialContext, options) {
|
|
@@ -33,8 +45,11 @@ const DEFAULT_FILE_SYSTEM = {
|
|
|
33
45
|
async rename(oldPath, newPath) {
|
|
34
46
|
await fs.rename(oldPath, newPath);
|
|
35
47
|
},
|
|
36
|
-
async access(path) {
|
|
37
|
-
await fs.access(path);
|
|
48
|
+
async access(path, mode) {
|
|
49
|
+
await fs.access(path, mode);
|
|
50
|
+
},
|
|
51
|
+
async stat(path) {
|
|
52
|
+
return await fs.stat(path);
|
|
38
53
|
},
|
|
39
54
|
};
|
|
40
55
|
const BORGEE_AGENT_SKILL_DIRNAME = 'borgee-agent';
|
|
@@ -62,19 +77,50 @@ function buildBorgeeAgentGatewayCredential(channelId, localhostGateway) {
|
|
|
62
77
|
},
|
|
63
78
|
};
|
|
64
79
|
}
|
|
65
|
-
function buildChannelContextPayload(channelId, collaborationOutcome, attentionSnapshot, taskThreadCollaborationContract, collaborationCapabilities, missedCollaborationDiagnostic, skillRuntime, localhostGateway, options) {
|
|
80
|
+
function buildChannelContextPayload(channelId, runtimeSurface, collaborationOutcome, attentionSnapshot, compactionSnapshot, taskThreadCollaborationContract, collaborationCapabilities, missedCollaborationDiagnostic, skillRuntime, localhostGateway, options) {
|
|
66
81
|
return {
|
|
67
82
|
schemaVersion: 1,
|
|
68
83
|
channelId,
|
|
84
|
+
runtimeSurface,
|
|
69
85
|
...(collaborationOutcome ? { collaborationOutcome } : {}),
|
|
70
86
|
...(attentionSnapshot ? { attentionSnapshot } : {}),
|
|
87
|
+
...(compactionSnapshot ? { compactionSnapshot } : {}),
|
|
71
88
|
...(taskThreadCollaborationContract ? { taskThreadCollaborationContract } : {}),
|
|
72
89
|
...(collaborationCapabilities ? { collaborationCapabilities } : {}),
|
|
73
90
|
...(missedCollaborationDiagnostic ? { missedCollaborationDiagnostic } : {}),
|
|
74
91
|
...(skillRuntime ? { skillRuntime: toSkillRuntimePayload(skillRuntime) } : {}),
|
|
75
92
|
...(localhostGateway ? { localhostGateway } : {}),
|
|
76
93
|
...(options?.taskAssignmentContext ? { taskAssignmentContext: options.taskAssignmentContext } : {}),
|
|
77
|
-
...(options?.
|
|
94
|
+
...(options?.resolvedWorkspace ? { resolvedWorkspace: options.resolvedWorkspace } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function hashUtf8Content(content) {
|
|
98
|
+
return createHash('sha256').update(content).digest('hex');
|
|
99
|
+
}
|
|
100
|
+
async function unlinkIfPresent(fileSystem, path) {
|
|
101
|
+
await fileSystem.unlink(path).catch((error) => {
|
|
102
|
+
if (error.code !== 'ENOENT') {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function buildPromptContextForClaudeProjectedBrief(preparedContext) {
|
|
108
|
+
return {
|
|
109
|
+
channelContextPayloadPath: preparedContext.payloadPath,
|
|
110
|
+
claudeProjectedBriefPath: preparedContext.claudeProjectedBriefPath,
|
|
111
|
+
claudeProjectedBriefHash: preparedContext.claudeProjectedBriefHash,
|
|
112
|
+
gatewayCredentialPath: preparedContext.gatewayCredentialPath,
|
|
113
|
+
skillRuntime: preparedContext.skillRuntime,
|
|
114
|
+
localhostGateway: preparedContext.localhostGateway,
|
|
115
|
+
taskAssignmentContext: preparedContext.payload.taskAssignmentContext,
|
|
116
|
+
resolvedWorkspace: preparedContext.resolvedWorkspace,
|
|
117
|
+
runtimeSurface: preparedContext.payload.runtimeSurface,
|
|
118
|
+
collaborationOutcome: preparedContext.payload.collaborationOutcome,
|
|
119
|
+
attentionSnapshot: preparedContext.payload.attentionSnapshot,
|
|
120
|
+
compactionSnapshot: preparedContext.payload.compactionSnapshot,
|
|
121
|
+
collaborationCapabilities: preparedContext.payload.collaborationCapabilities,
|
|
122
|
+
missedCollaborationDiagnostic: preparedContext.payload.missedCollaborationDiagnostic,
|
|
123
|
+
taskThreadCollaborationContract: preparedContext.payload.taskThreadCollaborationContract,
|
|
78
124
|
};
|
|
79
125
|
}
|
|
80
126
|
function isRecord(value) {
|
|
@@ -96,6 +142,73 @@ function isUsableTaskId(value) {
|
|
|
96
142
|
&& !/[\u0000-\u001f\u007f\s]/u.test(value)
|
|
97
143
|
&& canRoundTripThroughUriComponentEncoding(value);
|
|
98
144
|
}
|
|
145
|
+
function normalizeTaskAssignmentCurrentTaskId(taskAssignmentContext) {
|
|
146
|
+
const currentTaskId = typeof taskAssignmentContext?.currentTaskId === 'string'
|
|
147
|
+
? taskAssignmentContext.currentTaskId.trim()
|
|
148
|
+
: '';
|
|
149
|
+
return currentTaskId && isUsableTaskId(currentTaskId)
|
|
150
|
+
? currentTaskId
|
|
151
|
+
: undefined;
|
|
152
|
+
}
|
|
153
|
+
export function buildRuntimeSurfaceForTurn(options) {
|
|
154
|
+
const turnMode = options?.collaboration?.turnMode ?? 'ordinary';
|
|
155
|
+
const gatewayAvailable = options?.localhostGateway !== undefined;
|
|
156
|
+
const currentTaskId = normalizeTaskAssignmentCurrentTaskId(options?.taskAssignmentContext);
|
|
157
|
+
const taskAssignmentThreadActive = options?.taskAssignmentContext?.active === true;
|
|
158
|
+
const collaborationGatewayEnabled = gatewayAvailable && options.localhostGateway?.collaboration?.enabled === true;
|
|
159
|
+
const auxiliarySendAvailable = turnMode === 'ordinary'
|
|
160
|
+
&& collaborationGatewayEnabled
|
|
161
|
+
&& options?.collaboration?.sendRoutesAllowed === true;
|
|
162
|
+
return {
|
|
163
|
+
schemaVersion: 1,
|
|
164
|
+
turnMode,
|
|
165
|
+
gateway: {
|
|
166
|
+
available: gatewayAvailable,
|
|
167
|
+
readOnlyRoutesAuthorized: gatewayAvailable,
|
|
168
|
+
inspectBeforeAnswer: gatewayAvailable,
|
|
169
|
+
},
|
|
170
|
+
task: {
|
|
171
|
+
currentThread: taskAssignmentThreadActive ? 'task-assignment-thread' : 'parent-channel',
|
|
172
|
+
parentChannelTaskCollection: taskAssignmentThreadActive ? 'parent-channel-only' : 'available',
|
|
173
|
+
currentTaskShorthand: currentTaskId
|
|
174
|
+
? 'persisted-current-task'
|
|
175
|
+
: taskAssignmentThreadActive
|
|
176
|
+
? 'fallback-current-task'
|
|
177
|
+
: 'requires-task-id',
|
|
178
|
+
currentTaskIdPersisted: currentTaskId !== undefined,
|
|
179
|
+
currentTaskFallback: !currentTaskId && taskAssignmentThreadActive
|
|
180
|
+
? 'visible-parent-channel-thread-scan'
|
|
181
|
+
: 'disallowed',
|
|
182
|
+
},
|
|
183
|
+
collaboration: gatewayAvailable
|
|
184
|
+
? turnMode === 'ordinary'
|
|
185
|
+
? {
|
|
186
|
+
participantLookup: collaborationGatewayEnabled ? 'available' : 'unavailable',
|
|
187
|
+
draftRead: auxiliarySendAvailable ? 'available' : 'unavailable',
|
|
188
|
+
auxiliarySend: auxiliarySendAvailable ? 'available' : 'unavailable',
|
|
189
|
+
}
|
|
190
|
+
: {
|
|
191
|
+
participantLookup: 'unavailable',
|
|
192
|
+
draftRead: 'unavailable',
|
|
193
|
+
auxiliarySend: 'unavailable',
|
|
194
|
+
}
|
|
195
|
+
: {
|
|
196
|
+
participantLookup: 'unavailable',
|
|
197
|
+
draftRead: 'unavailable',
|
|
198
|
+
auxiliarySend: 'unavailable',
|
|
199
|
+
},
|
|
200
|
+
response: {
|
|
201
|
+
mainVisibleReply: turnMode === 'ordinary'
|
|
202
|
+
? 'model-visible-reply'
|
|
203
|
+
: turnMode === 'protocol-managed'
|
|
204
|
+
? 'host-visible-protocol-reply'
|
|
205
|
+
: 'no-visible-reply',
|
|
206
|
+
auxiliaryMessageUsage: auxiliarySendAvailable
|
|
207
|
+
? 'targeted-escalation-only'
|
|
208
|
+
: 'unavailable',
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
99
212
|
function sanitizeTaskAssignmentContext(value) {
|
|
100
213
|
if (!isRecord(value) || value.active !== true) {
|
|
101
214
|
return undefined;
|
|
@@ -123,9 +236,9 @@ function buildTaskAssignmentContextForTurn(options, existingContext) {
|
|
|
123
236
|
}
|
|
124
237
|
return existingContext;
|
|
125
238
|
}
|
|
126
|
-
async function readExistingTaskAssignmentContext(
|
|
239
|
+
async function readExistingTaskAssignmentContext(statePath, fileSystem) {
|
|
127
240
|
try {
|
|
128
|
-
const raw = await fileSystem.readFile(
|
|
241
|
+
const raw = await fileSystem.readFile(statePath, { encoding: 'utf8' });
|
|
129
242
|
const payload = JSON.parse(raw);
|
|
130
243
|
return sanitizeTaskAssignmentContext(payload.taskAssignmentContext);
|
|
131
244
|
}
|
|
@@ -133,6 +246,127 @@ async function readExistingTaskAssignmentContext(payloadPath, fileSystem) {
|
|
|
133
246
|
return undefined;
|
|
134
247
|
}
|
|
135
248
|
}
|
|
249
|
+
function buildChannelResolvedWorkspace(workspaceCollectionRootDir, channelId) {
|
|
250
|
+
return {
|
|
251
|
+
authority: 'channel',
|
|
252
|
+
owningChannelId: channelId,
|
|
253
|
+
rootPath: resolveChannelWorkspaceDirectory(workspaceCollectionRootDir, channelId),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function buildTaskThreadScratchResolvedWorkspace(workspaceCollectionRootDir, channelId, reason = 'missing-or-invalid-execution-target') {
|
|
257
|
+
return {
|
|
258
|
+
authority: 'task-thread-scratch',
|
|
259
|
+
rootPath: resolveTaskThreadScratchWorkspaceDirectory(workspaceCollectionRootDir, channelId),
|
|
260
|
+
reason,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async function isCrossAgentTaskCreatedByAnotherAgent(params) {
|
|
264
|
+
const selfAgentId = params.resolveStableAgentId?.()?.trim();
|
|
265
|
+
if (!selfAgentId) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
if (params.task.createdBy === selfAgentId) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
if (!params.taskReader) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
const users = await params.taskReader.listUsers();
|
|
275
|
+
const creator = users.find((user) => user.id === params.task.createdBy);
|
|
276
|
+
if (!creator) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return creator.kind === 'agent';
|
|
280
|
+
}
|
|
281
|
+
async function withDeadline(promise, timeoutMs, timeoutMessage) {
|
|
282
|
+
if (timeoutMs <= 0) {
|
|
283
|
+
return await promise;
|
|
284
|
+
}
|
|
285
|
+
let timer;
|
|
286
|
+
try {
|
|
287
|
+
return await Promise.race([
|
|
288
|
+
promise,
|
|
289
|
+
new Promise((_, reject) => {
|
|
290
|
+
timer = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
|
|
291
|
+
}),
|
|
292
|
+
]);
|
|
293
|
+
}
|
|
294
|
+
finally {
|
|
295
|
+
if (timer) {
|
|
296
|
+
clearTimeout(timer);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function isExistingDirectory(fileSystem, path) {
|
|
301
|
+
if (!isAbsolute(path)) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
await fileSystem.access(path, fsConstants.R_OK | fsConstants.W_OK | fsConstants.X_OK);
|
|
306
|
+
if (typeof fileSystem.stat !== 'function') {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
return (await fileSystem.stat(path)).isDirectory();
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function resolveTaskThreadWorkspaceContext(params) {
|
|
316
|
+
const channelWorkspace = buildChannelResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId);
|
|
317
|
+
const discussionOnlyWorkspace = buildTaskThreadScratchResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId);
|
|
318
|
+
const taskAssignmentContext = params.taskAssignmentContext;
|
|
319
|
+
const currentTaskId = taskAssignmentContext?.currentTaskId?.trim();
|
|
320
|
+
if (taskAssignmentContext?.active !== true) {
|
|
321
|
+
return { taskAssignmentContext, resolvedWorkspace: channelWorkspace };
|
|
322
|
+
}
|
|
323
|
+
if (!currentTaskId || !params.taskReader) {
|
|
324
|
+
return { taskAssignmentContext, resolvedWorkspace: discussionOnlyWorkspace };
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const task = await withDeadline(findTaskForThread(params.taskReader, params.channelId, currentTaskId), params.taskResolutionTimeoutMs, `workspace resolution timed out for task ${currentTaskId}`);
|
|
328
|
+
if (!task) {
|
|
329
|
+
return {
|
|
330
|
+
taskAssignmentContext,
|
|
331
|
+
resolvedWorkspace: discussionOnlyWorkspace,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const executionLocalDirectory = task.properties[TASK_EXECUTION_LOCAL_DIRECTORY_PROPERTY_KEY];
|
|
335
|
+
if (typeof executionLocalDirectory !== 'string'
|
|
336
|
+
|| executionLocalDirectory.length === 0
|
|
337
|
+
|| !(await isExistingDirectory(params.fileSystem, executionLocalDirectory))) {
|
|
338
|
+
return {
|
|
339
|
+
taskAssignmentContext,
|
|
340
|
+
resolvedWorkspace: discussionOnlyWorkspace,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (!params.allowCrossAgentIndependentWorkspaceHandoff
|
|
344
|
+
&& await isCrossAgentTaskCreatedByAnotherAgent({
|
|
345
|
+
task,
|
|
346
|
+
taskReader: params.taskReader,
|
|
347
|
+
resolveStableAgentId: params.resolveStableAgentId,
|
|
348
|
+
})) {
|
|
349
|
+
return {
|
|
350
|
+
taskAssignmentContext,
|
|
351
|
+
resolvedWorkspace: buildTaskThreadScratchResolvedWorkspace(params.workspaceCollectionRootDir, params.channelId, 'cross-agent-independent-workspace-disabled'),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
taskAssignmentContext,
|
|
356
|
+
resolvedWorkspace: {
|
|
357
|
+
authority: 'task-execution-target',
|
|
358
|
+
taskId: task.id,
|
|
359
|
+
rootPath: executionLocalDirectory,
|
|
360
|
+
},
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
return {
|
|
365
|
+
taskAssignmentContext,
|
|
366
|
+
resolvedWorkspace: discussionOnlyWorkspace,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
136
370
|
export function encodeChannelPathSegment(channelId) {
|
|
137
371
|
const encoded = Buffer.from(channelId, 'utf8').toString('hex');
|
|
138
372
|
return encoded.length > 0 ? encoded : 'empty';
|
|
@@ -143,11 +377,38 @@ export function resolveChannelContextDirectory(stateRootDir, channelId) {
|
|
|
143
377
|
export function resolveChannelContextPayloadPath(stateRootDir, channelId) {
|
|
144
378
|
return join(resolveChannelContextDirectory(stateRootDir, channelId), CHANNEL_CONTEXT_PAYLOAD_FILENAME);
|
|
145
379
|
}
|
|
380
|
+
export function resolveTaskAssignmentStateDirectory(stateRootDir, channelId) {
|
|
381
|
+
return resolve(stateRootDir, TASK_ASSIGNMENT_STATE_ROOT_DIRNAME, encodeChannelPathSegment(channelId));
|
|
382
|
+
}
|
|
383
|
+
export function resolveTaskAssignmentStatePath(stateRootDir, channelId) {
|
|
384
|
+
return join(resolveTaskAssignmentStateDirectory(stateRootDir, channelId), TASK_ASSIGNMENT_STATE_FILENAME);
|
|
385
|
+
}
|
|
146
386
|
export function resolveTaskWorkspaceRootDirectory(startupWorkspaceRootDir) {
|
|
147
|
-
return join(resolve(startupWorkspaceRootDir),
|
|
387
|
+
return join(resolve(startupWorkspaceRootDir), TASK_ISOLATED_WORKSPACE_COLLECTION_DIRNAME);
|
|
148
388
|
}
|
|
149
|
-
export function
|
|
150
|
-
|
|
389
|
+
export function resolveManagedWorkspaceCollectionRoot(workspaceCollectionRootDir, env = process.env, resolvedHomeDir = homedir()) {
|
|
390
|
+
const explicitRoot = workspaceCollectionRootDir?.trim();
|
|
391
|
+
if (explicitRoot) {
|
|
392
|
+
return resolve(explicitRoot);
|
|
393
|
+
}
|
|
394
|
+
const home = env.HOME?.trim() || resolvedHomeDir.trim();
|
|
395
|
+
if (!home) {
|
|
396
|
+
throw new Error('Unable to resolve a user home directory for agents-host workspaces');
|
|
397
|
+
}
|
|
398
|
+
return join(resolve(home), DEFAULT_HOME_BORGEE_DIRNAME, CHANNEL_WORKSPACE_COLLECTION_DIRNAME);
|
|
399
|
+
}
|
|
400
|
+
export function resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId) {
|
|
401
|
+
return join(resolve(workspaceCollectionRootDir), encodeChannelPathSegment(channelId));
|
|
402
|
+
}
|
|
403
|
+
export function resolveChannelWorkspaceDirectory(workspaceCollectionRootDir, channelId) {
|
|
404
|
+
return join(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId), CHANNEL_WORKSPACE_DIRNAME);
|
|
405
|
+
}
|
|
406
|
+
export function resolveTaskIsolatedWorkspaceDirectory(workspaceCollectionRootDir, channelId, taskId) {
|
|
407
|
+
return join(resolveTaskWorkspaceRootDirectory(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId)), encodeChannelPathSegment(taskId), CHANNEL_WORKSPACE_DIRNAME);
|
|
408
|
+
}
|
|
409
|
+
export const resolveTaskWorkspaceDirectory = resolveTaskIsolatedWorkspaceDirectory;
|
|
410
|
+
export function resolveTaskThreadScratchWorkspaceDirectory(workspaceCollectionRootDir, channelId) {
|
|
411
|
+
return join(resolveChannelWorkspaceRootDirectory(workspaceCollectionRootDir, channelId), TASK_THREAD_SCRATCH_WORKSPACE_DIRNAME, CHANNEL_WORKSPACE_DIRNAME);
|
|
151
412
|
}
|
|
152
413
|
export function resolveChannelGatewayCredentialPath(stateRootDir, channelId) {
|
|
153
414
|
return join(resolveChannelContextDirectory(stateRootDir, channelId), CHANNEL_GATEWAY_CREDENTIAL_FILENAME);
|
|
@@ -155,6 +416,9 @@ export function resolveChannelGatewayCredentialPath(stateRootDir, channelId) {
|
|
|
155
416
|
export function resolveGatewayCredentialPathFromPayloadPath(payloadPath) {
|
|
156
417
|
return join(dirname(payloadPath), CHANNEL_GATEWAY_CREDENTIAL_FILENAME);
|
|
157
418
|
}
|
|
419
|
+
export function resolveClaudeProjectedBriefPathFromPayloadPath(payloadPath) {
|
|
420
|
+
return join(dirname(payloadPath), CLAUDE_PROJECTED_BRIEF_FILENAME);
|
|
421
|
+
}
|
|
158
422
|
/**
|
|
159
423
|
* Every basename a gateway credential can carry in a channel context directory, including the ones
|
|
160
424
|
* this host no longer writes. The directory holds at most one live credential — the token is
|
|
@@ -245,14 +509,23 @@ export class FileChannelContextStore {
|
|
|
245
509
|
skillRuntimeEnabled;
|
|
246
510
|
skillAssetResolver;
|
|
247
511
|
localhostGateway;
|
|
248
|
-
|
|
512
|
+
workspaceCollectionRootDir;
|
|
513
|
+
taskReader;
|
|
514
|
+
taskResolutionTimeoutMs;
|
|
515
|
+
allowCrossAgentIndependentWorkspaceHandoff;
|
|
516
|
+
resolveStableAgentId;
|
|
249
517
|
constructor(stateRootDir, options = {}) {
|
|
250
518
|
this.stateRootDir = stateRootDir;
|
|
251
519
|
this.fileSystem = options.fileSystem ?? DEFAULT_FILE_SYSTEM;
|
|
252
520
|
this.skillRuntimeEnabled = options.skillRuntimeEnabled ?? false;
|
|
253
521
|
this.skillAssetResolver = options.skillAssetResolver ?? new PackageSkillAssetResolver(import.meta.url, this.fileSystem);
|
|
254
522
|
this.localhostGateway = options.localhostGateway;
|
|
255
|
-
this.
|
|
523
|
+
this.workspaceCollectionRootDir = resolveManagedWorkspaceCollectionRoot(options.workspaceCollectionRootDir, options.env, options.resolvedHomeDir);
|
|
524
|
+
this.taskReader = options.taskReader;
|
|
525
|
+
this.taskResolutionTimeoutMs = options.taskResolutionTimeoutMs ?? DEFAULT_TASK_WORKSPACE_RESOLUTION_TIMEOUT_MS;
|
|
526
|
+
this.allowCrossAgentIndependentWorkspaceHandoff
|
|
527
|
+
= options.allowCrossAgentIndependentWorkspaceHandoff ?? true;
|
|
528
|
+
this.resolveStableAgentId = options.resolveStableAgentId;
|
|
256
529
|
}
|
|
257
530
|
async prepare(inputOrChannelId, options) {
|
|
258
531
|
const input = typeof inputOrChannelId === 'string'
|
|
@@ -264,8 +537,10 @@ export class FileChannelContextStore {
|
|
|
264
537
|
const auxiliaryCollaborationEnabled = collaborationCommandsEnabled && collaborationRoutesAllowedForTurnMode;
|
|
265
538
|
const directoryPath = resolveChannelContextDirectory(this.stateRootDir, input.channelId);
|
|
266
539
|
const payloadPath = resolveChannelContextPayloadPath(this.stateRootDir, input.channelId);
|
|
540
|
+
const taskAssignmentStatePath = resolveTaskAssignmentStatePath(this.stateRootDir, input.channelId);
|
|
541
|
+
const claudeProjectedBriefPath = resolveClaudeProjectedBriefPathFromPayloadPath(payloadPath);
|
|
267
542
|
const gatewayCredentialPath = resolveGatewayCredentialPathFromPayloadPath(payloadPath);
|
|
268
|
-
const existingTaskAssignmentContext = await readExistingTaskAssignmentContext(
|
|
543
|
+
const existingTaskAssignmentContext = await readExistingTaskAssignmentContext(taskAssignmentStatePath, this.fileSystem);
|
|
269
544
|
const skillRuntime = this.skillRuntimeEnabled
|
|
270
545
|
? await this.resolveSkillRuntimeBestEffort()
|
|
271
546
|
: undefined;
|
|
@@ -291,24 +566,41 @@ export class FileChannelContextStore {
|
|
|
291
566
|
: {}),
|
|
292
567
|
}
|
|
293
568
|
: undefined;
|
|
294
|
-
const taskAssignmentContext =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
:
|
|
304
|
-
|
|
569
|
+
const taskAssignmentContext = input.taskAssignmentContextOverride
|
|
570
|
+
?? buildTaskAssignmentContextForTurn({
|
|
571
|
+
incomingMessageType: input.incomingMessageType,
|
|
572
|
+
incomingContent: input.incomingContent,
|
|
573
|
+
}, existingTaskAssignmentContext);
|
|
574
|
+
const workspaceResolution = await resolveTaskThreadWorkspaceContext({
|
|
575
|
+
channelId: input.channelId,
|
|
576
|
+
workspaceCollectionRootDir: this.workspaceCollectionRootDir,
|
|
577
|
+
taskAssignmentContext,
|
|
578
|
+
taskReader: this.taskReader,
|
|
579
|
+
taskResolutionTimeoutMs: this.taskResolutionTimeoutMs,
|
|
580
|
+
allowCrossAgentIndependentWorkspaceHandoff: this.allowCrossAgentIndependentWorkspaceHandoff,
|
|
581
|
+
resolveStableAgentId: this.resolveStableAgentId,
|
|
582
|
+
fileSystem: this.fileSystem,
|
|
583
|
+
});
|
|
584
|
+
const resolvedWorkspace = workspaceResolution.resolvedWorkspace;
|
|
585
|
+
const runtimeTaskAssignmentContext = workspaceResolution.taskAssignmentContext ?? taskAssignmentContext;
|
|
586
|
+
const persistedTaskAssignmentContext = input.taskAssignmentContextOverridePersistence === 'ephemeral'
|
|
587
|
+
? existingTaskAssignmentContext
|
|
588
|
+
: runtimeTaskAssignmentContext;
|
|
589
|
+
const runtimeSurface = buildRuntimeSurfaceForTurn({
|
|
590
|
+
localhostGateway,
|
|
591
|
+
collaboration: input.collaboration,
|
|
592
|
+
taskAssignmentContext: runtimeTaskAssignmentContext,
|
|
593
|
+
});
|
|
594
|
+
const payload = buildChannelContextPayload(input.channelId, runtimeSurface, input.collaborationOutcome, input.attentionSnapshot, input.compactionSnapshot, input.taskThreadCollaborationContract, input.collaborationCapabilities, input.missedCollaborationDiagnostic, skillRuntime, localhostGateway, persistedTaskAssignmentContext || resolvedWorkspace
|
|
305
595
|
? {
|
|
306
|
-
...(
|
|
307
|
-
|
|
596
|
+
...(persistedTaskAssignmentContext
|
|
597
|
+
? { taskAssignmentContext: persistedTaskAssignmentContext }
|
|
598
|
+
: {}),
|
|
599
|
+
resolvedWorkspace,
|
|
308
600
|
}
|
|
309
601
|
: undefined);
|
|
310
602
|
const gatewayCredential = buildBorgeeAgentGatewayCredential(input.channelId, issuedLocalhostGateway);
|
|
311
|
-
let
|
|
603
|
+
let claudeProjectedBriefWritten = false;
|
|
312
604
|
let payloadWritten = false;
|
|
313
605
|
let gatewayCredentialWritten = false;
|
|
314
606
|
const preparedContext = {
|
|
@@ -318,17 +610,39 @@ export class FileChannelContextStore {
|
|
|
318
610
|
gatewayCredentialPath: gatewayCredential ? gatewayCredentialPath : undefined,
|
|
319
611
|
skillRuntime,
|
|
320
612
|
localhostGateway,
|
|
321
|
-
|
|
613
|
+
resolvedWorkspace,
|
|
322
614
|
};
|
|
615
|
+
const shouldProjectClaudeBrief = input.provider === 'claude' && input.projectionStrategy === 'file-brief';
|
|
616
|
+
const claudeProjectedBriefContent = shouldProjectClaudeBrief
|
|
617
|
+
? buildClaudeFileBrief(buildPromptContextForClaudeProjectedBrief({
|
|
618
|
+
...preparedContext,
|
|
619
|
+
claudeProjectedBriefPath,
|
|
620
|
+
}))
|
|
621
|
+
: undefined;
|
|
622
|
+
if (claudeProjectedBriefContent) {
|
|
623
|
+
preparedContext.claudeProjectedBriefPath = claudeProjectedBriefPath;
|
|
624
|
+
preparedContext.claudeProjectedBriefHash = hashUtf8Content(claudeProjectedBriefContent);
|
|
625
|
+
}
|
|
323
626
|
try {
|
|
324
627
|
await this.fileSystem.mkdir(directoryPath, { recursive: true, mode: 0o700 });
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
628
|
+
await this.fileSystem.mkdir(resolveTaskAssignmentStateDirectory(this.stateRootDir, input.channelId), { recursive: true, mode: 0o700 });
|
|
629
|
+
if (resolvedWorkspace.authority !== 'task-execution-target') {
|
|
630
|
+
await this.fileSystem.mkdir(resolvedWorkspace.rootPath, { recursive: true, mode: 0o700 });
|
|
328
631
|
}
|
|
329
632
|
await pruneGatewayCredentialSidecars(this.fileSystem, directoryPath, gatewayCredential ? CHANNEL_GATEWAY_CREDENTIAL_FILENAME : undefined);
|
|
633
|
+
if (!claudeProjectedBriefContent) {
|
|
634
|
+
await unlinkIfPresent(this.fileSystem, claudeProjectedBriefPath);
|
|
635
|
+
}
|
|
330
636
|
await this.fileSystem.writeFile(payloadPath, `${JSON.stringify(payload, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
331
637
|
payloadWritten = true;
|
|
638
|
+
if (persistedTaskAssignmentContext) {
|
|
639
|
+
const stagingTaskAssignmentStatePath = join(resolveTaskAssignmentStateDirectory(this.stateRootDir, input.channelId), `.task-assignment-state.${randomUUID()}.json`);
|
|
640
|
+
await this.fileSystem.writeFile(stagingTaskAssignmentStatePath, `${JSON.stringify({ taskAssignmentContext: persistedTaskAssignmentContext }, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
641
|
+
await this.fileSystem.rename(stagingTaskAssignmentStatePath, taskAssignmentStatePath);
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
await unlinkIfPresent(this.fileSystem, taskAssignmentStatePath);
|
|
645
|
+
}
|
|
332
646
|
if (gatewayCredential) {
|
|
333
647
|
// Replaced atomically: a CLI invocation left over from an earlier turn must read either
|
|
334
648
|
// the old credential or the new one, never a missing or half-written file, because the
|
|
@@ -342,20 +656,40 @@ export class FileChannelContextStore {
|
|
|
342
656
|
else {
|
|
343
657
|
this.localhostGateway?.clearChannel(input.channelId);
|
|
344
658
|
}
|
|
659
|
+
if (claudeProjectedBriefContent) {
|
|
660
|
+
const stagingProjectedBriefPath = join(directoryPath, `.claude-file-brief.${randomUUID()}.md`);
|
|
661
|
+
await this.fileSystem.writeFile(stagingProjectedBriefPath, claudeProjectedBriefContent, { encoding: 'utf8', mode: 0o600 });
|
|
662
|
+
await this.fileSystem.rename(stagingProjectedBriefPath, claudeProjectedBriefPath);
|
|
663
|
+
claudeProjectedBriefWritten = true;
|
|
664
|
+
}
|
|
345
665
|
if (issuedLocalhostGateway) {
|
|
346
666
|
this.localhostGateway?.publishPayload(input.channelId, payloadPath, payload);
|
|
347
667
|
}
|
|
348
668
|
return preparedContext;
|
|
349
669
|
}
|
|
350
670
|
catch (error) {
|
|
671
|
+
if (claudeProjectedBriefContent && !claudeProjectedBriefWritten) {
|
|
672
|
+
await unlinkIfPresent(this.fileSystem, claudeProjectedBriefPath);
|
|
673
|
+
}
|
|
351
674
|
const partialContext = {
|
|
352
675
|
directoryPath,
|
|
353
|
-
payload
|
|
676
|
+
payload: {
|
|
677
|
+
...payload,
|
|
678
|
+
...(resolvedWorkspace.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
|
|
679
|
+
? {
|
|
680
|
+
resolvedWorkspace: buildTaskThreadScratchResolvedWorkspace(this.workspaceCollectionRootDir, input.channelId),
|
|
681
|
+
}
|
|
682
|
+
: {}),
|
|
683
|
+
},
|
|
684
|
+
claudeProjectedBriefPath: claudeProjectedBriefWritten ? claudeProjectedBriefPath : undefined,
|
|
685
|
+
claudeProjectedBriefHash: claudeProjectedBriefWritten ? preparedContext.claudeProjectedBriefHash : undefined,
|
|
354
686
|
payloadPath: payloadWritten ? payloadPath : undefined,
|
|
355
687
|
gatewayCredentialPath: gatewayCredentialWritten ? gatewayCredentialPath : undefined,
|
|
356
688
|
skillRuntime: payloadWritten ? skillRuntime : undefined,
|
|
357
689
|
localhostGateway: payloadWritten ? localhostGateway : undefined,
|
|
358
|
-
|
|
690
|
+
resolvedWorkspace: resolvedWorkspace.authority === 'task-execution-target' && runtimeTaskAssignmentContext?.active === true
|
|
691
|
+
? buildTaskThreadScratchResolvedWorkspace(this.workspaceCollectionRootDir, input.channelId)
|
|
692
|
+
: resolvedWorkspace,
|
|
359
693
|
};
|
|
360
694
|
throw new ChannelContextPreparationError('failed to persist channel context payload', partialContext, { cause: error });
|
|
361
695
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MAIN_SESSION_DELEGATION_LINES: readonly
|
|
1
|
+
export declare const MAIN_SESSION_DELEGATION_LINES: readonly ["This session is your coordination surface: read the channel, answer the human, break work down, and keep task state current here.", "Delegate the actual work to subagents — research, code edits, long builds and test runs, and broad repository sweeps belong in a delegated worker instead of running inline here.", "Delegation changes who executes, not who owns: the work still belongs to this thread, and you still report the result here yourself.", "Stay responsive while delegated work runs, so a new incoming message never waits behind a long local operation."];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DebugLogger } from '../debug.js';
|
|
2
|
+
import type { ChatControlPlane } from '../chat/chat-control-plane.js';
|
|
3
|
+
import type { ProjectionStrategy } from '../types.js';
|
|
4
|
+
interface ProjectionStrategyResolverOptions {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
agentApiKey?: string;
|
|
7
|
+
resolveStableAgentId?: () => string | undefined;
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
fallback?: () => ProjectionStrategy;
|
|
10
|
+
logger?: Pick<DebugLogger, 'error'>;
|
|
11
|
+
taskReader?: Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks'>;
|
|
12
|
+
}
|
|
13
|
+
export declare class ServerProjectionStrategyResolver {
|
|
14
|
+
private readonly options;
|
|
15
|
+
private readonly strategyCache;
|
|
16
|
+
private readonly channelAliases;
|
|
17
|
+
private readonly fetchImpl;
|
|
18
|
+
private readonly fallback;
|
|
19
|
+
constructor(options: ProjectionStrategyResolverOptions);
|
|
20
|
+
resolve(channelId: string): Promise<ProjectionStrategy>;
|
|
21
|
+
private resolveCachedStrategy;
|
|
22
|
+
private resolveEffectiveCacheKey;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { DEFAULT_PROJECTION_STRATEGY, normalizeProjectionStrategy, } from '../projection-strategy-values.js';
|
|
2
|
+
import { findTaskForThread } from '../task-thread-resolution.js';
|
|
3
|
+
export class ServerProjectionStrategyResolver {
|
|
4
|
+
options;
|
|
5
|
+
strategyCache = new Map();
|
|
6
|
+
channelAliases = new Map();
|
|
7
|
+
fetchImpl;
|
|
8
|
+
fallback;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
12
|
+
this.fallback = options.fallback ?? (() => DEFAULT_PROJECTION_STRATEGY);
|
|
13
|
+
}
|
|
14
|
+
async resolve(channelId) {
|
|
15
|
+
const baseUrl = this.options.baseUrl?.trim();
|
|
16
|
+
const agentApiKey = this.options.agentApiKey?.trim();
|
|
17
|
+
const agentId = this.options.resolveStableAgentId?.()?.trim();
|
|
18
|
+
if (!baseUrl || !agentApiKey || !agentId) {
|
|
19
|
+
return this.fallback();
|
|
20
|
+
}
|
|
21
|
+
const cacheKey = `${agentId}::${channelId}`;
|
|
22
|
+
const url = new URL(`/api/v1/agents/${encodeURIComponent(agentId)}/channels/${encodeURIComponent(channelId)}/projection-strategy`, baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
|
|
23
|
+
try {
|
|
24
|
+
const response = await this.fetchImpl(url.toString(), {
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: `Bearer ${agentApiKey}`,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (response.status === 404) {
|
|
30
|
+
const effectiveCacheKey = await this.resolveEffectiveCacheKey(agentId, channelId, cacheKey);
|
|
31
|
+
this.channelAliases.set(cacheKey, effectiveCacheKey);
|
|
32
|
+
this.strategyCache.set(effectiveCacheKey, DEFAULT_PROJECTION_STRATEGY);
|
|
33
|
+
return DEFAULT_PROJECTION_STRATEGY;
|
|
34
|
+
}
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`server returned ${response.status} ${response.statusText}`.trim());
|
|
37
|
+
}
|
|
38
|
+
const body = await response.json();
|
|
39
|
+
const strategy = normalizeProjectionStrategy(typeof body.projection_strategy === 'string' ? body.projection_strategy : undefined);
|
|
40
|
+
if (!strategy) {
|
|
41
|
+
throw new Error('server returned an invalid projection strategy');
|
|
42
|
+
}
|
|
43
|
+
const effectiveChannelId = typeof body.channel_id === 'string' && body.channel_id.length > 0
|
|
44
|
+
? body.channel_id
|
|
45
|
+
: channelId;
|
|
46
|
+
const effectiveCacheKey = `${agentId}::${effectiveChannelId}`;
|
|
47
|
+
this.channelAliases.set(cacheKey, effectiveCacheKey);
|
|
48
|
+
this.strategyCache.set(effectiveCacheKey, strategy);
|
|
49
|
+
return strategy;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const cached = await this.resolveCachedStrategy(agentId, channelId, cacheKey);
|
|
53
|
+
if (cached) {
|
|
54
|
+
return cached;
|
|
55
|
+
}
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
this.options.logger?.error('failed to resolve server-backed projection strategy; using fallback', {
|
|
58
|
+
agentId,
|
|
59
|
+
channelId,
|
|
60
|
+
error: message,
|
|
61
|
+
});
|
|
62
|
+
return DEFAULT_PROJECTION_STRATEGY;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async resolveCachedStrategy(agentId, channelId, cacheKey) {
|
|
66
|
+
const direct = this.strategyCache.get(this.channelAliases.get(cacheKey) ?? cacheKey);
|
|
67
|
+
if (direct) {
|
|
68
|
+
return direct;
|
|
69
|
+
}
|
|
70
|
+
if (!this.options.taskReader) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const effectiveCacheKey = await this.resolveEffectiveCacheKey(agentId, channelId, cacheKey);
|
|
74
|
+
if (effectiveCacheKey === cacheKey) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
this.channelAliases.set(cacheKey, effectiveCacheKey);
|
|
78
|
+
return this.strategyCache.get(effectiveCacheKey);
|
|
79
|
+
}
|
|
80
|
+
async resolveEffectiveCacheKey(agentId, channelId, cacheKey) {
|
|
81
|
+
if (!this.options.taskReader) {
|
|
82
|
+
return cacheKey;
|
|
83
|
+
}
|
|
84
|
+
const task = await findTaskForThread(this.options.taskReader, channelId);
|
|
85
|
+
if (!task?.channelId) {
|
|
86
|
+
return cacheKey;
|
|
87
|
+
}
|
|
88
|
+
return `${agentId}::${task.channelId}`;
|
|
89
|
+
}
|
|
90
|
+
}
|