@bbigbang/agent-node 0.1.0

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 (47) hide show
  1. package/dist/agentHost.js +483 -0
  2. package/dist/appVersion.js +14 -0
  3. package/dist/assetCachePaths.js +35 -0
  4. package/dist/attachmentInput.js +588 -0
  5. package/dist/attachmentMaterializer.js +230 -0
  6. package/dist/bigbangCli.js +17 -0
  7. package/dist/bigbangMessageSendDetection.js +284 -0
  8. package/dist/builtinSkillRoots.js +54 -0
  9. package/dist/claudeConfig.js +32 -0
  10. package/dist/claudeDirectRuntime.js +1960 -0
  11. package/dist/claudeSessionControls.js +78 -0
  12. package/dist/claudeTranscriptFs.js +147 -0
  13. package/dist/codexAppServerClient.js +188 -0
  14. package/dist/codexAppServerEnv.js +14 -0
  15. package/dist/codexAppServerRpc.js +273 -0
  16. package/dist/codexAppServerRuntime.js +3495 -0
  17. package/dist/codexBuiltinPrompt.js +117 -0
  18. package/dist/codexConversationSummarizer.js +76 -0
  19. package/dist/codexTranscriptFs.js +145 -0
  20. package/dist/config.js +129 -0
  21. package/dist/connection.js +151 -0
  22. package/dist/dispatchQueueStore.js +39 -0
  23. package/dist/dreamEnv.js +1 -0
  24. package/dist/dreamMemoryFallback.js +118 -0
  25. package/dist/dreamToolPolicy.js +293 -0
  26. package/dist/droidMissionRunner.js +808 -0
  27. package/dist/executor.js +1078 -0
  28. package/dist/hostRuntime.js +1 -0
  29. package/dist/libraryAuthorityFs.js +74 -0
  30. package/dist/libraryMirror.js +183 -0
  31. package/dist/main.js +1659 -0
  32. package/dist/native-worker/native-worker.mjs +475 -0
  33. package/dist/nativeMissionAgentDispatch.js +463 -0
  34. package/dist/nativeMissionRunner.js +461 -0
  35. package/dist/nativeSkillMounts.js +204 -0
  36. package/dist/nativeWorkerHost.js +142 -0
  37. package/dist/nodeSink.js +142 -0
  38. package/dist/panelHttpFetch.js +334 -0
  39. package/dist/runtimeDrivers.js +62 -0
  40. package/dist/skillFs.js +229 -0
  41. package/dist/soloHost.js +165 -0
  42. package/dist/soloNodeSink.js +138 -0
  43. package/dist/terminalManager.js +254 -0
  44. package/dist/workspaceFs.js +1020 -0
  45. package/dist/workspaceGit.js +694 -0
  46. package/dist/workspaceInspect.js +22 -0
  47. package/package.json +49 -0
