@rowan-agent/agent 0.20.2 → 0.22.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +43 -29
  2. package/dist/index.js +480 -177
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -944,7 +944,7 @@ function createRuntimeCoreTools(input = {}) {
944
944
  // src/runtime/durable-runtime.ts
945
945
  import { mkdir as mkdir2, chmod as chmod2, writeFile as writeFile2 } from "fs/promises";
946
946
  import { randomUUID } from "crypto";
947
- import { join as join5 } from "path";
947
+ import { join as join6 } from "path";
948
948
  import { createModelStream, isValidToolProgress } from "@rowan-agent/models";
949
949
 
950
950
  // src/utils.ts
@@ -1401,7 +1401,8 @@ async function invokeModel(input) {
1401
1401
  metadataPhase: input.phaseId,
1402
1402
  output: input.output,
1403
1403
  onThinkingDelta: input.onThinkingDelta,
1404
- onToolCallDelta: input.onToolCallDelta
1404
+ onToolCallDelta: input.onToolCallDelta,
1405
+ model: input.request.model
1405
1406
  });
1406
1407
  const response = result.doneResponse ?? {
1407
1408
  content: result.text,
@@ -1417,6 +1418,8 @@ async function invokeModel(input) {
1417
1418
  contentBlocks: result.contentBlocks,
1418
1419
  toolCalls: result.toolCalls,
1419
1420
  stopReason: result.stopReason,
1421
+ model: input.request.model,
1422
+ usage: response.usage,
1420
1423
  transcript: {
1421
1424
  request: input.request,
1422
1425
  response
@@ -1540,7 +1543,7 @@ async function collectStreamResult(input) {
1540
1543
  input.message.replaceContent(activeMessageId, contentBlocksToMessageContent(contentBlocks2));
1541
1544
  }
1542
1545
  if (persistAssistant && activeMessageId) {
1543
- if (hasContent2) await input.message.end(activeMessageId);
1546
+ if (hasContent2) await input.message.end(activeMessageId, { model: input.model, usage: event.response?.usage });
1544
1547
  else input.message.discard(activeMessageId);
1545
1548
  activeMessageId = void 0;
1546
1549
  }
@@ -1549,13 +1552,13 @@ async function collectStreamResult(input) {
1549
1552
  const abortResult = LoopGuard.checkAbort(input.config.signal);
1550
1553
  if (abortResult.stopReason !== "none") {
1551
1554
  if (persistAssistant && activeMessageId) {
1552
- await input.message.end(activeMessageId);
1555
+ await input.message.end(activeMessageId, { model: input.model });
1553
1556
  activeMessageId = void 0;
1554
1557
  }
1555
1558
  return { text: abortResult.message, contentBlocks: [], toolCalls: [], stopReason: "aborted" };
1556
1559
  }
1557
1560
  if (persistAssistant && activeMessageId) {
1558
- await input.message.end(activeMessageId);
1561
+ await input.message.end(activeMessageId, { model: input.model, usage: doneResponse?.usage });
1559
1562
  }
1560
1563
  const hasContent = (lastPartial?.contentBlocks?.length ?? 0) > 0 || (doneResponse?.content?.length ?? 0) > 0 || (doneResponse?.toolCalls?.length ?? 0) > 0;
1561
1564
  if (!hasContent && stopReason !== "error" && stopReason !== "aborted") {
@@ -1617,7 +1620,7 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1617
1620
  let outputPayload = phaseIn?.state?.payload;
1618
1621
  let nextPhase;
1619
1622
  let outputMessage;
1620
- return {
1623
+ const api = {
1621
1624
  hooks: {
1622
1625
  on: (eventType, handler) => {
1623
1626
  assertActive();
@@ -1780,27 +1783,17 @@ function createExtensionAPI(hooks, options, runtime, eventBus) {
1780
1783
  },
1781
1784
  getNextPhase: () => nextPhase,
1782
1785
  getMessage: () => outputMessage
1783
- },
1784
- host: {
1785
- contribute: (contribution) => {
1786
- assertActive();
1787
- if (!contribution || typeof contribution !== "object" || Array.isArray(contribution)) {
1788
- throw new TypeError("Host contribution must be an object.");
1789
- }
1790
- if (typeof contribution.kind !== "string" || contribution.kind.trim() === "") {
1791
- throw new TypeError("Host contribution requires a non-empty kind.");
1792
- }
1793
- if (typeof contribution.id !== "string" || contribution.id.trim() === "") {
1794
- throw new TypeError("Host contribution requires a non-empty id.");
1795
- }
1796
- assertJsonValue(contribution, "Host contribution");
1797
- const dispose = options?.contributeToHost ? options.contributeToHost(contribution) : () => {
1798
- };
1799
- trackCleanup?.(dispose);
1800
- return dispose;
1801
- }
1802
1786
  }
1803
1787
  };
1788
+ if (options?.namespaces) {
1789
+ for (const [key, value] of Object.entries(options.namespaces)) {
1790
+ if (key in api) {
1791
+ throw new Error(`Host API namespace collision: "${key}" is a built-in API key.`);
1792
+ }
1793
+ Object.defineProperty(api, key, { value, enumerable: true });
1794
+ }
1795
+ }
1796
+ return api;
1804
1797
  }
1805
1798
 
1806
1799
  // src/harness/phases/interactions.ts
@@ -2706,6 +2699,7 @@ function buildModelRequest(input, options) {
2706
2699
  model: options?.model ?? { provider: "", id: "" },
2707
2700
  ...options?.thinkingLevel === void 0 ? {} : { thinkingLevel: options.thinkingLevel },
2708
2701
  ...options?.providerOptions === void 0 ? {} : { providerOptions: options.providerOptions },
2702
+ ...options?.sessionId === void 0 ? {} : { sessionId: options.sessionId },
2709
2703
  system: systemText,
2710
2704
  messages,
2711
2705
  tools: modelTools.length > 0 ? modelTools : void 0
@@ -2971,9 +2965,15 @@ function createMessageManager(context, onMessage, onMessageDelta) {
2971
2965
  if (!active) return;
2972
2966
  active.message.content = content;
2973
2967
  },
2974
- async end(messageId) {
2968
+ async end(messageId, meta) {
2975
2969
  const active = activeMessages.get(messageId);
2976
2970
  if (!active) return;
2971
+ if (meta?.model) {
2972
+ active.message.model = meta.model;
2973
+ }
2974
+ if (meta?.usage) {
2975
+ active.message.usage = meta.usage;
2976
+ }
2977
2977
  if (!active.emitted && (active.message.content === "" || active.message.content.length === 0)) {
2978
2978
  activeMessages.delete(messageId);
2979
2979
  return;
@@ -3042,6 +3042,7 @@ async function executePhaseWithModel(ctx) {
3042
3042
  };
3043
3043
  let executableToolCalls;
3044
3044
  let roundContentBlocks;
3045
+ let collected;
3045
3046
  const checkpoint = ctx.execution.interaction.checkpoint();
3046
3047
  if (checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension") {
3047
3048
  const toolSuspension = checkpoint;
@@ -3054,7 +3055,7 @@ async function executePhaseWithModel(ctx) {
3054
3055
  toolCalls: [...toolSuspension.toolCalls]
3055
3056
  };
3056
3057
  } else {
3057
- const collected = await ctx.execution.invokeModel(roundContext);
3058
+ collected = await ctx.execution.invokeModel(roundContext);
3058
3059
  output = {
3059
3060
  message: collected.text,
3060
3061
  phase: ctx.phase.name,
@@ -3087,7 +3088,13 @@ async function executePhaseWithModel(ctx) {
3087
3088
  return output;
3088
3089
  }
3089
3090
  }
3090
- const results = await ctx.execution.executeTools(roundContext, executableToolCalls, roundContentBlocks);
3091
+ const currentModel = ctx.phase.model ?? ctx.config.model;
3092
+ const results = await ctx.execution.executeTools(
3093
+ roundContext,
3094
+ executableToolCalls,
3095
+ roundContentBlocks,
3096
+ { model: currentModel, usage: collected?.usage }
3097
+ );
3091
3098
  for (const result of results) {
3092
3099
  const messageId = ctx.messageManager.start("tool", createToolResultContent(result), {
3093
3100
  phase: ctx.phase.name
@@ -3641,7 +3648,9 @@ async function executeToolCalls(input) {
3641
3648
  config: input.config,
3642
3649
  toolCalls: input.toolCalls,
3643
3650
  contentBlocks: input.contentBlocks,
3644
- driver: input.driver
3651
+ driver: input.driver,
3652
+ model: input.model,
3653
+ usage: input.usage
3645
3654
  });
3646
3655
  if (results2.length !== input.toolCalls.length) throw new Error("Runtime returned an invalid Tool batch result");
3647
3656
  return results2.map((result, index) => ({
@@ -3713,6 +3722,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3713
3722
  appendSystemPrompt: phaseContext.appendSystemPrompt
3714
3723
  }, {
3715
3724
  model: phase.model ?? config.model,
3725
+ sessionId: config.execution.agentId,
3716
3726
  ...config.thinkingLevel === void 0 ? {} : { thinkingLevel: config.thinkingLevel },
3717
3727
  ...config.providerOptions === void 0 ? {} : { providerOptions: config.providerOptions }
3718
3728
  });
@@ -3798,7 +3808,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3798
3808
  await config.onPhaseStatus?.(phase.name, status);
3799
3809
  },
3800
3810
  executeTool: executePhaseTool,
3801
- async executeTools(phaseContext, toolCalls, contentBlocks) {
3811
+ async executeTools(phaseContext, toolCalls, contentBlocks, meta) {
3802
3812
  return runTurn(async () => {
3803
3813
  const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
3804
3814
  for (const toolCall of toolCalls) {
@@ -3816,7 +3826,9 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3816
3826
  tools,
3817
3827
  toolCalls,
3818
3828
  contentBlocks,
3819
- driver: interaction
3829
+ driver: interaction,
3830
+ model: meta?.model,
3831
+ usage: meta?.usage
3820
3832
  });
3821
3833
  for (const result of results) {
3822
3834
  await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
@@ -4283,7 +4295,7 @@ function normalizeUserInput(input) {
4283
4295
  return normalized;
4284
4296
  }
4285
4297
  function isAssistantMessage(value) {
4286
- return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "messageRevision", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && (value.messageRevision === void 0 || Number.isInteger(value.messageRevision) && value.messageRevision >= 0) && Number.isInteger(value.sequenceWithinRun) && value.sequenceWithinRun >= 0 && typeof value.createdAt === "string" && isAssistantContent(value.content) && (value.metadata === void 0 || isMetadata(value.metadata)) && (value.interrupted === void 0 || typeof value.interrupted === "boolean");
4298
+ return isRecord3(value) && hasOnlyKeys(value, ["id", "agentId", "runId", "messageRevision", "role", "content", "metadata", "sequenceWithinRun", "createdAt", "interrupted", "model", "usage"]) && typeof value.id === "string" && typeof value.agentId === "string" && typeof value.runId === "string" && value.role === "assistant" && (value.messageRevision === void 0 || Number.isInteger(value.messageRevision) && value.messageRevision >= 0) && Number.isInteger(value.sequenceWithinRun) && value.sequenceWithinRun >= 0 && typeof value.createdAt === "string" && isAssistantContent(value.content) && (value.metadata === void 0 || isMetadata(value.metadata)) && (value.interrupted === void 0 || typeof value.interrupted === "boolean") && (value.model === void 0 || isRecord3(value.model) && typeof value.model.provider === "string" && typeof value.model.id === "string") && (value.usage === void 0 || isRecord3(value.usage));
4287
4299
  }
4288
4300
  function isToolResult(value) {
4289
4301
  if (!isRecord3(value)) return false;
@@ -4999,7 +5011,9 @@ function projectAssistantMessage(message, agentId, runId, sequenceWithinRun, opt
4999
5011
  sequenceWithinRun,
5000
5012
  createdAt: message.createdAt,
5001
5013
  ...message.metadata ? { metadata: message.metadata } : {},
5002
- ...options.interrupted ? { interrupted: true } : {}
5014
+ ...options.interrupted ? { interrupted: true } : {},
5015
+ ...message.model ? { model: message.model } : {},
5016
+ ...message.usage ? { usage: message.usage } : {}
5003
5017
  };
5004
5018
  }
5005
5019
  function durableInterruptedAssistantContent(content) {
@@ -5251,6 +5265,333 @@ function fromLoopResult(result) {
5251
5265
  };
5252
5266
  }
5253
5267
 
5268
+ // src/runtime/session-files.ts
5269
+ import { appendFileSync, chmodSync, existsSync as existsSync3, mkdirSync, readFileSync } from "fs";
5270
+ import { dirname as dirname5, join as join3 } from "path";
5271
+ function toEpochMs(isoString) {
5272
+ if (!isoString) return Date.now();
5273
+ const ms = new Date(isoString).getTime();
5274
+ return Number.isNaN(ms) ? Date.now() : ms;
5275
+ }
5276
+ function isContentBlock(value) {
5277
+ if (!value || typeof value !== "object") return false;
5278
+ const candidate = value;
5279
+ if (candidate.type === "text" && typeof candidate.text === "string") return true;
5280
+ if (candidate.type === "image" && typeof candidate.data === "string" && typeof candidate.mimeType === "string") return true;
5281
+ return false;
5282
+ }
5283
+ function convertToolResultContent(content) {
5284
+ if (content === void 0 || content === null) {
5285
+ return [{ type: "text", text: "" }];
5286
+ }
5287
+ if (typeof content === "string") {
5288
+ return [{ type: "text", text: content }];
5289
+ }
5290
+ if (isContentBlock(content)) {
5291
+ return content.type === "image" ? [{ type: "image", data: content.data, mimeType: content.mimeType }] : [{ type: "text", text: content.text }];
5292
+ }
5293
+ if (Array.isArray(content)) {
5294
+ if (content.length > 0 && content.every(isContentBlock)) {
5295
+ return content.map(
5296
+ (item) => item.type === "image" ? { type: "image", data: item.data, mimeType: item.mimeType } : { type: "text", text: item.text }
5297
+ );
5298
+ }
5299
+ return [{ type: "text", text: JSON.stringify(content) }];
5300
+ }
5301
+ return [{ type: "text", text: JSON.stringify(content) }];
5302
+ }
5303
+ function convertUserContent(content) {
5304
+ if (typeof content === "string") return content;
5305
+ return content.map(
5306
+ (part) => part.type === "image" ? { type: "image", data: part.data, mimeType: part.mimeType } : { type: "text", text: part.text }
5307
+ );
5308
+ }
5309
+ function convertAssistantContent(content, onToolCall) {
5310
+ if (typeof content === "string") {
5311
+ return {
5312
+ parts: [{ type: "text", text: content }],
5313
+ hasToolCall: false
5314
+ };
5315
+ }
5316
+ let hasToolCall = false;
5317
+ const parts = [];
5318
+ for (const part of content) {
5319
+ if (part.type === "text") {
5320
+ parts.push({ type: "text", text: part.text });
5321
+ } else if (part.type === "thinking") {
5322
+ parts.push({
5323
+ type: "thinking",
5324
+ thinking: part.thinking,
5325
+ ...part.signature ? { thinkingSignature: part.signature } : {}
5326
+ });
5327
+ } else if (part.type === "tool_use") {
5328
+ hasToolCall = true;
5329
+ const callId = part.providerToolCallId ?? part.toolCallId;
5330
+ onToolCall?.(callId, part.name, part.toolCallId);
5331
+ parts.push({
5332
+ type: "toolCall",
5333
+ id: callId,
5334
+ name: part.name,
5335
+ arguments: part.input
5336
+ });
5337
+ }
5338
+ }
5339
+ return { parts, hasToolCall };
5340
+ }
5341
+ async function getAgentCreatedAt(runtime, agentId) {
5342
+ let after;
5343
+ while (true) {
5344
+ const page = await runtime.listAgents(after ? { after, limit: 100 } : { limit: 100 });
5345
+ const agent = page.items.find((item) => item.id === agentId);
5346
+ if (agent) return agent.createdAt;
5347
+ if (!page.next) break;
5348
+ after = page.next;
5349
+ }
5350
+ return (/* @__PURE__ */ new Date()).toISOString();
5351
+ }
5352
+ async function getFirstRunCwd(runtime, agentId) {
5353
+ let after;
5354
+ while (true) {
5355
+ const page = await runtime.listRuns(after ? { agentId, after, limit: 100 } : { agentId, limit: 100 });
5356
+ for (const runSummary of page.items) {
5357
+ try {
5358
+ const snapshot = await runtime.run(runSummary.id).snapshot();
5359
+ if (snapshot.cwd && snapshot.cwd.trim().length > 0) {
5360
+ return snapshot.cwd;
5361
+ }
5362
+ } catch {
5363
+ }
5364
+ }
5365
+ if (!page.next) break;
5366
+ after = page.next;
5367
+ }
5368
+ return "";
5369
+ }
5370
+ function writeSessionEntry(state, entry) {
5371
+ appendFileSync(state.filePath, `${JSON.stringify(entry)}
5372
+ `, { encoding: "utf8", mode: 384 });
5373
+ try {
5374
+ chmodSync(state.filePath, 384);
5375
+ } catch {
5376
+ }
5377
+ state.knownEntryIds.add(entry.id);
5378
+ state.entryIdToParentId.set(entry.id, entry.parentId);
5379
+ state.lastEntryId = entry.id;
5380
+ }
5381
+ async function startSessionFilesConsumer(runtime, options) {
5382
+ const agentStates = /* @__PURE__ */ new Map();
5383
+ async function getOrInitState(agentId) {
5384
+ let state = agentStates.get(agentId);
5385
+ if (state) return state;
5386
+ const createdAt = await getAgentCreatedAt(runtime, agentId);
5387
+ const cwd = await getFirstRunCwd(runtime, agentId);
5388
+ const folderName = cwd.length > 0 ? `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--` : "--unknown--";
5389
+ const fileTimestamp = createdAt.replace(/[:.]/g, "-");
5390
+ const fileName = `${fileTimestamp}_${agentId}.jsonl`;
5391
+ const filePath = join3(options.dir, folderName, fileName);
5392
+ const knownEntryIds = /* @__PURE__ */ new Set();
5393
+ let lastEntryId = null;
5394
+ let lastModel;
5395
+ const entryIdToParentId = /* @__PURE__ */ new Map();
5396
+ const toolCallIdToName = /* @__PURE__ */ new Map();
5397
+ const writeHeader = () => {
5398
+ const header = { type: "session", version: 3, id: agentId, timestamp: createdAt, cwd };
5399
+ appendFileSync(filePath, `${JSON.stringify(header)}
5400
+ `, { encoding: "utf8", mode: 384 });
5401
+ };
5402
+ mkdirSync(dirname5(filePath), { recursive: true, mode: 448 });
5403
+ if (existsSync3(filePath)) {
5404
+ const content = readFileSync(filePath, "utf8");
5405
+ if (content.trim().length === 0) {
5406
+ writeHeader();
5407
+ } else {
5408
+ for (const line of content.split("\n")) {
5409
+ const trimmed = line.trim();
5410
+ if (!trimmed) continue;
5411
+ let parsed;
5412
+ try {
5413
+ parsed = JSON.parse(trimmed);
5414
+ } catch {
5415
+ continue;
5416
+ }
5417
+ if (parsed.type === "session") continue;
5418
+ if (typeof parsed.id === "string") {
5419
+ knownEntryIds.add(parsed.id);
5420
+ entryIdToParentId.set(parsed.id, parsed.parentId ?? null);
5421
+ lastEntryId = parsed.id;
5422
+ }
5423
+ if (parsed.type === "model_change" && typeof parsed.provider === "string" && typeof parsed.modelId === "string") {
5424
+ lastModel = { provider: parsed.provider, modelId: parsed.modelId };
5425
+ }
5426
+ if (parsed.type === "message" && parsed.message?.role === "assistant" && Array.isArray(parsed.message.content)) {
5427
+ for (const part of parsed.message.content) {
5428
+ if (part && part.type === "toolCall" && typeof part.id === "string" && typeof part.name === "string") {
5429
+ toolCallIdToName.set(part.id, part.name);
5430
+ }
5431
+ }
5432
+ }
5433
+ }
5434
+ }
5435
+ } else {
5436
+ writeHeader();
5437
+ }
5438
+ state = {
5439
+ filePath,
5440
+ knownEntryIds,
5441
+ lastEntryId,
5442
+ entryIdToParentId,
5443
+ toolCallIdToName,
5444
+ ...lastModel ? { lastModel } : {}
5445
+ };
5446
+ agentStates.set(agentId, state);
5447
+ return state;
5448
+ }
5449
+ async function onEvent(event) {
5450
+ try {
5451
+ if (event.kind !== "message_committed" && event.kind !== "message_revised") {
5452
+ return;
5453
+ }
5454
+ if (event.kind === "message_committed" && event.message.role === "interaction") {
5455
+ return;
5456
+ }
5457
+ const state = await getOrInitState(event.agentId);
5458
+ if (event.kind === "message_committed") {
5459
+ if (event.message.role === "user") {
5460
+ if (state.knownEntryIds.has(event.message.id)) return;
5461
+ const content = convertUserContent(event.message.content);
5462
+ const entry = {
5463
+ type: "message",
5464
+ id: event.message.id,
5465
+ parentId: state.lastEntryId,
5466
+ timestamp: event.message.createdAt,
5467
+ message: {
5468
+ role: "user",
5469
+ content,
5470
+ timestamp: toEpochMs(event.message.createdAt)
5471
+ }
5472
+ };
5473
+ writeSessionEntry(state, entry);
5474
+ } else if (event.message.role === "assistant") {
5475
+ if (state.knownEntryIds.has(event.message.id)) return;
5476
+ const model = event.message.model;
5477
+ const rawUsage = event.message.usage;
5478
+ if (model) {
5479
+ const isDifferentModel = !state.lastModel || state.lastModel.provider !== model.provider || state.lastModel.modelId !== model.id;
5480
+ if (isDifferentModel) {
5481
+ const modelChangeId = `${event.message.id}:model`;
5482
+ if (!state.knownEntryIds.has(modelChangeId)) {
5483
+ const modelChangeEntry = {
5484
+ type: "model_change",
5485
+ id: modelChangeId,
5486
+ parentId: state.lastEntryId,
5487
+ timestamp: event.message.createdAt,
5488
+ provider: model.provider,
5489
+ modelId: model.id
5490
+ };
5491
+ writeSessionEntry(state, modelChangeEntry);
5492
+ }
5493
+ state.lastModel = { provider: model.provider, modelId: model.id };
5494
+ }
5495
+ }
5496
+ const { parts, hasToolCall } = convertAssistantContent(
5497
+ event.message.content,
5498
+ (id, name, internalId) => {
5499
+ state.toolCallIdToName.set(id, name);
5500
+ if (internalId) state.toolCallIdToName.set(internalId, name);
5501
+ }
5502
+ );
5503
+ const assistantMessagePayload = {
5504
+ role: "assistant",
5505
+ content: parts,
5506
+ stopReason: hasToolCall ? "toolUse" : "stop",
5507
+ timestamp: toEpochMs(event.message.createdAt)
5508
+ };
5509
+ if (model || rawUsage) {
5510
+ if (model) {
5511
+ assistantMessagePayload.provider = model.provider;
5512
+ assistantMessagePayload.model = model.id;
5513
+ }
5514
+ const input = rawUsage?.inputTokens ?? 0;
5515
+ const output = rawUsage?.outputTokens ?? 0;
5516
+ const cacheRead = rawUsage?.cacheReadTokens ?? 0;
5517
+ const cacheWrite = rawUsage?.cacheWriteTokens ?? 0;
5518
+ const totalTokens = rawUsage?.totalTokens ?? input + output + cacheRead + cacheWrite;
5519
+ const cost = rawUsage?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
5520
+ assistantMessagePayload.usage = {
5521
+ input,
5522
+ output,
5523
+ cacheRead,
5524
+ cacheWrite,
5525
+ ...rawUsage?.cacheWrite1hTokens !== void 0 ? { cacheWrite1h: rawUsage.cacheWrite1hTokens } : {},
5526
+ ...rawUsage?.reasoningTokens !== void 0 ? { reasoning: rawUsage.reasoningTokens } : {},
5527
+ totalTokens,
5528
+ cost
5529
+ };
5530
+ }
5531
+ const entry = {
5532
+ type: "message",
5533
+ id: event.message.id,
5534
+ parentId: state.lastEntryId,
5535
+ timestamp: event.message.createdAt,
5536
+ message: assistantMessagePayload
5537
+ };
5538
+ writeSessionEntry(state, entry);
5539
+ } else if (event.message.role === "tool") {
5540
+ for (let index = 0; index < event.message.content.length; index++) {
5541
+ const part = event.message.content[index];
5542
+ const entryId = `${event.message.id}:${index}`;
5543
+ if (state.knownEntryIds.has(entryId)) continue;
5544
+ const toolCallId = part.providerToolCallId ?? part.toolCallId;
5545
+ const toolName = state.toolCallIdToName.get(toolCallId) ?? state.toolCallIdToName.get(part.toolCallId) ?? "unknown";
5546
+ const isError = Boolean(
5547
+ part.result.isError ?? (part.result.ok !== void 0 ? !part.result.ok : Boolean(part.result.error))
5548
+ );
5549
+ const content = convertToolResultContent(part.result.content);
5550
+ const entry = {
5551
+ type: "message",
5552
+ id: entryId,
5553
+ parentId: state.lastEntryId,
5554
+ timestamp: event.message.createdAt,
5555
+ message: {
5556
+ role: "toolResult",
5557
+ toolCallId,
5558
+ toolName,
5559
+ content,
5560
+ isError,
5561
+ timestamp: toEpochMs(event.message.createdAt)
5562
+ }
5563
+ };
5564
+ writeSessionEntry(state, entry);
5565
+ }
5566
+ }
5567
+ } else if (event.kind === "message_revised") {
5568
+ const message = event.message;
5569
+ const revision = message.messageRevision ?? 1;
5570
+ const entryId = `${message.id}@${revision}`;
5571
+ if (state.knownEntryIds.has(entryId)) return;
5572
+ const parentId = state.entryIdToParentId.get(message.id) ?? null;
5573
+ const timestampIso = message.createdAt ?? event.createdAt;
5574
+ const content = convertUserContent(message.content);
5575
+ const entry = {
5576
+ type: "message",
5577
+ id: entryId,
5578
+ parentId,
5579
+ timestamp: timestampIso,
5580
+ message: {
5581
+ role: "user",
5582
+ content,
5583
+ timestamp: toEpochMs(timestampIso)
5584
+ }
5585
+ };
5586
+ writeSessionEntry(state, entry);
5587
+ }
5588
+ } catch (error) {
5589
+ console.error("Failed to write session file entry:", error);
5590
+ }
5591
+ }
5592
+ return runtime.consume({ consumerId: "rowan:session-files", signal: new AbortController().signal, onEvent });
5593
+ }
5594
+
5254
5595
  // src/runtime/transient-run-events.ts
5255
5596
  var MAX_BUFFERED_EVENTS = 128;
5256
5597
  var MAX_BUFFERED_TEXT = 64 * 1024;
@@ -5485,6 +5826,9 @@ var InMemoryExtensionHost = class {
5485
5826
  globalState = /* @__PURE__ */ new Map();
5486
5827
  listeners = /* @__PURE__ */ new Set();
5487
5828
  constructor(options = {}) {
5829
+ if (options.extendApi) {
5830
+ this.extendApi = options.extendApi;
5831
+ }
5488
5832
  if (options.configs) {
5489
5833
  for (const [scopeKey, extMap] of Object.entries(options.configs)) {
5490
5834
  const map = /* @__PURE__ */ new Map();
@@ -5607,7 +5951,7 @@ function scopeToKey(scope) {
5607
5951
 
5608
5952
  // src/extensions/runner.ts
5609
5953
  import { execFile } from "child_process";
5610
- import { dirname as dirname5, resolve as resolve5 } from "path";
5954
+ import { dirname as dirname6, resolve as resolve5 } from "path";
5611
5955
  import {
5612
5956
  registerModel,
5613
5957
  unregisterProviderModels,
@@ -5770,8 +6114,6 @@ var ExtensionRunner = class {
5770
6114
  errorListeners = /* @__PURE__ */ new Set();
5771
6115
  // Capability change listeners
5772
6116
  capabilityListeners = /* @__PURE__ */ new Set();
5773
- // Host contribution change listeners
5774
- hostContributionListeners = /* @__PURE__ */ new Set();
5775
6117
  /** Current agent context — set by the agent before each phase */
5776
6118
  currentContext;
5777
6119
  constructor(options) {
@@ -5869,18 +6211,19 @@ var ExtensionRunner = class {
5869
6211
  async loadExtensions(extensions) {
5870
6212
  for (const ext of extensions) {
5871
6213
  const extensionId = ext.id ?? ext.manifest?.id ?? ext.name;
6214
+ const hostApi = this.host.extendApi?.(extensionId);
5872
6215
  const extension = {
5873
6216
  id: extensionId,
5874
6217
  path: ext.path,
5875
6218
  tools: /* @__PURE__ */ new Map(),
5876
6219
  phases: /* @__PURE__ */ new Set(),
5877
6220
  capabilities: /* @__PURE__ */ new Map(),
5878
- hostContributions: /* @__PURE__ */ new Map(),
5879
6221
  cleanup: [],
5880
- runtime: createExtensionRuntime()
6222
+ runtime: createExtensionRuntime(),
6223
+ hostApi
5881
6224
  };
5882
6225
  try {
5883
- const api = this.createExtensionAPI(extension, ext.manifest);
6226
+ const api = this.createExtensionAPI(extension, ext.manifest, hostApi?.namespaces);
5884
6227
  const disposer = await ext.factory(api);
5885
6228
  if (typeof disposer === "function") extension.disposer = disposer;
5886
6229
  this.extensions.push(extension);
@@ -5888,9 +6231,6 @@ var ExtensionRunner = class {
5888
6231
  if (extension.capabilities.size > 0) {
5889
6232
  this.notifyCapabilitiesChanged();
5890
6233
  }
5891
- if (extension.hostContributions.size > 0) {
5892
- this.notifyHostContributionsChanged();
5893
- }
5894
6234
  } catch (error) {
5895
6235
  await this.rollbackExtension(extension);
5896
6236
  const message = error instanceof Error ? error.message : String(error);
@@ -6019,24 +6359,6 @@ var ExtensionRunner = class {
6019
6359
  this.capabilityListeners.add(listener);
6020
6360
  return () => this.capabilityListeners.delete(listener);
6021
6361
  }
6022
- /** Get all contributed host elements across all extensions. */
6023
- getHostContributions() {
6024
- const result = [];
6025
- for (const ext of this.extensions) {
6026
- for (const contrib of ext.hostContributions.values()) {
6027
- result.push(contrib);
6028
- }
6029
- }
6030
- return Object.freeze(result);
6031
- }
6032
- /**
6033
- * Subscribe to host contribution changes (contributions added, removed, or extensions disposed).
6034
- * Returns an unsubscribe function.
6035
- */
6036
- onHostContributionsChanged(listener) {
6037
- this.hostContributionListeners.add(listener);
6038
- return () => this.hostContributionListeners.delete(listener);
6039
- }
6040
6362
  triggerUiAction(event) {
6041
6363
  this.events.emit("ui.action", event);
6042
6364
  }
@@ -6197,7 +6519,7 @@ var ExtensionRunner = class {
6197
6519
  * Registration methods write to the extension tracking object.
6198
6520
  * Action methods delegate to the shared runtime.
6199
6521
  */
6200
- createExtensionAPI(extension, manifest) {
6522
+ createExtensionAPI(extension, manifest, namespaces) {
6201
6523
  const runner = this;
6202
6524
  const extContext = {
6203
6525
  get cwd() {
@@ -6254,9 +6576,9 @@ var ExtensionRunner = class {
6254
6576
  registerTool: (tool) => this.registerTool(extension, tool),
6255
6577
  unregisterTool: (toolName) => this.unregisterTool(extension, toolName),
6256
6578
  contributeCapability: (contribution) => this.contributeCapability(extension, contribution),
6257
- contributeToHost: (contribution) => this.contributeToHost(extension, contribution),
6258
6579
  context: extContext,
6259
6580
  manifest,
6581
+ namespaces,
6260
6582
  trackCleanup: (cleanup) => extension.cleanup.push(cleanup),
6261
6583
  config: {
6262
6584
  get: async (scope) => {
@@ -6364,44 +6686,6 @@ var ExtensionRunner = class {
6364
6686
  }
6365
6687
  }
6366
6688
  }
6367
- contributeToHost(extension, contribution) {
6368
- const key = `${contribution.kind}:${contribution.id}`;
6369
- const entry = {
6370
- ...contribution
6371
- };
6372
- extension.hostContributions.set(key, entry);
6373
- if (this.extensions.includes(extension)) {
6374
- this.notifyHostContributionsChanged();
6375
- }
6376
- let disposed = false;
6377
- const dispose = () => {
6378
- if (disposed) return;
6379
- disposed = true;
6380
- if (extension.hostContributions.get(key) === entry) {
6381
- extension.hostContributions.delete(key);
6382
- if (this.extensions.includes(extension)) {
6383
- this.notifyHostContributionsChanged();
6384
- }
6385
- }
6386
- };
6387
- extension.cleanup.push(dispose);
6388
- return dispose;
6389
- }
6390
- notifyHostContributionsChanged() {
6391
- const contributions = this.getHostContributions();
6392
- for (const listener of this.hostContributionListeners) {
6393
- try {
6394
- listener(contributions);
6395
- } catch (error) {
6396
- this.emitError({
6397
- extensionPath: "<runtime>",
6398
- event: "host_contributions_changed",
6399
- error: error instanceof Error ? error.message : String(error),
6400
- stack: error instanceof Error ? error.stack : void 0
6401
- });
6402
- }
6403
- }
6404
- }
6405
6689
  async registerPhase(extension, registration) {
6406
6690
  if (typeof registration === "string") {
6407
6691
  if (registration.length === 0) {
@@ -6436,7 +6720,7 @@ var ExtensionRunner = class {
6436
6720
  }
6437
6721
  }
6438
6722
  async loadRegisteredPhase(extension, registration) {
6439
- const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname5(extension.path);
6723
+ const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname6(extension.path);
6440
6724
  const phase = await loadPhase(resolve5(extensionBase, registration));
6441
6725
  const name = phase.name;
6442
6726
  if (this.phases.has(name)) {
@@ -6490,21 +6774,21 @@ var ExtensionRunner = class {
6490
6774
  for (const name of extension.phases) this.phases.delete(name);
6491
6775
  const hadCapabilities = extension.capabilities.size > 0;
6492
6776
  extension.capabilities.clear();
6493
- const hadHostContributions = extension.hostContributions.size > 0;
6494
- extension.hostContributions.clear();
6495
6777
  for (const cleanup of [...extension.cleanup].reverse()) {
6496
6778
  await Promise.resolve().then(() => cleanup()).catch(() => void 0);
6497
6779
  }
6498
6780
  if (extension.disposer) {
6499
6781
  await Promise.resolve().then(() => extension.disposer()).catch(() => void 0);
6500
6782
  }
6783
+ if (extension.hostApi?.dispose) {
6784
+ const hostDispose = extension.hostApi.dispose;
6785
+ extension.hostApi = { ...extension.hostApi, dispose: void 0 };
6786
+ await Promise.resolve().then(() => hostDispose()).catch(() => void 0);
6787
+ }
6501
6788
  this._phaseCache = null;
6502
6789
  if (hadCapabilities) {
6503
6790
  this.notifyCapabilitiesChanged();
6504
6791
  }
6505
- if (hadHostContributions) {
6506
- this.notifyHostContributionsChanged();
6507
- }
6508
6792
  }
6509
6793
  };
6510
6794
  function createExtensionRunner(options) {
@@ -6512,10 +6796,10 @@ function createExtensionRunner(options) {
6512
6796
  }
6513
6797
 
6514
6798
  // src/extensions/loader.ts
6515
- import { existsSync as existsSync3, readFileSync } from "fs";
6799
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
6516
6800
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
6517
6801
  import { fileURLToPath } from "url";
6518
- import { basename as basename3, dirname as dirname6, extname as extname2, join as join3, resolve as resolve6 } from "path";
6802
+ import { basename as basename3, dirname as dirname7, extname as extname2, join as join4, resolve as resolve6 } from "path";
6519
6803
  import { createJiti } from "jiti";
6520
6804
  function isExtensionFile(path) {
6521
6805
  const extension = extname2(path).toLowerCase();
@@ -6525,12 +6809,12 @@ function isSyntheticPath(path) {
6525
6809
  return path.startsWith("<") && path.endsWith(">");
6526
6810
  }
6527
6811
  function readManifestSync(dir) {
6528
- const manifestPath = join3(dir, "package.json");
6529
- if (!existsSync3(manifestPath)) {
6812
+ const manifestPath = join4(dir, "package.json");
6813
+ if (!existsSync4(manifestPath)) {
6530
6814
  return void 0;
6531
6815
  }
6532
6816
  try {
6533
- const content = readFileSync(manifestPath, "utf8");
6817
+ const content = readFileSync2(manifestPath, "utf8");
6534
6818
  const pkg = JSON.parse(content);
6535
6819
  const rowan = pkg.rowan;
6536
6820
  if (!rowan) return void 0;
@@ -6549,7 +6833,7 @@ function jitiAliases() {
6549
6833
  return {};
6550
6834
  }
6551
6835
  const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
6552
- if (!existsSync3(agentSourcePath)) {
6836
+ if (!existsSync4(agentSourcePath)) {
6553
6837
  return {};
6554
6838
  }
6555
6839
  return { "@rowan-agent/agent": agentSourcePath };
@@ -6573,21 +6857,21 @@ async function loadExtensionModule(extensionPath) {
6573
6857
  return typeof module === "function" ? module : void 0;
6574
6858
  }
6575
6859
  async function readPackageManifest(path) {
6576
- if (!existsSync3(path)) {
6860
+ if (!existsSync4(path)) {
6577
6861
  return void 0;
6578
6862
  }
6579
6863
  const content = await readFile3(path, "utf8");
6580
6864
  return JSON.parse(content);
6581
6865
  }
6582
6866
  async function resolveExtensionEntries(dir) {
6583
- const manifest = await readPackageManifest(join3(dir, "package.json"));
6867
+ const manifest = await readPackageManifest(join4(dir, "package.json"));
6584
6868
  const declared = manifest?.rowan?.extensions;
6585
6869
  if (declared && declared.length > 0) {
6586
6870
  return declared.map((entry) => resolve6(dir, entry));
6587
6871
  }
6588
6872
  for (const name of ["index.ts", "index.js"]) {
6589
- const entry = join3(dir, name);
6590
- if (existsSync3(entry)) {
6873
+ const entry = join4(dir, name);
6874
+ if (existsSync4(entry)) {
6591
6875
  return [entry];
6592
6876
  }
6593
6877
  }
@@ -6602,7 +6886,7 @@ async function discoverExtensionsInDir(dir) {
6602
6886
  });
6603
6887
  const paths = [];
6604
6888
  for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
6605
- const entryPath = join3(dir, entry.name);
6889
+ const entryPath = join4(dir, entry.name);
6606
6890
  if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
6607
6891
  paths.push(entryPath);
6608
6892
  continue;
@@ -6622,8 +6906,8 @@ async function discoverExtensionsInDir(dir) {
6622
6906
  function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>", options = {}) {
6623
6907
  const resolvedCwd = resolve6(cwd);
6624
6908
  const resolvedPath = isSyntheticPath(extensionPath) ? extensionPath : resolve6(resolvedCwd, extensionPath);
6625
- const name = options.name ?? (isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname6(resolvedPath)) || "unknown");
6626
- const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname6(resolvedPath);
6909
+ const name = options.name ?? (isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname7(resolvedPath)) || "unknown");
6910
+ const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname7(resolvedPath);
6627
6911
  const manifest = readManifestSync(manifestDir);
6628
6912
  const id = options.id ?? manifest?.id ?? name;
6629
6913
  return {
@@ -6663,7 +6947,7 @@ async function loadExtensionsFromPath(path) {
6663
6947
  const resolvedPath = resolve6(path);
6664
6948
  const info = await stat2(resolvedPath);
6665
6949
  if (!info.isDirectory()) {
6666
- return loadExtensions([resolvedPath], dirname6(resolvedPath));
6950
+ return loadExtensions([resolvedPath], dirname7(resolvedPath));
6667
6951
  }
6668
6952
  const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
6669
6953
  return loadExtensions(entries, resolvedPath);
@@ -6671,7 +6955,7 @@ async function loadExtensionsFromPath(path) {
6671
6955
 
6672
6956
  // src/runtime/resource-registry.ts
6673
6957
  import { stat as stat3, readdir as readdir4 } from "fs/promises";
6674
- import { join as join4, resolve as resolve7 } from "path";
6958
+ import { join as join5, resolve as resolve7 } from "path";
6675
6959
  var ResourceRegistryError = class extends Error {
6676
6960
  code;
6677
6961
  constructor(code, message) {
@@ -6989,7 +7273,7 @@ async function loadDirectoryValues(kind, directory, sourceId) {
6989
7273
  if (rootInfo.isFile()) {
6990
7274
  targets.push(root);
6991
7275
  } else if (rootInfo.isDirectory()) {
6992
- const markerAtRoot = join4(root, marker);
7276
+ const markerAtRoot = join5(root, marker);
6993
7277
  try {
6994
7278
  const markerInfo = await stat3(markerAtRoot);
6995
7279
  if (markerInfo.isFile()) targets.push(markerAtRoot);
@@ -7011,7 +7295,7 @@ async function loadDirectoryValues(kind, directory, sourceId) {
7011
7295
  }
7012
7296
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
7013
7297
  if (!entry.isDirectory()) continue;
7014
- const target = join4(root, entry.name, marker);
7298
+ const target = join5(root, entry.name, marker);
7015
7299
  try {
7016
7300
  const targetInfo = await stat3(target);
7017
7301
  if (targetInfo.isFile()) targets.push(target);
@@ -7267,7 +7551,7 @@ async function spillLargeToolResult(result, archiveDir, toolName) {
7267
7551
  await mkdir2(toolDir, { recursive: true, mode: 448 });
7268
7552
  await chmod2(toolDir, 448).catch(() => void 0);
7269
7553
  const safeToolName = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_");
7270
- const path = join5(toolDir, `rowan-${safeToolName}-${Date.now()}-${randomUUID()}.log`);
7554
+ const path = join6(toolDir, `rowan-${safeToolName}-${Date.now()}-${randomUUID()}.log`);
7271
7555
  await writeFile2(path, serialized, { encoding: "utf8", mode: 384 });
7272
7556
  await chmod2(path, 384).catch(() => void 0);
7273
7557
  const preview = new TextDecoder().decode(bytes.subarray(0, MAX_INLINE_TOOL_RESULT_BYTES));
@@ -7364,6 +7648,9 @@ var AgentRuntime = class _AgentRuntime {
7364
7648
  await options.bootstrap?.(runtime.resources);
7365
7649
  runtime.startHeartbeat();
7366
7650
  void runtime.pump();
7651
+ if (options.sessionFiles) {
7652
+ await startSessionFilesConsumer(runtime, options.sessionFiles);
7653
+ }
7367
7654
  return runtime;
7368
7655
  } catch (error) {
7369
7656
  await runtime.resources.closeExtensions().catch(() => void 0);
@@ -7624,14 +7911,6 @@ var AgentRuntime = class _AgentRuntime {
7624
7911
  this.assertOpen();
7625
7912
  return this.resources.extensionRunner.onCapabilitiesChanged(listener);
7626
7913
  }
7627
- listHostContributions() {
7628
- this.assertOpen();
7629
- return this.resources.extensionRunner.getHostContributions();
7630
- }
7631
- onHostContributionsChanged(listener) {
7632
- this.assertOpen();
7633
- return this.resources.extensionRunner.onHostContributionsChanged(listener);
7634
- }
7635
7914
  triggerUiAction(event) {
7636
7915
  this.assertOpen();
7637
7916
  this.resources.extensionRunner.triggerUiAction(event);
@@ -8108,7 +8387,7 @@ var AgentRuntime = class _AgentRuntime {
8108
8387
  onContext: assembly.setContext,
8109
8388
  runtime: {
8110
8389
  interact: (request) => this.liveInteract(run.id, request, controller.signal),
8111
- tools: ({ toolCall, contentBlocks, driver, options, providerInitiated }) => {
8390
+ tools: ({ toolCall, contentBlocks, driver, options, providerInitiated, model: callModel, usage: callUsage }) => {
8112
8391
  const task = toolQueue.then(async () => {
8113
8392
  if (!providerInitiated) {
8114
8393
  const execution = await this.executeToolBatch({
@@ -8122,6 +8401,8 @@ var AgentRuntime = class _AgentRuntime {
8122
8401
  signal: controller.signal,
8123
8402
  driver,
8124
8403
  options,
8404
+ model: callModel ?? model,
8405
+ usage: callUsage,
8125
8406
  onRevision: (rev) => {
8126
8407
  executionRevision = rev;
8127
8408
  }
@@ -8146,6 +8427,8 @@ var AgentRuntime = class _AgentRuntime {
8146
8427
  options,
8147
8428
  providerInitiated: true,
8148
8429
  interactionIds,
8430
+ model: callModel ?? model,
8431
+ usage: callUsage,
8149
8432
  onRevision: (rev) => {
8150
8433
  executionRevision = rev;
8151
8434
  }
@@ -8236,7 +8519,7 @@ var AgentRuntime = class _AgentRuntime {
8236
8519
  toolQueue = task.then(() => void 0, () => void 0);
8237
8520
  return task;
8238
8521
  },
8239
- toolsBatch: ({ toolCalls, contentBlocks, driver, options }) => {
8522
+ toolsBatch: ({ toolCalls, contentBlocks, driver, options, model: batchModel, usage: batchUsage }) => {
8240
8523
  const task = toolQueue.then(async () => {
8241
8524
  const execution = await this.executeToolBatch({
8242
8525
  run,
@@ -8249,6 +8532,8 @@ var AgentRuntime = class _AgentRuntime {
8249
8532
  signal: controller.signal,
8250
8533
  driver,
8251
8534
  options,
8535
+ model: batchModel ?? model,
8536
+ usage: batchUsage,
8252
8537
  onRevision: (rev) => {
8253
8538
  executionRevision = rev;
8254
8539
  }
@@ -8476,6 +8761,8 @@ var AgentRuntime = class _AgentRuntime {
8476
8761
  expectedRevision: revision,
8477
8762
  requestMessageId: createId("msg"),
8478
8763
  contentBlocks: input.contentBlocks,
8764
+ ...input.model ? { model: input.model } : {},
8765
+ ...input.usage ? { usage: input.usage } : {},
8479
8766
  calls: input.toolCalls.map((tc, i) => ({
8480
8767
  toolCallId: toolCallIds[i],
8481
8768
  providerToolCallId: tc.id,
@@ -8611,6 +8898,8 @@ var AgentRuntime = class _AgentRuntime {
8611
8898
  expectedRevision: revision,
8612
8899
  requestMessageId: createId("msg"),
8613
8900
  contentBlocks: input.contentBlocks,
8901
+ ...input.model ? { model: input.model } : {},
8902
+ ...input.usage ? { usage: input.usage } : {},
8614
8903
  calls: [{
8615
8904
  toolCallId,
8616
8905
  providerToolCallId,
@@ -8708,6 +8997,8 @@ var AgentRuntime = class _AgentRuntime {
8708
8997
  expectedRevision: revision,
8709
8998
  requestMessageId: createId("msg"),
8710
8999
  contentBlocks: input.contentBlocks,
9000
+ ...input.model ? { model: input.model } : {},
9001
+ ...input.usage ? { usage: input.usage } : {},
8711
9002
  calls: [{
8712
9003
  toolCallId,
8713
9004
  providerToolCallId,
@@ -8729,6 +9020,8 @@ var AgentRuntime = class _AgentRuntime {
8729
9020
  expectedRevision: revision,
8730
9021
  requestMessageId: createId("msg"),
8731
9022
  contentBlocks: input.contentBlocks,
9023
+ ...input.model ? { model: input.model } : {},
9024
+ ...input.usage ? { usage: input.usage } : {},
8732
9025
  calls: [{
8733
9026
  toolCallId,
8734
9027
  providerToolCallId,
@@ -8783,6 +9076,8 @@ var AgentRuntime = class _AgentRuntime {
8783
9076
  expectedRevision: revision,
8784
9077
  requestMessageId: createId("msg"),
8785
9078
  contentBlocks: input.contentBlocks,
9079
+ ...input.model ? { model: input.model } : {},
9080
+ ...input.usage ? { usage: input.usage } : {},
8786
9081
  calls: [{
8787
9082
  toolCallId,
8788
9083
  providerToolCallId,
@@ -8832,6 +9127,8 @@ var AgentRuntime = class _AgentRuntime {
8832
9127
  expectedRevision: revision,
8833
9128
  requestMessageId: createId("msg"),
8834
9129
  contentBlocks: input.contentBlocks,
9130
+ ...input.model ? { model: input.model } : {},
9131
+ ...input.usage ? { usage: input.usage } : {},
8835
9132
  calls: [{
8836
9133
  toolCallId,
8837
9134
  providerToolCallId,
@@ -9156,7 +9453,7 @@ ${content}`;
9156
9453
  archiveDirFor(agentId) {
9157
9454
  const store = this.owned;
9158
9455
  const archiveDir = store.contextArchiveDir?.(agentId);
9159
- return archiveDir ? join5(archiveDir, "tool-results") : void 0;
9456
+ return archiveDir ? join6(archiveDir, "tool-results") : void 0;
9160
9457
  }
9161
9458
  async runConsumer(subscription) {
9162
9459
  const registration = await this.owned.openConsumer(subscription.consumerId);
@@ -10314,6 +10611,8 @@ var InMemoryStore = class _InMemoryStore {
10314
10611
  execution: input.execution,
10315
10612
  expectedRevision: input.expectedRevision,
10316
10613
  requestMessageId: input.requestMessageId,
10614
+ ...input.model ? { model: input.model } : {},
10615
+ ...input.usage ? { usage: input.usage } : {},
10317
10616
  calls: [{
10318
10617
  providerToolCallId,
10319
10618
  name: input.name,
@@ -10345,7 +10644,9 @@ var InMemoryStore = class _InMemoryStore {
10345
10644
  input.execution.executionId,
10346
10645
  input.requestMessageId,
10347
10646
  input.calls,
10348
- input.contentBlocks ?? null
10647
+ input.contentBlocks ?? null,
10648
+ input.model ?? null,
10649
+ input.usage ?? null
10349
10650
  ]);
10350
10651
  const replay = this.replayOperation(operationKey, operationPayload);
10351
10652
  if (replay) return clone(replay);
@@ -10387,7 +10688,9 @@ var InMemoryStore = class _InMemoryStore {
10387
10688
  role: "assistant",
10388
10689
  content: requestMessageContent(toolCalls, input.contentBlocks),
10389
10690
  sequenceWithinRun: this.nextMessageSequence(run.id),
10390
- createdAt: timestamp
10691
+ createdAt: timestamp,
10692
+ ...input.model ? { model: input.model } : {},
10693
+ ...input.usage ? { usage: input.usage } : {}
10391
10694
  };
10392
10695
  for (const toolCall of toolCalls) this.storeToolCall(toolCall);
10393
10696
  this.storeMessage(requestMessage);
@@ -11439,8 +11742,8 @@ function requestMessageContent(toolCalls, contentBlocks) {
11439
11742
 
11440
11743
  // src/runtime/sqlite-durable-store.ts
11441
11744
  import { Database } from "bun:sqlite";
11442
- import { appendFileSync, chmodSync, mkdirSync, readFileSync as readFileSync2, rmSync } from "fs";
11443
- import { dirname as dirname7, join as join6, resolve as resolve8 } from "path";
11745
+ import { appendFileSync as appendFileSync2, chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync } from "fs";
11746
+ import { dirname as dirname8, join as join7, resolve as resolve8 } from "path";
11444
11747
  import { tmpdir as tmpdir2 } from "os";
11445
11748
  var SCHEMA_ID = "rowan.agent.runtime";
11446
11749
  var SCHEMA_VERSION = "2";
@@ -11661,7 +11964,7 @@ var SqliteStore = class {
11661
11964
  stmtUpdateState;
11662
11965
  constructor(filename = ":memory:") {
11663
11966
  this.database = new Database(filename, { create: true, readwrite: true, strict: true });
11664
- this.archiveRoot = filename === ":memory:" ? join6(tmpdir2(), `rowan-context-archives-${crypto.randomUUID()}`) : join6(dirname7(resolve8(filename)), "context-archives");
11967
+ this.archiveRoot = filename === ":memory:" ? join7(tmpdir2(), `rowan-context-archives-${crypto.randomUUID()}`) : join7(dirname8(resolve8(filename)), "context-archives");
11665
11968
  }
11666
11969
  async openOwner(input) {
11667
11970
  this.assertOpen();
@@ -11755,7 +12058,7 @@ var SqliteStore = class {
11755
12058
  }
11756
12059
  /** Filesystem location owned by this Store for one Agent's readable archive. */
11757
12060
  contextArchiveDir(agentId) {
11758
- return join6(this.archiveRoot, String(agentId));
12061
+ return join7(this.archiveRoot, String(agentId));
11759
12062
  }
11760
12063
  async reserveAgent(lease, input) {
11761
12064
  return this.invoke(lease, (store, current) => store.reserveAgent(current, input));
@@ -12351,7 +12654,7 @@ var SqliteStore = class {
12351
12654
  mirrorChanges(tracker) {
12352
12655
  for (const agentId of tracker.deletedAgentIds) {
12353
12656
  const idStr = String(agentId);
12354
- rmSync(join6(this.archiveRoot, idStr), { recursive: true, force: true });
12657
+ rmSync(join7(this.archiveRoot, idStr), { recursive: true, force: true });
12355
12658
  this.mirroredEventIds.delete(idStr);
12356
12659
  this.mirroredCompactionIds.delete(idStr);
12357
12660
  this.mirroredAgents.delete(idStr);
@@ -12359,15 +12662,15 @@ var SqliteStore = class {
12359
12662
  for (const agent of tracker.upsertedAgents.values()) {
12360
12663
  const idStr = String(agent.id);
12361
12664
  this.mirroredAgents.add(idStr);
12362
- const archiveDir = join6(this.archiveRoot, idStr);
12363
- const toolDir = join6(archiveDir, "tool-results");
12364
- mkdirSync(toolDir, { recursive: true, mode: 448 });
12365
- chmodSync(archiveDir, 448);
12366
- chmodSync(toolDir, 448);
12665
+ const archiveDir = join7(this.archiveRoot, idStr);
12666
+ const toolDir = join7(archiveDir, "tool-results");
12667
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12668
+ chmodSync2(archiveDir, 448);
12669
+ chmodSync2(toolDir, 448);
12367
12670
  }
12368
12671
  if (tracker.appendedEvents.length > 0) {
12369
- mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
12370
- chmodSync(this.archiveRoot, 448);
12672
+ mkdirSync2(this.archiveRoot, { recursive: true, mode: 448 });
12673
+ chmodSync2(this.archiveRoot, 448);
12371
12674
  const eventsByAgent = /* @__PURE__ */ new Map();
12372
12675
  for (const event of tracker.appendedEvents) {
12373
12676
  const agentId = String(event.agentId);
@@ -12379,22 +12682,22 @@ var SqliteStore = class {
12379
12682
  list.push(event);
12380
12683
  }
12381
12684
  for (const [agentId, events] of eventsByAgent) {
12382
- const archiveDir = join6(this.archiveRoot, agentId);
12383
- const toolDir = join6(archiveDir, "tool-results");
12384
- mkdirSync(toolDir, { recursive: true, mode: 448 });
12385
- chmodSync(archiveDir, 448);
12386
- chmodSync(toolDir, 448);
12387
- const sessionPath = join6(archiveDir, "session.jsonl");
12685
+ const archiveDir = join7(this.archiveRoot, agentId);
12686
+ const toolDir = join7(archiveDir, "tool-results");
12687
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12688
+ chmodSync2(archiveDir, 448);
12689
+ chmodSync2(toolDir, 448);
12690
+ const sessionPath = join7(archiveDir, "session.jsonl");
12388
12691
  try {
12389
- chmodSync(sessionPath, 384);
12692
+ chmodSync2(sessionPath, 384);
12390
12693
  } catch {
12391
12694
  }
12392
12695
  const known = this.mirroredEventIds.get(agentId) ?? /* @__PURE__ */ new Set();
12393
12696
  const newEvents = events.filter((e) => !known.has(String(e.id)));
12394
12697
  if (newEvents.length > 0) {
12395
12698
  const lines = newEvents.map((e) => JSON.stringify({ schemaVersion: 1, kind: "rowan_event", event: e }) + "\n").join("");
12396
- appendFileSync(sessionPath, lines, { encoding: "utf8", mode: 384 });
12397
- chmodSync(sessionPath, 384);
12699
+ appendFileSync2(sessionPath, lines, { encoding: "utf8", mode: 384 });
12700
+ chmodSync2(sessionPath, 384);
12398
12701
  for (const e of newEvents) known.add(String(e.id));
12399
12702
  this.mirroredEventIds.set(agentId, known);
12400
12703
  }
@@ -12403,13 +12706,13 @@ var SqliteStore = class {
12403
12706
  if (tracker.upsertedContextCompactions.size > 0) {
12404
12707
  for (const [agentId, record] of tracker.upsertedContextCompactions) {
12405
12708
  const idStr = String(agentId);
12406
- const archiveDir = join6(this.archiveRoot, idStr);
12407
- const sessionPath = join6(archiveDir, "session.jsonl");
12709
+ const archiveDir = join7(this.archiveRoot, idStr);
12710
+ const sessionPath = join7(archiveDir, "session.jsonl");
12408
12711
  const knownCompactions = this.mirroredCompactionIds.get(idStr) ?? /* @__PURE__ */ new Set();
12409
12712
  if (!knownCompactions.has(record.id)) {
12410
12713
  const line = JSON.stringify({ schemaVersion: 1, kind: "context_compacted", record }) + "\n";
12411
- appendFileSync(sessionPath, line, { encoding: "utf8", mode: 384 });
12412
- chmodSync(sessionPath, 384);
12714
+ appendFileSync2(sessionPath, line, { encoding: "utf8", mode: 384 });
12715
+ chmodSync2(sessionPath, 384);
12413
12716
  knownCompactions.add(record.id);
12414
12717
  this.mirroredCompactionIds.set(idStr, knownCompactions);
12415
12718
  }
@@ -12418,32 +12721,32 @@ var SqliteStore = class {
12418
12721
  }
12419
12722
  /** Append the semantic durable event mirror and remove deleted Agent sidecars. */
12420
12723
  mirrorInitialState(state) {
12421
- mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
12422
- chmodSync(this.archiveRoot, 448);
12724
+ mkdirSync2(this.archiveRoot, { recursive: true, mode: 448 });
12725
+ chmodSync2(this.archiveRoot, 448);
12423
12726
  const currentAgents = new Set(state.agents.map((agent) => String(agent.id)));
12424
12727
  for (const agentId of this.mirroredAgents) {
12425
12728
  if (currentAgents.has(agentId)) continue;
12426
- rmSync(join6(this.archiveRoot, agentId), { recursive: true, force: true });
12729
+ rmSync(join7(this.archiveRoot, agentId), { recursive: true, force: true });
12427
12730
  this.mirroredEventIds.delete(agentId);
12428
12731
  this.mirroredCompactionIds.delete(agentId);
12429
12732
  }
12430
12733
  for (const agent of state.agents) {
12431
12734
  const agentId = String(agent.id);
12432
- const archiveDir = join6(this.archiveRoot, agentId);
12433
- const toolDir = join6(archiveDir, "tool-results");
12434
- mkdirSync(toolDir, { recursive: true, mode: 448 });
12435
- chmodSync(archiveDir, 448);
12436
- chmodSync(toolDir, 448);
12437
- const sessionPath = join6(archiveDir, "session.jsonl");
12735
+ const archiveDir = join7(this.archiveRoot, agentId);
12736
+ const toolDir = join7(archiveDir, "tool-results");
12737
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12738
+ chmodSync2(archiveDir, 448);
12739
+ chmodSync2(toolDir, 448);
12740
+ const sessionPath = join7(archiveDir, "session.jsonl");
12438
12741
  try {
12439
- chmodSync(sessionPath, 384);
12742
+ chmodSync2(sessionPath, 384);
12440
12743
  } catch {
12441
12744
  }
12442
12745
  const known = this.mirroredEventIds.get(agentId) ?? /* @__PURE__ */ new Set();
12443
12746
  const knownCompactions = this.mirroredCompactionIds.get(agentId) ?? /* @__PURE__ */ new Set();
12444
12747
  if (known.size === 0 || knownCompactions.size === 0) {
12445
12748
  try {
12446
- for (const line of readFileSync2(sessionPath, "utf8").split("\n")) {
12749
+ for (const line of readFileSync3(sessionPath, "utf8").split("\n")) {
12447
12750
  if (!line.trim()) continue;
12448
12751
  const parsed = JSON.parse(line);
12449
12752
  if (parsed.event?.id) known.add(parsed.event.id);
@@ -12454,16 +12757,16 @@ var SqliteStore = class {
12454
12757
  }
12455
12758
  const lines = state.events.filter((event) => String(event.agentId) === agentId && !known.has(String(event.id))).map((event) => JSON.stringify({ schemaVersion: 1, kind: "rowan_event", event }) + "\n");
12456
12759
  if (lines.length > 0) {
12457
- appendFileSync(sessionPath, lines.join(""), { encoding: "utf8", mode: 384 });
12458
- chmodSync(sessionPath, 384);
12760
+ appendFileSync2(sessionPath, lines.join(""), { encoding: "utf8", mode: 384 });
12761
+ chmodSync2(sessionPath, 384);
12459
12762
  for (const event of state.events) {
12460
12763
  if (String(event.agentId) === agentId) known.add(String(event.id));
12461
12764
  }
12462
12765
  }
12463
12766
  const compactionLines = (state.contextCompactions ?? []).filter(([candidateId, record]) => String(candidateId) === agentId && !knownCompactions.has(record.id)).map(([, record]) => JSON.stringify({ schemaVersion: 1, kind: "context_compacted", record }) + "\n");
12464
12767
  if (compactionLines.length > 0) {
12465
- appendFileSync(sessionPath, compactionLines.join(""), { encoding: "utf8", mode: 384 });
12466
- chmodSync(sessionPath, 384);
12768
+ appendFileSync2(sessionPath, compactionLines.join(""), { encoding: "utf8", mode: 384 });
12769
+ chmodSync2(sessionPath, 384);
12467
12770
  for (const [, record] of state.contextCompactions ?? []) {
12468
12771
  if (String(record.agentId) === agentId) knownCompactions.add(record.id);
12469
12772
  }