stitchkit 0.85.1 → 0.86.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 (114) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/README.md +3 -0
  3. package/dist/agent-runtime/agent-tool.d.ts +25 -0
  4. package/dist/agent-runtime/agent-tool.d.ts.map +1 -0
  5. package/dist/agent-runtime/child-tools.d.ts +69 -0
  6. package/dist/agent-runtime/child-tools.d.ts.map +1 -0
  7. package/dist/agent-runtime/children.d.ts +113 -0
  8. package/dist/agent-runtime/children.d.ts.map +1 -0
  9. package/dist/agent-runtime/coding-tool-contract.d.ts +29 -0
  10. package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
  11. package/dist/agent-runtime/coding-tool-refusals.d.ts +2 -0
  12. package/dist/agent-runtime/coding-tool-refusals.d.ts.map +1 -1
  13. package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -1
  14. package/dist/agent-runtime/coding-tools.d.ts +1 -0
  15. package/dist/agent-runtime/coding-tools.d.ts.map +1 -1
  16. package/dist/agent-runtime/control-schema.d.ts +17 -0
  17. package/dist/agent-runtime/control-schema.d.ts.map +1 -1
  18. package/dist/agent-runtime/event-schema.d.ts +34 -0
  19. package/dist/agent-runtime/event-schema.d.ts.map +1 -1
  20. package/dist/agent-runtime/event-search-tools.d.ts +53 -0
  21. package/dist/agent-runtime/event-search-tools.d.ts.map +1 -0
  22. package/dist/agent-runtime/event-search.d.ts +23 -0
  23. package/dist/agent-runtime/event-search.d.ts.map +1 -0
  24. package/dist/agent-runtime/fault-bench.d.ts +45 -0
  25. package/dist/agent-runtime/fault-bench.d.ts.map +1 -0
  26. package/dist/agent-runtime/observability.d.ts +6 -0
  27. package/dist/agent-runtime/observability.d.ts.map +1 -1
  28. package/dist/agent-runtime/projections.d.ts +69 -0
  29. package/dist/agent-runtime/projections.d.ts.map +1 -0
  30. package/dist/agent-runtime/provider-failure.d.ts +5 -1
  31. package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
  32. package/dist/agent-runtime/provider-origin.d.ts +12 -0
  33. package/dist/agent-runtime/provider-origin.d.ts.map +1 -0
  34. package/dist/agent-runtime/retry-policy.d.ts +27 -0
  35. package/dist/agent-runtime/retry-policy.d.ts.map +1 -0
  36. package/dist/agent-runtime/run-execution.d.ts +0 -9
  37. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  38. package/dist/agent-runtime/run-mutation-queue.d.ts +21 -0
  39. package/dist/agent-runtime/run-mutation-queue.d.ts.map +1 -0
  40. package/dist/agent-runtime/run-operation-lifecycle.d.ts +3 -0
  41. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  42. package/dist/agent-runtime/runtime.d.ts +15 -0
  43. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  44. package/dist/agent-runtime/sandbox.d.ts +66 -0
  45. package/dist/agent-runtime/sandbox.d.ts.map +1 -0
  46. package/dist/agent-runtime/schedule-tools.d.ts +35 -0
  47. package/dist/agent-runtime/schedule-tools.d.ts.map +1 -0
  48. package/dist/agent-runtime/schedules.d.ts +63 -0
  49. package/dist/agent-runtime/schedules.d.ts.map +1 -0
  50. package/dist/agent-runtime/schemas.d.ts +9 -0
  51. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  52. package/dist/agent-runtime/spill.d.ts +24 -0
  53. package/dist/agent-runtime/spill.d.ts.map +1 -0
  54. package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
  55. package/dist/agent-runtime/sqlite.d.ts +26 -0
  56. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  57. package/dist/agent-runtime/state-slots.d.ts +48 -0
  58. package/dist/agent-runtime/state-slots.d.ts.map +1 -0
  59. package/dist/agent-runtime/state-tools.d.ts +58 -0
  60. package/dist/agent-runtime/state-tools.d.ts.map +1 -0
  61. package/dist/agent-runtime/store-driver.d.ts +13 -0
  62. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  63. package/dist/agent-runtime/store-events.d.ts +829 -0
  64. package/dist/agent-runtime/store-events.d.ts.map +1 -0
  65. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts +6 -0
  66. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts.map +1 -0
  67. package/dist/agent-runtime/store.d.ts +45 -0
  68. package/dist/agent-runtime/store.d.ts.map +1 -1
  69. package/dist/agent-runtime/terminal-commit.d.ts +1 -1
  70. package/dist/agent-runtime-browser.js +5 -3
  71. package/dist/agent-runtime-coding-tools.js +144 -15
  72. package/dist/agent-runtime-harness.js +12 -10
  73. package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
  74. package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
  75. package/dist/agent-runtime-sqlite-bun.js +4 -3
  76. package/dist/agent-runtime-sqlite-node.d.ts +1 -1
  77. package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
  78. package/dist/agent-runtime-sqlite-node.js +4 -3
  79. package/dist/agent-runtime-testing.d.ts +3 -0
  80. package/dist/agent-runtime-testing.d.ts.map +1 -0
  81. package/dist/agent-runtime-testing.js +156 -0
  82. package/dist/agent-runtime.d.ts +16 -2
  83. package/dist/agent-runtime.d.ts.map +1 -1
  84. package/dist/agent-runtime.js +1591 -154
  85. package/dist/application.js +14 -14
  86. package/dist/cli.js +6 -6
  87. package/dist/{index-fwbkp2jn.js → index-0vfdh685.js} +2 -2
  88. package/dist/{index-5z8tezpx.js → index-0ydchz76.js} +3 -3
  89. package/dist/{index-3ds42w1w.js → index-1szetq6f.js} +33 -4
  90. package/dist/{index-jpp7jd1p.js → index-33xckd7h.js} +898 -425
  91. package/dist/{index-27487ay5.js → index-9hw9eq48.js} +4 -4
  92. package/dist/{index-66nxrgy0.js → index-b7qwhbzq.js} +428 -46
  93. package/dist/index-cnyk6te3.js +77 -0
  94. package/dist/index-gskm1k5a.js +50 -0
  95. package/dist/{index-fq492hg0.js → index-hber83mk.js} +254 -157
  96. package/dist/{index-4hk633vz.js → index-hkm6wysp.js} +9 -2
  97. package/dist/{index-dx9xehwt.js → index-kc6h6hg0.js} +1 -1
  98. package/dist/{index-x15ss2dx.js → index-p1b1y93x.js} +3 -0
  99. package/dist/{index-mgtxfe6b.js → index-r159gjwy.js} +8 -1
  100. package/dist/{index-s4ckmrac.js → index-rcgsnrr6.js} +3 -3
  101. package/dist/index-vfgb58nf.js +252 -0
  102. package/dist/{index-dggwnnjv.js → index-wn9cb82y.js} +1 -1
  103. package/dist/{index-ff0kcqvp.js → index-z2452tsw.js} +3 -3
  104. package/dist/testing/agent-store-conformance.d.ts.map +1 -1
  105. package/dist/testing.js +54 -80
  106. package/dist/tool-invoker.js +4 -4
  107. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  108. package/dist/tools/manifest.d.ts +16 -0
  109. package/dist/tools/manifest.d.ts.map +1 -1
  110. package/dist/tools.d.ts +1 -1
  111. package/dist/tools.d.ts.map +1 -1
  112. package/dist/tools.js +10 -8
  113. package/llms-full.txt +321 -13
  114. package/package.json +6 -1
@@ -1,3 +1,10 @@
1
+ import {
2
+ messageOf
3
+ } from "./index-p13mwz16.js";
4
+ import {
5
+ AgentRuntimeEventSchema,
6
+ agentDurableEventId
7
+ } from "./index-hkm6wysp.js";
1
8
  import {
2
9
  advanceToolChronology,
3
10
  assistantStatus,
@@ -7,12 +14,8 @@ import {
7
14
  isCompleteAgentHistoryTurn
8
15
  } from "./index-y2s6h5bf.js";
9
16
  import {
10
- createBoundedSinkManager
11
- } from "./index-wnfk50r0.js";
12
- import {
13
- AgentRuntimeEventSchema,
14
- agentDurableEventId
15
- } from "./index-4hk633vz.js";
17
+ canonicalAgentJson
18
+ } from "./index-vfgb58nf.js";
16
19
  import {
17
20
  AgentAssistantPlaceholderSchema,
18
21
  AgentJsonObjectSchema,
@@ -23,19 +26,145 @@ import {
23
26
  AgentRunSchema,
24
27
  AgentSnapshotSchema,
25
28
  runStateForTerminalReason
26
- } from "./index-x15ss2dx.js";
29
+ } from "./index-p1b1y93x.js";
30
+ import {
31
+ createBoundedSinkManager
32
+ } from "./index-wnfk50r0.js";
27
33
  import {
28
34
  isAgentToolError
29
35
  } from "./index-3xnq72rz.js";
30
36
  import {
31
37
  ToolExecutionControlError,
32
38
  isToolExecutionControlError
33
- } from "./index-dggwnnjv.js";
39
+ } from "./index-wn9cb82y.js";
34
40
  import {
35
41
  isRecord,
36
42
  transportResult
37
43
  } from "./index-77fekveh.js";
38
44
 
