@corbat-tech/coco 1.5.0 → 1.7.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.
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ import { z } from 'zod';
18
18
  import { Logger } from 'tslog';
19
19
  import Anthropic from '@anthropic-ai/sdk';
20
20
  import OpenAI from 'openai';
21
+ import { jsonrepair } from 'jsonrepair';
21
22
  import 'http';
22
23
  import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
23
24
  import JSON5 from 'json5';
@@ -255,6 +256,71 @@ var init_allowed_paths = __esm({
255
256
  }
256
257
  });
257
258
 
259
+ // src/tools/utils/heartbeat.ts
260
+ var heartbeat_exports = {};
261
+ __export(heartbeat_exports, {
262
+ CommandHeartbeat: () => CommandHeartbeat
263
+ });
264
+ var CommandHeartbeat;
265
+ var init_heartbeat = __esm({
266
+ "src/tools/utils/heartbeat.ts"() {
267
+ CommandHeartbeat = class {
268
+ startTime = 0;
269
+ lastActivityTime = 0;
270
+ updateInterval = null;
271
+ warnThreshold = 30;
272
+ // seconds
273
+ updateIntervalSeconds = 10;
274
+ // seconds
275
+ callbacks;
276
+ constructor(callbacks = {}) {
277
+ this.callbacks = callbacks;
278
+ }
279
+ /**
280
+ * Start monitoring - begins periodic updates and silence warnings
281
+ */
282
+ start() {
283
+ this.startTime = Date.now();
284
+ this.lastActivityTime = Date.now();
285
+ this.updateInterval = setInterval(() => {
286
+ const stats = this.getStats();
287
+ this.callbacks.onUpdate?.(stats);
288
+ if (stats.silentSeconds >= this.warnThreshold) {
289
+ this.callbacks.onWarn?.(`\u26A0\uFE0F Command silent for ${stats.silentSeconds}s`);
290
+ }
291
+ }, this.updateIntervalSeconds * 1e3);
292
+ }
293
+ /**
294
+ * Register activity - call this when command produces output
295
+ * Resets the silence timer
296
+ */
297
+ activity() {
298
+ this.lastActivityTime = Date.now();
299
+ }
300
+ /**
301
+ * Stop monitoring - clears periodic interval
302
+ * Should be called in finally{} block to ensure cleanup
303
+ */
304
+ stop() {
305
+ if (this.updateInterval) {
306
+ clearInterval(this.updateInterval);
307
+ this.updateInterval = null;
308
+ }
309
+ }
310
+ /**
311
+ * Get current heartbeat statistics
312
+ */
313
+ getStats() {
314
+ const now = Date.now();
315
+ return {
316
+ elapsedSeconds: Math.floor((now - this.startTime) / 1e3),
317
+ silentSeconds: Math.floor((now - this.lastActivityTime) / 1e3)
318
+ };
319
+ }
320
+ };
321
+ }
322
+ });
323
+
258
324
  // src/cli/repl/allow-path-prompt.ts
259
325
  var allow_path_prompt_exports = {};
