opc-agent 4.0.10 → 4.0.11

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.
@@ -406,17 +406,7 @@ class ClaudeCLIProvider {
406
406
  }
407
407
  }
408
408
  async *chatStream(messages, systemPrompt) {
409
- // Build prompt same as chat()
410
- const parts = [];
411
- if (systemPrompt) {
412
- parts.push(`[System]: ${systemPrompt}`);
413
- }
414
- for (const m of messages) {
415
- const role = m.role === 'user' ? 'Human' : 'Assistant';
416
- parts.push(`${role}: ${m.content}`);
417
- }
418
- const prompt = parts.join('\n\n');
419
- const args = ['-p', '--output-format', 'text'];
409
+ const args = ['-p', '--output-format', 'stream-json', '--include-partial-messages'];
420
410
  if (this.model) {
421
411
  args.push('--model', this.model);
422
412
  }
@@ -431,7 +421,7 @@ class ClaudeCLIProvider {
431
421
  args.push('--system-prompt-file', tmpFile);
432
422
  }
433
423
  const lastMsg = messages[messages.length - 1];
434
- args.push(lastMsg?.content ?? prompt);
424
+ args.push(lastMsg?.content ?? '');
435
425
  const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
436
426
  try {
437
427
  const proc = spawn('claude', args, {
@@ -439,21 +429,71 @@ class ClaudeCLIProvider {
439
429
  stdio: ['pipe', 'pipe', 'pipe'],
440
430
  });
441
431
  proc.stdin.end();
442
- // Yield chunks as they arrive from stdout
443
- const readable = proc.stdout;
444
- for await (const chunk of readable) {
445
- yield chunk.toString();
432
+ let buffer = '';
433
+ let lastContent = '';
434
+ for await (const chunk of proc.stdout) {
435
+ buffer += chunk.toString();
436
+ const lines = buffer.split('\n');
437
+ buffer = lines.pop() ?? '';
438
+ for (const line of lines) {
439
+ const trimmed = line.trim();
440
+ if (!trimmed)
441
+ continue;
442
+ try {
443
+ const event = JSON.parse(trimmed);
444
+ // Handle partial message chunks (content_block_delta style)
445
+ if (event.type === 'content' && event.content) {
446
+ const newContent = event.content;
447
+ if (newContent.length > lastContent.length) {
448
+ yield newContent.slice(lastContent.length);
449
+ lastContent = newContent;
450
+ }
451
+ }
452
+ // Handle assistant message with content array
453
+ if (event.type === 'assistant' && event.message?.content) {
454
+ for (const block of event.message.content) {
455
+ if (block.type === 'text' && block.text) {
456
+ const newText = block.text;
457
+ if (newText.length > lastContent.length) {
458
+ yield newText.slice(lastContent.length);
459
+ lastContent = newText;
460
+ }
461
+ }
462
+ }
463
+ }
464
+ // Handle result message
465
+ if (event.type === 'result' && event.result) {
466
+ const resultText = typeof event.result === 'string' ? event.result : '';
467
+ if (resultText && resultText.length > lastContent.length) {
468
+ yield resultText.slice(lastContent.length);
469
+ }
470
+ }
471
+ }
472
+ catch {
473
+ // Not JSON, might be raw text
474
+ }
475
+ }
446
476
  }
447
- // Wait for process to finish
448
- await new Promise((resolve, reject) => {
449
- proc.on('close', (code) => {
450
- if (code !== 0 && code !== null) {
451
- // Already yielded content, just log
452
- console.warn(`[ClaudeCLI] Process exited with code ${code}`);
477
+ // Process remaining buffer
478
+ if (buffer.trim()) {
479
+ try {
480
+ const event = JSON.parse(buffer.trim());
481
+ if (event.type === 'result' && event.result) {
482
+ const resultText = typeof event.result === 'string' ? event.result : '';
483
+ if (resultText && resultText.length > lastContent.length) {
484
+ yield resultText.slice(lastContent.length);
485
+ }
453
486
  }
454
- resolve();
455
- });
456
- proc.on('error', reject);
487
+ }
488
+ catch {
489
+ // If not JSON, yield as raw text if we haven't yielded anything
490
+ if (!lastContent && buffer.trim()) {
491
+ yield buffer.trim();
492
+ }
493
+ }
494
+ }
495
+ await new Promise((resolve) => {
496
+ proc.on('close', () => resolve());
457
497
  });
458
498
  }
459
499
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opc-agent",
3
- "version": "4.0.10",
3
+ "version": "4.0.11",
4
4
  "description": "Open Agent Framework — Build, test, and run AI Agents for business workstations",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -410,18 +410,7 @@ class ClaudeCLIProvider implements LLMProvider {
410
410
  }
411
411
 
412
412
  async *chatStream(messages: Message[], systemPrompt?: string): AsyncIterable<string> {
413
- // Build prompt same as chat()
414
- const parts: string[] = [];
415
- if (systemPrompt) {
416
- parts.push(`[System]: ${systemPrompt}`);
417
- }
418
- for (const m of messages) {
419
- const role = m.role === 'user' ? 'Human' : 'Assistant';
420
- parts.push(`${role}: ${m.content}`);
421
- }
422
- const prompt = parts.join('\n\n');
423
-
424
- const args = ['-p', '--output-format', 'text'];
413
+ const args = ['-p', '--output-format', 'stream-json', '--include-partial-messages'];
425
414
  if (this.model) {
426
415
  args.push('--model', this.model);
427
416
  }
@@ -438,7 +427,7 @@ class ClaudeCLIProvider implements LLMProvider {
438
427
  }
439
428
 
440
429
  const lastMsg = messages[messages.length - 1];
441
- args.push(lastMsg?.content ?? prompt);
430
+ args.push(lastMsg?.content ?? '');
442
431
 
443
432
  const { spawn } = await import('child_process');
444
433
 
@@ -449,22 +438,72 @@ class ClaudeCLIProvider implements LLMProvider {
449
438
  });
450
439
  proc.stdin.end();
451
440
 
452
- // Yield chunks as they arrive from stdout
453
- const readable = proc.stdout;
454
- for await (const chunk of readable) {
455
- yield (chunk as Buffer).toString();
441
+ let buffer = '';
442
+ let lastContent = '';
443
+
444
+ for await (const chunk of proc.stdout) {
445
+ buffer += (chunk as Buffer).toString();
446
+ const lines = buffer.split('\n');
447
+ buffer = lines.pop() ?? '';
448
+
449
+ for (const line of lines) {
450
+ const trimmed = line.trim();
451
+ if (!trimmed) continue;
452
+ try {
453
+ const event = JSON.parse(trimmed);
454
+ // Handle partial message chunks (content_block_delta style)
455
+ if (event.type === 'content' && event.content) {
456
+ const newContent = event.content;
457
+ if (newContent.length > lastContent.length) {
458
+ yield newContent.slice(lastContent.length);
459
+ lastContent = newContent;
460
+ }
461
+ }
462
+ // Handle assistant message with content array
463
+ if (event.type === 'assistant' && event.message?.content) {
464
+ for (const block of event.message.content) {
465
+ if (block.type === 'text' && block.text) {
466
+ const newText = block.text;
467
+ if (newText.length > lastContent.length) {
468
+ yield newText.slice(lastContent.length);
469
+ lastContent = newText;
470
+ }
471
+ }
472
+ }
473
+ }
474
+ // Handle result message
475
+ if (event.type === 'result' && event.result) {
476
+ const resultText = typeof event.result === 'string' ? event.result : '';
477
+ if (resultText && resultText.length > lastContent.length) {
478
+ yield resultText.slice(lastContent.length);
479
+ }
480
+ }
481
+ } catch {
482
+ // Not JSON, might be raw text
483
+ }
484
+ }
456
485
  }
457
486
 
458
- // Wait for process to finish
459
- await new Promise<void>((resolve, reject) => {
460
- proc.on('close', (code) => {
461
- if (code !== 0 && code !== null) {
462
- // Already yielded content, just log
463
- console.warn(`[ClaudeCLI] Process exited with code ${code}`);
487
+ // Process remaining buffer
488
+ if (buffer.trim()) {
489
+ try {
490
+ const event = JSON.parse(buffer.trim());
491
+ if (event.type === 'result' && event.result) {
492
+ const resultText = typeof event.result === 'string' ? event.result : '';
493
+ if (resultText && resultText.length > lastContent.length) {
494
+ yield resultText.slice(lastContent.length);
495
+ }
464
496
  }
465
- resolve();
466
- });
467
- proc.on('error', reject);
497
+ } catch {
498
+ // If not JSON, yield as raw text if we haven't yielded anything
499
+ if (!lastContent && buffer.trim()) {
500
+ yield buffer.trim();
501
+ }
502
+ }
503
+ }
504
+
505
+ await new Promise<void>((resolve) => {
506
+ proc.on('close', () => resolve());
468
507
  });
469
508
  } finally {
470
509
  if (tmpFile) {