45
+ // src/agent-runtime/terminal-commit.ts
46
+ class AgentRuntimeConflictError extends Error {
47
+ constructor(operation) {
48
+ super(`Agent runtime store conflict during ${operation}`);
49
+ this.name = "AgentRuntimeConflictError";
50
+ }
51
+ }
52
+ function appliedSnapshot(result, operation) {
53
+ if (result.outcome === "applied" || result.outcome === "duplicate")
54
+ return result.snapshot;
55
+ throw new AgentRuntimeConflictError(operation);
56
+ }
57
+ function viewOfSnapshot(snapshot, runId, retainedAssistant) {
58
+ const run = snapshot.runs.find((candidate) => candidate.id === runId);
59
+ const assistant = snapshot.messages.find((message) => message.id === run?.assistantMessageId) ?? retainedAssistant;
60
+ return {
61
+ snapshotVersion: snapshot.version,
62
+ conversationId: snapshot.conversationId,
63
+ ...run && { run },
64
+ ...assistant && { assistant }
65
+ };
66
+ }
67
+ function viewOfRun(view, conversationId) {
68
+ return {
69
+ snapshotVersion: view.snapshotVersion,
70
+ conversationId,
71
+ run: view.run,
72
+ ...view.assistant && { assistant: view.assistant }
73
+ };
74
+ }
75
+ function canonicalTerminal(view) {
76
+ const { run, assistant } = view;
77
+ if (!run?.terminalReason)
78
+ return;
79
+ if (view.conversationId !== run.conversationId || !assistant || assistant.id !== run.assistantMessageId || assistant.conversationId !== run.conversationId || assistant.runId !== run.id || assistant.role !== "assistant" || assistant.status !== assistantStatus(run.terminalReason)) {
80
+ throw new AgentRuntimeConflictError("terminal result projection");
81
+ }
82
+ return {
83
+ snapshotVersion: view.snapshotVersion,
84
+ run,
85
+ assistant,
86
+ reason: run.terminalReason,
87
+ committedByCaller: false,
88
+ ...run.terminalPolicyName && { policyName: run.terminalPolicyName },
89
+ ...run.usage && { usage: run.usage }
90
+ };
91
+ }
92
+ function interruptedCandidate(candidate, run, now) {
93
+ const reason = candidate.reason === "superseded" ? "superseded" : "interrupted";
94
+ const hasInterruptControl = candidate.assistant.parts.some((part) => part.type === "control" && part.reason === "run-interrupted");
95
+ const parts = hasInterruptControl ? candidate.assistant.parts : [
96
+ ...candidate.assistant.parts,
97
+ AgentMessagePartSchema.parse({ type: "control", reason: "run-interrupted" })
98
+ ];
99
+ return {
100
+ ...candidate.usage && { usage: candidate.usage },
101
+ run,
102
+ assistant: AgentMessageSchema.parse({
103
+ ...candidate.assistant,
104
+ status: assistantStatus(reason),
105
+ parts,
106
+ updatedAt: now().toISOString()
107
+ }),
108
+ reason
109
+ };
110
+ }
111
+ function canRetryTerminal(current, previous, runtimeEpoch) {
112
+ return (current.state === "running" || current.state === "interrupt_requested") && current.ownerId === runtimeEpoch && current.fencingToken === previous.fencingToken;
113
+ }
114
+ var TERMINAL_COMMIT_ATTEMPTS = 32;
115
+ async function commitAgentRunTerminal(input) {
116
+ let candidate = input.candidate;
117
+ for (let attempt = 0;attempt < TERMINAL_COMMIT_ATTEMPTS; attempt += 1) {
118
+ const committed = await input.store.commitRunTerminal({
119
+ conversationId: candidate.run.conversationId,
120
+ runId: candidate.run.id,
121
+ expectedRevision: candidate.run.revision,
122
+ ownerId: input.runtimeEpoch,
123
+ ...candidate.run.fencingToken !== undefined && {
124
+ fencingToken: candidate.run.fencingToken
125
+ },
126
+ assistant: candidate.assistant,
127
+ reason: candidate.reason,
128
+ ...candidate.policyName && { policyName: candidate.policyName },
129
+ ...candidate.usage && { usage: candidate.usage },
130
+ ...candidate.absorb?.length && { absorb: candidate.absorb }
131
+ });
132
+ if (committed.outcome === "applied") {
133
+ const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id));
134
+ if (!terminal2)
135
+ throw new AgentRuntimeConflictError("terminal result projection");
136
+ const absorbed = committed.snapshot.runs.filter((run) => run.absorbedIntoRunId === candidate.run.id);
137
+ return {
138
+ ...terminal2,
139
+ committedByCaller: true,
140
+ ...absorbed.length > 0 && { absorbed }
141
+ };
142
+ }
143
+ if (committed.outcome === "duplicate") {
144
+ const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id, committed.assistant));
145
+ if (!terminal2)
146
+ throw new AgentRuntimeConflictError("terminal result projection");
147
+ return terminal2;
148
+ }
149
+ if (committed.outcome !== "conflict") {
150
+ throw new AgentRuntimeConflictError("terminal commit");
151
+ }
152
+ const latest = await input.store.loadRun({
153
+ conversationId: candidate.run.conversationId,
154
+ runId: candidate.run.id
155
+ });
156
+ const terminal = latest ? canonicalTerminal(viewOfRun(latest, candidate.run.conversationId)) : undefined;
157
+ if (terminal)
158
+ return terminal;
159
+ const current = latest?.run;
160
+ if (!current || !canRetryTerminal(current, candidate.run, input.runtimeEpoch)) {
161
+ throw new AgentRuntimeConflictError("terminal commit");
162
+ }
163
+ candidate = current.state === "interrupt_requested" ? interruptedCandidate(candidate, current, input.now) : { ...candidate, run: current };
164
+ }
165
+ throw new AgentRuntimeConflictError("terminal commit");
166
+ }
167
+
39
168
  // src/agent-runtime/context-refusal.ts
40
169
  class AgentContextOverflowError extends Error {
41
170
  name = "AgentContextOverflowError";
@@ -686,128 +815,232 @@ function composeAgentPrompt(sections) {
686
815
  };
687
816
  }
688
817
 
