@loom-framework/backend 0.1.0-alpha.1 → 0.1.0-alpha.10

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 (42) hide show
  1. package/dist/ai/button-resolver.d.ts +17 -0
  2. package/dist/ai/button-resolver.d.ts.map +1 -0
  3. package/dist/ai/button-resolver.js +40 -0
  4. package/dist/ai/button-resolver.js.map +1 -0
  5. package/dist/ai/engine.d.ts +14 -0
  6. package/dist/ai/engine.d.ts.map +1 -1
  7. package/dist/ai/engine.js +74 -23
  8. package/dist/ai/engine.js.map +1 -1
  9. package/dist/ai/index.d.ts +3 -1
  10. package/dist/ai/index.d.ts.map +1 -1
  11. package/dist/ai/index.js +1 -0
  12. package/dist/ai/index.js.map +1 -1
  13. package/dist/ai/output-parser.d.ts +14 -7
  14. package/dist/ai/output-parser.d.ts.map +1 -1
  15. package/dist/ai/output-parser.js +126 -117
  16. package/dist/ai/output-parser.js.map +1 -1
  17. package/dist/ai/session-manager.d.ts +11 -2
  18. package/dist/ai/session-manager.d.ts.map +1 -1
  19. package/dist/ai/session-manager.js +88 -12
  20. package/dist/ai/session-manager.js.map +1 -1
  21. package/dist/bin.js +0 -0
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/routes/chat.d.ts +31 -0
  27. package/dist/routes/chat.d.ts.map +1 -0
  28. package/dist/routes/chat.js +216 -0
  29. package/dist/routes/chat.js.map +1 -0
  30. package/dist/routes/index.d.ts +3 -0
  31. package/dist/routes/index.d.ts.map +1 -1
  32. package/dist/routes/index.js +2 -0
  33. package/dist/routes/index.js.map +1 -1
  34. package/dist/routes/upload.d.ts +24 -0
  35. package/dist/routes/upload.d.ts.map +1 -0
  36. package/dist/routes/upload.js +67 -0
  37. package/dist/routes/upload.js.map +1 -0
  38. package/package.json +12 -13
  39. package/dist/websocket/index.d.ts +0 -32
  40. package/dist/websocket/index.d.ts.map +0 -1
  41. package/dist/websocket/index.js +0 -320
  42. package/dist/websocket/index.js.map +0 -1
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Button Resolver - Resolve AI button prompts from configuration
3
+ *
4
+ * Shared between the SSE chat route and other modules.
5
+ */
6
+ import type { LoomConfig } from '@loom-framework/core';
7
+ /**
8
+ * Resolve prompt from AI button configuration.
9
+ * - Static buttons: return prompt directly
10
+ * - Template buttons: substitute {{var}} with context values
11
+ * Returns null if button not found or context is incomplete.
12
+ */
13
+ export declare function resolveButtonPrompt(buttonId: string, context: Record<string, string> | undefined, config: LoomConfig): {
14
+ prompt: string;
15
+ error?: string;
16
+ } | null;
17
+ //# sourceMappingURL=button-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-resolver.d.ts","sourceRoot":"","sources":["../../src/ai/button-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC3C,MAAM,EAAE,UAAU,GACjB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiC3C"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Button Resolver - Resolve AI button prompts from configuration
3
+ *
4
+ * Shared between the SSE chat route and other modules.
5
+ */
6
+ /**
7
+ * Resolve prompt from AI button configuration.
8
+ * - Static buttons: return prompt directly
9
+ * - Template buttons: substitute {{var}} with context values
10
+ * Returns null if button not found or context is incomplete.
11
+ */
12
+ export function resolveButtonPrompt(buttonId, context, config) {
13
+ const button = config.aiButtons?.find((b) => b.id === buttonId);
14
+ if (!button) {
15
+ return null;
16
+ }
17
+ // Static prompt
18
+ if (button.prompt) {
19
+ return { prompt: button.prompt };
20
+ }
21
+ // Template prompt
22
+ if (button.promptTemplate) {
23
+ const missing = (button.contextVars || []).filter((v) => !context || context[v] === undefined);
24
+ if (missing.length > 0) {
25
+ return {
26
+ prompt: '',
27
+ error: `Missing context variables: ${missing.join(', ')}. Required: ${button.contextVars.join(', ')}`,
28
+ };
29
+ }
30
+ let resolved = button.promptTemplate;
31
+ if (context) {
32
+ for (const [key, value] of Object.entries(context)) {
33
+ resolved = resolved.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
34
+ }
35
+ }
36
+ return { prompt: resolved };
37
+ }
38
+ return null;
39
+ }
40
+ //# sourceMappingURL=button-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-resolver.js","sourceRoot":"","sources":["../../src/ai/button-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,OAA2C,EAC3C,MAAkB;IAElB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gBAAgB;IAChB,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACnC,CAAC;IAED,kBAAkB;IAClB,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,MAAM,CAC/C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,SAAS,CAC5C,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,8BAA8B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,WAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aACvG,CAAC;QACJ,CAAC;QAED,IAAI,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC;QACrC,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,SAAS,GAAG,QAAQ,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -5,9 +5,21 @@
5
5
  * with support for --resume, --model, and MCP config.