package/dist/main.js ADDED
@@ -0,0 +1,1659 @@
1
+ #!/usr/bin/env node
2
+ import { randomUUID } from 'node:crypto';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import { openDb, migrate, log, WorkspaceLockManager } from '@bbigbang/runtime-acp';
6
+ import { BUILTIN_LIBRARY_DOCUMENTS_SKILL_ROOT_SENTINEL, BUILTIN_UI_PANEL_SKILL_ROOT_SENTINEL, BUILTIN_WORKSPACE_TOOL_SKILL_ROOT_SENTINEL, } from '@bbigbang/protocol';
7
+ import { loadConfig } from './config.js';
8
+ import { CoreConnection } from './connection.js';
9
+ import { Executor } from './executor.js';
10
+ import { ensureNativeSkillMounts } from './nativeSkillMounts.js';
11
+ import { CodexTranscriptFsError, listCodexTranscriptFiles, readCodexTranscriptFile, } from './codexTranscriptFs.js';
12
+ import { ClaudeTranscriptFsError, listClaudeTranscriptFiles, readClaudeTranscriptFile, } from './claudeTranscriptFs.js';
13
+ import { listWorkspaceDirectory, createExactPathReadStream, createWorkspaceDirectoryArchive, deleteWorkspaceFile, deleteWorkspacePath, extractWorkspaceDirectoryArchive, readExactPath, readWorkspaceFile, resetWorkspaceDirectory, statExactPathRawStream, suggestDirectoryPaths, writeWorkspaceFile, WorkspaceFsError, } from './workspaceFs.js';
14
+ import { applyLibraryMirror, snapshotLibraryMirror } from './libraryMirror.js';
15
+ import { deleteLibraryAuthorityPath, renameLibraryAuthorityPath } from './libraryAuthorityFs.js';
16
+ import { listSkills, readSkillFile } from './skillFs.js';
17
+ import { TerminalManager, TerminalManagerError } from './terminalManager.js';
18
+ import { inspectWorkspace, WorkspaceInspectError } from './workspaceInspect.js';
19
+ import { getWorkspaceGitDiff, getWorkspaceGitStatus, runWorkspaceGitAction, WorkspaceGitError } from './workspaceGit.js';
20
+ import { CodexAppServerRpc } from './codexAppServerRpc.js';
21
+ import { runCodexConversationSummary } from './codexConversationSummarizer.js';
22
+ import { inspectDroidMissionContinuation, runDroidMission } from './droidMissionRunner.js';
23
+ import { runNativeMission } from './nativeMissionRunner.js';
24
+ import { NativeMissionAgentExecutor } from './nativeMissionAgentDispatch.js';
25
+ import { fetchLoopbackHttpText, proxyWorkspaceServiceHttp } from './panelHttpFetch.js';
26
+ function isRecoverableRuntimeStreamError(error) {
27
+ const code = typeof error === 'object' && error !== null && 'code' in error
28
+ ? String(error.code ?? '')
29
+ : '';
30
+ const message = String(error?.message ?? error);
31
+ return code === 'EPIPE'
32
+ || code === 'ERR_STREAM_DESTROYED'
33
+ || message.includes('write EPIPE')
34
+ || message.includes('write after end')
35
+ || message.includes('Cannot call write after a stream was destroyed');
36
+ }
37
+ function resolvePanelApiJsonlAuthHeaders(profiles, profileName) {
38
+ const normalizedProfileName = profileName?.trim();
39
+ if (!normalizedProfileName)
40
+ return undefined;
41
+ const headers = profiles[normalizedProfileName];
42
+ if (!headers) {
43
+ throw new WorkspaceFsError('invalid_request', `Unknown api_jsonl auth profile "${normalizedProfileName}".`);
44
+ }
45
+ return headers;
46
+ }
47
+ async function main() {
48
+ if (process.argv.includes('--help') || process.argv.includes('-h')) {
49
+ console.log('Usage: bigbang-node');
50
+ return;
51
+ }
52
+ const config = loadConfig();
53
+ for (const unavailableDriver of config.unavailableRuntimeDrivers) {
54
+ log.warn('[agent-node] runtime driver unavailable; omitting from advertised capabilities', unavailableDriver);
55
+ }
56
+ // Ensure DB directory exists
57
+ fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
58
+ const db = openDb(config.dbPath);
59
+ migrate(db);
60
+ const workspaceLockManager = new WorkspaceLockManager();
61
+ const terminalManager = new TerminalManager();
62
+ const codexAppServerRpc = new CodexAppServerRpc({ config });
63
+ const terminalBackendReason = terminalManager.getBackendUnavailableReason();
64
+ if (terminalBackendReason) {
65
+ log.warn('[agent-node] terminal backend unavailable', { reason: terminalBackendReason });
66
+ }
67
+ let executor;
68
+ let connection;
69
+ const buildRuntimeSnapshot = () => ({
70
+ workspaceRoot: config.workspaceRoot,
71
+ terminalBackendAvailable: terminalManager.isBackendAvailable(),
72
+ runtimeDrivers: config.runtimeDrivers.map((driver) => ({
73
+ agentType: driver.agentType,
74
+ command: driver.command,
75
+ args: [...driver.args],
76
+ supportsResume: driver.supportsResume,
77
+ supportsPushNotifications: driver.supportsPushNotifications,
78
+ capabilities: [...driver.capabilities],
79
+ nativeMemoryBackend: driver.nativeMemoryBackend,
80
+ supportsClaudeSessionControls: driver.agentType === 'claude_sdk',
81
+ })),
82
+ capabilities: {
83
+ envProfile: process.env.BIGBANG_NODE_ENV_PROFILE?.trim() || null,
84
+ agentSurfaceMode: config.agentSurfaceMode,
85
+ bigbangCliAvailable: config.bigbangCliAvailable,
86
+ builtinSkillRoots: [
87
+ BUILTIN_WORKSPACE_TOOL_SKILL_ROOT_SENTINEL,
88
+ BUILTIN_UI_PANEL_SKILL_ROOT_SENTINEL,
89
+ BUILTIN_LIBRARY_DOCUMENTS_SKILL_ROOT_SENTINEL,
90
+ ],
91
+ panelApiJsonlAllowedOrigins: config.panelApiJsonlAllowedOrigins,
92
+ panelApiJsonlAllowedUrlPrefixes: config.panelApiJsonlAllowedUrlPrefixes,
93
+ panelApiJsonlAuthProfiles: Object.keys(config.panelApiJsonlAuthProfiles),
94
+ workspaceRpc: true,
95
+ workspaceInspect: true,
96
+ workspaceGit: true,
97
+ terminals: terminalManager.isBackendAvailable(),
98
+ skills: true,
99
+ codexAppServerRpc: config.agentTypes.includes('codex_app_server'),
100
+ codexTranscripts: true,
101
+ claudeTranscripts: true,
102
+ claudeControls: true,
103
+ nativeMissionWorker: true,
104
+ },
105
+ });
106
+ const publishHostSnapshot = () => {
107
+ if (!executor || !connection)
108
+ return;
109
+ connection.send({
110
+ type: 'node.hosts.snapshot',
111
+ nodeId: config.nodeId,
112
+ generatedAt: Date.now(),
113
+ hosts: executor.listHostSnapshots(),
114
+ });
115
+ };
116
+ const publishRuntimeSnapshot = () => {
117
+ if (!connection)
118
+ return;
119
+ connection.send({
120
+ type: 'node.runtime.snapshot',
121
+ nodeId: config.nodeId,
122
+ processInstanceId: config.processInstanceId,
123
+ generatedAt: Date.now(),
124
+ runtime: buildRuntimeSnapshot(),
125
+ });
126
+ };
127
+ terminalManager.onOutput(({ terminalId, data }) => {
128
+ connection.send({
129
+ type: 'terminal.output.event',
130
+ terminalId,
131
+ data,
132
+ });
133
+ });
134
+ terminalManager.onExit(({ terminalId, exitCode, signal }) => {
135
+ connection.send({
136
+ type: 'terminal.exit.event',
137
+ terminalId,
138
+ exitCode,
139
+ signal,
140
+ });
141
+ });
142
+ const activeNativeWorkers = new Map();
143
+ const pendingNativeCancellations = new Set();
144
+ let nativeAgentExecutor = null;
145
+ const handleMessage = (msg) => {
146
+ void (async () => {
147
+ switch (msg.type) {
148
+ case 'node.ack':
149
+ log.info(`[agent-node] registered with core as ${msg.nodeId}`);
150
+ executor.resumePendingDispatches();
151
+ publishHostSnapshot();
152
+ publishRuntimeSnapshot();
153
+ break;
154
+ case 'run.dispatch':
155
+ executor.dispatch(msg).catch((err) => {
156
+ log.warn('[agent-node] dispatch error', err);
157
+ });
158
+ break;
159
+ case 'run.cancel':
160
+ executor.cancelRun(msg.runId).then((handled) => {
161
+ if (!handled) {
162
+ log.warn('[agent-node] run.cancel had no active runtime', msg.runId);
163
+ }
164
+ }).catch((err) => {
165
+ log.warn('[agent-node] run.cancel error', err);
166
+ });
167
+ break;
168
+ case 'run.steer':
169
+ executor.steerRun(msg.runId, msg.prompt, msg.attachments).then((handled) => {
170
+ if (!handled) {
171
+ log.warn('[agent-node] run.steer had no active steerable runtime', msg.runId);
172
+ }
173
+ connection.send({
174
+ type: 'run.event',
175
+ runId: msg.runId,
176
+ conversationId: msg.conversationId,
177
+ event: {
178
+ type: 'run.steer.result',
179
+ requestId: msg.requestId,
180
+ runId: msg.runId,
181
+ ok: handled,
182
+ createdAt: Date.now(),
183
+ ...(!handled ? { error: 'Active runtime turn is not steerable.' } : {}),
184
+ },
185
+ });
186
+ connection.send({
187
+ type: 'run.steer.result',
188
+ requestId: msg.requestId,
189
+ runId: msg.runId,
190
+ conversationId: msg.conversationId,
191
+ ok: handled,
192
+ ...(!handled ? { error: 'Active runtime turn is not steerable.' } : {}),
193
+ });
194
+ if (handled && msg.delivery) {
195
+ connection.send({
196
+ type: 'run.delivery.ack',
197
+ deliveryId: msg.delivery.deliveryId,
198
+ roundId: msg.delivery.roundId,
199
+ runId: msg.runId,
200
+ conversationId: msg.conversationId,
201
+ ackedThroughSeq: msg.delivery.ackedThroughSeq,
202
+ mode: 'steer',
203
+ });
204
+ }
205
+ }).catch((err) => {
206
+ const message = String(err?.message ?? err);
207
+ log.warn('[agent-node] run.steer error', err);
208
+ connection.send({
209
+ type: 'run.event',
210
+ runId: msg.runId,
211
+ conversationId: msg.conversationId,
212
+ event: {
213
+ type: 'run.steer.result',
214
+ requestId: msg.requestId,
215
+ runId: msg.runId,
216
+ ok: false,
217
+ error: message,
218
+ createdAt: Date.now(),
219
+ },
220
+ });
221
+ connection.send({
222
+ type: 'run.steer.result',
223
+ requestId: msg.requestId,
224
+ runId: msg.runId,
225
+ conversationId: msg.conversationId,
226
+ ok: false,
227
+ error: message,
228
+ });
229
+ });
230
+ break;
231
+ case 'permission.response':
232
+ executor.handlePermissionResponse(msg.requestId, msg.decision, msg.selectedActionId, msg.responseText, msg.answers).then((handled) => {
233
+ if (!handled) {
234
+ log.warn('[agent-node] permission.response had no pending request', msg.requestId);
235
+ }
236
+ }).catch((err) => {
237
+ log.warn('[agent-node] permission.response error', err);
238
+ });
239
+ break;
240
+ case 'solo.run.dispatch':
241
+ executor.handleSoloDispatch(msg).catch((err) => {
242
+ log.warn('[agent-node] solo.run.dispatch error', err);
243
+ });
244
+ break;
245
+ case 'solo.cancel':
246
+ executor.handleSoloCancel(msg).catch((err) => {
247
+ log.warn('[agent-node] solo.cancel error', err);
248
+ });
249
+ break;
250
+ case 'solo.close':
251
+ executor.handleSoloClose(msg).catch((err) => {
252
+ log.warn('[agent-node] solo.close error', err);
253
+ });
254
+ break;
255
+ case 'solo.steer':
256
+ executor.handleSoloSteer(msg).then((handled) => {
257
+ if (!handled) {
258
+ log.warn('[agent-node] solo.steer had no active solo runtime', { soloSessionId: msg.soloSessionId });
259
+ }
260
+ }).catch((err) => {
261
+ log.warn('[agent-node] solo.steer error', err);
262
+ });
263
+ break;
264
+ case 'solo.permission.response':
265
+ executor.handleSoloPermissionResponse(msg).then((handled) => {
266
+ if (!handled) {
267
+ log.warn('[agent-node] solo.permission.response had no pending request', { soloSessionId: msg.soloSessionId });
268
+ }
269
+ }).catch((err) => {
270
+ log.warn('[agent-node] solo.permission.response error', err);
271
+ });
272
+ break;
273
+ case 'mission.run.request':
274
+ try {
275
+ if (msg.runtimeProvider === 'bigbang_native') {
276
+ const wasAlreadyCancelled = pendingNativeCancellations.has(msg.requestId);
277
+ runNativeMission(msg, (outbound) => connection.send(outbound), {
278
+ onHostCreated: (host) => {
279
+ activeNativeWorkers.set(msg.requestId, host);
280
+ // If a cancellation raced ahead of host creation, honour it
281
+ // immediately so the worker does not start running.
282
+ if (pendingNativeCancellations.has(msg.requestId)) {
283
+ host.cancel();
284
+ pendingNativeCancellations.delete(msg.requestId);
285
+ }
286
+ },
287
+ onHostExit: () => {
288
+ activeNativeWorkers.delete(msg.requestId);
289
+ pendingNativeCancellations.delete(msg.requestId);
290
+ },
291
+ onDispatchCreated: (dispatch) => {
292
+ activeNativeWorkers.set(msg.requestId, dispatch);
293
+ // If a cancellation raced ahead of dispatch creation, honour it
294
+ // immediately so the executor run does not start.
295
+ if (pendingNativeCancellations.has(msg.requestId)) {
296
+ dispatch.cancel();
297
+ pendingNativeCancellations.delete(msg.requestId);
298
+ }
299
+ },
300
+ onDispatchFinished: () => {
301
+ activeNativeWorkers.delete(msg.requestId);
302
+ pendingNativeCancellations.delete(msg.requestId);
303
+ },
304
+ }, nativeAgentExecutor ?? undefined);
305
+ if (wasAlreadyCancelled) {
306
+ pendingNativeCancellations.delete(msg.requestId);
307
+ }
308
+ }
309
+ else {
310
+ runDroidMission(msg, (outbound) => connection.send(outbound));
311
+ }
312
+ }
313
+ catch (error) {
314
+ activeNativeWorkers.delete(msg.requestId);
315
+ pendingNativeCancellations.delete(msg.requestId);
316
+ connection.send({
317
+ type: 'mission.run.end',
318
+ requestId: msg.requestId,
319
+ missionId: msg.missionId,
320
+ missionDir: msg.missionDir,
321
+ error: String(error.message ?? error),
322
+ });
323
+ }
324
+ break;
325
+ case 'mission.run.cancel': {
326
+ if (msg.runtimeProvider === 'bigbang_native') {
327
+ const active = activeNativeWorkers.get(msg.requestId);
328
+ if (active) {
329
+ active.cancel();
330
+ pendingNativeCancellations.delete(msg.requestId);
331
+ log.info('[agent-node] native mission worker cancelled', {
332
+ requestId: msg.requestId,
333
+ missionId: msg.missionId,
334
+ featureId: msg.featureId,
335
+ });
336
+ }
337
+ else {
338
+ // The worker may not have been created yet if core dispatched and
339
+ // immediately cancelled. Record the cancellation so it is honoured
340
+ // as soon as the host spawns.
341
+ pendingNativeCancellations.add(msg.requestId);
342
+ log.info('[agent-node] native mission cancellation recorded for pending worker', {
343
+ requestId: msg.requestId,
344
+ missionId: msg.missionId,
345
+ featureId: msg.featureId,
346
+ });
347
+ }
348
+ }
349
+ else {
350
+ log.warn('[agent-node] mission.run.cancel received for non-native runtime', {
351
+ requestId: msg.requestId,
352
+ missionId: msg.missionId,
353
+ runtimeProvider: msg.runtimeProvider,
354
+ });
355
+ }
356
+ break;
357
+ }
358
+ case 'mission.continuation.inspect.request':
359
+ connection.send(inspectDroidMissionContinuation(msg));
360
+ break;
361
+ case 'workspace.list.request':
362
+ try {
363
+ const readDirectory = () => listWorkspaceDirectory(msg.workspaceRoot, msg.relativePath, {
364
+ scaffold: msg.scaffold,
365
+ agentName: msg.agentName,
366
+ cursor: msg.cursor,
367
+ limit: msg.limit,
368
+ });
369
+ const result = executor.isRunActiveInWorkspace(msg.runId, msg.workspaceRoot)
370
+ ? await readDirectory()
371
+ : await workspaceLockManager.runAfterPendingWrites(msg.workspaceRoot, readDirectory);
372
+ connection.send({
373
+ type: 'workspace.list.response',
374
+ requestId: msg.requestId,
375
+ relativePath: result.relativePath,
376
+ entries: result.entries,
377
+ directoryPage: result.directoryPage,
378
+ });
379
+ }
380
+ catch (error) {
381
+ connection.send(workspacePathErrorResponse('workspace.list.response', msg.requestId, msg.relativePath, error));
382
+ }
383
+ break;
384
+ case 'workspace.read.request':
385
+ try {
386
+ const readFile = () => readWorkspaceFile(msg.workspaceRoot, msg.relativePath, {
387
+ scaffold: msg.scaffold,
388
+ agentName: msg.agentName,
389
+ offset: msg.offset,
390
+ limit: msg.limit,
391
+ });
392
+ const result = executor.isRunActiveInWorkspace(msg.runId, msg.workspaceRoot)
393
+ ? await readFile()
394
+ : await workspaceLockManager.runAfterPendingWrites(msg.workspaceRoot, readFile);
395
+ connection.send({
396
+ type: 'workspace.read.response',
397
+ requestId: msg.requestId,
398
+ relativePath: result.relativePath,
399
+ content: result.content,
400
+ mimeType: result.mimeType,
401
+ size: result.size,
402
+ modifiedAt: result.modifiedAt,
403
+ offset: result.offset,
404
+ limit: result.limit,
405
+ nextOffset: result.nextOffset,
406
+ hasMore: result.hasMore,
407
+ });
408
+ }
409
+ catch (error) {
410
+ connection.send(workspacePathErrorResponse('workspace.read.response', msg.requestId, msg.relativePath, error));
411
+ }
412
+ break;
413
+ case 'workspace.archive.request':
414
+ try {
415
+ const archiveDirectory = () => createWorkspaceDirectoryArchive(msg.workspaceRoot, msg.relativePath, {
416
+ maxBytes: msg.maxBytes,
417
+ });
418
+ const result = await workspaceLockManager.runAfterPendingWrites(msg.workspaceRoot, archiveDirectory);
419
+ connection.send({
420
+ type: 'workspace.archive.response',
421
+ requestId: msg.requestId,
422
+ relativePath: result.relativePath,
423
+ archiveBase64: result.archiveBase64,
424
+ byteSize: result.byteSize,
425
+ fileCount: result.fileCount,
426
+ });
427
+ }
428
+ catch (error) {
429
+ connection.send(workspaceArchiveErrorResponse(msg.requestId, msg.relativePath, error));
430
+ }
431
+ break;
432
+ case 'library.mirror.apply.request':
433
+ try {
434
+ applyLibraryMirror(msg.workspaceRoot, {
435
+ shared: msg.shared,
436
+ personal: msg.personal,
437
+ });
438
+ connection.send({
439
+ type: 'library.mirror.apply.response',
440
+ requestId: msg.requestId,
441
+ ok: true,
442
+ });
443
+ }
444
+ catch (error) {
445
+ connection.send(libraryMirrorErrorResponse('library.mirror.apply.response', msg.requestId, error));
446
+ }
447
+ break;
448
+ case 'library.mirror.snapshot.request':
449
+ try {
450
+ const snapshot = snapshotLibraryMirror(msg.workspaceRoot);
451
+ connection.send({
452
+ type: 'library.mirror.snapshot.response',
453
+ requestId: msg.requestId,
454
+ shared: snapshot.shared,
455
+ personal: snapshot.personal,
456
+ });
457
+ }
458
+ catch (error) {
459
+ connection.send(libraryMirrorErrorResponse('library.mirror.snapshot.response', msg.requestId, error));
460
+ }
461
+ break;
462
+ case 'library.authority.delete.request':
463
+ try {
464
+ deleteLibraryAuthorityPath(msg.libraryRoot, msg.relativePath, {
465
+ recursive: msg.recursive === true,
466
+ });
467
+ connection.send({
468
+ type: 'library.authority.delete.response',
469
+ requestId: msg.requestId,
470
+ ok: true,
471
+ });
472
+ }
473
+ catch (error) {
474
+ connection.send(libraryAuthorityDeleteErrorResponse(msg.requestId, error));
475
+ }
476
+ break;
477
+ case 'library.authority.rename.request':
478
+ try {
479
+ renameLibraryAuthorityPath(msg.libraryRoot, msg.relativePath, msg.nextRelativePath);
480
+ connection.send({
481
+ type: 'library.authority.rename.response',
482
+ requestId: msg.requestId,
483
+ ok: true,
484
+ });
485
+ }
486
+ catch (error) {
487
+ connection.send(libraryAuthorityRenameErrorResponse(msg.requestId, error));
488
+ }
489
+ break;
490
+ case 'fs.read_path.request':
491
+ try {
492
+ const result = readExactPath(msg.absolutePath, {
493
+ allowedRoot: msg.allowedRoot,
494
+ trustedRoot: msg.trustedRoot,
495
+ cursor: msg.cursor,
496
+ offset: msg.offset,
497
+ limit: msg.limit,
498
+ maxPreviewBytes: msg.maxPreviewBytes,
499
+ rawContentBase64: msg.rawContentBase64,
500
+ });
501
+ connection.send({
502
+ type: 'fs.read_path.response',
503
+ requestId: msg.requestId,
504
+ absolutePath: result.absolutePath,
505
+ kind: result.kind,
506
+ ...(result.kind === 'file'
507
+ ? {
508
+ content: result.content,
509
+ mimeType: result.mimeType,
510
+ size: result.size,
511
+ offset: result.offset,
512
+ limit: result.limit,
513
+ nextOffset: result.nextOffset,
514
+ hasMore: result.hasMore,
515
+ rawContentBase64: result.rawContentBase64,
516
+ }
517
+ : {
518
+ entries: result.entries,
519
+ directoryPage: result.directoryPage,
520
+ }),
521
+ modifiedAt: result.modifiedAt,
522
+ });
523
+ }
524
+ catch (error) {
525
+ connection.send(fsPathErrorResponse('fs.read_path.response', msg.requestId, msg.absolutePath, error));
526
+ }
527
+ break;
528
+ case 'fs.path_suggest.request':
529
+ try {
530
+ const result = suggestDirectoryPaths(msg.query, { limit: msg.limit });
531
+ connection.send({
532
+ type: 'fs.path_suggest.response',
533
+ requestId: msg.requestId,
534
+ query: result.query,
535
+ suggestions: result.suggestions,
536
+ });
537
+ }
538
+ catch (error) {
539
+ connection.send(fsPathSuggestErrorResponse(msg.requestId, msg.query, error));
540
+ }
541
+ break;
542
+ case 'http.fetch_text.request':
543
+ try {
544
+ const authHeaders = resolvePanelApiJsonlAuthHeaders(config.panelApiJsonlAuthProfiles, msg.authProfile);
545
+ const result = await fetchLoopbackHttpText(msg.url, {
546
+ maxBytes: msg.maxBytes,
547
+ allowedOrigins: config.panelApiJsonlAllowedOrigins,
548
+ allowedUrlPrefixes: config.panelApiJsonlAllowedUrlPrefixes,
549
+ ...(typeof msg.ifNoneMatch === 'string' && msg.ifNoneMatch.trim() ? { ifNoneMatch: msg.ifNoneMatch.trim() } : {}),
550
+ ...(typeof msg.ifModifiedSince === 'string' && msg.ifModifiedSince.trim() ? { ifModifiedSince: msg.ifModifiedSince.trim() } : {}),
551
+ ...(typeof msg.ifRange === 'string' && msg.ifRange.trim() ? { ifRange: msg.ifRange.trim() } : {}),
552
+ ...(typeof msg.rangeStart === 'number' ? { rangeStart: msg.rangeStart } : {}),
553
+ ...(typeof msg.rangeLength === 'number' ? { rangeLength: msg.rangeLength } : {}),
554
+ ...(authHeaders ? { authHeaders } : {}),
555
+ });
556
+ connection.send({
557
+ type: 'http.fetch_text.response',
558
+ requestId: msg.requestId,
559
+ url: result.url,
560
+ content: result.content,
561
+ status: result.status,
562
+ contentType: result.contentType,
563
+ size: result.size,
564
+ partialContent: result.partialContent,
565
+ rangeStart: result.rangeStart,
566
+ rangeEnd: result.rangeEnd,
567
+ totalSize: result.totalSize,
568
+ hasMore: result.hasMore,
569
+ notModified: result.notModified,
570
+ etag: result.etag,
571
+ lastModified: result.lastModified,
572
+ });
573
+ }
574
+ catch (error) {
575
+ connection.send(httpFetchTextErrorResponse(msg.requestId, msg.url, error));
576
+ }
577
+ break;
578
+ case 'workspace.service_proxy.request':
579
+ try {
580
+ const result = await proxyWorkspaceServiceHttp(msg.url, {
581
+ method: msg.method,
582
+ headers: msg.headers,
583
+ body: msg.body,
584
+ maxBytes: msg.maxBytes,
585
+ });
586
+ connection.send({
587
+ type: 'workspace.service_proxy.response',
588
+ requestId: msg.requestId,
589
+ url: result.url,
590
+ method: result.method,
591
+ status: result.status,
592
+ body: result.body,
593
+ contentType: result.contentType,
594
+ size: result.size,
595
+ });
596
+ }
597
+ catch (error) {
598
+ connection.send(workspaceServiceProxyErrorResponse(msg.requestId, msg.url, msg.method, error));
599
+ }
600
+ break;
601
+ case 'fs.read_path.stream.request':
602
+ try {
603
+ const info = statExactPathRawStream(msg.absolutePath, { allowedRoot: msg.allowedRoot });
604
+ const stream = createExactPathReadStream(info.absolutePath);
605
+ connection.send({
606
+ type: 'fs.read_path.stream.start',
607
+ requestId: msg.requestId,
608
+ absolutePath: info.absolutePath,
609
+ mimeType: info.mimeType,
610
+ size: info.size,
611
+ modifiedAt: info.modifiedAt,
612
+ });
613
+ stream.on('data', (chunk) => {
614
+ connection.send({
615
+ type: 'fs.read_path.stream.chunk',
616
+ requestId: msg.requestId,
617
+ encoding: 'base64',
618
+ chunk: Buffer.isBuffer(chunk) ? chunk.toString('base64') : Buffer.from(chunk).toString('base64'),
619
+ });
620
+ });
621
+ stream.on('end', () => {
622
+ connection.send({
623
+ type: 'fs.read_path.stream.end',
624
+ requestId: msg.requestId,
625
+ });
626
+ });
627
+ stream.on('error', (error) => {
628
+ connection.send(fsPathStreamEndErrorResponse(msg.requestId, error));
629
+ });
630
+ }
631
+ catch (error) {
632
+ connection.send(fsPathStreamEndErrorResponse(msg.requestId, error));
633
+ }
634
+ break;
635
+ case 'workspace.inspect.request':
636
+ try {
637
+ connection.send({
638
+ type: 'workspace.inspect.response',
639
+ requestId: msg.requestId,
640
+ inspect: inspectWorkspace(msg.workspaceRoot),
641
+ });
642
+ }
643
+ catch (error) {
644
+ connection.send(workspaceInspectErrorResponse(msg.requestId, error));
645
+ }
646
+ break;
647
+ case 'workspace.git_status.request':
648
+ try {
649
+ connection.send({
650
+ type: 'workspace.git_status.response',
651
+ requestId: msg.requestId,
652
+ status: getWorkspaceGitStatus(msg.workspaceRoot),
653
+ });
654
+ }
655
+ catch (error) {
656
+ connection.send(workspaceGitStatusErrorResponse(msg.requestId, error));
657
+ }
658
+ break;
659
+ case 'workspace.git_diff.request':
660
+ try {
661
+ connection.send({
662
+ type: 'workspace.git_diff.response',
663
+ requestId: msg.requestId,
664
+ diff: getWorkspaceGitDiff(msg.workspaceRoot, msg.mode),
665
+ });
666
+ }
667
+ catch (error) {
668
+ connection.send(workspaceGitDiffErrorResponse(msg.requestId, error));
669
+ }
670
+ break;
671
+ case 'workspace.git_action.request':
672
+ try {
673
+ connection.send({
674
+ type: 'workspace.git_action.response',
675
+ requestId: msg.requestId,
676
+ result: runWorkspaceGitAction(msg.workspaceRoot, msg.action, msg.commitMessage),
677
+ });
678
+ }
679
+ catch (error) {
680
+ connection.send(workspaceGitActionErrorResponse(msg.requestId, error));
681
+ }
682
+ break;
683
+ case 'claude.controls.request':
684
+ try {
685
+ connection.send({
686
+ type: 'claude.controls.response',
687
+ requestId: msg.requestId,
688
+ controls: await executor.getClaudeControls(msg.target),
689
+ });
690
+ }
691
+ catch (error) {
692
+ connection.send(claudeControlErrorResponse('claude.controls.response', msg.requestId, error));
693
+ }
694
+ break;
695
+ case 'claude.set_mode.request':
696
+ try {
697
+ connection.send({
698
+ type: 'claude.set_mode.response',
699
+ requestId: msg.requestId,
700
+ controls: await executor.setClaudeMode(msg.target, msg.modeId),
701
+ });
702
+ }
703
+ catch (error) {
704
+ connection.send(claudeControlErrorResponse('claude.set_mode.response', msg.requestId, error));
705
+ }
706
+ break;
707
+ case 'claude.set_model.request':
708
+ try {
709
+ connection.send({
710
+ type: 'claude.set_model.response',
711
+ requestId: msg.requestId,
712
+ controls: await executor.setClaudeModel(msg.target, msg.modelId ?? null),
713
+ });
714
+ }
715
+ catch (error) {
716
+ connection.send(claudeControlErrorResponse('claude.set_model.response', msg.requestId, error));
717
+ }
718
+ break;
719
+ case 'claude.command.request':
720
+ try {
721
+ const { controls, result } = await executor.executeClaudeCommand(msg.target, msg.commandName, msg.args ?? null);
722
+ connection.send({
723
+ type: 'claude.command.response',
724
+ requestId: msg.requestId,
725
+ controls,
726
+ result,
727
+ });
728
+ }
729
+ catch (error) {
730
+ connection.send(claudeControlErrorResponse('claude.command.response', msg.requestId, error));
731
+ }
732
+ break;
733
+ case 'codex.model.list.request':
734
+ try {
735
+ const result = await codexAppServerRpc.listModels({
736
+ workspaceRoot: msg.workspaceRoot ?? null,
737
+ cursor: msg.cursor ?? null,
738
+ limit: msg.limit ?? null,
739
+ includeHidden: msg.includeHidden ?? null,
740
+ });
741
+ connection.send({
742
+ type: 'codex.model.list.response',
743
+ requestId: msg.requestId,
744
+ models: result.models,
745
+ nextCursor: result.nextCursor,
746
+ defaultModel: result.defaultModel,
747
+ });
748
+ }
749
+ catch (error) {
750
+ connection.send(codexAppServerErrorResponse('codex.model.list.response', msg.requestId, error));
751
+ }
752
+ break;
753
+ case 'codex.skills.list.request':
754
+ try {
755
+ const result = await codexAppServerRpc.listSkills({
756
+ workspaceRoot: msg.workspaceRoot,
757
+ skillRoots: msg.skillRoots,
758
+ forceReload: msg.forceReload,
759
+ });
760
+ connection.send({
761
+ type: 'codex.skills.list.response',
762
+ requestId: msg.requestId,
763
+ data: result.data,
764
+ });
765
+ }
766
+ catch (error) {
767
+ connection.send(codexAppServerErrorResponse('codex.skills.list.response', msg.requestId, error));
768
+ }
769
+ break;
770
+ case 'codex.thread.list.request':
771
+ try {
772
+ const result = await codexAppServerRpc.listThreads({
773
+ workspaceRoot: msg.workspaceRoot,
774
+ cursor: msg.cursor ?? null,
775
+ limit: msg.limit ?? null,
776
+ archived: msg.archived ?? null,
777
+ searchTerm: msg.searchTerm ?? null,
778
+ useStateDbOnly: msg.useStateDbOnly,
779
+ });
780
+ connection.send({
781
+ type: 'codex.thread.list.response',
782
+ requestId: msg.requestId,
783
+ threads: result.threads,
784
+ nextCursor: result.nextCursor,
785
+ backwardsCursor: result.backwardsCursor,
786
+ });
787
+ }
788
+ catch (error) {
789
+ connection.send(codexAppServerErrorResponse('codex.thread.list.response', msg.requestId, error));
790
+ }
791
+ break;
792
+ case 'codex.thread.read.request':
793
+ try {
794
+ const result = await codexAppServerRpc.readThread({
795
+ workspaceRoot: msg.workspaceRoot,
796
+ threadId: msg.threadId,
797
+ includeTurns: msg.includeTurns,
798
+ });
799
+ connection.send({
800
+ type: 'codex.thread.read.response',
801
+ requestId: msg.requestId,
802
+ thread: result.thread,
803
+ });
804
+ }
805
+ catch (error) {
806
+ connection.send(codexAppServerErrorResponse('codex.thread.read.response', msg.requestId, error));
807
+ }
808
+ break;
809
+ case 'terminal.list.request':
810
+ try {
811
+ connection.send({
812
+ type: 'terminal.list.response',
813
+ requestId: msg.requestId,
814
+ workspaceRoot: msg.workspaceRoot,
815
+ terminals: terminalManager.list(msg.workspaceRoot),
816
+ });
817
+ }
818
+ catch (error) {
819
+ connection.send(terminalErrorResponse('terminal.list.response', msg.requestId, error, {
820
+ workspaceRoot: msg.workspaceRoot,
821
+ }));
822
+ }
823
+ break;
824
+ case 'terminal.create.request':
825
+ try {
826
+ const terminal = terminalManager.create({
827
+ terminalId: randomUUID(),
828
+ workspaceRoot: msg.workspaceRoot,
829
+ cwd: msg.cwd,
830
+ name: msg.name,
831
+ startupCommand: msg.startupCommand,
832
+ closeOnStartupCommand: msg.closeOnStartupCommand,
833
+ cols: msg.cols,
834
+ rows: msg.rows,
835
+ });
836
+ connection.send({
837
+ type: 'terminal.create.response',
838
+ requestId: msg.requestId,
839
+ terminal,
840
+ });
841
+ }
842
+ catch (error) {
843
+ connection.send(terminalErrorResponse('terminal.create.response', msg.requestId, error));
844
+ }
845
+ break;
846
+ case 'terminal.snapshot.request':
847
+ try {
848
+ const snapshot = terminalManager.snapshot(msg.terminalId);
849
+ connection.send({
850
+ type: 'terminal.snapshot.response',
851
+ requestId: msg.requestId,
852
+ terminal: snapshot.terminal,
853
+ buffer: snapshot.buffer,
854
+ });
855
+ }
856
+ catch (error) {
857
+ connection.send(terminalErrorResponse('terminal.snapshot.response', msg.requestId, error));
858
+ }
859
+ break;
860
+ case 'terminal.input.request':
861
+ try {
862
+ terminalManager.input(msg.terminalId, msg.data);
863
+ connection.send({
864
+ type: 'terminal.input.response',
865
+ requestId: msg.requestId,
866
+ ok: true,
867
+ });
868
+ }
869
+ catch (error) {
870
+ connection.send(terminalErrorResponse('terminal.input.response', msg.requestId, error));
871
+ }
872
+ break;
873
+ case 'terminal.resize.request':
874
+ try {
875
+ terminalManager.resize(msg.terminalId, msg.cols, msg.rows);
876
+ connection.send({
877
+ type: 'terminal.resize.response',
878
+ requestId: msg.requestId,
879
+ ok: true,
880
+ });
881
+ }
882
+ catch (error) {
883
+ connection.send(terminalErrorResponse('terminal.resize.response', msg.requestId, error));
884
+ }
885
+ break;
886
+ case 'terminal.close.request':
887
+ try {
888
+ terminalManager.close(msg.terminalId);
889
+ connection.send({
890
+ type: 'terminal.close.response',
891
+ requestId: msg.requestId,
892
+ ok: true,
893
+ });
894
+ }
895
+ catch (error) {
896
+ connection.send(terminalErrorResponse('terminal.close.response', msg.requestId, error));
897
+ }
898
+ break;
899
+ case 'skills.list.request':
900
+ try {
901
+ syncNativeSkillsFromRequest(msg);
902
+ const result = listSkills(msg.skillRoots, msg.path);
903
+ connection.send({
904
+ type: 'skills.list.response',
905
+ requestId: msg.requestId,
906
+ roots: result.roots,
907
+ path: result.path,
908
+ skills: result.skills,
909
+ entries: result.entries,
910
+ });
911
+ }
912
+ catch (error) {
913
+ connection.send(skillListErrorResponse(msg.requestId, msg.skillRoots, msg.path ?? null, error));
914
+ }
915
+ break;
916
+ case 'skills.read.request':
917
+ try {
918
+ syncNativeSkillsFromRequest(msg);
919
+ const result = readSkillFile(msg.skillRoots, msg.path);
920
+ connection.send({
921
+ type: 'skills.read.response',
922
+ requestId: msg.requestId,
923
+ path: result.path,
924
+ content: result.content,
925
+ mimeType: result.mimeType,
926
+ size: result.size,
927
+ modifiedAt: result.modifiedAt,
928
+ });
929
+ }
930
+ catch (error) {
931
+ connection.send(skillReadErrorResponse(msg.requestId, msg.path, error));
932
+ }
933
+ break;
934
+ case 'codex.transcript.list.request':
935
+ try {
936
+ const result = listCodexTranscriptFiles(msg.maxFiles);
937
+ connection.send({
938
+ type: 'codex.transcript.list.response',
939
+ requestId: msg.requestId,
940
+ rootPath: result.rootPath,
941
+ files: result.files,
942
+ truncated: result.truncated,
943
+ });
944
+ }
945
+ catch (error) {
946
+ connection.send(codexTranscriptListErrorResponse(msg.requestId, error));
947
+ }
948
+ break;
949
+ case 'codex.transcript.read.request':
950
+ try {
951
+ const result = readCodexTranscriptFile(msg.path);
952
+ connection.send({
953
+ type: 'codex.transcript.read.response',
954
+ requestId: msg.requestId,
955
+ rootPath: result.rootPath,
956
+ path: result.path,
957
+ content: result.content,
958
+ size: result.size,
959
+ modifiedAt: result.modifiedAt,
960
+ });
961
+ }
962
+ catch (error) {
963
+ connection.send(codexTranscriptReadErrorResponse(msg.requestId, msg.path, error));
964
+ }
965
+ break;
966
+ case 'claude.transcript.list.request':
967
+ try {
968
+ const result = listClaudeTranscriptFiles(msg.workspaceRoot, msg.maxFiles);
969
+ connection.send({
970
+ type: 'claude.transcript.list.response',
971
+ requestId: msg.requestId,
972
+ rootPath: result.rootPath,
973
+ files: result.files,
974
+ truncated: result.truncated,
975
+ });
976
+ }
977
+ catch (error) {
978
+ connection.send(claudeTranscriptListErrorResponse(msg.requestId, error));
979
+ }
980
+ break;
981
+ case 'claude.transcript.read.request':
982
+ try {
983
+ const result = readClaudeTranscriptFile(msg.workspaceRoot, msg.path);
984
+ connection.send({
985
+ type: 'claude.transcript.read.response',
986
+ requestId: msg.requestId,
987
+ rootPath: result.rootPath,
988
+ path: result.path,
989
+ content: result.content,
990
+ size: result.size,
991
+ modifiedAt: result.modifiedAt,
992
+ });
993
+ }
994
+ catch (error) {
995
+ connection.send(claudeTranscriptReadErrorResponse(msg.requestId, msg.path, error));
996
+ }
997
+ break;
998
+ case 'workspace.write.request':
999
+ try {
1000
+ const result = await workspaceLockManager.runExclusive(msg.workspaceRoot, async () => writeWorkspaceFile(msg.workspaceRoot, msg.relativePath, msg.content, msg.mode, {
1001
+ scaffold: msg.scaffold,
1002
+ agentName: msg.agentName,
1003
+ contentEncoding: msg.contentEncoding,
1004
+ }));
1005
+ connection.send({
1006
+ type: 'workspace.write.response',
1007
+ requestId: msg.requestId,
1008
+ relativePath: result.relativePath,
1009
+ ok: true,
1010
+ modifiedAt: result.modifiedAt,
1011
+ });
1012
+ }
1013
+ catch (error) {
1014
+ connection.send(workspaceWriteErrorResponse(msg.requestId, msg.relativePath, error));
1015
+ }
1016
+ break;
1017
+ case 'workspace.extract.request':
1018
+ try {
1019
+ const result = await workspaceLockManager.runExclusive(msg.workspaceRoot, async () => extractWorkspaceDirectoryArchive(msg.workspaceRoot, msg.relativePath, msg.archiveBase64, {
1020
+ maxBytes: msg.maxBytes,
1021
+ overwrite: msg.overwrite === true,
1022
+ }));
1023
+ connection.send({
1024
+ type: 'workspace.extract.response',
1025
+ requestId: msg.requestId,
1026
+ relativePath: result.relativePath,
1027
+ ok: true,
1028
+ byteSize: result.byteSize,
1029
+ fileCount: result.fileCount,
1030
+ });
1031
+ }
1032
+ catch (error) {
1033
+ connection.send(workspaceExtractErrorResponse(msg.requestId, msg.relativePath, error));
1034
+ }
1035
+ break;
1036
+ case 'workspace.delete_file.request':
1037
+ try {
1038
+ const result = await workspaceLockManager.runExclusive(msg.workspaceRoot, async () => deleteWorkspaceFile(msg.workspaceRoot, msg.relativePath));
1039
+ connection.send({
1040
+ type: 'workspace.delete_file.response',
1041
+ requestId: msg.requestId,
1042
+ relativePath: result.relativePath,
1043
+ ok: true,
1044
+ });
1045
+ }
1046
+ catch (error) {
1047
+ connection.send(workspaceDeleteFileErrorResponse(msg.requestId, msg.relativePath, error));
1048
+ }
1049
+ break;
1050
+ case 'workspace.delete_path.request':
1051
+ try {
1052
+ const result = await workspaceLockManager.runExclusive(msg.workspaceRoot, async () => deleteWorkspacePath(msg.workspaceRoot, msg.relativePath, { recursive: msg.recursive === true }));
1053
+ connection.send({
1054
+ type: 'workspace.delete_path.response',
1055
+ requestId: msg.requestId,
1056
+ relativePath: result.relativePath,
1057
+ ok: true,
1058
+ });
1059
+ }
1060
+ catch (error) {
1061
+ connection.send(workspaceDeletePathErrorResponse(msg.requestId, msg.relativePath, error));
1062
+ }
1063
+ break;
1064
+ case 'host.close':
1065
+ executor.closeHost(msg.hostKey);
1066
+ log.info('[agent-node] host closed', { hostKey: msg.hostKey });
1067
+ break;
1068
+ case 'codex.conversation.summary.request':
1069
+ try {
1070
+ const content = await runCodexConversationSummary({
1071
+ db,
1072
+ config,
1073
+ prompt: msg.prompt,
1074
+ envVars: msg.envVars,
1075
+ });
1076
+ connection.send({
1077
+ type: 'codex.conversation.summary.response',
1078
+ requestId: msg.requestId,
1079
+ content,
1080
+ });
1081
+ }
1082
+ catch (error) {
1083
+ connection.send({
1084
+ type: 'codex.conversation.summary.response',
1085
+ requestId: msg.requestId,
1086
+ error: String(error?.message ?? error),
1087
+ });
1088
+ }
1089
+ break;
1090
+ case 'agent.cleanup.request':
1091
+ try {
1092
+ const result = await executor.cleanupAgentRuntime(msg.hostKeys, msg.sessionKeys);
1093
+ log.info('[agent-node] agent runtime cleaned up', {
1094
+ agentId: msg.agentId,
1095
+ hostKeys: msg.hostKeys,
1096
+ sessionKeys: msg.sessionKeys,
1097
+ ...result,
1098
+ });
1099
+ connection.send({
1100
+ type: 'agent.cleanup.response',
1101
+ requestId: msg.requestId,
1102
+ agentId: msg.agentId,
1103
+ ok: true,
1104
+ });
1105
+ }
1106
+ catch (error) {
1107
+ connection.send({
1108
+ type: 'agent.cleanup.response',
1109
+ requestId: msg.requestId,
1110
+ agentId: msg.agentId,
1111
+ error: String(error?.message ?? error),
1112
+ });
1113
+ }
1114
+ break;
1115
+ case 'workspace.reset.request':
1116
+ try {
1117
+ executor.resetWorkspace(msg.workspaceRoot);
1118
+ await workspaceLockManager.runExclusive(msg.workspaceRoot, async () => {
1119
+ resetWorkspaceDirectory(msg.workspaceRoot, msg.agentName);
1120
+ });
1121
+ connection.send({
1122
+ type: 'workspace.reset.response',
1123
+ requestId: msg.requestId,
1124
+ workspaceRoot: msg.workspaceRoot,
1125
+ ok: true,
1126
+ });
1127
+ }
1128
+ catch (error) {
1129
+ connection.send(workspaceResetErrorResponse(msg.requestId, msg.workspaceRoot, error));
1130
+ }
1131
+ break;
1132
+ default: {
1133
+ const _exhaustive = msg;
1134
+ log.warn('[agent-node] unknown message', _exhaustive);
1135
+ }
1136
+ }
1137
+ })().catch((error) => {
1138
+ log.warn('[agent-node] message handler error', {
1139
+ type: msg.type,
1140
+ error: String(error?.message ?? error),
1141
+ });
1142
+ });
1143
+ };
1144
+ connection = new CoreConnection(config, handleMessage, {
1145
+ getTerminalBackendAvailable: () => terminalManager.isBackendAvailable(),
1146
+ onConnected: () => {
1147
+ log.info(`[agent-node] connected to ${config.coreUrl} as ${config.nodeId}`);
1148
+ },
1149
+ onDisconnected: () => {
1150
+ log.warn('[agent-node] disconnected from core, waiting to reconnect');
1151
+ executor?.closeSoloHosts();
1152
+ },
1153
+ });
1154
+ executor = new Executor({
1155
+ db,
1156
+ config,
1157
+ send: (msg) => connection.send(msg),
1158
+ workspaceLockManager,
1159
+ });
1160
+ nativeAgentExecutor = new NativeMissionAgentExecutor({
1161
+ db,
1162
+ config,
1163
+ send: (msg) => connection.send(msg),
1164
+ });
1165
+ process.on('uncaughtException', (error) => {
1166
+ if (!isRecoverableRuntimeStreamError(error)) {
1167
+ throw error;
1168
+ }
1169
+ const failed = executor.failCurrentRestoredDispatch(error);
1170
+ log.warn('[agent-node] swallowed recoverable runtime stream error', {
1171
+ error: String(error?.message ?? error),
1172
+ failedRestoredDispatch: failed,
1173
+ });
1174
+ });
1175
+ const hostSnapshotTimer = setInterval(() => {
1176
+ publishHostSnapshot();
1177
+ publishRuntimeSnapshot();
1178
+ }, config.heartbeatIntervalMs);
1179
+ hostSnapshotTimer.unref?.();
1180
+ await connection.connect();
1181
+ const shutdown = () => {
1182
+ log.warn('[agent-node] shutting down');
1183
+ clearInterval(hostSnapshotTimer);
1184
+ executor.close();
1185
+ connection.close();
1186
+ db.close();
1187
+ process.exit(0);
1188
+ };
1189
+ process.on('SIGINT', shutdown);
1190
+ process.on('SIGTERM', shutdown);
1191
+ }
1192
+ await main();
1193
+ function syncNativeSkillsFromRequest(msg) {
1194
+ if (!msg.agentType || !msg.workspaceRoot)
1195
+ return;
1196
+ ensureNativeSkillMounts({
1197
+ agentType: msg.agentType,
1198
+ workspaceRoot: msg.workspaceRoot,
1199
+ skillRoots: msg.skillRoots,
1200
+ });
1201
+ }
1202
+ function workspacePathErrorResponse(type, requestId, relativePath, error) {
1203
+ if (error instanceof WorkspaceFsError) {
1204
+ return {
1205
+ type,
1206
+ requestId,
1207
+ relativePath,
1208
+ error: error.message,
1209
+ errorCode: error.code,
1210
+ };
1211
+ }
1212
+ return {
1213
+ type,
1214
+ requestId,
1215
+ relativePath,
1216
+ error: String(error?.message ?? error),
1217
+ errorCode: 'io_error',
1218
+ };
1219
+ }
1220
+ function fsPathErrorResponse(type, requestId, absolutePath, error) {
1221
+ if (error instanceof WorkspaceFsError) {
1222
+ return {
1223
+ type,
1224
+ requestId,
1225
+ absolutePath,
1226
+ error: error.message,
1227
+ errorCode: error.code,
1228
+ };
1229
+ }
1230
+ return {
1231
+ type,
1232
+ requestId,
1233
+ absolutePath,
1234
+ error: String(error?.message ?? error),
1235
+ errorCode: 'io_error',
1236
+ };
1237
+ }
1238
+ function httpFetchTextErrorResponse(requestId, url, error) {
1239
+ if (error instanceof WorkspaceFsError) {
1240
+ return {
1241
+ type: 'http.fetch_text.response',
1242
+ requestId,
1243
+ url,
1244
+ error: error.message,
1245
+ errorCode: error.code,
1246
+ };
1247
+ }
1248
+ return {
1249
+ type: 'http.fetch_text.response',
1250
+ requestId,
1251
+ url,
1252
+ error: String(error?.message ?? error),
1253
+ errorCode: 'io_error',
1254
+ };
1255
+ }
1256
+ function workspaceServiceProxyErrorResponse(requestId, url, method, error) {
1257
+ const normalizedMethod = method ?? 'GET';
1258
+ if (error instanceof WorkspaceFsError) {
1259
+ return {
1260
+ type: 'workspace.service_proxy.response',
1261
+ requestId,
1262
+ url,
1263
+ method: normalizedMethod,
1264
+ error: error.message,
1265
+ errorCode: error.code,
1266
+ };
1267
+ }
1268
+ return {
1269
+ type: 'workspace.service_proxy.response',
1270
+ requestId,
1271
+ url,
1272
+ method: normalizedMethod,
1273
+ error: String(error?.message ?? error),
1274
+ errorCode: 'io_error',
1275
+ };
1276
+ }
1277
+ function fsPathSuggestErrorResponse(requestId, query, error) {
1278
+ if (error instanceof WorkspaceFsError) {
1279
+ return {
1280
+ type: 'fs.path_suggest.response',
1281
+ requestId,
1282
+ query,
1283
+ error: error.message,
1284
+ errorCode: error.code,
1285
+ };
1286
+ }
1287
+ return {
1288
+ type: 'fs.path_suggest.response',
1289
+ requestId,
1290
+ query,
1291
+ error: String(error?.message ?? error),
1292
+ errorCode: 'io_error',
1293
+ };
1294
+ }
1295
+ function fsPathStreamEndErrorResponse(requestId, error) {
1296
+ if (error instanceof WorkspaceFsError) {
1297
+ return {
1298
+ type: 'fs.read_path.stream.end',
1299
+ requestId,
1300
+ error: error.message,
1301
+ errorCode: error.code,
1302
+ };
1303
+ }
1304
+ return {
1305
+ type: 'fs.read_path.stream.end',
1306
+ requestId,
1307
+ error: String(error?.message ?? error),
1308
+ errorCode: 'io_error',
1309
+ };
1310
+ }
1311
+ function workspaceInspectErrorResponse(requestId, error) {
1312
+ if (error instanceof WorkspaceInspectError) {
1313
+ return {
1314
+ type: 'workspace.inspect.response',
1315
+ requestId,
1316
+ error: error.message,
1317
+ errorCode: error.code,
1318
+ };
1319
+ }
1320
+ return {
1321
+ type: 'workspace.inspect.response',
1322
+ requestId,
1323
+ error: String(error?.message ?? error),
1324
+ errorCode: 'io_error',
1325
+ };
1326
+ }
1327
+ function terminalErrorResponse(type, requestId, error, extra) {
1328
+ if (error instanceof TerminalManagerError) {
1329
+ return {
1330
+ type,
1331
+ requestId,
1332
+ ...extra,
1333
+ error: error.message,
1334
+ errorCode: error.code,
1335
+ };
1336
+ }
1337
+ return {
1338
+ type,
1339
+ requestId,
1340
+ ...extra,
1341
+ error: String(error?.message ?? error),
1342
+ errorCode: 'io_error',
1343
+ };
1344
+ }
1345
+ function workspaceGitStatusErrorResponse(requestId, error) {
1346
+ if (error instanceof WorkspaceGitError) {
1347
+ return {
1348
+ type: 'workspace.git_status.response',
1349
+ requestId,
1350
+ error: error.message,
1351
+ errorCode: error.code,
1352
+ };
1353
+ }
1354
+ return {
1355
+ type: 'workspace.git_status.response',
1356
+ requestId,
1357
+ error: String(error?.message ?? error),
1358
+ errorCode: 'io_error',
1359
+ };
1360
+ }
1361
+ function workspaceGitDiffErrorResponse(requestId, error) {
1362
+ if (error instanceof WorkspaceGitError) {
1363
+ return {
1364
+ type: 'workspace.git_diff.response',
1365
+ requestId,
1366
+ error: error.message,
1367
+ errorCode: error.code,
1368
+ };
1369
+ }
1370
+ return {
1371
+ type: 'workspace.git_diff.response',
1372
+ requestId,
1373
+ error: String(error?.message ?? error),
1374
+ errorCode: 'io_error',
1375
+ };
1376
+ }
1377
+ function workspaceGitActionErrorResponse(requestId, error) {
1378
+ if (error instanceof WorkspaceGitError) {
1379
+ return {
1380
+ type: 'workspace.git_action.response',
1381
+ requestId,
1382
+ error: error.message,
1383
+ errorCode: error.code,
1384
+ };
1385
+ }
1386
+ return {
1387
+ type: 'workspace.git_action.response',
1388
+ requestId,
1389
+ error: String(error?.message ?? error),
1390
+ errorCode: 'io_error',
1391
+ };
1392
+ }
1393
+ function claudeControlErrorResponse(type, requestId, error) {
1394
+ return {
1395
+ type,
1396
+ requestId,
1397
+ error: String(error?.message ?? error),
1398
+ };
1399
+ }
1400
+ function codexAppServerErrorResponse(type, requestId, error) {
1401
+ return {
1402
+ type,
1403
+ requestId,
1404
+ error: String(error?.message ?? error),
1405
+ };
1406
+ }
1407
+ function workspaceResetErrorResponse(requestId, workspaceRoot, error) {
1408
+ if (error instanceof WorkspaceFsError) {
1409
+ return {
1410
+ type: 'workspace.reset.response',
1411
+ requestId,
1412
+ workspaceRoot,
1413
+ error: error.message,
1414
+ errorCode: error.code,
1415
+ };
1416
+ }
1417
+ return {
1418
+ type: 'workspace.reset.response',
1419
+ requestId,
1420
+ workspaceRoot,
1421
+ error: String(error?.message ?? error),
1422
+ errorCode: 'io_error',
1423
+ };
1424
+ }
1425
+ function workspaceWriteErrorResponse(requestId, relativePath, error) {
1426
+ if (error instanceof WorkspaceFsError) {
1427
+ return {
1428
+ type: 'workspace.write.response',
1429
+ requestId,
1430
+ relativePath,
1431
+ error: error.message,
1432
+ errorCode: error.code,
1433
+ };
1434
+ }
1435
+ return {
1436
+ type: 'workspace.write.response',
1437
+ requestId,
1438
+ relativePath,
1439
+ error: String(error?.message ?? error),
1440
+ errorCode: 'io_error',
1441
+ };
1442
+ }
1443
+ function workspaceArchiveErrorResponse(requestId, relativePath, error) {
1444
+ if (error instanceof WorkspaceFsError) {
1445
+ return {
1446
+ type: 'workspace.archive.response',
1447
+ requestId,
1448
+ relativePath,
1449
+ error: error.message,
1450
+ errorCode: error.code,
1451
+ };
1452
+ }
1453
+ return {
1454
+ type: 'workspace.archive.response',
1455
+ requestId,
1456
+ relativePath,
1457
+ error: String(error?.message ?? error),
1458
+ errorCode: 'io_error',
1459
+ };
1460
+ }
1461
+ function workspaceExtractErrorResponse(requestId, relativePath, error) {
1462
+ if (error instanceof WorkspaceFsError) {
1463
+ return {
1464
+ type: 'workspace.extract.response',
1465
+ requestId,
1466
+ relativePath,
1467
+ error: error.message,
1468
+ errorCode: error.code,
1469
+ };
1470
+ }
1471
+ return {
1472
+ type: 'workspace.extract.response',
1473
+ requestId,
1474
+ relativePath,
1475
+ error: String(error?.message ?? error),
1476
+ errorCode: 'io_error',
1477
+ };
1478
+ }
1479
+ function workspaceDeleteFileErrorResponse(requestId, relativePath, error) {
1480
+ if (error instanceof WorkspaceFsError) {
1481
+ return {
1482
+ type: 'workspace.delete_file.response',
1483
+ requestId,
1484
+ relativePath,
1485
+ error: error.message,
1486
+ errorCode: error.code,
1487
+ };
1488
+ }
1489
+ return {
1490
+ type: 'workspace.delete_file.response',
1491
+ requestId,
1492
+ relativePath,
1493
+ error: String(error?.message ?? error),
1494
+ errorCode: 'io_error',
1495
+ };
1496
+ }
1497
+ function workspaceDeletePathErrorResponse(requestId, relativePath, error) {
1498
+ if (error instanceof WorkspaceFsError) {
1499
+ return {
1500
+ type: 'workspace.delete_path.response',
1501
+ requestId,
1502
+ relativePath,
1503
+ error: error.message,
1504
+ errorCode: error.code,
1505
+ };
1506
+ }
1507
+ return {
1508
+ type: 'workspace.delete_path.response',
1509
+ requestId,
1510
+ relativePath,
1511
+ error: String(error?.message ?? error),
1512
+ errorCode: 'io_error',
1513
+ };
1514
+ }
1515
+ function libraryMirrorErrorResponse(type, requestId, error) {
1516
+ return {
1517
+ type,
1518
+ requestId,
1519
+ error: String(error?.message ?? error),
1520
+ };
1521
+ }
1522
+ function libraryAuthorityDeleteErrorResponse(requestId, error) {
1523
+ if (error instanceof WorkspaceFsError) {
1524
+ return {
1525
+ type: 'library.authority.delete.response',
1526
+ requestId,
1527
+ error: error.message,
1528
+ errorCode: error.code,
1529
+ };
1530
+ }
1531
+ return {
1532
+ type: 'library.authority.delete.response',
1533
+ requestId,
1534
+ error: String(error?.message ?? error),
1535
+ errorCode: 'io_error',
1536
+ };
1537
+ }
1538
+ function libraryAuthorityRenameErrorResponse(requestId, error) {
1539
+ if (error instanceof WorkspaceFsError) {
1540
+ return {
1541
+ type: 'library.authority.rename.response',
1542
+ requestId,
1543
+ error: error.message,
1544
+ errorCode: error.code,
1545
+ };
1546
+ }
1547
+ return {
1548
+ type: 'library.authority.rename.response',
1549
+ requestId,
1550
+ error: String(error?.message ?? error),
1551
+ errorCode: 'io_error',
1552
+ };
1553
+ }
1554
+ function skillListErrorResponse(requestId, skillRoots, skillPath, error) {
1555
+ if (error instanceof WorkspaceFsError) {
1556
+ return {
1557
+ type: 'skills.list.response',
1558
+ requestId,
1559
+ roots: skillRoots,
1560
+ path: skillPath,
1561
+ error: error.message,
1562
+ errorCode: error.code,
1563
+ };
1564
+ }
1565
+ return {
1566
+ type: 'skills.list.response',
1567
+ requestId,
1568
+ roots: skillRoots,
1569
+ path: skillPath,
1570
+ error: String(error?.message ?? error),
1571
+ errorCode: 'io_error',
1572
+ };
1573
+ }
1574
+ function skillReadErrorResponse(requestId, skillPath, error) {
1575
+ if (error instanceof WorkspaceFsError) {
1576
+ return {
1577
+ type: 'skills.read.response',
1578
+ requestId,
1579
+ path: skillPath,
1580
+ error: error.message,
1581
+ errorCode: error.code,
1582
+ };
1583
+ }
1584
+ return {
1585
+ type: 'skills.read.response',
1586
+ requestId,
1587
+ path: skillPath,
1588
+ error: String(error?.message ?? error),
1589
+ errorCode: 'io_error',
1590
+ };
1591
+ }
1592
+ function codexTranscriptListErrorResponse(requestId, error) {
1593
+ if (error instanceof CodexTranscriptFsError) {
1594
+ return {
1595
+ type: 'codex.transcript.list.response',
1596
+ requestId,
1597
+ error: error.message,
1598
+ errorCode: error.code,
1599
+ };
1600
+ }
1601
+ return {
1602
+ type: 'codex.transcript.list.response',
1603
+ requestId,
1604
+ error: String(error?.message ?? error),
1605
+ errorCode: 'io_error',
1606
+ };
1607
+ }
1608
+ function codexTranscriptReadErrorResponse(requestId, transcriptPath, error) {
1609
+ if (error instanceof CodexTranscriptFsError) {
1610
+ return {
1611
+ type: 'codex.transcript.read.response',
1612
+ requestId,
1613
+ path: transcriptPath,
1614
+ error: error.message,
1615
+ errorCode: error.code,
1616
+ };
1617
+ }
1618
+ return {
1619
+ type: 'codex.transcript.read.response',
1620
+ requestId,
1621
+ path: transcriptPath,
1622
+ error: String(error?.message ?? error),
1623
+ errorCode: 'io_error',
1624
+ };
1625
+ }
1626
+ function claudeTranscriptListErrorResponse(requestId, error) {
1627
+ if (error instanceof ClaudeTranscriptFsError) {
1628
+ return {
1629
+ type: 'claude.transcript.list.response',
1630
+ requestId,
1631
+ error: error.message,
1632
+ errorCode: error.code,
1633
+ };
1634
+ }
1635
+ return {
1636
+ type: 'claude.transcript.list.response',
1637
+ requestId,
1638
+ error: String(error?.message ?? error),
1639
+ errorCode: 'io_error',
1640
+ };
1641
+ }
1642
+ function claudeTranscriptReadErrorResponse(requestId, transcriptPath, error) {
1643
+ if (error instanceof ClaudeTranscriptFsError) {
1644
+ return {
1645
+ type: 'claude.transcript.read.response',
1646
+ requestId,
1647
+ path: transcriptPath,
1648
+ error: error.message,
1649
+ errorCode: error.code,
1650
+ };
1651
+ }
1652
+ return {
1653
+ type: 'claude.transcript.read.response',
1654
+ requestId,
1655
+ path: transcriptPath,
1656
+ error: String(error?.message ?? error),
1657
+ errorCode: 'io_error',
1658
+ };
1659
+ }