sylas-edge-worker 0.2.21 → 0.2.23
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/LICENSE +21 -0
- package/dist/AgentSessionManager.d.ts +2 -65
- package/dist/AgentSessionManager.d.ts.map +1 -1
- package/dist/AgentSessionManager.js +60 -328
- package/dist/AgentSessionManager.js.map +1 -1
- package/dist/ChatSessionHandler.d.ts +1 -2
- package/dist/ChatSessionHandler.d.ts.map +1 -1
- package/dist/ChatSessionHandler.js +7 -9
- package/dist/ChatSessionHandler.js.map +1 -1
- package/dist/EdgeWorker.d.ts +1 -33
- package/dist/EdgeWorker.d.ts.map +1 -1
- package/dist/EdgeWorker.js +69 -447
- package/dist/EdgeWorker.js.map +1 -1
- package/dist/PromptBuilder.d.ts +2 -7
- package/dist/PromptBuilder.d.ts.map +1 -1
- package/dist/PromptBuilder.js +8 -24
- package/dist/PromptBuilder.js.map +1 -1
- package/dist/RunnerRegistry.d.ts +18 -0
- package/dist/RunnerRegistry.d.ts.map +1 -0
- package/dist/RunnerRegistry.js +95 -0
- package/dist/RunnerRegistry.js.map +1 -0
- package/dist/RunnerSelectionService.d.ts +5 -15
- package/dist/RunnerSelectionService.d.ts.map +1 -1
- package/dist/RunnerSelectionService.js +156 -113
- package/dist/RunnerSelectionService.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/procedures/ProcedureAnalyzer.d.ts +4 -0
- package/dist/procedures/ProcedureAnalyzer.d.ts.map +1 -1
- package/dist/procedures/ProcedureAnalyzer.js +48 -18
- package/dist/procedures/ProcedureAnalyzer.js.map +1 -1
- package/dist/prompt-assembly/types.d.ts +1 -3
- package/dist/prompt-assembly/types.d.ts.map +1 -1
- package/dist/types.d.ts +1 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +36 -14
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 smilebank7
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,48 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import type
|
|
3
|
-
import { type IAgentRunner, type ILogger, type IssueMinimal, type SerializedSylasAgentSession, type SerializedSylasAgentSessionEntry, type SylasAgentSession, type SylasAgentSessionEntry, type Workspace } from "sylas-core";
|
|
4
|
-
import type { ProcedureAnalyzer } from "./procedures/ProcedureAnalyzer.js";
|
|
5
|
-
import type { SharedApplicationServer } from "./SharedApplicationServer.js";
|
|
2
|
+
import { type IAgentRunner, type ILogger, type IssueMinimal, type SDKMessage, type SDKResultMessage, type SDKSystemMessage, type SerializedSylasAgentSession, type SerializedSylasAgentSessionEntry, type SylasAgentSession, type SylasAgentSessionEntry, type Workspace } from "sylas-core";
|
|
6
3
|
import type { IActivitySink } from "./sinks/index.js";
|
|
7
|
-
/**
|
|
8
|
-
* Events emitted by AgentSessionManager
|
|
9
|
-
*/
|
|
10
|
-
export interface AgentSessionManagerEvents {
|
|
11
|
-
subroutineComplete: (data: {
|
|
12
|
-
sessionId: string;
|
|
13
|
-
session: SylasAgentSession;
|
|
14
|
-
}) => void;
|
|
15
|
-
/**
|
|
16
|
-
* Emitted when validation fails and we need to run the validation-fixer
|
|
17
|
-
* The EdgeWorker should respond by running the fixer prompt and then re-running verifications
|
|
18
|
-
*/
|
|
19
|
-
validationLoopIteration: (data: {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
session: SylasAgentSession;
|
|
22
|
-
/** The fixer prompt to run (already rendered with failure context) */
|
|
23
|
-
fixerPrompt: string;
|
|
24
|
-
/** Current iteration (1-based) */
|
|
25
|
-
iteration: number;
|
|
26
|
-
/** Maximum iterations allowed */
|
|
27
|
-
maxIterations: number;
|
|
28
|
-
}) => void;
|
|
29
|
-
/**
|
|
30
|
-
* Emitted when we need to re-run the verifications subroutine
|
|
31
|
-
*/
|
|
32
|
-
validationLoopRerun: (data: {
|
|
33
|
-
sessionId: string;
|
|
34
|
-
session: SylasAgentSession;
|
|
35
|
-
/** Current iteration (1-based) */
|
|
36
|
-
iteration: number;
|
|
37
|
-
}) => void;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Type-safe event emitter interface for AgentSessionManager
|
|
41
|
-
*/
|
|
42
|
-
export declare interface AgentSessionManager {
|
|
43
|
-
on<K extends keyof AgentSessionManagerEvents>(event: K, listener: AgentSessionManagerEvents[K]): this;
|
|
44
|
-
emit<K extends keyof AgentSessionManagerEvents>(event: K, ...args: Parameters<AgentSessionManagerEvents[K]>): boolean;
|
|
45
|
-
}
|
|
46
4
|
/**
|
|
47
5
|
* Manages Agent Sessions integration with Claude Code SDK
|
|
48
6
|
* Transforms Claude streaming messages into Agent Session format
|
|
@@ -61,11 +19,9 @@ export declare class AgentSessionManager extends EventEmitter {
|
|
|
61
19
|
private taskSubjectsById;
|
|
62
20
|
private activeStatusActivitiesBySession;
|
|
63
21
|
private stopRequestedSessions;
|
|
64
|
-
private procedureAnalyzer?;
|
|
65
|
-
private sharedApplicationServer?;
|
|
66
22
|
private getParentSessionId?;
|
|
67
23
|
private resumeParentSession?;
|
|
68
|
-
constructor(activitySink: IActivitySink, getParentSessionId?: (childSessionId: string) => string | undefined, resumeParentSession?: (parentSessionId: string, prompt: string, childSessionId: string) => Promise<void>,
|
|
24
|
+
constructor(activitySink: IActivitySink, getParentSessionId?: (childSessionId: string) => string | undefined, resumeParentSession?: (parentSessionId: string, prompt: string, childSessionId: string) => Promise<void>, logger?: ILogger);
|
|
69
25
|
/**
|
|
70
26
|
* Get a session-scoped logger with context (sessionId, platform, issueIdentifier).
|
|
71
27
|
*/
|
|
@@ -104,27 +60,8 @@ export declare class AgentSessionManager extends EventEmitter {
|
|
|
104
60
|
* Complete a session from Claude result message
|
|
105
61
|
*/
|
|
106
62
|
completeSession(sessionId: string, resultMessage: SDKResultMessage): Promise<void>;
|
|
107
|
-
private shouldRecoverFromPreviousSubroutine;
|
|
108
63
|
private consumeStopRequest;
|
|
109
64
|
requestSessionStop(linearAgentActivitySessionId: string): void;
|
|
110
|
-
/**
|
|
111
|
-
* Handle completion using procedure routing system
|
|
112
|
-
*/
|
|
113
|
-
private handleProcedureCompletion;
|
|
114
|
-
/**
|
|
115
|
-
* Handle validation loop completion for subroutines that use usesValidationLoop
|
|
116
|
-
* Returns true if the validation loop took over control flow (needs fixer or retry)
|
|
117
|
-
* Returns false if validation passed or max retries reached (continue with normal advancement)
|
|
118
|
-
*/
|
|
119
|
-
private handleValidationLoopCompletion;
|
|
120
|
-
/**
|
|
121
|
-
* Update validation loop state in session metadata
|
|
122
|
-
*/
|
|
123
|
-
private updateValidationLoopState;
|
|
124
|
-
/**
|
|
125
|
-
* Clear validation loop state from session metadata
|
|
126
|
-
*/
|
|
127
|
-
private clearValidationLoopState;
|
|
128
65
|
/**
|
|
129
66
|
* Handle child session completion and resume parent
|
|
130
67
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentSessionManager.d.ts","sourceRoot":"","sources":["../src/AgentSessionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AgentSessionManager.d.ts","sourceRoot":"","sources":["../src/AgentSessionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { AgentSessionStatus, AgentSessionType, createLogger, } from "sylas-core";
|
|
3
|
-
import { DEFAULT_VALIDATION_LOOP_CONFIG, parseValidationResult, renderValidationFixerPrompt, } from "./validation/index.js";
|
|
4
3
|
/**
|
|
5
4
|
* Manages Agent Sessions integration with Claude Code SDK
|
|
6
5
|
* Transforms Claude streaming messages into Agent Session format
|
|
@@ -19,18 +18,14 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
19
18
|
taskSubjectsById = new Map(); // Cache task subjects by task ID (e.g., "1" → "Fix login bug")
|
|
20
19
|
activeStatusActivitiesBySession = new Map(); // Maps session ID to active compacting status activity ID
|
|
21
20
|
stopRequestedSessions = new Set(); // Sessions explicitly stopped by user signal
|
|
22
|
-
procedureAnalyzer;
|
|
23
|
-
sharedApplicationServer;
|
|
24
21
|
getParentSessionId;
|
|
25
22
|
resumeParentSession;
|
|
26
|
-
constructor(activitySink, getParentSessionId, resumeParentSession,
|
|
23
|
+
constructor(activitySink, getParentSessionId, resumeParentSession, logger) {
|
|
27
24
|
super();
|
|
28
25
|
this.logger = logger ?? createLogger({ component: "AgentSessionManager" });
|
|
29
26
|
this.activitySink = activitySink;
|
|
30
27
|
this.getParentSessionId = getParentSessionId;
|
|
31
28
|
this.resumeParentSession = resumeParentSession;
|
|
32
|
-
this.procedureAnalyzer = procedureAnalyzer;
|
|
33
|
-
this.sharedApplicationServer = sharedApplicationServer;
|
|
34
29
|
}
|
|
35
30
|
/**
|
|
36
31
|
* Get a session-scoped logger with context (sessionId, platform, issueIdentifier).
|
|
@@ -123,29 +118,31 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
123
118
|
const runner = linearSession.agentRunner;
|
|
124
119
|
const runnerType = runner?.constructor.name === "OpenCodeRunner"
|
|
125
120
|
? "opencode"
|
|
126
|
-
: runner?.constructor.name === "
|
|
127
|
-
? "
|
|
128
|
-
:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
: runner?.constructor.name === "CodexRunner"
|
|
122
|
+
? "codex"
|
|
123
|
+
: "claude";
|
|
124
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
125
|
+
// : runner?.constructor.name === "GeminiRunner"
|
|
126
|
+
// ? "gemini"
|
|
127
|
+
// : runner?.constructor.name === "CursorRunner"
|
|
128
|
+
// ? "cursor"
|
|
129
|
+
// : "claude";
|
|
133
130
|
// Update the appropriate session ID based on runner type
|
|
134
131
|
if (runnerType === "opencode") {
|
|
135
132
|
linearSession.openCodeSessionId = claudeSystemMessage.session_id;
|
|
136
133
|
}
|
|
137
|
-
else if (runnerType === "gemini") {
|
|
138
|
-
linearSession.geminiSessionId = claudeSystemMessage.session_id;
|
|
139
|
-
}
|
|
140
134
|
else if (runnerType === "codex") {
|
|
141
135
|
linearSession.codexSessionId = claudeSystemMessage.session_id;
|
|
142
136
|
}
|
|
143
|
-
else if (runnerType === "cursor") {
|
|
144
|
-
linearSession.cursorSessionId = claudeSystemMessage.session_id;
|
|
145
|
-
}
|
|
146
137
|
else {
|
|
147
138
|
linearSession.claudeSessionId = claudeSystemMessage.session_id;
|
|
148
139
|
}
|
|
140
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
141
|
+
// } else if (runnerType === "gemini") {
|
|
142
|
+
// linearSession.geminiSessionId = claudeSystemMessage.session_id;
|
|
143
|
+
// } else if (runnerType === "cursor") {
|
|
144
|
+
// linearSession.cursorSessionId = claudeSystemMessage.session_id;
|
|
145
|
+
// }
|
|
149
146
|
linearSession.updatedAt = Date.now();
|
|
150
147
|
linearSession.metadata = {
|
|
151
148
|
...linearSession.metadata, // Preserve existing metadata
|
|
@@ -176,24 +173,28 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
176
173
|
const runner = session?.agentRunner;
|
|
177
174
|
const runnerType = runner?.constructor.name === "OpenCodeRunner"
|
|
178
175
|
? "opencode"
|
|
179
|
-
: runner?.constructor.name === "
|
|
180
|
-
? "
|
|
181
|
-
:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
: runner?.constructor.name === "CodexRunner"
|
|
177
|
+
? "codex"
|
|
178
|
+
: "claude";
|
|
179
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
180
|
+
// : runner?.constructor.name === "GeminiRunner"
|
|
181
|
+
// ? "gemini"
|
|
182
|
+
// : runner?.constructor.name === "CursorRunner"
|
|
183
|
+
// ? "cursor"
|
|
184
|
+
// : "claude";
|
|
186
185
|
const sessionEntry = {
|
|
187
186
|
// Set the appropriate session ID based on runner type
|
|
188
187
|
...(runnerType === "opencode"
|
|
189
188
|
? { openCodeSessionId: sdkMessage.session_id }
|
|
190
|
-
: runnerType === "
|
|
191
|
-
? {
|
|
192
|
-
:
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
189
|
+
: runnerType === "codex"
|
|
190
|
+
? { codexSessionId: sdkMessage.session_id }
|
|
191
|
+
: { claudeSessionId: sdkMessage.session_id }),
|
|
192
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
193
|
+
// : runnerType === "gemini"
|
|
194
|
+
// ? { geminiSessionId: sdkMessage.session_id }
|
|
195
|
+
// : runnerType === "cursor"
|
|
196
|
+
// ? { cursorSessionId: sdkMessage.session_id }
|
|
197
|
+
// : { claudeSessionId: sdkMessage.session_id }),
|
|
197
198
|
type: sdkMessage.type,
|
|
198
199
|
content: this.extractContent(sdkMessage),
|
|
199
200
|
metadata: {
|
|
@@ -241,62 +242,20 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
241
242
|
totalCostUsd: resultMessage.total_cost_usd,
|
|
242
243
|
usage: resultMessage.usage,
|
|
243
244
|
});
|
|
244
|
-
// Handle result using procedure routing system (skip for sessions without procedures, e.g. Slack)
|
|
245
|
-
if (!this.procedureAnalyzer) {
|
|
246
|
-
log.info(`Session completed (no procedure routing)`);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
245
|
if (wasStopRequested) {
|
|
250
|
-
log.info(`Session ${sessionId} was stopped by user
|
|
246
|
+
log.info(`Session ${sessionId} was stopped by user`);
|
|
251
247
|
return;
|
|
252
248
|
}
|
|
253
|
-
if ("result" in resultMessage && resultMessage.result)
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
else if (resultMessage.subtype !== "success" &&
|
|
257
|
-
this.shouldRecoverFromPreviousSubroutine(resultMessage)) {
|
|
258
|
-
// Error result (e.g. error_max_turns from singleTurn subroutines) — try to
|
|
259
|
-
// recover from the last completed subroutine's result so the procedure can still complete.
|
|
260
|
-
const recoveredText = this.procedureAnalyzer?.getLastSubroutineResult(session);
|
|
261
|
-
if (recoveredText) {
|
|
262
|
-
log.info(`Recovered result from previous subroutine (subtype: ${resultMessage.subtype}), treating as success for procedure completion`);
|
|
263
|
-
// Create a synthetic success result for procedure routing
|
|
264
|
-
const syntheticResult = {
|
|
265
|
-
...resultMessage,
|
|
266
|
-
subtype: "success",
|
|
267
|
-
result: recoveredText,
|
|
268
|
-
is_error: false,
|
|
269
|
-
};
|
|
270
|
-
await this.handleProcedureCompletion(session, sessionId, syntheticResult);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
log.warn(`Error result with no recoverable text (subtype: ${resultMessage.subtype}), posting error to Linear`);
|
|
274
|
-
await this.addResultEntry(sessionId, resultMessage);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
else if (resultMessage.subtype !== "success") {
|
|
278
|
-
// Non-recoverable errors (e.g. stop/abort) should not advance procedures.
|
|
249
|
+
if (("result" in resultMessage && Boolean(resultMessage.result)) ||
|
|
250
|
+
resultMessage.subtype !== "success") {
|
|
279
251
|
await this.addResultEntry(sessionId, resultMessage);
|
|
280
252
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
253
|
+
const isChildSession = this.getParentSessionId?.(sessionId);
|
|
254
|
+
if (isChildSession &&
|
|
255
|
+
this.resumeParentSession &&
|
|
256
|
+
resultMessage.subtype === "success") {
|
|
257
|
+
await this.handleChildSessionCompletion(sessionId, resultMessage);
|
|
285
258
|
}
|
|
286
|
-
const errorText = [
|
|
287
|
-
resultMessage.subtype,
|
|
288
|
-
...("errors" in resultMessage && Array.isArray(resultMessage.errors)
|
|
289
|
-
? resultMessage.errors
|
|
290
|
-
: []),
|
|
291
|
-
"result" in resultMessage && typeof resultMessage.result === "string"
|
|
292
|
-
? resultMessage.result
|
|
293
|
-
: "",
|
|
294
|
-
]
|
|
295
|
-
.join(" ")
|
|
296
|
-
.toLowerCase();
|
|
297
|
-
return (errorText.includes("max turn") ||
|
|
298
|
-
errorText.includes("turn limit") ||
|
|
299
|
-
errorText.includes("turns limit"));
|
|
300
259
|
}
|
|
301
260
|
consumeStopRequest(linearAgentActivitySessionId) {
|
|
302
261
|
if (!this.stopRequestedSessions.has(linearAgentActivitySessionId)) {
|
|
@@ -308,227 +267,6 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
308
267
|
requestSessionStop(linearAgentActivitySessionId) {
|
|
309
268
|
this.stopRequestedSessions.add(linearAgentActivitySessionId);
|
|
310
269
|
}
|
|
311
|
-
/**
|
|
312
|
-
* Handle completion using procedure routing system
|
|
313
|
-
*/
|
|
314
|
-
async handleProcedureCompletion(session, sessionId, resultMessage) {
|
|
315
|
-
const log = this.sessionLog(sessionId);
|
|
316
|
-
if (!this.procedureAnalyzer) {
|
|
317
|
-
throw new Error("ProcedureAnalyzer not available");
|
|
318
|
-
}
|
|
319
|
-
// Check if error occurred
|
|
320
|
-
if (resultMessage.subtype !== "success") {
|
|
321
|
-
log.info(`Subroutine completed with error, not triggering next subroutine`);
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
// Get the runner session ID (Claude, Gemini, Codex, Cursor, or OpenCode)
|
|
325
|
-
const runnerSessionId = session.claudeSessionId ||
|
|
326
|
-
session.geminiSessionId ||
|
|
327
|
-
session.codexSessionId ||
|
|
328
|
-
session.cursorSessionId ||
|
|
329
|
-
session.openCodeSessionId;
|
|
330
|
-
if (!runnerSessionId) {
|
|
331
|
-
log.error(`No runner session ID found for procedure session`);
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
// Check if there's a next subroutine
|
|
335
|
-
const nextSubroutine = this.procedureAnalyzer.getNextSubroutine(session);
|
|
336
|
-
if (nextSubroutine) {
|
|
337
|
-
// More subroutines to run - check if current subroutine requires approval
|
|
338
|
-
const currentSubroutine = this.procedureAnalyzer.getCurrentSubroutine(session);
|
|
339
|
-
if (currentSubroutine?.requiresApproval) {
|
|
340
|
-
log.info(`Current subroutine "${currentSubroutine.name}" requires approval before proceeding`);
|
|
341
|
-
// Check if SharedApplicationServer is available
|
|
342
|
-
if (!this.sharedApplicationServer) {
|
|
343
|
-
log.error(`SharedApplicationServer not available for approval workflow`);
|
|
344
|
-
await this.createErrorActivity(sessionId, "Approval workflow failed: Server not available");
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
// Extract the final result from the completed subroutine
|
|
348
|
-
const subroutineResult = "result" in resultMessage && resultMessage.result
|
|
349
|
-
? resultMessage.result
|
|
350
|
-
: "No result available";
|
|
351
|
-
try {
|
|
352
|
-
// Register approval request with server
|
|
353
|
-
const approvalRequest = this.sharedApplicationServer.registerApprovalRequest(sessionId);
|
|
354
|
-
// Post approval elicitation to Linear with auth signal URL
|
|
355
|
-
const approvalMessage = `The previous step has completed. Please review the result below and approve to continue:\n\n${subroutineResult}`;
|
|
356
|
-
await this.createApprovalElicitation(sessionId, approvalMessage, approvalRequest.url);
|
|
357
|
-
log.info(`Waiting for approval at URL: ${approvalRequest.url}`);
|
|
358
|
-
// Wait for approval with timeout (30 minutes)
|
|
359
|
-
const approvalTimeout = 30 * 60 * 1000;
|
|
360
|
-
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Approval timeout")), approvalTimeout));
|
|
361
|
-
const { approved, feedback } = await Promise.race([
|
|
362
|
-
approvalRequest.promise,
|
|
363
|
-
timeoutPromise,
|
|
364
|
-
]);
|
|
365
|
-
if (!approved) {
|
|
366
|
-
log.info(`Approval rejected`);
|
|
367
|
-
await this.createErrorActivity(sessionId, `Workflow stopped: User rejected approval.${feedback ? `\n\nFeedback: ${feedback}` : ""}`);
|
|
368
|
-
return; // Stop workflow
|
|
369
|
-
}
|
|
370
|
-
log.info(`Approval granted, continuing to next subroutine`);
|
|
371
|
-
// Optionally post feedback as a thought
|
|
372
|
-
if (feedback) {
|
|
373
|
-
await this.createThoughtActivity(sessionId, `User feedback: ${feedback}`);
|
|
374
|
-
}
|
|
375
|
-
// Continue with advancement (fall through to existing code)
|
|
376
|
-
}
|
|
377
|
-
catch (error) {
|
|
378
|
-
const errorMessage = error.message;
|
|
379
|
-
if (errorMessage === "Approval timeout") {
|
|
380
|
-
log.info(`Approval timed out`);
|
|
381
|
-
await this.createErrorActivity(sessionId, "Workflow stopped: Approval request timed out after 30 minutes.");
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
log.error(`Approval request failed:`, error);
|
|
385
|
-
await this.createErrorActivity(sessionId, `Workflow stopped: Approval request failed - ${errorMessage}`);
|
|
386
|
-
}
|
|
387
|
-
return; // Stop workflow
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
// Check if current subroutine uses validation loop
|
|
391
|
-
if (currentSubroutine?.usesValidationLoop) {
|
|
392
|
-
const handled = await this.handleValidationLoopCompletion(session, sessionId, resultMessage, runnerSessionId, nextSubroutine);
|
|
393
|
-
if (handled) {
|
|
394
|
-
return; // Validation loop took over control flow
|
|
395
|
-
}
|
|
396
|
-
// If not handled (validation passed or max retries), continue with normal advancement
|
|
397
|
-
}
|
|
398
|
-
// Advance procedure state
|
|
399
|
-
log.info(`Subroutine completed, advancing to next: ${nextSubroutine.name}`);
|
|
400
|
-
const subroutineResult = "result" in resultMessage ? resultMessage.result : undefined;
|
|
401
|
-
this.procedureAnalyzer.advanceToNextSubroutine(session, runnerSessionId, subroutineResult);
|
|
402
|
-
// Emit event for EdgeWorker to handle subroutine transition
|
|
403
|
-
// This replaces the callback pattern and allows EdgeWorker to subscribe
|
|
404
|
-
this.emit("subroutineComplete", {
|
|
405
|
-
sessionId,
|
|
406
|
-
session,
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
// Procedure complete - post final result
|
|
411
|
-
log.info(`All subroutines completed, posting final result to Linear`);
|
|
412
|
-
await this.addResultEntry(sessionId, resultMessage);
|
|
413
|
-
// Handle child session completion
|
|
414
|
-
const isChildSession = this.getParentSessionId?.(sessionId);
|
|
415
|
-
if (isChildSession && this.resumeParentSession) {
|
|
416
|
-
await this.handleChildSessionCompletion(sessionId, resultMessage);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Handle validation loop completion for subroutines that use usesValidationLoop
|
|
422
|
-
* Returns true if the validation loop took over control flow (needs fixer or retry)
|
|
423
|
-
* Returns false if validation passed or max retries reached (continue with normal advancement)
|
|
424
|
-
*/
|
|
425
|
-
async handleValidationLoopCompletion(session, sessionId, resultMessage, _runnerSessionId, _nextSubroutine) {
|
|
426
|
-
const log = this.sessionLog(sessionId);
|
|
427
|
-
const maxIterations = DEFAULT_VALIDATION_LOOP_CONFIG.maxIterations;
|
|
428
|
-
// Get or initialize validation loop state
|
|
429
|
-
let validationLoop = session.metadata?.procedure?.validationLoop;
|
|
430
|
-
if (!validationLoop) {
|
|
431
|
-
validationLoop = {
|
|
432
|
-
iteration: 0,
|
|
433
|
-
inFixerMode: false,
|
|
434
|
-
attempts: [],
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
// Check if we're coming back from the fixer
|
|
438
|
-
if (validationLoop.inFixerMode) {
|
|
439
|
-
// Fixer completed, now we need to re-run verifications
|
|
440
|
-
log.info(`Validation fixer completed for iteration ${validationLoop.iteration}, re-running verifications`);
|
|
441
|
-
// Clear fixer mode flag
|
|
442
|
-
validationLoop.inFixerMode = false;
|
|
443
|
-
this.updateValidationLoopState(session, validationLoop);
|
|
444
|
-
// Emit event to re-run verifications
|
|
445
|
-
this.emit("validationLoopRerun", {
|
|
446
|
-
sessionId,
|
|
447
|
-
session,
|
|
448
|
-
iteration: validationLoop.iteration,
|
|
449
|
-
});
|
|
450
|
-
return true;
|
|
451
|
-
}
|
|
452
|
-
// Parse the validation result from the response
|
|
453
|
-
const resultText = "result" in resultMessage ? resultMessage.result : undefined;
|
|
454
|
-
const structuredOutput = "structured_output" in resultMessage
|
|
455
|
-
? resultMessage.structured_output
|
|
456
|
-
: undefined;
|
|
457
|
-
const validationResult = parseValidationResult(resultText, structuredOutput);
|
|
458
|
-
// Record this attempt
|
|
459
|
-
const newIteration = validationLoop.iteration + 1;
|
|
460
|
-
validationLoop.iteration = newIteration;
|
|
461
|
-
validationLoop.attempts.push({
|
|
462
|
-
iteration: newIteration,
|
|
463
|
-
pass: validationResult.pass,
|
|
464
|
-
reason: validationResult.reason,
|
|
465
|
-
timestamp: Date.now(),
|
|
466
|
-
});
|
|
467
|
-
log.info(`Validation result for iteration ${newIteration}/${maxIterations}: pass=${validationResult.pass}, reason="${validationResult.reason.substring(0, 100)}..."`);
|
|
468
|
-
// Update state in session
|
|
469
|
-
this.updateValidationLoopState(session, validationLoop);
|
|
470
|
-
// Check if validation passed
|
|
471
|
-
if (validationResult.pass) {
|
|
472
|
-
log.info(`Validation passed after ${newIteration} iteration(s)`);
|
|
473
|
-
// Clear validation loop state for next subroutine
|
|
474
|
-
this.clearValidationLoopState(session);
|
|
475
|
-
return false; // Continue with normal advancement
|
|
476
|
-
}
|
|
477
|
-
// Check if we've exceeded max retries
|
|
478
|
-
if (newIteration >= maxIterations) {
|
|
479
|
-
log.info(`Validation failed after ${newIteration} iterations, continuing anyway`);
|
|
480
|
-
// Post a thought about the failures
|
|
481
|
-
await this.createThoughtActivity(sessionId, `Validation loop exhausted after ${newIteration} attempts. Last failure: ${validationResult.reason}`);
|
|
482
|
-
// Clear validation loop state for next subroutine
|
|
483
|
-
this.clearValidationLoopState(session);
|
|
484
|
-
return false; // Continue with normal advancement
|
|
485
|
-
}
|
|
486
|
-
// Validation failed and we have retries left - run the fixer
|
|
487
|
-
log.info(`Validation failed, running fixer (iteration ${newIteration}/${maxIterations})`);
|
|
488
|
-
// Set fixer mode flag
|
|
489
|
-
validationLoop.inFixerMode = true;
|
|
490
|
-
this.updateValidationLoopState(session, validationLoop);
|
|
491
|
-
// Render the fixer prompt with context
|
|
492
|
-
const previousAttempts = validationLoop.attempts.slice(0, -1).map((a) => ({
|
|
493
|
-
iteration: a.iteration,
|
|
494
|
-
reason: a.reason,
|
|
495
|
-
}));
|
|
496
|
-
const fixerPrompt = renderValidationFixerPrompt({
|
|
497
|
-
failureReason: validationResult.reason,
|
|
498
|
-
iteration: newIteration,
|
|
499
|
-
maxIterations,
|
|
500
|
-
previousAttempts,
|
|
501
|
-
});
|
|
502
|
-
// Emit event for EdgeWorker to run the fixer
|
|
503
|
-
this.emit("validationLoopIteration", {
|
|
504
|
-
sessionId,
|
|
505
|
-
session,
|
|
506
|
-
fixerPrompt,
|
|
507
|
-
iteration: newIteration,
|
|
508
|
-
maxIterations,
|
|
509
|
-
});
|
|
510
|
-
return true; // Validation loop took over control flow
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* Update validation loop state in session metadata
|
|
514
|
-
*/
|
|
515
|
-
updateValidationLoopState(session, validationLoop) {
|
|
516
|
-
if (!session.metadata) {
|
|
517
|
-
session.metadata = {};
|
|
518
|
-
}
|
|
519
|
-
if (!session.metadata.procedure) {
|
|
520
|
-
return; // No procedure metadata, can't update
|
|
521
|
-
}
|
|
522
|
-
session.metadata.procedure.validationLoop = validationLoop;
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Clear validation loop state from session metadata
|
|
526
|
-
*/
|
|
527
|
-
clearValidationLoopState(session) {
|
|
528
|
-
if (session.metadata?.procedure) {
|
|
529
|
-
delete session.metadata.procedure.validationLoop;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
270
|
/**
|
|
533
271
|
* Handle child session completion and resume parent
|
|
534
272
|
*/
|
|
@@ -622,13 +360,15 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
622
360
|
const runner = session?.agentRunner;
|
|
623
361
|
const runnerType = runner?.constructor.name === "OpenCodeRunner"
|
|
624
362
|
? "opencode"
|
|
625
|
-
: runner?.constructor.name === "
|
|
626
|
-
? "
|
|
627
|
-
:
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
363
|
+
: runner?.constructor.name === "CodexRunner"
|
|
364
|
+
? "codex"
|
|
365
|
+
: "claude";
|
|
366
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
367
|
+
// : runner?.constructor.name === "GeminiRunner"
|
|
368
|
+
// ? "gemini"
|
|
369
|
+
// : runner?.constructor.name === "CursorRunner"
|
|
370
|
+
// ? "cursor"
|
|
371
|
+
// : "claude";
|
|
632
372
|
// For error results, content may be in errors[] rather than result
|
|
633
373
|
const content = "result" in resultMessage && typeof resultMessage.result === "string"
|
|
634
374
|
? resultMessage.result
|
|
@@ -642,13 +382,15 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
642
382
|
// Set the appropriate session ID based on runner type
|
|
643
383
|
...(runnerType === "opencode"
|
|
644
384
|
? { openCodeSessionId: resultMessage.session_id }
|
|
645
|
-
: runnerType === "
|
|
646
|
-
? {
|
|
647
|
-
:
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
385
|
+
: runnerType === "codex"
|
|
386
|
+
? { codexSessionId: resultMessage.session_id }
|
|
387
|
+
: { claudeSessionId: resultMessage.session_id }),
|
|
388
|
+
// TEMPORARILY DISABLED: runner consolidation v2
|
|
389
|
+
// : runnerType === "gemini"
|
|
390
|
+
// ? { geminiSessionId: resultMessage.session_id }
|
|
391
|
+
// : runnerType === "cursor"
|
|
392
|
+
// ? { cursorSessionId: resultMessage.session_id }
|
|
393
|
+
// : { claudeSessionId: resultMessage.session_id }),
|
|
652
394
|
type: "result",
|
|
653
395
|
content,
|
|
654
396
|
metadata: {
|
|
@@ -1080,16 +822,6 @@ export class AgentSessionManager extends EventEmitter {
|
|
|
1080
822
|
body: entry.content,
|
|
1081
823
|
};
|
|
1082
824
|
}
|
|
1083
|
-
// Check if current subroutine has suppressThoughtPosting enabled
|
|
1084
|
-
// If so, suppress thoughts and actions (but still post responses and results)
|
|
1085
|
-
const currentSubroutine = this.procedureAnalyzer?.getCurrentSubroutine(session);
|
|
1086
|
-
if (currentSubroutine?.suppressThoughtPosting) {
|
|
1087
|
-
// Only suppress thoughts and actions, not responses or results
|
|
1088
|
-
if (content.type === "thought" || content.type === "action") {
|
|
1089
|
-
log.debug(`Suppressing ${content.type} posting for subroutine "${currentSubroutine.name}"`);
|
|
1090
|
-
return; // Don't post to tracker
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
825
|
// Ensure we have an external session ID for activity posting
|
|
1094
826
|
if (!session.externalSessionId) {
|
|
1095
827
|
log.debug(`Skipping activity sync - no external session ID (platform: ${session.issueContext?.trackerId || "unknown"})`);
|