6
6
  */
7
7
  import type { AIEngine, AICallOptions, AIChunk, ClaudeCodeConfig } from '@loom-framework/core';
8
+ /** Log levels for engine diagnostics */
9
+ export type EngineLogLevel = 'COMMAND' | 'PROMPT' | 'STDOUT_CHUNK' | 'RESPONSE_PARSED' | 'STDERR' | 'PROCESS_EXIT';
10
+ export interface EngineLogEntry {
11
+ level: EngineLogLevel;
12
+ sessionId: string;
13
+ timestamp: string;
14
+ message: string;
15
+ data?: unknown;
16
+ }
17
+ export type EngineLogger = (entry: EngineLogEntry) => void;
8
18
  export interface ClaudeCodeEngineOptions {
9
19
  config: ClaudeCodeConfig;
10
20
  projectRoot: string;
21
+ /** Optional diagnostic logger for engine operations */
22
+ logger?: EngineLogger;
11
23
  }
12
24
  /**
13
25
  * ClaudeCodeEngine - Spawns Claude Code in headless mode
@@ -20,7 +32,9 @@ export declare class ClaudeCodeEngine implements AIEngine {
20
32
  private config;
21
33
  private projectRoot;
22
34
  private activeProcesses;
35
+ private logger?;
23
36
  constructor(options: ClaudeCodeEngineOptions);
37
+ private log;
24
38
  /**
25
39
  * Call Claude Code with a prompt and stream back AIChunk objects
26
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/ai/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAM/F,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,QAAQ;IAC/C,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAwC;gBAEnD,OAAO,EAAE,uBAAuB;IAK5C;;OAEG;IACI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,cAAc,CAAC,OAAO,CAAC;IAgG5E;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAUvC;;OAEG;IACH,OAAO,IAAI,IAAI;CAQhB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/ai/engine.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAM/F,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,cAAc,GAAG,iBAAiB,GAAG,QAAQ,GAAG,cAAc,CAAC;AAEnH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,QAAQ;IAC/C,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,MAAM,CAAC,CAAe;gBAElB,OAAO,EAAE,uBAAuB;IAM5C,OAAO,CAAC,GAAG;IAUX;;OAEG;IACI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,cAAc,CAAC,OAAO,CAAC;IAkI5E;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAUvC;;OAEG;IACH,OAAO,IAAI,IAAI;CAQhB"}
package/dist/ai/engine.js CHANGED
@@ -20,9 +20,20 @@ export class ClaudeCodeEngine {
20
20
  config;
21
21
  projectRoot;
22
22
  activeProcesses = new Map();
23
+ logger;
23
24
  constructor(options) {
24
25
  this.config = options.config;
25
26
  this.projectRoot = options.projectRoot;
27
+ this.logger = options.logger;
28
+ }
29
+ log(level, sessionId, message, data) {
30
+ this.logger?.({
31
+ level,
32
+ sessionId,
33
+ timestamp: new Date().toISOString(),
34
+ message,
35
+ data,
36
+ });
26
37
  }
27
38
  /**
28
39
  * Call Claude Code with a prompt and stream back AIChunk objects
@@ -32,10 +43,15 @@ export class ClaudeCodeEngine {
32
43
  const timeout = options.timeout ?? this.config.timeout ?? DEFAULT_PROCESS_TIMEOUT_MS;
33
44
  const pluginRoot = this.config.pluginRoot || path.join(this.projectRoot, '.claude');
34
45
  const mcpConfigPath = path.join(this.projectRoot, '.loom', 'mcp.json');
46
+ // Build prompt with file references if provided
47
+ const fullPrompt = buildPromptWithFiles(prompt, options.files);
35
48
  const args = [
36
49
  '-p',
37
- '--output-format', 'json',
50
+ '--output-format', 'stream-json',
51
+ '--verbose',
52
+ '--include-partial-messages',
38
53
  '--mcp-config', mcpConfigPath,
54
+ '--strict-mcp-config',
39
55
  ];
40
56
  if (this.config.skipPermissions !== false) {
41
57
  args.push('--dangerously-skip-permissions');
@@ -48,6 +64,11 @@ export class ClaudeCodeEngine {
48
64
  }
49
65
  // Read prompt from stdin to avoid ARG_MAX
50
66
  args.push('-');
67
+ this.log('COMMAND', options.sessionId, `Spawning claude process`, { claudePath, args });
68
+ this.log('PROMPT', options.sessionId, `Prompt length: ${fullPrompt.length}`, {
69
+ promptPreview: fullPrompt.slice(0, 200),
70
+ filesCount: options.files?.length ?? 0,
71
+ });
51
72
  const childProcess = spawn(claudePath, args, {
52
73
  cwd: this.projectRoot,
53
74
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -58,12 +79,49 @@ export class ClaudeCodeEngine {
58
79
  });
59
80
  // Track active process for cleanup
60
81
  this.activeProcesses.set(options.sessionId, childProcess);
82
+ // Capture stderr for error reporting
83
+ let stderrOutput = '';
84
+ childProcess.stderr.on('data', (data) => {
85
+ const text = data.toString();
86
+ stderrOutput += text;
87
+ this.log('STDERR', options.sessionId, text.trim());
88
+ });
89
+ // Wait for process completion with timeout
90
+ let timeoutHandle;
91
+ const completionPromise = new Promise((resolve, reject) => {
92
+ timeoutHandle = setTimeout(() => {
93
+ childProcess.kill('SIGTERM');
94
+ setTimeout(() => childProcess.kill('SIGKILL'), 5_000);
95
+ reject(new Error(`Claude Code process timed out after ${timeout}ms`));
96
+ }, timeout);
97
+ childProcess.on('close', (code) => {
98
+ if (timeoutHandle)
99
+ clearTimeout(timeoutHandle);
100
+ this.activeProcesses.delete(options.sessionId);
101
+ if (code !== null && code !== 0) {
102
+ this.log('PROCESS_EXIT', options.sessionId, `Process exited with code ${code}`, { exitCode: code, stderr: stderrOutput });
103
+ }
104
+ resolve();
105
+ });
106
+ childProcess.on('error', (err) => {
107
+ if (timeoutHandle)
108
+ clearTimeout(timeoutHandle);
109
+ this.activeProcesses.delete(options.sessionId);
110
+ reject(err);
111
+ });
112
+ });
61
113
  // Send prompt via stdin then close
62
- childProcess.stdin.write(prompt);
114
+ childProcess.stdin.write(fullPrompt);
63
115
  childProcess.stdin.end();
64
116
  // Parse output stream
65
117
  try {
66
- for await (const chunk of parseClaudeOutput(childProcess.stdout)) {
118
+ const parserLogger = this.logger
119
+ ? (entry) => {
120
+ this.log('STDOUT_CHUNK', options.sessionId, entry.message, entry.data);
121
+ }
122
+ : undefined;
123
+ for await (const chunk of parseClaudeOutput(childProcess.stdout, parserLogger)) {
124
+ this.log('RESPONSE_PARSED', options.sessionId, `Chunk type: ${chunk.type}`, { chunkType: chunk.type });
67
125
  // For result-type content chunks that carry sessionId, split into two chunks
68
126
  if (chunk.type === 'content' && chunk.sessionId) {
69
127
  const { sessionId, usage, ...contentChunk } = chunk;
@@ -88,26 +146,7 @@ export class ClaudeCodeEngine {
88
146
  };
89
147
  }
90
148
  // Wait for process completion with timeout
91
- await new Promise((resolve, reject) => {
92
- const timeoutHandle = setTimeout(() => {
93
- childProcess.kill('SIGTERM');
94
- // Force kill after 5s
95
- setTimeout(() => {
96
- childProcess.kill('SIGKILL');
97
- }, 5_000);
98
- reject(new Error(`Claude Code process timed out after ${timeout}ms`));
99
- }, timeout);
100
- childProcess.on('close', () => {
101
- clearTimeout(timeoutHandle);
102
- this.activeProcesses.delete(options.sessionId);
103
- resolve();
104
- });
105
- childProcess.on('error', (err) => {
106
- clearTimeout(timeoutHandle);
107
- this.activeProcesses.delete(options.sessionId);
108
- reject(err);
109
- });
110
- });
149
+ await completionPromise;
111
150
  yield { type: 'done' };
112
151
  }
113
152
  /**
@@ -134,4 +173,16 @@ export class ClaudeCodeEngine {
134
173
  }
135
174
  }
136
175
  }
176
+ /**
177
+ * Build prompt with file references injected for Claude Code.
178
+ * Formats file paths as inline references so Claude Code can access them.
179
+ */
180
+ function buildPromptWithFiles(prompt, files) {
181
+ if (!files || files.length === 0)
182
+ return prompt;
183
+ const fileList = files
184
+ .map((f) => ` - ${f.name}: ${f.path}`)
185
+ .join('\n');
186
+ return `${prompt}\n\nAttached files:\n${fileList}`;
187
+ }
137
188
  //# sourceMappingURL=engine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/ai/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/ai/engine.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,10 @@
