@sudocode-ai/local-server 0.1.7 → 0.1.8
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 +6 -0
- package/dist/errors/agent-errors.d.ts +43 -0
- package/dist/errors/agent-errors.d.ts.map +1 -0
- package/dist/errors/agent-errors.js +69 -0
- package/dist/errors/agent-errors.js.map +1 -0
- package/dist/execution/adapters/claude-adapter.d.ts +63 -0
- package/dist/execution/adapters/claude-adapter.d.ts.map +1 -0
- package/dist/execution/adapters/claude-adapter.js +82 -0
- package/dist/execution/adapters/claude-adapter.js.map +1 -0
- package/dist/execution/adapters/codex-adapter.d.ts +67 -0
- package/dist/execution/adapters/codex-adapter.d.ts.map +1 -0
- package/dist/execution/adapters/codex-adapter.js +183 -0
- package/dist/execution/adapters/codex-adapter.js.map +1 -0
- package/dist/execution/adapters/codex-config-builder.d.ts +30 -0
- package/dist/execution/adapters/codex-config-builder.d.ts.map +1 -0
- package/dist/execution/adapters/codex-config-builder.js +110 -0
- package/dist/execution/adapters/codex-config-builder.js.map +1 -0
- package/dist/execution/adapters/copilot-adapter.d.ts +94 -0
- package/dist/execution/adapters/copilot-adapter.d.ts.map +1 -0
- package/dist/execution/adapters/copilot-adapter.js +163 -0
- package/dist/execution/adapters/copilot-adapter.js.map +1 -0
- package/dist/execution/adapters/copilot-config-builder.d.ts +48 -0
- package/dist/execution/adapters/copilot-config-builder.d.ts.map +1 -0
- package/dist/execution/adapters/copilot-config-builder.js +125 -0
- package/dist/execution/adapters/copilot-config-builder.js.map +1 -0
- package/dist/execution/adapters/cursor-adapter.d.ts +66 -0
- package/dist/execution/adapters/cursor-adapter.d.ts.map +1 -0
- package/dist/execution/adapters/cursor-adapter.js +121 -0
- package/dist/execution/adapters/cursor-adapter.js.map +1 -0
- package/dist/execution/adapters/cursor-config-builder.d.ts +29 -0
- package/dist/execution/adapters/cursor-config-builder.d.ts.map +1 -0
- package/dist/execution/adapters/cursor-config-builder.js +49 -0
- package/dist/execution/adapters/cursor-config-builder.js.map +1 -0
- package/dist/execution/adapters/shared/config-presets.d.ts +102 -0
- package/dist/execution/adapters/shared/config-presets.d.ts.map +1 -0
- package/dist/execution/adapters/shared/config-presets.js +205 -0
- package/dist/execution/adapters/shared/config-presets.js.map +1 -0
- package/dist/execution/adapters/shared/config-utils.d.ts +95 -0
- package/dist/execution/adapters/shared/config-utils.d.ts.map +1 -0
- package/dist/execution/adapters/shared/config-utils.js +163 -0
- package/dist/execution/adapters/shared/config-utils.js.map +1 -0
- package/dist/execution/adapters/shared/index.d.ts +8 -0
- package/dist/execution/adapters/shared/index.d.ts.map +1 -0
- package/dist/execution/adapters/shared/index.js +8 -0
- package/dist/execution/adapters/shared/index.js.map +1 -0
- package/dist/execution/executors/agent-executor-wrapper.d.ts +153 -0
- package/dist/execution/executors/agent-executor-wrapper.d.ts.map +1 -0
- package/dist/execution/executors/agent-executor-wrapper.js +652 -0
- package/dist/execution/executors/agent-executor-wrapper.js.map +1 -0
- package/dist/execution/executors/executor-factory.d.ts +95 -0
- package/dist/execution/executors/executor-factory.d.ts.map +1 -0
- package/dist/execution/executors/executor-factory.js +120 -0
- package/dist/execution/executors/executor-factory.js.map +1 -0
- package/dist/execution/output/ag-ui-adapter.d.ts +0 -2
- package/dist/execution/output/ag-ui-adapter.d.ts.map +1 -1
- package/dist/execution/output/ag-ui-adapter.js +0 -2
- package/dist/execution/output/ag-ui-adapter.js.map +1 -1
- package/dist/execution/output/index.d.ts +0 -3
- package/dist/execution/output/index.d.ts.map +1 -1
- package/dist/execution/output/index.js +0 -2
- package/dist/execution/output/index.js.map +1 -1
- package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts +108 -0
- package/dist/execution/output/normalized-to-ag-ui-adapter.d.ts.map +1 -0
- package/dist/execution/output/normalized-to-ag-ui-adapter.js +321 -0
- package/dist/execution/output/normalized-to-ag-ui-adapter.js.map +1 -0
- package/dist/execution/process/builders/claude.d.ts +24 -57
- package/dist/execution/process/builders/claude.d.ts.map +1 -1
- package/dist/execution/process/builders/claude.js +153 -19
- package/dist/execution/process/builders/claude.js.map +1 -1
- package/dist/execution/transport/ipc-transport-manager.d.ts +74 -0
- package/dist/execution/transport/ipc-transport-manager.d.ts.map +1 -0
- package/dist/execution/transport/ipc-transport-manager.js +104 -0
- package/dist/execution/transport/ipc-transport-manager.js.map +1 -0
- package/dist/execution/transport/transport-manager.d.ts.map +1 -1
- package/dist/execution/transport/transport-manager.js +3 -0
- package/dist/execution/transport/transport-manager.js.map +1 -1
- package/dist/execution/worktree/conflict-detector.d.ts +85 -0
- package/dist/execution/worktree/conflict-detector.d.ts.map +1 -0
- package/dist/execution/worktree/conflict-detector.js +129 -0
- package/dist/execution/worktree/conflict-detector.js.map +1 -0
- package/dist/execution/worktree/git-cli.d.ts +9 -0
- package/dist/execution/worktree/git-cli.d.ts.map +1 -1
- package/dist/execution/worktree/git-cli.js +10 -0
- package/dist/execution/worktree/git-cli.js.map +1 -1
- package/dist/execution/worktree/git-sync-cli.d.ts +187 -0
- package/dist/execution/worktree/git-sync-cli.d.ts.map +1 -0
- package/dist/execution/worktree/git-sync-cli.js +350 -0
- package/dist/execution/worktree/git-sync-cli.js.map +1 -0
- package/dist/execution/worktree/manager.d.ts +18 -0
- package/dist/execution/worktree/manager.d.ts.map +1 -1
- package/dist/execution/worktree/manager.js +9 -3
- package/dist/execution/worktree/manager.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +132 -211
- package/dist/index.js.map +1 -1
- package/dist/middleware/project-context.d.ts +37 -0
- package/dist/middleware/project-context.d.ts.map +1 -0
- package/dist/middleware/project-context.js +91 -0
- package/dist/middleware/project-context.js.map +1 -0
- package/dist/public/assets/index-Bb_W5bUr.css +1 -0
- package/dist/public/assets/index-CFKL113G.js +710 -0
- package/dist/public/assets/index-CFKL113G.js.map +1 -0
- package/dist/public/assets/{react-vendor-ByUx1V_q.js → react-vendor-DiL5hC7l.js} +2 -2
- package/dist/public/assets/{react-vendor-ByUx1V_q.js.map → react-vendor-DiL5hC7l.js.map} +1 -1
- package/dist/public/assets/ui-vendor-B4WMPEfa.js +54 -0
- package/dist/public/assets/ui-vendor-B4WMPEfa.js.map +1 -0
- package/dist/public/index.html +4 -4
- package/dist/routes/agents.d.ts +3 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +62 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/config.d.ts +3 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +25 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/editors.d.ts +15 -0
- package/dist/routes/editors.d.ts.map +1 -0
- package/dist/routes/editors.js +98 -0
- package/dist/routes/editors.js.map +1 -0
- package/dist/routes/executions-stream.d.ts +8 -5
- package/dist/routes/executions-stream.d.ts.map +1 -1
- package/dist/routes/executions-stream.js +10 -6
- package/dist/routes/executions-stream.js.map +1 -1
- package/dist/routes/executions.d.ts +6 -10
- package/dist/routes/executions.d.ts.map +1 -1
- package/dist/routes/executions.js +506 -54
- package/dist/routes/executions.js.map +1 -1
- package/dist/routes/feedback.d.ts +3 -2
- package/dist/routes/feedback.d.ts.map +1 -1
- package/dist/routes/feedback.js +12 -10
- package/dist/routes/feedback.js.map +1 -1
- package/dist/routes/files.d.ts +18 -0
- package/dist/routes/files.d.ts.map +1 -0
- package/dist/routes/files.js +89 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/issues.d.ts +3 -2
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +19 -18
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/projects.d.ts +11 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/routes/projects.js +447 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/relationships.d.ts +3 -2
- package/dist/routes/relationships.d.ts.map +1 -1
- package/dist/routes/relationships.js +12 -10
- package/dist/routes/relationships.js.map +1 -1
- package/dist/routes/repo-info.d.ts +3 -0
- package/dist/routes/repo-info.d.ts.map +1 -0
- package/dist/routes/repo-info.js +126 -0
- package/dist/routes/repo-info.js.map +1 -0
- package/dist/routes/specs.d.ts +3 -2
- package/dist/routes/specs.d.ts.map +1 -1
- package/dist/routes/specs.js +19 -18
- package/dist/routes/specs.js.map +1 -1
- package/dist/services/agent-registry.d.ts +140 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +272 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/editor-service.d.ts +57 -0
- package/dist/services/editor-service.d.ts.map +1 -0
- package/dist/services/editor-service.js +204 -0
- package/dist/services/editor-service.js.map +1 -0
- package/dist/services/execution-changes-service.d.ts +92 -0
- package/dist/services/execution-changes-service.d.ts.map +1 -0
- package/dist/services/execution-changes-service.js +546 -0
- package/dist/services/execution-changes-service.js.map +1 -0
- package/dist/services/execution-lifecycle.d.ts +1 -0
- package/dist/services/execution-lifecycle.d.ts.map +1 -1
- package/dist/services/execution-lifecycle.js +37 -7
- package/dist/services/execution-lifecycle.js.map +1 -1
- package/dist/services/execution-logs-store.d.ts +75 -0
- package/dist/services/execution-logs-store.d.ts.map +1 -1
- package/dist/services/execution-logs-store.js +142 -2
- package/dist/services/execution-logs-store.js.map +1 -1
- package/dist/services/execution-service.d.ts +50 -58
- package/dist/services/execution-service.d.ts.map +1 -1
- package/dist/services/execution-service.js +433 -469
- package/dist/services/execution-service.js.map +1 -1
- package/dist/services/execution-worker-pool.d.ts +116 -0
- package/dist/services/execution-worker-pool.d.ts.map +1 -0
- package/dist/services/execution-worker-pool.js +326 -0
- package/dist/services/execution-worker-pool.js.map +1 -0
- package/dist/services/executions.d.ts +3 -0
- package/dist/services/executions.d.ts.map +1 -1
- package/dist/services/executions.js +11 -17
- package/dist/services/executions.js.map +1 -1
- package/dist/services/export.d.ts +8 -2
- package/dist/services/export.d.ts.map +1 -1
- package/dist/services/export.js +29 -23
- package/dist/services/export.js.map +1 -1
- package/dist/services/file-search/git-ls-files-strategy.d.ts +72 -0
- package/dist/services/file-search/git-ls-files-strategy.d.ts.map +1 -0
- package/dist/services/file-search/git-ls-files-strategy.js +176 -0
- package/dist/services/file-search/git-ls-files-strategy.js.map +1 -0
- package/dist/services/file-search/index.d.ts +9 -0
- package/dist/services/file-search/index.d.ts.map +1 -0
- package/dist/services/file-search/index.js +10 -0
- package/dist/services/file-search/index.js.map +1 -0
- package/dist/services/file-search/registry.d.ts +97 -0
- package/dist/services/file-search/registry.d.ts.map +1 -0
- package/dist/services/file-search/registry.js +140 -0
- package/dist/services/file-search/registry.js.map +1 -0
- package/dist/services/file-search/strategy.d.ts +58 -0
- package/dist/services/file-search/strategy.d.ts.map +1 -0
- package/dist/services/file-search/strategy.js +8 -0
- package/dist/services/file-search/strategy.js.map +1 -0
- package/dist/services/project-context.d.ts +69 -0
- package/dist/services/project-context.d.ts.map +1 -0
- package/dist/services/project-context.js +113 -0
- package/dist/services/project-context.js.map +1 -0
- package/dist/services/project-manager.d.ts +95 -0
- package/dist/services/project-manager.d.ts.map +1 -0
- package/dist/services/project-manager.js +388 -0
- package/dist/services/project-manager.js.map +1 -0
- package/dist/services/project-registry.d.ts +98 -0
- package/dist/services/project-registry.d.ts.map +1 -0
- package/dist/services/project-registry.js +289 -0
- package/dist/services/project-registry.js.map +1 -0
- package/dist/services/prompt-resolver.d.ts +97 -0
- package/dist/services/prompt-resolver.d.ts.map +1 -0
- package/dist/services/prompt-resolver.js +377 -0
- package/dist/services/prompt-resolver.js.map +1 -0
- package/dist/services/repo-info.d.ts +12 -0
- package/dist/services/repo-info.d.ts.map +1 -1
- package/dist/services/repo-info.js +46 -0
- package/dist/services/repo-info.js.map +1 -1
- package/dist/services/watcher.d.ts +3 -4
- package/dist/services/watcher.d.ts.map +1 -1
- package/dist/services/watcher.js +18 -35
- package/dist/services/watcher.js.map +1 -1
- package/dist/services/websocket.d.ts +30 -16
- package/dist/services/websocket.d.ts.map +1 -1
- package/dist/services/websocket.js +102 -37
- package/dist/services/websocket.js.map +1 -1
- package/dist/services/worktree-sync-service.d.ts +228 -0
- package/dist/services/worktree-sync-service.d.ts.map +1 -0
- package/dist/services/worktree-sync-service.js +563 -0
- package/dist/services/worktree-sync-service.js.map +1 -0
- package/dist/types/editor.d.ts +49 -0
- package/dist/types/editor.d.ts.map +1 -0
- package/dist/types/editor.js +50 -0
- package/dist/types/editor.js.map +1 -0
- package/dist/types/project.d.ts +58 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +10 -0
- package/dist/types/project.js.map +1 -0
- package/dist/utils/executable-check.d.ts +36 -0
- package/dist/utils/executable-check.d.ts.map +1 -0
- package/dist/utils/executable-check.js +79 -0
- package/dist/utils/executable-check.js.map +1 -0
- package/dist/workers/execution-worker.d.ts +18 -0
- package/dist/workers/execution-worker.d.ts.map +1 -0
- package/dist/workers/execution-worker.js +340 -0
- package/dist/workers/execution-worker.js.map +1 -0
- package/dist/workers/worker-ipc.d.ts +84 -0
- package/dist/workers/worker-ipc.d.ts.map +1 -0
- package/dist/workers/worker-ipc.js +29 -0
- package/dist/workers/worker-ipc.js.map +1 -0
- package/package.json +6 -5
- package/dist/execution/output/ag-ui-integration.d.ts +0 -96
- package/dist/execution/output/ag-ui-integration.d.ts.map +0 -1
- package/dist/execution/output/ag-ui-integration.js +0 -96
- package/dist/execution/output/ag-ui-integration.js.map +0 -1
- package/dist/execution/output/claude-code-output-processor.d.ts +0 -321
- package/dist/execution/output/claude-code-output-processor.d.ts.map +0 -1
- package/dist/execution/output/claude-code-output-processor.js +0 -769
- package/dist/execution/output/claude-code-output-processor.js.map +0 -1
- package/dist/public/assets/index-B3SEMufD.js +0 -580
- package/dist/public/assets/index-B3SEMufD.js.map +0 -1
- package/dist/public/assets/index-D2YGL3gX.css +0 -1
- package/dist/public/assets/ui-vendor-CotR6bx9.js +0 -54
- package/dist/public/assets/ui-vendor-CotR6bx9.js.map +0 -1
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentExecutorWrapper - Generic wrapper for any agent adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified execution interface that works with any IAgentAdapter.
|
|
5
|
+
* Supports Claude Code, Codex, Cursor, Copilot, and any future agents.
|
|
6
|
+
*
|
|
7
|
+
* @module execution/executors/agent-executor-wrapper
|
|
8
|
+
*/
|
|
9
|
+
import { ClaudeCodeExecutor, CodexExecutor, CursorExecutor, CopilotExecutor, } from "agent-execution-engine/agents";
|
|
10
|
+
import { NormalizedEntryToAgUiAdapter } from "../output/normalized-to-ag-ui-adapter.js";
|
|
11
|
+
import { AgUiEventAdapter } from "../output/ag-ui-adapter.js";
|
|
12
|
+
import { updateExecution, getExecution } from "../../services/executions.js";
|
|
13
|
+
import { broadcastExecutionUpdate } from "../../services/websocket.js";
|
|
14
|
+
import { execSync } from "child_process";
|
|
15
|
+
import { ExecutionChangesService } from "../../services/execution-changes-service.js";
|
|
16
|
+
/**
|
|
17
|
+
* Generic wrapper for any agent adapter
|
|
18
|
+
*
|
|
19
|
+
* Provides basic execution lifecycle management for any agent that implements
|
|
20
|
+
* IAgentAdapter. This is a simplified version compared to ClaudeExecutorWrapper,
|
|
21
|
+
* which has specialized logic for Claude Code's protocol peer.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const wrapper = new AgentExecutorWrapper({
|
|
26
|
+
* adapter: codexAdapter,
|
|
27
|
+
* agentConfig: {
|
|
28
|
+
* workDir: '/path/to/repo',
|
|
29
|
+
* apiKey: 'sk-...',
|
|
30
|
+
* model: 'code-davinci-002',
|
|
31
|
+
* },
|
|
32
|
+
* lifecycleService,
|
|
33
|
+
* logsStore,
|
|
34
|
+
* projectId: 'my-project',
|
|
35
|
+
* db,
|
|
36
|
+
* transportManager,
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* await wrapper.executeWithLifecycle(executionId, task, workDir);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export class AgentExecutorWrapper {
|
|
43
|
+
adapter;
|
|
44
|
+
executor;
|
|
45
|
+
/** Agent type used for executor selection and logging */
|
|
46
|
+
agentType;
|
|
47
|
+
_agentConfig;
|
|
48
|
+
logsStore;
|
|
49
|
+
transportManager;
|
|
50
|
+
projectId;
|
|
51
|
+
db;
|
|
52
|
+
processConfig;
|
|
53
|
+
activeExecutions;
|
|
54
|
+
/** Track completion state for Claude Code executions (from protocol peer) */
|
|
55
|
+
completionState;
|
|
56
|
+
constructor(config) {
|
|
57
|
+
this.adapter = config.adapter;
|
|
58
|
+
this.agentType = config.agentType;
|
|
59
|
+
this._agentConfig = config.agentConfig;
|
|
60
|
+
this.logsStore = config.logsStore;
|
|
61
|
+
this.transportManager = config.transportManager;
|
|
62
|
+
this.projectId = config.projectId;
|
|
63
|
+
this.db = config.db;
|
|
64
|
+
this.activeExecutions = new Map();
|
|
65
|
+
this.completionState = new Map();
|
|
66
|
+
// Build process configuration from agent-specific config
|
|
67
|
+
this.processConfig = this.adapter.buildProcessConfig(this._agentConfig);
|
|
68
|
+
// Create executor instance based on agent type
|
|
69
|
+
this.executor = this.createExecutor(config.agentType, this._agentConfig);
|
|
70
|
+
console.log("[AgentExecutorWrapper] Initialized", {
|
|
71
|
+
agentType: this.agentType,
|
|
72
|
+
adapterName: this.adapter.metadata.name,
|
|
73
|
+
projectId: this.projectId,
|
|
74
|
+
workDir: this.processConfig.workDir,
|
|
75
|
+
hasTransport: !!this.transportManager,
|
|
76
|
+
hasLogsStore: !!this.logsStore,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create the appropriate executor for the given agent type
|
|
81
|
+
*
|
|
82
|
+
* @param agentType - Type of agent
|
|
83
|
+
* @param agentConfig - Agent configuration
|
|
84
|
+
* @returns Executor instance
|
|
85
|
+
*/
|
|
86
|
+
createExecutor(agentType, agentConfig) {
|
|
87
|
+
switch (agentType) {
|
|
88
|
+
case "claude-code":
|
|
89
|
+
return new ClaudeCodeExecutor({
|
|
90
|
+
workDir: agentConfig.workDir,
|
|
91
|
+
executablePath: agentConfig.claudePath,
|
|
92
|
+
print: agentConfig.print ?? true,
|
|
93
|
+
outputFormat: agentConfig.outputFormat ?? "stream-json",
|
|
94
|
+
verbose: agentConfig.verbose ?? true,
|
|
95
|
+
dangerouslySkipPermissions: agentConfig.dangerouslySkipPermissions ?? true,
|
|
96
|
+
});
|
|
97
|
+
case "codex":
|
|
98
|
+
return new CodexExecutor(agentConfig);
|
|
99
|
+
case "cursor":
|
|
100
|
+
return new CursorExecutor(agentConfig);
|
|
101
|
+
case "copilot":
|
|
102
|
+
return new CopilotExecutor(agentConfig);
|
|
103
|
+
default:
|
|
104
|
+
throw new Error(`Unknown agent type: ${agentType}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Execute a task with full lifecycle management
|
|
109
|
+
*
|
|
110
|
+
* @param executionId - Unique execution identifier
|
|
111
|
+
* @param task - Task to execute
|
|
112
|
+
* @param workDir - Working directory for execution
|
|
113
|
+
*/
|
|
114
|
+
async executeWithLifecycle(executionId, task, workDir) {
|
|
115
|
+
console.log(`[AgentExecutorWrapper] Starting execution ${executionId}`, {
|
|
116
|
+
agentType: this.adapter.metadata.name,
|
|
117
|
+
taskId: task.id,
|
|
118
|
+
workDir,
|
|
119
|
+
});
|
|
120
|
+
// 1. Setup AG-UI system
|
|
121
|
+
const { agUiAdapter, normalizedAdapter } = this.setupAgUiSystem(executionId);
|
|
122
|
+
// 2. Connect to transport
|
|
123
|
+
if (this.transportManager) {
|
|
124
|
+
this.transportManager.connectAdapter(agUiAdapter, executionId);
|
|
125
|
+
console.log(`[AgentExecutorWrapper] Connected AG-UI adapter to transport for ${executionId}`);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
// 3. Emit run started event
|
|
129
|
+
agUiAdapter.emitRunStarted({
|
|
130
|
+
model: task.config?.model || this.adapter.metadata.name,
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
});
|
|
133
|
+
// 4. Update execution status to running
|
|
134
|
+
updateExecution(this.db, executionId, { status: "running" });
|
|
135
|
+
const execution = getExecution(this.db, executionId);
|
|
136
|
+
if (execution) {
|
|
137
|
+
broadcastExecutionUpdate(this.projectId, executionId, "status_changed", execution, execution.issue_id || undefined);
|
|
138
|
+
}
|
|
139
|
+
// 5. Execute task with agent executor
|
|
140
|
+
console.log(`[AgentExecutorWrapper] Spawning ${this.adapter.metadata.name} process for ${executionId}`, {
|
|
141
|
+
taskId: task.id,
|
|
142
|
+
workDir,
|
|
143
|
+
promptLength: task.prompt.length,
|
|
144
|
+
});
|
|
145
|
+
const spawned = await this.executor.executeTask(task);
|
|
146
|
+
console.log(`[AgentExecutorWrapper] ${this.adapter.metadata.name} process spawned for ${executionId}`, {
|
|
147
|
+
pid: spawned.process.process?.pid,
|
|
148
|
+
spawnfile: spawned.process.process?.spawnfile,
|
|
149
|
+
});
|
|
150
|
+
// 6. Store cancellation handle
|
|
151
|
+
this.activeExecutions.set(executionId, {
|
|
152
|
+
cancel: () => {
|
|
153
|
+
if (spawned.process.process) {
|
|
154
|
+
spawned.process.process.kill("SIGTERM");
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
// 7. Initialize completion state for Claude Code
|
|
159
|
+
if (this.agentType === "claude-code") {
|
|
160
|
+
this.completionState.set(executionId, {
|
|
161
|
+
completed: false,
|
|
162
|
+
exitCode: 0,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// 8. Create output stream from process stdout/stderr
|
|
166
|
+
const outputStream = this.createOutputChunks(spawned.process, executionId);
|
|
167
|
+
const normalized = this.executor.normalizeOutput(outputStream, workDir);
|
|
168
|
+
// 9. Process normalized output (runs concurrently with process)
|
|
169
|
+
const processOutputPromise = this.processNormalizedOutput(executionId, normalized, normalizedAdapter);
|
|
170
|
+
// 10. Capture stderr for debugging
|
|
171
|
+
const childProcess = spawned.process.process;
|
|
172
|
+
if (childProcess && childProcess.stderr) {
|
|
173
|
+
let stderrOutput = "";
|
|
174
|
+
childProcess.stderr.on("data", (data) => {
|
|
175
|
+
const chunk = data.toString();
|
|
176
|
+
stderrOutput += chunk;
|
|
177
|
+
console.error(`[AgentExecutorWrapper] ${this.adapter.metadata.name} stderr for ${executionId}:`, chunk);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// 11. Wait for output processing to complete
|
|
181
|
+
await processOutputPromise;
|
|
182
|
+
console.log(`[AgentExecutorWrapper] Output processing completed for ${executionId}`);
|
|
183
|
+
// 12. Close stdin to signal the process to exit (for Claude Code with peer)
|
|
184
|
+
if (this.agentType === "claude-code" &&
|
|
185
|
+
childProcess &&
|
|
186
|
+
childProcess.stdin) {
|
|
187
|
+
try {
|
|
188
|
+
childProcess.stdin.end();
|
|
189
|
+
console.log(`[AgentExecutorWrapper] Closed stdin for Claude Code process ${executionId}`);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.error(`[AgentExecutorWrapper] Error closing stdin for ${executionId}:`, error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// 13. Wait for process to exit (with timeout for Claude Code)
|
|
196
|
+
const exitCode = await Promise.race([
|
|
197
|
+
new Promise((resolve) => {
|
|
198
|
+
if (!childProcess) {
|
|
199
|
+
// Use completion state for Claude Code if available
|
|
200
|
+
if (this.agentType === "claude-code") {
|
|
201
|
+
const state = this.completionState.get(executionId);
|
|
202
|
+
resolve(state?.exitCode ?? 0);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
resolve(0);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
childProcess.on("exit", (code) => {
|
|
210
|
+
console.log(`[AgentExecutorWrapper] Process exited with code ${code} for ${executionId}`);
|
|
211
|
+
// For Claude Code, use completion state exit code if available (more reliable)
|
|
212
|
+
if (this.agentType === "claude-code") {
|
|
213
|
+
const state = this.completionState.get(executionId);
|
|
214
|
+
if (state?.completed) {
|
|
215
|
+
console.log(`[AgentExecutorWrapper] Using completion state exit code ${state.exitCode} for ${executionId}`);
|
|
216
|
+
resolve(state.exitCode);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
resolve(code ?? 0);
|
|
221
|
+
});
|
|
222
|
+
childProcess.on("error", (error) => {
|
|
223
|
+
console.error(`[AgentExecutorWrapper] Process error for ${executionId}:`, error);
|
|
224
|
+
resolve(1);
|
|
225
|
+
});
|
|
226
|
+
}),
|
|
227
|
+
// Timeout for Claude Code processes that may not exit cleanly
|
|
228
|
+
this.agentType === "claude-code"
|
|
229
|
+
? new Promise((resolve) => {
|
|
230
|
+
setTimeout(() => {
|
|
231
|
+
console.log(`[AgentExecutorWrapper] Process exit timeout for ${executionId}, using completion state`);
|
|
232
|
+
if (childProcess && !childProcess.killed) {
|
|
233
|
+
childProcess.kill("SIGTERM");
|
|
234
|
+
}
|
|
235
|
+
// Use completion state exit code
|
|
236
|
+
const state = this.completionState.get(executionId);
|
|
237
|
+
resolve(state?.exitCode ?? 0);
|
|
238
|
+
}, 5000);
|
|
239
|
+
})
|
|
240
|
+
: new Promise(() => { }), // Never resolves for non-Claude agents
|
|
241
|
+
]);
|
|
242
|
+
// 14. Handle completion
|
|
243
|
+
if (exitCode === 0) {
|
|
244
|
+
await this.handleSuccess(executionId);
|
|
245
|
+
agUiAdapter.emitRunFinished({ exitCode });
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
throw new Error(`Process exited with code ${exitCode}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
console.error(`[AgentExecutorWrapper] Execution failed for ${executionId}:`, error);
|
|
253
|
+
await this.handleError(executionId, error);
|
|
254
|
+
agUiAdapter.emitRunError(error instanceof Error ? error.message : String(error));
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
// Cleanup
|
|
259
|
+
this.activeExecutions.delete(executionId);
|
|
260
|
+
this.completionState.delete(executionId);
|
|
261
|
+
if (this.transportManager) {
|
|
262
|
+
this.transportManager.disconnectAdapter(agUiAdapter);
|
|
263
|
+
console.log(`[AgentExecutorWrapper] Disconnected AG-UI adapter for ${executionId}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Resume a task from a previous session
|
|
269
|
+
*
|
|
270
|
+
* @param executionId - Unique execution identifier
|
|
271
|
+
* @param sessionId - Session ID to resume from
|
|
272
|
+
* @param task - Task to resume
|
|
273
|
+
* @param workDir - Working directory for execution
|
|
274
|
+
*/
|
|
275
|
+
async resumeWithLifecycle(executionId, sessionId, task, workDir) {
|
|
276
|
+
// Check if executor supports resume
|
|
277
|
+
const capabilities = this.executor.getCapabilities?.();
|
|
278
|
+
if (!capabilities?.supportsSessionResume || !this.executor.resumeTask) {
|
|
279
|
+
console.log(`[AgentExecutorWrapper] Resume not supported for agent '${this.adapter.metadata.name}'`);
|
|
280
|
+
throw new Error(`Resume functionality not supported for agent '${this.adapter.metadata.name}'`);
|
|
281
|
+
}
|
|
282
|
+
console.log(`[AgentExecutorWrapper] Resuming session ${sessionId} for ${executionId}`);
|
|
283
|
+
// Setup AG-UI system
|
|
284
|
+
const { agUiAdapter, normalizedAdapter } = this.setupAgUiSystem(executionId);
|
|
285
|
+
if (this.transportManager) {
|
|
286
|
+
this.transportManager.connectAdapter(agUiAdapter, executionId);
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
agUiAdapter.emitRunStarted({
|
|
290
|
+
model: task.config?.model || this.adapter.metadata.name,
|
|
291
|
+
sessionId,
|
|
292
|
+
resumed: true,
|
|
293
|
+
});
|
|
294
|
+
// Update status and session_id (sessionId is the path used for resumption)
|
|
295
|
+
updateExecution(this.db, executionId, {
|
|
296
|
+
status: "running",
|
|
297
|
+
session_id: sessionId,
|
|
298
|
+
});
|
|
299
|
+
const execution = getExecution(this.db, executionId);
|
|
300
|
+
if (execution) {
|
|
301
|
+
broadcastExecutionUpdate(this.projectId, executionId, "status_changed", execution, execution.issue_id || undefined);
|
|
302
|
+
}
|
|
303
|
+
// Use resumeTask instead of executeTask
|
|
304
|
+
const spawned = await this.executor.resumeTask(task, sessionId);
|
|
305
|
+
this.activeExecutions.set(executionId, {
|
|
306
|
+
cancel: () => {
|
|
307
|
+
if (spawned.process.process) {
|
|
308
|
+
spawned.process.process.kill("SIGTERM");
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
// Initialize completion state for Claude Code
|
|
313
|
+
if (this.agentType === "claude-code") {
|
|
314
|
+
this.completionState.set(executionId, {
|
|
315
|
+
completed: false,
|
|
316
|
+
exitCode: 0,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// Create output streams
|
|
320
|
+
const outputChunks = this.createOutputChunks(spawned.process, executionId);
|
|
321
|
+
const normalized = this.executor.normalizeOutput(outputChunks, workDir);
|
|
322
|
+
const processOutputPromise = this.processNormalizedOutput(executionId, normalized, normalizedAdapter);
|
|
323
|
+
await processOutputPromise;
|
|
324
|
+
// For Claude Code, use completion state; for others, wait for process exit
|
|
325
|
+
let exitCode = 0;
|
|
326
|
+
if (this.agentType === "claude-code") {
|
|
327
|
+
const state = this.completionState.get(executionId);
|
|
328
|
+
exitCode = state?.exitCode ?? 0;
|
|
329
|
+
console.log(`[AgentExecutorWrapper] Using completion state exit code ${exitCode} for resumed ${executionId}`);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
exitCode = await new Promise((resolve, reject) => {
|
|
333
|
+
const childProcess = spawned.process.process;
|
|
334
|
+
if (!childProcess) {
|
|
335
|
+
reject(new Error("No child process available"));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
childProcess.on("exit", (code) => resolve(code || 0));
|
|
339
|
+
childProcess.on("error", (error) => reject(error));
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (exitCode === 0) {
|
|
343
|
+
await this.handleSuccess(executionId);
|
|
344
|
+
agUiAdapter.emitRunFinished({ exitCode });
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
throw new Error(`Process exited with code ${exitCode}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
await this.handleError(executionId, error);
|
|
352
|
+
agUiAdapter.emitRunError(error instanceof Error ? error.message : String(error));
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
finally {
|
|
356
|
+
this.activeExecutions.delete(executionId);
|
|
357
|
+
this.completionState.delete(executionId);
|
|
358
|
+
if (this.transportManager) {
|
|
359
|
+
this.transportManager.disconnectAdapter(agUiAdapter);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Cancel a running execution
|
|
365
|
+
*
|
|
366
|
+
* @param executionId - Execution ID to cancel
|
|
367
|
+
*/
|
|
368
|
+
async cancel(executionId) {
|
|
369
|
+
console.log(`[AgentExecutorWrapper] Cancel execution ${executionId}`);
|
|
370
|
+
// Kill the process if active
|
|
371
|
+
const execution = this.activeExecutions.get(executionId);
|
|
372
|
+
if (execution) {
|
|
373
|
+
execution.cancel();
|
|
374
|
+
this.activeExecutions.delete(executionId);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.warn(`[AgentExecutorWrapper] No active execution found for ${executionId}`);
|
|
378
|
+
}
|
|
379
|
+
// Capture final commit before marking stopped
|
|
380
|
+
const dbExecution = getExecution(this.db, executionId);
|
|
381
|
+
const repoPath = dbExecution?.worktree_path || this.processConfig.workDir;
|
|
382
|
+
let afterCommit;
|
|
383
|
+
try {
|
|
384
|
+
afterCommit = execSync("git rev-parse HEAD", {
|
|
385
|
+
cwd: repoPath,
|
|
386
|
+
encoding: "utf-8",
|
|
387
|
+
}).trim();
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
console.warn(`[AgentExecutorWrapper] Failed to capture after_commit for cancelled execution ${executionId}:`, error instanceof Error ? error.message : String(error));
|
|
391
|
+
// Continue - this is supplementary data
|
|
392
|
+
}
|
|
393
|
+
// Update database status
|
|
394
|
+
updateExecution(this.db, executionId, {
|
|
395
|
+
after_commit: afterCommit,
|
|
396
|
+
status: "stopped",
|
|
397
|
+
completed_at: new Date().toISOString(),
|
|
398
|
+
});
|
|
399
|
+
const updatedExecution = getExecution(this.db, executionId);
|
|
400
|
+
if (updatedExecution) {
|
|
401
|
+
broadcastExecutionUpdate(this.projectId, executionId, "status_changed", updatedExecution, updatedExecution.issue_id || undefined);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Setup AG-UI system for execution
|
|
406
|
+
*
|
|
407
|
+
* @private
|
|
408
|
+
*/
|
|
409
|
+
setupAgUiSystem(executionId) {
|
|
410
|
+
const agUiAdapter = new AgUiEventAdapter(executionId);
|
|
411
|
+
const normalizedAdapter = new NormalizedEntryToAgUiAdapter(agUiAdapter);
|
|
412
|
+
console.log(`[AgentExecutorWrapper] Setup AG-UI system for ${executionId}`);
|
|
413
|
+
return { agUiAdapter, normalizedAdapter };
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Process normalized output from agent
|
|
417
|
+
*
|
|
418
|
+
* For Claude Code: Also captures session ID from metadata for session resumption
|
|
419
|
+
*
|
|
420
|
+
* @private
|
|
421
|
+
*/
|
|
422
|
+
async processNormalizedOutput(executionId, normalized, normalizedAdapter) {
|
|
423
|
+
console.log(`[AgentExecutorWrapper] Processing normalized output for ${executionId}`);
|
|
424
|
+
let entryCount = 0;
|
|
425
|
+
let sessionIdCaptured = false;
|
|
426
|
+
for await (const entry of normalized) {
|
|
427
|
+
entryCount++;
|
|
428
|
+
// Log first 10 entries and every 100th entry for debugging
|
|
429
|
+
if (entryCount <= 10 || entryCount % 100 === 0) {
|
|
430
|
+
console.log(`[AgentExecutorWrapper] Entry ${entryCount} for ${executionId}:`, {
|
|
431
|
+
index: entry.index,
|
|
432
|
+
kind: entry.type.kind,
|
|
433
|
+
timestamp: entry.timestamp,
|
|
434
|
+
hasMetadata: !!entry.metadata,
|
|
435
|
+
sessionId: entry.metadata?.sessionId,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
// Capture session ID from metadata for Claude Code (populated by normalizer from SystemMessage)
|
|
440
|
+
if (this.agentType === "claude-code" &&
|
|
441
|
+
!sessionIdCaptured &&
|
|
442
|
+
entry.metadata?.sessionId) {
|
|
443
|
+
const sessionId = entry.metadata.sessionId;
|
|
444
|
+
updateExecution(this.db, executionId, { session_id: sessionId });
|
|
445
|
+
console.log(`[AgentExecutorWrapper] Captured session ID from metadata: ${sessionId} for ${executionId}`);
|
|
446
|
+
sessionIdCaptured = true;
|
|
447
|
+
}
|
|
448
|
+
// 1. Store normalized entry for historical replay
|
|
449
|
+
this.logsStore.appendNormalizedEntry(executionId, entry);
|
|
450
|
+
// 2. Convert to AG-UI and broadcast for real-time streaming
|
|
451
|
+
await normalizedAdapter.processEntry(entry);
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
console.error(`[AgentExecutorWrapper] Error processing entry for ${executionId}:`, {
|
|
455
|
+
entryIndex: entry.index,
|
|
456
|
+
entryType: entry.type.kind,
|
|
457
|
+
error: error instanceof Error ? error.message : String(error),
|
|
458
|
+
});
|
|
459
|
+
// Continue processing (don't fail entire execution for one entry)
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
console.log(`[AgentExecutorWrapper] Finished processing ${entryCount} entries for ${executionId}`);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Handle successful execution
|
|
466
|
+
*
|
|
467
|
+
* @private
|
|
468
|
+
*/
|
|
469
|
+
async handleSuccess(executionId) {
|
|
470
|
+
console.log(`[AgentExecutorWrapper] Execution ${executionId} completed successfully`);
|
|
471
|
+
// Capture final commit before marking complete
|
|
472
|
+
const execution = getExecution(this.db, executionId);
|
|
473
|
+
const repoPath = execution?.worktree_path || this.processConfig.workDir;
|
|
474
|
+
let afterCommit;
|
|
475
|
+
try {
|
|
476
|
+
afterCommit = execSync("git rev-parse HEAD", {
|
|
477
|
+
cwd: repoPath,
|
|
478
|
+
encoding: "utf-8",
|
|
479
|
+
}).trim();
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
console.warn(`[AgentExecutorWrapper] Failed to capture after_commit for execution ${executionId}:`, error instanceof Error ? error.message : String(error));
|
|
483
|
+
// Continue - this is supplementary data
|
|
484
|
+
}
|
|
485
|
+
// Calculate file changes using ExecutionChangesService
|
|
486
|
+
let filesChangedJson = null;
|
|
487
|
+
try {
|
|
488
|
+
// Import and instantiate ExecutionChangesService
|
|
489
|
+
const changesService = new ExecutionChangesService(this.db, this.processConfig.workDir);
|
|
490
|
+
// Get changes for this execution
|
|
491
|
+
const changesResult = await changesService.getChanges(executionId);
|
|
492
|
+
if (changesResult.available && changesResult.captured) {
|
|
493
|
+
// Extract just the file paths from the changes
|
|
494
|
+
const filePaths = changesResult.captured.files.map((f) => f.path);
|
|
495
|
+
filesChangedJson = JSON.stringify(filePaths);
|
|
496
|
+
console.log(`[AgentExecutorWrapper] Captured ${filePaths.length} file changes for execution ${executionId}`);
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
console.log(`[AgentExecutorWrapper] No file changes detected for execution ${executionId}:`, changesResult.reason);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
console.warn(`[AgentExecutorWrapper] Failed to calculate files_changed for execution ${executionId}:`, error instanceof Error ? error.message : String(error));
|
|
504
|
+
// Continue - this is supplementary data
|
|
505
|
+
}
|
|
506
|
+
updateExecution(this.db, executionId, {
|
|
507
|
+
after_commit: afterCommit,
|
|
508
|
+
files_changed: filesChangedJson,
|
|
509
|
+
status: "completed",
|
|
510
|
+
completed_at: new Date().toISOString(),
|
|
511
|
+
exit_code: 0,
|
|
512
|
+
});
|
|
513
|
+
const updatedExecution = getExecution(this.db, executionId);
|
|
514
|
+
if (updatedExecution) {
|
|
515
|
+
broadcastExecutionUpdate(this.projectId, executionId, "status_changed", updatedExecution, updatedExecution.issue_id || undefined);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Handle execution error
|
|
520
|
+
*
|
|
521
|
+
* @private
|
|
522
|
+
*/
|
|
523
|
+
async handleError(executionId, error) {
|
|
524
|
+
console.error(`[AgentExecutorWrapper] Execution ${executionId} failed:`, error);
|
|
525
|
+
// Calculate file changes even for failed executions
|
|
526
|
+
// (user may want to commit partial work)
|
|
527
|
+
let filesChangedJson = null;
|
|
528
|
+
try {
|
|
529
|
+
const changesService = new ExecutionChangesService(this.db, this.processConfig.workDir);
|
|
530
|
+
const changesResult = await changesService.getChanges(executionId);
|
|
531
|
+
if (changesResult.available && changesResult.captured) {
|
|
532
|
+
const filePaths = changesResult.captured.files.map((f) => f.path);
|
|
533
|
+
filesChangedJson = JSON.stringify(filePaths);
|
|
534
|
+
console.log(`[AgentExecutorWrapper] Captured ${filePaths.length} file changes for failed execution ${executionId}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch (calcError) {
|
|
538
|
+
console.warn(`[AgentExecutorWrapper] Failed to calculate files_changed for failed execution ${executionId}:`, calcError instanceof Error ? calcError.message : String(calcError));
|
|
539
|
+
}
|
|
540
|
+
updateExecution(this.db, executionId, {
|
|
541
|
+
status: "failed",
|
|
542
|
+
completed_at: new Date().toISOString(),
|
|
543
|
+
error_message: error.message,
|
|
544
|
+
files_changed: filesChangedJson,
|
|
545
|
+
});
|
|
546
|
+
const execution = getExecution(this.db, executionId);
|
|
547
|
+
if (execution) {
|
|
548
|
+
broadcastExecutionUpdate(this.projectId, executionId, "status_changed", execution, execution.issue_id || undefined);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Create output chunk stream from ManagedProcess
|
|
553
|
+
*
|
|
554
|
+
* For Claude Code: Uses protocol peer to receive messages
|
|
555
|
+
* For other agents: Reads directly from stdout/stderr streams
|
|
556
|
+
*
|
|
557
|
+
* @private
|
|
558
|
+
*/
|
|
559
|
+
async *createOutputChunks(process, executionId) {
|
|
560
|
+
// Check if this is a Claude Code process with a protocol peer
|
|
561
|
+
const peer = process.peer;
|
|
562
|
+
if (this.agentType === "claude-code") {
|
|
563
|
+
// Claude Code REQUIRES a protocol peer
|
|
564
|
+
if (!peer) {
|
|
565
|
+
throw new Error("No peer attached to Claude Code process - cannot read output");
|
|
566
|
+
}
|
|
567
|
+
// Claude Code: Use protocol peer messages
|
|
568
|
+
console.log("[AgentExecutorWrapper] Using protocol peer for Claude Code output");
|
|
569
|
+
yield* this.peerMessagesToOutputChunks(peer, executionId);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
// Other agents: Use stdout/stderr streams
|
|
573
|
+
if (!process.streams) {
|
|
574
|
+
throw new Error("Process does not have streams available");
|
|
575
|
+
}
|
|
576
|
+
const { stdout, stderr } = process.streams;
|
|
577
|
+
// Merge stdout and stderr
|
|
578
|
+
const streams = [];
|
|
579
|
+
if (stdout) {
|
|
580
|
+
streams.push(this.streamToChunks(stdout, "stdout"));
|
|
581
|
+
}
|
|
582
|
+
if (stderr) {
|
|
583
|
+
streams.push(this.streamToChunks(stderr, "stderr"));
|
|
584
|
+
}
|
|
585
|
+
// Yield chunks from all streams
|
|
586
|
+
for (const stream of streams) {
|
|
587
|
+
for await (const chunk of stream) {
|
|
588
|
+
yield chunk;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Convert protocol peer messages to output chunks
|
|
594
|
+
*
|
|
595
|
+
* Used for Claude Code which uses a protocol peer that consumes stdout
|
|
596
|
+
* Updates completion state when result message is received
|
|
597
|
+
*
|
|
598
|
+
* @private
|
|
599
|
+
*/
|
|
600
|
+
async *peerMessagesToOutputChunks(peer, executionId) {
|
|
601
|
+
const messageQueue = [];
|
|
602
|
+
let streamEnded = false;
|
|
603
|
+
let exitDetected = false;
|
|
604
|
+
// Register message handler
|
|
605
|
+
peer.onMessage((message) => {
|
|
606
|
+
messageQueue.push(message);
|
|
607
|
+
// Detect completion and update completion state
|
|
608
|
+
if (message.type === "result" &&
|
|
609
|
+
(message.subtype === "success" || message.subtype === "failure")) {
|
|
610
|
+
exitDetected = true;
|
|
611
|
+
const exitCode = message.subtype === "success" ? 0 : 1;
|
|
612
|
+
this.completionState.set(executionId, { completed: true, exitCode });
|
|
613
|
+
console.log(`[AgentExecutorWrapper] Detected completion from peer for ${executionId}:`, { subtype: message.subtype, exitCode });
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
// Process messages until completion
|
|
617
|
+
while (!streamEnded || messageQueue.length > 0) {
|
|
618
|
+
if (messageQueue.length === 0) {
|
|
619
|
+
if (exitDetected) {
|
|
620
|
+
streamEnded = true;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
// Wait for more messages
|
|
624
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
// Convert message to stream-json line format
|
|
628
|
+
const message = messageQueue.shift();
|
|
629
|
+
const line = JSON.stringify(message) + "\n";
|
|
630
|
+
yield {
|
|
631
|
+
type: "stdout",
|
|
632
|
+
data: Buffer.from(line, "utf-8"),
|
|
633
|
+
timestamp: new Date(),
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Convert a readable stream to output chunks
|
|
639
|
+
*
|
|
640
|
+
* @private
|
|
641
|
+
*/
|
|
642
|
+
async *streamToChunks(stream, type) {
|
|
643
|
+
for await (const chunk of stream) {
|
|
644
|
+
yield {
|
|
645
|
+
type,
|
|
646
|
+
data: Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk),
|
|
647
|
+
timestamp: new Date(),
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
//# sourceMappingURL=agent-executor-wrapper.js.map
|