@corbat-tech/coco 2.35.0 → 2.36.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/cli/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { execFileSync, execSync, spawn, execFile, exec } from 'child_process';
2
+ import { execFileSync, spawn, execSync, execFile, exec } from 'child_process';
3
3
  import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici';
4
4
  import * as fs5 from 'fs';
5
- import fs5__default, { accessSync, readFileSync, constants as constants$1 } from 'fs';
5
+ import fs5__default, { accessSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, constants as constants$1 } from 'fs';
6
6
  import * as path39 from 'path';
7
7
  import path39__default, { join, dirname, resolve, basename } from 'path';
8
8
  import { URL as URL$1, fileURLToPath } from 'url';
@@ -10,7 +10,7 @@ import { z } from 'zod';
10
10
  import * as os4 from 'os';
11
11
  import os4__default, { homedir } from 'os';
12
12
  import * as fs35 from 'fs/promises';
13
- import fs35__default, { mkdir, writeFile, readFile, access, constants, readdir, rm } from 'fs/promises';
13
+ import fs35__default, { mkdir, writeFile, readFile, access, readdir, rm, constants } from 'fs/promises';
14
14
  import JSON5 from 'json5';
15
15
  import * as crypto from 'crypto';
16
16
  import { randomUUID, randomBytes, createHash } from 'crypto';
@@ -23,7 +23,7 @@ import Anthropic from '@anthropic-ai/sdk';
23
23
  import { jsonrepair } from 'jsonrepair';
24
24
  import OpenAI from 'openai';
25
25
  import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
26
- import { parse as parse$1 } from 'yaml';
26
+ import { parse } from 'yaml';
27
27
  import { minimatch } from 'minimatch';
28
28
  import hljs from 'highlight.js/lib/core';
29
29
  import bash from 'highlight.js/lib/languages/bash';
@@ -42,7 +42,7 @@ import yaml from 'highlight.js/lib/languages/yaml';
42
42
  import { diffLines, diffWords } from 'diff';
43
43
  import { glob } from 'glob';
44
44
  import { execa } from 'execa';
45
- import { parse } from '@typescript-eslint/typescript-estree';
45
+ import { parse as parse$1 } from '@typescript-eslint/typescript-estree';
46
46
  import { simpleGit } from 'simple-git';
47
47
  import { Marked } from 'marked';
48
48
  import { markedTerminal } from 'marked-terminal';
@@ -893,8 +893,8 @@ async function configExists(configPath, scope = "any") {
893
893
  }
894
894
  return false;
895
895
  }