260
326
  __export(allow_path_prompt_exports, {
@@ -10616,8 +10682,10 @@ var OpenAIProvider = class {
10616
10682
  const timeoutInterval = setInterval(checkTimeout, 5e3);
10617
10683
  try {
10618
10684
  for await (const chunk of stream) {
10619
- lastActivityTime = Date.now();
10620
10685
  const delta = chunk.choices[0]?.delta;
10686
+ if (delta?.content || delta?.tool_calls) {
10687
+ lastActivityTime = Date.now();
10688
+ }
10621
10689
  if (delta?.content) {
10622
10690
  yield { type: "text", text: delta.content };
10623
10691
  }
@@ -10663,10 +10731,22 @@ var OpenAIProvider = class {
10663
10731
  let input = {};
10664
10732
  try {
10665
10733
  input = builder.arguments ? JSON.parse(builder.arguments) : {};
10666
- } catch {
10734
+ } catch (error) {
10667
10735
  console.warn(
10668
- `[OpenAI] Failed to parse tool call arguments: ${builder.arguments?.slice(0, 100)}`
10736
+ `[${this.name}] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 100)}`
10669
10737
  );
10738
+ try {
10739
+ if (builder.arguments) {
10740
+ const repaired = jsonrepair(builder.arguments);
10741
+ input = JSON.parse(repaired);
10742
+ console.log(`[${this.name}] \u2713 Successfully repaired JSON for ${builder.name}`);
10743
+ }
10744
+ } catch (repairError) {
10745
+ console.error(
10746
+ `[${this.name}] Cannot repair JSON for ${builder.name}, using empty object`
10747
+ );
10748
+ console.error(`[${this.name}] Original error:`, error);
10749
+ }
10670
10750
  }
10671
10751
  yield {
10672
10752
  type: "tool_use_end",
@@ -10960,7 +11040,7 @@ var OpenAIProvider = class {
10960
11040
  return JSON.parse(tc.function.arguments || "{}");
10961
11041
  } catch {
10962
11042
  console.warn(
10963
- `[OpenAI] Failed to parse tool call arguments: ${tc.function.arguments?.slice(0, 100)}`
11043
+ `[${this.name}] Failed to parse tool call arguments: ${tc.function.arguments?.slice(0, 100)}`
10964
11044
  );
10965
11045
  return {};
10966
11046
  }
@@ -11898,12 +11978,12 @@ async function createProvider(type, config = {}) {
11898
11978
  await provider.initialize(mergedConfig);
11899
11979
  return provider;
11900
11980
  case "lmstudio":
11901
- provider = new OpenAIProvider();
11981
+ provider = new OpenAIProvider("lmstudio", "LM Studio");
11902
11982
  mergedConfig.baseUrl = mergedConfig.baseUrl ?? "http://localhost:1234/v1";
11903
11983
  mergedConfig.apiKey = mergedConfig.apiKey ?? "lm-studio";
11904
11984
  break;
11905
11985
  case "ollama":
11906
- provider = new OpenAIProvider();
11986
+ provider = new OpenAIProvider("ollama", "Ollama");
11907
11987
  mergedConfig.baseUrl = mergedConfig.baseUrl ?? "http://localhost:11434/v1";
11908
11988
  mergedConfig.apiKey = mergedConfig.apiKey ?? "ollama";
11909
11989
  break;
@@ -13394,23 +13474,50 @@ Examples:
13394
13474
  }
13395
13475
  const startTime = performance.now();
13396
13476
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS;
13477
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
13478
+ const heartbeat = new CommandHeartbeat2({
13479
+ onUpdate: (stats) => {
13480
+ if (stats.elapsedSeconds > 10) {
13481
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
13482
+ }
13483
+ },
13484
+ onWarn: (message) => {
13485
+ process.stderr.write(`
13486
+ ${message}
13487
+ `);
13488
+ }
13489
+ });
13397
13490
  try {
13491
+ heartbeat.start();
13398
13492
  const options = {
13399
13493
  cwd: cwd ?? process.cwd(),
13400
13494
  timeout: timeoutMs,
13401
13495
  env: { ...process.env, ...env2 },
13402
13496
  shell: true,
13403
13497
  reject: false,
13498
+ buffer: false,
13499
+ // Enable streaming
13404
13500
  maxBuffer: MAX_OUTPUT_SIZE
13405
13501
  };
13406
- const result = await execa(command, options);
13502
+ const subprocess = execa(command, options);
13503
+ let stdoutBuffer = "";
13504
+ let stderrBuffer = "";
13505
+ subprocess.stdout?.on("data", (chunk) => {
13506
+ const text = chunk.toString();
13507
+ stdoutBuffer += text;
13508
+ process.stdout.write(text);
13509
+ heartbeat.activity();
13510
+ });
13511
+ subprocess.stderr?.on("data", (chunk) => {
13512
+ const text = chunk.toString();
13513
+ stderrBuffer += text;
13514
+ process.stderr.write(text);
13515
+ heartbeat.activity();
13516
+ });
13517
+ const result = await subprocess;
13407
13518
  return {
13408
- stdout: truncateOutput(
13409
- typeof result.stdout === "string" ? result.stdout : String(result.stdout ?? "")
13410
- ),
13411
- stderr: truncateOutput(
13412
- typeof result.stderr === "string" ? result.stderr : String(result.stderr ?? "")
13413
- ),
13519
+ stdout: truncateOutput(stdoutBuffer),
13520
+ stderr: truncateOutput(stderrBuffer),
13414
13521
  exitCode: result.exitCode ?? 0,
13415
13522
  duration: performance.now() - startTime
13416
13523
  };
@@ -13425,6 +13532,9 @@ Examples:
13425
13532
  `Command execution failed: ${error instanceof Error ? error.message : String(error)}`,
13426
13533
  { tool: "bash_exec", cause: error instanceof Error ? error : void 0 }
13427
13534
  );
13535
+ } finally {
13536
+ heartbeat.stop();
13537
+ process.stderr.write("\r \r");
13428
13538
  }
13429
13539
  }
13430
13540
  });
@@ -15051,7 +15161,21 @@ Examples:
15051
15161
  const projectDir = cwd ?? process.cwd();
15052
15162
  const startTime = performance.now();
15053
15163
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
15164
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
15165
+ const heartbeat = new CommandHeartbeat2({
15166
+ onUpdate: (stats) => {
15167
+ if (stats.elapsedSeconds > 10) {
15168
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
15169
+ }
15170
+ },
15171
+ onWarn: (message) => {
15172
+ process.stderr.write(`
15173
+ ${message}
15174
+ `);
15175
+ }
15176
+ });
15054
15177
  try {
15178
+ heartbeat.start();
15055
15179
  const pm = packageManager ?? await detectPackageManager(projectDir);
15056
15180
  const cmdArgs = ["run", script];
15057
15181
  if (args && args.length > 0) {
@@ -15062,13 +15186,30 @@ Examples:
15062
15186
  timeout: timeoutMs,
15063
15187
  env: { ...process.env, ...env2 },
15064
15188
  reject: false,
15189
+ buffer: false,
15190
+ // Enable streaming
15065
15191
  maxBuffer: MAX_OUTPUT_SIZE2
15066
15192
  };
15067
- const result = await execa(pm, cmdArgs, options);
15193
+ const subprocess = execa(pm, cmdArgs, options);
15194
+ let stdoutBuffer = "";
15195
+ let stderrBuffer = "";
15196
+ subprocess.stdout?.on("data", (chunk) => {
15197
+ const text = chunk.toString();
15198
+ stdoutBuffer += text;
15199
+ process.stdout.write(text);
15200
+ heartbeat.activity();
15201
+ });
15202
+ subprocess.stderr?.on("data", (chunk) => {
15203
+ const text = chunk.toString();
15204
+ stderrBuffer += text;
15205
+ process.stderr.write(text);
15206
+ heartbeat.activity();
15207
+ });
15208
+ const result = await subprocess;
15068
15209
  return {
15069
15210
  success: result.exitCode === 0,
15070
- stdout: truncateOutput2(String(result.stdout ?? "")),
15071
- stderr: truncateOutput2(String(result.stderr ?? "")),
15211
+ stdout: truncateOutput2(stdoutBuffer),
15212
+ stderr: truncateOutput2(stderrBuffer),
15072
15213
  exitCode: result.exitCode ?? 0,
15073
15214
  duration: performance.now() - startTime,
15074
15215
  packageManager: pm
@@ -15084,6 +15225,9 @@ Examples:
15084
15225
  `Failed to run script '${script}': ${error instanceof Error ? error.message : String(error)}`,
15085
15226
  { tool: "run_script", cause: error instanceof Error ? error : void 0 }
15086
15227
  );
15228
+ } finally {
15229
+ heartbeat.stop();
15230
+ process.stderr.write("\r \r");
15087
15231
  }
15088
15232
  }
15089
15233
  });
