@corbat-tech/coco 2.36.0 → 2.38.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
@@ -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, readdir, rm, constants } from 'fs/promises';
13
+ import fs35__default, { mkdir, writeFile, readFile, access, constants, readdir, rm } from 'fs/promises';
14
14
  import JSON5 from 'json5';
15
15
  import * as crypto from 'crypto';
16
16
  import { randomUUID, randomBytes, createHash } from 'crypto';
@@ -40857,9 +40857,6 @@ function getSessionStore() {
40857
40857
  }
40858
40858
  return defaultStore;
40859
40859
  }
40860
- function createSessionStore(config) {
40861
- return new SessionStore(config);
40862
- }
40863
40860
 
40864
40861
  // src/cli/repl/commands/resume.ts
40865
40862
  function formatRelativeTime2(date) {
@@ -43230,6 +43227,145 @@ Response format (JSON only, no prose):
43230
43227
  return spec;
43231
43228
  }
43232
43229
 
43230
+ // src/runtime/multi-agent.ts
43231
+ function createAgentArtifact(input) {
43232
+ return {
43233
+ ...input,
43234
+ id: input.id ?? `artifact-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
43235
+ createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
43236
+ };
43237
+ }
43238
+ function createSummaryArtifact(output, metadata = {}) {
43239
+ return createAgentArtifact({
43240
+ kind: "summary",
43241
+ content: output,
43242
+ title: metadata.title ?? "Agent summary",
43243
+ agentRunId: metadata.agentRunId,
43244
+ taskId: metadata.taskId
43245
+ });
43246
+ }
43247
+ function normalizeAgentRunResult(input) {
43248
+ const completedAt = input.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
43249
+ const startedAt = input.startedAt ?? completedAt;
43250
+ const status = input.status ?? (input.success ? "completed" : "failed");
43251
+ const artifacts = input.artifacts && input.artifacts.length > 0 ? input.artifacts.map(cloneArtifact) : [createSummaryArtifact(input.output, { agentRunId: input.id, taskId: input.taskId })];
43252
+ return {
43253
+ id: input.id,
43254
+ taskId: input.taskId,
43255
+ role: input.role,
43256
+ status,
43257
+ success: input.success,
43258
+ output: input.output,
43259
+ artifacts,
43260
+ toolsUsed: [...input.toolsUsed ?? []],
43261
+ turns: input.turns ?? 0,
43262
+ durationMs: input.durationMs ?? 0,
43263
+ usage: input.usage,
43264
+ error: input.error,
43265
+ startedAt,
43266
+ completedAt,
43267
+ metadata: input.metadata
43268
+ };
43269
+ }
43270
+ function validateAgentGraph(graph) {
43271
+ const issues = [];
43272
+ const nodeIds = /* @__PURE__ */ new Set();
43273
+ const gateIds = new Set((graph.gates ?? []).map((gate) => gate.id));
43274
+ if (graph.parallelism !== void 0 && graph.parallelism < 1) {
43275
+ issues.push({
43276
+ code: "invalid-parallelism",
43277
+ message: "Graph parallelism must be greater than zero."
43278
+ });
43279
+ }
43280
+ for (const node of graph.nodes) {
43281
+ if (nodeIds.has(node.id)) {
43282
+ issues.push({
43283
+ code: "duplicate-node",
43284
+ message: `Duplicate graph node '${node.id}'.`,
43285
+ nodeId: node.id
43286
+ });
43287
+ }
43288
+ nodeIds.add(node.id);
43289
+ if (node.retryPolicy && node.retryPolicy.maxAttempts < 1) {
43290
+ issues.push({
43291
+ code: "invalid-retry-policy",
43292
+ message: `Node '${node.id}' retry policy must allow at least one attempt.`,
43293
+ nodeId: node.id
43294
+ });
43295
+ }
43296
+ for (const dep of node.dependsOn ?? []) {
43297
+ if (!nodeIds.has(dep) && !graph.nodes.some((candidate) => candidate.id === dep)) {
43298
+ issues.push({
43299
+ code: "missing-dependency",
43300
+ message: `Node '${node.id}' depends on missing node '${dep}'.`,
43301
+ nodeId: node.id
43302
+ });
43303
+ }
43304
+ }
43305
+ for (const gate of node.gates ?? []) {
43306
+ if (!gateIds.has(gate)) {
43307
+ issues.push({
43308
+ code: "missing-gate",
43309
+ message: `Node '${node.id}' references missing gate '${gate}'.`,
43310
+ nodeId: node.id,
43311
+ gateId: gate
43312
+ });
43313
+ }
43314
+ }
43315
+ }
43316
+ for (const edge of graph.edges ?? []) {
43317
+ if (!nodeIds.has(edge.from)) {
43318
+ issues.push({
43319
+ code: "missing-edge-node",
43320
+ message: `Graph edge references missing source node '${edge.from}'.`,
43321
+ nodeId: edge.from
43322
+ });
43323
+ }
43324
+ if (!nodeIds.has(edge.to)) {
43325
+ issues.push({
43326
+ code: "missing-edge-node",
43327
+ message: `Graph edge references missing target node '${edge.to}'.`,
43328
+ nodeId: edge.to
43329
+ });
43330
+ }
43331
+ }
43332
+ const levels = buildExecutionLevels(graph, issues);
43333
+ return { valid: issues.length === 0, issues, levels };
43334
+ }
43335
+ function buildExecutionLevels(graph, issues) {
43336
+ const dependencies = /* @__PURE__ */ new Map();
43337
+ for (const node of graph.nodes) {
43338
+ dependencies.set(node.id, new Set(node.dependsOn ?? []));
43339
+ }
43340
+ for (const edge of graph.edges ?? []) {
43341
+ if (dependencies.has(edge.to)) {
43342
+ dependencies.get(edge.to).add(edge.from);
43343
+ }
43344
+ }
43345
+ const completed = /* @__PURE__ */ new Set();
43346
+ const levels = [];
43347
+ while (completed.size < dependencies.size) {
43348
+ const level = [...dependencies.entries()].filter(([id, deps]) => !completed.has(id) && [...deps].every((dep) => completed.has(dep))).map(([id]) => id);
43349
+ if (level.length === 0) {
43350
+ const remaining = [...dependencies.keys()].filter((id) => !completed.has(id));
43351
+ issues.push({
43352
+ code: "cycle",
43353
+ message: `Graph contains a cycle involving: ${remaining.join(", ")}.`
43354
+ });
43355
+ return levels;
43356
+ }
43357
+ for (const id of level) completed.add(id);
43358
+ levels.push(level);
43359
+ }
43360
+ return levels;
43361
+ }
43362
+ function cloneArtifact(artifact) {
43363
+ return {
43364
+ ...artifact,
43365
+ metadata: artifact.metadata ? { ...artifact.metadata } : void 0
43366
+ };
43367
+ }
43368
+
43233
43369
  // src/agents/executor.ts
43234
43370
  var AgentExecutor = class {
43235
43371
  constructor(provider, toolRegistry) {
@@ -43243,6 +43379,7 @@ var AgentExecutor = class {
43243
43379
  */
43244
43380
  async execute(agent, task) {
43245
43381
  const startTime = Date.now();
43382
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
43246
43383
  const toolsUsed = /* @__PURE__ */ new Set();
43247
43384
  const messages = [
43248
43385
  {
@@ -43253,6 +43390,8 @@ var AgentExecutor = class {
43253
43390
  const agentToolDefs = this.getToolDefinitionsForAgent(agent.allowedTools);
43254
43391
  let turn = 0;
43255
43392
  let totalTokens = 0;
43393
+ let totalInputTokens = 0;
43394
+ let totalOutputTokens = 0;
43256
43395
  while (turn < agent.maxTurns) {
43257
43396
  turn++;
43258
43397
  try {
@@ -43261,16 +43400,23 @@ var AgentExecutor = class {
43261
43400
  system: agent.systemPrompt
43262
43401
  });
43263
43402
  const usage = response.usage;
43403
+ totalInputTokens += usage?.inputTokens || 0;
43404
+ totalOutputTokens += usage?.outputTokens || 0;
43264
43405
  totalTokens += (usage?.inputTokens || 0) + (usage?.outputTokens || 0);
43265
43406
  if (response.stopReason !== "tool_use" || response.toolCalls.length === 0) {
43266
- return {
43407
+ return this.toAgentResult({
43408
+ agent,
43409
+ task,
43267
43410
  output: response.content,
43268
43411
  success: true,
43269
43412
  turns: turn,
43270
43413
  toolsUsed: Array.from(toolsUsed),
43271
43414
  tokensUsed: totalTokens,
43272
- duration: Date.now() - startTime
43273
- };
43415
+ inputTokens: totalInputTokens,
43416
+ outputTokens: totalOutputTokens,
43417
+ duration: Date.now() - startTime,
43418
+ startedAt
43419
+ });
43274
43420
  }
43275
43421
  const assistantContent = [];
43276
43422
  if (response.content) {
@@ -43316,24 +43462,38 @@ var AgentExecutor = class {
43316
43462
  content: toolResults
43317
43463
  });
43318
43464
  } catch (error) {
43319
- return {
43320
- output: `Agent error on turn ${turn}: ${error instanceof Error ? error.message : String(error)}`,
43465
+ const output2 = `Agent error on turn ${turn}: ${error instanceof Error ? error.message : String(error)}`;
43466
+ return this.toAgentResult({
43467
+ agent,
43468
+ task,
43469
+ output: output2,
43321
43470
  success: false,
43322
43471
  turns: turn,
43323
43472
  toolsUsed: Array.from(toolsUsed),
43324
43473
  tokensUsed: totalTokens,
43325
- duration: Date.now() - startTime
43326
- };
43474
+ inputTokens: totalInputTokens,
43475
+ outputTokens: totalOutputTokens,
43476
+ duration: Date.now() - startTime,
43477
+ startedAt,
43478
+ error: output2
43479
+ });
43327
43480
  }
43328
43481
  }
43329
- return {
43330
- output: "Agent reached maximum turns without completing task",
43482
+ const output = "Agent reached maximum turns without completing task";
43483
+ return this.toAgentResult({
43484
+ agent,
43485
+ task,
43486
+ output,
43331
43487
  success: false,
43332
43488
  turns: turn,
43333
43489
  toolsUsed: Array.from(toolsUsed),
43334
43490
  tokensUsed: totalTokens,
43335
- duration: Date.now() - startTime
43336
- };
43491
+ inputTokens: totalInputTokens,
43492
+ outputTokens: totalOutputTokens,
43493
+ duration: Date.now() - startTime,
43494
+ startedAt,
43495
+ error: output
43496
+ });
43337
43497
  }
43338
43498
  /**
43339
43499
  * Build task prompt with context
@@ -43359,7 +43519,36 @@ Complete this task autonomously using the available tools. When done, provide a
43359
43519
  if (allowedToolNames.length === 0) return allDefs;
43360
43520
  return allDefs.filter((def) => allowedToolNames.includes(def.name));
43361
43521
  }
43522
+ toAgentResult(input) {
43523
+ const structuredResult = normalizeAgentRunResult({
43524
+ id: `${input.task.id}-${Date.now().toString(36)}`,
43525
+ taskId: input.task.id,
43526
+ role: normalizeRole(input.agent.role),
43527
+ success: input.success,
43528
+ output: input.output,
43529
+ turns: input.turns,
43530
+ toolsUsed: input.toolsUsed,
43531
+ durationMs: input.duration,
43532
+ startedAt: input.startedAt,
43533
+ usage: { inputTokens: input.inputTokens, outputTokens: input.outputTokens },
43534
+ error: input.error,
43535
+ metadata: { legacyRole: input.agent.role }
43536
+ });
43537
+ return {
43538
+ output: input.output,
43539
+ success: input.success,
43540
+ turns: input.turns,
43541
+ toolsUsed: input.toolsUsed,
43542
+ tokensUsed: input.tokensUsed,
43543
+ duration: input.duration,
43544
+ artifacts: structuredResult.artifacts,
43545
+ structuredResult
43546
+ };
43547
+ }
43362
43548
  };
43549
+ function normalizeRole(role) {
43550
+ return role === "researcher" || role === "architect" || role === "editor" || role === "coder" || role === "tester" || role === "reviewer" || role === "optimizer" || role === "planner" ? role : "coder";
43551
+ }
43363
43552
  var AGENT_ROLES = {
43364
43553
  researcher: {
43365
43554
  role: "researcher",
@@ -44197,6 +44386,9 @@ var testTools = [runTestsTool, getCoverageTool, runTestFileTool];
44197
44386
  // src/tools/index.ts
44198
44387
  init_registry4();
44199
44388
 
44389
+ // src/tools/profiles.ts
44390
+ init_registry4();
44391
+
44200
44392
  // src/tools/file.ts
44201
44393
  init_registry4();
44202
44394
  init_errors();
@@ -45823,11 +46015,15 @@ var AgentManager = class extends EventEmitter {
45823
46015
  failedAgent.status = "failed";
45824
46016
  failedAgent.error = error;
45825
46017
  failedAgent.completedAt = /* @__PURE__ */ new Date();
45826
- return {
46018
+ return this.buildResult({
45827
46019
  agent: failedAgent,
45828
46020
  success: false,
45829
- output: error
45830
- };
46021
+ output: error,
46022
+ turns: 0,
46023
+ toolsUsed: [],
46024
+ startedAt: failedAgent.createdAt.toISOString(),
46025
+ usage: { inputTokens: 0, outputTokens: 0 }
46026
+ });
45831
46027
  }
45832
46028
  const agent = this.createAgent(type, task);
45833
46029
  this.activeAgents.set(agent.id, agent);
@@ -45879,11 +46075,15 @@ var AgentManager = class extends EventEmitter {
45879
46075
  options.onStatusChange?.(agent);
45880
46076
  this.logger.error(`Agent ${agent.id} failed unexpectedly`, { error: errorMessage });
45881
46077
  this.emitEvent("fail", agent);
45882
- return {
46078
+ return this.buildResult({
45883
46079
  agent,
45884
46080
  success: false,
45885
- output: errorMessage
45886
- };
46081
+ output: errorMessage,
46082
+ turns: 0,
46083
+ toolsUsed: [],
46084
+ startedAt: agent.createdAt.toISOString(),
46085
+ usage: { inputTokens: 0, outputTokens: 0 }
46086
+ });
45887
46087
  }
45888
46088
  }
45889
46089
  /**
@@ -46004,6 +46204,8 @@ var AgentManager = class extends EventEmitter {
46004
46204
  let finalOutput = "";
46005
46205
  let iteration = 0;
46006
46206
  const maxTurns = config.maxTurns ?? 10;
46207
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
46208
+ const toolsUsed = /* @__PURE__ */ new Set();
46007
46209
  while (iteration < maxTurns) {
46008
46210
  iteration++;
46009
46211
  if (options.signal?.aborted) {
@@ -46012,12 +46214,15 @@ var AgentManager = class extends EventEmitter {
46012
46214
  agent.completedAt = /* @__PURE__ */ new Date();
46013
46215
  this.moveToCompleted(agent.id);
46014
46216
  options.onStatusChange?.(agent);
46015
- return {
46217
+ return this.buildResult({
46016
46218
  agent,
46017
46219
  success: false,
46018
46220
  output: "Agent execution was aborted",
46221
+ turns: iteration,
46222
+ toolsUsed: Array.from(toolsUsed),
46223
+ startedAt,
46019
46224
  usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
46020
- };
46225
+ });
46021
46226
  }
46022
46227
  const response = await this.provider.chatWithTools(messages, {
46023
46228
  system: config.systemPrompt,
@@ -46034,7 +46239,7 @@ var AgentManager = class extends EventEmitter {
46034
46239
  messages.push({ role: "assistant", content: response.content });
46035
46240
  break;
46036
46241
  }
46037
- const toolResults = await this.executeToolCalls(response.toolCalls, config);
46242
+ const toolResults = await this.executeToolCalls(response.toolCalls, config, toolsUsed);
46038
46243
  const toolUses = response.toolCalls.map((tc) => ({
46039
46244
  type: "tool_use",
46040
46245
  id: tc.id,
@@ -46058,12 +46263,15 @@ var AgentManager = class extends EventEmitter {
46058
46263
  iterations: iteration,
46059
46264
  outputLength: finalOutput.length
46060
46265
  });
46061
- return {
46266
+ return this.buildResult({
46062
46267
  agent,
46063
46268
  success: true,
46064
46269
  output: finalOutput,
46270
+ turns: iteration,
46271
+ toolsUsed: Array.from(toolsUsed),
46272
+ startedAt,
46065
46273
  usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
46066
- };
46274
+ });
46067
46275
  }
46068
46276
  /**
46069
46277
  * Get tool definitions filtered for the agent's allowed tools
@@ -46076,10 +46284,11 @@ var AgentManager = class extends EventEmitter {
46076
46284
  /**
46077
46285
  * Execute tool calls and return results
46078
46286
  */
46079
- async executeToolCalls(toolCalls, config) {
46287
+ async executeToolCalls(toolCalls, config, toolsUsed) {
46080
46288
  const results = [];
46081
46289
  const allowedTools = new Set(config.tools);
46082
46290
  for (const toolCall of toolCalls) {
46291
+ toolsUsed.add(toolCall.name);
46083
46292
  if (!allowedTools.has(toolCall.name)) {
46084
46293
  results.push({
46085
46294
  type: "tool_result",
@@ -46120,6 +46329,33 @@ var AgentManager = class extends EventEmitter {
46120
46329
  this.abortControllers.delete(agentId);
46121
46330
  }
46122
46331
  }
46332
+ buildResult(input) {
46333
+ const structuredResult = normalizeAgentRunResult({
46334
+ id: `${input.agent.id}-run`,
46335
+ taskId: input.agent.id,
46336
+ role: agentTypeToRuntimeRole(input.agent.type),
46337
+ success: input.success,
46338
+ output: input.output,
46339
+ turns: input.turns,
46340
+ toolsUsed: input.toolsUsed,
46341
+ usage: input.usage,
46342
+ startedAt: input.startedAt,
46343
+ durationMs: input.agent.completedAt ? input.agent.completedAt.getTime() - input.agent.createdAt.getTime() : 0,
46344
+ status: input.success ? "completed" : "failed",
46345
+ error: input.success ? void 0 : input.agent.error,
46346
+ metadata: { agentType: input.agent.type }
46347
+ });
46348
+ return {
46349
+ agent: input.agent,
46350
+ success: input.success,
46351
+ output: input.output,
46352
+ artifacts: structuredResult.artifacts,
46353
+ structuredResult,
46354
+ toolsUsed: input.toolsUsed,
46355
+ turns: input.turns,
46356
+ usage: input.usage
46357
+ };
46358
+ }
46123
46359
  /**
46124
46360
  * Emit an agent event
46125
46361
  */
@@ -46129,6 +46365,31 @@ var AgentManager = class extends EventEmitter {
46129
46365
  this.emit("agent", event);
46130
46366
  }
46131
46367
  };
46368
+ function agentTypeToRuntimeRole(type) {
46369
+ switch (type) {
46370
+ case "explore":
46371
+ return "researcher";
46372
+ case "plan":
46373
+ return "planner";
46374
+ case "test":
46375
+ case "e2e":
46376
+ case "tdd":
46377
+ return "tester";
46378
+ case "debug":
46379
+ case "refactor":
46380
+ return "coder";
46381
+ case "review":
46382
+ return "reviewer";
46383
+ case "architect":
46384
+ return "architect";
46385
+ case "security":
46386
+ return "security";
46387
+ case "docs":
46388
+ return "docs";
46389
+ case "database":
46390
+ return "database";
46391
+ }
46392
+ }
46132
46393
 
46133
46394
  // src/agents/provider-bridge.ts
46134
46395
  var agentProvider = null;
@@ -52606,7 +52867,7 @@ var repoMapCommand = {
52606
52867
  }
52607
52868
  };
52608
52869
 
52609
- // src/cli/repl/modes.ts
52870
+ // src/runtime/agent-modes.ts
52610
52871
  var AGENT_MODES = {
52611
52872
  ask: {
52612
52873
  id: "ask",
@@ -57374,6 +57635,39 @@ init_providers();
57374
57635
 
57375
57636
  // src/runtime/agent-runtime.ts
57376
57637
  init_env();
57638
+
57639
+ // src/runtime/default-turn-runner.ts
57640
+ var DefaultRuntimeTurnRunner = class {
57641
+ async run(input, context) {
57642
+ const messages = [
57643
+ ...context.session.messages,
57644
+ {
57645
+ role: "user",
57646
+ content: input.content
57647
+ }
57648
+ ];
57649
+ const response = await context.provider.chat(messages, {
57650
+ model: input.options?.model,
57651
+ maxTokens: input.options?.maxTokens,
57652
+ temperature: input.options?.temperature,
57653
+ stopSequences: input.options?.stopSequences,
57654
+ system: context.session.instructions ?? input.options?.system,
57655
+ timeout: input.options?.timeout,
57656
+ signal: input.options?.signal,
57657
+ thinking: input.options?.thinking
57658
+ });
57659
+ return {
57660
+ sessionId: context.session.id,
57661
+ content: response.content,
57662
+ usage: response.usage,
57663
+ model: response.model,
57664
+ mode: context.session.mode
57665
+ };
57666
+ }
57667
+ };
57668
+ function createDefaultRuntimeTurnRunner() {
57669
+ return new DefaultRuntimeTurnRunner();
57670
+ }
57377
57671
  var InMemoryEventLog = class {
57378
57672
  events = [];
57379
57673
  record(type, data = {}) {
@@ -57492,7 +57786,8 @@ var DESTRUCTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
57492
57786
  "delete_file",
57493
57787
  "restore_checkpoint",
57494
57788
  "git_commit",
57495
- "git_push"
57789
+ "git_push",
57790
+ "request_human_escalation"
57496
57791
  ]);
57497
57792
  function riskForTool(tool) {
57498
57793
  if (READ_ONLY_TOOL_NAMES.has(tool.name)) return "read-only";
@@ -57525,6 +57820,22 @@ var DefaultPermissionPolicy = class {
57525
57820
  }
57526
57821
  return { allowed: true, risk };
57527
57822
  }
57823
+ canExecuteToolInput(mode, tool, input) {
57824
+ if (tool.name !== "run_linter") {
57825
+ return this.canExecuteTool(mode, tool);
57826
+ }
57827
+ const definition = getAgentMode(mode);
57828
+ const fixEnabled = input["fix"] === true;
57829
+ const decision = fixEnabled ? { allowed: true, risk: "write" } : { allowed: true, risk: "read-only" };
57830
+ if (definition.readOnly && fixEnabled) {
57831
+ return {
57832
+ allowed: false,
57833
+ reason: `${definition.label} mode is read-only; run_linter with fix=true can modify files.`,
57834
+ risk: "write"
57835
+ };
57836
+ }
57837
+ return decision;
57838
+ }
57528
57839
  };
57529
57840
  function createPermissionPolicy() {
57530
57841
  return new DefaultPermissionPolicy();
@@ -57566,6 +57877,353 @@ var ProviderRegistry = class {
57566
57877
  function createProviderRegistry() {
57567
57878
  return new ProviderRegistry();
57568
57879
  }
57880
+ function createSessionId() {
57881
+ return `rt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
57882
+ }
57883
+ function cloneSession(session) {
57884
+ return structuredClone(session);
57885
+ }
57886
+ var InMemoryRuntimeSessionStore = class {
57887
+ sessions = /* @__PURE__ */ new Map();
57888
+ create(options = {}) {
57889
+ const now = (/* @__PURE__ */ new Date()).toISOString();
57890
+ const session = {
57891
+ id: options.id ?? createSessionId(),
57892
+ createdAt: now,
57893
+ updatedAt: now,
57894
+ mode: options.mode ?? "ask",
57895
+ messages: options.messages ? options.messages.map((message) => ({ ...message })) : [],
57896
+ instructions: options.instructions,
57897
+ metadata: { ...options.metadata }
57898
+ };
57899
+ this.sessions.set(session.id, cloneSession(session));
57900
+ return cloneSession(session);
57901
+ }
57902
+ get(id) {
57903
+ const session = this.sessions.get(id);
57904
+ return session ? cloneSession(session) : void 0;
57905
+ }
57906
+ update(session) {
57907
+ const updated = {
57908
+ ...session,
57909
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
57910
+ messages: session.messages.map((message) => ({ ...message })),
57911
+ metadata: { ...session.metadata }
57912
+ };
57913
+ this.sessions.set(updated.id, cloneSession(updated));
57914
+ return cloneSession(updated);
57915
+ }
57916
+ list() {
57917
+ return [...this.sessions.values()].map(cloneSession);
57918
+ }
57919
+ delete(id) {
57920
+ return this.sessions.delete(id);
57921
+ }
57922
+ };
57923
+ function createRuntimeSessionStore() {
57924
+ return new InMemoryRuntimeSessionStore();
57925
+ }
57926
+
57927
+ // src/runtime/workflow-registry.ts
57928
+ function cloneWorkflow(workflow) {
57929
+ return {
57930
+ ...workflow,
57931
+ checks: [...workflow.checks],
57932
+ steps: workflow.steps.map((step) => ({
57933
+ ...step,
57934
+ requiredTools: [...step.requiredTools]
57935
+ })),
57936
+ nodes: workflow.nodes?.map((node) => ({
57937
+ ...node,
57938
+ dependsOn: node.dependsOn ? [...node.dependsOn] : void 0,
57939
+ requiredTools: node.requiredTools ? [...node.requiredTools] : void 0,
57940
+ gates: node.gates ? [...node.gates] : void 0,
57941
+ retryPolicy: node.retryPolicy ? { ...node.retryPolicy } : void 0
57942
+ })),
57943
+ edges: workflow.edges?.map((edge) => ({ ...edge })),
57944
+ gates: workflow.gates?.map((gate) => ({ ...gate })),
57945
+ retryPolicy: workflow.retryPolicy ? { ...workflow.retryPolicy } : void 0
57946
+ };
57947
+ }
57948
+ function workflowToAgentGraph(workflow) {
57949
+ const nodes = workflow.nodes ?? workflow.steps.map((step, index) => ({
57950
+ id: step.id,
57951
+ description: step.description,
57952
+ requiredTools: [...step.requiredTools],
57953
+ risk: step.risk,
57954
+ dependsOn: index > 0 ? [workflow.steps[index - 1].id] : []
57955
+ }));
57956
+ return {
57957
+ nodes,
57958
+ edges: workflow.edges,
57959
+ gates: workflow.gates,
57960
+ parallelism: workflow.parallelism
57961
+ };
57962
+ }
57963
+ var WorkflowCatalog = class {
57964
+ workflows = /* @__PURE__ */ new Map();
57965
+ constructor(workflows = DEFAULT_WORKFLOWS) {
57966
+ for (const workflow of workflows) {
57967
+ this.register(workflow);
57968
+ }
57969
+ }
57970
+ register(workflow) {
57971
+ const validation = validateAgentGraph(workflowToAgentGraph(workflow));
57972
+ if (!validation.valid) {
57973
+ throw new Error(
57974
+ `Invalid workflow graph for '${workflow.id}': ${validation.issues.map((issue) => issue.message).join("; ")}`
57975
+ );
57976
+ }
57977
+ this.workflows.set(workflow.id, cloneWorkflow(workflow));
57978
+ }
57979
+ get(id) {
57980
+ const workflow = this.workflows.get(id);
57981
+ return workflow ? cloneWorkflow(workflow) : void 0;
57982
+ }
57983
+ list() {
57984
+ return [...this.workflows.values()].map(cloneWorkflow).sort((a, b) => a.id.localeCompare(b.id));
57985
+ }
57986
+ createPlan(workflowId, input, eventLog) {
57987
+ const workflow = this.get(workflowId);
57988
+ if (!workflow) {
57989
+ throw new Error(`Unknown workflow: ${workflowId}`);
57990
+ }
57991
+ const plan = {
57992
+ id: `${workflowId}-${Date.now().toString(36)}`,
57993
+ workflowId,
57994
+ input,
57995
+ status: "planned",
57996
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
57997
+ };
57998
+ eventLog?.record("workflow.planned", {
57999
+ workflowId,
58000
+ planId: plan.id,
58001
+ replayable: workflow.replayable,
58002
+ checks: workflow.checks,
58003
+ graphLevels: validateAgentGraph(workflowToAgentGraph(workflow)).levels
58004
+ });
58005
+ return plan;
58006
+ }
58007
+ };
58008
+ var DEFAULT_WORKFLOWS = [
58009
+ {
58010
+ id: "architect-editor-verifier",
58011
+ name: "Architect / Editor / Verifier",
58012
+ description: "Plan read-only, apply approved changes, then verify and summarize risks.",
58013
+ inputSchema: "task: string; approvedPlan?: string",
58014
+ outputKind: "patch",
58015
+ replayable: true,
58016
+ checks: ["pnpm check", "diff summary", "review risks"],
58017
+ steps: [
58018
+ {
58019
+ id: "architect",
58020
+ description: "Inspect context and produce a read-only implementation plan.",
58021
+ requiredTools: ["repo_context", "read_file", "git_diff"],
58022
+ risk: "read-only"
58023
+ },
58024
+ {
58025
+ id: "editor",
58026
+ description: "Apply the approved plan without reinterpreting the objective.",
58027
+ requiredTools: ["read_file", "edit_file", "write_file"],
58028
+ risk: "write"
58029
+ },
58030
+ {
58031
+ id: "verifier",
58032
+ description: "Run checks, review diff, and report residual risk.",
58033
+ requiredTools: ["bash_exec", "git_diff", "review_code"],
58034
+ risk: "destructive"
58035
+ }
58036
+ ]
58037
+ },
58038
+ {
58039
+ id: "provider-diagnosis",
58040
+ name: "Provider Diagnosis",
58041
+ description: "Probe provider/model capabilities, endpoint strategy, credentials, and fallbacks.",
58042
+ inputSchema: "provider?: string; model?: string; live?: boolean",
58043
+ outputKind: "json",
58044
+ replayable: true,
58045
+ checks: ["provider capability matrix", "optional live probe"],
58046
+ steps: [
58047
+ {
58048
+ id: "capability",
58049
+ description: "Resolve catalog metadata and runtime endpoint strategy.",
58050
+ requiredTools: [],
58051
+ risk: "read-only"
58052
+ },
58053
+ {
58054
+ id: "fallbacks",
58055
+ description: "Suggest fallback provider/model choices when unsupported.",
58056
+ requiredTools: [],
58057
+ risk: "read-only"
58058
+ }
58059
+ ]
58060
+ },
58061
+ {
58062
+ id: "review-pr",
58063
+ name: "Review PR",
58064
+ description: "Review a branch or PR read-only and emit severity-ranked findings.",
58065
+ inputSchema: "target: string",
58066
+ outputKind: "markdown",
58067
+ replayable: true,
58068
+ checks: ["git diff", "tests gap review", "security review"],
58069
+ steps: [
58070
+ {
58071
+ id: "collect-diff",
58072
+ description: "Collect PR diff and related context.",
58073
+ requiredTools: ["git_diff", "repo_context"],
58074
+ risk: "read-only"
58075
+ },
58076
+ {
58077
+ id: "findings",
58078
+ description: "Produce prioritized findings with file and line references.",
58079
+ requiredTools: ["read_file", "review_code"],
58080
+ risk: "read-only"
58081
+ }
58082
+ ]
58083
+ },
58084
+ {
58085
+ id: "best-of-n",
58086
+ name: "Best Of N",
58087
+ description: "Run multiple isolated attempts, score them, and select a winning patch.",
58088
+ inputSchema: "task: string; attempts: number",
58089
+ outputKind: "patch",
58090
+ replayable: true,
58091
+ checks: ["worktree isolation", "checks pass", "diff risk score"],
58092
+ steps: [
58093
+ {
58094
+ id: "fanout",
58095
+ description: "Create isolated attempts in temporary worktrees.",
58096
+ requiredTools: ["git_status", "bash_exec"],
58097
+ risk: "destructive"
58098
+ },
58099
+ {
58100
+ id: "score",
58101
+ description: "Run checks and compare quality, cost, latency, and diff risk.",
58102
+ requiredTools: ["bash_exec", "git_diff"],
58103
+ risk: "destructive"
58104
+ },
58105
+ {
58106
+ id: "apply-winner",
58107
+ description: "Apply the winning patch only if conservative checks pass.",
58108
+ requiredTools: ["git_diff", "edit_file"],
58109
+ risk: "write"
58110
+ }
58111
+ ]
58112
+ },
58113
+ {
58114
+ id: "release",
58115
+ name: "Release",
58116
+ description: "Follow the project release skill: changelog, version bump, PR, merge, tag, publish verify.",
58117
+ inputSchema: "bump?: patch|minor|major",
58118
+ outputKind: "release",
58119
+ replayable: true,
58120
+ checks: ["pnpm check", "PR checks", "release.yml", "npm view"],
58121
+ steps: [
58122
+ {
58123
+ id: "preflight",
58124
+ description: "Verify branch, clean tree, GitHub auth, and remote state.",
58125
+ requiredTools: ["git_status", "bash_exec"],
58126
+ risk: "destructive"
58127
+ },
58128
+ {
58129
+ id: "version",
58130
+ description: "Update changelog and package versions using the release skill.",
58131
+ requiredTools: ["read_file", "edit_file", "bash_exec"],
58132
+ risk: "destructive"
58133
+ },
58134
+ {
58135
+ id: "publish",
58136
+ description: "Merge release PR, tag main, and verify release workflow outputs.",
58137
+ requiredTools: ["bash_exec"],
58138
+ risk: "destructive"
58139
+ }
58140
+ ]
58141
+ }
58142
+ ];
58143
+ function createWorkflowCatalog(workflows) {
58144
+ return new WorkflowCatalog(workflows);
58145
+ }
58146
+
58147
+ // src/runtime/workflow-engine.ts
58148
+ var WorkflowEngine = class {
58149
+ constructor(catalog = createWorkflowCatalog(), eventLog = createEventLog()) {
58150
+ this.catalog = catalog;
58151
+ this.eventLog = eventLog;
58152
+ }
58153
+ catalog;
58154
+ eventLog;
58155
+ handlers = /* @__PURE__ */ new Map();
58156
+ registerHandler(workflowId, handler) {
58157
+ if (!this.catalog.get(workflowId)) {
58158
+ throw new Error(`Unknown workflow: ${workflowId}`);
58159
+ }
58160
+ this.handlers.set(workflowId, handler);
58161
+ }
58162
+ createPlan(workflowId, input) {
58163
+ return this.catalog.createPlan(workflowId, input, this.eventLog);
58164
+ }
58165
+ async run(request) {
58166
+ const workflow = this.catalog.get(request.workflowId);
58167
+ if (!workflow) {
58168
+ throw new Error(`Unknown workflow: ${request.workflowId}`);
58169
+ }
58170
+ const handler = this.handlers.get(request.workflowId);
58171
+ if (!handler) {
58172
+ throw new Error(`No handler registered for workflow: ${request.workflowId}`);
58173
+ }
58174
+ const plan = request.plan ?? this.createPlan(request.workflowId, request.input);
58175
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
58176
+ const runId = `${request.workflowId}-run-${Date.now().toString(36)}`;
58177
+ this.eventLog.record("workflow.started", {
58178
+ workflowId: request.workflowId,
58179
+ planId: plan.id,
58180
+ runId
58181
+ });
58182
+ try {
58183
+ const output = await handler(request.input, {
58184
+ workflow,
58185
+ plan,
58186
+ eventLog: this.eventLog
58187
+ });
58188
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
58189
+ const result = {
58190
+ id: runId,
58191
+ workflowId: request.workflowId,
58192
+ status: "completed",
58193
+ output,
58194
+ startedAt,
58195
+ completedAt
58196
+ };
58197
+ this.eventLog.record("workflow.completed", {
58198
+ workflowId: request.workflowId,
58199
+ planId: plan.id,
58200
+ runId
58201
+ });
58202
+ return result;
58203
+ } catch (error) {
58204
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
58205
+ const message = error instanceof Error ? error.message : String(error);
58206
+ this.eventLog.record("workflow.failed", {
58207
+ workflowId: request.workflowId,
58208
+ planId: plan.id,
58209
+ runId,
58210
+ error: message
58211
+ });
58212
+ return {
58213
+ id: runId,
58214
+ workflowId: request.workflowId,
58215
+ status: "failed",
58216
+ output: null,
58217
+ startedAt,
58218
+ completedAt,
58219
+ error: message
58220
+ };
58221
+ }
58222
+ }
58223
+ };
58224
+ function createWorkflowEngine(catalog, eventLog) {
58225
+ return new WorkflowEngine(catalog, eventLog);
58226
+ }
57569
58227
 
57570
58228
  // src/runtime/agent-runtime.ts
57571
58229
  var AgentRuntime = class {
@@ -57573,9 +58231,12 @@ var AgentRuntime = class {
57573
58231
  this.options = options;
57574
58232
  this.providerRegistry = createProviderRegistry();
57575
58233
  this.toolRegistry = options.toolRegistry ?? createFullToolRegistry();
57576
- this.sessionStore = options.sessionStore ?? createSessionStore({});
57577
- this.permissionPolicy = options.permissionPolicy ?? createPermissionPolicy();
58234
+ this.sessionStore = options.sessionStore;
58235
+ this.runtimeSessionStore = options.runtimeSessionStore ?? createRuntimeSessionStore();
57578
58236
  this.eventLog = options.eventLog ?? (options.eventLogPath ? createFileEventLog(options.eventLogPath) : createEventLog());
58237
+ this.workflowEngine = options.workflowEngine ?? createWorkflowEngine(void 0, this.eventLog);
58238
+ this.permissionPolicy = options.permissionPolicy ?? createPermissionPolicy();
58239
+ this.turnRunner = options.turnRunner ?? createDefaultRuntimeTurnRunner();
57579
58240
  this.providerType = options.providerType;
57580
58241
  this.model = options.model ?? options.providerConfig?.model ?? getDefaultModel(options.providerType);
57581
58242
  }
@@ -57583,16 +58244,21 @@ var AgentRuntime = class {
57583
58244
  providerRegistry;
57584
58245
  toolRegistry;
57585
58246
  sessionStore;
58247
+ runtimeSessionStore;
58248
+ workflowEngine;
57586
58249
  permissionPolicy;
57587
58250
  eventLog;
58251
+ turnRunner;
57588
58252
  providerType;
57589
58253
  model;
58254
+ provider;
57590
58255
  async initialize() {
57591
58256
  const providerInjected = Boolean(this.options.provider);
57592
58257
  const provider = this.options.provider ?? await this.providerRegistry.createProvider(this.providerType, {
57593
58258
  ...this.options.providerConfig,
57594
58259
  model: this.getModel()
57595
58260
  });
58261
+ this.provider = provider;
57596
58262
  this.publishToGlobalBridge(provider);
57597
58263
  this.eventLog.record(providerInjected ? "provider.attached" : "provider.created", {
57598
58264
  provider: this.providerType,
@@ -57607,6 +58273,7 @@ var AgentRuntime = class {
57607
58273
  updateProvider(providerType, model2, provider) {
57608
58274
  this.providerType = providerType;
57609
58275
  this.model = model2 ?? getDefaultModel(providerType);
58276
+ this.provider = provider;
57610
58277
  this.publishToGlobalBridge(provider);
57611
58278
  this.eventLog.record("provider.updated", {
57612
58279
  provider: this.providerType,
@@ -57634,7 +58301,276 @@ var AgentRuntime = class {
57634
58301
  modes: listAgentModes()
57635
58302
  };
57636
58303
  }
57637
- assertToolAllowed(mode, toolName) {
58304
+ createSession(options = {}) {
58305
+ const session = this.runtimeSessionStore.create(options);
58306
+ this.eventLog.record("session.created", {
58307
+ sessionId: session.id,
58308
+ mode: session.mode,
58309
+ metadataKeys: Object.keys(session.metadata).sort()
58310
+ });
58311
+ return session;
58312
+ }
58313
+ getSession(sessionId) {
58314
+ return this.runtimeSessionStore.get(sessionId);
58315
+ }
58316
+ listSessions() {
58317
+ return this.runtimeSessionStore.list();
58318
+ }
58319
+ async runTurn(input) {
58320
+ const provider = this.provider;
58321
+ if (!provider) {
58322
+ throw new Error("Runtime provider is not initialized.");
58323
+ }
58324
+ const session = input.sessionId ? this.runtimeSessionStore.get(input.sessionId) : this.createSession({ mode: input.mode, metadata: input.metadata });
58325
+ if (!session) {
58326
+ throw new Error(`Runtime session not found: ${input.sessionId}`);
58327
+ }
58328
+ const effectiveSession = input.mode && input.mode !== session.mode ? { ...session, mode: input.mode } : session;
58329
+ this.eventLog.record("turn.started", {
58330
+ sessionId: effectiveSession.id,
58331
+ provider: this.providerType,
58332
+ model: this.getModel(),
58333
+ mode: effectiveSession.mode,
58334
+ runtimeApi: true
58335
+ });
58336
+ try {
58337
+ const result = await this.turnRunner.run(input, {
58338
+ runtime: this,
58339
+ session: effectiveSession,
58340
+ provider,
58341
+ toolRegistry: this.toolRegistry,
58342
+ permissionPolicy: this.permissionPolicy,
58343
+ eventLog: this.eventLog
58344
+ });
58345
+ const updatedSession = this.runtimeSessionStore.update({
58346
+ ...effectiveSession,
58347
+ messages: [
58348
+ ...effectiveSession.messages,
58349
+ { role: "user", content: input.content },
58350
+ { role: "assistant", content: result.content }
58351
+ ]
58352
+ });
58353
+ this.eventLog.record("session.updated", {
58354
+ sessionId: updatedSession.id,
58355
+ messages: updatedSession.messages.length
58356
+ });
58357
+ this.eventLog.record("turn.completed", {
58358
+ sessionId: updatedSession.id,
58359
+ inputTokens: result.usage.inputTokens,
58360
+ outputTokens: result.usage.outputTokens,
58361
+ model: result.model,
58362
+ runtimeApi: true
58363
+ });
58364
+ return { ...result, sessionId: updatedSession.id, mode: updatedSession.mode };
58365
+ } catch (error) {
58366
+ this.eventLog.record("turn.failed", {
58367
+ sessionId: effectiveSession.id,
58368
+ error: error instanceof Error ? error.message : String(error),
58369
+ runtimeApi: true
58370
+ });
58371
+ throw error;
58372
+ }
58373
+ }
58374
+ async *streamTurn(input) {
58375
+ const provider = this.provider;
58376
+ if (!provider) {
58377
+ throw new Error("Runtime provider is not initialized.");
58378
+ }
58379
+ const session = input.sessionId ? this.runtimeSessionStore.get(input.sessionId) : this.createSession({ mode: input.mode, metadata: input.metadata });
58380
+ if (!session) {
58381
+ throw new Error(`Runtime session not found: ${input.sessionId}`);
58382
+ }
58383
+ const effectiveSession = input.mode && input.mode !== session.mode ? { ...session, mode: input.mode } : session;
58384
+ const messages = [
58385
+ ...effectiveSession.messages,
58386
+ {
58387
+ role: "user",
58388
+ content: input.content
58389
+ }
58390
+ ];
58391
+ this.eventLog.record("turn.started", {
58392
+ sessionId: effectiveSession.id,
58393
+ provider: this.providerType,
58394
+ model: this.getModel(),
58395
+ mode: effectiveSession.mode,
58396
+ streaming: true,
58397
+ runtimeApi: true
58398
+ });
58399
+ let content = "";
58400
+ let completed = false;
58401
+ let failed = false;
58402
+ try {
58403
+ for await (const chunk of provider.stream(messages, {
58404
+ model: input.options?.model,
58405
+ maxTokens: input.options?.maxTokens,
58406
+ temperature: input.options?.temperature,
58407
+ stopSequences: input.options?.stopSequences,
58408
+ system: effectiveSession.instructions ?? input.options?.system,
58409
+ timeout: input.options?.timeout,
58410
+ signal: input.options?.signal,
58411
+ thinking: input.options?.thinking
58412
+ })) {
58413
+ if (chunk.type === "text" && chunk.text) {
58414
+ content += chunk.text;
58415
+ yield {
58416
+ type: "text",
58417
+ sessionId: effectiveSession.id,
58418
+ text: chunk.text
58419
+ };
58420
+ }
58421
+ }
58422
+ const updatedSession = this.runtimeSessionStore.update({
58423
+ ...effectiveSession,
58424
+ messages: [
58425
+ ...effectiveSession.messages,
58426
+ { role: "user", content: input.content },
58427
+ { role: "assistant", content }
58428
+ ]
58429
+ });
58430
+ const result = {
58431
+ sessionId: updatedSession.id,
58432
+ content,
58433
+ usage: {
58434
+ inputTokens: provider.countTokens(input.content),
58435
+ outputTokens: provider.countTokens(content),
58436
+ estimated: true
58437
+ },
58438
+ model: input.options?.model ?? this.getModel(),
58439
+ mode: updatedSession.mode
58440
+ };
58441
+ this.eventLog.record("session.updated", {
58442
+ sessionId: updatedSession.id,
58443
+ messages: updatedSession.messages.length
58444
+ });
58445
+ this.eventLog.record("turn.completed", {
58446
+ sessionId: updatedSession.id,
58447
+ inputTokens: result.usage.inputTokens,
58448
+ outputTokens: result.usage.outputTokens,
58449
+ model: result.model,
58450
+ streaming: true,
58451
+ runtimeApi: true
58452
+ });
58453
+ completed = true;
58454
+ yield { type: "done", sessionId: updatedSession.id, result };
58455
+ } catch (error) {
58456
+ failed = true;
58457
+ const message = error instanceof Error ? error.message : String(error);
58458
+ this.eventLog.record("turn.failed", {
58459
+ sessionId: effectiveSession.id,
58460
+ error: message,
58461
+ streaming: true,
58462
+ runtimeApi: true
58463
+ });
58464
+ yield {
58465
+ type: "error",
58466
+ sessionId: effectiveSession.id,
58467
+ error: message
58468
+ };
58469
+ } finally {
58470
+ if (!completed && !failed) {
58471
+ this.eventLog.record("turn.cancelled", {
58472
+ sessionId: effectiveSession.id,
58473
+ outputTokens: provider.countTokens(content),
58474
+ streaming: true,
58475
+ runtimeApi: true
58476
+ });
58477
+ }
58478
+ }
58479
+ }
58480
+ async executeTool(input) {
58481
+ const startedAt = performance.now();
58482
+ const session = input.sessionId ? this.getSession(input.sessionId) : void 0;
58483
+ if (input.sessionId && !session) {
58484
+ const decision2 = {
58485
+ allowed: false,
58486
+ reason: `Runtime session not found: ${input.sessionId}`,
58487
+ risk: "read-only"
58488
+ };
58489
+ this.eventLog.record("tool.blocked", {
58490
+ sessionId: input.sessionId,
58491
+ mode: input.mode ?? "ask",
58492
+ tool: input.toolName,
58493
+ reason: decision2.reason,
58494
+ runtimeApi: true
58495
+ });
58496
+ return {
58497
+ toolName: input.toolName,
58498
+ success: false,
58499
+ error: decision2.reason,
58500
+ duration: performance.now() - startedAt,
58501
+ decision: decision2
58502
+ };
58503
+ }
58504
+ const mode = input.mode ?? session?.mode ?? "ask";
58505
+ const tool = this.toolRegistry.get(input.toolName);
58506
+ if (!tool) {
58507
+ const decision2 = {
58508
+ allowed: false,
58509
+ reason: "Tool not registered.",
58510
+ risk: "read-only"
58511
+ };
58512
+ this.eventLog.record("tool.blocked", {
58513
+ sessionId: input.sessionId,
58514
+ mode,
58515
+ tool: input.toolName,
58516
+ reason: decision2.reason,
58517
+ runtimeApi: true
58518
+ });
58519
+ return {
58520
+ toolName: input.toolName,
58521
+ success: false,
58522
+ error: decision2.reason,
58523
+ duration: performance.now() - startedAt,
58524
+ decision: decision2
58525
+ };
58526
+ }
58527
+ const decision = this.permissionPolicy.canExecuteToolInput ? this.permissionPolicy.canExecuteToolInput(mode, tool, input.input) : this.permissionPolicy.canExecuteTool(mode, tool);
58528
+ if (!decision.allowed || decision.requiresConfirmation && input.confirmed !== true) {
58529
+ const reason = decision.reason ?? (decision.requiresConfirmation ? "Tool requires explicit runtime confirmation." : "Tool is not allowed.");
58530
+ this.eventLog.record("tool.blocked", {
58531
+ sessionId: input.sessionId,
58532
+ mode,
58533
+ tool: input.toolName,
58534
+ reason,
58535
+ risk: decision.risk,
58536
+ requiresConfirmation: decision.requiresConfirmation,
58537
+ runtimeApi: true
58538
+ });
58539
+ return {
58540
+ toolName: input.toolName,
58541
+ success: false,
58542
+ error: reason,
58543
+ duration: performance.now() - startedAt,
58544
+ decision
58545
+ };
58546
+ }
58547
+ this.eventLog.record("tool.started", {
58548
+ sessionId: input.sessionId,
58549
+ mode,
58550
+ tool: input.toolName,
58551
+ risk: decision.risk,
58552
+ runtimeApi: true,
58553
+ metadataKeys: Object.keys(input.metadata ?? {}).sort()
58554
+ });
58555
+ const result = await this.toolRegistry.execute(input.toolName, input.input);
58556
+ this.eventLog.record("tool.completed", {
58557
+ sessionId: input.sessionId,
58558
+ mode,
58559
+ tool: input.toolName,
58560
+ success: result.success,
58561
+ duration: result.duration,
58562
+ runtimeApi: true
58563
+ });
58564
+ return {
58565
+ toolName: input.toolName,
58566
+ success: result.success,
58567
+ output: result.data,
58568
+ error: result.error,
58569
+ duration: result.duration,
58570
+ decision
58571
+ };
58572
+ }
58573
+ assertToolAllowed(mode, toolName, input) {
57638
58574
  const tool = this.toolRegistry.get(toolName);
57639
58575
  if (!tool) {
57640
58576
  this.eventLog.record("tool.blocked", {
@@ -57644,7 +58580,7 @@ var AgentRuntime = class {
57644
58580
  });
57645
58581
  return false;
57646
58582
  }
57647
- const decision = this.permissionPolicy.canExecuteTool(mode, tool);
58583
+ const decision = input && this.permissionPolicy.canExecuteToolInput ? this.permissionPolicy.canExecuteToolInput(mode, tool, input) : this.permissionPolicy.canExecuteTool(mode, tool);
57648
58584
  this.eventLog.record(decision.allowed ? "tool.allowed" : "tool.blocked", {
57649
58585
  mode,
57650
58586
  tool: toolName,
@@ -57659,6 +58595,118 @@ async function createAgentRuntime(options) {
57659
58595
  return runtime;
57660
58596
  }
57661
58597
 
58598
+ // src/runtime/tool-calling-turn-runner.ts
58599
+ function runtimeWithTools(runtime) {
58600
+ if (runtime && typeof runtime === "object" && "executeTool" in runtime && typeof runtime.executeTool === "function") {
58601
+ return runtime;
58602
+ }
58603
+ throw new Error("ToolCallingRuntimeTurnRunner requires a runtime with executeTool().");
58604
+ }
58605
+ function toolResultToContent(result) {
58606
+ if (!result.success) {
58607
+ return `Error: ${result.error ?? "Tool failed."}`;
58608
+ }
58609
+ if (typeof result.output === "string") return result.output;
58610
+ return JSON.stringify(result.output ?? null);
58611
+ }
58612
+ var ToolCallingRuntimeTurnRunner = class {
58613
+ maxToolIterations;
58614
+ constructor(options = {}) {
58615
+ this.maxToolIterations = options.maxToolIterations ?? 10;
58616
+ }
58617
+ async run(input, context) {
58618
+ const runtime = runtimeWithTools(context.runtime);
58619
+ const messages = [
58620
+ ...context.session.messages,
58621
+ {
58622
+ role: "user",
58623
+ content: input.content
58624
+ }
58625
+ ];
58626
+ const tools = context.toolRegistry.getToolDefinitionsForLLM();
58627
+ const confirmedTools = new Set(input.confirmedTools ?? []);
58628
+ let inputTokens = 0;
58629
+ let outputTokens = 0;
58630
+ let lastModel = input.options?.model ?? context.provider.id;
58631
+ for (let iteration = 0; iteration < this.maxToolIterations; iteration++) {
58632
+ const response = await context.provider.chatWithTools(messages, {
58633
+ tools,
58634
+ model: input.options?.model,
58635
+ maxTokens: input.options?.maxTokens,
58636
+ temperature: input.options?.temperature,
58637
+ stopSequences: input.options?.stopSequences,
58638
+ system: context.session.instructions ?? input.options?.system,
58639
+ timeout: input.options?.timeout,
58640
+ signal: input.options?.signal,
58641
+ thinking: input.options?.thinking
58642
+ });
58643
+ inputTokens += response.usage.inputTokens;
58644
+ outputTokens += response.usage.outputTokens;
58645
+ lastModel = response.model;
58646
+ if (response.stopReason !== "tool_use" || response.toolCalls.length === 0) {
58647
+ return {
58648
+ sessionId: context.session.id,
58649
+ content: response.content,
58650
+ usage: { inputTokens, outputTokens },
58651
+ model: response.model,
58652
+ mode: context.session.mode
58653
+ };
58654
+ }
58655
+ const assistantContent = [];
58656
+ if (response.content.trim().length > 0) {
58657
+ assistantContent.push({ type: "text", text: response.content });
58658
+ }
58659
+ for (const toolCall of response.toolCalls) {
58660
+ assistantContent.push({
58661
+ type: "tool_use",
58662
+ id: toolCall.id,
58663
+ name: toolCall.name,
58664
+ input: toolCall.input,
58665
+ geminiThoughtSignature: toolCall.geminiThoughtSignature
58666
+ });
58667
+ }
58668
+ messages.push({
58669
+ role: "assistant",
58670
+ content: assistantContent
58671
+ });
58672
+ const toolResults = [];
58673
+ for (const toolCall of response.toolCalls) {
58674
+ const result = await runtime.executeTool({
58675
+ sessionId: context.session.id,
58676
+ mode: context.session.mode,
58677
+ toolName: toolCall.name,
58678
+ input: toolCall.input,
58679
+ confirmed: confirmedTools.has(toolCall.name),
58680
+ metadata: input.metadata
58681
+ });
58682
+ toolResults.push({
58683
+ type: "tool_result",
58684
+ tool_use_id: toolCall.id,
58685
+ content: toolResultToContent(result),
58686
+ is_error: !result.success
58687
+ });
58688
+ }
58689
+ messages.push({
58690
+ role: "user",
58691
+ content: toolResults
58692
+ });
58693
+ }
58694
+ return {
58695
+ sessionId: context.session.id,
58696
+ content: "The tool-calling runtime reached its maximum tool iteration budget.",
58697
+ usage: { inputTokens, outputTokens },
58698
+ model: lastModel,
58699
+ mode: context.session.mode
58700
+ };
58701
+ }
58702
+ };
58703
+ function createToolCallingRuntimeTurnRunner(options) {
58704
+ return new ToolCallingRuntimeTurnRunner(options);
58705
+ }
58706
+
58707
+ // src/runtime/blueprints.ts
58708
+ init_registry4();
58709
+
57662
58710
  // src/cli/repl/index.ts
57663
58711
  init_version();
57664
58712
  init_trust_store();
@@ -59493,12 +60541,43 @@ ${stdinContent}
59493
60541
  model: session.config.provider.model || void 0,
59494
60542
  provider,
59495
60543
  eventLogPath: path39__default.join(options.projectPath, ".coco", "events", `${session.id}.jsonl`),
60544
+ turnRunner: options.useRuntimeRunner ? createToolCallingRuntimeTurnRunner() : void 0,
59496
60545
  publishToGlobalBridge: true
59497
60546
  });
59498
60547
  session.runtime = runtime;
59499
60548
  const toolRegistry = runtime.toolRegistry;
59500
60549
  await loadAllowedPaths(options.projectPath);
59501
60550
  await initializeContextManager(session, provider);
60551
+ if (options.useRuntimeRunner) {
60552
+ const runtimeSession = runtime.createSession({
60553
+ id: session.id,
60554
+ mode: "build",
60555
+ instructions: session.config.agent.systemPrompt || void 0,
60556
+ metadata: {
60557
+ surface: "cli",
60558
+ product: "coco-code",
60559
+ execution: "headless-runtime-runner"
60560
+ }
60561
+ });
60562
+ const result2 = await runtime.runTurn({
60563
+ sessionId: runtimeSession.id,
60564
+ content: task,
60565
+ metadata: { surface: "cli", product: "coco-code" }
60566
+ });
60567
+ const events = runtime.eventLog.list();
60568
+ const headlessResult2 = {
60569
+ success: true,
60570
+ output: result2.content,
60571
+ toolsExecuted: events.filter((event) => event.type === "tool.completed").length,
60572
+ usage: result2.usage
60573
+ };
60574
+ if (options.outputFormat === "json") {
60575
+ process.stdout.write(JSON.stringify(headlessResult2, null, 2) + "\n");
60576
+ } else {
60577
+ process.stdout.write(result2.content + "\n");
60578
+ }
60579
+ return headlessResult2;
60580
+ }
59502
60581
  const result = await executeAgentTurn(session, task, provider, toolRegistry, {
59503
60582
  skipConfirmation: true,
59504
60583
  // No interactive confirmations in headless mode
@@ -59564,7 +60643,10 @@ program.command("setup").description("Configure AI provider and API key").action
59564
60643
  console.log("\n\u274C Setup cancelled.");
59565
60644
  }
59566
60645
  });
59567
- program.command("chat", { isDefault: true }).description("Start interactive chat session with the agent").option("-m, --model <model>", "LLM model to use").option("--provider <provider>", "LLM provider (anthropic, openai, codex, gemini, kimi)").option("--editor-model <model>", "Cheap model for file edits (architect/editor split)").option("--weak-model <model>", "Cheap model for background tasks (compaction, summaries)").option("-p, --path <path>", "Project path", process.cwd()).option("-P, --print [task]", "Headless mode: run task and print output (no interactive UI)").option("--output <format>", "Output format for headless mode (text or json)", "text").option("--setup", "Run setup wizard before starting").action(
60646
+ program.command("chat", { isDefault: true }).description("Start interactive chat session with the agent").option("-m, --model <model>", "LLM model to use").option("--provider <provider>", "LLM provider (anthropic, openai, codex, gemini, kimi)").option("--editor-model <model>", "Cheap model for file edits (architect/editor split)").option("--weak-model <model>", "Cheap model for background tasks (compaction, summaries)").option("-p, --path <path>", "Project path", process.cwd()).option("-P, --print [task]", "Headless mode: run task and print output (no interactive UI)").option(
60647
+ "--runtime-runner",
60648
+ "Experimental: run headless tasks through the reusable runtime tool-calling runner"
60649
+ ).option("--output <format>", "Output format for headless mode (text or json)", "text").option("--setup", "Run setup wizard before starting").action(
59568
60650
  async (options) => {
59569
60651
  if (options.setup) {
59570
60652
  const result = await runOnboardingV2();
@@ -59581,6 +60663,7 @@ program.command("chat", { isDefault: true }).description("Start interactive chat
59581
60663
  task,
59582
60664
  projectPath: options.path,
59583
60665
  outputFormat: options.output === "json" ? "json" : "text",
60666
+ useRuntimeRunner: options.runtimeRunner === true,
59584
60667
  config: {
59585
60668
  provider: {
59586
60669
  type: providerType,