@seawork/server 1.0.7 → 1.0.10-rc.5

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.
Files changed (178) hide show
  1. package/dist/server/client/daemon-client.d.ts +32 -1
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +69 -0
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
  6. package/dist/server/server/agent/agent-management-mcp.js +6 -5
  7. package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
  8. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
  9. package/dist/server/server/agent/agent-response-loop.js +0 -1
  10. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  11. package/dist/server/server/agent/agent-storage.d.ts +68 -68
  12. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  13. package/dist/server/server/agent/mcp-server.js +6 -5
  14. package/dist/server/server/agent/mcp-server.js.map +1 -1
  15. package/dist/server/server/agent/mcp-shared.d.ts +30 -30
  16. package/dist/server/server/agent/provider-manifest.d.ts +2 -1
  17. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  18. package/dist/server/server/agent/provider-manifest.js +3 -64
  19. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  20. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  21. package/dist/server/server/agent/provider-registry.js +2 -10
  22. package/dist/server/server/agent/provider-registry.js.map +1 -1
  23. package/dist/server/server/agent/providers/claude/claude-models.d.ts.map +1 -1
  24. package/dist/server/server/agent/providers/claude/claude-models.js +3 -0
  25. package/dist/server/server/agent/providers/claude/claude-models.js.map +1 -1
  26. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
  27. package/dist/server/server/agent/providers/claude-agent.d.ts +2 -1
  28. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  29. package/dist/server/server/agent/providers/claude-agent.js +167 -22
  30. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  31. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  32. package/dist/server/server/agent/providers/codex-app-server-agent.js +106 -3
  33. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  34. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -1
  35. package/dist/server/server/agent/providers/codex-rollout-timeline.js +76 -0
  36. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -1
  37. package/dist/server/server/agent/providers/seawork-models.d.ts.map +1 -1
  38. package/dist/server/server/agent/providers/seawork-models.js +9 -0
  39. package/dist/server/server/agent/providers/seawork-models.js.map +1 -1
  40. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +16 -16
  41. package/dist/server/server/bug-report-handler.d.ts +9 -0
  42. package/dist/server/server/bug-report-handler.d.ts.map +1 -0
  43. package/dist/server/server/bug-report-handler.js +111 -0
  44. package/dist/server/server/bug-report-handler.js.map +1 -0
  45. package/dist/server/server/bug-report-redact.d.ts +12 -0
  46. package/dist/server/server/bug-report-redact.d.ts.map +1 -0
  47. package/dist/server/server/bug-report-redact.js +63 -0
  48. package/dist/server/server/bug-report-redact.js.map +1 -0
  49. package/dist/server/server/chat/chat-rpc-schemas.d.ts +26 -26
  50. package/dist/server/server/chat/chat-types.d.ts +2 -2
  51. package/dist/server/server/exports.d.ts +4 -2
  52. package/dist/server/server/exports.d.ts.map +1 -1
  53. package/dist/server/server/exports.js +3 -0
  54. package/dist/server/server/exports.js.map +1 -1
  55. package/dist/server/server/index.js +47 -0
  56. package/dist/server/server/index.js.map +1 -1
  57. package/dist/server/server/loop/rpc-schemas.d.ts +775 -775
  58. package/dist/server/server/loop-service.d.ts +108 -108
  59. package/dist/server/server/persisted-config.d.ts +121 -121
  60. package/dist/server/server/sac/errors.d.ts +1 -0
  61. package/dist/server/server/sac/errors.d.ts.map +1 -1
  62. package/dist/server/server/sac/errors.js +2 -1
  63. package/dist/server/server/sac/errors.js.map +1 -1
  64. package/dist/server/server/sac/generate.d.ts +19 -11
  65. package/dist/server/server/sac/generate.d.ts.map +1 -1
  66. package/dist/server/server/sac/generate.js +131 -36
  67. package/dist/server/server/sac/generate.js.map +1 -1
  68. package/dist/server/server/sac/index.d.ts +8 -10
  69. package/dist/server/server/sac/index.d.ts.map +1 -1
  70. package/dist/server/server/sac/index.js +5 -17
  71. package/dist/server/server/sac/index.js.map +1 -1
  72. package/dist/server/server/sac/providers/alibaba.d.ts +2 -0
  73. package/dist/server/server/sac/providers/alibaba.d.ts.map +1 -0
  74. package/dist/server/server/sac/providers/alibaba.js +506 -0
  75. package/dist/server/server/sac/providers/alibaba.js.map +1 -0
  76. package/dist/server/server/sac/providers/audio.d.ts +2 -0
  77. package/dist/server/server/sac/providers/audio.d.ts.map +1 -0
  78. package/dist/server/server/sac/providers/audio.js +57 -0
  79. package/dist/server/server/sac/providers/audio.js.map +1 -0
  80. package/dist/server/server/sac/providers/kling.d.ts +2 -0
  81. package/dist/server/server/sac/providers/kling.d.ts.map +1 -0
  82. package/dist/server/server/sac/providers/kling.js +589 -0
  83. package/dist/server/server/sac/providers/kling.js.map +1 -0
  84. package/dist/server/server/sac/providers/nano.d.ts +2 -0
  85. package/dist/server/server/sac/providers/nano.d.ts.map +1 -0
  86. package/dist/server/server/sac/providers/nano.js +37 -0
  87. package/dist/server/server/sac/providers/nano.js.map +1 -0
  88. package/dist/server/server/sac/providers/pixverse.d.ts +2 -0
  89. package/dist/server/server/sac/providers/pixverse.d.ts.map +1 -0
  90. package/dist/server/server/sac/providers/pixverse.js +1017 -0
  91. package/dist/server/server/sac/providers/pixverse.js.map +1 -0
  92. package/dist/server/server/sac/providers/registry.d.ts +34 -0
  93. package/dist/server/server/sac/providers/registry.d.ts.map +1 -0
  94. package/dist/server/server/sac/providers/registry.js +57 -0
  95. package/dist/server/server/sac/providers/registry.js.map +1 -0
  96. package/dist/server/server/sac/providers/seaart.d.ts +1 -4
  97. package/dist/server/server/sac/providers/seaart.d.ts.map +1 -1
  98. package/dist/server/server/sac/providers/seaart.js +8 -18
  99. package/dist/server/server/sac/providers/seaart.js.map +1 -1
  100. package/dist/server/server/sac/providers/store.d.ts +9 -5
  101. package/dist/server/server/sac/providers/store.d.ts.map +1 -1
  102. package/dist/server/server/sac/providers/store.js +0 -3
  103. package/dist/server/server/sac/providers/store.js.map +1 -1
  104. package/dist/server/server/sac/providers/tencent-3d.d.ts +2 -0
  105. package/dist/server/server/sac/providers/tencent-3d.d.ts.map +1 -0
  106. package/dist/server/server/sac/providers/tencent-3d.js +210 -0
  107. package/dist/server/server/sac/providers/tencent-3d.js.map +1 -0
  108. package/dist/server/server/sac/providers/tencent-image.d.ts +2 -0
  109. package/dist/server/server/sac/providers/tencent-image.d.ts.map +1 -0
  110. package/dist/server/server/sac/providers/tencent-image.js +90 -0
  111. package/dist/server/server/sac/providers/tencent-image.js.map +1 -0
  112. package/dist/server/server/sac/providers/tencent-video.d.ts +2 -0
  113. package/dist/server/server/sac/providers/tencent-video.d.ts.map +1 -0
  114. package/dist/server/server/sac/providers/tencent-video.js +103 -0
  115. package/dist/server/server/sac/providers/tencent-video.js.map +1 -0
  116. package/dist/server/server/sac/providers/tripo3d.d.ts +2 -0
  117. package/dist/server/server/sac/providers/tripo3d.d.ts.map +1 -0
  118. package/dist/server/server/sac/providers/tripo3d.js +302 -0
  119. package/dist/server/server/sac/providers/tripo3d.js.map +1 -0
  120. package/dist/server/server/sac/providers/vidu.d.ts +2 -0
  121. package/dist/server/server/sac/providers/vidu.d.ts.map +1 -0
  122. package/dist/server/server/sac/providers/vidu.js +965 -0
  123. package/dist/server/server/sac/providers/vidu.js.map +1 -0
  124. package/dist/server/server/sac/providers/volces-3d.d.ts +2 -0
  125. package/dist/server/server/sac/providers/volces-3d.d.ts.map +1 -0
  126. package/dist/server/server/sac/providers/volces-3d.js +77 -0
  127. package/dist/server/server/sac/providers/volces-3d.js.map +1 -0
  128. package/dist/server/server/sac/providers/volces-video.d.ts +2 -0
  129. package/dist/server/server/sac/providers/volces-video.d.ts.map +1 -0
  130. package/dist/server/server/sac/providers/volces-video.js +392 -0
  131. package/dist/server/server/sac/providers/volces-video.js.map +1 -0
  132. package/dist/server/server/sac/providers/volces.d.ts +1 -4
  133. package/dist/server/server/sac/providers/volces.d.ts.map +1 -1
  134. package/dist/server/server/sac/providers/volces.js +279 -66
  135. package/dist/server/server/sac/providers/volces.js.map +1 -1
  136. package/dist/server/server/sac/types.d.ts +13 -30
  137. package/dist/server/server/sac/types.d.ts.map +1 -1
  138. package/dist/server/server/schedule/rpc-schemas.d.ts +493 -493
  139. package/dist/server/server/schedule/types.d.ts +140 -140
  140. package/dist/server/server/session.d.ts +9 -2
  141. package/dist/server/server/session.d.ts.map +1 -1
  142. package/dist/server/server/session.js +137 -19
  143. package/dist/server/server/session.js.map +1 -1
  144. package/dist/server/server/speech/speech-types.d.ts +2 -2
  145. package/dist/server/server/workspace-registry.d.ts +4 -4
  146. package/dist/server/shared/messages.d.ts +17455 -15917
  147. package/dist/server/shared/messages.d.ts.map +1 -1
  148. package/dist/server/shared/messages.js +83 -0
  149. package/dist/server/shared/messages.js.map +1 -1
  150. package/dist/server/utils/spawn.d.ts.map +1 -1
  151. package/dist/server/utils/spawn.js +8 -3
  152. package/dist/server/utils/spawn.js.map +1 -1
  153. package/dist/server/utils/worktree-metadata.d.ts +4 -4
  154. package/package.json +7 -9
  155. package/dist/server/server/agent/providers/acp-agent.d.ts +0 -202
  156. package/dist/server/server/agent/providers/acp-agent.d.ts.map +0 -1
  157. package/dist/server/server/agent/providers/acp-agent.js +0 -1650
  158. package/dist/server/server/agent/providers/acp-agent.js.map +0 -1
  159. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +0 -16
  160. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +0 -1
  161. package/dist/server/server/agent/providers/copilot-acp-agent.js +0 -95
  162. package/dist/server/server/agent/providers/copilot-acp-agent.js.map +0 -1
  163. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +0 -3
  164. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +0 -1
  165. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +0 -39
  166. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +0 -1
  167. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +0 -13
  168. package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts.map +0 -1
  169. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +0 -144
  170. package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +0 -1
  171. package/dist/server/server/agent/providers/opencode-agent.d.ts +0 -121
  172. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +0 -1
  173. package/dist/server/server/agent/providers/opencode-agent.js +0 -1649
  174. package/dist/server/server/agent/providers/opencode-agent.js.map +0 -1
  175. package/dist/server/server/agent/providers/pi-acp-agent.d.ts +0 -28
  176. package/dist/server/server/agent/providers/pi-acp-agent.d.ts.map +0 -1
  177. package/dist/server/server/agent/providers/pi-acp-agent.js +0 -302
  178. package/dist/server/server/agent/providers/pi-acp-agent.js.map +0 -1