896
- function getConfigValue(config, path63) {
897
- const keys = path63.split(".");
896
+ function getConfigValue(config, path65) {
897
+ const keys = path65.split(".");
898
898
  let current = config;
899
899
  for (const key of keys) {
900
900
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -956,6 +956,10 @@ function getCatalogDefaultModel(provider) {
956
956
  function getCatalogModel(provider, modelId) {
957
957
  return getProviderCatalogEntry(provider).models.find((modelEntry) => modelEntry.id === modelId);
958
958
  }
959
+ function getCatalogRecommendedModel(provider) {
960
+ const entry = getProviderCatalogEntry(provider);
961
+ return entry.models.find((modelEntry) => modelEntry.recommended) ?? entry.models[0];
962
+ }
959
963
  function getCatalogContextWindow(provider, modelId, fallback) {
960
964
  if (!modelId) return fallback;
961
965
  const exact = getCatalogModel(provider, modelId);
@@ -9496,9 +9500,9 @@ async function migrateGlobalConfig(oldDir, newConfigPath) {
9496
9500
  );
9497
9501
  }
9498
9502
  }
9499
- async function fileExists(path63) {
9503
+ async function fileExists(path65) {
9500
9504
  try {
9501
- await access(path63);
9505
+ await access(path65);
9502
9506
  return true;
9503
9507
  } catch {
9504
9508
  return false;
@@ -9881,8 +9885,8 @@ var init_registry = __esm({
9881
9885
  /**
9882
9886
  * Ensure directory exists
9883
9887
  */
9884
- async ensureDir(path63) {
9885
- await mkdir(dirname(path63), { recursive: true });
9888
+ async ensureDir(path65) {
9889
+ await mkdir(dirname(path65), { recursive: true });
9886
9890
  }
9887
9891
  };
9888
9892
  }
@@ -10057,7 +10061,7 @@ function parseSkillMarkdown(raw) {
10057
10061
  const frontmatter = normalized.slice(3, closeIndex).trim();
10058
10062
  const afterMarkerStart = closeIndex + closeMarker.length;
10059
10063
  const contentStart = normalized[afterMarkerStart] === "\n" ? afterMarkerStart + 1 : afterMarkerStart;
10060
- const parsed = frontmatter.length > 0 ? parse$1(frontmatter) : {};
10064
+ const parsed = frontmatter.length > 0 ? parse(frontmatter) : {};
10061
10065
  return {
10062
10066
  data: parsed && typeof parsed === "object" ? parsed : {},
10063
10067
  content: normalized.slice(contentStart)
@@ -11563,9 +11567,9 @@ function createEmptyMemoryContext() {
11563
11567
  errors: []
11564
11568
  };
11565
11569
  }
11566
- function createMissingMemoryFile(path63, level) {
11570
+ function createMissingMemoryFile(path65, level) {
11567
11571
  return {
11568
- path: path63,
11572
+ path: path65,
11569
11573
  level,
11570
11574
  content: "",
11571
11575
  sections: [],
@@ -13126,8 +13130,8 @@ __export(trust_store_exports, {
13126
13130
  saveTrustStore: () => saveTrustStore,
13127
13131
  updateLastAccessed: () => updateLastAccessed
13128
13132
  });
13129
- async function ensureDir(path63) {
13130
- await mkdir(dirname(path63), { recursive: true });
13133
+ async function ensureDir(path65) {
13134
+ await mkdir(dirname(path65), { recursive: true });
13131
13135
  }
13132
13136
  async function loadTrustStore(storePath = TRUST_STORE_PATH) {
13133
13137
  try {
@@ -13212,8 +13216,8 @@ function canPerformOperation(store, projectPath, operation) {
13212
13216
  };
13213
13217
  return permissions[level]?.includes(operation) ?? false;
13214
13218
  }
13215
- function normalizePath(path63) {
13216
- return join(path63);
13219
+ function normalizePath(path65) {
13220
+ return join(path65);
13217
13221
  }
13218
13222
  function createTrustStore(storePath = TRUST_STORE_PATH) {
13219
13223
  let store = null;
@@ -13569,12 +13573,12 @@ function humanizeError(message, toolName) {
13569
13573
  return msg;
13570
13574
  }
13571
13575
  if (/ENOENT/i.test(msg)) {
13572
- const path63 = extractQuotedPath(msg);
13573
- return path63 ? `File or directory not found: ${path63}` : "File or directory not found";
13576
+ const path65 = extractQuotedPath(msg);
13577
+ return path65 ? `File or directory not found: ${path65}` : "File or directory not found";
13574
13578
  }
13575
13579
  if (/EACCES/i.test(msg)) {
13576
- const path63 = extractQuotedPath(msg);
13577
- return path63 ? `Permission denied: ${path63}` : "Permission denied \u2014 check file permissions";
13580
+ const path65 = extractQuotedPath(msg);
13581
+ return path65 ? `Permission denied: ${path65}` : "Permission denied \u2014 check file permissions";
13578
13582
  }
13579
13583
  if (/EISDIR/i.test(msg)) {
13580
13584
  return "Expected a file but found a directory at the specified path";
@@ -14516,9 +14520,9 @@ var init_diff_renderer = __esm({
14516
14520
  getTerminalWidth = () => process.stdout.columns || 80;
14517
14521
  }
14518
14522
  });
14519
- async function fileExists4(path63) {
14523
+ async function fileExists4(path65) {
14520
14524
  try {
14521
- await access(path63);
14525
+ await access(path65);
14522
14526
  return true;
14523
14527
  } catch {
14524
14528
  return false;
@@ -15461,7 +15465,7 @@ var init_complexity = __esm({
15461
15465
  * Analyze single file
15462
15466
  */
15463
15467
  async analyzeFile(file, content) {
15464
- const ast = parse(content, {
15468
+ const ast = parse$1(content, {
15465
15469
  loc: true,
15466
15470
  range: true,
15467
15471
  comment: false,
@@ -16058,7 +16062,7 @@ var init_completeness = __esm({
16058
16062
  for (const file of files) {
16059
16063
  try {
16060
16064
  const content = await readFile(file, "utf-8");
16061
- const ast = parse(content, {
16065
+ const ast = parse$1(content, {
16062
16066
  loc: true,
16063
16067
  range: true,
16064
16068
  jsx: file.endsWith(".tsx") || file.endsWith(".jsx")
@@ -16271,7 +16275,7 @@ var init_robustness = __esm({
16271
16275
  for (const file of targetFiles) {
16272
16276
  try {
16273
16277
  const content = await readFile(file, "utf-8");
16274
- const ast = parse(content, {
16278
+ const ast = parse$1(content, {
16275
16279
  loc: true,
16276
16280
  range: true,
16277
16281
  jsx: file.endsWith(".tsx") || file.endsWith(".jsx")
@@ -16564,7 +16568,7 @@ var init_documentation = __esm({
16564
16568
  for (const file of targetFiles) {
16565
16569
  try {
16566
16570
  const content = await readFile(file, "utf-8");
16567
- const ast = parse(content, {
16571
+ const ast = parse$1(content, {
16568
16572
  loc: true,
16569
16573
  range: true,
16570
16574
  comment: true,
@@ -16942,7 +16946,7 @@ var init_readability = __esm({
16942
16946
  for (const file of targetFiles) {
16943
16947
  try {
16944
16948
  const content = await readFile(file, "utf-8");
16945
- const ast = parse(content, {
16949
+ const ast = parse$1(content, {
16946
16950
  loc: true,
16947
16951
  range: true,
16948
16952
  jsx: file.endsWith(".tsx") || file.endsWith(".jsx")
@@ -17100,7 +17104,7 @@ var init_maintainability = __esm({
17100
17104
  try {
17101
17105
  const content = await readFile(file, "utf-8");
17102
17106
  const lineCount = countLines(content);
17103
- const ast = parse(content, {
17107
+ const ast = parse$1(content, {
17104
17108
  loc: true,
17105
17109
  range: true,
17106
17110
  jsx: file.endsWith(".tsx") || file.endsWith(".jsx")
@@ -26985,8 +26989,8 @@ Generated by Corbat-Coco v0.1.0
26985
26989
 
26986
26990
  // src/cli/commands/init.ts
26987
26991
  function registerInitCommand(program2) {
26988
- program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path63, options) => {
26989
- await runInit(path63, options);
26992
+ program2.command("init").description("Initialize a new Corbat-Coco project").argument("[path]", "Project directory path", ".").option("-t, --template <template>", "Project template to use").option("-y, --yes", "Skip prompts and use defaults").option("--skip-discovery", "Skip the discovery phase (use existing spec)").action(async (path65, options) => {
26993
+ await runInit(path65, options);
26990
26994
  });
26991
26995
  }
26992
26996
  async function runInit(projectPath, options) {
@@ -27065,18 +27069,18 @@ async function gatherProjectInfo() {
27065
27069
  language
27066
27070
  };
27067
27071
  }
27068
- function getDefaultProjectInfo(path63) {
27069
- const name = path63 === "." ? "my-project" : path63.split("/").pop() || "my-project";
27072
+ function getDefaultProjectInfo(path65) {
27073
+ const name = path65 === "." ? "my-project" : path65.split("/").pop() || "my-project";
27070
27074
  return {
27071
27075
  name,
27072
27076
  description: "",
27073
27077
  language: "typescript"
27074
27078
  };
27075
27079
  }
27076
- async function checkExistingProject(path63) {
27080
+ async function checkExistingProject(path65) {
27077
27081
  try {
27078
27082
  const fs57 = await import('fs/promises');
27079
- await fs57.access(`${path63}/.coco`);
27083
+ await fs57.access(`${path65}/.coco`);
27080
27084
  return true;
27081
27085
  } catch {
27082
27086
  return false;
@@ -30577,20 +30581,20 @@ async function createCliPhaseContext(projectPath, _onUserInput) {
30577
30581
  },
30578
30582
  tools: {
30579
30583
  file: {
30580
- async read(path63) {
30584
+ async read(path65) {
30581
30585
  const fs57 = await import('fs/promises');
30582
- return fs57.readFile(path63, "utf-8");
30586
+ return fs57.readFile(path65, "utf-8");
30583
30587
  },
30584
- async write(path63, content) {
30588
+ async write(path65, content) {
30585
30589
  const fs57 = await import('fs/promises');
30586
30590
  const nodePath = await import('path');
30587
- await fs57.mkdir(nodePath.dirname(path63), { recursive: true });
30588
- await fs57.writeFile(path63, content, "utf-8");
30591
+ await fs57.mkdir(nodePath.dirname(path65), { recursive: true });
30592
+ await fs57.writeFile(path65, content, "utf-8");
30589
30593
  },
30590
- async exists(path63) {
30594
+ async exists(path65) {
30591
30595
  const fs57 = await import('fs/promises');
30592
30596
  try {
30593
- await fs57.access(path63);
30597
+ await fs57.access(path65);
30594
30598
  return true;
30595
30599
  } catch {
30596
30600
  return false;
@@ -30946,10 +30950,10 @@ function getPhaseStatusForPhase(phase) {
30946
30950
  }
30947
30951
  async function loadProjectState(cwd, config) {
30948
30952
  const fs57 = await import('fs/promises');
30949
- const path63 = await import('path');
30950
- const statePath = path63.join(cwd, ".coco", "state.json");
30951
- const backlogPath = path63.join(cwd, ".coco", "planning", "backlog.json");
30952
- const checkpointDir = path63.join(cwd, ".coco", "checkpoints");
30953
+ const path65 = await import('path');
30954
+ const statePath = path65.join(cwd, ".coco", "state.json");
30955
+ const backlogPath = path65.join(cwd, ".coco", "planning", "backlog.json");
30956
+ const checkpointDir = path65.join(cwd, ".coco", "checkpoints");
30953
30957
  let currentPhase = "idle";
30954
30958
  let metrics;
30955
30959
  let sprint;
@@ -32521,8 +32525,8 @@ async function saveConfig2(config) {
32521
32525
  await fs57.mkdir(dir, { recursive: true });
32522
32526
  await fs57.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2));
32523
32527
  }
32524
- function getNestedValue(obj, path63) {
32525
- const keys = path63.split(".");
32528
+ function getNestedValue(obj, path65) {
32529
+ const keys = path65.split(".");
32526
32530
  let current = obj;
32527
32531
  for (const key of keys) {
32528
32532
  if (current === null || current === void 0 || typeof current !== "object") {
@@ -32532,8 +32536,8 @@ function getNestedValue(obj, path63) {
32532
32536
  }
32533
32537
  return current;
32534
32538
  }
32535
- function setNestedValue(obj, path63, value) {
32536
- const keys = path63.split(".");
32539
+ function setNestedValue(obj, path65, value) {
32540
+ const keys = path65.split(".");
32537
32541
  let current = obj;
32538
32542
  for (let i = 0; i < keys.length - 1; i++) {
32539
32543
  const key = keys[i];
@@ -33565,9 +33569,9 @@ function registerCheckCommand(program2) {
33565
33569
  // src/swarm/spec-parser.ts
33566
33570
  async function parseSwarmSpec(filePath) {
33567
33571
  const fs57 = await import('fs/promises');
33568
- const path63 = await import('path');
33572
+ const path65 = await import('path');
33569
33573
  const rawContent = await fs57.readFile(filePath, "utf-8");
33570
- const ext = path63.extname(filePath).toLowerCase();
33574
+ const ext = path65.extname(filePath).toLowerCase();
33571
33575
  if (ext === ".yaml" || ext === ".yml") {
33572
33576
  return parseYamlSpec(rawContent);
33573
33577
  }
@@ -33897,8 +33901,8 @@ var DEFAULT_AGENT_CONFIG = {
33897
33901
  };
33898
33902
  async function loadAgentConfig(projectPath) {
33899
33903
  const fs57 = await import('fs/promises');
33900
- const path63 = await import('path');
33901
- const configPath = path63.join(projectPath, ".coco", "swarm", "agents.json");
33904
+ const path65 = await import('path');
33905
+ const configPath = path65.join(projectPath, ".coco", "swarm", "agents.json");
33902
33906
  try {
33903
33907
  const raw = await fs57.readFile(configPath, "utf-8");
33904
33908
  const parsed = JSON.parse(raw);
@@ -34089,16 +34093,16 @@ async function createBoard(projectPath, spec) {
34089
34093
  }
34090
34094
  async function loadBoard(projectPath) {
34091
34095
  const fs57 = await import('fs/promises');
34092
- const path63 = await import('path');
34093
- const boardPath = path63.join(projectPath, ".coco", "swarm", "task-board.json");
34096
+ const path65 = await import('path');
34097
+ const boardPath = path65.join(projectPath, ".coco", "swarm", "task-board.json");
34094
34098
  const raw = await fs57.readFile(boardPath, "utf-8");
34095
34099
  return JSON.parse(raw);
34096
34100
  }
34097
34101
  async function saveBoard(projectPath, board) {
34098
34102
  const fs57 = await import('fs/promises');
34099
- const path63 = await import('path');
34100
- const boardDir = path63.join(projectPath, ".coco", "swarm");
34101
- const boardPath = path63.join(boardDir, "task-board.json");
34103
+ const path65 = await import('path');
34104
+ const boardDir = path65.join(projectPath, ".coco", "swarm");
34105
+ const boardPath = path65.join(boardDir, "task-board.json");
34102
34106
  await fs57.mkdir(boardDir, { recursive: true });
34103
34107
  await fs57.writeFile(boardPath, JSON.stringify(board, null, 2), "utf-8");
34104
34108
  }
@@ -34266,9 +34270,9 @@ async function defaultPromptHandler(q) {
34266
34270
  }
34267
34271
  async function writeAssumptionsFile(projectPath, projectName, questions, assumptions) {
34268
34272
  const fs57 = await import('fs/promises');
34269
- const path63 = await import('path');
34270
- const swarmDir = path63.join(projectPath, ".coco", "swarm");
34271
- const assumptionsPath = path63.join(swarmDir, "assumptions.md");
34273
+ const path65 = await import('path');
34274
+ const swarmDir = path65.join(projectPath, ".coco", "swarm");
34275
+ const assumptionsPath = path65.join(swarmDir, "assumptions.md");
34272
34276
  await fs57.mkdir(swarmDir, { recursive: true });
34273
34277
  const now = (/* @__PURE__ */ new Date()).toISOString();
34274
34278
  const content = [
@@ -34292,9 +34296,9 @@ async function writeAssumptionsFile(projectPath, projectName, questions, assumpt
34292
34296
  // src/swarm/events.ts
34293
34297
  async function appendSwarmEvent(projectPath, event) {
34294
34298
  const fs57 = await import('fs/promises');
34295
- const path63 = await import('path');
34296
- const eventsDir = path63.join(projectPath, ".coco", "swarm");
34297
- const eventsFile = path63.join(eventsDir, "events.jsonl");
34299
+ const path65 = await import('path');
34300
+ const eventsDir = path65.join(projectPath, ".coco", "swarm");
34301
+ const eventsFile = path65.join(eventsDir, "events.jsonl");
34298
34302
  await fs57.mkdir(eventsDir, { recursive: true });
34299
34303
  await fs57.appendFile(eventsFile, JSON.stringify(event) + "\n", "utf-8");
34300
34304
  }
@@ -34305,9 +34309,9 @@ function createEventId() {
34305
34309
  // src/swarm/knowledge.ts
34306
34310
  async function appendKnowledge(projectPath, entry) {
34307
34311
  const fs57 = await import('fs/promises');
34308
- const path63 = await import('path');
34309
- const knowledgeDir = path63.join(projectPath, ".coco", "swarm");
34310
- const knowledgeFile = path63.join(knowledgeDir, "knowledge.jsonl");
34312
+ const path65 = await import('path');
34313
+ const knowledgeDir = path65.join(projectPath, ".coco", "swarm");
34314
+ const knowledgeFile = path65.join(knowledgeDir, "knowledge.jsonl");
34311
34315
  await fs57.mkdir(knowledgeDir, { recursive: true });
34312
34316
  await fs57.appendFile(knowledgeFile, JSON.stringify(entry) + "\n", "utf-8");
34313
34317
  }
@@ -34614,10 +34618,10 @@ async function runSwarmLifecycle(options) {
34614
34618
  async function stageInit(ctx) {
34615
34619
  const { projectPath, spec } = ctx.options;
34616
34620
  const fs57 = await import('fs/promises');
34617
- const path63 = await import('path');
34618
- await fs57.mkdir(path63.join(projectPath, ".coco", "swarm"), { recursive: true });
34621
+ const path65 = await import('path');
34622
+ await fs57.mkdir(path65.join(projectPath, ".coco", "swarm"), { recursive: true });
34619
34623
  await fs57.mkdir(ctx.options.outputPath, { recursive: true });
34620
- const specSummaryPath = path63.join(projectPath, ".coco", "swarm", "spec-summary.json");
34624
+ const specSummaryPath = path65.join(projectPath, ".coco", "swarm", "spec-summary.json");
34621
34625
  const specSummary = {
34622
34626
  projectName: spec.projectName,
34623
34627
  description: spec.description,
@@ -34689,8 +34693,8 @@ async function stagePlan(ctx) {
34689
34693
  ]);
34690
34694
  await createBoard(projectPath, spec);
34691
34695
  const fs57 = await import('fs/promises');
34692
- const path63 = await import('path');
34693
- const planPath = path63.join(projectPath, ".coco", "swarm", "plan.json");
34696
+ const path65 = await import('path');
34697
+ const planPath = path65.join(projectPath, ".coco", "swarm", "plan.json");
34694
34698
  await fs57.writeFile(
34695
34699
  planPath,
34696
34700
  JSON.stringify({ pm: pmResult, architect: archResult, bestPractices: bpResult }, null, 2),
@@ -34907,7 +34911,7 @@ async function stageIntegrate(ctx) {
34907
34911
  async function stageOutput(ctx) {
34908
34912
  const { projectPath, outputPath } = ctx.options;
34909
34913
  const fs57 = await import('fs/promises');
34910
- const path63 = await import('path');
34914
+ const path65 = await import('path');
34911
34915
  const board = await loadBoard(projectPath);
34912
34916
  const featureResults = Array.from(ctx.featureResults.values());
34913
34917
  const summary = {
@@ -34922,7 +34926,7 @@ async function stageOutput(ctx) {
34922
34926
  globalScore: computeGlobalScore(featureResults)
34923
34927
  };
34924
34928
  await fs57.mkdir(outputPath, { recursive: true });
34925
- const summaryPath = path63.join(outputPath, "swarm-summary.json");
34929
+ const summaryPath = path65.join(outputPath, "swarm-summary.json");
34926
34930
  await fs57.writeFile(summaryPath, JSON.stringify(summary, null, 2), "utf-8");
34927
34931
  const passed = summary.globalScore >= ctx.options.minScore;
34928
34932
  await emitGate(projectPath, "global-score", passed, `Global score: ${summary.globalScore}`);
@@ -35269,8 +35273,8 @@ var SwarmOrchestrator = class {
35269
35273
  noQuestions = false,
35270
35274
  onProgress
35271
35275
  } = options;
35272
- const path63 = await import('path');
35273
- const projectPath = path63.dirname(path63.resolve(specFile));
35276
+ const path65 = await import('path');
35277
+ const projectPath = path65.dirname(path65.resolve(specFile));
35274
35278
  onProgress?.("init", `Parsing spec file: ${specFile}`);
35275
35279
  const spec = await parseSwarmSpec(specFile);
35276
35280
  onProgress?.("init", `Initializing provider: ${providerType}`);
@@ -35281,7 +35285,7 @@ var SwarmOrchestrator = class {
35281
35285
  await runSwarmLifecycle({
35282
35286
  spec,
35283
35287
  projectPath,
35284
- outputPath: path63.resolve(outputPath),
35288
+ outputPath: path65.resolve(outputPath),
35285
35289
  provider,
35286
35290
  agentConfig,
35287
35291
  minScore,
@@ -39219,8 +39223,8 @@ async function listTrustedProjects2(trustStore) {
39219
39223
  p26.log.message("");
39220
39224
  for (const project of projects) {
39221
39225
  const level = project.approvalLevel.toUpperCase().padEnd(5);
39222
- const path63 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
39223
- p26.log.message(` [${level}] ${path63}`);
39226
+ const path65 = project.path.length > 50 ? "..." + project.path.slice(-47) : project.path;
39227
+ p26.log.message(` [${level}] ${path65}`);
39224
39228
  p26.log.message(` Last accessed: ${new Date(project.lastAccessed).toLocaleString()}`);
39225
39229
  }
39226
39230
  p26.log.message("");
@@ -40451,8 +40455,8 @@ function displayRewindResult(result) {
40451
40455
  const fileName = filePath.split("/").pop() ?? filePath;
40452
40456
  console.log(`${chalk.green(String.fromCodePoint(10003))} Restored: ${fileName}`);
40453
40457
  }
40454
- for (const { path: path63, error } of result.filesFailed) {
40455
- const fileName = path63.split("/").pop() ?? path63;
40458
+ for (const { path: path65, error } of result.filesFailed) {
40459
+ const fileName = path65.split("/").pop() ?? path65;
40456
40460
  console.log(`${chalk.red(String.fromCodePoint(10007))} Failed: ${fileName} (${error})`);
40457
40461
  }
40458
40462
  if (result.conversationRestored) {
@@ -40853,6 +40857,9 @@ function getSessionStore() {
40853
40857
  }
40854
40858
  return defaultStore;
40855
40859
  }
40860
+ function createSessionStore(config) {
40861
+ return new SessionStore(config);
40862
+ }
40856
40863
 
40857
40864
  // src/cli/repl/commands/resume.ts
40858
40865
  function formatRelativeTime2(date) {
@@ -49993,7 +50000,7 @@ async function validateCode(code, filePath, _language) {
49993
50000
  const errors = [];
49994
50001
  const warnings = [];
49995
50002
  try {
49996
- const ast = parse(code, {
50003
+ const ast = parse$1(code, {
49997
50004
  loc: true,
49998
50005
  range: true,
49999
50006
  comment: true,
@@ -50120,7 +50127,7 @@ async function analyzeFile(filePath, includeAst = false) {
50120
50127
  const exports$1 = [];
50121
50128
  let ast;
50122
50129
  try {
50123
- ast = parse(content, {
50130
+ ast = parse$1(content, {
50124
50131
  loc: true,
50125
50132
  range: true,
50126
50133
  comment: true,
@@ -54880,8 +54887,8 @@ function formatToolSummary(toolName, input) {
54880
54887
  case "grep":
54881
54888
  case "search_files": {
54882
54889
  const pattern = String(input.pattern || "");
54883
- const path63 = input.path ? ` in ${input.path}` : "";
54884
- return `"${pattern}"${path63}`;
54890
+ const path65 = input.path ? ` in ${input.path}` : "";
54891
+ return `"${pattern}"${path65}`;
54885
54892
  }
54886
54893
  case "bash_exec": {
54887
54894
  const cmd = String(input.command || "");
@@ -54902,8 +54909,8 @@ function formatToolSummary(toolName, input) {
54902
54909
  function formatUrl(url) {
54903
54910
  try {
54904
54911
  const u = new URL(url);
54905
- const path63 = u.pathname.replace(/\/$/, "");
54906
- const display = path63 ? `${u.hostname} \u203A ${path63.slice(1)}` : u.hostname;
54912
+ const path65 = u.pathname.replace(/\/$/, "");
54913
+ const display = path65 ? `${u.hostname} \u203A ${path65.slice(1)}` : u.hostname;
54907
54914
  const max = Math.max(getTerminalWidth2() - 20, 50);
54908
54915
  return display.length > max ? display.slice(0, max - 1) + "\u2026" : display;
54909
54916
  } catch {
@@ -55083,6 +55090,12 @@ var RepeatedOutputSuppressor = class {
55083
55090
  // src/cli/repl/agent-loop.ts
55084
55091
  async function executeAgentTurn(session, userMessage, provider, toolRegistry, options = {}) {
55085
55092
  resetLineBuffer();
55093
+ session.runtime?.eventLog.record("turn.started", {
55094
+ sessionId: session.id,
55095
+ provider: session.config.provider.type,
55096
+ model: session.config.provider.model,
55097
+ mode: session.agentMode ?? (session.planMode ? "plan" : "build")
55098
+ });
55086
55099
  const messageSnapshot = session.messages.length;
55087
55100
  addMessage(session, { role: "user", content: userMessage });
55088
55101
  const executedTools = [];
@@ -55099,6 +55112,15 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
55099
55112
  const readOnlyModeEnabled = session.planMode === true || activeMode?.readOnly === true;
55100
55113
  const strictPlanModeEnabled = readOnlyModeEnabled && session.config.agent.planModeStrict === true;
55101
55114
  const outputOffloadObservationEnabled = session.config.agent.outputOffload === true;
55115
+ const recordToolSkipped = (toolCall, reason) => {
55116
+ session.runtime?.eventLog.record("tool.skipped", {
55117
+ sessionId: session.id,
55118
+ tool: toolCall.name,
55119
+ toolCallId: toolCall.id,
55120
+ reason
55121
+ });
55122
+ options.onToolSkipped?.(toolCall, reason);
55123
+ };
55102
55124
  const buildQualityMetrics = () => computeTurnQualityMetrics({
55103
55125
  iterationsUsed: iteration,
55104
55126
  maxIterations,
@@ -55110,7 +55132,7 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
55110
55132
  });
55111
55133
  const abortReturn = () => {
55112
55134
  session.messages.length = messageSnapshot;
55113
- return {
55135
+ const result2 = {
55114
55136
  content: finalContent,
55115
55137
  toolCalls: executedTools,
55116
55138
  usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
@@ -55119,6 +55141,14 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
55119
55141
  partialContent: finalContent || void 0,
55120
55142
  abortReason: "user_cancel"
55121
55143
  };
55144
+ session.runtime?.eventLog.record("turn.completed", {
55145
+ sessionId: session.id,
55146
+ aborted: true,
55147
+ toolsExecuted: executedTools.length,
55148
+ inputTokens: totalInputTokens,
55149
+ outputTokens: totalOutputTokens
55150
+ });
55151
+ return result2;
55122
55152
  };
55123
55153
  const allTools = toolRegistry.getToolDefinitionsForLLM();
55124
55154
  const tools = readOnlyModeEnabled ? strictPlanModeEnabled ? filterStrictPlanModeTools(allTools) : filterReadOnlyTools(allTools) : allTools;
@@ -55441,10 +55471,20 @@ ${tail}`;
55441
55471
  return abortReturn();
55442
55472
  }
55443
55473
  if (classification.kind === "provider_non_retryable") {
55474
+ session.runtime?.eventLog.record("turn.failed", {
55475
+ sessionId: session.id,
55476
+ error: classification.original instanceof Error ? classification.original.message : String(classification.original),
55477
+ toolsExecuted: executedTools.length
55478
+ });
55444
55479
  throw classification.original;
55445
55480
  }
55446
55481
  hadTurnError = true;
55447
55482
  const errorMsg = classification.message;
55483
+ session.runtime?.eventLog.record("turn.failed", {
55484
+ sessionId: session.id,
55485
+ error: errorMsg,
55486
+ toolsExecuted: executedTools.length
55487
+ });
55448
55488
  addMessage(session, {
55449
55489
  role: "assistant",
55450
55490
  content: `[Error during streaming: ${errorMsg}]`
@@ -55550,7 +55590,7 @@ ${tail}`;
55550
55590
  toolCall.id,
55551
55591
  `User explicitly requested MCP, but the model selected '${toolCall.name}' instead. Use an MCP tool (${availableMcpToolNames.join(", ")}) for this service access.`
55552
55592
  );
55553
- options.onToolSkipped?.(toolCall, "Use MCP tool instead of generic fetch");
55593
+ recordToolSkipped(toolCall, "Use MCP tool instead of generic fetch");
55554
55594
  continue;
55555
55595
  }
55556
55596
  if (shouldBlockShellMcpInspection(toolCall)) {
@@ -55558,7 +55598,7 @@ ${tail}`;
55558
55598
  toolCall.id,
55559
55599
  "Use the native mcp_list_servers tool to inspect configured and connected MCP services in this session. Do not shell out to `coco mcp ...` for runtime MCP diagnosis unless the user explicitly asked for the CLI command."
55560
55600
  );
55561
- options.onToolSkipped?.(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
55601
+ recordToolSkipped(toolCall, "Use mcp_list_servers instead of coco mcp CLI");
55562
55602
  continue;
55563
55603
  }
55564
55604
  if (strictPlanModeEnabled && !STRICT_PLAN_MODE_ALLOWED_TOOLS.has(toolCall.name)) {
@@ -55566,7 +55606,7 @@ ${tail}`;
55566
55606
  toolCall.id,
55567
55607
  `Blocked by strict plan mode: tool '${toolCall.name}' is not read-only`
55568
55608
  );
55569
- options.onToolSkipped?.(toolCall, "Blocked by strict plan mode");
55609
+ recordToolSkipped(toolCall, "Blocked by strict plan mode");
55570
55610
  continue;
55571
55611
  }
55572
55612
  const trustPattern = getTrustPattern(toolCall.name, toolCall.input);
@@ -55579,7 +55619,7 @@ ${tail}`;
55579
55619
  } catch (confirmError) {
55580
55620
  options.onAfterConfirmation?.();
55581
55621
  declinedTools.set(toolCall.id, "Confirmation failed");
55582
- options.onToolSkipped?.(
55622
+ recordToolSkipped(
55583
55623
  toolCall,
55584
55624
  `Confirmation failed: ${confirmError instanceof Error ? confirmError.message : String(confirmError)}`
55585
55625
  );
@@ -55598,7 +55638,7 @@ ${tail}`;
55598
55638
  switch (confirmResult) {
55599
55639
  case "no":
55600
55640
  declinedTools.set(toolCall.id, "User declined");
55601
- options.onToolSkipped?.(toolCall, "User declined");
55641
+ recordToolSkipped(toolCall, "User declined");
55602
55642
  continue;
55603
55643
  case "abort":
55604
55644
  turnAborted = true;
@@ -55628,15 +55668,33 @@ ${tail}`;
55628
55668
  onToolStart: (toolCall, _index, _total) => {
55629
55669
  const originalIndex = response.toolCalls.findIndex((tc) => tc.id === toolCall.id) + 1;
55630
55670
  options.onToolStart?.(toolCall, originalIndex, totalTools);
55671
+ session.runtime?.eventLog.record("tool.started", {
55672
+ sessionId: session.id,
55673
+ tool: toolCall.name,
55674
+ toolCallId: toolCall.id,
55675
+ index: originalIndex,
55676
+ total: totalTools
55677
+ });
55678
+ },
55679
+ onToolEnd: (result2) => {
55680
+ session.runtime?.eventLog.record("tool.completed", {
55681
+ sessionId: session.id,
55682
+ tool: result2.name,
55683
+ toolCallId: result2.id,
55684
+ success: result2.result.success,
55685
+ duration: result2.duration
55686
+ });
55687
+ options.onToolEnd?.(result2);
55688
+ },
55689
+ onToolSkipped: (toolCall, reason) => {
55690
+ recordToolSkipped(toolCall, reason);
55631
55691
  },
55632
- onToolEnd: options.onToolEnd,
55633
- onToolSkipped: options.onToolSkipped,
55634
55692
  signal: options.signal,
55635
55693
  onPathAccessDenied: async (dirPath) => {
55636
55694
  options.onBeforeConfirmation?.();
55637
- const result = await promptAllowPath(dirPath);
55695
+ const result2 = await promptAllowPath(dirPath);
55638
55696
  options.onAfterConfirmation?.();
55639
- return result;
55697
+ return result2;
55640
55698
  },
55641
55699
  // Pass hooks through so PreToolUse/PostToolUse hooks fire during execution
55642
55700
  hookRegistry: options.hookRegistry,
@@ -55910,13 +55968,21 @@ I have reached the maximum iteration limit (${maxIterations}). The task may be i
55910
55968
  }
55911
55969
  }
55912
55970
  options.onStream?.({ type: "done" });
55913
- return {
55971
+ const result = {
55914
55972
  content: finalContent,
55915
55973
  toolCalls: executedTools,
55916
55974
  usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
55917
55975
  quality: buildQualityMetrics(),
55918
55976
  aborted: false
55919
55977
  };
55978
+ session.runtime?.eventLog.record("turn.completed", {
55979
+ sessionId: session.id,
55980
+ aborted: false,
55981
+ toolsExecuted: executedTools.length,
55982
+ inputTokens: totalInputTokens,
55983
+ outputTokens: totalOutputTokens
55984
+ });
55985
+ return result;
55920
55986
  }
55921
55987
  function formatAbortSummary(executedTools) {
55922
55988
  if (executedTools.length === 0) return null;
@@ -56307,6 +56373,9 @@ var statsCommand = {
56307
56373
  console.log(` Model: ${chalk.cyan(session.config.provider.model)}`);
56308
56374
  console.log(` Endpoint: ${chalk.yellow(runtime.endpoint)}`);
56309
56375
  console.log(` Mode: ${session.agentMode ?? (session.planMode ? "plan" : "build")}`);
56376
+ if (session.runtime) {
56377
+ console.log(` Runtime events: ${chalk.yellow(String(session.runtime.eventLog.count()))}`);
56378
+ }
56310
56379
  console.log();
56311
56380
  console.log(chalk.dim(" Messages:"));
56312
56381
  console.log(chalk.dim(` user: ${userMessages}`));
@@ -57302,6 +57371,295 @@ function createSpinner(message) {
57302
57371
  // src/cli/repl/index.ts
57303
57372
  init_error_resilience();
57304
57373
  init_providers();
57374
+
57375
+ // src/runtime/agent-runtime.ts
57376
+ init_env();
57377
+ var InMemoryEventLog = class {
57378
+ events = [];
57379
+ record(type, data = {}) {
57380
+ const event = {
57381
+ id: randomUUID(),
57382
+ type,
57383
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
57384
+ data
57385
+ };
57386
+ this.events.push(event);
57387
+ return event;
57388
+ }
57389
+ list() {
57390
+ return [...this.events];
57391
+ }
57392
+ count() {
57393
+ return this.events.length;
57394
+ }
57395
+ clear() {
57396
+ this.events = [];
57397
+ }
57398
+ };
57399
+ var FileEventLog = class {
57400
+ constructor(filePath) {
57401
+ this.filePath = filePath;
57402
+ try {
57403
+ mkdirSync(dirname(filePath), { recursive: true });
57404
+ } catch {
57405
+ this.writable = false;
57406
+ }
57407
+ }
57408
+ filePath;
57409
+ memory = new InMemoryEventLog();
57410
+ writable = true;
57411
+ record(type, data = {}) {
57412
+ const event = this.memory.record(type, data);
57413
+ if (this.writable) {
57414
+ try {
57415
+ appendFileSync(this.filePath, JSON.stringify(event) + "\n", "utf-8");
57416
+ } catch {
57417
+ this.writable = false;
57418
+ }
57419
+ }
57420
+ return event;
57421
+ }
57422
+ list() {
57423
+ if (!this.writable) return this.memory.list();
57424
+ try {
57425
+ const raw = readFileSync(this.filePath, "utf-8");
57426
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
57427
+ try {
57428
+ return [JSON.parse(line)];
57429
+ } catch {
57430
+ return [];
57431
+ }
57432
+ });
57433
+ } catch {
57434
+ return this.memory.list();
57435
+ }
57436
+ }
57437
+ count() {
57438
+ return this.list().length;
57439
+ }
57440
+ clear() {
57441
+ this.memory.clear();
57442
+ if (this.writable) {
57443
+ try {
57444
+ writeFileSync(this.filePath, "", "utf-8");
57445
+ } catch {
57446
+ this.writable = false;
57447
+ }
57448
+ }
57449
+ }
57450
+ };
57451
+ function createEventLog() {
57452
+ return new InMemoryEventLog();
57453
+ }
57454
+ function createFileEventLog(filePath) {
57455
+ return new FileEventLog(filePath);
57456
+ }
57457
+
57458
+ // src/runtime/permission-policy.ts
57459
+ var READ_ONLY_CATEGORIES = /* @__PURE__ */ new Set(["search", "web", "document"]);
57460
+ var WRITE_CATEGORIES = /* @__PURE__ */ new Set(["file", "git", "test", "build", "memory"]);
57461
+ var READ_ONLY_TOOL_NAMES = /* @__PURE__ */ new Set([
57462
+ "glob",
57463
+ "read_file",
57464
+ "list_dir",
57465
+ "tree",
57466
+ "grep",
57467
+ "find_in_file",
57468
+ "semantic_search",
57469
+ "codebase_map",
57470
+ "repo_context",
57471
+ "lsp_status",
57472
+ "lsp_document_symbols",
57473
+ "lsp_workspace_symbols",
57474
+ "lsp_definition",
57475
+ "lsp_references",
57476
+ "git_status",
57477
+ "git_log",
57478
+ "git_diff",
57479
+ "git_show",
57480
+ "git_branch",
57481
+ "recall_memory",
57482
+ "list_memories",
57483
+ "list_checkpoints",
57484
+ "spawnSimpleAgent",
57485
+ "checkAgentCapability"
57486
+ ]);
57487
+ var WRITE_CAPABLE_TOOL_NAMES = /* @__PURE__ */ new Set(["run_linter"]);
57488
+ var DESTRUCTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
57489
+ "bash_exec",
57490
+ "write_file",
57491
+ "edit_file",
57492
+ "delete_file",
57493
+ "restore_checkpoint",
57494
+ "git_commit",
57495
+ "git_push"
57496
+ ]);
57497
+ function riskForTool(tool) {
57498
+ if (READ_ONLY_TOOL_NAMES.has(tool.name)) return "read-only";
57499
+ if (DESTRUCTIVE_TOOL_NAMES.has(tool.name)) return "destructive";
57500
+ if (WRITE_CAPABLE_TOOL_NAMES.has(tool.name)) return "write";
57501
+ if (tool.category === "web") return "network";
57502
+ if (WRITE_CATEGORIES.has(tool.category)) return "write";
57503
+ if (tool.category === "quality") return "write";
57504
+ return "read-only";
57505
+ }
57506
+ var DefaultPermissionPolicy = class {
57507
+ canExecuteTool(mode, tool) {
57508
+ const definition = getAgentMode(mode);
57509
+ const risk = riskForTool(tool);
57510
+ const readOnlyTool = READ_ONLY_TOOL_NAMES.has(tool.name) || READ_ONLY_CATEGORIES.has(tool.category);
57511
+ if (definition.readOnly && !readOnlyTool) {
57512
+ return {
57513
+ allowed: false,
57514
+ reason: `${definition.label} mode is read-only; ${tool.name} is a ${tool.category} tool.`,
57515
+ risk
57516
+ };
57517
+ }
57518
+ if (risk === "destructive") {
57519
+ return {
57520
+ allowed: true,
57521
+ requiresConfirmation: true,
57522
+ reason: `${tool.name} can change repository state and should be confirmed.`,
57523
+ risk
57524
+ };
57525
+ }
57526
+ return { allowed: true, risk };
57527
+ }
57528
+ };
57529
+ function createPermissionPolicy() {
57530
+ return new DefaultPermissionPolicy();
57531
+ }
57532
+
57533
+ // src/runtime/provider-registry.ts
57534
+ init_providers();
57535
+ init_catalog();
57536
+ init_runtime_capabilities();
57537
+ var ProviderRegistry = class {
57538
+ listProviders() {
57539
+ return Object.values(PROVIDER_CATALOG);
57540
+ }
57541
+ getProvider(provider) {
57542
+ return getProviderCatalogEntry(provider);
57543
+ }
57544
+ listModels(provider) {
57545
+ return this.getProvider(provider).models;
57546
+ }
57547
+ getModel(provider, model2) {
57548
+ return getCatalogModel(provider, model2);
57549
+ }
57550
+ getDefaultModel(provider) {
57551
+ return getCatalogDefaultModel(provider);
57552
+ }
57553
+ getRecommendedModel(provider) {
57554
+ return getCatalogRecommendedModel(provider);
57555
+ }
57556
+ getCapability(provider, model2) {
57557
+ return getProviderRuntimeCapability(provider, model2);
57558
+ }
57559
+ async createProvider(provider, config = {}) {
57560
+ return createProvider(provider, config);
57561
+ }
57562
+ async probe(provider, model2, checkAvailability) {
57563
+ return probeProviderRuntimeCapability(provider, model2, checkAvailability);
57564
+ }
57565
+ };
57566
+ function createProviderRegistry() {
57567
+ return new ProviderRegistry();
57568
+ }
57569
+
57570
+ // src/runtime/agent-runtime.ts
57571
+ var AgentRuntime = class {
57572
+ constructor(options) {
57573
+ this.options = options;
57574
+ this.providerRegistry = createProviderRegistry();
57575
+ this.toolRegistry = options.toolRegistry ?? createFullToolRegistry();
57576
+ this.sessionStore = options.sessionStore ?? createSessionStore({});
57577
+ this.permissionPolicy = options.permissionPolicy ?? createPermissionPolicy();
57578
+ this.eventLog = options.eventLog ?? (options.eventLogPath ? createFileEventLog(options.eventLogPath) : createEventLog());
57579
+ this.providerType = options.providerType;
57580
+ this.model = options.model ?? options.providerConfig?.model ?? getDefaultModel(options.providerType);
57581
+ }
57582
+ options;
57583
+ providerRegistry;
57584
+ toolRegistry;
57585
+ sessionStore;
57586
+ permissionPolicy;
57587
+ eventLog;
57588
+ providerType;
57589
+ model;
57590
+ async initialize() {
57591
+ const providerInjected = Boolean(this.options.provider);
57592
+ const provider = this.options.provider ?? await this.providerRegistry.createProvider(this.providerType, {
57593
+ ...this.options.providerConfig,
57594
+ model: this.getModel()
57595
+ });
57596
+ this.publishToGlobalBridge(provider);
57597
+ this.eventLog.record(providerInjected ? "provider.attached" : "provider.created", {
57598
+ provider: this.providerType,
57599
+ model: this.getModel(),
57600
+ createdByRuntime: !providerInjected
57601
+ });
57602
+ this.eventLog.record("runtime.initialized", { snapshot: this.snapshot() });
57603
+ }
57604
+ getModel() {
57605
+ return this.model;
57606
+ }
57607
+ updateProvider(providerType, model2, provider) {
57608
+ this.providerType = providerType;
57609
+ this.model = model2 ?? getDefaultModel(providerType);
57610
+ this.publishToGlobalBridge(provider);
57611
+ this.eventLog.record("provider.updated", {
57612
+ provider: this.providerType,
57613
+ model: this.model
57614
+ });
57615
+ }
57616
+ publishToGlobalBridge(provider) {
57617
+ if (this.options.publishToGlobalBridge !== true) return;
57618
+ setAgentProvider(provider);
57619
+ setAgentToolRegistry(this.toolRegistry);
57620
+ }
57621
+ snapshot() {
57622
+ const capability = this.providerRegistry.getCapability(this.providerType, this.getModel());
57623
+ const toolNames = this.toolRegistry.getAll().map((tool) => tool.name).sort();
57624
+ return {
57625
+ provider: {
57626
+ type: this.providerType,
57627
+ model: this.getModel(),
57628
+ capability
57629
+ },
57630
+ tools: {
57631
+ count: toolNames.length,
57632
+ names: toolNames
57633
+ },
57634
+ modes: listAgentModes()
57635
+ };
57636
+ }
57637
+ assertToolAllowed(mode, toolName) {
57638
+ const tool = this.toolRegistry.get(toolName);
57639
+ if (!tool) {
57640
+ this.eventLog.record("tool.blocked", {
57641
+ mode,
57642
+ tool: toolName,
57643
+ reason: "Tool not registered."
57644
+ });
57645
+ return false;
57646
+ }
57647
+ const decision = this.permissionPolicy.canExecuteTool(mode, tool);
57648
+ this.eventLog.record(decision.allowed ? "tool.allowed" : "tool.blocked", {
57649
+ mode,
57650
+ tool: toolName,
57651
+ ...decision
57652
+ });
57653
+ return decision.allowed;
57654
+ }
57655
+ };
57656
+ async function createAgentRuntime(options) {
57657
+ const runtime = new AgentRuntime(options);
57658
+ await runtime.initialize();
57659
+ return runtime;
57660
+ }
57661
+
57662
+ // src/cli/repl/index.ts
57305
57663
  init_version();
57306
57664
  init_trust_store();
57307
57665
 
@@ -57854,9 +58212,15 @@ async function startRepl(options = {}) {
57854
58212
  loadFullAccessPreference2(),
57855
58213
  loadFullPowerRiskPreference2()
57856
58214
  ]);
57857
- const toolRegistry = createFullToolRegistry();
57858
- setAgentProvider(provider);
57859
- setAgentToolRegistry(toolRegistry);
58215
+ const runtime = await createAgentRuntime({
58216
+ providerType: internalProviderId,
58217
+ model: session.config.provider.model || void 0,
58218
+ provider,
58219
+ eventLogPath: path39__default.join(projectPath, ".coco", "events", `${session.id}.jsonl`),
58220
+ publishToGlobalBridge: true
58221
+ });
58222
+ session.runtime = runtime;
58223
+ const toolRegistry = runtime.toolRegistry;
57860
58224
  try {
57861
58225
  const { createUnifiedSkillRegistry: createUnifiedSkillRegistry2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
57862
58226
  const { getBuiltinSkillsForDiscovery: getBuiltinSkillsForDiscovery2 } = await Promise.resolve().then(() => (init_skills2(), skills_exports2));
@@ -58040,7 +58404,7 @@ async function startRepl(options = {}) {
58040
58404
  let consecutiveErrors = 0;
58041
58405
  const AUTO_SWITCH_THRESHOLD = 2;
58042
58406
  const autoSwitchHistory = /* @__PURE__ */ new Set();
58043
- const enableAutoSwitchProvider = session.config.agent.enableAutoSwitchProvider === true;
58407
+ const enableAutoSwitchProvider = session.config.agent?.enableAutoSwitchProvider === true;
58044
58408
  const buildReplayMessage = (message) => {
58045
58409
  if (typeof message === "string") {
58046
58410
  const trimmed = message.trim();
@@ -58143,7 +58507,8 @@ async function startRepl(options = {}) {
58143
58507
  provider = nextProvider;
58144
58508
  session.config.provider.type = candidate;
58145
58509
  session.config.provider.model = getDefaultModel(candidate);
58146
- setAgentProvider(provider);
58510
+ runtime.updateProvider(nextInternalId, session.config.provider.model, provider);
58511
+ session.runtime = runtime;
58147
58512
  initializeContextManager(session, provider);
58148
58513
  llmClassifier = createLLMClassifier2(provider);
58149
58514
  autoSwitchHistory.add(edge);
@@ -58205,7 +58570,12 @@ async function startRepl(options = {}) {
58205
58570
  project: session.config.provider.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"],
58206
58571
  location: session.config.provider.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"]
58207
58572
  });
58208
- setAgentProvider(provider);
58573
+ runtime.updateProvider(
58574
+ newInternalId,
58575
+ session.config.provider.model || void 0,
58576
+ provider
58577
+ );
58578
+ session.runtime = runtime;
58209
58579
  initializeContextManager(session, provider);
58210
58580
  } catch (err) {
58211
58581
  session.config.provider.type = prevProviderType;
@@ -59118,9 +59488,15 @@ ${stdinContent}
59118
59488
  const provider = await createProvider(providerType, {
59119
59489
  model: session.config.provider.model || void 0
59120
59490
  });
59121
- const toolRegistry = createFullToolRegistry();
59122
- setAgentProvider(provider);
59123
- setAgentToolRegistry(toolRegistry);
59491
+ const runtime = await createAgentRuntime({
59492
+ providerType,
59493
+ model: session.config.provider.model || void 0,
59494
+ provider,
59495
+ eventLogPath: path39__default.join(options.projectPath, ".coco", "events", `${session.id}.jsonl`),
59496
+ publishToGlobalBridge: true
59497
+ });
59498
+ session.runtime = runtime;
59499
+ const toolRegistry = runtime.toolRegistry;
59124
59500
  await loadAllowedPaths(options.projectPath);
59125
59501
  await initializeContextManager(session, provider);
59126
59502
  const result = await executeAgentTurn(session, task, provider, toolRegistry, {