2
2
  * AI Module - Barrel Export
3
3
  */
4
4
  export { ClaudeCodeEngine } from './engine.js';
5
- export type { ClaudeCodeEngineOptions } from './engine.js';
5
+ export type { ClaudeCodeEngineOptions, EngineLogger, EngineLogLevel, EngineLogEntry } from './engine.js';
6
6
  export { SessionManager } from './session-manager.js';
7
7
  export type { SessionRecord, SessionMessage, SessionFileRef } from './session-manager.js';
8
8
  export { parseClaudeOutput } from './output-parser.js';
9
+ export type { ParserLogger, ParserLogEntry } from './output-parser.js';
10
+ export { resolveButtonPrompt } from './button-resolver.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzG,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/ai/index.js CHANGED
@@ -4,4 +4,5 @@
4
4
  export { ClaudeCodeEngine } from './engine.js';
5
5
  export { SessionManager } from './session-manager.js';
6
6
  export { parseClaudeOutput } from './output-parser.js';
7
+ export { resolveButtonPrompt } from './button-resolver.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,19 +1,26 @@
1
1
  /**
2
2
  * Claude Code Output Parser
3
3
  *
4
- * Parses `claude -p --output-format json` output stream into AIChunk objects.
5
- * Handles both newline-delimited JSON (streaming) and single JSON object (batch) modes.
4
+ * Parses `claude -p --output-format stream-json --verbose --include-partial-messages` output stream.
5
+ * Supports real-time streaming with thinking, tool calls, and text deltas.
6
6
  */
