@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
@@ -0,0 +1,463 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { Executor } from './executor.js';
5
+ const NATIVE_OUTPUT_DIR = 'native-output';
6
+ function hashString(input) {
7
+ return createHash('sha256').update(input).digest('hex').slice(0, 32);
8
+ }
9
+ function deriveAgentType(workerModel, modelMode) {
10
+ const model = (workerModel ?? '').trim().toLowerCase();
11
+ if (model.startsWith('claude'))
12
+ return 'claude_sdk';
13
+ if (model.startsWith('codex_acp'))
14
+ return 'codex_acp';
15
+ if (model.startsWith('codex'))
16
+ return 'codex_app_server';
17
+ if (modelMode === 'droid_default')
18
+ return 'claude_sdk';
19
+ return 'codex_app_server';
20
+ }
21
+ function buildAgentPrompt(context) {
22
+ const sections = [];
23
+ if (context.title) {
24
+ sections.push(`# ${context.title}`);
25
+ }
26
+ sections.push(`Feature ID: ${context.featureId}`, `Milestone: ${context.milestone}`, '', context.featureDescription, '', context.prompt);
27
+ return sections.join('\n');
28
+ }
29
+ function buildSystemPrompt() {
30
+ return [
31
+ 'You are a senior software engineering agent working inside a mission-driven native runtime.',
32
+ 'Your job is to implement the requested feature, write or modify files in the workspace, run relevant tests or commands, and produce a concise handoff summary.',
33
+ 'Follow the project conventions, do not add unnecessary dependencies, and prefer small, focused changes.',
34
+ 'Use bash for shell commands, read_file to inspect existing code, and write_file or edit tools to make changes.',
35
+ 'When you finish, ensure the workspace is in a consistent state and summarize what you implemented.',
36
+ ].join('\n');
37
+ }
38
+ function redactUrlCredentials(value) {
39
+ if (!value)
40
+ return '';
41
+ try {
42
+ const url = new URL(value);
43
+ url.username = '';
44
+ url.password = '';
45
+ return url.toString().replace(/\/$/, '');
46
+ }
47
+ catch {
48
+ return value;
49
+ }
50
+ }
51
+ function buildPlaceholderRuntimeConfig() {
52
+ return {
53
+ model: 'unknown',
54
+ modelMode: 'unknown',
55
+ orchestratorModel: null,
56
+ workerModel: null,
57
+ validatorModel: null,
58
+ providerBaseUrl: 'unknown',
59
+ apiKeyConfigured: false,
60
+ workerTimeoutMs: 0,
61
+ workerGracePeriodMs: 0,
62
+ platform: process.platform,
63
+ nodeVersion: process.version,
64
+ };
65
+ }
66
+ export class LocalMissionSink {
67
+ context;
68
+ send;
69
+ requestId;
70
+ runId;
71
+ events = [];
72
+ toolCallsById = new Map();
73
+ runEndResult = null;
74
+ constructor(context, send, requestId, runId) {
75
+ this.context = context;
76
+ this.send = send;
77
+ this.requestId = requestId;
78
+ this.runId = runId;
79
+ }
80
+ onContentDelta(text) {
81
+ this.forwardEvent({ type: 'content.delta', text });
82
+ }
83
+ onActivityDelta(text) {
84
+ this.forwardEvent({ type: 'activity.delta', text });
85
+ }
86
+ onThinkingDelta(text) {
87
+ this.forwardEvent({ type: 'thinking.delta', text });
88
+ }
89
+ onToolCall(event) {
90
+ this.events.push(event);
91
+ this.toolCallsById.set(event.toolCallId, event);
92
+ this.sendMissionEvent('mission_tool_call', {
93
+ toolName: event.name,
94
+ toolCallId: event.toolCallId,
95
+ input: event.input,
96
+ status: event.status,
97
+ metadata: event.metadata,
98
+ });
99
+ this.sendTranscriptEvent({
100
+ role: 'assistant',
101
+ kind: 'tool_call',
102
+ toolName: event.name,
103
+ toolCallId: event.toolCallId,
104
+ content: JSON.stringify(event.input ?? {}),
105
+ });
106
+ }
107
+ onToolResult(event) {
108
+ this.events.push(event);
109
+ const call = this.toolCallsById.get(event.toolCallId);
110
+ this.sendMissionEvent('mission_tool_result', {
111
+ toolCallId: event.toolCallId,
112
+ output: event.output,
113
+ detail: event.detail,
114
+ error: event.error,
115
+ status: event.status,
116
+ metadata: event.metadata,
117
+ });
118
+ this.sendTranscriptEvent({
119
+ role: 'tool_result',
120
+ kind: 'tool_result',
121
+ toolName: call?.name,
122
+ toolCallId: event.toolCallId,
123
+ content: typeof event.output === 'string' ? event.output : '',
124
+ });
125
+ }
126
+ onRunEnd(result) {
127
+ this.runEndResult = result;
128
+ }
129
+ getRunEndResult() {
130
+ return this.runEndResult;
131
+ }
132
+ assembleHandoff() {
133
+ const { context, runId, events } = this;
134
+ const whatWasImplemented = [];
135
+ const commandsRun = [];
136
+ let commitId;
137
+ const toolCallsById = new Map();
138
+ for (const event of events) {
139
+ if (event.type === 'tool.call') {
140
+ const entry = toolCallsById.get(event.toolCallId) ?? {};
141
+ entry.call = event;
142
+ toolCallsById.set(event.toolCallId, entry);
143
+ }
144
+ else if (event.type === 'tool.result') {
145
+ const entry = toolCallsById.get(event.toolCallId) ?? {};
146
+ entry.result = event;
147
+ toolCallsById.set(event.toolCallId, entry);
148
+ }
149
+ }
150
+ for (const { call, result } of toolCallsById.values()) {
151
+ if (!call)
152
+ continue;
153
+ const name = call.name.toLowerCase();
154
+ const input = call.input;
155
+ if (name === 'bash' || name.includes('bash')) {
156
+ const command = typeof input === 'string' ? input : String((input?.command ?? input ?? ''));
157
+ const exitCode = result?.status === 'failed' ? 1 : result?.error ? 1 : 0;
158
+ const observation = typeof result?.output === 'string' ? result.output : '';
159
+ if (command) {
160
+ commandsRun.push({ command, exitCode, observation });
161
+ }
162
+ const commitMatch = observation.match(/\[\S+\s+([0-9a-f]{7,40})\]/);
163
+ if (commitMatch) {
164
+ commitId = commitMatch[1];
165
+ }
166
+ }
167
+ if (name === 'write_file' || name.includes('write') || name.includes('edit')) {
168
+ const filePath = typeof input === 'string'
169
+ ? ''
170
+ : String((input?.path ?? input?.file_path ?? input?.relativePath ?? ''));
171
+ if (filePath) {
172
+ whatWasImplemented.push(`Modified file: ${filePath}`);
173
+ }
174
+ }
175
+ }
176
+ const stopReason = this.runEndResult?.stopReason ?? 'unknown';
177
+ const successState = deriveSuccessState(stopReason, this.runEndResult?.error);
178
+ return {
179
+ schemaVersion: '2.0.0',
180
+ salientSummary: `Agent run ${runId} completed with stop reason ${stopReason}.`,
181
+ whatWasImplemented: whatWasImplemented.length > 0 ? whatWasImplemented : ['No file modifications recorded.'],
182
+ whatWasLeftUndone: '',
183
+ verification: {
184
+ commandsRun,
185
+ interactiveChecks: [],
186
+ },
187
+ tests: {
188
+ added: [],
189
+ },
190
+ discoveredIssues: [],
191
+ commitId,
192
+ repoPath: context.workspaceRoot,
193
+ successState,
194
+ returnToOrchestrator: successState === 'failure',
195
+ workerSessionId: runId,
196
+ };
197
+ }
198
+ forwardEvent(event) {
199
+ this.events.push(event);
200
+ if (event.type === 'content.delta') {
201
+ this.sendMissionEvent('mission_content_delta', { text: event.text });
202
+ }
203
+ else if (event.type === 'activity.delta') {
204
+ this.sendMissionEvent('mission_activity_delta', { text: event.text });
205
+ }
206
+ else if (event.type === 'thinking.delta') {
207
+ this.sendMissionEvent('mission_thinking_delta', { text: event.text });
208
+ }
209
+ }
210
+ sendMissionEvent(eventType, payload) {
211
+ this.send({
212
+ type: 'mission.run.event',
213
+ requestId: this.requestId,
214
+ missionId: this.context.missionId,
215
+ runtimeProvider: 'bigbang_native',
216
+ eventType,
217
+ source: 'bigbang_native',
218
+ eventTime: Date.now(),
219
+ featureId: this.context.featureId,
220
+ payload,
221
+ });
222
+ }
223
+ sendTranscriptEvent(partial) {
224
+ const entry = {
225
+ workerSessionId: this.runId,
226
+ featureId: this.context.featureId,
227
+ milestone: this.context.milestone,
228
+ role: partial.role,
229
+ kind: partial.kind,
230
+ toolName: partial.toolName,
231
+ toolCallId: partial.toolCallId,
232
+ content: partial.content,
233
+ timestamp: Date.now(),
234
+ };
235
+ this.sendMissionEvent('mission_worker_transcript', entry);
236
+ }
237
+ }
238
+ function deriveSuccessState(stopReason, error) {
239
+ if (error)
240
+ return 'failure';
241
+ if (stopReason === 'completed' || stopReason === 'end_turn' || stopReason === 'stop')
242
+ return 'success';
243
+ if (stopReason === 'failed' || stopReason === 'cancelled' || stopReason === 'approval_denied') {
244
+ return 'failure';
245
+ }
246
+ return 'partial';
247
+ }
248
+ export class NativeMissionAgentExecutor {
249
+ executor;
250
+ sinks = new Map();
251
+ completions = new Map();
252
+ send;
253
+ constructor(params) {
254
+ this.send = params.send;
255
+ this.executor = new Executor({
256
+ db: params.db,
257
+ config: params.config,
258
+ send: (msg) => this.handleExecutorMessage(msg),
259
+ });
260
+ }
261
+ async dispatch(msg, sink) {
262
+ this.sinks.set(msg.runId, sink);
263
+ return new Promise((resolve, reject) => {
264
+ this.completions.set(msg.runId, { resolve, reject });
265
+ this.executor
266
+ .dispatch(msg, { persist: false })
267
+ .then(() => {
268
+ this.completions.delete(msg.runId);
269
+ resolve();
270
+ })
271
+ .catch((error) => {
272
+ this.completions.delete(msg.runId);
273
+ reject(error instanceof Error ? error : new Error(String(error)));
274
+ });
275
+ }).finally(() => {
276
+ this.sinks.delete(msg.runId);
277
+ });
278
+ }
279
+ async cancelRun(runId) {
280
+ return this.executor.cancelRun(runId);
281
+ }
282
+ handleExecutorMessage(msg) {
283
+ if (msg.type === 'run.event') {
284
+ const sink = this.sinks.get(msg.runId);
285
+ if (sink) {
286
+ this.forwardServerEvent(sink, msg.event);
287
+ }
288
+ return;
289
+ }
290
+ if (msg.type === 'run.end') {
291
+ const sink = this.sinks.get(msg.runId);
292
+ if (sink) {
293
+ sink.onRunEnd({ stopReason: msg.stopReason, error: msg.error });
294
+ }
295
+ const completion = this.completions.get(msg.runId);
296
+ if (completion) {
297
+ this.completions.delete(msg.runId);
298
+ if (msg.error) {
299
+ completion.reject(new Error(msg.error));
300
+ }
301
+ else {
302
+ completion.resolve();
303
+ }
304
+ }
305
+ return;
306
+ }
307
+ if (msg.type === 'run.accepted') {
308
+ // NativeMissionAgentDispatch sends mission.run.accepted itself.
309
+ return;
310
+ }
311
+ this.send(msg);
312
+ }
313
+ forwardServerEvent(sink, event) {
314
+ if (event.type === 'content.delta') {
315
+ sink.onContentDelta(event.text);
316
+ }
317
+ else if (event.type === 'activity.delta') {
318
+ sink.onActivityDelta(event.text);
319
+ }
320
+ else if (event.type === 'thinking.delta') {
321
+ sink.onThinkingDelta(event.text);
322
+ }
323
+ else if (event.type === 'tool.call') {
324
+ sink.onToolCall(event);
325
+ }
326
+ else if (event.type === 'tool.result') {
327
+ sink.onToolResult(event);
328
+ }
329
+ }
330
+ }
331
+ export class NativeMissionAgentDispatch {
332
+ context;
333
+ send;
334
+ executor;
335
+ requestId;
336
+ runId;
337
+ cancelRequested = false;
338
+ constructor(context, send, executor, requestId) {
339
+ this.context = context;
340
+ this.send = send;
341
+ this.executor = executor;
342
+ this.requestId = requestId;
343
+ this.runId = randomUUID();
344
+ }
345
+ get pid() {
346
+ return null;
347
+ }
348
+ async dispatch() {
349
+ const { context, send, runId, requestId } = this;
350
+ const outputDir = path.join(context.missionDir, NATIVE_OUTPUT_DIR);
351
+ mkdirSync(outputDir, { recursive: true });
352
+ const conversationId = hashString(`${context.missionDir}:${context.featureId}`);
353
+ const sessionKey = hashString(context.featureId);
354
+ const hostKey = hashString(context.workspaceRoot);
355
+ send({
356
+ type: 'mission.run.accepted',
357
+ requestId,
358
+ missionId: context.missionId,
359
+ missionDir: context.missionDir,
360
+ runtimeProvider: 'bigbang_native',
361
+ featureId: context.featureId,
362
+ pid: null,
363
+ });
364
+ const runDispatchMsg = {
365
+ type: 'run.dispatch',
366
+ runId,
367
+ conversationId,
368
+ agentType: deriveAgentType(context.workerModel, context.modelMode),
369
+ workspacePath: context.workspaceRoot,
370
+ prompt: buildAgentPrompt(context),
371
+ systemPromptText: buildSystemPrompt(),
372
+ sessionKey,
373
+ hostKey,
374
+ dispatchMode: 'cold_start',
375
+ };
376
+ const sink = new LocalMissionSink(context, send, requestId, runId);
377
+ let runError;
378
+ try {
379
+ await this.executor.dispatch(runDispatchMsg, sink);
380
+ }
381
+ catch (error) {
382
+ runError = String(error?.message ?? error);
383
+ if (!sink.getRunEndResult()) {
384
+ sink.onRunEnd({ stopReason: 'failed', error: runError });
385
+ }
386
+ }
387
+ const runEnd = sink.getRunEndResult() ?? { stopReason: 'failed', error: runError ?? 'Run ended without result' };
388
+ const handoff = sink.assembleHandoff();
389
+ if (this.cancelRequested || runEnd.stopReason === 'cancelled') {
390
+ handoff.successState = 'failure';
391
+ handoff.returnToOrchestrator = true;
392
+ if (!runError)
393
+ runError = 'Worker was cancelled';
394
+ }
395
+ const failed = handoff.successState === 'failure';
396
+ const exitCode = failed ? 1 : 0;
397
+ const status = failed ? 'failed' : 'completed';
398
+ const output = {
399
+ schemaVersion: '2.0.0',
400
+ featureId: context.featureId,
401
+ status,
402
+ implementedFiles: handoff.whatWasImplemented.filter((item) => item.includes('Modified file:')),
403
+ fileOperations: handoff.whatWasImplemented.map((item) => ({
404
+ path: item.replace('Modified file: ', ''),
405
+ relativePath: item.replace('Modified file: ', ''),
406
+ operation: 'write',
407
+ timestamp: Date.now(),
408
+ })),
409
+ runtimeConfig: buildPlaceholderRuntimeConfig(),
410
+ handoff,
411
+ exitCode,
412
+ error: runError,
413
+ };
414
+ const outputPath = path.join(outputDir, `${context.featureId}.json`);
415
+ writeFileSync(outputPath, `${JSON.stringify(output, null, 2)}\n`, 'utf8');
416
+ send({
417
+ type: 'mission.run.end',
418
+ requestId,
419
+ missionId: context.missionId,
420
+ missionDir: context.missionDir,
421
+ runtimeProvider: 'bigbang_native',
422
+ featureId: context.featureId,
423
+ exitCode,
424
+ reason: runEnd.stopReason ?? (failed ? 'failed' : undefined),
425
+ output: output,
426
+ handoff: handoff,
427
+ ...(runError ? { error: runError } : {}),
428
+ });
429
+ return output;
430
+ }
431
+ cancel() {
432
+ this.cancelRequested = true;
433
+ void this.executor.cancelRun?.(this.runId).catch(() => undefined);
434
+ }
435
+ }
436
+ export function buildContextFromRequest(msg) {
437
+ if (!msg.featureDescription) {
438
+ throw new Error('Native mission run request is missing featureDescription.');
439
+ }
440
+ if (!msg.milestone) {
441
+ throw new Error('Native mission run request is missing milestone.');
442
+ }
443
+ return {
444
+ featureId: msg.featureId,
445
+ featureDescription: msg.featureDescription,
446
+ milestone: msg.milestone,
447
+ expectedBehavior: msg.expectedBehavior,
448
+ title: msg.title,
449
+ prompt: msg.prompt,
450
+ missionId: msg.missionId,
451
+ workspaceRoot: msg.workspaceRoot,
452
+ missionDir: msg.missionDir,
453
+ modelMode: msg.modelMode,
454
+ orchestratorModel: msg.orchestratorModel,
455
+ workerModel: msg.workerModel,
456
+ validatorModel: msg.validatorModel,
457
+ priorHandoffs: msg.priorHandoffs,
458
+ preconditions: msg.preconditions,
459
+ validationContract: msg.validationContract,
460
+ workspaceFiles: msg.workspaceFiles,
461
+ useAgentDispatch: msg.useAgentDispatch,
462
+ };
463
+ }