@@ -1,1650 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { Readable, Writable } from "node:stream";
5
- import { ClientSideConnection, PROTOCOL_VERSION, ndJsonStream, } from "@agentclientprotocol/sdk";
6
- import { applyProviderEnv, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
7
- import { findExecutable } from "../../../utils/executable.js";
8
- import { spawnProcess } from "../../../utils/spawn.js";
9
- const DEFAULT_ACP_CAPABILITIES = {
10
- supportsStreaming: true,
11
- supportsSessionPersistence: true,
12
- supportsDynamicModes: true,
13
- supportsMcpServers: true,
14
- supportsReasoningStream: true,
15
- supportsToolInvocations: true,
16
- };
17
- const ACP_CLIENT_CAPABILITIES = {
18
- fs: {
19
- readTextFile: true,
20
- writeTextFile: true,
21
- },
22
- terminal: true,
23
- };
24
- const COPILOT_AUTOPILOT_MODE = "https://agentclientprotocol.com/protocol/session-modes#autopilot";
25
- export function mapACPUsage(usage) {
26
- if (!usage) {
27
- return undefined;
28
- }
29
- return {
30
- inputTokens: usage.inputTokens ?? undefined,
31
- outputTokens: usage.outputTokens ?? undefined,
32
- cachedInputTokens: usage.cachedReadTokens ?? undefined,
33
- };
34
- }
35
- export function deriveModesFromACP(fallbackModes, modeState, configOptions) {
36
- if (modeState?.availableModes?.length) {
37
- return {
38
- modes: modeState.availableModes.map((mode) => ({
39
- id: mode.id,
40
- label: mode.name,
41
- description: mode.description ?? undefined,
42
- })),
43
- currentModeId: modeState.currentModeId ?? null,
44
- };
45
- }
46
- const modeOption = configOptions?.find((option) => option.type === "select" && option.category === "mode");
47
- if (modeOption?.type === "select") {
48
- const flatOptions = flattenSelectOptions(modeOption.options);
49
- return {
50
- modes: flatOptions.map((option) => ({
51
- id: option.value,
52
- label: option.name,
53
- description: option.description ?? undefined,
54
- })),
55
- currentModeId: modeOption.currentValue,
56
- };
57
- }
58
- return {
59
- modes: fallbackModes,
60
- currentModeId: null,
61
- };
62
- }
63
- export function deriveModelDefinitionsFromACP(provider, models, configOptions) {
64
- const thinkingOptions = deriveSelectorOptions(configOptions, "thought_level");
65
- const defaultThinkingOptionId = thinkingOptions.find((option) => option.isDefault)?.id ?? null;
66
- if (models?.availableModels?.length) {
67
- return models.availableModels.map((model) => ({
68
- provider,
69
- id: model.modelId,
70
- label: model.name,
71
- description: model.description ?? undefined,
72
- isDefault: model.modelId === models.currentModelId,
73
- thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
74
- defaultThinkingOptionId: defaultThinkingOptionId ?? undefined,
75
- }));
76
- }
77
- const modelOptions = deriveSelectorOptions(configOptions, "model");
78
- return modelOptions.map((option) => ({
79
- provider,
80
- id: option.id,
81
- label: option.label,
82
- description: option.description,
83
- isDefault: option.isDefault,
84
- thinkingOptions: thinkingOptions.length > 0 ? thinkingOptions : undefined,
85
- defaultThinkingOptionId: defaultThinkingOptionId ?? undefined,
86
- metadata: option.metadata,
87
- }));
88
- }
89
- export class ACPAgentClient {
90
- constructor(options) {
91
- this.provider = options.provider;
92
- this.capabilities = options.capabilities ?? DEFAULT_ACP_CAPABILITIES;
93
- this.logger = options.logger.child({ module: "agent", provider: options.provider });
94
- this.runtimeSettings = options.runtimeSettings;
95
- this.defaultCommand = options.defaultCommand;
96
- this.defaultModes = options.defaultModes ?? [];
97
- this.modelTransformer = options.modelTransformer;
98
- this.sessionResponseTransformer = options.sessionResponseTransformer;
99
- this.toolSnapshotTransformer = options.toolSnapshotTransformer;
100
- this.thinkingOptionWriter = options.thinkingOptionWriter;
101
- this.waitForInitialCommands = options.waitForInitialCommands ?? false;
102
- this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
103
- }
104
- async createSession(config, launchContext) {
105
- this.assertProvider(config);
106
- const session = new ACPAgentSession({ ...config, provider: this.provider }, {
107
- provider: this.provider,
108
- logger: this.logger,
109
- runtimeSettings: this.runtimeSettings,
110
- defaultCommand: this.defaultCommand,
111
- defaultModes: this.defaultModes,
112
- modelTransformer: this.modelTransformer,
113
- sessionResponseTransformer: this.sessionResponseTransformer,
114
- toolSnapshotTransformer: this.toolSnapshotTransformer,
115
- thinkingOptionWriter: this.thinkingOptionWriter,
116
- capabilities: this.capabilities,
117
- launchEnv: launchContext?.env,
118
- waitForInitialCommands: this.waitForInitialCommands,
119
- initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
120
- });
121
- await session.initializeNewSession();
122
- return session;
123
- }
124
- async resumeSession(handle, overrides, launchContext) {
125
- if (handle.provider !== this.provider) {
126
- throw new Error(`Cannot resume ${handle.provider} handle with ${this.provider} provider`);
127
- }
128
- const storedConfig = coerceSessionConfigMetadata(handle.metadata);
129
- const cwd = overrides?.cwd ?? storedConfig.cwd;
130
- if (!cwd) {
131
- throw new Error(`${this.provider} resume requires the original working directory`);
132
- }
133
- const mergedConfig = {
134
- ...storedConfig,
135
- ...overrides,
136
- provider: this.provider,
137
- cwd,
138
- };
139
- const session = new ACPAgentSession(mergedConfig, {
140
- provider: this.provider,
141
- logger: this.logger,
142
- runtimeSettings: this.runtimeSettings,
143
- defaultCommand: this.defaultCommand,
144
- defaultModes: this.defaultModes,
145
- modelTransformer: this.modelTransformer,
146
- sessionResponseTransformer: this.sessionResponseTransformer,
147
- toolSnapshotTransformer: this.toolSnapshotTransformer,
148
- thinkingOptionWriter: this.thinkingOptionWriter,
149
- capabilities: this.capabilities,
150
- handle,
151
- launchEnv: launchContext?.env,
152
- waitForInitialCommands: this.waitForInitialCommands,
153
- initialCommandsWaitTimeoutMs: this.initialCommandsWaitTimeoutMs,
154
- });
155
- await session.initializeResumedSession();
156
- return session;
157
- }
158
- async listModels(options) {
159
- const cwd = options?.cwd ?? process.cwd();
160
- const probe = await this.spawnProcess(undefined);
161
- try {
162
- const response = await probe.connection.newSession({
163
- cwd,
164
- mcpServers: [],
165
- });
166
- const transformed = this.transformSessionResponse(response);
167
- const models = deriveModelDefinitionsFromACP(this.provider, transformed.models, transformed.configOptions);
168
- return this.modelTransformer ? this.modelTransformer(models) : models;
169
- }
170
- finally {
171
- await this.closeProbe(probe);
172
- }
173
- }
174
- async listModes(options) {
175
- const cwd = options?.cwd ?? process.cwd();
176
- const probe = await this.spawnProcess(undefined);
177
- try {
178
- const response = await probe.connection.newSession({
179
- cwd,
180
- mcpServers: [],
181
- });
182
- const transformed = this.transformSessionResponse(response);
183
- const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, transformed.configOptions);
184
- return modeInfo.modes;
185
- }
186
- finally {
187
- await this.closeProbe(probe);
188
- }
189
- }
190
- async listPersistedAgents(options) {
191
- const probe = await this.spawnProcess(undefined);
192
- try {
193
- if (!probe.initialize.agentCapabilities?.sessionCapabilities?.list) {
194
- return [];
195
- }
196
- const sessions = [];
197
- let cursor;
198
- do {
199
- const page = await probe.connection.listSessions({
200
- ...(cursor ? { cursor } : {}),
201
- });
202
- for (const session of page.sessions) {
203
- sessions.push({
204
- provider: this.provider,
205
- sessionId: session.sessionId,
206
- cwd: session.cwd,
207
- title: session.title ?? null,
208
- lastActivityAt: session.updatedAt ? new Date(session.updatedAt) : new Date(0),
209
- persistence: {
210
- provider: this.provider,
211
- sessionId: session.sessionId,
212
- nativeHandle: session.sessionId,
213
- metadata: {
214
- provider: this.provider,
215
- cwd: session.cwd,
216
- title: session.title ?? null,
217
- },
218
- },
219
- timeline: [],
220
- });
221
- }
222
- cursor = page.nextCursor ?? null;
223
- } while (cursor && (!options?.limit || sessions.length < options.limit));
224
- return typeof options?.limit === "number" ? sessions.slice(0, options.limit) : sessions;
225
- }
226
- finally {
227
- await this.closeProbe(probe);
228
- }
229
- }
230
- async isAvailable() {
231
- try {
232
- await this.resolveLaunchCommand();
233
- return true;
234
- }
235
- catch {
236
- return false;
237
- }
238
- }
239
- async spawnProcess(launchEnv) {
240
- const { command, args } = await this.resolveLaunchCommand();
241
- const child = spawnProcess(command, args, {
242
- cwd: process.cwd(),
243
- env: {
244
- ...applyProviderEnv(process.env, this.runtimeSettings),
245
- ...(launchEnv ?? {}),
246
- },
247
- stdio: ["pipe", "pipe", "pipe"],
248
- });
249
- const stderrChunks = [];
250
- child.stderr.on("data", (chunk) => {
251
- stderrChunks.push(chunk.toString());
252
- });
253
- const spawnErrorPromise = new Promise((_, reject) => {
254
- child.once("error", (error) => {
255
- const stderr = stderrChunks.join("").trim();
256
- reject(new Error(stderr ? `${String(error)}\n${stderr}` : String(error)));
257
- });
258
- });
259
- if (!child.stdin || !child.stdout) {
260
- throw new Error(`${this.provider} ACP process did not expose stdio pipes`);
261
- }
262
- const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
263
- const connection = new ClientSideConnection(() => this.buildProbeClient(), stream);
264
- const initialize = (await Promise.race([
265
- connection.initialize({
266
- protocolVersion: PROTOCOL_VERSION,
267
- clientCapabilities: ACP_CLIENT_CAPABILITIES,
268
- clientInfo: { name: "Seawork", version: "dev" },
269
- }),
270
- spawnErrorPromise,
271
- ]));
272
- return { child, connection, initialize };
273
- }
274
- buildProbeClient() {
275
- return {
276
- async requestPermission() {
277
- return { outcome: { outcome: "cancelled" } };
278
- },
279
- async sessionUpdate() { },
280
- async readTextFile(params) {
281
- const content = await fs.readFile(params.path, "utf8");
282
- return { content };
283
- },
284
- async writeTextFile(params) {
285
- await fs.mkdir(path.dirname(params.path), { recursive: true });
286
- await fs.writeFile(params.path, params.content, "utf8");
287
- return {};
288
- },
289
- async createTerminal() {
290
- throw new Error("ACP model probe does not support terminal execution");
291
- },
292
- };
293
- }
294
- async closeProbe(probe) {
295
- try {
296
- if (probe.initialize.agentCapabilities?.sessionCapabilities?.close) {
297
- // No active session to close here; ignore capability.
298
- }
299
- }
300
- finally {
301
- probe.child.kill("SIGTERM");
302
- await waitForChildExit(probe.child, 2000);
303
- }
304
- }
305
- async resolveLaunchCommand() {
306
- const resolved = await findExecutable(this.defaultCommand[0]);
307
- const prefix = await resolveProviderCommandPrefix(this.runtimeSettings?.command, () => {
308
- if (!resolved) {
309
- throw new Error(`${this.provider} command '${this.defaultCommand[0]}' not found`);
310
- }
311
- return resolved;
312
- });
313
- return {
314
- command: prefix.command,
315
- args: [...prefix.args, ...this.defaultCommand.slice(1)],
316
- };
317
- }
318
- assertProvider(config) {
319
- if (config.provider !== this.provider) {
320
- throw new Error(`Expected ${this.provider} config, received ${config.provider}`);
321
- }
322
- }
323
- transformSessionResponse(response) {
324
- return this.sessionResponseTransformer ? this.sessionResponseTransformer(response) : response;
325
- }
326
- }
327
- export class ACPAgentSession {
328
- constructor(config, options) {
329
- this.subscribers = new Set();
330
- this.pendingPermissions = new Map();
331
- this.messageAssemblies = new Map();
332
- this.toolCalls = new Map();
333
- this.terminalEntries = new Map();
334
- this.persistedHistory = [];
335
- this.child = null;
336
- this.connection = null;
337
- this.agentCapabilities = null;
338
- this.sessionId = null;
339
- this.currentMode = null;
340
- this.availableModels = [];
341
- this.currentModel = null;
342
- this.thinkingOptionId = null;
343
- this.currentTitle = null;
344
- this.lastActivityAt = null;
345
- this.configOptions = [];
346
- this.cachedCommands = [];
347
- this.commandsReadyDeferred = null;
348
- this.commandsReadySettled = false;
349
- this.activeForegroundTurnId = null;
350
- this.closed = false;
351
- this.historyPending = false;
352
- this.replayingHistory = false;
353
- this.suppressUserEchoMessageId = null;
354
- this.suppressUserEchoText = null;
355
- this.bootstrapThreadEventPending = false;
356
- this.provider = options.provider;
357
- this.capabilities = options.capabilities;
358
- this.logger = options.logger.child({ module: "agent", provider: options.provider });
359
- this.runtimeSettings = options.runtimeSettings;
360
- this.defaultCommand = options.defaultCommand;
361
- this.defaultModes = options.defaultModes;
362
- this.modelTransformer = options.modelTransformer;
363
- this.sessionResponseTransformer = options.sessionResponseTransformer;
364
- this.toolSnapshotTransformer = options.toolSnapshotTransformer;
365
- this.thinkingOptionWriter = options.thinkingOptionWriter;
366
- this.availableModes = options.defaultModes;
367
- this.launchEnv = options.launchEnv;
368
- this.initialHandle = options.handle;
369
- this.config = { ...config, provider: options.provider };
370
- this.currentMode = config.modeId ?? null;
371
- this.currentModel = config.model ?? null;
372
- this.thinkingOptionId = config.thinkingOptionId ?? null;
373
- this.currentTitle = config.title ?? null;
374
- this.waitForInitialCommands = options.waitForInitialCommands ?? false;
375
- this.initialCommandsWaitTimeoutMs = options.initialCommandsWaitTimeoutMs ?? 1500;
376
- }
377
- get id() {
378
- return this.sessionId;
379
- }
380
- async initializeNewSession() {
381
- const spawned = await this.spawnProcess();
382
- this.child = spawned.child;
383
- this.connection = spawned.connection;
384
- this.agentCapabilities = spawned.initialize.agentCapabilities ?? null;
385
- const response = await this.connection.newSession({
386
- cwd: this.config.cwd,
387
- mcpServers: normalizeMcpServers(this.config.mcpServers),
388
- });
389
- this.sessionId = response.sessionId;
390
- this.bootstrapThreadEventPending = true;
391
- this.applySessionState(response);
392
- await this.applyConfiguredOverrides();
393
- }
394
- async initializeResumedSession() {
395
- const handle = this.initialHandle;
396
- if (!handle) {
397
- throw new Error("Resume requested without persistence handle");
398
- }
399
- const spawned = await this.spawnProcess();
400
- this.child = spawned.child;
401
- this.connection = spawned.connection;
402
- this.agentCapabilities = spawned.initialize.agentCapabilities ?? null;
403
- this.sessionId = handle.sessionId;
404
- this.bootstrapThreadEventPending = true;
405
- const sessionCapabilities = this.agentCapabilities?.sessionCapabilities;
406
- if (this.agentCapabilities?.loadSession) {
407
- this.replayingHistory = true;
408
- const response = await this.connection.loadSession({
409
- sessionId: handle.sessionId,
410
- cwd: this.config.cwd,
411
- mcpServers: normalizeMcpServers(this.config.mcpServers),
412
- });
413
- this.replayingHistory = false;
414
- this.historyPending = this.persistedHistory.length > 0;
415
- this.applySessionState(response);
416
- }
417
- else if (sessionCapabilities?.resume) {
418
- const response = await this.connection.unstable_resumeSession({
419
- sessionId: handle.sessionId,
420
- cwd: this.config.cwd,
421
- mcpServers: normalizeMcpServers(this.config.mcpServers),
422
- });
423
- this.applySessionState(response);
424
- }
425
- else {
426
- throw new Error(`${this.provider} does not support ACP session resume`);
427
- }
428
- await this.applyConfiguredOverrides();
429
- }
430
- async run(prompt, options) {
431
- const timeline = [];
432
- let finalText = "";
433
- let usage;
434
- let turnId = null;
435
- let settled = false;
436
- let resolveCompletion;
437
- let rejectCompletion;
438
- const buffered = [];
439
- const completion = new Promise((resolve, reject) => {
440
- resolveCompletion = resolve;
441
- rejectCompletion = reject;
442
- });
443
- const processEvent = (event) => {
444
- if (settled) {
445
- return;
446
- }
447
- if (turnId && "turnId" in event && event.turnId && event.turnId !== turnId) {
448
- return;
449
- }
450
- if (event.type === "timeline") {
451
- timeline.push(event.item);
452
- if (event.item.type === "assistant_message") {
453
- finalText = event.item.text.startsWith(finalText)
454
- ? event.item.text
455
- : `${finalText}${event.item.text}`;
456
- }
457
- return;
458
- }
459
- if (event.type === "turn_completed") {
460
- usage = event.usage;
461
- settled = true;
462
- resolveCompletion();
463
- return;
464
- }
465
- if (event.type === "turn_failed") {
466
- settled = true;
467
- rejectCompletion(new Error(event.error));
468
- return;
469
- }
470
- if (event.type === "turn_canceled") {
471
- settled = true;
472
- resolveCompletion();
473
- }
474
- };
475
- const unsubscribe = this.subscribe((event) => {
476
- if (!turnId) {
477
- buffered.push(event);
478
- return;
479
- }
480
- processEvent(event);
481
- });
482
- try {
483
- const started = await this.startTurn(prompt, options);
484
- turnId = started.turnId;
485
- for (const event of buffered) {
486
- processEvent(event);
487
- }
488
- if (!settled) {
489
- await completion;
490
- }
491
- }
492
- finally {
493
- unsubscribe();
494
- }
495
- if (!this.sessionId) {
496
- throw new Error("ACP session did not expose a session id");
497
- }
498
- return {
499
- sessionId: this.sessionId,
500
- finalText,
501
- usage,
502
- timeline,
503
- };
504
- }
505
- async startTurn(prompt, _options) {
506
- if (this.closed) {
507
- throw new Error(`${this.provider} session is closed`);
508
- }
509
- if (!this.connection || !this.sessionId) {
510
- throw new Error(`${this.provider} session is not initialized`);
511
- }
512
- if (this.activeForegroundTurnId) {
513
- throw new Error("A foreground turn is already active");
514
- }
515
- const turnId = randomUUID();
516
- const messageId = randomUUID();
517
- this.activeForegroundTurnId = turnId;
518
- this.suppressUserEchoMessageId = messageId;
519
- this.suppressUserEchoText = extractPromptText(prompt);
520
- this.emitBootstrapThreadEvent();
521
- this.pushEvent({ type: "turn_started", provider: this.provider, turnId });
522
- void this.connection
523
- .prompt({
524
- sessionId: this.sessionId,
525
- messageId,
526
- prompt: toACPContentBlocks(prompt),
527
- })
528
- .then((response) => {
529
- this.handlePromptResponse(response, turnId);
530
- })
531
- .catch((error) => {
532
- const message = error instanceof Error ? error.message : String(error);
533
- this.finishTurn({
534
- type: "turn_failed",
535
- provider: this.provider,
536
- error: message,
537
- diagnostic: this.collectDiagnostic(message),
538
- turnId,
539
- });
540
- });
541
- return { turnId };
542
- }
543
- subscribe(callback) {
544
- this.subscribers.add(callback);
545
- if (this.sessionId) {
546
- callback({
547
- type: "thread_started",
548
- provider: this.provider,
549
- sessionId: this.sessionId,
550
- });
551
- }
552
- return () => {
553
- this.subscribers.delete(callback);
554
- };
555
- }
556
- async *streamHistory() {
557
- if (!this.historyPending || this.persistedHistory.length === 0) {
558
- return;
559
- }
560
- const history = [...this.persistedHistory];
561
- this.persistedHistory.length = 0;
562
- this.historyPending = false;
563
- for (const item of history) {
564
- yield { type: "timeline", provider: this.provider, item };
565
- }
566
- }
567
- async getRuntimeInfo() {
568
- return {
569
- provider: this.provider,
570
- sessionId: this.sessionId,
571
- model: this.currentModel,
572
- thinkingOptionId: this.thinkingOptionId,
573
- modeId: this.currentMode,
574
- extra: {
575
- title: this.currentTitle,
576
- updatedAt: this.lastActivityAt,
577
- },
578
- };
579
- }
580
- async getAvailableModes() {
581
- return [...this.availableModes];
582
- }
583
- async getCurrentMode() {
584
- return this.currentMode;
585
- }
586
- ensureCommandsReadyDeferred() {
587
- if (this.commandsReadyDeferred || this.commandsReadySettled || this.cachedCommands.length > 0) {
588
- return;
589
- }
590
- let resolve;
591
- const promise = new Promise((r) => {
592
- resolve = r;
593
- });
594
- this.commandsReadyDeferred = { promise, resolve };
595
- }
596
- settleCommandsReady() {
597
- if (this.commandsReadySettled) {
598
- return;
599
- }
600
- this.commandsReadySettled = true;
601
- this.commandsReadyDeferred?.resolve();
602
- this.commandsReadyDeferred = null;
603
- }
604
- async waitForCommandsReady() {
605
- const deferred = this.commandsReadyDeferred;
606
- if (!deferred) {
607
- return;
608
- }
609
- let timer = null;
610
- try {
611
- await Promise.race([
612
- deferred.promise,
613
- new Promise((resolve) => {
614
- timer = setTimeout(resolve, this.initialCommandsWaitTimeoutMs);
615
- }),
616
- ]);
617
- }
618
- finally {
619
- if (timer) {
620
- clearTimeout(timer);
621
- }
622
- }
623
- }
624
- async listCommands() {
625
- if (this.cachedCommands.length > 0) {
626
- return this.cachedCommands;
627
- }
628
- if (!this.waitForInitialCommands || this.closed) {
629
- return this.cachedCommands;
630
- }
631
- this.ensureCommandsReadyDeferred();
632
- await this.waitForCommandsReady();
633
- this.settleCommandsReady();
634
- return this.cachedCommands;
635
- }
636
- async setMode(modeId) {
637
- if (!this.connection || !this.sessionId) {
638
- throw new Error("ACP session not initialized");
639
- }
640
- const modeExists = this.availableModes.some((mode) => mode.id === modeId);
641
- if (!modeExists && this.availableModes.length > 0) {
642
- throw new Error(`Unknown ${this.provider} mode '${modeId}'`);
643
- }
644
- if (this.availableModes.length > 0) {
645
- await this.connection.setSessionMode({ sessionId: this.sessionId, modeId });
646
- this.currentMode = modeId;
647
- return;
648
- }
649
- const modeOption = this.getSelectConfigOption("mode");
650
- if (!modeOption) {
651
- throw new Error(`${this.provider} does not expose ACP mode switching`);
652
- }
653
- await this.connection.setSessionConfigOption({
654
- sessionId: this.sessionId,
655
- configId: modeOption.id,
656
- value: modeId,
657
- });
658
- this.currentMode = modeId;
659
- }
660
- async setModel(modelId) {
661
- if (!this.connection || !this.sessionId) {
662
- throw new Error("ACP session not initialized");
663
- }
664
- if (!modelId) {
665
- this.currentModel = null;
666
- return;
667
- }
668
- const modelExists = this.availableModels.some((model) => model.id === modelId);
669
- if (!modelExists && this.availableModels.length > 0) {
670
- throw new Error(`Unknown ${this.provider} model '${modelId}'`);
671
- }
672
- if ("unstable_setSessionModel" in this.connection) {
673
- try {
674
- await this.connection.unstable_setSessionModel({
675
- sessionId: this.sessionId,
676
- modelId,
677
- });
678
- this.currentModel = modelId;
679
- return;
680
- }
681
- catch {
682
- // Fall through to config option path.
683
- }
684
- }
685
- const modelOption = this.getSelectConfigOption("model");
686
- if (!modelOption) {
687
- throw new Error(`${this.provider} does not expose ACP model selection`);
688
- }
689
- await this.connection.setSessionConfigOption({
690
- sessionId: this.sessionId,
691
- configId: modelOption.id,
692
- value: modelId,
693
- });
694
- this.currentModel = modelId;
695
- }
696
- async setThinkingOption(thinkingOptionId) {
697
- if (!this.connection || !this.sessionId) {
698
- throw new Error("ACP session not initialized");
699
- }
700
- if (!thinkingOptionId) {
701
- this.thinkingOptionId = null;
702
- return;
703
- }
704
- if (this.thinkingOptionWriter) {
705
- await this.thinkingOptionWriter(this.connection, this.sessionId, thinkingOptionId);
706
- this.thinkingOptionId = thinkingOptionId;
707
- return;
708
- }
709
- const option = this.getSelectConfigOption("thought_level");
710
- if (!option) {
711
- throw new Error(`${this.provider} does not expose ACP thought-level selection`);
712
- }
713
- await this.connection.setSessionConfigOption({
714
- sessionId: this.sessionId,
715
- configId: option.id,
716
- value: thinkingOptionId,
717
- });
718
- this.thinkingOptionId = thinkingOptionId;
719
- }
720
- getPendingPermissions() {
721
- return Array.from(this.pendingPermissions.values(), (entry) => entry.request);
722
- }
723
- async respondToPermission(requestId, response) {
724
- const pending = this.pendingPermissions.get(requestId);
725
- if (!pending) {
726
- throw new Error(`No pending permission request with id '${requestId}'`);
727
- }
728
- this.pendingPermissions.delete(requestId);
729
- const selectedOption = selectPermissionOption(pending.options, response);
730
- pending.resolve(selectedOption
731
- ? {
732
- outcome: {
733
- outcome: "selected",
734
- optionId: selectedOption.optionId,
735
- },
736
- }
737
- : { outcome: { outcome: "cancelled" } });
738
- this.pushEvent({
739
- type: "permission_resolved",
740
- provider: this.provider,
741
- requestId,
742
- resolution: response,
743
- turnId: pending.turnId ?? undefined,
744
- });
745
- if (response.behavior === "deny" && response.interrupt && this.connection && this.sessionId) {
746
- await this.connection.cancel({ sessionId: this.sessionId });
747
- }
748
- }
749
- describePersistence() {
750
- if (!this.sessionId) {
751
- return null;
752
- }
753
- return {
754
- provider: this.provider,
755
- sessionId: this.sessionId,
756
- nativeHandle: this.sessionId,
757
- metadata: {
758
- ...this.config,
759
- title: this.currentTitle,
760
- },
761
- };
762
- }
763
- async interrupt() {
764
- if (!this.connection || !this.sessionId) {
765
- return;
766
- }
767
- for (const pending of this.pendingPermissions.values()) {
768
- pending.resolve({ outcome: { outcome: "cancelled" } });
769
- }
770
- this.pendingPermissions.clear();
771
- if (this.activeForegroundTurnId) {
772
- await this.connection.cancel({ sessionId: this.sessionId });
773
- }
774
- }
775
- async close() {
776
- if (this.closed) {
777
- return;
778
- }
779
- this.closed = true;
780
- this.settleCommandsReady();
781
- for (const pending of this.pendingPermissions.values()) {
782
- pending.resolve({ outcome: { outcome: "cancelled" } });
783
- }
784
- this.pendingPermissions.clear();
785
- if (this.connection && this.sessionId) {
786
- try {
787
- if (this.activeForegroundTurnId) {
788
- await this.connection.cancel({ sessionId: this.sessionId });
789
- }
790
- }
791
- catch { }
792
- try {
793
- if (this.agentCapabilities?.sessionCapabilities?.close) {
794
- await this.connection.unstable_closeSession({ sessionId: this.sessionId });
795
- }
796
- }
797
- catch (error) {
798
- this.logger.debug({ err: error }, "ACP closeSession failed during shutdown");
799
- }
800
- }
801
- for (const terminal of this.terminalEntries.values()) {
802
- terminal.child.kill("SIGTERM");
803
- }
804
- this.terminalEntries.clear();
805
- if (this.child) {
806
- this.child.kill("SIGTERM");
807
- await waitForChildExit(this.child, 2000);
808
- }
809
- this.subscribers.clear();
810
- this.connection = null;
811
- this.child = null;
812
- this.activeForegroundTurnId = null;
813
- }
814
- async requestPermission(params) {
815
- if (shouldAutoApprovePermissionRequest(this.provider, this.currentMode)) {
816
- const selectedOption = selectPermissionOption(params.options, { behavior: "allow" });
817
- return selectedOption
818
- ? {
819
- outcome: {
820
- outcome: "selected",
821
- optionId: selectedOption.optionId,
822
- },
823
- }
824
- : { outcome: { outcome: "cancelled" } };
825
- }
826
- const requestId = randomUUID();
827
- let toolSnapshot = this.toolCalls.get(params.toolCall.toolCallId) ??
828
- mergeToolSnapshot(params.toolCall.toolCallId, params.toolCall);
829
- if (this.toolSnapshotTransformer) {
830
- toolSnapshot = this.toolSnapshotTransformer(toolSnapshot);
831
- }
832
- const request = mapPermissionRequest(this.provider, requestId, params, toolSnapshot);
833
- const promise = new Promise((resolve, reject) => {
834
- this.pendingPermissions.set(requestId, {
835
- request,
836
- options: params.options,
837
- resolve,
838
- reject,
839
- turnId: this.activeForegroundTurnId,
840
- });
841
- });
842
- this.pushEvent({
843
- type: "permission_requested",
844
- provider: this.provider,
845
- request,
846
- turnId: this.activeForegroundTurnId ?? undefined,
847
- });
848
- return promise;
849
- }
850
- async sessionUpdate(params) {
851
- if (params.sessionId !== this.sessionId) {
852
- return;
853
- }
854
- const events = this.translateSessionUpdate(params.update);
855
- if (this.replayingHistory) {
856
- for (const event of events) {
857
- if (event.type === "timeline") {
858
- this.persistedHistory.push(event.item);
859
- }
860
- }
861
- return;
862
- }
863
- for (const event of events) {
864
- this.pushEvent(event);
865
- }
866
- }
867
- async readTextFile(params) {
868
- const raw = await fs.readFile(params.path, "utf8");
869
- if (!params.line && !params.limit) {
870
- return { content: raw };
871
- }
872
- const lines = raw.split(/\r?\n/);
873
- const start = Math.max((params.line ?? 1) - 1, 0);
874
- const end = params.limit ? start + params.limit : undefined;
875
- return { content: lines.slice(start, end).join("\n") };
876
- }
877
- async writeTextFile(params) {
878
- await fs.mkdir(path.dirname(params.path), { recursive: true });
879
- await fs.writeFile(params.path, params.content, "utf8");
880
- return {};
881
- }
882
- async createTerminal(params) {
883
- const terminalId = randomUUID();
884
- const env = Object.fromEntries((params.env ?? []).map((entry) => [entry.name, entry.value]));
885
- const child = spawnProcess(params.command, params.args ?? [], {
886
- cwd: params.cwd ?? this.config.cwd,
887
- env: {
888
- ...applyProviderEnv(process.env, this.runtimeSettings),
889
- ...env,
890
- },
891
- stdio: ["ignore", "pipe", "pipe"],
892
- });
893
- let resolveExit;
894
- let rejectExit;
895
- const waitForExit = new Promise((resolve, reject) => {
896
- resolveExit = resolve;
897
- rejectExit = reject;
898
- });
899
- const entry = {
900
- id: terminalId,
901
- child,
902
- output: "",
903
- truncated: false,
904
- outputByteLimit: params.outputByteLimit ?? null,
905
- exit: null,
906
- waitForExit,
907
- resolveExit,
908
- rejectExit,
909
- };
910
- child.stdout.on("data", (chunk) => appendTerminalOutput(entry, chunk.toString()));
911
- child.stderr.on("data", (chunk) => appendTerminalOutput(entry, chunk.toString()));
912
- child.once("error", (error) => rejectExit(error instanceof Error ? error : new Error(String(error))));
913
- child.once("exit", (code, signal) => {
914
- const exit = { exitCode: code, signal };
915
- entry.exit = exit;
916
- resolveExit(exit);
917
- });
918
- this.terminalEntries.set(terminalId, entry);
919
- return { terminalId };
920
- }
921
- async terminalOutput(params) {
922
- const entry = this.getTerminalEntry(params.terminalId);
923
- return {
924
- output: entry.output,
925
- truncated: entry.truncated,
926
- exitStatus: entry.exit ?? undefined,
927
- };
928
- }
929
- async waitForTerminalExit(params) {
930
- const entry = this.getTerminalEntry(params.terminalId);
931
- return entry.waitForExit;
932
- }
933
- async releaseTerminal(params) {
934
- const entry = this.getTerminalEntry(params.terminalId);
935
- if (!entry.exit) {
936
- entry.child.kill("SIGTERM");
937
- }
938
- this.terminalEntries.delete(params.terminalId);
939
- }
940
- async killTerminal(params) {
941
- const entry = this.getTerminalEntry(params.terminalId);
942
- if (!entry.exit) {
943
- entry.child.kill("SIGTERM");
944
- }
945
- return {};
946
- }
947
- async spawnProcess() {
948
- const resolved = await findExecutable(this.defaultCommand[0]);
949
- const prefix = await resolveProviderCommandPrefix(this.runtimeSettings?.command, () => {
950
- if (!resolved) {
951
- throw new Error(`${this.provider} command '${this.defaultCommand[0]}' not found`);
952
- }
953
- return resolved;
954
- });
955
- const command = prefix.command;
956
- const args = [...prefix.args, ...this.defaultCommand.slice(1)];
957
- const child = spawnProcess(command, args, {
958
- cwd: this.config.cwd,
959
- env: {
960
- ...applyProviderEnv(process.env, this.runtimeSettings),
961
- ...(this.launchEnv ?? {}),
962
- },
963
- stdio: ["pipe", "pipe", "pipe"],
964
- });
965
- const stderrChunks = [];
966
- child.stderr.on("data", (chunk) => {
967
- stderrChunks.push(chunk.toString());
968
- });
969
- child.once("exit", (code, signal) => {
970
- if (this.closed) {
971
- return;
972
- }
973
- if (this.activeForegroundTurnId) {
974
- this.synthesizeCanceledToolCalls();
975
- this.finishTurn({
976
- type: "turn_failed",
977
- provider: this.provider,
978
- error: `ACP agent exited unexpectedly (${code ?? "null"}${signal ? `, ${signal}` : ""})`,
979
- diagnostic: stderrChunks.join("").trim() || undefined,
980
- turnId: this.activeForegroundTurnId,
981
- });
982
- }
983
- });
984
- if (!child.stdin || !child.stdout) {
985
- throw new Error(`${this.provider} ACP process did not expose stdio pipes`);
986
- }
987
- const stream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
988
- const connection = new ClientSideConnection(() => this, stream);
989
- const initialize = await connection.initialize({
990
- protocolVersion: PROTOCOL_VERSION,
991
- clientCapabilities: ACP_CLIENT_CAPABILITIES,
992
- clientInfo: { name: "Seawork", version: "dev" },
993
- });
994
- return { child, connection, initialize };
995
- }
996
- applySessionState(response) {
997
- const transformed = this.sessionResponseTransformer
998
- ? this.sessionResponseTransformer(response)
999
- : response;
1000
- this.configOptions = transformed.configOptions ?? [];
1001
- const modeInfo = deriveModesFromACP(this.defaultModes, transformed.modes, this.configOptions);
1002
- this.availableModes = modeInfo.modes;
1003
- this.availableModels = this.deriveAvailableModels(transformed.models);
1004
- this.currentMode = modeInfo.currentModeId ?? this.currentMode;
1005
- this.currentModel =
1006
- transformed.models?.currentModelId ?? deriveCurrentConfigValue(this.configOptions, "model");
1007
- this.thinkingOptionId =
1008
- deriveCurrentConfigValue(this.configOptions, "thought_level") ?? this.thinkingOptionId;
1009
- }
1010
- async applyConfiguredOverrides() {
1011
- if (this.config.modeId && this.config.modeId !== this.currentMode) {
1012
- await this.setMode(this.config.modeId);
1013
- }
1014
- if (this.config.model && this.config.model !== this.currentModel) {
1015
- await this.setModel(this.config.model);
1016
- }
1017
- if (this.config.thinkingOptionId && this.config.thinkingOptionId !== this.thinkingOptionId) {
1018
- await this.setThinkingOption(this.config.thinkingOptionId);
1019
- }
1020
- }
1021
- translateSessionUpdate(update) {
1022
- switch (update.sessionUpdate) {
1023
- case "user_message_chunk": {
1024
- const item = this.createMessageTimelineItem("user_message", update);
1025
- if (!item) {
1026
- return [];
1027
- }
1028
- const shouldSuppress = this.suppressUserEchoMessageId &&
1029
- update.messageId === this.suppressUserEchoMessageId &&
1030
- this.suppressUserEchoText &&
1031
- item.text === this.suppressUserEchoText;
1032
- if (shouldSuppress) {
1033
- return [];
1034
- }
1035
- return [this.wrapTimeline(item)];
1036
- }
1037
- case "agent_message_chunk": {
1038
- const item = this.createMessageTimelineItem("assistant_message", update);
1039
- return item ? [this.wrapTimeline(item)] : [];
1040
- }
1041
- case "agent_thought_chunk": {
1042
- const item = this.createMessageTimelineItem("reasoning", update);
1043
- return item ? [this.wrapTimeline(item)] : [];
1044
- }
1045
- case "tool_call": {
1046
- let snapshot = mergeToolSnapshot(update.toolCallId, update);
1047
- if (this.toolSnapshotTransformer) {
1048
- snapshot = this.toolSnapshotTransformer(snapshot);
1049
- }
1050
- this.toolCalls.set(update.toolCallId, snapshot);
1051
- return [this.wrapTimeline(mapToolSnapshotToTimeline(snapshot, this.terminalEntries))];
1052
- }
1053
- case "tool_call_update": {
1054
- const previous = this.toolCalls.get(update.toolCallId);
1055
- let snapshot = mergeToolSnapshot(update.toolCallId, update, previous);
1056
- if (this.toolSnapshotTransformer) {
1057
- snapshot = this.toolSnapshotTransformer(snapshot);
1058
- }
1059
- this.toolCalls.set(update.toolCallId, snapshot);
1060
- return [this.wrapTimeline(mapToolSnapshotToTimeline(snapshot, this.terminalEntries))];
1061
- }
1062
- case "plan":
1063
- return [this.wrapTimeline(mapPlanToTimeline(update))];
1064
- case "current_mode_update":
1065
- this.handleCurrentModeUpdate(update);
1066
- return [];
1067
- case "config_option_update":
1068
- this.handleConfigOptionUpdate(update);
1069
- return [];
1070
- case "session_info_update":
1071
- this.handleSessionInfoUpdate(update);
1072
- return [];
1073
- case "usage_update":
1074
- this.handleUsageUpdate(update);
1075
- return [];
1076
- case "available_commands_update":
1077
- this.cachedCommands = update.availableCommands.map((command) => ({
1078
- name: command.name,
1079
- description: command.description,
1080
- argumentHint: "",
1081
- }));
1082
- this.settleCommandsReady();
1083
- return [];
1084
- default:
1085
- return [];
1086
- }
1087
- }
1088
- createMessageTimelineItem(type, update) {
1089
- const chunkText = contentBlockToText(update.content);
1090
- if (!chunkText) {
1091
- return null;
1092
- }
1093
- const key = `${type}:${update.messageId ?? "default"}`;
1094
- const state = this.messageAssemblies.get(key) ?? { text: "" };
1095
- state.text += chunkText;
1096
- this.messageAssemblies.set(key, state);
1097
- if (type === "user_message") {
1098
- return { type: "user_message", text: state.text, messageId: update.messageId ?? undefined };
1099
- }
1100
- if (type === "assistant_message") {
1101
- return { type: "assistant_message", text: chunkText };
1102
- }
1103
- return { type: "reasoning", text: chunkText };
1104
- }
1105
- handleCurrentModeUpdate(update) {
1106
- this.currentMode = update.currentModeId;
1107
- }
1108
- handleConfigOptionUpdate(update) {
1109
- this.configOptions = update.configOptions;
1110
- const modeInfo = deriveModesFromACP(this.defaultModes, null, this.configOptions);
1111
- this.availableModes = modeInfo.modes;
1112
- this.availableModels = this.deriveAvailableModels(null);
1113
- this.currentMode = modeInfo.currentModeId ?? this.currentMode;
1114
- this.currentModel = deriveCurrentConfigValue(this.configOptions, "model") ?? this.currentModel;
1115
- this.thinkingOptionId =
1116
- deriveCurrentConfigValue(this.configOptions, "thought_level") ?? this.thinkingOptionId;
1117
- }
1118
- deriveAvailableModels(models) {
1119
- const availableModels = deriveModelDefinitionsFromACP(this.provider, models, this.configOptions);
1120
- return this.modelTransformer ? this.modelTransformer(availableModels) : availableModels;
1121
- }
1122
- handleSessionInfoUpdate(update) {
1123
- if ("title" in update) {
1124
- this.currentTitle = update.title ?? null;
1125
- }
1126
- if ("updatedAt" in update) {
1127
- this.lastActivityAt = update.updatedAt ?? null;
1128
- }
1129
- }
1130
- handleUsageUpdate(update) {
1131
- void update;
1132
- }
1133
- handlePromptResponse(response, turnId) {
1134
- this.currentTurnUsage = mapACPUsage(response.usage) ?? this.currentTurnUsage;
1135
- switch (response.stopReason) {
1136
- case "cancelled":
1137
- this.synthesizeCanceledToolCalls();
1138
- this.finishTurn({
1139
- type: "turn_canceled",
1140
- provider: this.provider,
1141
- reason: "Interrupted",
1142
- turnId,
1143
- });
1144
- break;
1145
- case "end_turn":
1146
- case "max_tokens":
1147
- case "max_turn_requests":
1148
- case "refusal":
1149
- default:
1150
- this.finishTurn({
1151
- type: "turn_completed",
1152
- provider: this.provider,
1153
- usage: this.currentTurnUsage,
1154
- turnId,
1155
- });
1156
- break;
1157
- }
1158
- }
1159
- wrapTimeline(item) {
1160
- return {
1161
- type: "timeline",
1162
- provider: this.provider,
1163
- item,
1164
- turnId: this.activeForegroundTurnId ?? undefined,
1165
- };
1166
- }
1167
- pushEvent(event) {
1168
- for (const subscriber of this.subscribers) {
1169
- subscriber(event);
1170
- }
1171
- }
1172
- finishTurn(event) {
1173
- this.activeForegroundTurnId = null;
1174
- this.suppressUserEchoMessageId = null;
1175
- this.suppressUserEchoText = null;
1176
- this.pushEvent(event);
1177
- }
1178
- emitBootstrapThreadEvent() {
1179
- if (!this.bootstrapThreadEventPending || !this.sessionId) {
1180
- return;
1181
- }
1182
- this.bootstrapThreadEventPending = false;
1183
- this.pushEvent({
1184
- type: "thread_started",
1185
- provider: this.provider,
1186
- sessionId: this.sessionId,
1187
- });
1188
- }
1189
- synthesizeCanceledToolCalls() {
1190
- for (const snapshot of this.toolCalls.values()) {
1191
- const mapped = mapToolSnapshotToTimeline(snapshot, this.terminalEntries);
1192
- if (mapped.status === "running") {
1193
- this.pushEvent(this.wrapTimeline({
1194
- ...mapped,
1195
- status: "canceled",
1196
- error: null,
1197
- }));
1198
- }
1199
- }
1200
- }
1201
- collectDiagnostic(message) {
1202
- const parts = [message];
1203
- if (this.child?.exitCode != null) {
1204
- parts.push(`exitCode=${this.child.exitCode}`);
1205
- }
1206
- if (this.child?.signalCode) {
1207
- parts.push(`signal=${this.child.signalCode}`);
1208
- }
1209
- return parts.length > 0 ? parts.join(" | ") : undefined;
1210
- }
1211
- getSelectConfigOption(category) {
1212
- const option = this.configOptions.find((entry) => entry.type === "select" && entry.category === category);
1213
- return option ?? null;
1214
- }
1215
- getTerminalEntry(terminalId) {
1216
- const entry = this.terminalEntries.get(terminalId);
1217
- if (!entry) {
1218
- throw new Error(`Unknown terminal '${terminalId}'`);
1219
- }
1220
- return entry;
1221
- }
1222
- }
1223
- function flattenSelectOptions(options) {
1224
- const flattened = [];
1225
- for (const option of options) {
1226
- if ("value" in option) {
1227
- flattened.push(option);
1228
- continue;
1229
- }
1230
- for (const groupOption of option.options) {
1231
- flattened.push({ ...groupOption, group: option.group });
1232
- }
1233
- }
1234
- return flattened;
1235
- }
1236
- function deriveSelectorOptions(configOptions, category) {
1237
- const option = configOptions?.find((entry) => entry.type === "select" && entry.category === category);
1238
- if (!option) {
1239
- return [];
1240
- }
1241
- return flattenSelectOptions(option.options).map((value) => ({
1242
- id: value.value,
1243
- label: value.name,
1244
- description: value.description ?? undefined,
1245
- isDefault: value.value === option.currentValue,
1246
- metadata: value.group ? { group: value.group } : undefined,
1247
- }));
1248
- }
1249
- function deriveCurrentConfigValue(configOptions, category) {
1250
- const option = configOptions?.find((entry) => entry.type === "select" && entry.category === category);
1251
- return option?.currentValue ?? null;
1252
- }
1253
- function normalizeMcpServers(servers) {
1254
- if (!servers) {
1255
- return [];
1256
- }
1257
- return Object.entries(servers).map(([name, config]) => {
1258
- if (config.type === "stdio") {
1259
- return {
1260
- name,
1261
- command: config.command,
1262
- args: config.args ?? [],
1263
- env: Object.entries(config.env ?? {}).map(([envName, value]) => ({
1264
- name: envName,
1265
- value,
1266
- })),
1267
- };
1268
- }
1269
- if (config.type === "http") {
1270
- return {
1271
- type: "http",
1272
- name,
1273
- url: config.url,
1274
- headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
1275
- name: headerName,
1276
- value,
1277
- })),
1278
- };
1279
- }
1280
- return {
1281
- type: "sse",
1282
- name,
1283
- url: config.url,
1284
- headers: Object.entries(config.headers ?? {}).map(([headerName, value]) => ({
1285
- name: headerName,
1286
- value,
1287
- })),
1288
- };
1289
- });
1290
- }
1291
- function toACPContentBlocks(prompt) {
1292
- if (typeof prompt === "string") {
1293
- return [{ type: "text", text: prompt }];
1294
- }
1295
- return prompt.map((block) => {
1296
- if (block.type === "text") {
1297
- return { type: "text", text: block.text };
1298
- }
1299
- return {
1300
- type: "image",
1301
- data: block.data,
1302
- mimeType: block.mimeType,
1303
- };
1304
- });
1305
- }
1306
- function extractPromptText(prompt) {
1307
- if (typeof prompt === "string") {
1308
- return prompt;
1309
- }
1310
- return prompt
1311
- .filter((block) => block.type === "text")
1312
- .map((block) => block.text)
1313
- .join("");
1314
- }
1315
- function contentBlockToText(content) {
1316
- switch (content.type) {
1317
- case "text":
1318
- return content.text;
1319
- case "resource_link":
1320
- return content.title ?? content.uri;
1321
- case "resource":
1322
- return "text" in content.resource
1323
- ? content.resource.text
1324
- : `[resource:${content.resource.mimeType ?? "binary"}]`;
1325
- case "image":
1326
- return "[image]";
1327
- case "audio":
1328
- return "[audio]";
1329
- default:
1330
- return "";
1331
- }
1332
- }
1333
- function mergeToolSnapshot(toolCallId, update, previous) {
1334
- const isFull = "title" in update && typeof update.title === "string";
1335
- return {
1336
- toolCallId,
1337
- title: (update.title ?? previous?.title ?? toolCallId),
1338
- kind: update.kind ?? previous?.kind ?? null,
1339
- status: update.status ?? previous?.status ?? null,
1340
- content: update.content !== undefined ? update.content : (previous?.content ?? null),
1341
- locations: update.locations !== undefined ? update.locations : (previous?.locations ?? null),
1342
- rawInput: update.rawInput !== undefined ? update.rawInput : previous?.rawInput,
1343
- rawOutput: update.rawOutput !== undefined ? update.rawOutput : previous?.rawOutput,
1344
- ...(isFull ? {} : {}),
1345
- };
1346
- }
1347
- function mapPlanToTimeline(plan) {
1348
- return {
1349
- type: "todo",
1350
- items: plan.entries.map((entry) => ({
1351
- text: entry.content,
1352
- completed: entry.status === "completed",
1353
- })),
1354
- };
1355
- }
1356
- function mapToolSnapshotToTimeline(snapshot, terminals) {
1357
- const status = mapToolStatus(snapshot.status);
1358
- const detail = mapToolDetail(snapshot, terminals);
1359
- const base = {
1360
- type: "tool_call",
1361
- callId: snapshot.toolCallId,
1362
- name: snapshot.kind ?? snapshot.title,
1363
- detail,
1364
- metadata: {
1365
- kind: snapshot.kind ?? undefined,
1366
- title: snapshot.title,
1367
- },
1368
- };
1369
- if (status === "failed") {
1370
- return {
1371
- ...base,
1372
- status: "failed",
1373
- error: { message: readErrorMessage(snapshot.rawOutput) },
1374
- };
1375
- }
1376
- if (status === "completed") {
1377
- return {
1378
- ...base,
1379
- status: "completed",
1380
- error: null,
1381
- };
1382
- }
1383
- return {
1384
- ...base,
1385
- status: "running",
1386
- error: null,
1387
- };
1388
- }
1389
- function mapToolStatus(status) {
1390
- switch (status) {
1391
- case "completed":
1392
- return "completed";
1393
- case "failed":
1394
- return "failed";
1395
- case "pending":
1396
- case "in_progress":
1397
- default:
1398
- return "running";
1399
- }
1400
- }
1401
- function mapToolDetail(snapshot, terminals) {
1402
- const firstLocation = snapshot.locations?.[0]?.path;
1403
- const textContent = extractToolText(snapshot.content);
1404
- const diffContent = extractDiffContent(snapshot.content);
1405
- const terminalContent = extractTerminalContent(snapshot.content, terminals);
1406
- const rawInput = readRecord(snapshot.rawInput);
1407
- const rawOutput = readRecord(snapshot.rawOutput);
1408
- switch (snapshot.kind) {
1409
- case "read":
1410
- return {
1411
- type: "read",
1412
- filePath: firstLocation ?? readString(rawInput, ["path", "filePath", "file"]) ?? snapshot.title,
1413
- content: textContent ?? readString(rawOutput, ["content", "text"]),
1414
- offset: readNumber(rawInput, ["offset", "line"]),
1415
- limit: readNumber(rawInput, ["limit"]),
1416
- };
1417
- case "edit":
1418
- case "delete":
1419
- return {
1420
- type: "edit",
1421
- filePath: firstLocation ?? readString(rawInput, ["path", "filePath", "file"]) ?? snapshot.title,
1422
- oldString: diffContent?.oldText ?? readString(rawInput, ["oldText", "oldString"]),
1423
- newString: snapshot.kind === "delete"
1424
- ? ""
1425
- : (diffContent?.newText ?? readString(rawInput, ["newText", "newString"])),
1426
- unifiedDiff: textContent ?? undefined,
1427
- };
1428
- case "search":
1429
- return {
1430
- type: "search",
1431
- query: readString(rawInput, ["query", "pattern"]) ?? snapshot.title,
1432
- toolName: "search",
1433
- content: textContent ?? readString(rawOutput, ["content", "text"]),
1434
- filePaths: snapshot.locations?.map((location) => location.path),
1435
- };
1436
- case "execute":
1437
- return {
1438
- type: "shell",
1439
- command: terminalContent?.command ??
1440
- buildShellCommand(rawInput) ??
1441
- readString(rawInput, ["command"]) ??
1442
- snapshot.title,
1443
- cwd: terminalContent?.cwd ?? readString(rawInput, ["cwd"]),
1444
- output: terminalContent?.output ?? textContent ?? readString(rawOutput, ["output", "text"]),
1445
- exitCode: terminalContent?.exitCode ?? readNumber(rawOutput, ["exitCode"]),
1446
- };
1447
- case "fetch":
1448
- return {
1449
- type: "fetch",
1450
- url: readString(rawInput, ["url"]) ?? snapshot.title,
1451
- prompt: readString(rawInput, ["prompt"]),
1452
- result: textContent ?? readString(rawOutput, ["result", "text", "content"]),
1453
- code: readNumber(rawOutput, ["status", "code"]),
1454
- };
1455
- case "think":
1456
- return {
1457
- type: "plain_text",
1458
- label: snapshot.title,
1459
- icon: "brain",
1460
- text: textContent ?? stringifyUnknown(snapshot.rawOutput),
1461
- };
1462
- case "switch_mode":
1463
- return {
1464
- type: "plain_text",
1465
- label: snapshot.title,
1466
- icon: "sparkles",
1467
- text: textContent ?? stringifyUnknown(snapshot.rawInput),
1468
- };
1469
- default:
1470
- if (terminalContent) {
1471
- return {
1472
- type: "shell",
1473
- command: terminalContent.command ?? snapshot.title,
1474
- cwd: terminalContent.cwd,
1475
- output: terminalContent.output,
1476
- exitCode: terminalContent.exitCode,
1477
- };
1478
- }
1479
- if (textContent) {
1480
- return {
1481
- type: "plain_text",
1482
- label: snapshot.title,
1483
- text: textContent,
1484
- icon: "wrench",
1485
- };
1486
- }
1487
- return {
1488
- type: "unknown",
1489
- input: snapshot.rawInput ?? null,
1490
- output: snapshot.rawOutput ?? null,
1491
- };
1492
- }
1493
- }
1494
- function extractToolText(content) {
1495
- if (!content) {
1496
- return undefined;
1497
- }
1498
- const parts = [];
1499
- for (const item of content) {
1500
- if (item.type === "content") {
1501
- const text = contentBlockToText(item.content);
1502
- if (text) {
1503
- parts.push(text);
1504
- }
1505
- }
1506
- }
1507
- return parts.length > 0 ? parts.join("\n") : undefined;
1508
- }
1509
- function extractDiffContent(content) {
1510
- const diff = content?.find((item) => item.type === "diff");
1511
- return diff ? { oldText: diff.oldText ?? undefined, newText: diff.newText } : null;
1512
- }
1513
- function extractTerminalContent(content, terminals) {
1514
- const terminal = content?.find((item) => item.type === "terminal");
1515
- if (!terminal) {
1516
- return undefined;
1517
- }
1518
- const entry = terminals.get(terminal.terminalId);
1519
- if (!entry) {
1520
- return undefined;
1521
- }
1522
- return {
1523
- output: entry.output,
1524
- exitCode: entry.exit?.exitCode ?? null,
1525
- };
1526
- }
1527
- function mapPermissionRequest(provider, requestId, params, snapshot) {
1528
- const kind = snapshot.kind === "switch_mode" ? "mode" : "tool";
1529
- return {
1530
- id: requestId,
1531
- provider,
1532
- name: snapshot.kind ?? snapshot.title,
1533
- kind,
1534
- title: params.toolCall.title ?? snapshot.title,
1535
- detail: mapToolDetail(snapshot, new Map()),
1536
- metadata: {
1537
- toolCallId: params.toolCall.toolCallId,
1538
- rawRequest: params,
1539
- options: params.options,
1540
- },
1541
- };
1542
- }
1543
- function shouldAutoApprovePermissionRequest(provider, currentMode) {
1544
- return provider === "copilot" && currentMode === COPILOT_AUTOPILOT_MODE;
1545
- }
1546
- function selectPermissionOption(options, response) {
1547
- const order = response.behavior === "allow"
1548
- ? ["allow_once", "allow_always"]
1549
- : ["reject_once", "reject_always"];
1550
- for (const kind of order) {
1551
- const match = options.find((option) => option.kind === kind);
1552
- if (match) {
1553
- return match;
1554
- }
1555
- }
1556
- return null;
1557
- }
1558
- function appendTerminalOutput(entry, chunk) {
1559
- entry.output += chunk;
1560
- const limit = entry.outputByteLimit;
1561
- if (!limit) {
1562
- return;
1563
- }
1564
- while (Buffer.byteLength(entry.output, "utf8") > limit && entry.output.length > 0) {
1565
- entry.output = entry.output.slice(1);
1566
- entry.truncated = true;
1567
- }
1568
- }
1569
- function readRecord(value) {
1570
- return value && typeof value === "object" && !Array.isArray(value)
1571
- ? value
1572
- : null;
1573
- }
1574
- function readString(record, keys) {
1575
- if (!record) {
1576
- return undefined;
1577
- }
1578
- for (const key of keys) {
1579
- const value = record[key];
1580
- if (typeof value === "string" && value.trim().length > 0) {
1581
- return value;
1582
- }
1583
- }
1584
- return undefined;
1585
- }
1586
- function readNumber(record, keys) {
1587
- if (!record) {
1588
- return undefined;
1589
- }
1590
- for (const key of keys) {
1591
- const value = record[key];
1592
- if (typeof value === "number" && Number.isFinite(value)) {
1593
- return value;
1594
- }
1595
- }
1596
- return undefined;
1597
- }
1598
- function buildShellCommand(record) {
1599
- if (!record) {
1600
- return undefined;
1601
- }
1602
- const command = readString(record, ["command"]);
1603
- const args = Array.isArray(record["args"])
1604
- ? record["args"].filter((value) => typeof value === "string")
1605
- : [];
1606
- if (!command) {
1607
- return undefined;
1608
- }
1609
- return args.length > 0 ? `${command} ${args.join(" ")}` : command;
1610
- }
1611
- function readErrorMessage(value) {
1612
- if (typeof value === "string") {
1613
- return value;
1614
- }
1615
- const record = readRecord(value);
1616
- return readString(record, ["message", "error"]) ?? "Tool call failed";
1617
- }
1618
- function stringifyUnknown(value) {
1619
- if (value == null) {
1620
- return undefined;
1621
- }
1622
- if (typeof value === "string") {
1623
- return value;
1624
- }
1625
- try {
1626
- return JSON.stringify(value);
1627
- }
1628
- catch {
1629
- return String(value);
1630
- }
1631
- }
1632
- function coerceSessionConfigMetadata(metadata) {
1633
- if (!metadata || typeof metadata !== "object") {
1634
- return {};
1635
- }
1636
- return metadata;
1637
- }
1638
- async function waitForChildExit(child, timeoutMs) {
1639
- if (child.exitCode !== null || child.signalCode !== null) {
1640
- return;
1641
- }
1642
- await Promise.race([
1643
- new Promise((resolve) => child.once("exit", () => resolve())),
1644
- new Promise((resolve) => setTimeout(resolve, timeoutMs)),
1645
- ]);
1646
- if (child.exitCode === null && child.signalCode === null) {
1647
- child.kill("SIGKILL");
1648
- }
1649
- }
1650
- //# sourceMappingURL=acp-agent.js.map