7
7
  import type { AIChunk } from '@loom-framework/core';
8
+ /** Parser log entry for diagnostics */
9
+ export interface ParserLogEntry {
10
+ type: 'CHUNK_RECEIVED' | 'LINE_PARSED' | 'PARSE_ERROR';
11
+ timestamp: string;
12
+ message: string;
13
+ data?: unknown;
14
+ }
15
+ export type ParserLogger = (entry: ParserLogEntry) => void;
8
16
  /**
9
17
  * Parse Claude Code stdout stream into typed AIChunk objects
10
18
  *
11
19
  * Strategy:
12
- * 1. Collect all stdout into a buffer
20
+ * 1. Process lines as they arrive (true streaming)
13
21
  * 2. Strip ANSI escape codes
14
- * 3. Try parsing as newline-delimited JSON lines first
15
- * 4. Fall back to parsing the entire buffer as a single JSON object
16
- * 5. Ultimate fallback: yield raw text as content chunk
22
+ * 3. Parse each line as JSON
23
+ * 4. Map to appropriate AIChunk types
17
24
  */
18
- export declare function parseClaudeOutput(stdout: NodeJS.ReadableStream): AsyncGenerator<AIChunk>;
25
+ export declare function parseClaudeOutput(stdout: NodeJS.ReadableStream, logger?: ParserLogger): AsyncGenerator<AIChunk>;
19
26
  //# sourceMappingURL=output-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../../src/ai/output-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAC;AA6B7D;;;;;;;;;GASG;AACH,wBAAuB,iBAAiB,CACtC,MAAM,EAAE,MAAM,CAAC,cAAc,GAC5B,cAAc,CAAC,OAAO,CAAC,CAiCzB"}