689
- // src/agent-runtime/terminal-commit.ts
690
- class AgentRuntimeConflictError extends Error {
691
- constructor(operation) {
692
- super(`Agent runtime store conflict during ${operation}`);
693
- this.name = "AgentRuntimeConflictError";
818
+ // src/agent-runtime/provider-failure.ts
819
+ var BY_STATUS = {
820
+ 402: "insufficient-credits",
821
+ 404: "model-unavailable",
822
+ 408: "timeout",
823
+ 413: "context-overflow",
824
+ 429: "rate-limited"
825
+ };
826
+ var BY_MESSAGE = [
827
+ [/insufficient|not enough credit|payment required|quota exceeded/i, "insufficient-credits"],
828
+ [/rate.?limit|too many requests|slow down/i, "rate-limited"],
829
+ [
830
+ /no endpoints found|model not found|unknown model|no allowed providers/i,
831
+ "model-unavailable"
832
+ ],
833
+ [/context length|maximum context|too many tokens|prompt is too long/i, "context-overflow"],
834
+ [/timed? ?out|deadline exceeded|etimedout/i, "timeout"],
835
+ [/abort|cancell?ed/i, "cancelled"]
836
+ ];
837
+ var RETRYABLE = new Set([
838
+ "rate-limited",
839
+ "timeout",
840
+ "insufficient-credits",
841
+ "stream-cut"
842
+ ]);
843
+
844
+ class AgentProviderStreamCutError extends Error {
845
+ reason = "stream-cut";
846
+ constructor(message = "Provider stream ended before a terminal part") {
847
+ super(message);
848
+ this.name = "AgentProviderStreamCutError";
694
849
  }
695
850
  }
696
- function appliedSnapshot(result, operation) {
697
- if (result.outcome === "applied" || result.outcome === "duplicate")
698
- return result.snapshot;
699
- throw new AgentRuntimeConflictError(operation);
851
+ function statusOf(value) {
852
+ if (!isRecord(value))
853
+ return;
854
+ for (const key of ["statusCode", "status", "code"]) {
855
+ const candidate = value[key];
856
+ if (typeof candidate === "number" && candidate >= 400 && candidate < 600)
857
+ return candidate;
858
+ }
859
+ const nested = value.cause ?? value.response ?? value.error;
860
+ return nested === value ? undefined : statusOf(nested);
700
861
  }
701
- function viewOfSnapshot(snapshot, runId, retainedAssistant) {
702
- const run = snapshot.runs.find((candidate) => candidate.id === runId);
703
- const assistant = snapshot.messages.find((message) => message.id === run?.assistantMessageId) ?? retainedAssistant;
862
+ function classifyProviderFailure(error) {
863
+ if (error instanceof AgentProviderStreamCutError) {
864
+ return { reason: "stream-cut", retryable: true, evidence: "none" };
865
+ }
866
+ const status = statusOf(error);
867
+ const byStatus = status === undefined ? undefined : BY_STATUS[status];
868
+ if (byStatus) {
869
+ return {
870
+ reason: byStatus,
871
+ status,
872
+ retryable: RETRYABLE.has(byStatus),
873
+ evidence: "status"
874
+ };
875
+ }
876
+ const message = messageOf(error);
877
+ for (const [pattern, reason] of BY_MESSAGE) {
878
+ if (pattern.test(message)) {
879
+ return {
880
+ reason,
881
+ ...status !== undefined && { status },
882
+ retryable: RETRYABLE.has(reason),
883
+ evidence: "message"
884
+ };
885
+ }
886
+ }
704
887
  return {
705
- snapshotVersion: snapshot.version,
706
- conversationId: snapshot.conversationId,
707
- ...run && { run },
708
- ...assistant && { assistant }
888
+ reason: "unknown",
889
+ ...status !== undefined && { status },
890
+ retryable: false,
891
+ evidence: "none"
709
892
  };
710
893
  }
711
- function viewOfRun(view, conversationId) {
712
- return {
713
- snapshotVersion: view.snapshotVersion,
714
- conversationId,
715
- run: view.run,
716
- ...view.assistant && { assistant: view.assistant }
717
- };
894
+ function isToolResultFailure(output) {
895
+ const parsed = typeof output === "string" ? parseJson(output) : output;
896
+ if (!isRecord(parsed))
897
+ return false;
898
+ if ("error" in parsed)
899
+ return true;
900
+ return isRecord(parsed.value) && "error" in parsed.value;
718
901
  }
719
- function canonicalTerminal(view) {
720
- const { run, assistant } = view;
721
- if (!run?.terminalReason)
902
+ function parseJson(value) {
903
+ try {
904
+ return JSON.parse(value);
905
+ } catch {
722
906
  return;
723
- if (view.conversationId !== run.conversationId || !assistant || assistant.id !== run.assistantMessageId || assistant.conversationId !== run.conversationId || assistant.runId !== run.id || assistant.role !== "assistant" || assistant.status !== assistantStatus(run.terminalReason)) {
724
- throw new AgentRuntimeConflictError("terminal result projection");
725
907
  }
726
- return {
727
- snapshotVersion: view.snapshotVersion,
728
- run,
729
- assistant,
730
- reason: run.terminalReason,
731
- committedByCaller: false,
732
- ...run.terminalPolicyName && { policyName: run.terminalPolicyName },
733
- ...run.usage && { usage: run.usage }
734
- };
735
908
  }
736
- function interruptedCandidate(candidate, run, now) {
737
- const reason = candidate.reason === "superseded" ? "superseded" : "interrupted";
738
- const hasInterruptControl = candidate.assistant.parts.some((part) => part.type === "control" && part.reason === "run-interrupted");
739
- const parts = hasInterruptControl ? candidate.assistant.parts : [
740
- ...candidate.assistant.parts,
741
- AgentMessagePartSchema.parse({ type: "control", reason: "run-interrupted" })
742
- ];
909
+
910
+ // src/agent-runtime/retry-policy.ts
911
+ async function recordAgentRetryDecision(input) {
912
+ const retry = input.failure.retryable && input.attempt < input.policy.maxAttempts;
913
+ const delayMs = retry ? input.policy.delayMs({ attempt: input.attempt, failure: input.failure }) : 0;
914
+ if (!Number.isInteger(delayMs) || delayMs < 0) {
915
+ throw new TypeError("Retry delay must be a non-negative integer");
916
+ }
917
+ if (retry) {
918
+ await input.store.appendEvent({
919
+ conversationId: input.conversationId,
920
+ kind: "retry/scheduled",
921
+ payload: { attempt: input.attempt + 1, delayMs, reason: input.failure.reason }
922
+ });
923
+ }
924
+ return { retry, delayMs };
925
+ }
926
+
927
+ // src/agent-runtime/state-slots.ts
928
+ import { z as z2 } from "zod";
929
+ function defineStateSlot(input) {
930
+ if (input.name.length === 0)
931
+ throw new TypeError("State slot name must not be empty");
932
+ const version = input.version ?? 1;
933
+ if (!Number.isInteger(version) || version < 1) {
934
+ throw new TypeError("State slot version must be a positive integer");
935
+ }
743
936
  return {
744
- ...candidate.usage && { usage: candidate.usage },
745
- run,
746
- assistant: AgentMessageSchema.parse({
747
- ...candidate.assistant,
748
- status: assistantStatus(reason),
749
- parts,
750
- updatedAt: now().toISOString()
751
- }),
752
- reason
937
+ name: input.name,
938
+ version,
939
+ schema: input.schema,
940
+ ...input.description && { description: input.description }
753
941
  };
754
942
  }
755
- function canRetryTerminal(current, previous, runtimeEpoch) {
756
- return (current.state === "running" || current.state === "interrupt_requested") && current.ownerId === runtimeEpoch && current.fencingToken === previous.fencingToken;
943
+ var StateSetPayloadSchema = z2.object({
944
+ name: z2.string().min(1),
945
+ version: z2.int().positive(),
946
+ value: z2.json(),
947
+ actor: z2.enum(["agent", "human", "system"])
948
+ }).strict();
949
+ function slotRegistry(definitions) {
950
+ const result = new Map;
951
+ for (const definition of definitions) {
952
+ if (result.has(definition.name))
953
+ throw new TypeError(`Duplicate state slot: ${definition.name}`);
954
+ result.set(definition.name, definition);
955
+ }
956
+ return result;
757
957
  }
758
- var TERMINAL_COMMIT_ATTEMPTS = 32;
759
- async function commitAgentRunTerminal(input) {
760
- let candidate = input.candidate;
761
- for (let attempt = 0;attempt < TERMINAL_COMMIT_ATTEMPTS; attempt += 1) {
762
- const committed = await input.store.commitRunTerminal({
763
- conversationId: candidate.run.conversationId,
764
- runId: candidate.run.id,
765
- expectedRevision: candidate.run.revision,
766
- ownerId: input.runtimeEpoch,
767
- ...candidate.run.fencingToken !== undefined && {
768
- fencingToken: candidate.run.fencingToken
769
- },
770
- assistant: candidate.assistant,
771
- reason: candidate.reason,
772
- ...candidate.policyName && { policyName: candidate.policyName },
773
- ...candidate.usage && { usage: candidate.usage },
774
- ...candidate.absorb?.length && { absorb: candidate.absorb }
958
+ function createAgentStateSlotStore(input) {
959
+ const definitions = slotRegistry(input.definitions);
960
+ const set = async (request) => {
961
+ const definition = definitions.get(request.name);
962
+ if (!definition)
963
+ throw new TypeError(`Unknown state slot: ${request.name}`);
964
+ const value = definition.schema.parse(request.value);
965
+ const payload = StateSetPayloadSchema.parse({
966
+ name: definition.name,
967
+ version: definition.version,
968
+ value,
969
+ actor: request.actor
775
970
  });
776
- if (committed.outcome === "applied") {
777
- const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id));
778
- if (!terminal2)
779
- throw new AgentRuntimeConflictError("terminal result projection");
780
- const absorbed = committed.snapshot.runs.filter((run) => run.absorbedIntoRunId === candidate.run.id);
781
- return {
782
- ...terminal2,
783
- committedByCaller: true,
784
- ...absorbed.length > 0 && { absorbed }
785
- };
786
- }
787
- if (committed.outcome === "duplicate") {
788
- const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id, committed.assistant));
789
- if (!terminal2)
790
- throw new AgentRuntimeConflictError("terminal result projection");
791
- return terminal2;
792
- }
793
- if (committed.outcome !== "conflict") {
794
- throw new AgentRuntimeConflictError("terminal commit");
795
- }
796
- const latest = await input.store.loadRun({
797
- conversationId: candidate.run.conversationId,
798
- runId: candidate.run.id
971
+ const event = await input.store.appendEvent({
972
+ conversationId: request.conversationId,
973
+ kind: "state/set",
974
+ payload
799
975
  });
800
- const terminal = latest ? canonicalTerminal(viewOfRun(latest, candidate.run.conversationId)) : undefined;
801
- if (terminal)
802
- return terminal;
803
- const current = latest?.run;
804
- if (!current || !canRetryTerminal(current, candidate.run, input.runtimeEpoch)) {
805
- throw new AgentRuntimeConflictError("terminal commit");
976
+ return {
977
+ name: definition.name,
978
+ version: definition.version,
979
+ value,
980
+ updatedAt: event.occurredAt,
981
+ seq: event.seq
982
+ };
983
+ };
984
+ const list = async (conversationId) => {
985
+ const values = new Map;
986
+ let cursor = 1;
987
+ for (;; ) {
988
+ const page = await input.store.readEvents({
989
+ conversationId,
990
+ fromSeq: cursor,
991
+ limit: 1000
992
+ });
993
+ for (const event of page.items) {
994
+ if (event.kind !== "state/set")
995
+ continue;
996
+ const payload = StateSetPayloadSchema.parse(event.payload);
997
+ const definition = definitions.get(payload.name);
998
+ if (!definition || definition.version !== payload.version)
999
+ continue;
1000
+ values.set(payload.name, {
1001
+ name: payload.name,
1002
+ version: payload.version,
1003
+ value: definition.schema.parse(payload.value),
1004
+ updatedAt: event.occurredAt,
1005
+ seq: event.seq
1006
+ });
1007
+ }
1008
+ if (page.nextSeq === undefined)
1009
+ break;
1010
+ cursor = page.nextSeq;
806
1011
  }
807
- candidate = current.state === "interrupt_requested" ? interruptedCandidate(candidate, current, input.now) : { ...candidate, run: current };
808
- }
809
- throw new AgentRuntimeConflictError("terminal commit");
1012
+ return [...values.values()].sort((left, right) => left.name.localeCompare(right.name));
1013
+ };
1014
+ const get = async (conversationId, name) => (await list(conversationId)).find((value) => value.name === name);
1015
+ return { set, get, list };
810
1016
  }
1017
+ function renderAgentStateSlots(values) {
1018
+ if (values.length === 0)
1019
+ return;
1020
+ return [
1021
+ "Durable conversation state (authoritative; newer than compacted history):",
1022
+ ...values.map((slot) => `${slot.name}: ${JSON.stringify(slot.value)}`)
1023
+ ].join(`
1024
+ `);
1025
+ }
1026
+ var agentGoalStateSlot = defineStateSlot({
1027
+ name: "goal",
1028
+ description: "The durable objective and lifecycle of this conversation.",
1029
+ schema: z2.object({
1030
+ objective: z2.string().min(1),
1031
+ status: z2.enum(["active", "complete", "blocked"])
1032
+ }).strict()
1033
+ });
1034
+ var agentTodoStateSlot = defineStateSlot({
1035
+ name: "todo",
1036
+ description: "The durable ordered work plan for this conversation.",
1037
+ schema: z2.object({
1038
+ items: z2.array(z2.object({
1039
+ step: z2.string().min(1),
1040
+ status: z2.enum(["pending", "in_progress", "completed"])
1041
+ }).strict())
1042
+ }).strict()
1043
+ });
811
1044
 
812
1045
  // src/agent-runtime/admission-lanes.ts
813
1046
  function createRuntimeAdmissionLanes() {
@@ -904,6 +1137,8 @@ function createAgentInjectionRegistry() {
904
1137
  }
905
1138
 
906
1139
  // src/agent-runtime/run-execution.ts
1140
+ import { createHash } from "node:crypto";
1141
+ import { setTimeout as delay } from "node:timers/promises";
907
1142
  import {
908
1143
  stepCountIs
909
1144
  } from "ai";
@@ -934,11 +1169,44 @@ function repairedSearchCall(toolCall, searchName) {
934
1169
  };
935
1170
  }
936
1171
 
1172
+ // src/agent-runtime/provider-origin.ts
1173
+ import {
1174
+ InvalidToolApprovalError,
1175
+ InvalidToolApprovalSignatureError,
1176
+ ToolCallNotFoundForApprovalError
1177
+ } from "ai";
1178
+ var providerOrigin = new WeakSet;
1179
+ function isOwnInputRefusal(error) {
1180
+ return InvalidToolApprovalSignatureError.isInstance(error) || InvalidToolApprovalError.isInstance(error) || ToolCallNotFoundForApprovalError.isInstance(error);
1181
+ }
1182
+ function markProviderOrigin(error) {
1183
+ if (typeof error === "object" && error !== null)
1184
+ providerOrigin.add(error);
1185
+ return error;
1186
+ }
1187
+ function hasProviderOrigin(error) {
1188
+ return typeof error === "object" && error !== null && providerOrigin.has(error);
1189
+ }
1190
+
1191
+ // src/agent-runtime/run-mutation-queue.ts
1192
+ function createRunMutationQueue() {
1193
+ let tail = Promise.resolve();
1194
+ return (mutation) => {
1195
+ const result = tail.then(mutation, mutation);
1196
+ tail = result.then(() => {
1197
+ return;
1198
+ }, () => {
1199
+ return;
1200
+ });
1201
+ return result;
1202
+ };
1203
+ }
1204
+
937
1205
  // src/agent-runtime/run-operation-lifecycle.ts
938
1206
  import { wrapLanguageModel } from "ai";
939
1207
 
940
1208
  // src/agent-runtime/runtime-internals.ts
941
- import { z as z2 } from "zod";
1209
+ import { z as z3 } from "zod";
942
1210
  function findRun(runs, runId) {
943
1211
  const run = runs.find((candidate) => candidate.id === runId);
944
1212
  if (!run)
@@ -946,7 +1214,7 @@ function findRun(runs, runId) {
946
1214
  return run;
947
1215
  }
948
1216
  function jsonValue(value) {
949
- const parsed = z2.json().safeParse(value);
1217
+ const parsed = z3.json().safeParse(value);
950
1218
  return parsed.success ? parsed.data : { message: "Non-JSON tool output omitted" };
951
1219
  }
952
1220
  function providerEnvelope(value) {
@@ -1084,7 +1352,7 @@ function createAgentRunOperationLifecycle(config) {
1084
1352
  let providerCallId;
1085
1353
  let checkpointedStep = -1;
1086
1354
  const checkpointWaiters = new Map;
1087
- const record = async (operation) => {
1355
+ const record = (operation) => config.serialize(async () => {
1088
1356
  const current = config.currentRun();
1089
1357
  const snapshot = appliedSnapshot(await config.store.recordRunOperation({
1090
1358
  conversationId: current.conversationId,
@@ -1106,14 +1374,16 @@ function createAgentRunOperationLifecycle(config) {
1106
1374
  operation,
1107
1375
  emittedAt: config.now().toISOString()
1108
1376
  });
1377
+ });
1378
+ const startModelRequest = async (callId, step) => {
1379
+ await record(AgentRunOperationSchema.parse({
1380
+ operationId: `${callId}:${step}`,
1381
+ kind: "model-request",
1382
+ phase: "started",
1383
+ step,
1384
+ startedAt: config.now().toISOString()
1385
+ }));
1109
1386
  };
1110
- const startModelRequest = (callId, step) => record(AgentRunOperationSchema.parse({
1111
- operationId: `${callId}:${step}`,
1112
- kind: "model-request",
1113
- phase: "started",
1114
- step,
1115
- startedAt: config.now().toISOString()
1116
- }));
1117
1387
  return {
1118
1388
  noteProviderCall(callId) {
1119
1389
  providerCallId = callId;
@@ -1139,7 +1409,11 @@ function createAgentRunOperationLifecycle(config) {
1139
1409
  throw new Error("AgentRuntime model call reached the provider without lifecycle identity");
1140
1410
  }
1141
1411
  await startModelRequest(callId, step);
1142
- return doStream();
1412
+ try {
1413
+ return await doStream();
1414
+ } catch (error) {
1415
+ throw isOwnInputRefusal(error) ? error : markProviderOrigin(error);
1416
+ }
1143
1417
  }
1144
1418
  }
1145
1419
  });
@@ -1204,6 +1478,39 @@ function snapshotForRunPrompt(snapshot, runId) {
1204
1478
  messages: snapshot.messages.filter((message) => !ownedIds.has(message.id) || eligibleIds.has(message.id))
1205
1479
  });
1206
1480
  }
1481
+ function failureThisRuntimeOwns(error) {
1482
+ if (error instanceof AgentContextOverflowError)
1483
+ return "context_overflow";
1484
+ if (error instanceof AgentRuntimeConflictError)
1485
+ return "storage_conflict";
1486
+ if (isOwnInputRefusal(error))
1487
+ return "runtime_failure";
1488
+ return;
1489
+ }
1490
+ function withoutBinaryParts(message) {
1491
+ const reference = (value) => {
1492
+ if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
1493
+ const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
1494
+ return {
1495
+ binary: true,
1496
+ sha256: createHash("sha256").update(bytes).digest("hex"),
1497
+ bytes: bytes.byteLength
1498
+ };
1499
+ }
1500
+ if (value instanceof URL)
1501
+ return value.toString();
1502
+ if (Array.isArray(value))
1503
+ return value.map(reference);
1504
+ if (value && typeof value === "object") {
1505
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
1506
+ key,
1507
+ reference(entry)
1508
+ ]));
1509
+ }
1510
+ return value;
1511
+ };
1512
+ return reference(message);
1513
+ }
1207
1514
  function createRunExecutor(dependencies) {
1208
1515
  const {
1209
1516
  config,
@@ -1301,9 +1608,15 @@ function createRunExecutor(dependencies) {
1301
1608
  const absorbed = new Map;
1302
1609
  let eventsSinceCheckpoint = 0;
1303
1610
  let sequence = 0;
1611
+ const transientSequence = () => {
1612
+ sequence += 1;
1613
+ return sequence;
1614
+ };
1304
1615
  let terminalReason = "success";
1305
1616
  let nonModelUsage = input.acceptedRun.usage;
1306
1617
  let modelUsage;
1618
+ let abandonedUsage;
1619
+ const billedUsage = () => abandonedUsage && modelUsage ? addUsage(abandonedUsage, modelUsage) : abandonedUsage ?? modelUsage;
1307
1620
  let usage = nonModelUsage;
1308
1621
  let sawProviderFinish = false;
1309
1622
  let step = 0;
@@ -1314,9 +1627,11 @@ function createRunExecutor(dependencies) {
1314
1627
  let terminalPolicyName;
1315
1628
  const idleDeadline = createIdleDeadline(input.signal, idleTimeoutMs);
1316
1629
  const executionSignal = idleDeadline.signal;
1630
+ const serialize = createRunMutationQueue();
1317
1631
  const operationLifecycle = createAgentRunOperationLifecycle({
1318
1632
  store: config.store,
1319
1633
  runtimeEpoch,
1634
+ serialize,
1320
1635
  currentRun: () => run,
1321
1636
  acceptSnapshot: (next) => {
1322
1637
  snapshot = next;
@@ -1347,7 +1662,7 @@ function createRunExecutor(dependencies) {
1347
1662
  ...provider && { provider }
1348
1663
  }));
1349
1664
  };
1350
- const checkpoint = async () => {
1665
+ const checkpoint = () => serialize(async () => {
1351
1666
  assistant = AgentMessageSchema.parse({
1352
1667
  ...assistant,
1353
1668
  parts,
@@ -1380,7 +1695,7 @@ function createRunExecutor(dependencies) {
1380
1695
  metrics: checkpointMetrics,
1381
1696
  emittedAt: now().toISOString()
1382
1697
  });
1383
- };
1698
+ });
1384
1699
  try {
1385
1700
  if (config.history?.compact) {
1386
1701
  await operationLifecycle.startCompaction(generateId());
@@ -1409,7 +1724,8 @@ function createRunExecutor(dependencies) {
1409
1724
  }
1410
1725
  if (compacted.usage) {
1411
1726
  nonModelUsage = addUsage(nonModelUsage, compacted.usage);
1412
- usage = modelUsage ? addUsage(nonModelUsage, modelUsage) : nonModelUsage;
1727
+ const billed = billedUsage();
1728
+ usage = billed ? addUsage(nonModelUsage, billed) : nonModelUsage;
1413
1729
  }
1414
1730
  }
1415
1731
  const assertCurrent = async () => {
@@ -1473,6 +1789,18 @@ function createRunExecutor(dependencies) {
1473
1789
  throw new AgentContextOverflowError("Agent context still exceeds the model budget after compaction");
1474
1790
  }
1475
1791
  let carriedSystem = [];
1792
+ let durableSystem = [];
1793
+ if (config.stateSlots && config.stateSlots.length > 0) {
1794
+ const slotValues = await createAgentStateSlotStore({
1795
+ store: config.store,
1796
+ definitions: config.stateSlots
1797
+ }).list(run.conversationId);
1798
+ const renderedSlots = renderAgentStateSlots(slotValues);
1799
+ if (renderedSlots) {
1800
+ durableSystem = [renderedSlots];
1801
+ carriedSystem = durableSystem;
1802
+ }
1803
+ }
1476
1804
  const projectHistory = async (source) => {
1477
1805
  if (config.history?.project)
1478
1806
  return config.history.project(source.messages);
@@ -1493,7 +1821,7 @@ function createRunExecutor(dependencies) {
1493
1821
  throw new Error(`Invalid approval continuation ${decision.messageId}: ${decision.reason}`);
1494
1822
  }
1495
1823
  }
1496
- carriedSystem = detailed.system;
1824
+ carriedSystem = [...durableSystem, ...detailed.system];
1497
1825
  return [...detailed.messages];
1498
1826
  };
1499
1827
  const projectInputs = async (source) => {
@@ -1540,10 +1868,12 @@ function createRunExecutor(dependencies) {
1540
1868
  return stopped;
1541
1869
  });
1542
1870
  }
1871
+ let takenThisAttempt = [];
1543
1872
  const takeInjectedMessages = async () => {
1544
1873
  const taken = injection?.take(input.key, run.id) ?? [];
1545
1874
  if (taken.length === 0)
1546
1875
  return [];
1876
+ takenThisAttempt.push(...taken);
1547
1877
  const messages = [];
1548
1878
  for (const entry of taken) {
1549
1879
  messages.push(...await projectInputs([entry.input]));
@@ -1554,324 +1884,456 @@ function createRunExecutor(dependencies) {
1554
1884
  return messages;
1555
1885
  };
1556
1886
  const fallbackModel = selectedModel.model;
1557
- const result = streamAgentTextBoundary({
1558
- model: fallbackModel,
1559
- tools,
1560
- instructions: withCarriedSystem(prompt.instructions),
1561
- messages: history,
1562
- abortSignal: executionSignal,
1563
- maxRetries: 0,
1564
- stopWhen: stopConditions,
1565
- onLanguageModelCallStart: (event) => {
1566
- operationLifecycle.noteProviderCall(event.callId);
1567
- },
1568
- repairToolCall: deferredToolRepair(config.loop?.prepareStep),
1569
- ...config.loop?.toolApproval && {
1570
- toolApproval: config.loop.toolApproval,
1571
- runtimeContext: input.context
1572
- },
1573
- ...config.loop?.toolApprovalSecret && {
1574
- experimental_toolApprovalSecret: config.loop.toolApprovalSecret
1575
- },
1576
- prepareStep: async (options) => {
1577
- const previousStep = options.steps.at(-1);
1578
- if (previousStep) {
1579
- lastPromptTokens = selectedModel?.normalizeUsage?.({
1580
- usage: previousStep.usage,
1581
- providerMetadata: previousStep.providerMetadata
1582
- })?.inputTokens ?? normalizeSdkUsage(previousStep.usage).inputTokens;
1583
- }
1584
- const prepared = await config.loop?.prepareStep?.({
1585
- ...options,
1586
- ...runtimeContext
1587
- });
1588
- const injected = await takeInjectedMessages();
1589
- const preparedStep = injected.length === 0 ? prepared : {
1590
- ...prepared,
1591
- messages: [...prepared?.messages ?? options.messages, ...injected]
1592
- };
1593
- return {
1594
- ...preparedStep,
1595
- model: await operationLifecycle.prepareModel(preparedStep?.model ?? fallbackModel, options.stepNumber, generateId())
1596
- };
1597
- }
1598
- });
1599
- for await (const part of result.stream) {
1600
- idleDeadline.touch();
1601
- eventsSinceCheckpoint += 1;
1602
- sequence += 1;
1603
- if (firstOutputAt === undefined && (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call")) {
1604
- firstOutputAt = performance.now();
1605
- }
1606
- if (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call") {
1607
- await operationLifecycle.firstOutput();
1608
- }
1609
- if (part.type === "text-delta") {
1610
- appendText(parts, part.text);
1611
- await publish({
1612
- type: "assistant-delta",
1613
- conversationId: run.conversationId,
1614
- runId: run.id,
1615
- runtimeEpoch,
1616
- sequence,
1617
- textDelta: part.text,
1618
- emittedAt: now().toISOString()
1619
- });
1620
- } else if (part.type === "reasoning-start") {
1621
- reasoningPartIndex = undefined;
1622
- updateReasoning("", part.providerMetadata);
1623
- const provider = providerEnvelope(part.providerMetadata);
1624
- await publish({
1625
- type: "reasoning-start",
1887
+ const requestModelId = selectedModel.descriptor.modelId;
1888
+ let retryAttempt = 1;
1889
+ let instructionsOverride;
1890
+ const knownMessageShas = new Set;
1891
+ {
1892
+ let fromSeq;
1893
+ do {
1894
+ const page = await config.store.readEvents({
1626
1895
  conversationId: run.conversationId,
1627
- runId: run.id,
1628
- runtimeEpoch,
1629
- sequence,
1630
- ...provider && { provider },
1631
- emittedAt: now().toISOString()
1632
- });
1633
- } else if (part.type === "reasoning-delta") {
1634
- updateReasoning(part.text, part.providerMetadata);
1635
- const provider = providerEnvelope(part.providerMetadata);
1636
- await publish({
1637
- type: "reasoning-delta",
1638
- conversationId: run.conversationId,
1639
- runId: run.id,
1640
- runtimeEpoch,
1641
- sequence,
1642
- textDelta: part.text,
1643
- ...provider && { provider },
1644
- emittedAt: now().toISOString()
1645
- });
1646
- } else if (part.type === "reasoning-end") {
1647
- updateReasoning("", part.providerMetadata);
1648
- reasoningPartIndex = undefined;
1649
- const provider = providerEnvelope(part.providerMetadata);
1650
- await publish({
1651
- type: "reasoning-end",
1652
- conversationId: run.conversationId,
1653
- runId: run.id,
1654
- runtimeEpoch,
1655
- sequence,
1656
- ...provider && { provider },
1657
- emittedAt: now().toISOString()
1896
+ ...fromSeq && { fromSeq },
1897
+ limit: 1e4
1658
1898
  });
1659
- } else if (part.type === "tool-call") {
1660
- const provider = providerEnvelope(part.providerMetadata);
1661
- parts.push(AgentMessagePartSchema.parse({
1662
- type: "tool-call",
1663
- callId: part.toolCallId,
1664
- toolName: part.toolName,
1665
- input: jsonValue(part.input),
1666
- ...provider && { provider }
1667
- }));
1668
- await publish({
1669
- type: "tool-status",
1899
+ for (const event of page.items) {
1900
+ if (event.kind !== "provider/message")
1901
+ continue;
1902
+ const sha = event.payload?.sha256;
1903
+ if (typeof sha === "string")
1904
+ knownMessageShas.add(sha);
1905
+ }
1906
+ fromSeq = page.nextSeq;
1907
+ } while (fromSeq !== undefined);
1908
+ }
1909
+ const recordProviderRequest = async (request) => {
1910
+ const messageShas = [];
1911
+ for (const message of request.messages) {
1912
+ const body = canonicalAgentJson(JSON.parse(JSON.stringify(withoutBinaryParts(message))));
1913
+ const sha256 = createHash("sha256").update(body).digest("hex");
1914
+ messageShas.push(sha256);
1915
+ if (knownMessageShas.has(sha256))
1916
+ continue;
1917
+ await config.store.appendEvent({
1670
1918
  conversationId: run.conversationId,
1671
- runId: run.id,
1672
- runtimeEpoch,
1673
- sequence,
1674
- callId: part.toolCallId,
1675
- toolName: part.toolName,
1676
- status: "started",
1677
- input: jsonValue(part.input),
1678
- emittedAt: now().toISOString()
1919
+ kind: "provider/message",
1920
+ payload: { sha256, message: JSON.parse(body) }
1679
1921
  });
1680
- } else if (part.type === "tool-result") {
1681
- parts.push(AgentMessagePartSchema.parse({
1682
- type: "tool-result",
1683
- callId: part.toolCallId,
1684
- toolName: part.toolName,
1685
- outcome: "success",
1686
- output: jsonValue(part.output)
1687
- }));
1688
- await publish({
1689
- type: "tool-status",
1690
- conversationId: run.conversationId,
1922
+ knownMessageShas.add(sha256);
1923
+ }
1924
+ const instructionsSha256 = createHash("sha256").update(typeof request.instructions === "string" ? request.instructions : canonicalAgentJson(JSON.parse(JSON.stringify(withoutBinaryParts(request.instructions))))).digest("hex");
1925
+ const bodySha256 = createHash("sha256").update(instructionsSha256).update(messageShas.join(",")).digest("hex");
1926
+ await config.store.appendEvent({
1927
+ conversationId: run.conversationId,
1928
+ kind: "provider/request",
1929
+ payload: {
1691
1930
  runId: run.id,
1692
- runtimeEpoch,
1693
- sequence,
1694
- callId: part.toolCallId,
1695
- toolName: part.toolName,
1696
- status: "completed",
1697
- output: jsonValue(part.output),
1698
- emittedAt: now().toISOString()
1699
- });
1700
- } else if (part.type === "tool-error") {
1701
- internalCause = part.error;
1702
- if (isToolExecutionControlError(part.error)) {
1931
+ attempt: request.attempt,
1932
+ stepNumber: request.stepNumber,
1933
+ modelId: requestModelId,
1934
+ instructionsSha256,
1935
+ messageShas,
1936
+ bodySha256
1937
+ }
1938
+ });
1939
+ };
1940
+ for (;; ) {
1941
+ const attemptPartStart = parts.length;
1942
+ let retrying = false;
1943
+ let draining = false;
1944
+ const result = streamAgentTextBoundary({
1945
+ model: fallbackModel,
1946
+ tools,
1947
+ instructions: withCarriedSystem(prompt.instructions),
1948
+ messages: history,
1949
+ abortSignal: executionSignal,
1950
+ maxRetries: 0,
1951
+ stopWhen: stopConditions,
1952
+ onLanguageModelCallStart: (event) => {
1953
+ operationLifecycle.noteProviderCall(event.callId);
1954
+ },
1955
+ repairToolCall: deferredToolRepair(config.loop?.prepareStep),
1956
+ ...config.loop?.toolApproval && {
1957
+ toolApproval: config.loop.toolApproval,
1958
+ runtimeContext: input.context
1959
+ },
1960
+ ...config.loop?.toolApprovalSecret && {
1961
+ experimental_toolApprovalSecret: config.loop.toolApprovalSecret
1962
+ },
1963
+ prepareStep: async (options) => {
1964
+ const previousStep = options.steps.at(-1);
1965
+ if (previousStep) {
1966
+ lastPromptTokens = selectedModel?.normalizeUsage?.({
1967
+ usage: previousStep.usage,
1968
+ providerMetadata: previousStep.providerMetadata
1969
+ })?.inputTokens ?? normalizeSdkUsage(previousStep.usage).inputTokens;
1970
+ }
1971
+ const prepared = await config.loop?.prepareStep?.({
1972
+ ...options,
1973
+ ...runtimeContext
1974
+ });
1975
+ const injected = await takeInjectedMessages();
1976
+ const preparedStep = injected.length === 0 ? prepared : {
1977
+ ...prepared,
1978
+ messages: [...prepared?.messages ?? options.messages, ...injected]
1979
+ };
1980
+ const providerMessages = preparedStep?.messages ?? options.messages;
1981
+ const model = await operationLifecycle.prepareModel(preparedStep?.model ?? fallbackModel, options.stepNumber, generateId());
1982
+ const override = preparedStep;
1983
+ if (override?.instructions !== undefined)
1984
+ instructionsOverride = override.instructions;
1985
+ else if (override?.system !== undefined)
1986
+ instructionsOverride = override.system;
1987
+ await recordProviderRequest({
1988
+ stepNumber: options.stepNumber,
1989
+ attempt: retryAttempt,
1990
+ instructions: instructionsOverride ?? withCarriedSystem(prompt.instructions),
1991
+ messages: providerMessages
1992
+ });
1993
+ return { ...preparedStep, model };
1994
+ }
1995
+ });
1996
+ for await (const part of result.stream) {
1997
+ idleDeadline.touch();
1998
+ if (draining) {
1999
+ if (part.type === "finish-step") {
2000
+ const failedStepUsage = selectedModel.normalizeUsage?.({
2001
+ usage: part.usage,
2002
+ providerMetadata: part.providerMetadata
2003
+ }) ?? normalizeSdkUsage(part.usage);
2004
+ abandonedUsage = abandonedUsage ? addUsage(abandonedUsage, failedStepUsage) : failedStepUsage;
2005
+ const billed = billedUsage();
2006
+ usage = nonModelUsage && billed ? addUsage(nonModelUsage, billed) : billed ?? nonModelUsage;
2007
+ }
2008
+ continue;
2009
+ }
2010
+ eventsSinceCheckpoint += 1;
2011
+ if (part.type === "error" && config.loop?.retry && failureThisRuntimeOwns(part.error) === undefined) {
2012
+ const invokedTool = parts.slice(attemptPartStart).some((candidate) => candidate.type === "tool-call");
2013
+ const decision = invokedTool ? { retry: false, delayMs: 0 } : await recordAgentRetryDecision({
2014
+ store: config.store,
2015
+ conversationId: run.conversationId,
2016
+ attempt: retryAttempt,
2017
+ failure: classifyProviderFailure(part.error),
2018
+ policy: config.loop.retry
2019
+ });
2020
+ if (decision.retry) {
2021
+ await operationLifecycle.finish("failed");
2022
+ parts.splice(attemptPartStart);
2023
+ for (const entry of takenThisAttempt) {
2024
+ injection?.offer(input.key, entry);
2025
+ const ids = absorbed.get(entry.runId);
2026
+ if (ids) {
2027
+ const kept = ids.filter((id) => id !== entry.input.id);
2028
+ if (kept.length === 0)
2029
+ absorbed.delete(entry.runId);
2030
+ else
2031
+ absorbed.set(entry.runId, kept);
2032
+ }
2033
+ }
2034
+ takenThisAttempt = [];
2035
+ await checkpoint();
2036
+ eventsSinceCheckpoint = 0;
2037
+ if (decision.delayMs > 0) {
2038
+ await delay(decision.delayMs, undefined, { signal: executionSignal });
2039
+ }
2040
+ retryAttempt += 1;
2041
+ await config.store.appendEvent({
2042
+ conversationId: run.conversationId,
2043
+ kind: "retry/started",
2044
+ payload: { attempt: retryAttempt }
2045
+ });
2046
+ retrying = true;
2047
+ draining = true;
2048
+ continue;
2049
+ }
2050
+ }
2051
+ if (firstOutputAt === undefined && (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call")) {
2052
+ firstOutputAt = performance.now();
2053
+ }
2054
+ if (part.type === "text-delta" && part.text.length > 0 || part.type === "reasoning-delta" && part.text.length > 0 || part.type === "tool-input-delta" && part.delta.length > 0 || part.type === "tool-call") {
2055
+ await operationLifecycle.firstOutput();
2056
+ }
2057
+ if (part.type === "text-delta") {
2058
+ appendText(parts, part.text);
2059
+ await publish({
2060
+ type: "assistant-delta",
2061
+ conversationId: run.conversationId,
2062
+ runId: run.id,
2063
+ runtimeEpoch,
2064
+ sequence: transientSequence(),
2065
+ textDelta: part.text,
2066
+ emittedAt: now().toISOString()
2067
+ });
2068
+ } else if (part.type === "reasoning-start") {
2069
+ reasoningPartIndex = undefined;
2070
+ updateReasoning("", part.providerMetadata);
2071
+ const provider = providerEnvelope(part.providerMetadata);
2072
+ await publish({
2073
+ type: "reasoning-start",
2074
+ conversationId: run.conversationId,
2075
+ runId: run.id,
2076
+ runtimeEpoch,
2077
+ sequence: transientSequence(),
2078
+ ...provider && { provider },
2079
+ emittedAt: now().toISOString()
2080
+ });
2081
+ } else if (part.type === "reasoning-delta") {
2082
+ updateReasoning(part.text, part.providerMetadata);
2083
+ const provider = providerEnvelope(part.providerMetadata);
2084
+ await publish({
2085
+ type: "reasoning-delta",
2086
+ conversationId: run.conversationId,
2087
+ runId: run.id,
2088
+ runtimeEpoch,
2089
+ sequence: transientSequence(),
2090
+ textDelta: part.text,
2091
+ ...provider && { provider },
2092
+ emittedAt: now().toISOString()
2093
+ });
2094
+ } else if (part.type === "reasoning-end") {
2095
+ updateReasoning("", part.providerMetadata);
2096
+ reasoningPartIndex = undefined;
2097
+ const provider = providerEnvelope(part.providerMetadata);
2098
+ await publish({
2099
+ type: "reasoning-end",
2100
+ conversationId: run.conversationId,
2101
+ runId: run.id,
2102
+ runtimeEpoch,
2103
+ sequence: transientSequence(),
2104
+ ...provider && { provider },
2105
+ emittedAt: now().toISOString()
2106
+ });
2107
+ } else if (part.type === "tool-call") {
2108
+ const provider = providerEnvelope(part.providerMetadata);
2109
+ parts.push(AgentMessagePartSchema.parse({
2110
+ type: "tool-call",
2111
+ callId: part.toolCallId,
2112
+ toolName: part.toolName,
2113
+ input: jsonValue(part.input),
2114
+ ...provider && { provider }
2115
+ }));
2116
+ await publish({
2117
+ type: "tool-status",
2118
+ conversationId: run.conversationId,
2119
+ runId: run.id,
2120
+ runtimeEpoch,
2121
+ sequence: transientSequence(),
2122
+ callId: part.toolCallId,
2123
+ toolName: part.toolName,
2124
+ status: "started",
2125
+ input: jsonValue(part.input),
2126
+ emittedAt: now().toISOString()
2127
+ });
2128
+ } else if (part.type === "tool-result") {
2129
+ parts.push(AgentMessagePartSchema.parse({
2130
+ type: "tool-result",
2131
+ callId: part.toolCallId,
2132
+ toolName: part.toolName,
2133
+ outcome: "success",
2134
+ output: jsonValue(part.output)
2135
+ }));
1703
2136
  await publish({
1704
2137
  type: "tool-status",
1705
2138
  conversationId: run.conversationId,
1706
2139
  runId: run.id,
1707
2140
  runtimeEpoch,
1708
- sequence,
2141
+ sequence: transientSequence(),
2142
+ callId: part.toolCallId,
2143
+ toolName: part.toolName,
2144
+ status: "completed",
2145
+ output: jsonValue(part.output),
2146
+ emittedAt: now().toISOString()
2147
+ });
2148
+ } else if (part.type === "tool-error") {
2149
+ internalCause = part.error;
2150
+ if (isToolExecutionControlError(part.error)) {
2151
+ await publish({
2152
+ type: "tool-status",
2153
+ conversationId: run.conversationId,
2154
+ runId: run.id,
2155
+ runtimeEpoch,
2156
+ sequence: transientSequence(),
2157
+ callId: part.toolCallId,
2158
+ toolName: part.toolName,
2159
+ status: "interrupted",
2160
+ emittedAt: now().toISOString()
2161
+ });
2162
+ await checkpoint();
2163
+ throw part.error;
2164
+ }
2165
+ const output = isAgentToolError(part.error) ? jsonValue(part.error.output) : { message: "Tool execution failed" };
2166
+ parts.push(AgentMessagePartSchema.parse({
2167
+ type: "tool-result",
1709
2168
  callId: part.toolCallId,
1710
2169
  toolName: part.toolName,
1711
- status: "interrupted",
2170
+ outcome: "error",
2171
+ output
2172
+ }));
2173
+ await publish({
2174
+ type: "tool-status",
2175
+ conversationId: run.conversationId,
2176
+ runId: run.id,
2177
+ runtimeEpoch,
2178
+ sequence: transientSequence(),
2179
+ callId: part.toolCallId,
2180
+ toolName: part.toolName,
2181
+ status: "failed",
2182
+ output,
2183
+ emittedAt: now().toISOString()
2184
+ });
2185
+ } else if (part.type === "tool-output-denied") {
2186
+ parts.push(AgentMessagePartSchema.parse({
2187
+ type: "tool-result",
2188
+ callId: part.toolCallId,
2189
+ toolName: part.toolName,
2190
+ outcome: "error",
2191
+ output: { message: "Tool output denied" }
2192
+ }));
2193
+ await publish({
2194
+ type: "tool-status",
2195
+ conversationId: run.conversationId,
2196
+ runId: run.id,
2197
+ runtimeEpoch,
2198
+ sequence: transientSequence(),
2199
+ callId: part.toolCallId,
2200
+ toolName: part.toolName,
2201
+ status: "failed",
2202
+ output: { message: "Tool output denied" },
2203
+ emittedAt: now().toISOString()
2204
+ });
2205
+ } else if (part.type === "source") {
2206
+ parts.push(AgentMessagePartSchema.parse({
2207
+ type: "source",
2208
+ sourceId: part.id,
2209
+ ...part.sourceType === "url" && { url: part.url },
2210
+ ...part.title && { title: part.title }
2211
+ }));
2212
+ } else if (part.type === "file" && config.persistGeneratedFile) {
2213
+ const persisted = await config.persistGeneratedFile(part.file);
2214
+ parts.push(AgentMessagePartSchema.parse({
2215
+ type: "file",
2216
+ mediaType: part.file.mediaType,
2217
+ reference: persisted.reference,
2218
+ ...persisted.filename && { filename: persisted.filename }
2219
+ }));
2220
+ } else if (part.type === "file") {
2221
+ throw new Error("persistGeneratedFile is required for generated file output");
2222
+ } else if (part.type === "reasoning-file" && config.persistGeneratedFile) {
2223
+ const persisted = await config.persistGeneratedFile(part.file);
2224
+ parts.push(AgentMessagePartSchema.parse({
2225
+ type: "file",
2226
+ mediaType: part.file.mediaType,
2227
+ reference: persisted.reference,
2228
+ ...persisted.filename && { filename: persisted.filename }
2229
+ }));
2230
+ } else if (part.type === "reasoning-file") {
2231
+ throw new Error("persistGeneratedFile is required for generated reasoning files");
2232
+ } else if (part.type === "tool-approval-request") {
2233
+ parts.push(AgentMessagePartSchema.parse({
2234
+ type: "tool-approval-request",
2235
+ approvalId: part.approvalId,
2236
+ callId: part.toolCall.toolCallId,
2237
+ ...part.isAutomatic !== undefined && { isAutomatic: part.isAutomatic },
2238
+ ...part.signature && { signature: part.signature }
2239
+ }));
2240
+ } else if (part.type === "tool-approval-response") {
2241
+ parts.push(AgentMessagePartSchema.parse({
2242
+ type: "tool-approval-response",
2243
+ approvalId: part.approvalId,
2244
+ approved: part.approved,
2245
+ ...part.reason && { reason: part.reason }
2246
+ }));
2247
+ } else if (part.type === "custom") {
2248
+ const provider = providerEnvelope(part.providerMetadata);
2249
+ parts.push(AgentMessagePartSchema.parse({
2250
+ type: "provider",
2251
+ envelope: {
2252
+ schemaVersion: 1,
2253
+ provider: "ai-sdk-custom",
2254
+ data: { kind: part.kind, ...provider && { provider: provider.data } }
2255
+ }
2256
+ }));
2257
+ } else if (part.type === "raw") {
2258
+ parts.push(AgentMessagePartSchema.parse({
2259
+ type: "provider",
2260
+ envelope: {
2261
+ schemaVersion: 1,
2262
+ provider: "ai-sdk-raw",
2263
+ data: { value: jsonValue(part.rawValue) }
2264
+ }
2265
+ }));
2266
+ } else if (part.type === "abort") {
2267
+ terminalReason = "interrupted";
2268
+ } else if (part.type === "error") {
2269
+ terminalReason = failureThisRuntimeOwns(part.error) ?? "provider_failure";
2270
+ internalCause = part.error;
2271
+ } else if (part.type === "finish-step") {
2272
+ await operationLifecycle.finish(part.finishReason === "error" ? "failed" : "completed");
2273
+ const stepTrace = trace ? config.observe?.rootTrace(trace) : undefined;
2274
+ const stepUsage = selectedModel.normalizeUsage?.({
2275
+ usage: part.usage,
2276
+ providerMetadata: part.providerMetadata
2277
+ }) ?? normalizeSdkUsage(part.usage);
2278
+ lastPromptTokens = stepUsage.inputTokens;
2279
+ modelUsage = addUsage(modelUsage, stepUsage);
2280
+ usage = nonModelUsage ? addUsage(nonModelUsage, billedUsage() ?? modelUsage) : billedUsage();
2281
+ config.observe?.emit({
2282
+ schemaVersion: 1,
2283
+ eventId: generateId(),
2284
+ type: "step-finished",
2285
+ conversationId: run.conversationId,
2286
+ runId: run.id,
2287
+ traceId: stepTrace?.traceId ?? trace?.traceId ?? generateId(),
2288
+ spanId: stepTrace?.spanId ?? generateId(),
2289
+ ...stepTrace?.parentSpanId && { parentSpanId: stepTrace.parentSpanId },
2290
+ state: run.state,
2291
+ modelId: selectedModel.descriptor.modelId,
2292
+ step,
2293
+ usage: stepUsage,
1712
2294
  emittedAt: now().toISOString()
1713
2295
  });
2296
+ step += 1;
2297
+ } else if (part.type === "finish" && part.finishReason !== "stop") {
2298
+ if (terminalReason === "success") {
2299
+ terminalReason = part.finishReason === "error" ? "provider_failure" : "provider_stop";
2300
+ internalCause ??= { finishReason: part.finishReason };
2301
+ }
2302
+ }
2303
+ if (part.type === "finish") {
2304
+ sawProviderFinish = true;
2305
+ modelUsage = mergeModelTotals(normalizeSdkUsage(part.totalUsage), modelUsage);
2306
+ usage = nonModelUsage ? addUsage(nonModelUsage, billedUsage() ?? modelUsage) : billedUsage();
2307
+ }
2308
+ const structuralBoundary = [
2309
+ "tool-call",
2310
+ "tool-result",
2311
+ "tool-error",
2312
+ "tool-output-denied",
2313
+ "tool-approval-request",
2314
+ "tool-approval-response",
2315
+ "finish-step"
2316
+ ].includes(part.type);
2317
+ if (structuralBoundary || eventsSinceCheckpoint >= checkpointEveryEvents) {
1714
2318
  await checkpoint();
1715
- throw part.error;
2319
+ eventsSinceCheckpoint = 0;
2320
+ if (part.type === "finish-step")
2321
+ operationLifecycle.checkpointStep(step - 1);
1716
2322
  }
1717
- const output = isAgentToolError(part.error) ? jsonValue(part.error.output) : { message: "Tool execution failed" };
1718
- parts.push(AgentMessagePartSchema.parse({
1719
- type: "tool-result",
1720
- callId: part.toolCallId,
1721
- toolName: part.toolName,
1722
- outcome: "error",
1723
- output
1724
- }));
1725
- await publish({
1726
- type: "tool-status",
1727
- conversationId: run.conversationId,
1728
- runId: run.id,
1729
- runtimeEpoch,
1730
- sequence,
1731
- callId: part.toolCallId,
1732
- toolName: part.toolName,
1733
- status: "failed",
1734
- output,
1735
- emittedAt: now().toISOString()
1736
- });
1737
- } else if (part.type === "tool-output-denied") {
1738
- parts.push(AgentMessagePartSchema.parse({
1739
- type: "tool-result",
1740
- callId: part.toolCallId,
1741
- toolName: part.toolName,
1742
- outcome: "error",
1743
- output: { message: "Tool output denied" }
1744
- }));
2323
+ }
2324
+ if (retrying) {
1745
2325
  await publish({
1746
- type: "tool-status",
2326
+ type: "attempt-reset",
1747
2327
  conversationId: run.conversationId,
1748
2328
  runId: run.id,
1749
2329
  runtimeEpoch,
1750
- sequence,
1751
- callId: part.toolCallId,
1752
- toolName: part.toolName,
1753
- status: "failed",
1754
- output: { message: "Tool output denied" },
2330
+ sequence: transientSequence(),
2331
+ attempt: retryAttempt,
1755
2332
  emittedAt: now().toISOString()
1756
2333
  });
1757
- } else if (part.type === "source") {
1758
- parts.push(AgentMessagePartSchema.parse({
1759
- type: "source",
1760
- sourceId: part.id,
1761
- ...part.sourceType === "url" && { url: part.url },
1762
- ...part.title && { title: part.title }
1763
- }));
1764
- } else if (part.type === "file" && config.persistGeneratedFile) {
1765
- const persisted = await config.persistGeneratedFile(part.file);
1766
- parts.push(AgentMessagePartSchema.parse({
1767
- type: "file",
1768
- mediaType: part.file.mediaType,
1769
- reference: persisted.reference,
1770
- ...persisted.filename && { filename: persisted.filename }
1771
- }));
1772
- } else if (part.type === "file") {
1773
- throw new Error("persistGeneratedFile is required for generated file output");
1774
- } else if (part.type === "reasoning-file" && config.persistGeneratedFile) {
1775
- const persisted = await config.persistGeneratedFile(part.file);
1776
- parts.push(AgentMessagePartSchema.parse({
1777
- type: "file",
1778
- mediaType: part.file.mediaType,
1779
- reference: persisted.reference,
1780
- ...persisted.filename && { filename: persisted.filename }
1781
- }));
1782
- } else if (part.type === "reasoning-file") {
1783
- throw new Error("persistGeneratedFile is required for generated reasoning files");
1784
- } else if (part.type === "tool-approval-request") {
1785
- parts.push(AgentMessagePartSchema.parse({
1786
- type: "tool-approval-request",
1787
- approvalId: part.approvalId,
1788
- callId: part.toolCall.toolCallId,
1789
- ...part.isAutomatic !== undefined && { isAutomatic: part.isAutomatic },
1790
- ...part.signature && { signature: part.signature }
1791
- }));
1792
- } else if (part.type === "tool-approval-response") {
1793
- parts.push(AgentMessagePartSchema.parse({
1794
- type: "tool-approval-response",
1795
- approvalId: part.approvalId,
1796
- approved: part.approved,
1797
- ...part.reason && { reason: part.reason }
1798
- }));
1799
- } else if (part.type === "custom") {
1800
- const provider = providerEnvelope(part.providerMetadata);
1801
- parts.push(AgentMessagePartSchema.parse({
1802
- type: "provider",
1803
- envelope: {
1804
- schemaVersion: 1,
1805
- provider: "ai-sdk-custom",
1806
- data: { kind: part.kind, ...provider && { provider: provider.data } }
1807
- }
1808
- }));
1809
- } else if (part.type === "raw") {
1810
- parts.push(AgentMessagePartSchema.parse({
1811
- type: "provider",
1812
- envelope: {
1813
- schemaVersion: 1,
1814
- provider: "ai-sdk-raw",
1815
- data: { value: jsonValue(part.rawValue) }
1816
- }
1817
- }));
1818
- } else if (part.type === "abort") {
1819
- terminalReason = "interrupted";
1820
- } else if (part.type === "error") {
1821
- terminalReason = part.error instanceof AgentContextOverflowError ? "context_overflow" : "provider_failure";
1822
- internalCause = part.error;
1823
- } else if (part.type === "finish-step") {
1824
- await operationLifecycle.finish(part.finishReason === "error" ? "failed" : "completed");
1825
- const stepTrace = trace ? config.observe?.rootTrace(trace) : undefined;
1826
- const stepUsage = selectedModel.normalizeUsage?.({
1827
- usage: part.usage,
1828
- providerMetadata: part.providerMetadata
1829
- }) ?? normalizeSdkUsage(part.usage);
1830
- lastPromptTokens = stepUsage.inputTokens;
1831
- modelUsage = addUsage(modelUsage, stepUsage);
1832
- usage = nonModelUsage ? addUsage(nonModelUsage, modelUsage) : modelUsage;
1833
- config.observe?.emit({
1834
- schemaVersion: 1,
1835
- eventId: generateId(),
1836
- type: "step-finished",
1837
- conversationId: run.conversationId,
1838
- runId: run.id,
1839
- traceId: stepTrace?.traceId ?? trace?.traceId ?? generateId(),
1840
- spanId: stepTrace?.spanId ?? generateId(),
1841
- ...stepTrace?.parentSpanId && { parentSpanId: stepTrace.parentSpanId },
1842
- state: run.state,
1843
- modelId: selectedModel.descriptor.modelId,
1844
- step,
1845
- usage: stepUsage,
1846
- emittedAt: now().toISOString()
1847
- });
1848
- step += 1;
1849
- } else if (part.type === "finish" && part.finishReason !== "stop") {
1850
- if (terminalReason === "success") {
1851
- terminalReason = part.finishReason === "error" ? "provider_failure" : "provider_stop";
1852
- internalCause ??= { finishReason: part.finishReason };
1853
- }
1854
- }
1855
- if (part.type === "finish") {
1856
- sawProviderFinish = true;
1857
- modelUsage = mergeModelTotals(normalizeSdkUsage(part.totalUsage), modelUsage);
1858
- usage = nonModelUsage ? addUsage(nonModelUsage, modelUsage) : modelUsage;
1859
- }
1860
- const structuralBoundary = [
1861
- "tool-call",
1862
- "tool-result",
1863
- "tool-error",
1864
- "tool-output-denied",
1865
- "tool-approval-request",
1866
- "tool-approval-response",
1867
- "finish-step"
1868
- ].includes(part.type);
1869
- if (structuralBoundary || eventsSinceCheckpoint >= checkpointEveryEvents) {
1870
- await checkpoint();
1871
- eventsSinceCheckpoint = 0;
1872
- if (part.type === "finish-step")
1873
- operationLifecycle.checkpointStep(step - 1);
2334
+ continue;
1874
2335
  }
2336
+ break;
1875
2337
  }
1876
2338
  await operationLifecycle.finish(executionSignal.aborted ? "cancelled" : terminalReason === "provider_failure" ? "failed" : "completed");
1877
2339
  if (terminalPolicyName !== undefined)
@@ -1891,7 +2353,7 @@ function createRunExecutor(dependencies) {
1891
2353
  reason: terminalReason,
1892
2354
  ...terminalPolicyName && { policyName: terminalPolicyName }
1893
2355
  })) {
1894
- terminalReason = "provider_failure";
2356
+ terminalReason = "output_rejected";
1895
2357
  internalCause = new Error("Agent terminal output was rejected by the protocol");
1896
2358
  }
1897
2359
  }
@@ -1918,8 +2380,12 @@ function createRunExecutor(dependencies) {
1918
2380
  type: "control",
1919
2381
  reason: isToolExecutionControlError(error) && error.reason === "stale_run" ? "stale-run" : "run-interrupted"
1920
2382
  }));
2383
+ } else if (error instanceof AgentContextOverflowError) {
2384
+ terminalReason = "context_overflow";
2385
+ } else if (error instanceof AgentRuntimeConflictError) {
2386
+ terminalReason = "storage_conflict";
1921
2387
  } else {
1922
- terminalReason = error instanceof AgentContextOverflowError ? "context_overflow" : "provider_failure";
2388
+ terminalReason = failureThisRuntimeOwns(error) ?? (hasProviderOrigin(error) ? "provider_failure" : "runtime_failure");
1923
2389
  }
1924
2390
  } finally {
1925
2391
  idleDeadline.dispose();
@@ -1991,6 +2457,13 @@ function createRunExecutor(dependencies) {
1991
2457
  usage: spent,
1992
2458
  ...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
1993
2459
  } : undefined;
2460
+ if (config.children && terminal.committedByCaller && (terminalReason === "interrupted" || terminalReason === "cancelled" || terminalReason === "timeout" || terminalReason === "shutdown")) {
2461
+ try {
2462
+ await config.children.stopChildren(run.conversationId);
2463
+ } catch (error) {
2464
+ internalCause ??= error;
2465
+ }
2466
+ }
1994
2467
  emitSpend({
1995
2468
  eventId: terminal.committedByCaller ? agentDurableEventId("terminal", run.id, observedVersion) : unsettledEventId(observedVersion),
1996
2469
  state: run.state,
@@ -2623,4 +3096,4 @@ function createAgentRuntime(config) {
2623
3096
  };
2624
3097
  }
2625
3098
 
2626
- export { AgentContextOverflowError, createAgentSessionCoordinator, registerDeferredToolRepair, registerDeferredToolCatalog, deferredToolCatalog, repairedSearchCall, createAgentRuntimeEventSink, projectAgentHistoryDetailed, projectAgentHistory, createAgentToolFenceLifecycle, AgentTokenCountSchema, selectAgentHistory, composeAgentPrompt, AgentRuntimeConflictError, createAgentRuntime };
3099
+ export { AgentRuntimeConflictError, normalizeSdkUsage, AgentContextOverflowError, createAgentSessionCoordinator, registerDeferredToolRepair, registerDeferredToolCatalog, deferredToolCatalog, repairedSearchCall, createAgentRuntimeEventSink, projectAgentHistoryDetailed, projectAgentHistory, createAgentToolFenceLifecycle, AgentTokenCountSchema, selectAgentHistory, composeAgentPrompt, AgentProviderStreamCutError, classifyProviderFailure, isToolResultFailure, recordAgentRetryDecision, defineStateSlot, createAgentStateSlotStore, renderAgentStateSlots, agentGoalStateSlot, agentTodoStateSlot, createAgentRuntime };