cyrus-edge-worker 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgentSessionManager.d.ts +19 -5
- package/dist/AgentSessionManager.d.ts.map +1 -1
- package/dist/AgentSessionManager.js +353 -39
- package/dist/AgentSessionManager.js.map +1 -1
- package/dist/EdgeWorker.d.ts +4 -4
- package/dist/EdgeWorker.d.ts.map +1 -1
- package/dist/EdgeWorker.js +155 -141
- package/dist/EdgeWorker.js.map +1 -1
- package/dist/RepositoryRouter.d.ts +5 -6
- package/dist/RepositoryRouter.d.ts.map +1 -1
- package/dist/RepositoryRouter.js +17 -13
- package/dist/RepositoryRouter.js.map +1 -1
- package/dist/prompt-assembly/types.d.ts +5 -6
- package/dist/prompt-assembly/types.d.ts.map +1 -1
- package/dist/types.d.ts +5 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { type LinearClient } from "@linear/sdk";
|
|
2
1
|
import type { ClaudeRunner, SDKMessage, SDKResultMessage, SDKSystemMessage } from "cyrus-claude-runner";
|
|
3
|
-
import type
|
|
2
|
+
import { type CyrusAgentSession, type CyrusAgentSessionEntry, type IIssueTrackerService, type IssueMinimal, type SerializedCyrusAgentSession, type SerializedCyrusAgentSessionEntry, type Workspace } from "cyrus-core";
|
|
4
3
|
import type { ProcedureRouter } from "./procedures/ProcedureRouter.js";
|
|
5
4
|
import type { SharedApplicationServer } from "./SharedApplicationServer.js";
|
|
6
5
|
/**
|
|
7
|
-
* Manages
|
|
6
|
+
* Manages Agent Sessions integration with Claude Code SDK
|
|
8
7
|
* Transforms Claude streaming messages into Agent Session format
|
|
9
8
|
* Handles session lifecycle: create → active → complete/error
|
|
10
9
|
*
|
|
11
10
|
* CURRENTLY BEING HANDLED 'per repository'
|
|
12
11
|
*/
|
|
13
12
|
export declare class AgentSessionManager {
|
|
14
|
-
private
|
|
13
|
+
private issueTracker;
|
|
15
14
|
private sessions;
|
|
16
15
|
private entries;
|
|
17
16
|
private activeTasksBySession;
|
|
@@ -22,7 +21,7 @@ export declare class AgentSessionManager {
|
|
|
22
21
|
private getParentSessionId?;
|
|
23
22
|
private resumeParentSession?;
|
|
24
23
|
private resumeNextSubroutine?;
|
|
25
|
-
constructor(
|
|
24
|
+
constructor(issueTracker: IIssueTrackerService, getParentSessionId?: (childSessionId: string) => string | undefined, resumeParentSession?: (parentSessionId: string, prompt: string, childSessionId: string) => Promise<void>, resumeNextSubroutine?: (linearAgentActivitySessionId: string) => Promise<void>, procedureRouter?: ProcedureRouter, sharedApplicationServer?: SharedApplicationServer);
|
|
26
25
|
/**
|
|
27
26
|
* Initialize a Linear agent session from webhook
|
|
28
27
|
* The session is already created by Linear, we just need to track it
|
|
@@ -40,6 +39,21 @@ export declare class AgentSessionManager {
|
|
|
40
39
|
* Format TodoWrite tool parameter as a nice checklist
|
|
41
40
|
*/
|
|
42
41
|
private formatTodoWriteParameter;
|
|
42
|
+
/**
|
|
43
|
+
* Format tool input for display in Linear agent activities
|
|
44
|
+
* Converts raw tool inputs into user-friendly parameter strings
|
|
45
|
+
*/
|
|
46
|
+
private formatToolParameter;
|
|
47
|
+
/**
|
|
48
|
+
* Format tool action name with description for Bash tool
|
|
49
|
+
* Puts the description in round brackets after the tool name in the action field
|
|
50
|
+
*/
|
|
51
|
+
private formatToolActionName;
|
|
52
|
+
/**
|
|
53
|
+
* Format tool result for display in Linear agent activities
|
|
54
|
+
* Converts raw tool results into formatted Markdown
|
|
55
|
+
*/
|
|
56
|
+
private formatToolResult;
|
|
43
57
|
/**
|
|
44
58
|
* Complete a session from Claude result message
|
|
45
59
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentSessionManager.d.ts","sourceRoot":"","sources":["../src/AgentSessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"AgentSessionManager.d.ts","sourceRoot":"","sources":["../src/AgentSessionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AgentActivitySignal, AgentSessionStatus, AgentSessionType, } from "cyrus-core";
|
|
2
2
|
/**
|
|
3
|
-
* Manages
|
|
3
|
+
* Manages Agent Sessions integration with Claude Code SDK
|
|
4
4
|
* Transforms Claude streaming messages into Agent Session format
|
|
5
5
|
* Handles session lifecycle: create → active → complete/error
|
|
6
6
|
*
|
|
7
7
|
* CURRENTLY BEING HANDLED 'per repository'
|
|
8
8
|
*/
|
|
9
9
|
export class AgentSessionManager {
|
|
10
|
-
|
|
10
|
+
issueTracker;
|
|
11
11
|
sessions = new Map();
|
|
12
12
|
entries = new Map(); // Stores a list of session entries per each session by its linearAgentActivitySessionId
|
|
13
13
|
activeTasksBySession = new Map(); // Maps session ID to active Task tool use ID
|
|
@@ -18,8 +18,8 @@ export class AgentSessionManager {
|
|
|
18
18
|
getParentSessionId;
|
|
19
19
|
resumeParentSession;
|
|
20
20
|
resumeNextSubroutine;
|
|
21
|
-
constructor(
|
|
22
|
-
this.
|
|
21
|
+
constructor(issueTracker, getParentSessionId, resumeParentSession, resumeNextSubroutine, procedureRouter, sharedApplicationServer) {
|
|
22
|
+
this.issueTracker = issueTracker;
|
|
23
23
|
this.getParentSessionId = getParentSessionId;
|
|
24
24
|
this.resumeParentSession = resumeParentSession;
|
|
25
25
|
this.resumeNextSubroutine = resumeNextSubroutine;
|
|
@@ -34,9 +34,9 @@ export class AgentSessionManager {
|
|
|
34
34
|
console.log(`[AgentSessionManager] Tracking Linear session ${linearAgentActivitySessionId} for issue ${issueId}`);
|
|
35
35
|
const agentSession = {
|
|
36
36
|
linearAgentActivitySessionId,
|
|
37
|
-
type:
|
|
38
|
-
status:
|
|
39
|
-
context:
|
|
37
|
+
type: AgentSessionType.CommentThread,
|
|
38
|
+
status: AgentSessionStatus.Active,
|
|
39
|
+
context: AgentSessionType.CommentThread,
|
|
40
40
|
createdAt: Date.now(),
|
|
41
41
|
updatedAt: Date.now(),
|
|
42
42
|
issueId,
|
|
@@ -133,6 +133,319 @@ export class AgentSessionManager {
|
|
|
133
133
|
return jsonContent;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Format tool input for display in Linear agent activities
|
|
138
|
+
* Converts raw tool inputs into user-friendly parameter strings
|
|
139
|
+
*/
|
|
140
|
+
formatToolParameter(toolName, toolInput) {
|
|
141
|
+
// If input is already a string, return it
|
|
142
|
+
if (typeof toolInput === "string") {
|
|
143
|
+
return toolInput;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
switch (toolName) {
|
|
147
|
+
case "Bash":
|
|
148
|
+
case "↪ Bash": {
|
|
149
|
+
// Show command only - description goes in action field via formatToolActionName
|
|
150
|
+
return toolInput.command || JSON.stringify(toolInput);
|
|
151
|
+
}
|
|
152
|
+
case "Read":
|
|
153
|
+
case "↪ Read":
|
|
154
|
+
if (toolInput.file_path) {
|
|
155
|
+
let param = toolInput.file_path;
|
|
156
|
+
if (toolInput.offset !== undefined ||
|
|
157
|
+
toolInput.limit !== undefined) {
|
|
158
|
+
const start = toolInput.offset || 0;
|
|
159
|
+
const end = toolInput.limit ? start + toolInput.limit : "end";
|
|
160
|
+
param += ` (lines ${start + 1}-${end})`;
|
|
161
|
+
}
|
|
162
|
+
return param;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
case "Edit":
|
|
166
|
+
case "↪ Edit":
|
|
167
|
+
if (toolInput.file_path) {
|
|
168
|
+
return toolInput.file_path;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
case "Write":
|
|
172
|
+
case "↪ Write":
|
|
173
|
+
if (toolInput.file_path) {
|
|
174
|
+
return toolInput.file_path;
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case "Grep":
|
|
178
|
+
case "↪ Grep":
|
|
179
|
+
if (toolInput.pattern) {
|
|
180
|
+
let param = `Pattern: \`${toolInput.pattern}\``;
|
|
181
|
+
if (toolInput.path) {
|
|
182
|
+
param += ` in ${toolInput.path}`;
|
|
183
|
+
}
|
|
184
|
+
if (toolInput.glob) {
|
|
185
|
+
param += ` (${toolInput.glob})`;
|
|
186
|
+
}
|
|
187
|
+
if (toolInput.type) {
|
|
188
|
+
param += ` [${toolInput.type} files]`;
|
|
189
|
+
}
|
|
190
|
+
return param;
|
|
191
|
+
}
|
|
192
|
+
break;
|
|
193
|
+
case "Glob":
|
|
194
|
+
case "↪ Glob":
|
|
195
|
+
if (toolInput.pattern) {
|
|
196
|
+
let param = `Pattern: \`${toolInput.pattern}\``;
|
|
197
|
+
if (toolInput.path) {
|
|
198
|
+
param += ` in ${toolInput.path}`;
|
|
199
|
+
}
|
|
200
|
+
return param;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
case "Task":
|
|
204
|
+
case "↪ Task":
|
|
205
|
+
if (toolInput.description) {
|
|
206
|
+
return toolInput.description;
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
case "WebFetch":
|
|
210
|
+
case "↪ WebFetch":
|
|
211
|
+
if (toolInput.url) {
|
|
212
|
+
return toolInput.url;
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
case "WebSearch":
|
|
216
|
+
case "↪ WebSearch":
|
|
217
|
+
if (toolInput.query) {
|
|
218
|
+
return `Query: ${toolInput.query}`;
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
case "NotebookEdit":
|
|
222
|
+
case "↪ NotebookEdit":
|
|
223
|
+
if (toolInput.notebook_path) {
|
|
224
|
+
let param = toolInput.notebook_path;
|
|
225
|
+
if (toolInput.cell_id) {
|
|
226
|
+
param += ` (cell ${toolInput.cell_id})`;
|
|
227
|
+
}
|
|
228
|
+
return param;
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
default:
|
|
232
|
+
// For MCP tools or other unknown tools, try to extract meaningful info
|
|
233
|
+
if (toolName.startsWith("mcp__")) {
|
|
234
|
+
// Extract key fields that are commonly meaningful
|
|
235
|
+
const meaningfulFields = [
|
|
236
|
+
"query",
|
|
237
|
+
"id",
|
|
238
|
+
"issueId",
|
|
239
|
+
"title",
|
|
240
|
+
"name",
|
|
241
|
+
"path",
|
|
242
|
+
"file",
|
|
243
|
+
];
|
|
244
|
+
for (const field of meaningfulFields) {
|
|
245
|
+
if (toolInput[field]) {
|
|
246
|
+
return `${field}: ${toolInput[field]}`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
// Fallback to JSON but make it compact
|
|
253
|
+
return JSON.stringify(toolInput);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error("[AgentSessionManager] Failed to format tool parameter:", error);
|
|
257
|
+
return JSON.stringify(toolInput);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Format tool action name with description for Bash tool
|
|
262
|
+
* Puts the description in round brackets after the tool name in the action field
|
|
263
|
+
*/
|
|
264
|
+
formatToolActionName(toolName, toolInput, isError) {
|
|
265
|
+
// Handle Bash tool with description
|
|
266
|
+
if (toolName === "Bash" || toolName === "↪ Bash") {
|
|
267
|
+
// Check if toolInput has a description field
|
|
268
|
+
if (toolInput &&
|
|
269
|
+
typeof toolInput === "object" &&
|
|
270
|
+
"description" in toolInput &&
|
|
271
|
+
toolInput.description) {
|
|
272
|
+
const baseName = isError ? `${toolName} (Error)` : toolName;
|
|
273
|
+
return `${baseName} (${toolInput.description})`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Default formatting for other tools or Bash without description
|
|
277
|
+
return isError ? `${toolName} (Error)` : toolName;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Format tool result for display in Linear agent activities
|
|
281
|
+
* Converts raw tool results into formatted Markdown
|
|
282
|
+
*/
|
|
283
|
+
formatToolResult(toolName, toolInput, result, isError) {
|
|
284
|
+
// If there's an error, wrap in error formatting
|
|
285
|
+
if (isError) {
|
|
286
|
+
return `\`\`\`\n${result}\n\`\`\``;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
switch (toolName) {
|
|
290
|
+
case "Bash":
|
|
291
|
+
case "↪ Bash": {
|
|
292
|
+
// Show command first if not already in parameter
|
|
293
|
+
let formatted = "";
|
|
294
|
+
if (toolInput.command && !toolInput.description) {
|
|
295
|
+
formatted += `\`\`\`bash\n${toolInput.command}\n\`\`\`\n\n`;
|
|
296
|
+
}
|
|
297
|
+
// Then show output
|
|
298
|
+
if (result?.trim()) {
|
|
299
|
+
formatted += `\`\`\`\n${result}\n\`\`\``;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
formatted += "*No output*";
|
|
303
|
+
}
|
|
304
|
+
return formatted;
|
|
305
|
+
}
|
|
306
|
+
case "Read":
|
|
307
|
+
case "↪ Read":
|
|
308
|
+
// For Read, the result is file content - use code block
|
|
309
|
+
if (result?.trim()) {
|
|
310
|
+
// Clean up the result: remove line numbers and system-reminder tags
|
|
311
|
+
let cleanedResult = result;
|
|
312
|
+
// Remove line numbers (format: " 123→")
|
|
313
|
+
cleanedResult = cleanedResult.replace(/^\s*\d+→/gm, "");
|
|
314
|
+
// Remove system-reminder blocks
|
|
315
|
+
cleanedResult = cleanedResult.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
|
|
316
|
+
// Trim only blank lines (not horizontal whitespace) to preserve indentation
|
|
317
|
+
cleanedResult = cleanedResult
|
|
318
|
+
.replace(/^\n+/, "")
|
|
319
|
+
.replace(/\n+$/, "");
|
|
320
|
+
// Try to detect language from file extension
|
|
321
|
+
let lang = "";
|
|
322
|
+
if (toolInput.file_path) {
|
|
323
|
+
const ext = toolInput.file_path.split(".").pop()?.toLowerCase();
|
|
324
|
+
const langMap = {
|
|
325
|
+
ts: "typescript",
|
|
326
|
+
tsx: "typescript",
|
|
327
|
+
js: "javascript",
|
|
328
|
+
jsx: "javascript",
|
|
329
|
+
py: "python",
|
|
330
|
+
rb: "ruby",
|
|
331
|
+
go: "go",
|
|
332
|
+
rs: "rust",
|
|
333
|
+
java: "java",
|
|
334
|
+
c: "c",
|
|
335
|
+
cpp: "cpp",
|
|
336
|
+
cs: "csharp",
|
|
337
|
+
php: "php",
|
|
338
|
+
swift: "swift",
|
|
339
|
+
kt: "kotlin",
|
|
340
|
+
scala: "scala",
|
|
341
|
+
sh: "bash",
|
|
342
|
+
bash: "bash",
|
|
343
|
+
zsh: "bash",
|
|
344
|
+
yml: "yaml",
|
|
345
|
+
yaml: "yaml",
|
|
346
|
+
json: "json",
|
|
347
|
+
xml: "xml",
|
|
348
|
+
html: "html",
|
|
349
|
+
css: "css",
|
|
350
|
+
scss: "scss",
|
|
351
|
+
md: "markdown",
|
|
352
|
+
sql: "sql",
|
|
353
|
+
};
|
|
354
|
+
lang = langMap[ext || ""] || "";
|
|
355
|
+
}
|
|
356
|
+
return `\`\`\`${lang}\n${cleanedResult}\n\`\`\``;
|
|
357
|
+
}
|
|
358
|
+
return "*Empty file*";
|
|
359
|
+
case "Edit":
|
|
360
|
+
case "↪ Edit": {
|
|
361
|
+
// For Edit, show changes as a diff
|
|
362
|
+
// Extract old_string and new_string from toolInput
|
|
363
|
+
if (toolInput.old_string && toolInput.new_string) {
|
|
364
|
+
// Format as a unified diff
|
|
365
|
+
const oldLines = toolInput.old_string.split("\n");
|
|
366
|
+
const newLines = toolInput.new_string.split("\n");
|
|
367
|
+
let diff = "```diff\n";
|
|
368
|
+
// Add context lines before changes (show all old lines with - prefix)
|
|
369
|
+
for (const line of oldLines) {
|
|
370
|
+
diff += `-${line}\n`;
|
|
371
|
+
}
|
|
372
|
+
// Add new lines with + prefix
|
|
373
|
+
for (const line of newLines) {
|
|
374
|
+
diff += `+${line}\n`;
|
|
375
|
+
}
|
|
376
|
+
diff += "```";
|
|
377
|
+
return diff;
|
|
378
|
+
}
|
|
379
|
+
// Fallback to result if old/new strings not available
|
|
380
|
+
if (result?.trim()) {
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
return "*Edit completed*";
|
|
384
|
+
}
|
|
385
|
+
case "Write":
|
|
386
|
+
case "↪ Write":
|
|
387
|
+
// For Write, just confirm
|
|
388
|
+
if (result?.trim()) {
|
|
389
|
+
return result; // In case there's an error or message
|
|
390
|
+
}
|
|
391
|
+
return "*File written successfully*";
|
|
392
|
+
case "Grep":
|
|
393
|
+
case "↪ Grep": {
|
|
394
|
+
// Format grep results
|
|
395
|
+
if (result?.trim()) {
|
|
396
|
+
const lines = result.split("\n");
|
|
397
|
+
// If it looks like file paths (files_with_matches mode)
|
|
398
|
+
if (lines.length > 0 &&
|
|
399
|
+
lines[0] &&
|
|
400
|
+
!lines[0].includes(":") &&
|
|
401
|
+
lines[0].trim().length > 0) {
|
|
402
|
+
return `Found ${lines.filter((l) => l.trim()).length} matching files:\n\`\`\`\n${result}\n\`\`\``;
|
|
403
|
+
}
|
|
404
|
+
// Otherwise it's content matches
|
|
405
|
+
return `\`\`\`\n${result}\n\`\`\``;
|
|
406
|
+
}
|
|
407
|
+
return "*No matches found*";
|
|
408
|
+
}
|
|
409
|
+
case "Glob":
|
|
410
|
+
case "↪ Glob": {
|
|
411
|
+
if (result?.trim()) {
|
|
412
|
+
const lines = result.split("\n").filter((l) => l.trim());
|
|
413
|
+
return `Found ${lines.length} matching files:\n\`\`\`\n${result}\n\`\`\``;
|
|
414
|
+
}
|
|
415
|
+
return "*No files found*";
|
|
416
|
+
}
|
|
417
|
+
case "Task":
|
|
418
|
+
case "↪ Task":
|
|
419
|
+
// Task results can be complex - keep as is but in code block if multiline
|
|
420
|
+
if (result?.trim()) {
|
|
421
|
+
if (result.includes("\n")) {
|
|
422
|
+
return `\`\`\`\n${result}\n\`\`\``;
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
return "*Task completed*";
|
|
427
|
+
case "WebFetch":
|
|
428
|
+
case "↪ WebFetch":
|
|
429
|
+
case "WebSearch":
|
|
430
|
+
case "↪ WebSearch":
|
|
431
|
+
// Web results are usually formatted, keep as is
|
|
432
|
+
return result || "*No results*";
|
|
433
|
+
default:
|
|
434
|
+
// For unknown tools, use code block if result has multiple lines
|
|
435
|
+
if (result?.trim()) {
|
|
436
|
+
if (result.includes("\n") && result.length > 100) {
|
|
437
|
+
return `\`\`\`\n${result}\n\`\`\``;
|
|
438
|
+
}
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
return "*Completed*";
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
console.error("[AgentSessionManager] Failed to format tool result:", error);
|
|
446
|
+
return result || "";
|
|
447
|
+
}
|
|
448
|
+
}
|
|
136
449
|
/**
|
|
137
450
|
* Complete a session from Claude result message
|
|
138
451
|
*/
|
|
@@ -148,8 +461,8 @@ export class AgentSessionManager {
|
|
|
148
461
|
// Note: We should ideally track by session, but for now clearing all is safer
|
|
149
462
|
// to prevent memory leaks
|
|
150
463
|
const status = resultMessage.subtype === "success"
|
|
151
|
-
?
|
|
152
|
-
:
|
|
464
|
+
? AgentSessionStatus.Complete
|
|
465
|
+
: AgentSessionStatus.Error;
|
|
153
466
|
// Update session status and metadata
|
|
154
467
|
await this.updateSessionStatus(linearAgentActivitySessionId, status, {
|
|
155
468
|
totalCostUsd: resultMessage.total_cost_usd,
|
|
@@ -322,7 +635,7 @@ export class AgentSessionManager {
|
|
|
322
635
|
catch (error) {
|
|
323
636
|
console.error(`[AgentSessionManager] Error handling message:`, error);
|
|
324
637
|
// Mark session as error state
|
|
325
|
-
await this.updateSessionStatus(linearAgentActivitySessionId,
|
|
638
|
+
await this.updateSessionStatus(linearAgentActivitySessionId, AgentSessionStatus.Error);
|
|
326
639
|
}
|
|
327
640
|
}
|
|
328
641
|
/**
|
|
@@ -491,17 +804,16 @@ export class AgentSessionManager {
|
|
|
491
804
|
if (toolName === "TodoWrite" || toolName === "↪ TodoWrite") {
|
|
492
805
|
return;
|
|
493
806
|
}
|
|
494
|
-
// Format
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const wrappedResult = toolResult.content?.trim() || "";
|
|
807
|
+
// Format parameter and result using our formatters
|
|
808
|
+
const formattedParameter = this.formatToolParameter(toolName, toolInput);
|
|
809
|
+
const formattedResult = this.formatToolResult(toolName, toolInput, toolResult.content?.trim() || "", toolResult.isError);
|
|
810
|
+
// Format the action name (with description for Bash tool)
|
|
811
|
+
const formattedAction = this.formatToolActionName(toolName, toolInput, toolResult.isError);
|
|
500
812
|
content = {
|
|
501
813
|
type: "action",
|
|
502
|
-
action:
|
|
503
|
-
parameter:
|
|
504
|
-
result:
|
|
814
|
+
action: formattedAction,
|
|
815
|
+
parameter: formattedParameter,
|
|
816
|
+
result: formattedResult,
|
|
505
817
|
};
|
|
506
818
|
}
|
|
507
819
|
else {
|
|
@@ -544,7 +856,8 @@ export class AgentSessionManager {
|
|
|
544
856
|
}
|
|
545
857
|
else if (toolName === "Task") {
|
|
546
858
|
// Special handling for Task tool - add start marker and track active task
|
|
547
|
-
const
|
|
859
|
+
const toolInput = entry.metadata.toolInput || entry.content;
|
|
860
|
+
const formattedParameter = this.formatToolParameter(toolName, toolInput);
|
|
548
861
|
const displayName = toolName;
|
|
549
862
|
// Track this as the active Task for this session
|
|
550
863
|
if (entry.metadata?.toolUseId) {
|
|
@@ -553,7 +866,7 @@ export class AgentSessionManager {
|
|
|
553
866
|
content = {
|
|
554
867
|
type: "action",
|
|
555
868
|
action: displayName,
|
|
556
|
-
parameter:
|
|
869
|
+
parameter: formattedParameter,
|
|
557
870
|
// result will be added later when we get tool result
|
|
558
871
|
};
|
|
559
872
|
// Task is not ephemeral
|
|
@@ -561,7 +874,7 @@ export class AgentSessionManager {
|
|
|
561
874
|
}
|
|
562
875
|
else {
|
|
563
876
|
// Other tools - check if they're within an active Task
|
|
564
|
-
const
|
|
877
|
+
const toolInput = entry.metadata.toolInput || entry.content;
|
|
565
878
|
let displayName = toolName;
|
|
566
879
|
if (entry.metadata?.parentToolUseId) {
|
|
567
880
|
const activeTaskId = this.activeTasksBySession.get(linearAgentActivitySessionId);
|
|
@@ -569,10 +882,11 @@ export class AgentSessionManager {
|
|
|
569
882
|
displayName = `↪ ${toolName}`;
|
|
570
883
|
}
|
|
571
884
|
}
|
|
885
|
+
const formattedParameter = this.formatToolParameter(displayName, toolInput);
|
|
572
886
|
content = {
|
|
573
887
|
type: "action",
|
|
574
888
|
action: displayName,
|
|
575
|
-
parameter:
|
|
889
|
+
parameter: formattedParameter,
|
|
576
890
|
// result will be added later when we get tool result
|
|
577
891
|
};
|
|
578
892
|
// Standard tool calls are ephemeral
|
|
@@ -632,7 +946,7 @@ export class AgentSessionManager {
|
|
|
632
946
|
content,
|
|
633
947
|
...(ephemeral && { ephemeral: true }),
|
|
634
948
|
};
|
|
635
|
-
const result = await this.
|
|
949
|
+
const result = await this.issueTracker.createAgentActivity(activityInput);
|
|
636
950
|
if (result.success && result.agentActivity) {
|
|
637
951
|
const agentActivity = await result.agentActivity;
|
|
638
952
|
entry.linearAgentActivityId = agentActivity.id;
|
|
@@ -662,7 +976,7 @@ export class AgentSessionManager {
|
|
|
662
976
|
* Get all active sessions
|
|
663
977
|
*/
|
|
664
978
|
getActiveSessions() {
|
|
665
|
-
return Array.from(this.sessions.values()).filter((session) => session.status ===
|
|
979
|
+
return Array.from(this.sessions.values()).filter((session) => session.status === AgentSessionStatus.Active);
|
|
666
980
|
}
|
|
667
981
|
/**
|
|
668
982
|
* Add or update ClaudeRunner for a session
|
|
@@ -705,7 +1019,7 @@ export class AgentSessionManager {
|
|
|
705
1019
|
*/
|
|
706
1020
|
getActiveSessionsByIssueId(issueId) {
|
|
707
1021
|
return Array.from(this.sessions.values()).filter((session) => session.issueId === issueId &&
|
|
708
|
-
session.status ===
|
|
1022
|
+
session.status === AgentSessionStatus.Active);
|
|
709
1023
|
}
|
|
710
1024
|
/**
|
|
711
1025
|
* Get all sessions
|
|
@@ -737,7 +1051,7 @@ export class AgentSessionManager {
|
|
|
737
1051
|
return;
|
|
738
1052
|
}
|
|
739
1053
|
try {
|
|
740
|
-
const result = await this.
|
|
1054
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
741
1055
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
742
1056
|
content: {
|
|
743
1057
|
type: "thought",
|
|
@@ -773,7 +1087,7 @@ export class AgentSessionManager {
|
|
|
773
1087
|
if (result !== undefined) {
|
|
774
1088
|
content.result = result;
|
|
775
1089
|
}
|
|
776
|
-
const response = await this.
|
|
1090
|
+
const response = await this.issueTracker.createAgentActivity({
|
|
777
1091
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
778
1092
|
content,
|
|
779
1093
|
});
|
|
@@ -798,7 +1112,7 @@ export class AgentSessionManager {
|
|
|
798
1112
|
return;
|
|
799
1113
|
}
|
|
800
1114
|
try {
|
|
801
|
-
const result = await this.
|
|
1115
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
802
1116
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
803
1117
|
content: {
|
|
804
1118
|
type: "response",
|
|
@@ -826,7 +1140,7 @@ export class AgentSessionManager {
|
|
|
826
1140
|
return;
|
|
827
1141
|
}
|
|
828
1142
|
try {
|
|
829
|
-
const result = await this.
|
|
1143
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
830
1144
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
831
1145
|
content: {
|
|
832
1146
|
type: "error",
|
|
@@ -854,7 +1168,7 @@ export class AgentSessionManager {
|
|
|
854
1168
|
return;
|
|
855
1169
|
}
|
|
856
1170
|
try {
|
|
857
|
-
const result = await this.
|
|
1171
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
858
1172
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
859
1173
|
content: {
|
|
860
1174
|
type: "elicitation",
|
|
@@ -882,13 +1196,13 @@ export class AgentSessionManager {
|
|
|
882
1196
|
return;
|
|
883
1197
|
}
|
|
884
1198
|
try {
|
|
885
|
-
const result = await this.
|
|
1199
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
886
1200
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
887
1201
|
content: {
|
|
888
1202
|
type: "elicitation",
|
|
889
1203
|
body,
|
|
890
1204
|
},
|
|
891
|
-
signal:
|
|
1205
|
+
signal: AgentActivitySignal.Auth,
|
|
892
1206
|
signalMetadata: {
|
|
893
1207
|
url: approvalUrl,
|
|
894
1208
|
},
|
|
@@ -966,7 +1280,7 @@ export class AgentSessionManager {
|
|
|
966
1280
|
*/
|
|
967
1281
|
async postModelNotificationThought(linearAgentActivitySessionId, model) {
|
|
968
1282
|
try {
|
|
969
|
-
const result = await this.
|
|
1283
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
970
1284
|
agentSessionId: linearAgentActivitySessionId,
|
|
971
1285
|
content: {
|
|
972
1286
|
type: "thought",
|
|
@@ -989,7 +1303,7 @@ export class AgentSessionManager {
|
|
|
989
1303
|
*/
|
|
990
1304
|
async postRoutingThought(linearAgentActivitySessionId) {
|
|
991
1305
|
try {
|
|
992
|
-
const result = await this.
|
|
1306
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
993
1307
|
agentSessionId: linearAgentActivitySessionId,
|
|
994
1308
|
content: {
|
|
995
1309
|
type: "thought",
|
|
@@ -1017,7 +1331,7 @@ export class AgentSessionManager {
|
|
|
1017
1331
|
*/
|
|
1018
1332
|
async postProcedureSelectionThought(linearAgentActivitySessionId, procedureName, classification) {
|
|
1019
1333
|
try {
|
|
1020
|
-
const result = await this.
|
|
1334
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
1021
1335
|
agentSessionId: linearAgentActivitySessionId,
|
|
1022
1336
|
content: {
|
|
1023
1337
|
type: "thought",
|
|
@@ -1048,7 +1362,7 @@ export class AgentSessionManager {
|
|
|
1048
1362
|
try {
|
|
1049
1363
|
if (message.status === "compacting") {
|
|
1050
1364
|
// Create an ephemeral thought for the compacting status
|
|
1051
|
-
const result = await this.
|
|
1365
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
1052
1366
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
1053
1367
|
content: {
|
|
1054
1368
|
type: "thought",
|
|
@@ -1068,7 +1382,7 @@ export class AgentSessionManager {
|
|
|
1068
1382
|
}
|
|
1069
1383
|
else if (message.status === null) {
|
|
1070
1384
|
// Clear the status - post a non-ephemeral thought to replace the ephemeral one
|
|
1071
|
-
const result = await this.
|
|
1385
|
+
const result = await this.issueTracker.createAgentActivity({
|
|
1072
1386
|
agentSessionId: session.linearAgentActivitySessionId,
|
|
1073
1387
|
content: {
|
|
1074
1388
|
type: "thought",
|