@@ -15109,7 +15253,21 @@ Examples:
15109
15253
  const projectDir = cwd ?? process.cwd();
15110
15254
  const startTime = performance.now();
15111
15255
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
15256
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
15257
+ const heartbeat = new CommandHeartbeat2({
15258
+ onUpdate: (stats) => {
15259
+ if (stats.elapsedSeconds > 10) {
15260
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
15261
+ }
15262
+ },
15263
+ onWarn: (message) => {
15264
+ process.stderr.write(`
15265
+ ${message}
15266
+ `);
15267
+ }
15268
+ });
15112
15269
  try {
15270
+ heartbeat.start();
15113
15271
  const pm = packageManager ?? await detectPackageManager(projectDir);
15114
15272
  let cmdArgs;
15115
15273
  if (packages && packages.length > 0) {
@@ -15149,13 +15307,30 @@ Examples:
15149
15307
  cwd: projectDir,
15150
15308
  timeout: timeoutMs,
15151
15309
  reject: false,
15310
+ buffer: false,
15311
+ // Enable streaming
15152
15312
  maxBuffer: MAX_OUTPUT_SIZE2
15153
15313
  };
15154
- const result = await execa(pm, cmdArgs, options);
15314
+ const subprocess = execa(pm, cmdArgs, options);
15315
+ let stdoutBuffer = "";
15316
+ let stderrBuffer = "";
15317
+ subprocess.stdout?.on("data", (chunk) => {
15318
+ const text = chunk.toString();
15319
+ stdoutBuffer += text;
15320
+ process.stdout.write(text);
15321
+ heartbeat.activity();
15322
+ });
15323
+ subprocess.stderr?.on("data", (chunk) => {
15324
+ const text = chunk.toString();
15325
+ stderrBuffer += text;
15326
+ process.stderr.write(text);
15327
+ heartbeat.activity();
15328
+ });
15329
+ const result = await subprocess;
15155
15330
  return {
15156
15331
  success: result.exitCode === 0,
15157
- stdout: truncateOutput2(String(result.stdout ?? "")),
15158
- stderr: truncateOutput2(String(result.stderr ?? "")),
15332
+ stdout: truncateOutput2(stdoutBuffer),
15333
+ stderr: truncateOutput2(stderrBuffer),
15159
15334
  exitCode: result.exitCode ?? 0,
15160
15335
  duration: performance.now() - startTime,
15161
15336
  packageManager: pm
@@ -15171,6 +15346,9 @@ Examples:
15171
15346
  `Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`,
15172
15347
  { tool: "install_deps", cause: error instanceof Error ? error : void 0 }
15173
15348
  );
15349
+ } finally {
15350
+ heartbeat.stop();
15351
+ process.stderr.write("\r \r");
15174
15352
  }
