@rowan-agent/agent 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _rowan_agent_models from '@rowan-agent/models';
2
- import { ContentBlock, LlmContentPart, ModelRef, ScopeRef, ToolCallStatus, JsonValue as JsonValue$1, ToolProgress, ToolCallContent, ToolCallLocation, JsonObject as JsonObject$1, ToolKind, ModelConfig, StreamFn, ToolAnnotations, ThinkingLevel, ToolCall as ToolCall$1, ToolCallUpdate } from '@rowan-agent/models';
2
+ import { ContentBlock, ModelRef, LlmTokenUsage, LlmContentPart, ScopeRef, ToolCallStatus, JsonValue as JsonValue$1, ToolProgress, ToolCallContent, ToolCallLocation, JsonObject as JsonObject$1, ToolKind, ModelConfig, StreamFn, ToolAnnotations, ThinkingLevel, ToolCall as ToolCall$1, ToolCallUpdate } from '@rowan-agent/models';
3
3
  export { ModelConfig, ModelRef, ProviderCallContext, ProviderConfig, ProviderModelConfig, ProviderStreamFn, ScopeRef, StreamFn, ThinkingLevel, ToolAnnotations, ToolCall, ToolCallContent, ToolCallLocation, ToolCallOptions, ToolCallOutcome, ToolCallStatus, ToolCallUpdate, ToolDefinitionSummary, ToolKind, ToolProgress } from '@rowan-agent/models';
4
4
  import Type from 'typebox';
5
5
 
@@ -8,6 +8,8 @@ type ModelInvokeOutput = {
8
8
  contentBlocks: ContentBlock[];
9
9
  toolCalls: ToolCall[];
10
10
  stopReason?: string;
11
+ model?: ModelRef;
12
+ usage?: LlmTokenUsage;
11
13
  };
12
14
  /** Snapshot of phase-level context for restore */
13
15
  type PhaseContextSnapshot = {
@@ -27,7 +29,10 @@ type PhaseExecution = {
27
29
  }): Promise<ModelInvokeOutput>;
28
30
  reportStatus(status: PhaseStatus): Promise<void>;
29
31
  executeTool(context: AgentContext, toolCall: ToolCall): Promise<ToolResult>;
30
- executeTools(context: AgentContext, toolCalls: readonly ToolCall[], contentBlocks?: readonly ContentBlock[]): Promise<readonly ToolResult[]>;
32
+ executeTools(context: AgentContext, toolCalls: readonly ToolCall[], contentBlocks?: readonly ContentBlock[], meta?: {
33
+ model?: ModelRef;
34
+ usage?: LlmTokenUsage;
35
+ }): Promise<readonly ToolResult[]>;
31
36
  interaction: RunInteractionDriver;
32
37
  /** Message lifecycle manager for streaming updates from programmatic phases */
33
38
  messages: PhaseMessageManager;
@@ -45,7 +50,10 @@ type PhaseMessageManager = {
45
50
  /** Replace the active message content with model-native content parts */
46
51
  replaceContent(messageId: string, content: string | LlmContentPart[]): void;
47
52
  /** End the message stream, appends to transcript */
48
- end(messageId: string): Promise<void>;
53
+ end(messageId: string, meta?: {
54
+ model?: ModelRef;
55
+ usage?: LlmTokenUsage;
56
+ }): Promise<void>;
49
57
  /** Drop an unstarted stream that produced no content. */
50
58
  discard(messageId: string): void;
51
59
  };
@@ -1078,6 +1086,8 @@ interface OwnedStore {
1078
1086
  locations?: ToolCallLocation[];
1079
1087
  content?: ToolCallContent[];
1080
1088
  _meta?: JsonObject;
1089
+ model?: ModelRef;
1090
+ usage?: LlmTokenUsage;
1081
1091
  }): Promise<ToolCommit>;