1
+ {"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../../src/ai/output-parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,sBAAsB,CAAC;AAE7D,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,gBAAgB,GAAG,aAAa,GAAG,aAAa,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAmF3D;;;;;;;;GAQG;AACH,wBAAuB,iBAAiB,CACtC,MAAM,EAAE,MAAM,CAAC,cAAc,EAC7B,MAAM,CAAC,EAAE,YAAY,GACpB,cAAc,CAAC,OAAO,CAAC,CA+BzB"}
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Claude Code Output Parser
3
3
  *
4
- * Parses `claude -p --output-format json` output stream into AIChunk objects.
5
- * Handles both newline-delimited JSON (streaming) and single JSON object (batch) modes.
4
+ * Parses `claude -p --output-format stream-json --verbose --include-partial-messages` output stream.
5
+ * Supports real-time streaming with thinking, tool calls, and text deltas.
6
6
  */
7
7
  /** ANSI escape code pattern */
8
8
  const ANSI_ESCAPE = /\x1b\[[0-9;]*m/g;
@@ -10,145 +10,154 @@ const ANSI_ESCAPE = /\x1b\[[0-9;]*m/g;
10
10
  * Parse Claude Code stdout stream into typed AIChunk objects
11
11
  *
12
12
  * Strategy:
13
- * 1. Collect all stdout into a buffer
13
+ * 1. Process lines as they arrive (true streaming)
14
14
  * 2. Strip ANSI escape codes
15
- * 3. Try parsing as newline-delimited JSON lines first
16
- * 4. Fall back to parsing the entire buffer as a single JSON object
17
- * 5. Ultimate fallback: yield raw text as content chunk
15
+ * 3. Parse each line as JSON
16
+ * 4. Map to appropriate AIChunk types
18
17
  */
19
- export async function* parseClaudeOutput(stdout) {
18
+ export async function* parseClaudeOutput(stdout, logger) {
20
19
  let buffer = '';
20
+ let currentToolUseId;
21
21
  for await (const chunk of stdout) {
22
22
  buffer += chunk.toString();
23
- }
24
- const cleanOutput = buffer.replace(ANSI_ESCAPE, '').trim();
25
- if (!cleanOutput)
26
- return;
27
- // Strategy 1: Try newline-delimited JSON
28
- const lines = cleanOutput.split('\n').map((l) => l.trim()).filter(Boolean);
29
- const parsedChunks = tryParseJsonLines(lines);
30
- if (parsedChunks.length > 0) {
31
- for (const chunk of parsedChunks) {
32
- yield chunk;
23
+ // Process complete lines
24
+ const lines = buffer.split('\n');
25
+ buffer = lines.pop() || '';
26
+ for (const line of lines) {
27
+ const trimmed = line.trim();
28
+ if (!trimmed)
29
+ continue;
30
+ const chunks = parseLine(trimmed, logger, currentToolUseId);
31
+ for (const c of chunks) {
32
+ if (c.type === 'tool_call' && c.toolUseId) {
33
+ currentToolUseId = c.toolUseId;
34
+ }
35
+ yield c;
36
+ }
33
37
  }
34
- return;
35
- }
36
- // Strategy 2: Try single JSON object
37
- const singleChunk = tryParseSingleJson(cleanOutput);
38
- if (singleChunk) {
39
- yield singleChunk;
40
- return;
41
38
  }
42
- // Strategy 3: Raw text fallback
43
- yield {
44
- type: 'content',
45
- content: cleanOutput,
46
- };
47
- }
48
- /**
49
- * Attempt to parse multiple JSON lines into AIChunk objects
50
- */
51
- function tryParseJsonLines(lines) {
52
- const chunks = [];
53
- let hasValidJson = false;
54
- for (const line of lines) {
55
- try {
56
- const data = JSON.parse(line);
57
- hasValidJson = true;
58
- const chunk = mapJsonToChunk(data);
59
- if (chunk)
60
- chunks.push(chunk);
61
- }
62
- catch {
63
- // Not valid JSON on this line, skip
64
- }
39
+ // Process any remaining buffer
40
+ if (buffer.trim()) {
41
+ const cleanOutput = buffer.replace(ANSI_ESCAPE, '').trim();
42
+ const chunks = parseLine(cleanOutput, logger, currentToolUseId);
43
+ for (const c of chunks)
44
+ yield c;
65
45
  }
66
- return hasValidJson ? chunks : [];
67
46
  }
68
- /**
69
- * Attempt to parse the entire output as a single JSON object
70
- */
71
- function tryParseSingleJson(output) {
47
+ function parseLine(line, logger, currentToolUseId) {
72
48
  try {
73
- const data = JSON.parse(output);
74
- return mapJsonToChunk(data);
49
+ const data = JSON.parse(line);
50
+ logger?.({ type: 'LINE_PARSED', timestamp: new Date().toISOString(), message: `Parsed ${data.type}` });
51
+ return mapToChunks(data, currentToolUseId);
75
52
  }
76
53
  catch {
77
- return null;
54
+ logger?.({ type: 'PARSE_ERROR', timestamp: new Date().toISOString(), message: 'Failed to parse line', data: line.slice(0, 200) });
55
+ return [];
78
56
  }
79
57
  }
80
- /**
81
- * Map a parsed JSON object to an AIChunk
82
- */
83
- function mapJsonToChunk(data) {
58
+ function mapToChunks(data, currentToolUseId) {
84
59
  switch (data.type) {
85
- case 'result': {
86
- if (data.is_error) {
87
- return {
88
- type: 'error',
89
- error: data.result || 'Unknown error from Claude Code',
90
- };
60
+ case 'system':
61
+ if (data.subtype === 'init' && data.session_id) {
62
+ return [{ type: 'session_info', sessionId: data.session_id }];
91
63
  }
92
- // For single-result mode, emit content + session_info together
93
- // The caller (engine) will handle splitting them
94
- if (data.result) {
95
- return {
96
- type: 'content',
97
- content: data.result,
98
- sessionId: data.session_id,
99
- usage: extractUsage(data),
100
- };
64
+ return [];
65
+ case 'stream_event':
66
+ return handleStreamEvent(data);
67
+ case 'assistant':
68
+ return handleAssistantMessage(data);
69
+ case 'user':
70
+ return handleUserMessage(data);
71
+ case 'result':
72
+ return handleResult(data);
73
+ default:
74
+ return [];
75
+ }
76
+ }
77
+ function handleStreamEvent(wrapper) {
78
+ const { event } = wrapper;
79
+ switch (event.type) {
80
+ case 'content_block_delta':
81
+ if (event.delta?.type === 'thinking_delta' && event.delta.thinking) {
82
+ return [{ type: 'thinking', content: event.delta.thinking }];
101
83
  }
102
- // No content but has session info
103
- return extractSessionInfo(data);
104
- }
105
- case 'assistant': {
106
- if (data.subtype === 'text' && data.content) {
107
- return { type: 'content', content: data.content };
84
+ if (event.delta?.type === 'text_delta' && event.delta.text) {
85
+ return [{ type: 'content', content: event.delta.text }];
108
86
  }
109
- return null;
110
- }
87
+ return [];
88
+ case 'content_block_start':
89
+ if (event.content_block?.type === 'tool_use') {
90
+ return [{
91
+ type: 'tool_call',
92
+ toolUseId: event.content_block.id || '',
93
+ toolName: event.content_block.name || 'unknown',
94
+ toolInput: event.content_block.input,
95
+ }];
96
+ }
97
+ return [];
111
98
  default:
112
- return null;
99
+ return [];
113
100
  }
114
101
  }
115
- /**
116
- * Extract session metadata from a result object
117
- */
118
- function extractSessionInfo(data) {
119
- if (!data.session_id)
120
- return null;
121
- const usage = extractUsage(data);
122
- return {
123
- type: 'session_info',
124
- sessionId: data.session_id,
125
- usage,
126
- };
102
+ function handleAssistantMessage(data) {
103
+ const chunks = [];
104
+ for (const block of data.message.content || []) {
105
+ if (block.type === 'thinking' && block.thinking) {
106
+ chunks.push({ type: 'thinking', content: block.thinking });
107
+ }
108
+ if (block.type === 'text' && block.text) {
109
+ chunks.push({ type: 'content', content: block.text });
110
+ }
111
+ if (block.type === 'tool_use') {
112
+ chunks.push({
113
+ type: 'tool_call',
114
+ toolUseId: block.id || '',
115
+ toolName: block.name || 'unknown',
116
+ toolInput: block.input,
117
+ });
118
+ }
119
+ }
120
+ return chunks;
127
121
  }
128
- /**
129
- * Extract usage info, dynamically handling any model name key
130
- */
131
- function extractUsage(data) {
132
- // Try modelUsage first (dynamic keys)
133
- if (data.modelUsage) {
134
- const firstKey = Object.keys(data.modelUsage)[0];
135
- const modelUsage = firstKey ? data.modelUsage[firstKey] : undefined;
136
- if (modelUsage) {
137
- return {
138
- inputTokens: modelUsage.inputTokens,
139
- outputTokens: modelUsage.outputTokens,
140
- contextWindow: modelUsage.contextWindow,
141
- };
122
+ function handleUserMessage(data) {
123
+ const chunks = [];
124
+ for (const block of data.message.content) {
125
+ if (block.type === 'tool_result') {
126
+ chunks.push({
127
+ type: 'tool_result',
128
+ toolUseId: block.tool_use_id,
129
+ toolResult: block.content,
130
+ });
142
131
  }
143
132
  }
144
- // Fallback to top-level usage
145
- if (data.usage) {
146
- return {
147
- inputTokens: data.usage.input_tokens,
148
- outputTokens: data.usage.output_tokens,
149
- contextWindow: 0,
150
- };
133
+ return chunks;
134
+ }
135
+ function handleResult(data) {
136
+ const chunks = [];
137
+ if (data.session_id) {
138
+ chunks.push({
139
+ type: 'session_info',
140
+ sessionId: data.session_id,
141
+ usage: extractStreamUsage(data.usage),
142
+ });
151
143
  }
152
- return undefined;
144
+ if (data.is_error) {
145
+ chunks.push({ type: 'error', error: data.result || 'Unknown error' });
146
+ }
147
+ chunks.push({ type: 'done' });
148
+ return chunks;
149
+ }
150
+ function extractStreamUsage(usage) {
151
+ if (!usage)
152
+ return undefined;
153
+ const firstKey = Object.keys(usage)[0];
154
+ const modelUsage = firstKey ? usage[firstKey] : undefined;
155
+ if (!modelUsage)
156
+ return undefined;
157
+ return {
158
+ inputTokens: modelUsage.inputTokens,
159
+ outputTokens: modelUsage.outputTokens,
160
+ contextWindow: 0,
161
+ };
153
162
  }
154
163
  //# sourceMappingURL=output-parser.js.map