15175
15353
  }
15176
15354
  });
@@ -15195,12 +15373,26 @@ Examples:
15195
15373
  const projectDir = cwd ?? process.cwd();
15196
15374
  const startTime = performance.now();
15197
15375
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
15376
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
15377
+ const heartbeat = new CommandHeartbeat2({
15378
+ onUpdate: (stats) => {
15379
+ if (stats.elapsedSeconds > 10) {
15380
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
15381
+ }
15382
+ },
15383
+ onWarn: (message) => {
15384
+ process.stderr.write(`
15385
+ ${message}
15386
+ `);
15387
+ }
15388
+ });
15198
15389
  try {
15199
15390
  try {
15200
15391
  await fs14__default.access(path14__default.join(projectDir, "Makefile"));
15201
15392
  } catch {
15202
15393
  throw new ToolError("No Makefile found in directory", { tool: "make" });
15203
15394
  }
15395
+ heartbeat.start();
15204
15396
  const cmdArgs = [];
15205
15397
  if (target) {
15206
15398
  cmdArgs.push(...target.split(/\s+/));
@@ -15213,13 +15405,30 @@ Examples:
15213
15405
  timeout: timeoutMs,
15214
15406
  env: { ...process.env, ...env2 },
15215
15407
  reject: false,
15408
+ buffer: false,
15409
+ // Enable streaming
15216
15410
  maxBuffer: MAX_OUTPUT_SIZE2
15217
15411
  };
15218
- const result = await execa("make", cmdArgs, options);
15412
+ const subprocess = execa("make", cmdArgs, options);
15413
+ let stdoutBuffer = "";
15414
+ let stderrBuffer = "";
15415
+ subprocess.stdout?.on("data", (chunk) => {
15416
+ const text = chunk.toString();
15417
+ stdoutBuffer += text;
15418
+ process.stdout.write(text);
15419
+ heartbeat.activity();
15420
+ });
15421
+ subprocess.stderr?.on("data", (chunk) => {
15422
+ const text = chunk.toString();
15423
+ stderrBuffer += text;
15424
+ process.stderr.write(text);
15425
+ heartbeat.activity();
15426
+ });
15427
+ const result = await subprocess;
15219
15428
  return {
15220
15429
  success: result.exitCode === 0,
15221
- stdout: truncateOutput2(String(result.stdout ?? "")),
15222
- stderr: truncateOutput2(String(result.stderr ?? "")),
15430
+ stdout: truncateOutput2(stdoutBuffer),
15431
+ stderr: truncateOutput2(stderrBuffer),
15223
15432
  exitCode: result.exitCode ?? 0,
15224
15433
  duration: performance.now() - startTime
15225
15434
  };
@@ -15235,6 +15444,9 @@ Examples:
15235
15444
  `Make failed: ${error instanceof Error ? error.message : String(error)}`,
15236
15445
  { tool: "make", cause: error instanceof Error ? error : void 0 }
15237
15446
  );
