@rowan-agent/agent 0.35.0 → 0.35.2

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 (2) hide show
  1. package/dist/runtime.js +47 -20
  2. package/package.json +1 -1
package/dist/runtime.js CHANGED
@@ -1767,6 +1767,17 @@ function resolveCoreToolPath(context, path = ".") {
1767
1767
  function toolResult(input) {
1768
1768
  return input.ok ? { ok: true, content: input.content } : { ok: false, content: input.content, error: input.error ?? "Tool execution failed" };
1769
1769
  }
1770
+ function isSchemaParseError(error) {
1771
+ return !(error instanceof Error) && Array.isArray(error?.errors);
1772
+ }
1773
+ function toolErrorMessage(error) {
1774
+ if (error instanceof Error) return error.message;
1775
+ if (isSchemaParseError(error) && error.errors.length > 0) {
1776
+ const { errors } = error;
1777
+ return `Invalid arguments: ${errors.map((item) => `${item.instancePath || "/"} ${item.message ?? "is invalid"}`).join("; ")}`;
1778
+ }
1779
+ return String(error);
1780
+ }
1770
1781
  async function executeRuntimeToolCall(input) {
1771
1782
  const tool = input.tools.find((candidate) => candidate.name === input.toolCall.name);
1772
1783
  if (!tool) {
@@ -1780,13 +1791,19 @@ async function executeRuntimeToolCall(input) {
1780
1791
  }
1781
1792
  let args;
1782
1793
  try {
1783
- const rawArgs = input.toolCall.arguments;
1794
+ let rawArgs = input.toolCall.arguments;
1795
+ if (typeof rawArgs === "string") {
1796
+ try {
1797
+ rawArgs = JSON.parse(rawArgs);
1798
+ } catch {
1799
+ }
1800
+ }
1784
1801
  args = validatorFor(tool.parameters).Parse(rawArgs);
1785
1802
  } catch (error) {
1786
1803
  const result = toolResult({
1787
1804
  ok: false,
1788
1805
  content: null,
1789
- error: error instanceof Error ? error.message : String(error)
1806
+ error: toolErrorMessage(error)
1790
1807
  });
1791
1808
  await input.observe?.({ type: "tool_end", toolName: tool.name, result });
1792
1809
  return result;
@@ -1963,7 +1980,7 @@ function createReadTool(context) {
1963
1980
  return toolResult({
1964
1981
  ok: false,
1965
1982
  content: null,
1966
- error: error instanceof Error ? error.message : String(error)
1983
+ error: toolErrorMessage(error)
1967
1984
  });
1968
1985
  }
1969
1986
  if (!fileStat.isFile()) {
@@ -1980,7 +1997,7 @@ function createReadTool(context) {
1980
1997
  return toolResult({
1981
1998
  ok: false,
1982
1999
  content: null,
1983
- error: error instanceof Error ? error.message : String(error)
2000
+ error: toolErrorMessage(error)
1984
2001
  });
1985
2002
  }
1986
2003
  const source = new TextDecoder().decode(bytes);
@@ -2095,7 +2112,7 @@ function createEditTool(context) {
2095
2112
  return toolResult({
2096
2113
  ok: false,
2097
2114
  content: null,
2098
- error: error instanceof Error ? error.message : String(error)
2115
+ error: toolErrorMessage(error)
2099
2116
  });
2100
2117
  }
2101
2118
  await writeFile(resolved.absolutePath, next, "utf8");
@@ -2632,9 +2649,6 @@ function createRouteToolResultContent(toolCall, error, executionContext) {
2632
2649
  ];
2633
2650
  }
2634
2651
  async function executePhaseWithModel(ctx) {
2635
- const executableToolNames = new Set(
2636
- ctx.context.tools.filter((tool) => tool.name !== PhaseRouteTool).map((tool) => tool.name)
2637
- );
2638
2652
  let output = {
2639
2653
  message: "",
2640
2654
  phase: ctx.phase.name,
@@ -2652,7 +2666,10 @@ async function executePhaseWithModel(ctx) {
2652
2666
  if (checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension") {
2653
2667
  const toolSuspension = checkpoint;
2654
2668
  executableToolCalls = toolSuspension.toolCalls.filter(
2655
- (toolCall) => executableToolNames.has(toolCall.name)
2669
+ (toolCall) => (
2670
+ // An unknown name still runs, so the model gets "not available" back instead of a silent end of turn.
2671
+ toolCall.name !== PhaseRouteTool
2672
+ )
2656
2673
  );
2657
2674
  output = {
2658
2675
  message: "",
@@ -2686,7 +2703,10 @@ async function executePhaseWithModel(ctx) {
2686
2703
  await ctx.messageManager.end(messageId);
2687
2704
  }
2688
2705
  executableToolCalls = collected.toolCalls.filter(
2689
- (toolCall) => executableToolNames.has(toolCall.name)
2706
+ (toolCall) => (
2707
+ // An unknown name still runs, so the model gets "not available" back instead of a silent end of turn.
2708
+ toolCall.name !== PhaseRouteTool
2709
+ )
2690
2710
  );
2691
2711
  roundContentBlocks = collected.contentBlocks;
2692
2712
  if (executableToolCalls.length === 0) {
@@ -5325,7 +5345,7 @@ var InMemoryStore = class _InMemoryStore {
5325
5345
  interruptOwner(ownerEpoch, message = "The previous Runtime owner expired or closed.") {
5326
5346
  this.ownerEpoch = Math.max(this.ownerEpoch, ownerEpoch);
5327
5347
  for (const run of this.runs.values()) {
5328
- if (run.state !== "running" || run.execution?.ownerEpoch !== ownerEpoch) continue;
5348
+ if (run.state !== "running" || !run.execution || run.execution.ownerEpoch > ownerEpoch) continue;
5329
5349
  const activeToolCalls = [...this.toolCalls.values()].filter((toolCall) => toolCall.runId === run.id && toolCall.executionId === run.execution?.executionId && (toolCall.status === "pending" || toolCall.status === "in_progress" || toolCall.state === "pending" || toolCall.state === "in_progress"));
5330
5350
  const indeterminateToolCallIds = [];
5331
5351
  for (const toolCall of activeToolCalls) {
@@ -10668,12 +10688,12 @@ var AgentRuntime = class _AgentRuntime {
10668
10688
  expectedRevision: revision,
10669
10689
  toolCallId,
10670
10690
  state: "failed",
10671
- result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` },
10691
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available. Available tools: ${input.toolConfig.tools.map(({ name }) => name).join(", ")}.` },
10672
10692
  title: toolCall.name,
10673
10693
  kind: "other"
10674
10694
  });
10675
10695
  setRevision(failed.run.revision);
10676
- results2.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
10696
+ results2.push(protocolFailure(`Tool ${toolCall.name} is not available. Available tools: ${input.toolConfig.tools.map(({ name }) => name).join(", ")}.`));
10677
10697
  continue;
10678
10698
  }
10679
10699
  const decision = preDecisions[i] ?? { allow: true };
@@ -10791,12 +10811,12 @@ var AgentRuntime = class _AgentRuntime {
10791
10811
  expectedRevision: revision,
10792
10812
  toolCallId,
10793
10813
  state: "failed",
10794
- result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` },
10814
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available. Available tools: ${input.toolConfig.tools.map(({ name }) => name).join(", ")}.` },
10795
10815
  title: toolCall.name,
10796
10816
  kind: "other"
10797
10817
  });
10798
10818
  setRevision(failed.run.revision);
10799
- results.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
10819
+ results.push(protocolFailure(`Tool ${toolCall.name} is not available. Available tools: ${input.toolConfig.tools.map(({ name }) => name).join(", ")}.`));
10800
10820
  continue;
10801
10821
  }
10802
10822
  const pendingPres = await presentToolPending(durableTool, toolCallArgs(toolCall));
@@ -11167,7 +11187,11 @@ var AgentRuntime = class _AgentRuntime {
11167
11187
  throw error;
11168
11188
  }
11169
11189
  let result = await withAbort(
11170
- input.durableTool.execute(toolCallArgs(input.toolCall), context, effectiveSignal),
11190
+ // Invalid arguments are the model's to fix: a result it can read, not an indeterminate outcome.
11191
+ Promise.resolve(input.durableTool.execute(toolCallArgs(input.toolCall), context, effectiveSignal)).catch((error) => {
11192
+ if (isSchemaParseError(error)) return { ok: false, content: null, error: toolErrorMessage(error) };
11193
+ throw error;
11194
+ }),
11171
11195
  input.options?.signal
11172
11196
  );
11173
11197
  if (!("ok" in result)) {
@@ -11606,6 +11630,12 @@ function toJsonValue(value) {
11606
11630
  return value;
11607
11631
  }
11608
11632
  function toolCallArgs(toolCall) {
11633
+ if (typeof toolCall.arguments === "string") {
11634
+ try {
11635
+ return toJsonValue(JSON.parse(toolCall.arguments));
11636
+ } catch {
11637
+ }
11638
+ }
11609
11639
  return toJsonValue(toolCall.arguments);
11610
11640
  }
11611
11641
  function createMergedToolCall(toolCallId, status, current, fallbackTitle, fallbackKind = "other") {
@@ -11896,10 +11926,6 @@ var SqliteStore = class {
11896
11926
  }
11897
11927
  const state = this.readState();
11898
11928
  const memory = InMemoryStore.fromState(state);
11899
- if (row.owner_id && row.owner_token && row.epoch > 0) {
11900
- memory.attachOwner(ownerLease(row));
11901
- memory.interruptOwner(row.epoch);
11902
- }
11903
11929
  const epoch = row.epoch + 1;
11904
11930
  const token = `${state.incarnation}:${epoch}:${input.ownerId}`;
11905
11931
  const next = {
@@ -11921,6 +11947,7 @@ var SqliteStore = class {
11921
11947
  const tracker = new ChangeTracker();
11922
11948
  memory.setChangeRecorder(tracker);
11923
11949
  try {
11950
+ if (row.epoch > 0) memory.interruptOwner(row.epoch);
11924
11951
  memory.dropSettledClaimReceipts();
11925
11952
  memory.compactTerminalReceipts();
11926
11953
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.35.0",
3
+ "version": "0.35.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",