cyrus-edge-worker 0.0.37 → 0.0.38
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 +26 -1
- package/dist/AgentSessionManager.d.ts.map +1 -1
- package/dist/AgentSessionManager.js +227 -30
- package/dist/AgentSessionManager.js.map +1 -1
- package/dist/EdgeWorker.d.ts +4 -3
- package/dist/EdgeWorker.d.ts.map +1 -1
- package/dist/EdgeWorker.js +191 -25
- package/dist/EdgeWorker.js.map +1 -1
- package/dist/SharedApplicationServer.d.ts +29 -4
- package/dist/SharedApplicationServer.d.ts.map +1 -1
- package/dist/SharedApplicationServer.js +262 -0
- package/dist/SharedApplicationServer.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/procedures/ProcedureRouter.d.ts +60 -0
- package/dist/procedures/ProcedureRouter.d.ts.map +1 -0
- package/dist/procedures/ProcedureRouter.js +201 -0
- package/dist/procedures/ProcedureRouter.js.map +1 -0
- package/dist/procedures/index.d.ts +7 -0
- package/dist/procedures/index.d.ts.map +1 -0
- package/dist/procedures/index.js +7 -0
- package/dist/procedures/index.js.map +1 -0
- package/dist/procedures/registry.d.ts +76 -0
- package/dist/procedures/registry.d.ts.map +1 -0
- package/dist/procedures/registry.js +130 -0
- package/dist/procedures/registry.js.map +1 -0
- package/dist/procedures/types.d.ts +64 -0
- package/dist/procedures/types.d.ts.map +1 -0
- package/dist/procedures/types.js +5 -0
- package/dist/procedures/types.js.map +1 -0
- package/dist/prompts/subroutines/concise-summary.md +53 -0
- package/dist/prompts/subroutines/debugger-fix.md +108 -0
- package/dist/prompts/subroutines/debugger-reproduction.md +106 -0
- package/dist/prompts/subroutines/get-approval.md +175 -0
- package/dist/prompts/subroutines/git-gh.md +52 -0
- package/dist/prompts/subroutines/verbose-summary.md +46 -0
- package/dist/prompts/subroutines/verifications.md +46 -0
- package/dist/types.d.ts +0 -97
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -6
- package/prompt-template-v2.md +3 -19
- package/prompts/builder.md +1 -23
- package/prompts/debugger.md +11 -174
- package/prompts/orchestrator.md +41 -64
package/dist/EdgeWorker.js
CHANGED
|
@@ -4,14 +4,14 @@ import { basename, dirname, extname, join, resolve } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { LinearClient, } from "@linear/sdk";
|
|
6
6
|
import { watch as chokidarWatch } from "chokidar";
|
|
7
|
-
import { ClaudeRunner, createCyrusToolsServer, getAllTools, getCoordinatorTools, getReadOnlyTools, getSafeTools, } from "cyrus-claude-runner";
|
|
7
|
+
import { ClaudeRunner, createCyrusToolsServer, createImageToolsServer, createSoraToolsServer, getAllTools, getCoordinatorTools, getReadOnlyTools, getSafeTools, } from "cyrus-claude-runner";
|
|
8
8
|
import { isAgentSessionCreatedWebhook, isAgentSessionPromptedWebhook, isIssueAssignedWebhook, isIssueCommentMentionWebhook, isIssueNewCommentWebhook, isIssueUnassignedWebhook, PersistenceManager, } from "cyrus-core";
|
|
9
9
|
import { LinearWebhookClient } from "cyrus-linear-webhook-client";
|
|
10
10
|
import { NdjsonClient } from "cyrus-ndjson-client";
|
|
11
11
|
import { fileTypeFromBuffer } from "file-type";
|
|
12
12
|
import { AgentSessionManager } from "./AgentSessionManager.js";
|
|
13
|
+
import { ProcedureRouter, } from "./procedures/index.js";
|
|
13
14
|
import { SharedApplicationServer } from "./SharedApplicationServer.js";
|
|
14
|
-
const LAST_MESSAGE_MARKER = "\n\nIMPORTANT: When providing your final summary response, include the special marker ___LAST_MESSAGE_MARKER___ at the very beginning of your message. This marker will be automatically removed before posting.";
|
|
15
15
|
/**
|
|
16
16
|
* Unified edge worker that **orchestrates**
|
|
17
17
|
* capturing Linear webhooks,
|
|
@@ -28,6 +28,7 @@ export class EdgeWorker extends EventEmitter {
|
|
|
28
28
|
sharedApplicationServer;
|
|
29
29
|
cyrusHome;
|
|
30
30
|
childToParentAgentSession = new Map(); // Maps child agentSessionId to parent agentSessionId
|
|
31
|
+
procedureRouter; // Intelligent workflow routing
|
|
31
32
|
configWatcher; // File watcher for config.json
|
|
32
33
|
configPath; // Path to config.json file
|
|
33
34
|
tokenToRepoIds = new Map(); // Maps Linear token to repository IDs using that token
|
|
@@ -36,6 +37,12 @@ export class EdgeWorker extends EventEmitter {
|
|
|
36
37
|
this.config = config;
|
|
37
38
|
this.cyrusHome = config.cyrusHome;
|
|
38
39
|
this.persistenceManager = new PersistenceManager(join(this.cyrusHome, "state"));
|
|
40
|
+
// Initialize procedure router with haiku model for fast classification
|
|
41
|
+
this.procedureRouter = new ProcedureRouter({
|
|
42
|
+
cyrusHome: this.cyrusHome,
|
|
43
|
+
model: "haiku",
|
|
44
|
+
timeoutMs: 10000,
|
|
45
|
+
});
|
|
39
46
|
console.log(`[EdgeWorker Constructor] Initializing parent-child session mapping system`);
|
|
40
47
|
console.log(`[EdgeWorker Constructor] Parent-child mapping initialized with 0 entries`);
|
|
41
48
|
// Initialize shared application server
|
|
@@ -72,7 +79,47 @@ export class EdgeWorker extends EventEmitter {
|
|
|
72
79
|
return parentId;
|
|
73
80
|
}, async (parentSessionId, prompt, childSessionId) => {
|
|
74
81
|
await this.handleResumeParentSession(parentSessionId, prompt, childSessionId, repo, agentSessionManager);
|
|
75
|
-
})
|
|
82
|
+
}, async (linearAgentActivitySessionId) => {
|
|
83
|
+
console.log(`[Subroutine Transition] Advancing to next subroutine for session ${linearAgentActivitySessionId}`);
|
|
84
|
+
// Get the session
|
|
85
|
+
const session = agentSessionManager.getSession(linearAgentActivitySessionId);
|
|
86
|
+
if (!session) {
|
|
87
|
+
console.error(`[Subroutine Transition] Session ${linearAgentActivitySessionId} not found`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Get next subroutine (advancement already handled by AgentSessionManager)
|
|
91
|
+
const nextSubroutine = this.procedureRouter.getCurrentSubroutine(session);
|
|
92
|
+
if (!nextSubroutine) {
|
|
93
|
+
console.log(`[Subroutine Transition] Procedure complete for session ${linearAgentActivitySessionId}`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`[Subroutine Transition] Next subroutine: ${nextSubroutine.name}`);
|
|
97
|
+
// Load subroutine prompt
|
|
98
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
99
|
+
const __dirname = dirname(__filename);
|
|
100
|
+
const subroutinePromptPath = join(__dirname, "prompts", nextSubroutine.promptPath);
|
|
101
|
+
let subroutinePrompt;
|
|
102
|
+
try {
|
|
103
|
+
subroutinePrompt = await readFile(subroutinePromptPath, "utf-8");
|
|
104
|
+
console.log(`[Subroutine Transition] Loaded ${nextSubroutine.name} subroutine prompt (${subroutinePrompt.length} characters)`);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(`[Subroutine Transition] Failed to load subroutine prompt from ${subroutinePromptPath}:`, error);
|
|
108
|
+
// Fallback to simple prompt
|
|
109
|
+
subroutinePrompt = `Continue with: ${nextSubroutine.description}`;
|
|
110
|
+
}
|
|
111
|
+
// Resume Claude session with subroutine prompt
|
|
112
|
+
try {
|
|
113
|
+
await this.resumeClaudeSession(session, repo, linearAgentActivitySessionId, agentSessionManager, subroutinePrompt, "", // No attachment manifest
|
|
114
|
+
false, // Not a new session
|
|
115
|
+
[], // No additional allowed directories
|
|
116
|
+
nextSubroutine.maxTurns);
|
|
117
|
+
console.log(`[Subroutine Transition] Successfully resumed session for ${nextSubroutine.name} subroutine${nextSubroutine.maxTurns ? ` (maxTurns=${nextSubroutine.maxTurns})` : ""}`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error(`[Subroutine Transition] Failed to resume session for ${nextSubroutine.name} subroutine:`, error);
|
|
121
|
+
}
|
|
122
|
+
}, this.procedureRouter, this.sharedApplicationServer);
|
|
76
123
|
this.agentSessionManagers.set(repo.id, agentSessionManager);
|
|
77
124
|
}
|
|
78
125
|
}
|
|
@@ -461,7 +508,8 @@ export class EdgeWorker extends EventEmitter {
|
|
|
461
508
|
return this.childToParentAgentSession.get(childSessionId);
|
|
462
509
|
}, async (parentSessionId, prompt, childSessionId) => {
|
|
463
510
|
await this.handleResumeParentSession(parentSessionId, prompt, childSessionId, repo, agentSessionManager);
|
|
464
|
-
}
|
|
511
|
+
}, undefined, // No resumeNextSubroutine callback for dynamically added repos
|
|
512
|
+
this.procedureRouter, this.sharedApplicationServer);
|
|
465
513
|
this.agentSessionManagers.set(repo.id, agentSessionManager);
|
|
466
514
|
// Update token-to-repo mapping
|
|
467
515
|
const repoIds = this.tokenToRepoIds.get(repo.linearToken) || [];
|
|
@@ -1002,8 +1050,62 @@ export class EdgeWorker extends EventEmitter {
|
|
|
1002
1050
|
const sessionData = await this.createLinearAgentSession(linearAgentActivitySessionId, issue, repository, agentSessionManager);
|
|
1003
1051
|
// Destructure the session data (excluding allowedTools which we'll build with promptType)
|
|
1004
1052
|
const { session, fullIssue, workspace: _workspace, attachmentResult, attachmentsDir: _attachmentsDir, allowedDirectories, } = sessionData;
|
|
1005
|
-
//
|
|
1053
|
+
// Initialize procedure metadata using intelligent routing
|
|
1054
|
+
if (!session.metadata) {
|
|
1055
|
+
session.metadata = {};
|
|
1056
|
+
}
|
|
1057
|
+
// Post ephemeral "Routing..." thought
|
|
1058
|
+
await agentSessionManager.postRoutingThought(linearAgentActivitySessionId);
|
|
1059
|
+
// Fetch labels early (needed for label override check)
|
|
1006
1060
|
const labels = await this.fetchIssueLabels(fullIssue);
|
|
1061
|
+
// Check for label overrides BEFORE AI routing
|
|
1062
|
+
const debuggerConfig = repository.labelPrompts?.debugger;
|
|
1063
|
+
const debuggerLabels = Array.isArray(debuggerConfig)
|
|
1064
|
+
? debuggerConfig
|
|
1065
|
+
: debuggerConfig?.labels;
|
|
1066
|
+
const hasDebuggerLabel = debuggerLabels?.some((label) => labels.includes(label));
|
|
1067
|
+
const orchestratorConfig = repository.labelPrompts?.orchestrator;
|
|
1068
|
+
const orchestratorLabels = Array.isArray(orchestratorConfig)
|
|
1069
|
+
? orchestratorConfig
|
|
1070
|
+
: orchestratorConfig?.labels;
|
|
1071
|
+
const hasOrchestratorLabel = orchestratorLabels?.some((label) => labels.includes(label));
|
|
1072
|
+
let finalProcedure;
|
|
1073
|
+
let finalClassification;
|
|
1074
|
+
// If labels indicate a specific procedure, use that instead of AI routing
|
|
1075
|
+
if (hasDebuggerLabel) {
|
|
1076
|
+
const debuggerProcedure = this.procedureRouter.getProcedure("debugger-full");
|
|
1077
|
+
if (!debuggerProcedure) {
|
|
1078
|
+
throw new Error("debugger-full procedure not found in registry");
|
|
1079
|
+
}
|
|
1080
|
+
finalProcedure = debuggerProcedure;
|
|
1081
|
+
finalClassification = "debugger";
|
|
1082
|
+
console.log(`[EdgeWorker] Using debugger-full procedure due to debugger label (skipping AI routing)`);
|
|
1083
|
+
}
|
|
1084
|
+
else if (hasOrchestratorLabel) {
|
|
1085
|
+
const orchestratorProcedure = this.procedureRouter.getProcedure("orchestrator-full");
|
|
1086
|
+
if (!orchestratorProcedure) {
|
|
1087
|
+
throw new Error("orchestrator-full procedure not found in registry");
|
|
1088
|
+
}
|
|
1089
|
+
finalProcedure = orchestratorProcedure;
|
|
1090
|
+
finalClassification = "orchestrator";
|
|
1091
|
+
console.log(`[EdgeWorker] Using orchestrator-full procedure due to orchestrator label (skipping AI routing)`);
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
// No label override - use AI routing
|
|
1095
|
+
const issueDescription = `${issue.title}\n\n${fullIssue.description || ""}`.trim();
|
|
1096
|
+
const routingDecision = await this.procedureRouter.determineRoutine(issueDescription);
|
|
1097
|
+
finalProcedure = routingDecision.procedure;
|
|
1098
|
+
finalClassification = routingDecision.classification;
|
|
1099
|
+
// Log AI routing decision
|
|
1100
|
+
console.log(`[EdgeWorker] AI routing decision for ${linearAgentActivitySessionId}:`);
|
|
1101
|
+
console.log(` Classification: ${routingDecision.classification}`);
|
|
1102
|
+
console.log(` Procedure: ${finalProcedure.name}`);
|
|
1103
|
+
console.log(` Reasoning: ${routingDecision.reasoning}`);
|
|
1104
|
+
}
|
|
1105
|
+
// Initialize procedure metadata in session with final decision
|
|
1106
|
+
this.procedureRouter.initializeProcedureMetadata(session, finalProcedure);
|
|
1107
|
+
// Post single procedure selection result (replaces ephemeral routing thought)
|
|
1108
|
+
await agentSessionManager.postProcedureSelectionThought(linearAgentActivitySessionId, finalProcedure.name, finalClassification);
|
|
1007
1109
|
// Only determine system prompt for delegation (not mentions) or when /label-based-prompt is requested
|
|
1008
1110
|
let systemPrompt;
|
|
1009
1111
|
let systemPromptVersion;
|
|
@@ -1098,6 +1200,7 @@ export class EdgeWorker extends EventEmitter {
|
|
|
1098
1200
|
}
|
|
1099
1201
|
let session = agentSessionManager.getSession(linearAgentActivitySessionId);
|
|
1100
1202
|
let isNewSession = false;
|
|
1203
|
+
let fullIssue = null;
|
|
1101
1204
|
if (!session) {
|
|
1102
1205
|
console.log(`[EdgeWorker] No existing session found for agent activity session ${linearAgentActivitySessionId}, creating new session`);
|
|
1103
1206
|
isNewSession = true;
|
|
@@ -1106,23 +1209,70 @@ export class EdgeWorker extends EventEmitter {
|
|
|
1106
1209
|
// Create the session using the shared method
|
|
1107
1210
|
const sessionData = await this.createLinearAgentSession(linearAgentActivitySessionId, issue, repository, agentSessionManager);
|
|
1108
1211
|
// Destructure session data for new session
|
|
1109
|
-
|
|
1212
|
+
fullIssue = sessionData.fullIssue;
|
|
1110
1213
|
session = sessionData.session;
|
|
1214
|
+
console.log(`[EdgeWorker] Created new session ${linearAgentActivitySessionId} (prompted webhook)`);
|
|
1111
1215
|
// Save state and emit events for new session
|
|
1112
1216
|
await this.savePersistedState();
|
|
1113
|
-
this.emit("session:started",
|
|
1114
|
-
this.config.handlers?.onSessionStart?.(
|
|
1217
|
+
this.emit("session:started", fullIssue.id, fullIssue, repository.id);
|
|
1218
|
+
this.config.handlers?.onSessionStart?.(fullIssue.id, fullIssue, repository.id);
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
console.log(`[EdgeWorker] Found existing session ${linearAgentActivitySessionId} for new user prompt`);
|
|
1222
|
+
// Post instant acknowledgment for existing session BEFORE any async work
|
|
1223
|
+
// Check streaming status first to determine the message
|
|
1224
|
+
const isCurrentlyStreaming = session?.claudeRunner?.isStreaming() || false;
|
|
1225
|
+
await this.postInstantPromptedAcknowledgment(linearAgentActivitySessionId, repository.id, isCurrentlyStreaming);
|
|
1226
|
+
// Need to fetch full issue for routing context
|
|
1227
|
+
const linearClient = this.linearClients.get(repository.id);
|
|
1228
|
+
if (linearClient) {
|
|
1229
|
+
try {
|
|
1230
|
+
fullIssue = await linearClient.issue(issue.id);
|
|
1231
|
+
}
|
|
1232
|
+
catch (error) {
|
|
1233
|
+
console.warn(`[EdgeWorker] Failed to fetch full issue for routing: ${issue.id}`, error);
|
|
1234
|
+
// Continue with degraded routing context
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// Check if runner is actively streaming before routing
|
|
1239
|
+
const existingRunner = session?.claudeRunner;
|
|
1240
|
+
const isStreaming = existingRunner?.isStreaming() || false;
|
|
1241
|
+
// Always route procedure for new comments, UNLESS actively streaming
|
|
1242
|
+
if (!isStreaming) {
|
|
1243
|
+
// Initialize procedure metadata using intelligent routing
|
|
1244
|
+
if (!session.metadata) {
|
|
1245
|
+
session.metadata = {};
|
|
1246
|
+
}
|
|
1247
|
+
// Post ephemeral "Routing..." thought
|
|
1248
|
+
await agentSessionManager.postRoutingThought(linearAgentActivitySessionId);
|
|
1249
|
+
// For prompted events, use the actual prompt content from the user
|
|
1250
|
+
// Combine with issue context for better routing
|
|
1251
|
+
if (!fullIssue) {
|
|
1252
|
+
console.warn(`[EdgeWorker] Routing without full issue details for ${linearAgentActivitySessionId}`);
|
|
1253
|
+
}
|
|
1254
|
+
const promptBody = webhook.agentActivity.content.body;
|
|
1255
|
+
const routingDecision = await this.procedureRouter.determineRoutine(promptBody.trim());
|
|
1256
|
+
const selectedProcedure = routingDecision.procedure;
|
|
1257
|
+
// Initialize procedure metadata in session (resets for each new comment)
|
|
1258
|
+
this.procedureRouter.initializeProcedureMetadata(session, selectedProcedure);
|
|
1259
|
+
// Post procedure selection result (replaces ephemeral routing thought)
|
|
1260
|
+
await agentSessionManager.postProcedureSelectionThought(linearAgentActivitySessionId, selectedProcedure.name, routingDecision.classification);
|
|
1261
|
+
// Log routing decision
|
|
1262
|
+
console.log(`[EdgeWorker] Routing decision for ${linearAgentActivitySessionId} (prompted webhook, ${isNewSession ? "new" : "existing"} session):`);
|
|
1263
|
+
console.log(` Classification: ${routingDecision.classification}`);
|
|
1264
|
+
console.log(` Procedure: ${selectedProcedure.name}`);
|
|
1265
|
+
console.log(` Reasoning: ${routingDecision.reasoning}`);
|
|
1266
|
+
}
|
|
1267
|
+
else {
|
|
1268
|
+
console.log(`[EdgeWorker] Skipping routing for ${linearAgentActivitySessionId} - runner is actively streaming`);
|
|
1115
1269
|
}
|
|
1116
1270
|
// Ensure session is not null after creation/retrieval
|
|
1117
1271
|
if (!session) {
|
|
1118
1272
|
throw new Error(`Failed to get or create session for agent activity session ${linearAgentActivitySessionId}`);
|
|
1119
1273
|
}
|
|
1120
|
-
//
|
|
1121
|
-
|
|
1122
|
-
if (!isNewSession) {
|
|
1123
|
-
// Only post acknowledgment for existing sessions (new sessions already handled it above)
|
|
1124
|
-
await this.postInstantPromptedAcknowledgment(linearAgentActivitySessionId, repository.id, existingRunner?.isStreaming() || false);
|
|
1125
|
-
}
|
|
1274
|
+
// Acknowledgment already posted above for both new and existing sessions
|
|
1275
|
+
// (before any async routing work to ensure instant user feedback)
|
|
1126
1276
|
// Get Linear client for this repository
|
|
1127
1277
|
const linearClient = this.linearClients.get(repository.id);
|
|
1128
1278
|
if (!linearClient) {
|
|
@@ -1186,7 +1336,6 @@ export class EdgeWorker extends EventEmitter {
|
|
|
1186
1336
|
if (attachmentManifest) {
|
|
1187
1337
|
fullPrompt = `${promptBody}\n\n${attachmentManifest}`;
|
|
1188
1338
|
}
|
|
1189
|
-
fullPrompt = `${fullPrompt}${LAST_MESSAGE_MARKER}`;
|
|
1190
1339
|
existingRunner.addStreamMessage(fullPrompt);
|
|
1191
1340
|
return; // Exit early - comment has been added to stream
|
|
1192
1341
|
}
|
|
@@ -1416,7 +1565,6 @@ export class EdgeWorker extends EventEmitter {
|
|
|
1416
1565
|
console.log(`[EdgeWorker] Adding attachment manifest to label-based prompt, length: ${attachmentManifest.length} characters`);
|
|
1417
1566
|
prompt = `${prompt}\n\n${attachmentManifest}`;
|
|
1418
1567
|
}
|
|
1419
|
-
prompt = `${prompt}${LAST_MESSAGE_MARKER}`;
|
|
1420
1568
|
console.log(`[EdgeWorker] Label-based prompt built successfully, length: ${prompt.length} characters`);
|
|
1421
1569
|
return { prompt, version: templateVersion };
|
|
1422
1570
|
}
|
|
@@ -1460,7 +1608,6 @@ IMPORTANT: You were specifically mentioned in the comment above. Focus on addres
|
|
|
1460
1608
|
if (attachmentManifest) {
|
|
1461
1609
|
prompt = `${prompt}\n\n${attachmentManifest}`;
|
|
1462
1610
|
}
|
|
1463
|
-
prompt = `${prompt}${LAST_MESSAGE_MARKER}`;
|
|
1464
1611
|
return { prompt };
|
|
1465
1612
|
}
|
|
1466
1613
|
catch (error) {
|
|
@@ -1781,7 +1928,6 @@ IMPORTANT: Focus specifically on addressing the new comment above. This is a new
|
|
|
1781
1928
|
console.log(`[EdgeWorker] Adding repository-specific instruction`);
|
|
1782
1929
|
prompt = `${prompt}\n\n<repository-specific-instruction>\n${repository.appendInstruction}\n</repository-specific-instruction>`;
|
|
1783
1930
|
}
|
|
1784
|
-
prompt = `${prompt}${LAST_MESSAGE_MARKER}`;
|
|
1785
1931
|
console.log(`[EdgeWorker] Final prompt length: ${prompt.length} characters`);
|
|
1786
1932
|
return { prompt, version: templateVersion };
|
|
1787
1933
|
}
|
|
@@ -1805,7 +1951,7 @@ Branch: ${issue.branchName}
|
|
|
1805
1951
|
Working directory: ${repository.repositoryPath}
|
|
1806
1952
|
Base branch: ${baseBranch}
|
|
1807
1953
|
|
|
1808
|
-
${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please analyze this issue and help implement a solution
|
|
1954
|
+
${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please analyze this issue and help implement a solution.`;
|
|
1809
1955
|
return { prompt: fallbackPrompt, version: undefined };
|
|
1810
1956
|
}
|
|
1811
1957
|
}
|
|
@@ -2378,6 +2524,20 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2378
2524
|
},
|
|
2379
2525
|
}),
|
|
2380
2526
|
};
|
|
2527
|
+
// Add OpenAI-based MCP servers if API key is configured
|
|
2528
|
+
if (repository.openaiApiKey) {
|
|
2529
|
+
// Sora video generation tools
|
|
2530
|
+
mcpConfig["sora-tools"] = createSoraToolsServer({
|
|
2531
|
+
apiKey: repository.openaiApiKey,
|
|
2532
|
+
outputDirectory: repository.openaiOutputDirectory,
|
|
2533
|
+
});
|
|
2534
|
+
// GPT Image generation tools
|
|
2535
|
+
mcpConfig["image-tools"] = createImageToolsServer({
|
|
2536
|
+
apiKey: repository.openaiApiKey,
|
|
2537
|
+
outputDirectory: repository.openaiOutputDirectory,
|
|
2538
|
+
});
|
|
2539
|
+
console.log(`[EdgeWorker] Configured OpenAI MCP servers (Sora + GPT Image) for repository: ${repository.name}`);
|
|
2540
|
+
}
|
|
2381
2541
|
return mcpConfig;
|
|
2382
2542
|
}
|
|
2383
2543
|
/**
|
|
@@ -2416,13 +2576,13 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2416
2576
|
const manifestSuffix = attachmentManifest
|
|
2417
2577
|
? `\n\n${attachmentManifest}`
|
|
2418
2578
|
: "";
|
|
2419
|
-
return `${promptBody}${manifestSuffix}
|
|
2579
|
+
return `${promptBody}${manifestSuffix}`;
|
|
2420
2580
|
}
|
|
2421
2581
|
}
|
|
2422
2582
|
/**
|
|
2423
2583
|
* Build Claude runner configuration with common settings
|
|
2424
2584
|
*/
|
|
2425
|
-
buildClaudeRunnerConfig(session, repository, linearAgentActivitySessionId, systemPrompt, allowedTools, allowedDirectories, disallowedTools, resumeSessionId, labels) {
|
|
2585
|
+
buildClaudeRunnerConfig(session, repository, linearAgentActivitySessionId, systemPrompt, allowedTools, allowedDirectories, disallowedTools, resumeSessionId, labels, maxTurns) {
|
|
2426
2586
|
// Configure PostToolUse hook for playwright screenshots
|
|
2427
2587
|
const hooks = {
|
|
2428
2588
|
PostToolUse: [
|
|
@@ -2482,7 +2642,7 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2482
2642
|
cyrusHome: this.cyrusHome,
|
|
2483
2643
|
mcpConfigPath: repository.mcpConfigPath,
|
|
2484
2644
|
mcpConfig: this.buildMcpConfig(repository, linearAgentActivitySessionId),
|
|
2485
|
-
appendSystemPrompt:
|
|
2645
|
+
appendSystemPrompt: systemPrompt || "",
|
|
2486
2646
|
// Priority order: label override > repository config > global default
|
|
2487
2647
|
model: modelOverride || repository.model || this.config.defaultModel,
|
|
2488
2648
|
fallbackModel: fallbackModelOverride ||
|
|
@@ -2497,6 +2657,9 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2497
2657
|
if (resumeSessionId) {
|
|
2498
2658
|
config.resumeSessionId = resumeSessionId;
|
|
2499
2659
|
}
|
|
2660
|
+
if (maxTurns !== undefined) {
|
|
2661
|
+
config.maxTurns = maxTurns;
|
|
2662
|
+
}
|
|
2500
2663
|
return config;
|
|
2501
2664
|
}
|
|
2502
2665
|
/**
|
|
@@ -2812,7 +2975,7 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2812
2975
|
* @param attachmentManifest Optional attachment manifest
|
|
2813
2976
|
* @param isNewSession Whether this is a new session
|
|
2814
2977
|
*/
|
|
2815
|
-
async resumeClaudeSession(session, repository, linearAgentActivitySessionId, agentSessionManager, promptBody, attachmentManifest = "", isNewSession = false, additionalAllowedDirectories = []) {
|
|
2978
|
+
async resumeClaudeSession(session, repository, linearAgentActivitySessionId, agentSessionManager, promptBody, attachmentManifest = "", isNewSession = false, additionalAllowedDirectories = [], maxTurns) {
|
|
2816
2979
|
// Check for existing runner
|
|
2817
2980
|
const existingRunner = session.claudeRunner;
|
|
2818
2981
|
// If there's an existing streaming runner, add to it
|
|
@@ -2821,7 +2984,6 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2821
2984
|
if (attachmentManifest) {
|
|
2822
2985
|
fullPrompt = `${promptBody}\n\n${attachmentManifest}`;
|
|
2823
2986
|
}
|
|
2824
|
-
fullPrompt = `${fullPrompt}${LAST_MESSAGE_MARKER}`;
|
|
2825
2987
|
existingRunner.addStreamMessage(fullPrompt);
|
|
2826
2988
|
return;
|
|
2827
2989
|
}
|
|
@@ -2854,7 +3016,11 @@ ${newComment ? `New comment to address:\n${newComment.body}\n\n` : ""}Please ana
|
|
|
2854
3016
|
...additionalAllowedDirectories,
|
|
2855
3017
|
];
|
|
2856
3018
|
// Create runner configuration
|
|
2857
|
-
const
|
|
3019
|
+
const resumeSessionId = needsNewClaudeSession
|
|
3020
|
+
? undefined
|
|
3021
|
+
: session.claudeSessionId;
|
|
3022
|
+
const runnerConfig = this.buildClaudeRunnerConfig(session, repository, linearAgentActivitySessionId, systemPrompt, allowedTools, allowedDirectories, disallowedTools, resumeSessionId, labels, // Pass labels for model override
|
|
3023
|
+
maxTurns);
|
|
2858
3024
|
const runner = new ClaudeRunner(runnerConfig);
|
|
2859
3025
|
// Store runner
|
|
2860
3026
|
agentSessionManager.addClaudeRunner(linearAgentActivitySessionId, runner);
|