1082
1092
  reserveToolCalls(input: {
1083
1093
  runId: RunId;
@@ -1087,6 +1097,8 @@ interface OwnedStore {
1087
1097
  calls: readonly ToolCallReservation[];
1088
1098
  /** The model response's blocks, so the request message keeps what preceded the Tool Calls. */
1089
1099
  contentBlocks?: readonly ContentBlock[];
1100
+ model?: ModelRef;
1101
+ usage?: LlmTokenUsage;
1090
1102
  }): Promise<ToolBatchCommit>;
1091
1103
  startToolCall(input: {
1092
1104
  runId: RunId;
@@ -1163,6 +1175,9 @@ type AgentRuntimeOptions = Readonly<{
1163
1175
  concurrency?: number;
1164
1176
  bootstrap?: (registry: RuntimeBootstrapRegistry) => void | Promise<void>;
1165
1177
  host?: ExtensionHost;
1178
+ sessionFiles?: Readonly<{
1179
+ dir: string;
1180
+ }>;
1166
1181
  /** @internal Private / testing parameter to override retry delay */
1167
1182
  retryDelayMs?: number;
1168
1183
  }>;
@@ -2088,6 +2103,8 @@ type AssistantMessage = MessageBase & Readonly<{
2088
2103
  role: "assistant";
2089
2104
  content: AssistantContent;
2090
2105
  interrupted?: boolean;
2106
+ model?: ModelRef;
2107
+ usage?: LlmTokenUsage;
2091
2108
  }>;
2092
2109
  type ToolMessage = MessageBase & Readonly<{
2093
2110
  role: "tool";
@@ -2343,6 +2360,8 @@ type AgentMessage = {
2343
2360
  phase?: string;
2344
2361
  };
2345
2362
  contexts?: readonly ContextCandidate[];
2363
+ model?: ModelRef;
2364
+ usage?: LlmTokenUsage;
2346
2365
  };
2347
2366
  type Skill = {
2348
2367
  name: string;
@@ -2754,6 +2773,8 @@ declare class InMemoryStore implements DurableStore {
2754
2773
  args: JsonValue;
2755
2774
  toolCallId?: ToolCallId;
2756
2775
  providerToolCallId?: string;
2776
+ model?: ModelRef;
2777
+ usage?: LlmTokenUsage;
2757
2778
  }): ToolCommit;
2758
2779
  reserveToolCalls(lease: OwnerLease, input: {
2759
2780
  runId: RunId;
@@ -2763,6 +2784,8 @@ declare class InMemoryStore implements DurableStore {
2763
2784
  calls: readonly ToolCallReservation[];
2764
2785
  /** The model response's blocks, so the request message keeps what preceded the Tool Calls. */
2765
2786
  contentBlocks?: readonly ContentBlock[];
2787
+ model?: ModelRef;
2788
+ usage?: LlmTokenUsage;
2766
2789
  }): ToolBatchCommit;
2767
2790
  startToolCall(lease: OwnerLease, input: {
2768
2791
  runId: RunId;
@@ -3018,6 +3041,8 @@ declare class SqliteStore implements DurableStore {
3018
3041
  locations?: ToolCallLocation[];
3019
3042
  content?: ToolCallContent[];
3020
3043
  _meta?: JsonObject;
3044
+ model?: _rowan_agent_models.ModelRef;
3045
+ usage?: _rowan_agent_models.LlmTokenUsage;
3021
3046
  }): Promise<ToolCommit>;
3022
3047
  reserveToolCalls(lease: OwnerLease, input: {
3023
3048
  runId: RunId;
@@ -3026,6 +3051,8 @@ declare class SqliteStore implements DurableStore {
3026
3051
  requestMessageId: MessageId;
3027
3052
  calls: readonly ToolCallReservation[];
3028
3053
  contentBlocks?: readonly ContentBlock[];
3054
+ model?: _rowan_agent_models.ModelRef;
3055
+ usage?: _rowan_agent_models.LlmTokenUsage;
3029
3056
  }): Promise<ToolBatchCommit>;
3030
3057
  startToolCall(lease: OwnerLease, input: {
3031
3058
  runId: RunId;
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") {
@@ -2706,6 +2709,7 @@ function buildModelRequest(input, options) {
2706
2709
  model: options?.model ?? { provider: "", id: "" },
2707
2710
  ...options?.thinkingLevel === void 0 ? {} : { thinkingLevel: options.thinkingLevel },
2708
2711
  ...options?.providerOptions === void 0 ? {} : { providerOptions: options.providerOptions },
2712
+ ...options?.sessionId === void 0 ? {} : { sessionId: options.sessionId },
2709
2713
  system: systemText,
2710
2714
  messages,
2711
2715
  tools: modelTools.length > 0 ? modelTools : void 0
@@ -2971,9 +2975,15 @@ function createMessageManager(context, onMessage, onMessageDelta) {
2971
2975
  if (!active) return;
2972
2976
  active.message.content = content;
2973
2977
  },
2974
- async end(messageId) {
2978
+ async end(messageId, meta) {
2975
2979
  const active = activeMessages.get(messageId);
2976
2980
  if (!active) return;
2981
+ if (meta?.model) {
2982
+ active.message.model = meta.model;
2983
+ }
2984
+ if (meta?.usage) {
2985
+ active.message.usage = meta.usage;
2986
+ }
2977
2987
  if (!active.emitted && (active.message.content === "" || active.message.content.length === 0)) {
2978
2988
  activeMessages.delete(messageId);
2979
2989
  return;
@@ -3042,6 +3052,7 @@ async function executePhaseWithModel(ctx) {
3042
3052
  };
3043
3053
  let executableToolCalls;
3044
3054
  let roundContentBlocks;
3055
+ let collected;
3045
3056
  const checkpoint = ctx.execution.interaction.checkpoint();
3046
3057
  if (checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension") {
3047
3058
  const toolSuspension = checkpoint;
@@ -3054,7 +3065,7 @@ async function executePhaseWithModel(ctx) {
3054
3065
  toolCalls: [...toolSuspension.toolCalls]
3055
3066
  };
3056
3067
  } else {
3057
- const collected = await ctx.execution.invokeModel(roundContext);
3068
+ collected = await ctx.execution.invokeModel(roundContext);
3058
3069
  output = {
3059
3070
  message: collected.text,
3060
3071
  phase: ctx.phase.name,
@@ -3087,7 +3098,13 @@ async function executePhaseWithModel(ctx) {
3087
3098
  return output;
3088
3099
  }
3089
3100
  }
3090
- const results = await ctx.execution.executeTools(roundContext, executableToolCalls, roundContentBlocks);
3101
+ const currentModel = ctx.phase.model ?? ctx.config.model;
3102
+ const results = await ctx.execution.executeTools(
3103
+ roundContext,
3104
+ executableToolCalls,
3105
+ roundContentBlocks,
3106
+ { model: currentModel, usage: collected?.usage }
3107
+ );
3091
3108
  for (const result of results) {
3092
3109
  const messageId = ctx.messageManager.start("tool", createToolResultContent(result), {
3093
3110
  phase: ctx.phase.name
@@ -3641,7 +3658,9 @@ async function executeToolCalls(input) {
3641
3658
  config: input.config,
3642
3659
  toolCalls: input.toolCalls,
3643
3660
  contentBlocks: input.contentBlocks,
3644
- driver: input.driver
3661
+ driver: input.driver,
3662
+ model: input.model,
3663
+ usage: input.usage
3645
3664
  });
3646
3665
  if (results2.length !== input.toolCalls.length) throw new Error("Runtime returned an invalid Tool batch result");
3647
3666
  return results2.map((result, index) => ({
@@ -3713,6 +3732,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3713
3732
  appendSystemPrompt: phaseContext.appendSystemPrompt
3714
3733
  }, {
3715
3734
  model: phase.model ?? config.model,
3735
+ sessionId: config.execution.agentId,
3716
3736
  ...config.thinkingLevel === void 0 ? {} : { thinkingLevel: config.thinkingLevel },
3717
3737
  ...config.providerOptions === void 0 ? {} : { providerOptions: config.providerOptions }
3718
3738
  });
@@ -3798,7 +3818,7 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3798
3818
  await config.onPhaseStatus?.(phase.name, status);
3799
3819
  },
3800
3820
  executeTool: executePhaseTool,
3801
- async executeTools(phaseContext, toolCalls, contentBlocks) {
3821
+ async executeTools(phaseContext, toolCalls, contentBlocks, meta) {
3802
3822
  return runTurn(async () => {
3803
3823
  const tools = phaseContext.tools.filter((tool) => tool.name !== PhaseRouteTool);
3804
3824
  for (const toolCall of toolCalls) {
@@ -3816,7 +3836,9 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3816
3836
  tools,
3817
3837
  toolCalls,
3818
3838
  contentBlocks,
3819
- driver: interaction
3839
+ driver: interaction,
3840
+ model: meta?.model,
3841
+ usage: meta?.usage
3820
3842
  });
3821
3843
  for (const result of results) {
3822
3844
  await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
@@ -4283,7 +4305,7 @@ function normalizeUserInput(input) {
4283
4305
  return normalized;
4284
4306
  }
4285
4307
  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");
4308
+ 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
4309
  }
4288
4310
  function isToolResult(value) {
4289
4311
  if (!isRecord3(value)) return false;
@@ -4999,7 +5021,9 @@ function projectAssistantMessage(message, agentId, runId, sequenceWithinRun, opt
4999
5021
  sequenceWithinRun,
5000
5022
  createdAt: message.createdAt,
5001
5023
  ...message.metadata ? { metadata: message.metadata } : {},
5002
- ...options.interrupted ? { interrupted: true } : {}
5024
+ ...options.interrupted ? { interrupted: true } : {},
5025
+ ...message.model ? { model: message.model } : {},
5026
+ ...message.usage ? { usage: message.usage } : {}
5003
5027
  };
5004
5028
  }
5005
5029
  function durableInterruptedAssistantContent(content) {
@@ -5251,6 +5275,333 @@ function fromLoopResult(result) {
5251
5275
  };
5252
5276
  }
5253
5277
 
5278
+ // src/runtime/session-files.ts
5279
+ import { appendFileSync, chmodSync, existsSync as existsSync3, mkdirSync, readFileSync } from "fs";
5280
+ import { dirname as dirname5, join as join3 } from "path";
5281
+ function toEpochMs(isoString) {
5282
+ if (!isoString) return Date.now();
5283
+ const ms = new Date(isoString).getTime();
5284
+ return Number.isNaN(ms) ? Date.now() : ms;
5285
+ }
5286
+ function isContentBlock(value) {
5287
+ if (!value || typeof value !== "object") return false;
5288
+ const candidate = value;
5289
+ if (candidate.type === "text" && typeof candidate.text === "string") return true;
5290
+ if (candidate.type === "image" && typeof candidate.data === "string" && typeof candidate.mimeType === "string") return true;
5291
+ return false;
5292
+ }
5293
+ function convertToolResultContent(content) {
5294
+ if (content === void 0 || content === null) {
5295
+ return [{ type: "text", text: "" }];
5296
+ }
5297
+ if (typeof content === "string") {
5298
+ return [{ type: "text", text: content }];
5299
+ }
5300
+ if (isContentBlock(content)) {
5301
+ return content.type === "image" ? [{ type: "image", data: content.data, mimeType: content.mimeType }] : [{ type: "text", text: content.text }];
5302
+ }
5303
+ if (Array.isArray(content)) {
5304
+ if (content.length > 0 && content.every(isContentBlock)) {
5305
+ return content.map(
5306
+ (item) => item.type === "image" ? { type: "image", data: item.data, mimeType: item.mimeType } : { type: "text", text: item.text }
5307
+ );
5308
+ }
5309
+ return [{ type: "text", text: JSON.stringify(content) }];
5310
+ }
5311
+ return [{ type: "text", text: JSON.stringify(content) }];
5312
+ }
5313
+ function convertUserContent(content) {
5314
+ if (typeof content === "string") return content;
5315
+ return content.map(
5316
+ (part) => part.type === "image" ? { type: "image", data: part.data, mimeType: part.mimeType } : { type: "text", text: part.text }
5317
+ );
5318
+ }
5319
+ function convertAssistantContent(content, onToolCall) {
5320
+ if (typeof content === "string") {
5321
+ return {
5322
+ parts: [{ type: "text", text: content }],
5323
+ hasToolCall: false
5324
+ };
5325
+ }
5326
+ let hasToolCall = false;
5327
+ const parts = [];
5328
+ for (const part of content) {
5329
+ if (part.type === "text") {
5330
+ parts.push({ type: "text", text: part.text });
5331
+ } else if (part.type === "thinking") {
5332
+ parts.push({
5333
+ type: "thinking",
5334
+ thinking: part.thinking,
5335
+ ...part.signature ? { thinkingSignature: part.signature } : {}
5336
+ });
5337
+ } else if (part.type === "tool_use") {
5338
+ hasToolCall = true;
5339
+ const callId = part.providerToolCallId ?? part.toolCallId;
5340
+ onToolCall?.(callId, part.name, part.toolCallId);
5341
+ parts.push({
5342
+ type: "toolCall",
5343
+ id: callId,
5344
+ name: part.name,
5345
+ arguments: part.input
5346
+ });
5347
+ }
5348
+ }
5349
+ return { parts, hasToolCall };
5350
+ }
5351
+ async function getAgentCreatedAt(runtime, agentId) {
5352
+ let after;
5353
+ while (true) {
5354
+ const page = await runtime.listAgents(after ? { after, limit: 100 } : { limit: 100 });
5355
+ const agent = page.items.find((item) => item.id === agentId);
5356
+ if (agent) return agent.createdAt;
5357
+ if (!page.next) break;
5358
+ after = page.next;
5359
+ }
5360
+ return (/* @__PURE__ */ new Date()).toISOString();
5361
+ }
5362
+ async function getFirstRunCwd(runtime, agentId) {
5363
+ let after;
5364
+ while (true) {
5365
+ const page = await runtime.listRuns(after ? { agentId, after, limit: 100 } : { agentId, limit: 100 });
5366
+ for (const runSummary of page.items) {
5367
+ try {
5368
+ const snapshot = await runtime.run(runSummary.id).snapshot();
5369
+ if (snapshot.cwd && snapshot.cwd.trim().length > 0) {
5370
+ return snapshot.cwd;
5371
+ }
5372
+ } catch {
5373
+ }
5374
+ }
5375
+ if (!page.next) break;
5376
+ after = page.next;
5377
+ }
5378
+ return "";
5379
+ }
5380
+ function writeSessionEntry(state, entry) {
5381
+ appendFileSync(state.filePath, `${JSON.stringify(entry)}
5382
+ `, { encoding: "utf8", mode: 384 });
5383
+ try {
5384
+ chmodSync(state.filePath, 384);
5385
+ } catch {
5386
+ }
5387
+ state.knownEntryIds.add(entry.id);
5388
+ state.entryIdToParentId.set(entry.id, entry.parentId);
5389
+ state.lastEntryId = entry.id;
5390
+ }
5391
+ async function startSessionFilesConsumer(runtime, options) {
5392
+ const agentStates = /* @__PURE__ */ new Map();
5393
+ async function getOrInitState(agentId) {
5394
+ let state = agentStates.get(agentId);
5395
+ if (state) return state;
5396
+ const createdAt = await getAgentCreatedAt(runtime, agentId);
5397
+ const cwd = await getFirstRunCwd(runtime, agentId);
5398
+ const folderName = cwd.length > 0 ? `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--` : "--unknown--";
5399
+ const fileTimestamp = createdAt.replace(/[:.]/g, "-");
5400
+ const fileName = `${fileTimestamp}_${agentId}.jsonl`;
5401
+ const filePath = join3(options.dir, folderName, fileName);
5402
+ const knownEntryIds = /* @__PURE__ */ new Set();
5403
+ let lastEntryId = null;
5404
+ let lastModel;
5405
+ const entryIdToParentId = /* @__PURE__ */ new Map();
5406
+ const toolCallIdToName = /* @__PURE__ */ new Map();
5407
+ const writeHeader = () => {
5408
+ const header = { type: "session", version: 3, id: agentId, timestamp: createdAt, cwd };
5409
+ appendFileSync(filePath, `${JSON.stringify(header)}
5410
+ `, { encoding: "utf8", mode: 384 });
5411
+ };
5412
+ mkdirSync(dirname5(filePath), { recursive: true, mode: 448 });
5413
+ if (existsSync3(filePath)) {
5414
+ const content = readFileSync(filePath, "utf8");
5415
+ if (content.trim().length === 0) {
5416
+ writeHeader();
5417
+ } else {
5418
+ for (const line of content.split("\n")) {
5419
+ const trimmed = line.trim();
5420
+ if (!trimmed) continue;
5421
+ let parsed;
5422
+ try {
5423
+ parsed = JSON.parse(trimmed);
5424
+ } catch {
5425
+ continue;
5426
+ }
5427
+ if (parsed.type === "session") continue;
5428
+ if (typeof parsed.id === "string") {
5429
+ knownEntryIds.add(parsed.id);
5430
+ entryIdToParentId.set(parsed.id, parsed.parentId ?? null);
5431
+ lastEntryId = parsed.id;
5432
+ }
5433
+ if (parsed.type === "model_change" && typeof parsed.provider === "string" && typeof parsed.modelId === "string") {
5434
+ lastModel = { provider: parsed.provider, modelId: parsed.modelId };
5435
+ }
5436
+ if (parsed.type === "message" && parsed.message?.role === "assistant" && Array.isArray(parsed.message.content)) {
5437
+ for (const part of parsed.message.content) {
5438
+ if (part && part.type === "toolCall" && typeof part.id === "string" && typeof part.name === "string") {
5439
+ toolCallIdToName.set(part.id, part.name);
5440
+ }
5441
+ }
5442
+ }
5443
+ }
5444
+ }
5445
+ } else {
5446
+ writeHeader();
5447
+ }
5448
+ state = {
5449
+ filePath,
5450
+ knownEntryIds,
5451
+ lastEntryId,
5452
+ entryIdToParentId,
5453
+ toolCallIdToName,
5454
+ ...lastModel ? { lastModel } : {}
5455
+ };
5456
+ agentStates.set(agentId, state);
5457
+ return state;
5458
+ }
5459
+ async function onEvent(event) {
5460
+ try {
5461
+ if (event.kind !== "message_committed" && event.kind !== "message_revised") {
5462
+ return;
5463
+ }
5464
+ if (event.kind === "message_committed" && event.message.role === "interaction") {
5465
+ return;
5466
+ }
5467
+ const state = await getOrInitState(event.agentId);
5468
+ if (event.kind === "message_committed") {
5469
+ if (event.message.role === "user") {
5470
+ if (state.knownEntryIds.has(event.message.id)) return;
5471
+ const content = convertUserContent(event.message.content);
5472
+ const entry = {
5473
+ type: "message",
5474
+ id: event.message.id,
5475
+ parentId: state.lastEntryId,
5476
+ timestamp: event.message.createdAt,
5477
+ message: {
5478
+ role: "user",
5479
+ content,
5480
+ timestamp: toEpochMs(event.message.createdAt)
5481
+ }
5482
+ };
5483
+ writeSessionEntry(state, entry);
5484
+ } else if (event.message.role === "assistant") {
5485
+ if (state.knownEntryIds.has(event.message.id)) return;
5486
+ const model = event.message.model;
5487
+ const rawUsage = event.message.usage;
5488
+ if (model) {
5489
+ const isDifferentModel = !state.lastModel || state.lastModel.provider !== model.provider || state.lastModel.modelId !== model.id;
5490
+ if (isDifferentModel) {
5491
+ const modelChangeId = `${event.message.id}:model`;
5492
+ if (!state.knownEntryIds.has(modelChangeId)) {
5493
+ const modelChangeEntry = {
5494
+ type: "model_change",
5495
+ id: modelChangeId,
5496
+ parentId: state.lastEntryId,
5497
+ timestamp: event.message.createdAt,
5498
+ provider: model.provider,
5499
+ modelId: model.id
5500
+ };
5501
+ writeSessionEntry(state, modelChangeEntry);
5502
+ }
5503
+ state.lastModel = { provider: model.provider, modelId: model.id };
5504
+ }
5505
+ }
5506
+ const { parts, hasToolCall } = convertAssistantContent(
5507
+ event.message.content,
5508
+ (id, name, internalId) => {
5509
+ state.toolCallIdToName.set(id, name);
5510
+ if (internalId) state.toolCallIdToName.set(internalId, name);
5511
+ }
5512
+ );
5513
+ const assistantMessagePayload = {
5514
+ role: "assistant",
5515
+ content: parts,
5516
+ stopReason: hasToolCall ? "toolUse" : "stop",
5517
+ timestamp: toEpochMs(event.message.createdAt)
5518
+ };
5519
+ if (model || rawUsage) {
5520
+ if (model) {
5521
+ assistantMessagePayload.provider = model.provider;
5522
+ assistantMessagePayload.model = model.id;
5523
+ }
5524
+ const input = rawUsage?.inputTokens ?? 0;
5525
+ const output = rawUsage?.outputTokens ?? 0;
5526
+ const cacheRead = rawUsage?.cacheReadTokens ?? 0;
5527
+ const cacheWrite = rawUsage?.cacheWriteTokens ?? 0;
5528
+ const totalTokens = rawUsage?.totalTokens ?? input + output + cacheRead + cacheWrite;
5529
+ const cost = rawUsage?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
5530
+ assistantMessagePayload.usage = {
5531
+ input,
5532
+ output,
5533
+ cacheRead,
5534
+ cacheWrite,
5535
+ ...rawUsage?.cacheWrite1hTokens !== void 0 ? { cacheWrite1h: rawUsage.cacheWrite1hTokens } : {},
5536
+ ...rawUsage?.reasoningTokens !== void 0 ? { reasoning: rawUsage.reasoningTokens } : {},
5537
+ totalTokens,
5538
+ cost
5539
+ };
5540
+ }
5541
+ const entry = {
5542
+ type: "message",
5543
+ id: event.message.id,
5544
+ parentId: state.lastEntryId,
5545
+ timestamp: event.message.createdAt,
5546
+ message: assistantMessagePayload
5547
+ };
5548
+ writeSessionEntry(state, entry);
5549
+ } else if (event.message.role === "tool") {
5550
+ for (let index = 0; index < event.message.content.length; index++) {
5551
+ const part = event.message.content[index];
5552
+ const entryId = `${event.message.id}:${index}`;
5553
+ if (state.knownEntryIds.has(entryId)) continue;
5554
+ const toolCallId = part.providerToolCallId ?? part.toolCallId;
5555
+ const toolName = state.toolCallIdToName.get(toolCallId) ?? state.toolCallIdToName.get(part.toolCallId) ?? "unknown";
5556
+ const isError = Boolean(
5557
+ part.result.isError ?? (part.result.ok !== void 0 ? !part.result.ok : Boolean(part.result.error))
5558
+ );
5559
+ const content = convertToolResultContent(part.result.content);
5560
+ const entry = {
5561
+ type: "message",
5562
+ id: entryId,
5563
+ parentId: state.lastEntryId,
5564
+ timestamp: event.message.createdAt,
5565
+ message: {
5566
+ role: "toolResult",
5567
+ toolCallId,
5568
+ toolName,
5569
+ content,
5570
+ isError,
5571
+ timestamp: toEpochMs(event.message.createdAt)
5572
+ }
5573
+ };
5574
+ writeSessionEntry(state, entry);
5575
+ }
5576
+ }
5577
+ } else if (event.kind === "message_revised") {
5578
+ const message = event.message;
5579
+ const revision = message.messageRevision ?? 1;
5580
+ const entryId = `${message.id}@${revision}`;
5581
+ if (state.knownEntryIds.has(entryId)) return;
5582
+ const parentId = state.entryIdToParentId.get(message.id) ?? null;
5583
+ const timestampIso = message.createdAt ?? event.createdAt;
5584
+ const content = convertUserContent(message.content);
5585
+ const entry = {
5586
+ type: "message",
5587
+ id: entryId,
5588
+ parentId,
5589
+ timestamp: timestampIso,
5590
+ message: {
5591
+ role: "user",
5592
+ content,
5593
+ timestamp: toEpochMs(timestampIso)
5594
+ }
5595
+ };
5596
+ writeSessionEntry(state, entry);
5597
+ }
5598
+ } catch (error) {
5599
+ console.error("Failed to write session file entry:", error);
5600
+ }
5601
+ }
5602
+ return runtime.consume({ consumerId: "rowan:session-files", signal: new AbortController().signal, onEvent });
5603
+ }
5604
+
5254
5605
  // src/runtime/transient-run-events.ts
5255
5606
  var MAX_BUFFERED_EVENTS = 128;
5256
5607
  var MAX_BUFFERED_TEXT = 64 * 1024;
@@ -5607,7 +5958,7 @@ function scopeToKey(scope) {
5607
5958
 
5608
5959
  // src/extensions/runner.ts
5609
5960
  import { execFile } from "child_process";
5610
- import { dirname as dirname5, resolve as resolve5 } from "path";
5961
+ import { dirname as dirname6, resolve as resolve5 } from "path";
5611
5962
  import {
5612
5963
  registerModel,
5613
5964
  unregisterProviderModels,
@@ -6436,7 +6787,7 @@ var ExtensionRunner = class {
6436
6787
  }
6437
6788
  }
6438
6789
  async loadRegisteredPhase(extension, registration) {
6439
- const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname5(extension.path);
6790
+ const extensionBase = extension.path.startsWith("<") ? this.cwd : dirname6(extension.path);
6440
6791
  const phase = await loadPhase(resolve5(extensionBase, registration));
6441
6792
  const name = phase.name;
6442
6793
  if (this.phases.has(name)) {
@@ -6512,10 +6863,10 @@ function createExtensionRunner(options) {
6512
6863
  }
6513
6864
 
6514
6865
  // src/extensions/loader.ts
6515
- import { existsSync as existsSync3, readFileSync } from "fs";
6866
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
6516
6867
  import { readdir as readdir3, readFile as readFile3, stat as stat2 } from "fs/promises";
6517
6868
  import { fileURLToPath } from "url";
6518
- import { basename as basename3, dirname as dirname6, extname as extname2, join as join3, resolve as resolve6 } from "path";
6869
+ import { basename as basename3, dirname as dirname7, extname as extname2, join as join4, resolve as resolve6 } from "path";
6519
6870
  import { createJiti } from "jiti";
6520
6871
  function isExtensionFile(path) {
6521
6872
  const extension = extname2(path).toLowerCase();
@@ -6525,12 +6876,12 @@ function isSyntheticPath(path) {
6525
6876
  return path.startsWith("<") && path.endsWith(">");
6526
6877
  }
6527
6878
  function readManifestSync(dir) {
6528
- const manifestPath = join3(dir, "package.json");
6529
- if (!existsSync3(manifestPath)) {
6879
+ const manifestPath = join4(dir, "package.json");
6880
+ if (!existsSync4(manifestPath)) {
6530
6881
  return void 0;
6531
6882
  }
6532
6883
  try {
6533
- const content = readFileSync(manifestPath, "utf8");
6884
+ const content = readFileSync2(manifestPath, "utf8");
6534
6885
  const pkg = JSON.parse(content);
6535
6886
  const rowan = pkg.rowan;
6536
6887
  if (!rowan) return void 0;
@@ -6549,7 +6900,7 @@ function jitiAliases() {
6549
6900
  return {};
6550
6901
  }
6551
6902
  const agentSourcePath = fileURLToPath(new URL("../index.ts", moduleUrl));
6552
- if (!existsSync3(agentSourcePath)) {
6903
+ if (!existsSync4(agentSourcePath)) {
6553
6904
  return {};
6554
6905
  }
6555
6906
  return { "@rowan-agent/agent": agentSourcePath };
@@ -6573,21 +6924,21 @@ async function loadExtensionModule(extensionPath) {
6573
6924
  return typeof module === "function" ? module : void 0;
6574
6925
  }
6575
6926
  async function readPackageManifest(path) {
6576
- if (!existsSync3(path)) {
6927
+ if (!existsSync4(path)) {
6577
6928
  return void 0;
6578
6929
  }
6579
6930
  const content = await readFile3(path, "utf8");
6580
6931
  return JSON.parse(content);
6581
6932
  }
6582
6933
  async function resolveExtensionEntries(dir) {
6583
- const manifest = await readPackageManifest(join3(dir, "package.json"));
6934
+ const manifest = await readPackageManifest(join4(dir, "package.json"));
6584
6935
  const declared = manifest?.rowan?.extensions;
6585
6936
  if (declared && declared.length > 0) {
6586
6937
  return declared.map((entry) => resolve6(dir, entry));
6587
6938
  }
6588
6939
  for (const name of ["index.ts", "index.js"]) {
6589
- const entry = join3(dir, name);
6590
- if (existsSync3(entry)) {
6940
+ const entry = join4(dir, name);
6941
+ if (existsSync4(entry)) {
6591
6942
  return [entry];
6592
6943
  }
6593
6944
  }
@@ -6602,7 +6953,7 @@ async function discoverExtensionsInDir(dir) {
6602
6953
  });
6603
6954
  const paths = [];
6604
6955
  for (const entry of [...entries].sort((a, b) => a.name.localeCompare(b.name))) {
6605
- const entryPath = join3(dir, entry.name);
6956
+ const entryPath = join4(dir, entry.name);
6606
6957
  if ((entry.isFile() || entry.isSymbolicLink()) && isExtensionFile(entry.name)) {
6607
6958
  paths.push(entryPath);
6608
6959
  continue;
@@ -6622,8 +6973,8 @@ async function discoverExtensionsInDir(dir) {
6622
6973
  function loadExtensionFromFactory(factory, cwd, extensionPath = "<inline>", options = {}) {
6623
6974
  const resolvedCwd = resolve6(cwd);
6624
6975
  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);
6976
+ const name = options.name ?? (isSyntheticPath(extensionPath) ? extensionPath : basename3(dirname7(resolvedPath)) || "unknown");
6977
+ const manifestDir = isSyntheticPath(extensionPath) ? resolvedCwd : dirname7(resolvedPath);
6627
6978
  const manifest = readManifestSync(manifestDir);
6628
6979
  const id = options.id ?? manifest?.id ?? name;
6629
6980
  return {
@@ -6663,7 +7014,7 @@ async function loadExtensionsFromPath(path) {
6663
7014
  const resolvedPath = resolve6(path);
6664
7015
  const info = await stat2(resolvedPath);
6665
7016
  if (!info.isDirectory()) {
6666
- return loadExtensions([resolvedPath], dirname6(resolvedPath));
7017
+ return loadExtensions([resolvedPath], dirname7(resolvedPath));
6667
7018
  }
6668
7019
  const entries = await resolveExtensionEntries(resolvedPath) ?? await discoverExtensionsInDir(resolvedPath);
6669
7020
  return loadExtensions(entries, resolvedPath);
@@ -6671,7 +7022,7 @@ async function loadExtensionsFromPath(path) {
6671
7022
 
6672
7023
  // src/runtime/resource-registry.ts
6673
7024
  import { stat as stat3, readdir as readdir4 } from "fs/promises";
6674
- import { join as join4, resolve as resolve7 } from "path";
7025
+ import { join as join5, resolve as resolve7 } from "path";
6675
7026
  var ResourceRegistryError = class extends Error {
6676
7027
  code;
6677
7028
  constructor(code, message) {
@@ -6989,7 +7340,7 @@ async function loadDirectoryValues(kind, directory, sourceId) {
6989
7340
  if (rootInfo.isFile()) {
6990
7341
  targets.push(root);
6991
7342
  } else if (rootInfo.isDirectory()) {
6992
- const markerAtRoot = join4(root, marker);
7343
+ const markerAtRoot = join5(root, marker);
6993
7344
  try {
6994
7345
  const markerInfo = await stat3(markerAtRoot);
6995
7346
  if (markerInfo.isFile()) targets.push(markerAtRoot);
@@ -7011,7 +7362,7 @@ async function loadDirectoryValues(kind, directory, sourceId) {
7011
7362
  }
7012
7363
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
7013
7364
  if (!entry.isDirectory()) continue;
7014
- const target = join4(root, entry.name, marker);
7365
+ const target = join5(root, entry.name, marker);
7015
7366
  try {
7016
7367
  const targetInfo = await stat3(target);
7017
7368
  if (targetInfo.isFile()) targets.push(target);
@@ -7267,7 +7618,7 @@ async function spillLargeToolResult(result, archiveDir, toolName) {
7267
7618
  await mkdir2(toolDir, { recursive: true, mode: 448 });
7268
7619
  await chmod2(toolDir, 448).catch(() => void 0);
7269
7620
  const safeToolName = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_");
7270
- const path = join5(toolDir, `rowan-${safeToolName}-${Date.now()}-${randomUUID()}.log`);
7621
+ const path = join6(toolDir, `rowan-${safeToolName}-${Date.now()}-${randomUUID()}.log`);
7271
7622
  await writeFile2(path, serialized, { encoding: "utf8", mode: 384 });
7272
7623
  await chmod2(path, 384).catch(() => void 0);
7273
7624
  const preview = new TextDecoder().decode(bytes.subarray(0, MAX_INLINE_TOOL_RESULT_BYTES));
@@ -7364,6 +7715,9 @@ var AgentRuntime = class _AgentRuntime {
7364
7715
  await options.bootstrap?.(runtime.resources);
7365
7716
  runtime.startHeartbeat();
7366
7717
  void runtime.pump();
7718
+ if (options.sessionFiles) {
7719
+ await startSessionFilesConsumer(runtime, options.sessionFiles);
7720
+ }
7367
7721
  return runtime;
7368
7722
  } catch (error) {
7369
7723
  await runtime.resources.closeExtensions().catch(() => void 0);
@@ -8108,7 +8462,7 @@ var AgentRuntime = class _AgentRuntime {
8108
8462
  onContext: assembly.setContext,
8109
8463
  runtime: {
8110
8464
  interact: (request) => this.liveInteract(run.id, request, controller.signal),
8111
- tools: ({ toolCall, contentBlocks, driver, options, providerInitiated }) => {
8465
+ tools: ({ toolCall, contentBlocks, driver, options, providerInitiated, model: callModel, usage: callUsage }) => {
8112
8466
  const task = toolQueue.then(async () => {
8113
8467
  if (!providerInitiated) {
8114
8468
  const execution = await this.executeToolBatch({
@@ -8122,6 +8476,8 @@ var AgentRuntime = class _AgentRuntime {
8122
8476
  signal: controller.signal,
8123
8477
  driver,
8124
8478
  options,
8479
+ model: callModel ?? model,
8480
+ usage: callUsage,
8125
8481
  onRevision: (rev) => {
8126
8482
  executionRevision = rev;
8127
8483
  }
@@ -8146,6 +8502,8 @@ var AgentRuntime = class _AgentRuntime {
8146
8502
  options,
8147
8503
  providerInitiated: true,
8148
8504
  interactionIds,
8505
+ model: callModel ?? model,
8506
+ usage: callUsage,
8149
8507
  onRevision: (rev) => {
8150
8508
  executionRevision = rev;
8151
8509
  }
@@ -8236,7 +8594,7 @@ var AgentRuntime = class _AgentRuntime {
8236
8594
  toolQueue = task.then(() => void 0, () => void 0);
8237
8595
  return task;
8238
8596
  },
8239
- toolsBatch: ({ toolCalls, contentBlocks, driver, options }) => {
8597
+ toolsBatch: ({ toolCalls, contentBlocks, driver, options, model: batchModel, usage: batchUsage }) => {
8240
8598
  const task = toolQueue.then(async () => {
8241
8599
  const execution = await this.executeToolBatch({
8242
8600
  run,
@@ -8249,6 +8607,8 @@ var AgentRuntime = class _AgentRuntime {
8249
8607
  signal: controller.signal,
8250
8608
  driver,
8251
8609
  options,
8610
+ model: batchModel ?? model,
8611
+ usage: batchUsage,
8252
8612
  onRevision: (rev) => {
8253
8613
  executionRevision = rev;
8254
8614
  }
@@ -8476,6 +8836,8 @@ var AgentRuntime = class _AgentRuntime {
8476
8836
  expectedRevision: revision,
8477
8837
  requestMessageId: createId("msg"),
8478
8838
  contentBlocks: input.contentBlocks,
8839
+ ...input.model ? { model: input.model } : {},
8840
+ ...input.usage ? { usage: input.usage } : {},
8479
8841
  calls: input.toolCalls.map((tc, i) => ({
8480
8842
  toolCallId: toolCallIds[i],
8481
8843
  providerToolCallId: tc.id,
@@ -8611,6 +8973,8 @@ var AgentRuntime = class _AgentRuntime {
8611
8973
  expectedRevision: revision,
8612
8974
  requestMessageId: createId("msg"),
8613
8975
  contentBlocks: input.contentBlocks,
8976
+ ...input.model ? { model: input.model } : {},
8977
+ ...input.usage ? { usage: input.usage } : {},
8614
8978
  calls: [{
8615
8979
  toolCallId,
8616
8980
  providerToolCallId,
@@ -8708,6 +9072,8 @@ var AgentRuntime = class _AgentRuntime {
8708
9072
  expectedRevision: revision,
8709
9073
  requestMessageId: createId("msg"),
8710
9074
  contentBlocks: input.contentBlocks,
9075
+ ...input.model ? { model: input.model } : {},
9076
+ ...input.usage ? { usage: input.usage } : {},
8711
9077
  calls: [{
8712
9078
  toolCallId,
8713
9079
  providerToolCallId,
@@ -8729,6 +9095,8 @@ var AgentRuntime = class _AgentRuntime {
8729
9095
  expectedRevision: revision,
8730
9096
  requestMessageId: createId("msg"),
8731
9097
  contentBlocks: input.contentBlocks,
9098
+ ...input.model ? { model: input.model } : {},
9099
+ ...input.usage ? { usage: input.usage } : {},
8732
9100
  calls: [{
8733
9101
  toolCallId,
8734
9102
  providerToolCallId,
@@ -8783,6 +9151,8 @@ var AgentRuntime = class _AgentRuntime {
8783
9151
  expectedRevision: revision,
8784
9152
  requestMessageId: createId("msg"),
8785
9153
  contentBlocks: input.contentBlocks,
9154
+ ...input.model ? { model: input.model } : {},
9155
+ ...input.usage ? { usage: input.usage } : {},
8786
9156
  calls: [{
8787
9157
  toolCallId,
8788
9158
  providerToolCallId,
@@ -8832,6 +9202,8 @@ var AgentRuntime = class _AgentRuntime {
8832
9202
  expectedRevision: revision,
8833
9203
  requestMessageId: createId("msg"),
8834
9204
  contentBlocks: input.contentBlocks,
9205
+ ...input.model ? { model: input.model } : {},
9206
+ ...input.usage ? { usage: input.usage } : {},
8835
9207
  calls: [{
8836
9208
  toolCallId,
8837
9209
  providerToolCallId,
@@ -9156,7 +9528,7 @@ ${content}`;
9156
9528
  archiveDirFor(agentId) {
9157
9529
  const store = this.owned;
9158
9530
  const archiveDir = store.contextArchiveDir?.(agentId);
9159
- return archiveDir ? join5(archiveDir, "tool-results") : void 0;
9531
+ return archiveDir ? join6(archiveDir, "tool-results") : void 0;
9160
9532
  }
9161
9533
  async runConsumer(subscription) {
9162
9534
  const registration = await this.owned.openConsumer(subscription.consumerId);
@@ -10314,6 +10686,8 @@ var InMemoryStore = class _InMemoryStore {
10314
10686
  execution: input.execution,
10315
10687
  expectedRevision: input.expectedRevision,
10316
10688
  requestMessageId: input.requestMessageId,
10689
+ ...input.model ? { model: input.model } : {},
10690
+ ...input.usage ? { usage: input.usage } : {},
10317
10691
  calls: [{
10318
10692
  providerToolCallId,
10319
10693
  name: input.name,
@@ -10345,7 +10719,9 @@ var InMemoryStore = class _InMemoryStore {
10345
10719
  input.execution.executionId,
10346
10720
  input.requestMessageId,
10347
10721
  input.calls,
10348
- input.contentBlocks ?? null
10722
+ input.contentBlocks ?? null,
10723
+ input.model ?? null,
10724
+ input.usage ?? null
10349
10725
  ]);
10350
10726
  const replay = this.replayOperation(operationKey, operationPayload);
10351
10727
  if (replay) return clone(replay);
@@ -10387,7 +10763,9 @@ var InMemoryStore = class _InMemoryStore {
10387
10763
  role: "assistant",
10388
10764
  content: requestMessageContent(toolCalls, input.contentBlocks),
10389
10765
  sequenceWithinRun: this.nextMessageSequence(run.id),
10390
- createdAt: timestamp
10766
+ createdAt: timestamp,
10767
+ ...input.model ? { model: input.model } : {},
10768
+ ...input.usage ? { usage: input.usage } : {}
10391
10769
  };
10392
10770
  for (const toolCall of toolCalls) this.storeToolCall(toolCall);
10393
10771
  this.storeMessage(requestMessage);
@@ -11439,8 +11817,8 @@ function requestMessageContent(toolCalls, contentBlocks) {
11439
11817
 
11440
11818
  // src/runtime/sqlite-durable-store.ts
11441
11819
  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";
11820
+ import { appendFileSync as appendFileSync2, chmodSync as chmodSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync } from "fs";
11821
+ import { dirname as dirname8, join as join7, resolve as resolve8 } from "path";
11444
11822
  import { tmpdir as tmpdir2 } from "os";
11445
11823
  var SCHEMA_ID = "rowan.agent.runtime";
11446
11824
  var SCHEMA_VERSION = "2";
@@ -11661,7 +12039,7 @@ var SqliteStore = class {
11661
12039
  stmtUpdateState;
11662
12040
  constructor(filename = ":memory:") {
11663
12041
  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");
12042
+ this.archiveRoot = filename === ":memory:" ? join7(tmpdir2(), `rowan-context-archives-${crypto.randomUUID()}`) : join7(dirname8(resolve8(filename)), "context-archives");
11665
12043
  }
11666
12044
  async openOwner(input) {
11667
12045
  this.assertOpen();
@@ -11755,7 +12133,7 @@ var SqliteStore = class {
11755
12133
  }
11756
12134
  /** Filesystem location owned by this Store for one Agent's readable archive. */
11757
12135
  contextArchiveDir(agentId) {
11758
- return join6(this.archiveRoot, String(agentId));
12136
+ return join7(this.archiveRoot, String(agentId));
11759
12137
  }
11760
12138
  async reserveAgent(lease, input) {
11761
12139
  return this.invoke(lease, (store, current) => store.reserveAgent(current, input));
@@ -12351,7 +12729,7 @@ var SqliteStore = class {
12351
12729
  mirrorChanges(tracker) {
12352
12730
  for (const agentId of tracker.deletedAgentIds) {
12353
12731
  const idStr = String(agentId);
12354
- rmSync(join6(this.archiveRoot, idStr), { recursive: true, force: true });
12732
+ rmSync(join7(this.archiveRoot, idStr), { recursive: true, force: true });
12355
12733
  this.mirroredEventIds.delete(idStr);
12356
12734
  this.mirroredCompactionIds.delete(idStr);
12357
12735
  this.mirroredAgents.delete(idStr);
@@ -12359,15 +12737,15 @@ var SqliteStore = class {
12359
12737
  for (const agent of tracker.upsertedAgents.values()) {
12360
12738
  const idStr = String(agent.id);
12361
12739
  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);
12740
+ const archiveDir = join7(this.archiveRoot, idStr);
12741
+ const toolDir = join7(archiveDir, "tool-results");
12742
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12743
+ chmodSync2(archiveDir, 448);
12744
+ chmodSync2(toolDir, 448);
12367
12745
  }
12368
12746
  if (tracker.appendedEvents.length > 0) {
12369
- mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
12370
- chmodSync(this.archiveRoot, 448);
12747
+ mkdirSync2(this.archiveRoot, { recursive: true, mode: 448 });
12748
+ chmodSync2(this.archiveRoot, 448);
12371
12749
  const eventsByAgent = /* @__PURE__ */ new Map();
12372
12750
  for (const event of tracker.appendedEvents) {
12373
12751
  const agentId = String(event.agentId);
@@ -12379,22 +12757,22 @@ var SqliteStore = class {
12379
12757
  list.push(event);
12380
12758
  }
12381
12759
  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");
12760
+ const archiveDir = join7(this.archiveRoot, agentId);
12761
+ const toolDir = join7(archiveDir, "tool-results");
12762
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12763
+ chmodSync2(archiveDir, 448);
12764
+ chmodSync2(toolDir, 448);
12765
+ const sessionPath = join7(archiveDir, "session.jsonl");
12388
12766
  try {
12389
- chmodSync(sessionPath, 384);
12767
+ chmodSync2(sessionPath, 384);
12390
12768
  } catch {
12391
12769
  }
12392
12770
  const known = this.mirroredEventIds.get(agentId) ?? /* @__PURE__ */ new Set();
12393
12771
  const newEvents = events.filter((e) => !known.has(String(e.id)));
12394
12772
  if (newEvents.length > 0) {
12395
12773
  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);
12774
+ appendFileSync2(sessionPath, lines, { encoding: "utf8", mode: 384 });
12775
+ chmodSync2(sessionPath, 384);
12398
12776
  for (const e of newEvents) known.add(String(e.id));
12399
12777
  this.mirroredEventIds.set(agentId, known);
12400
12778
  }
@@ -12403,13 +12781,13 @@ var SqliteStore = class {
12403
12781
  if (tracker.upsertedContextCompactions.size > 0) {
12404
12782
  for (const [agentId, record] of tracker.upsertedContextCompactions) {
12405
12783
  const idStr = String(agentId);
12406
- const archiveDir = join6(this.archiveRoot, idStr);
12407
- const sessionPath = join6(archiveDir, "session.jsonl");
12784
+ const archiveDir = join7(this.archiveRoot, idStr);
12785
+ const sessionPath = join7(archiveDir, "session.jsonl");
12408
12786
  const knownCompactions = this.mirroredCompactionIds.get(idStr) ?? /* @__PURE__ */ new Set();
12409
12787
  if (!knownCompactions.has(record.id)) {
12410
12788
  const line = JSON.stringify({ schemaVersion: 1, kind: "context_compacted", record }) + "\n";
12411
- appendFileSync(sessionPath, line, { encoding: "utf8", mode: 384 });
12412
- chmodSync(sessionPath, 384);
12789
+ appendFileSync2(sessionPath, line, { encoding: "utf8", mode: 384 });
12790
+ chmodSync2(sessionPath, 384);
12413
12791
  knownCompactions.add(record.id);
12414
12792
  this.mirroredCompactionIds.set(idStr, knownCompactions);
12415
12793
  }
@@ -12418,32 +12796,32 @@ var SqliteStore = class {
12418
12796
  }
12419
12797
  /** Append the semantic durable event mirror and remove deleted Agent sidecars. */
12420
12798
  mirrorInitialState(state) {
12421
- mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
12422
- chmodSync(this.archiveRoot, 448);
12799
+ mkdirSync2(this.archiveRoot, { recursive: true, mode: 448 });
12800
+ chmodSync2(this.archiveRoot, 448);
12423
12801
  const currentAgents = new Set(state.agents.map((agent) => String(agent.id)));
12424
12802
  for (const agentId of this.mirroredAgents) {
12425
12803
  if (currentAgents.has(agentId)) continue;
12426
- rmSync(join6(this.archiveRoot, agentId), { recursive: true, force: true });
12804
+ rmSync(join7(this.archiveRoot, agentId), { recursive: true, force: true });
12427
12805
  this.mirroredEventIds.delete(agentId);
12428
12806
  this.mirroredCompactionIds.delete(agentId);
12429
12807
  }
12430
12808
  for (const agent of state.agents) {
12431
12809
  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");
12810
+ const archiveDir = join7(this.archiveRoot, agentId);
12811
+ const toolDir = join7(archiveDir, "tool-results");
12812
+ mkdirSync2(toolDir, { recursive: true, mode: 448 });
12813
+ chmodSync2(archiveDir, 448);
12814
+ chmodSync2(toolDir, 448);
12815
+ const sessionPath = join7(archiveDir, "session.jsonl");
12438
12816
  try {
12439
- chmodSync(sessionPath, 384);
12817
+ chmodSync2(sessionPath, 384);
12440
12818
  } catch {
12441
12819
  }
12442
12820
  const known = this.mirroredEventIds.get(agentId) ?? /* @__PURE__ */ new Set();
12443
12821
  const knownCompactions = this.mirroredCompactionIds.get(agentId) ?? /* @__PURE__ */ new Set();
12444
12822
  if (known.size === 0 || knownCompactions.size === 0) {
12445
12823
  try {
12446
- for (const line of readFileSync2(sessionPath, "utf8").split("\n")) {
12824
+ for (const line of readFileSync3(sessionPath, "utf8").split("\n")) {
12447
12825
  if (!line.trim()) continue;
12448
12826
  const parsed = JSON.parse(line);
12449
12827
  if (parsed.event?.id) known.add(parsed.event.id);
@@ -12454,16 +12832,16 @@ var SqliteStore = class {
12454
12832
  }
12455
12833
  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
12834
  if (lines.length > 0) {
12457
- appendFileSync(sessionPath, lines.join(""), { encoding: "utf8", mode: 384 });
12458
- chmodSync(sessionPath, 384);
12835
+ appendFileSync2(sessionPath, lines.join(""), { encoding: "utf8", mode: 384 });
12836
+ chmodSync2(sessionPath, 384);
12459
12837
  for (const event of state.events) {
12460
12838
  if (String(event.agentId) === agentId) known.add(String(event.id));
12461
12839
  }
12462
12840
  }
12463
12841
  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
12842
  if (compactionLines.length > 0) {
12465
- appendFileSync(sessionPath, compactionLines.join(""), { encoding: "utf8", mode: 384 });
12466
- chmodSync(sessionPath, 384);
12843
+ appendFileSync2(sessionPath, compactionLines.join(""), { encoding: "utf8", mode: 384 });
12844
+ chmodSync2(sessionPath, 384);
12467
12845
  for (const [, record] of state.contextCompactions ?? []) {
12468
12846
  if (String(record.agentId) === agentId) knownCompactions.add(record.id);
12469
12847
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,7 @@
23
23
  "build": "tsup && bun scripts/check-public-interface.ts"
24
24
  },
25
25
  "dependencies": {
26
- "@rowan-agent/models": "0.9.0",
26
+ "@rowan-agent/models": "0.10.0",
27
27
  "jiti": "2.7.0",
28
28
  "typebox": "^1.0.0",
29
29
  "yaml": "^2.7.0"