15447
+ } finally {
15448
+ heartbeat.stop();
15449
+ process.stderr.write("\r \r");
15238
15450
  }
15239
15451
  }
15240
15452
  });
@@ -15260,7 +15472,21 @@ Examples:
15260
15472
  const projectDir = cwd ?? process.cwd();
15261
15473
  const startTime = performance.now();
15262
15474
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS3;
15475
+ const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
15476
+ const heartbeat = new CommandHeartbeat2({
15477
+ onUpdate: (stats) => {
15478
+ if (stats.elapsedSeconds > 10) {
15479
+ process.stderr.write(`\r\u23F1\uFE0F ${stats.elapsedSeconds}s elapsed`);
15480
+ }
15481
+ },
15482
+ onWarn: (message) => {
15483
+ process.stderr.write(`
15484
+ ${message}
15485
+ `);
15486
+ }
15487
+ });
15263
15488
  try {
15489
+ heartbeat.start();
15264
15490
  const cmdArgs = [];
15265
15491
  if (project) {
15266
15492
  cmdArgs.push("--project", project);
@@ -15278,13 +15504,30 @@ Examples:
15278
15504
  cwd: projectDir,
15279
15505
  timeout: timeoutMs,
15280
15506
  reject: false,
15507
+ buffer: false,
15508
+ // Enable streaming
15281
15509
  maxBuffer: MAX_OUTPUT_SIZE2
15282
15510
  };
15283
- const result = await execa("npx", ["tsc", ...cmdArgs], options);
15511
+ const subprocess = execa("npx", ["tsc", ...cmdArgs], options);
15512
+ let stdoutBuffer = "";
15513
+ let stderrBuffer = "";
15514
+ subprocess.stdout?.on("data", (chunk) => {
15515
+ const text = chunk.toString();
15516
+ stdoutBuffer += text;
15517
+ process.stdout.write(text);
15518
+ heartbeat.activity();
15519
+ });
15520
+ subprocess.stderr?.on("data", (chunk) => {
15521
+ const text = chunk.toString();
15522
+ stderrBuffer += text;
15523
+ process.stderr.write(text);
15524
+ heartbeat.activity();
15525
+ });
15526
+ const result = await subprocess;
15284
15527
  return {
15285
15528
  success: result.exitCode === 0,
15286
- stdout: truncateOutput2(String(result.stdout ?? "")),
15287
- stderr: truncateOutput2(String(result.stderr ?? "")),
15529
+ stdout: truncateOutput2(stdoutBuffer),
15530
+ stderr: truncateOutput2(stderrBuffer),
15288
15531
  exitCode: result.exitCode ?? 0,
15289
15532
  duration: performance.now() - startTime
15290
15533
  };
@@ -15299,6 +15542,9 @@ Examples:
15299
15542
  `TypeScript compile failed: ${error instanceof Error ? error.message : String(error)}`,
15300
15543
  { tool: "tsc", cause: error instanceof Error ? error : void 0 }
15301
15544
  );
15545
+ } finally {
15546
+ heartbeat.stop();
15547
+ process.stderr.write("\r \r");
15302
15548
  }
15303
15549
  }
15304
15550
  });