maka-agent 0.2.0-dev.30.20260912 → 0.2.0-dev.31.20260913

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 (194) hide show
  1. package/THIRD_PARTY_NOTICES.txt +392 -0
  2. package/dist/acp/maka-acp-agent.js +8 -2
  3. package/dist/acp/prompt-content.js +225 -0
  4. package/dist/acp/session-event-mapper.js +103 -0
  5. package/dist/acp/session-registry.js +654 -17
  6. package/dist/acp/stdio-server.js +10 -1
  7. package/dist/cli-core.js +1 -1
  8. package/dist/pi-transcript-tools.js +3 -2
  9. package/dist/pi-transcript.js +12 -2
  10. package/dist/runtime-host-session-channel.js +63 -9
  11. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  12. package/node_modules/@maka/core/dist/artifacts.js +6 -0
  13. package/node_modules/@maka/core/dist/attachments.js +7 -2
  14. package/node_modules/@maka/core/dist/durable-tool-result-projection.js +3 -1
  15. package/node_modules/@maka/core/dist/events.js +21 -0
  16. package/node_modules/@maka/core/dist/goal.js +44 -2
  17. package/node_modules/@maka/core/dist/permission.js +0 -1
  18. package/node_modules/@maka/core/dist/runtime-boundary.js +73 -29
  19. package/node_modules/@maka/core/dist/runtime-event.js +6 -3
  20. package/node_modules/@maka/core/dist/runtime-handoff.js +10 -2
  21. package/node_modules/@maka/core/dist/runtime-logical-execution.js +39 -14
  22. package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +3 -1
  23. package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
  24. package/node_modules/@maka/core/dist/unified-diff.js +33 -26
  25. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +49 -178
  26. package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +7 -2
  27. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -53
  28. package/node_modules/@maka/runtime/dist/bash-model-output.js +7 -1
  29. package/node_modules/@maka/runtime/dist/bash-tail-buffer.js +13 -52
  30. package/node_modules/@maka/runtime/dist/builtin-tools.js +59 -111
  31. package/node_modules/@maka/runtime/dist/child-fd-input.js +3 -2
  32. package/node_modules/@maka/runtime/dist/context-budget-policy.js +1 -11
  33. package/node_modules/@maka/runtime/dist/context-budget.js +12 -1
  34. package/node_modules/@maka/runtime/dist/conversation-copy.js +104 -9
  35. package/node_modules/@maka/runtime/dist/filesystem-executor.js +10 -6
  36. package/node_modules/@maka/runtime/dist/filesystem-worker/client.js +77 -19
  37. package/node_modules/@maka/runtime/dist/filesystem-worker/launch-spec.js +66 -55
  38. package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +34 -97
  39. package/node_modules/@maka/runtime/dist/filesystem-worker/protocol.js +25 -10
  40. package/node_modules/@maka/runtime/dist/filesystem-worker/worker-entry.js +2 -0
  41. package/node_modules/@maka/runtime/dist/glob-search.js +74 -0
  42. package/node_modules/@maka/runtime/dist/goal-continuation.js +78 -8
  43. package/node_modules/@maka/runtime/dist/goal-state.js +7 -0
  44. package/node_modules/@maka/runtime/dist/grep-search.js +155 -0
  45. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +84 -94
  46. package/node_modules/@maka/runtime/dist/model-history.js +25 -3
  47. package/node_modules/@maka/runtime/dist/pipe-tail-collector.js +6 -9
  48. package/node_modules/@maka/runtime/dist/read-page.js +231 -0
  49. package/node_modules/@maka/runtime/dist/request-projection.js +2 -15
  50. package/node_modules/@maka/runtime/dist/ripgrep-executable.js +62 -0
  51. package/node_modules/@maka/runtime/dist/ripgrep-guidance.js +81 -0
  52. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +187 -20
  53. package/node_modules/@maka/runtime/dist/runtime-read-model.js +2 -1
  54. package/node_modules/@maka/runtime/dist/sandbox/linux-profile-path.js +7 -1
  55. package/node_modules/@maka/runtime/dist/sandbox/linux-sandbox.js +20 -5
  56. package/node_modules/@maka/runtime/dist/session-manager.js +6 -6
  57. package/node_modules/@maka/runtime/dist/session-recap.js +9 -5
  58. package/node_modules/@maka/runtime/dist/shell-exec.js +7 -25
  59. package/node_modules/@maka/runtime/dist/shell-run-manager.js +1 -1
  60. package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +1 -1
  61. package/node_modules/@maka/runtime/dist/shell-tools.js +6 -12
  62. package/node_modules/@maka/runtime/dist/tool-availability.js +0 -1
  63. package/node_modules/@maka/runtime/dist/tool-output.js +1 -1
  64. package/node_modules/@maka/runtime/dist/tool-result-archive-capability.js +24 -36
  65. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +32 -527
  66. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +17 -119
  67. package/node_modules/@maka/runtime/dist/tool-result-archive.js +7 -25
  68. package/node_modules/@maka/runtime/dist/tool-runtime.js +7 -5
  69. package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +6933 -3681
  70. package/node_modules/@maka/runtime/dist/workspace-executor.js +20 -24
  71. package/node_modules/@maka/runtime/package.json +2 -0
  72. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +185 -191
  73. package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +3 -0
  74. package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
  75. package/node_modules/@maka/runtime-host/dist/protocol/message.js +2 -2
  76. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  77. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +2 -2
  78. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +10 -2
  79. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +38 -0
  80. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +2 -8
  81. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +2 -0
  82. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +81 -7
  83. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +13 -13
  84. package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +65 -0
  85. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +1 -0
  86. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +5 -2
  87. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +88 -72
  88. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +21 -4
  89. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +145 -1
  90. package/node_modules/@maka/storage/dist/agent-run-store.js +5 -2
  91. package/node_modules/@maka/storage/dist/execution-stores.js +2 -1
  92. package/node_modules/@maka/storage/dist/runtime-transcript-query.js +27 -14
  93. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +69 -3
  94. package/node_modules/balanced-match/LICENSE.md +23 -0
  95. package/node_modules/balanced-match/README.md +57 -0
  96. package/node_modules/balanced-match/dist/commonjs/index.js +59 -0
  97. package/node_modules/balanced-match/dist/commonjs/package.json +3 -0
  98. package/node_modules/balanced-match/dist/esm/index.js +54 -0
  99. package/node_modules/balanced-match/dist/esm/package.json +3 -0
  100. package/node_modules/balanced-match/package.json +68 -0
  101. package/node_modules/brace-expansion/LICENSE +23 -0
  102. package/node_modules/brace-expansion/README.md +105 -0
  103. package/node_modules/brace-expansion/dist/commonjs/index.js +289 -0
  104. package/node_modules/brace-expansion/dist/commonjs/package.json +3 -0
  105. package/node_modules/brace-expansion/dist/esm/index.js +285 -0
  106. package/node_modules/brace-expansion/dist/esm/package.json +3 -0
  107. package/node_modules/brace-expansion/package.json +64 -0
  108. package/node_modules/glob/LICENSE.md +63 -0
  109. package/node_modules/glob/README.md +1203 -0
  110. package/node_modules/glob/dist/commonjs/glob.js +248 -0
  111. package/node_modules/glob/dist/commonjs/has-magic.js +27 -0
  112. package/node_modules/glob/dist/commonjs/ignore.js +119 -0
  113. package/node_modules/glob/dist/commonjs/index.js +68 -0
  114. package/node_modules/glob/dist/commonjs/index.min.js +4 -0
  115. package/node_modules/glob/dist/commonjs/package.json +3 -0
  116. package/node_modules/glob/dist/commonjs/pattern.js +223 -0
  117. package/node_modules/glob/dist/commonjs/processor.js +301 -0
  118. package/node_modules/glob/dist/commonjs/walker.js +387 -0
  119. package/node_modules/glob/dist/esm/glob.js +244 -0
  120. package/node_modules/glob/dist/esm/has-magic.js +23 -0
  121. package/node_modules/glob/dist/esm/ignore.js +115 -0
  122. package/node_modules/glob/dist/esm/index.js +55 -0
  123. package/node_modules/glob/dist/esm/index.min.js +4 -0
  124. package/node_modules/glob/dist/esm/package.json +3 -0
  125. package/node_modules/glob/dist/esm/pattern.js +219 -0
  126. package/node_modules/glob/dist/esm/processor.js +294 -0
  127. package/node_modules/glob/dist/esm/walker.js +381 -0
  128. package/node_modules/glob/package.json +98 -0
  129. package/node_modules/minimatch/LICENSE.md +55 -0
  130. package/node_modules/minimatch/README.md +528 -0
  131. package/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js +14 -0
  132. package/node_modules/minimatch/dist/commonjs/ast.js +845 -0
  133. package/node_modules/minimatch/dist/commonjs/brace-expressions.js +150 -0
  134. package/node_modules/minimatch/dist/commonjs/escape.js +30 -0
  135. package/node_modules/minimatch/dist/commonjs/index.js +1127 -0
  136. package/node_modules/minimatch/dist/commonjs/package.json +3 -0
  137. package/node_modules/minimatch/dist/commonjs/unescape.js +38 -0
  138. package/node_modules/minimatch/dist/esm/assert-valid-pattern.js +10 -0
  139. package/node_modules/minimatch/dist/esm/ast.js +841 -0
  140. package/node_modules/minimatch/dist/esm/brace-expressions.js +146 -0
  141. package/node_modules/minimatch/dist/esm/escape.js +26 -0
  142. package/node_modules/minimatch/dist/esm/index.js +1114 -0
  143. package/node_modules/minimatch/dist/esm/package.json +3 -0
  144. package/node_modules/minimatch/dist/esm/unescape.js +34 -0
  145. package/node_modules/minimatch/package.json +73 -0
  146. package/node_modules/minipass/LICENSE.md +55 -0
  147. package/node_modules/minipass/README.md +825 -0
  148. package/node_modules/minipass/dist/commonjs/index.js +1038 -0
  149. package/node_modules/minipass/dist/commonjs/package.json +3 -0
  150. package/node_modules/minipass/dist/esm/index.js +1020 -0
  151. package/node_modules/minipass/dist/esm/package.json +3 -0
  152. package/node_modules/minipass/package.json +77 -0
  153. package/node_modules/path-scurry/LICENSE.md +55 -0
  154. package/node_modules/path-scurry/README.md +636 -0
  155. package/node_modules/path-scurry/dist/commonjs/index.js +2018 -0
  156. package/node_modules/path-scurry/dist/commonjs/package.json +3 -0
  157. package/node_modules/path-scurry/dist/esm/index.js +1983 -0
  158. package/node_modules/path-scurry/dist/esm/package.json +3 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/LICENSE.md +55 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/README.md +469 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +1726 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +2 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  173. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/package.json +3 -0
  174. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  175. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  176. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  177. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  178. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  179. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  180. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  181. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +1722 -0
  182. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +2 -0
  183. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  184. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  185. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  186. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  187. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/package.json +3 -0
  188. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  189. package/node_modules/path-scurry/node_modules/lru-cache/package.json +154 -0
  190. package/node_modules/path-scurry/package.json +72 -0
  191. package/package.json +1 -1
  192. package/node_modules/@maka/runtime/dist/active-tool-result-prune.js +0 -248
  193. package/node_modules/@maka/runtime/dist/active-tool-result-working-set.js +0 -218
  194. package/node_modules/@maka/runtime/dist/archive-read-tool.js +0 -115
@@ -20,7 +20,8 @@ import { continuationStartEventMatchesClaim, createRuntimeBoundaryCursor, invoca
20
20
  import { readRunInvocation, } from './runtime-event-store.js';
21
21
  import { assertHandoffClaimSource, runtimeHandoffPause, } from './runtime-handoff.js';
22
22
  /** All logical snapshot and command routing paths share these edge checks. */
23
- export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
23
+ export async function readLogicalRuntimeExecution(store, identity, knownRoot, options) {
24
+ const membership = options?.mode === 'membership';
24
25
  const root = knownRoot ?? (await readRunInvocation(store, identity.sessionId, identity.runId));
25
26
  if (!root)
26
27
  return undefined;
@@ -37,17 +38,25 @@ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
37
38
  if (seen.has(tip.runId) || seen.size >= 64)
38
39
  throw new Error('Invalid logical execution handoff lineage');
39
40
  seen.add(tip.runId);
40
- const events = await store.readImmutableRuntimeEvents(identity.sessionId, tip.runId);
41
- const last = events.at(-1);
41
+ const events = membership
42
+ ? undefined
43
+ : await store.readImmutableRuntimeEvents(identity.sessionId, tip.runId);
44
+ const last = membership ? tip.terminalEvent : events?.at(-1);
42
45
  const pause = last && runtimeHandoffPause(last);
43
46
  if (!pause)
44
- return { root, tip, events, runIds: [...seen] };
47
+ return logicalExecutionResult(root, tip, [...seen], events);
45
48
  if (pause.rootRunId !== root.runId)
46
49
  throw new Error('Handoff seal changes the logical root');
47
- const prefix = await store.readImmutableRuntimePrefix({
48
- sessionId: identity.sessionId,
49
- runId: tip.runId,
50
- });
50
+ const prefix = membership
51
+ ? await store.readImmutableRuntimePrefixProof({
52
+ sessionId: identity.sessionId,
53
+ runId: tip.runId,
54
+ })
55
+ : await store.readImmutableRuntimePrefix({
56
+ sessionId: identity.sessionId,
57
+ runId: tip.runId,
58
+ });
59
+ const segment = runtimePrefixSegment(prefix);
51
60
  if (prefix.position.lastEventId !== last.id)
52
61
  throw new Error('Handoff source changed after its seal');
53
62
  if (!segments) {
@@ -59,9 +68,9 @@ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
59
68
  throw new Error('Handoff requires authenticated source lineage');
60
69
  const initial = await store.readContinuationClaimStateByBoundary(source.boundaryDigest);
61
70
  if (!initial ||
62
- initial.startEventId !== prefix.events[0]?.id ||
71
+ initial.startEventId !== prefixFirstEvent(prefix).id ||
63
72
  !invocationMatchesClaimTarget(root, initial.claim) ||
64
- !continuationStartEventMatchesClaim(prefix.events[0], initial.claim, initial.startKind)) {
73
+ !continuationStartEventMatchesClaim(prefixFirstEvent(prefix), initial.claim, initial.startKind)) {
65
74
  throw new Error('Handoff root continuation is not authenticated');
66
75
  }
67
76
  segments = [...initial.claim.boundary.segments];
@@ -69,7 +78,7 @@ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
69
78
  else
70
79
  segments = [];
71
80
  }
72
- segments.push(runtimePrefixSegment(prefix));
81
+ segments.push(segment);
73
82
  const boundary = createRuntimeBoundaryCursor(segments);
74
83
  let state = await store.readContinuationClaimStateByBoundary(boundary.manifestDigest);
75
84
  const next = await readRunInvocation(store, identity.sessionId, pause.successorRunId);
@@ -81,15 +90,21 @@ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
81
90
  if (!state) {
82
91
  if (next)
83
92
  throw new Error('Handoff successor opened without its continuation claim');
84
- return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
93
+ return logicalExecutionResult(root, tip, [...seen], events, pause);
85
94
  }
86
95
  assertHandoffClaimSource(state.claim, prefix);
87
96
  if (!next) {
88
97
  if (state.startEventId)
89
98
  throw new Error('Handoff successor opening is missing');
90
- return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
99
+ return logicalExecutionResult(root, tip, [...seen], events, pause);
91
100
  }
92
- const first = (await store.readImmutableRuntimeEvents(identity.sessionId, next.runId))[0];
101
+ const first = membership
102
+ ? prefixFirstEvent(await store.readImmutableRuntimePrefixProof({
103
+ sessionId: identity.sessionId,
104
+ runId: next.runId,
105
+ upToEventSeq: 1,
106
+ }))
107
+ : (await store.readImmutableRuntimeEvents(identity.sessionId, next.runId))[0];
93
108
  if (state.startEventId !== first?.id ||
94
109
  !invocationMatchesClaimTarget(next, state.claim) ||
95
110
  !continuationStartEventMatchesClaim(first, state.claim, state.startKind)) {
@@ -98,6 +113,16 @@ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
98
113
  tip = next;
99
114
  }
100
115
  }
116
+ function prefixFirstEvent(prefix) {
117
+ const first = prefix.protocol === 'immutable_runtime_prefix_v1' ? prefix.events[0] : prefix.firstEvent;
118
+ if (!first)
119
+ throw new Error('immutable RuntimeEvent prefix is empty');
120
+ return first;
121
+ }
122
+ function logicalExecutionResult(root, tip, runIds, events, pendingHandoff) {
123
+ const result = { root, tip, runIds, ...(pendingHandoff ? { pendingHandoff } : {}) };
124
+ return events ? { ...result, events } : result;
125
+ }
101
126
  /** Resolve a durable physical proof back to its root, then authenticate membership. */
102
127
  export async function readLogicalRuntimeExecutionForRun(store, identity) {
103
128
  const run = await readRunInvocation(store, identity.sessionId, identity.runId);
@@ -16,5 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ import { DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES } from './durable-tool-result-projection.js';
19
20
  export const TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_TRANSITIONS = 64;
20
- export const TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_BYTES = 2 * 1024 * 1024;
21
+ // Include event identity and transition overhead around the stored projection.
22
+ export const TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_BYTES = DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES + 128 * 1024;
@@ -162,7 +162,8 @@ function isNonShellToolResultContent(value) {
162
162
  isFiniteNumber(value.originalEstimatedTokens) &&
163
163
  isFiniteNumber(value.originalBytes) &&
164
164
  isFiniteNumber(value.rewriteVersion) &&
165
- (value.reason === 'stale_tool_result_pruned_before_compact' ||
165
+ (value.reason === 'tool_result_pruned' ||
166
+ value.reason === 'stale_tool_result_pruned_before_compact' ||
166
167
  value.reason === 'active_current_turn_tool_result_pruned_before_next_step'));
167
168
  case 'image':
168
169
  return (hasExactShape(value, IMAGE_SHAPE) &&
@@ -26,39 +26,57 @@ const HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
26
26
  * structure — degrades to unnumbered meta rows.
27
27
  */
28
28
  export function parseUnifiedDiffRows(diff) {
29
- const lines = diff.split('\n');
30
- // A trailing newline terminates the diff rather than starting an empty row.
31
- if (lines.length > 1 && lines[lines.length - 1] === '')
32
- lines.pop();
33
29
  const rows = [];
30
+ scanUnifiedDiff(diff, rows);
31
+ return rows;
32
+ }
33
+ /** Green `+N` / red `-N` counts, without materializing display rows or body text. */
34
+ export function countDiffLineStats(diff) {
35
+ return scanUnifiedDiff(diff);
36
+ }
37
+ /**
38
+ * One structural scan for both consumers. The optional output array keeps
39
+ * display objects and body substrings out of the count-only path.
40
+ */
41
+ function scanUnifiedDiff(diff, rows) {
42
+ let additions = 0;
43
+ let deletions = 0;
34
44
  let oldLine = 0;
35
45
  let newLine = 0;
36
46
  let remainingOld = 0;
37
47
  let remainingNew = 0;
38
48
  let inHunk = false;
39
- for (const line of lines) {
49
+ let offset = 0;
50
+ // Visit an empty input once, but do not invent a row after a trailing newline.
51
+ do {
52
+ const start = offset;
53
+ const newline = diff.indexOf('\n', start);
54
+ const end = newline === -1 ? diff.length : newline;
55
+ offset = end + 1;
40
56
  if (inHunk && remainingOld + remainingNew > 0) {
41
- const marker = line.charAt(0);
57
+ const marker = diff.charAt(start);
42
58
  if (marker === '\\') {
43
59
  // `` annotates the previous row without
44
60
  // consuming a line on either side.
45
- rows.push({ kind: 'meta', text: line });
61
+ rows?.push({ kind: 'meta', text: diff.slice(start, end) });
46
62
  continue;
47
63
  }
48
64
  if (marker === '-') {
49
- rows.push({ kind: 'del', text: line, oldLine });
65
+ rows?.push({ kind: 'del', text: diff.slice(start, end), oldLine });
66
+ deletions += 1;
50
67
  oldLine += 1;
51
68
  remainingOld -= 1;
52
69
  continue;
53
70
  }
54
71
  if (marker === '+') {
55
- rows.push({ kind: 'add', text: line, newLine });
72
+ rows?.push({ kind: 'add', text: diff.slice(start, end), newLine });
73
+ additions += 1;
56
74
  newLine += 1;
57
75
  remainingNew -= 1;
58
76
  continue;
59
77
  }
60
78
  // ' ' context, and the bare empty line some generators emit for one.
61
- rows.push({ kind: 'ctx', text: line, oldLine, newLine });
79
+ rows?.push({ kind: 'ctx', text: diff.slice(start, end), oldLine, newLine });
62
80
  oldLine += 1;
63
81
  newLine += 1;
64
82
  remainingOld -= 1;
@@ -66,6 +84,7 @@ export function parseUnifiedDiffRows(diff) {
66
84
  continue;
67
85
  }
68
86
  inHunk = false;
87
+ const line = diff.slice(start, end);
69
88
  const hunk = HUNK_HEADER.exec(line);
70
89
  if (hunk) {
71
90
  oldLine = Number(hunk[1]);
@@ -73,29 +92,17 @@ export function parseUnifiedDiffRows(diff) {
73
92
  remainingOld = hunk[2] === undefined ? 1 : Number(hunk[2]);
74
93
  remainingNew = hunk[4] === undefined ? 1 : Number(hunk[4]);
75
94
  inHunk = true;
76
- rows.push({ kind: 'hunk', text: line });
95
+ rows?.push({ kind: 'hunk', text: line });
77
96
  continue;
78
97
  }
79
98
  if (line.startsWith('diff ')) {
80
- rows.push({ kind: 'meta', text: line });
99
+ rows?.push({ kind: 'meta', text: line });
81
100
  continue;
82
101
  }
83
102
  if (line.startsWith('--- ') || line.startsWith('+++ ') || line.startsWith('index ')) {
84
103
  continue;
85
104
  }
86
- rows.push({ kind: 'meta', text: line });
87
- }
88
- return rows;
89
- }
90
- /** Green `+N` / red `-N` counts, from the structural parse. */
91
- export function countDiffLineStats(diff) {
92
- let additions = 0;
93
- let deletions = 0;
94
- for (const row of parseUnifiedDiffRows(diff)) {
95
- if (row.kind === 'add')
96
- additions += 1;
97
- else if (row.kind === 'del')
98
- deletions += 1;
99
- }
105
+ rows?.push({ kind: 'meta', text: line });
106
+ } while (offset < diff.length);
100
107
  return { additions, deletions };
101
108
  }
@@ -22,10 +22,9 @@ import { isHistoryCompactContentEvent } from './history-compaction.js';
22
22
  import { canContinueHistoryCompactCheckpointForModel, canReplayHistoryCompactCheckpointForModel, historyCompactSourceDigest, matchHistoryCompactCheckpointPrefix, projectHistoryCompactCheckpointReplay, } from './history-compact-checkpoint.js';
23
23
  import { HistoryCompactSummarizerError, isMalformedHistoryCompactSummaryReason, } from './history-compact-error.js';
24
24
  import { createHash } from 'node:crypto';
25
- import { rewriteActiveToolResultsInMessages, } from './active-tool-result-prune.js';
26
- import { archiveToolResultAsTransition, collectStaleToolResultArchiveCandidates, serializedToolResultProjection, } from './tool-result-archive-transition.js';
25
+ import { archiveToolResultAsTransition, collectToolResultArchiveCandidates, serializedToolResultProjection, } from './tool-result-archive-transition.js';
27
26
  import { estimateTokens } from './context-budget-helpers.js';
28
- import { baseToolResultProjection, reduceEffectiveModelProjections, } from './model-projection-transition-ledger.js';
27
+ import { reduceEffectiveModelProjections, } from './model-projection-transition-ledger.js';
29
28
  import { buildRuntimeEventModelReplayPlan, collectToolActivityTurnIds, compatibleProviderReasoningReplayEventIds, } from './model-history.js';
30
29
  import { planHistoryCompaction } from './history-compaction.js';
31
30
  import { resolveDeclaredContextWindow } from './context-budget-policy.js';
@@ -98,7 +97,6 @@ export class AiSdkCompaction {
98
97
  sessionId: this.sessionId,
99
98
  archiveToolResult: (candidate) => archive(candidate),
100
99
  recordTransition: (transition) => record(transition, turnId),
101
- loadTransitions: () => this.loadModelProjectionTransitions(),
102
100
  now: this.now,
103
101
  };
104
102
  }
@@ -335,11 +333,11 @@ export class AiSdkCompaction {
335
333
  *
336
334
  * The current Turn's own events go through here on every provider step, for
337
335
  * the same reason prior Turns do: what the model sees is the folded ledger,
338
- * not the raw one. A ledger this build cannot read in full leaves the slice
339
- * untouched — the content is then merely unpruned, never wrongly replaced.
336
+ * not the raw one. Checkpoint validation and replay pass the same snapshot;
337
+ * standalone callers read one here. Unreadable targets remain withheld.
340
338
  */
341
- async foldEffectiveModelHistory(events) {
342
- const loaded = await this.loadModelProjectionTransitions();
339
+ async foldEffectiveModelHistory(events, snapshot) {
340
+ const loaded = snapshot ?? (await this.loadModelProjectionTransitions());
343
341
  if (loaded.transitions.length === 0 && loaded.unreadableTargets.size === 0) {
344
342
  return [...events];
345
343
  }
@@ -371,39 +369,32 @@ export class AiSdkCompaction {
371
369
  return historyCompactSourceDigest(covered) === pinned;
372
370
  }
373
371
  /**
374
- * Fold the durable transition ledger onto this session's prior history, and
375
- * commit any new stale-result transition the prune policy calls for.
372
+ * Apply the same bounded-result rule to current and prior events.
376
373
  *
377
374
  * This is the one seam where raw RuntimeEvents become effective model
378
375
  * history: the caller uses the returned events for replay, budgeting and
379
376
  * compaction alike, so no later stage can read content a transition removed.
380
377
  */
381
- async prepareContextBudgetPolicy(runtimeContext, turnId) {
378
+ async pruneToolResults(runtimeContext, turnId) {
382
379
  const policy = this.input.contextBudget;
383
- const loaded = await this.loadModelProjectionTransitions();
384
- let transitions = loaded.transitions;
385
- let effective = reduceEffectiveModelProjections(runtimeContext, transitions, loaded.unreadableTargets);
380
+ let loaded = await this.loadModelProjectionTransitions();
381
+ let effective = reduceEffectiveModelProjections(runtimeContext, loaded.transitions, loaded.unreadableTargets);
386
382
  if (!policy)
387
- return { policy, events: effective.events };
388
- let nextPolicy = policy;
389
- let diagnosticPatch;
383
+ return { events: effective.events, projectionSnapshot: loaded };
384
+ let stats;
390
385
  // A chain this reader cannot see in full is a chain it must not extend: a
391
386
  // successor built on a partly known state would name the wrong predecessor
392
387
  // and be permanently inert, losing the content it archived.
393
388
  const services = loaded.unreadableTargets.size === 0
394
389
  ? this.toolResultArchiveTransitionServices(turnId)
395
390
  : undefined;
396
- if (policy.staleToolResultPrune?.enabled === true && services) {
391
+ if (policy.toolResultPrune?.enabled === true && services) {
397
392
  // The decision is taken over EFFECTIVE history, so a result an earlier
398
393
  // Turn already replaced is never re-measured — or re-archived — at the
399
394
  // size it used to have.
400
- const candidates = collectStaleToolResultArchiveCandidates(effective.events, policy.staleToolResultPrune, policy.charsPerToken ?? 4);
401
- const committed = [];
402
- let archiveFailures = 0;
403
- let estimatedTokensBefore = 0;
404
- let estimatedTokensAfter = 0;
395
+ const candidates = collectToolResultArchiveCandidates(effective.events, policy.toolResultPrune, policy.charsPerToken ?? 4);
405
396
  for (const candidate of candidates) {
406
- const outcome = await archiveToolResultAsTransition(services, {
397
+ await archiveToolResultAsTransition(services, {
407
398
  runtimeEventId: candidate.runtimeEventId,
408
399
  turnId: candidate.turnId,
409
400
  toolCallId: candidate.toolCallId,
@@ -415,37 +406,41 @@ export class AiSdkCompaction {
415
406
  reason: candidate.reason,
416
407
  result: candidate.result,
417
408
  });
418
- if (!outcome) {
419
- archiveFailures += 1;
420
- continue;
421
- }
422
- committed.push(outcome.transition);
423
- estimatedTokensBefore += candidate.originalEstimatedTokens;
424
- estimatedTokensAfter += estimateTokens(serializedToolResultProjection(outcome.transition.replacement).length, policy.charsPerToken ?? 4);
425
409
  }
426
- if (committed.length > 0) {
427
- transitions = [...transitions, ...committed];
428
- effective = reduceEffectiveModelProjections(runtimeContext, transitions, loaded.unreadableTargets);
429
- }
430
- if (committed.length > 0 || archiveFailures > 0) {
431
- diagnosticPatch = {
432
- ...(committed.length > 0
433
- ? {
434
- prunedToolResults: committed.length,
435
- prunedToolResultEstimatedTokensBefore: estimatedTokensBefore,
436
- prunedToolResultEstimatedTokensAfter: estimatedTokensAfter,
437
- archivePlaceholders: committed.length,
438
- archivePlaceholderReasonCounts: {
439
- stale_tool_result_pruned_before_compact: committed.length,
440
- },
441
- }
442
- : {}),
443
- ...(archiveFailures > 0
444
- ? { archiveWriteFailures: archiveFailures, unarchivedToolResults: archiveFailures }
445
- : {}),
410
+ if (candidates.length > 0) {
411
+ // Writes (including a rival winner or an uncertain commit) cannot tell
412
+ // us what the model sees. Read once after the batch and use this same
413
+ // snapshot for checkpoint validation, replay, and diagnostics.
414
+ loaded = await this.loadModelProjectionTransitions();
415
+ effective = reduceEffectiveModelProjections(runtimeContext, loaded.transitions, loaded.unreadableTargets);
416
+ stats = {
417
+ prunedToolResults: 0,
418
+ prunedToolResultEstimatedTokensBefore: 0,
419
+ prunedToolResultEstimatedTokensAfter: 0,
420
+ archiveWriteFailures: 0,
446
421
  };
422
+ for (const candidate of candidates) {
423
+ const applied = effective.applied.find((transition) => transition.target.runtimeEventId === candidate.runtimeEventId);
424
+ if (!applied) {
425
+ stats.archiveWriteFailures++;
426
+ continue;
427
+ }
428
+ stats.prunedToolResults++;
429
+ stats.prunedToolResultEstimatedTokensBefore += candidate.originalEstimatedTokens;
430
+ stats.prunedToolResultEstimatedTokensAfter += estimateTokens(serializedToolResultProjection(applied.replacement).length, policy.charsPerToken ?? 4);
431
+ }
447
432
  }
448
433
  }
434
+ return { events: effective.events, projectionSnapshot: loaded, ...(stats ? { stats } : {}) };
435
+ }
436
+ async prepareContextBudgetPolicy(runtimeContext, turnId) {
437
+ const policy = this.input.contextBudget;
438
+ const prepared = await this.pruneToolResults(runtimeContext, turnId);
439
+ const effective = { events: prepared.events };
440
+ if (!policy)
441
+ return { policy, events: effective.events, projectionSnapshot: prepared.projectionSnapshot };
442
+ let nextPolicy = policy;
443
+ let diagnosticPatch = prepared.stats;
449
444
  let loadedCheckpoint;
450
445
  try {
451
446
  loadedCheckpoint = await Promise.resolve(this.input.loadHistoryCompactCheckpoint?.());
@@ -492,93 +487,10 @@ export class AiSdkCompaction {
492
487
  return {
493
488
  policy: nextPolicy,
494
489
  events: effective.events,
490
+ projectionSnapshot: prepared.projectionSnapshot,
495
491
  ...(diagnosticPatch ? { diagnosticPatch } : {}),
496
492
  };
497
493
  }
498
- buildActiveToolResultPruneProjection(turnId, includeNewestStep, onDiagnosticPatch) {
499
- const policy = this.input.contextBudget?.activeToolResultPrune;
500
- if (policy?.enabled !== true)
501
- return undefined;
502
- const services = this.toolResultArchiveTransitionServices(turnId);
503
- // No durable ledger, no lossy rewrite. The old per-Turn placeholder map let
504
- // this run prune content that the NEXT request would have shown again.
505
- if (!services || !this.input.loadTurnRuntimeEvents)
506
- return undefined;
507
- // The current Turn reads the same folded history as every other consumer.
508
- // Nothing here remembers what this run already archived: the ledger says it,
509
- // and a step that measures the raw body again would archive it again.
510
- let effective;
511
- const loadEffectiveTurnEvents = async () => {
512
- if (effective)
513
- return effective;
514
- const loaded = await this.loadModelProjectionTransitions();
515
- if (loaded.unreadableTargets.size > 0)
516
- return undefined;
517
- let turnEvents;
518
- try {
519
- turnEvents = await this.input.loadTurnRuntimeEvents(turnId);
520
- }
521
- catch {
522
- return undefined;
523
- }
524
- const reduction = reduceEffectiveModelProjections(turnEvents, loaded.transitions);
525
- const lastApplied = new Map();
526
- for (const transition of reduction.applied) {
527
- lastApplied.set(transition.target.runtimeEventId, transition.transitionId);
528
- }
529
- effective = { events: reduction.events, lastApplied };
530
- return effective;
531
- };
532
- const resolveProjection = async (toolCallId) => {
533
- const current = await loadEffectiveTurnEvents();
534
- if (!current)
535
- return undefined;
536
- const event = current.events.find((candidate) => candidate.partial !== true &&
537
- candidate.content?.kind === 'function_response' &&
538
- candidate.content.id === toolCallId);
539
- if (!event || event.content?.kind !== 'function_response')
540
- return undefined;
541
- const projection = baseToolResultProjection(event);
542
- if (!projection)
543
- return undefined;
544
- const previousTransitionId = current.lastApplied.get(event.id);
545
- return {
546
- runtimeEventId: event.id,
547
- turnId: event.turnId,
548
- toolName: event.content.name,
549
- projection,
550
- ...(previousTransitionId ? { previousTransitionId } : {}),
551
- };
552
- };
553
- return async (options) => {
554
- const eligibleToolCallIds = collectPrunableCompletedStepToolCallIds(options.completedSteps, includeNewestStep);
555
- if (eligibleToolCallIds.size === 0)
556
- return undefined;
557
- // Each provider step rebuilds its messages from the durable Turn ledger,
558
- // so each step must re-fold it too.
559
- effective = undefined;
560
- const rewritten = await rewriteActiveToolResultsInMessages({
561
- messages: options.messages,
562
- policy,
563
- stepNumber: options.stepNumber,
564
- turnId,
565
- charsPerToken: this.input.contextBudget?.charsPerToken,
566
- eligibleToolCallIds,
567
- completedToolCalls: options.completedSteps.flatMap((step, stepNumber) => (step.toolCalls ?? []).map((call) => ({
568
- toolCallId: call.toolCallId,
569
- toolName: call.toolName,
570
- input: call.input,
571
- stepNumber,
572
- }))),
573
- resolveProjection,
574
- transitions: services,
575
- });
576
- if (hasActiveToolResultPruneDiagnosticPatch(rewritten.diagnosticPatch)) {
577
- onDiagnosticPatch?.(rewritten.diagnosticPatch);
578
- }
579
- return rewritten.rewritten > 0 ? { messages: rewritten.messages } : undefined;
580
- };
581
- }
582
494
  /**
583
495
  * Mid-turn capacity compaction eligibility (issue #882 PR 1). Explicit
584
496
  * opt-in via `historyCompact.midTurn.enabled`; requires the checkpoint
@@ -907,7 +819,8 @@ export class AiSdkCompaction {
907
819
  // materializable and replay-admissible. A checkpoint that fails either
908
820
  // check must never be persisted because it would poison every later
909
821
  // projection.
910
- const replayPlan = buildRuntimeEventModelReplayPlan(plan.replacementEvents, {
822
+ const effectiveReplacement = await this.foldEffectiveModelHistory(plan.replacementEvents);
823
+ const replayPlan = buildRuntimeEventModelReplayPlan(effectiveReplacement, {
911
824
  toolActivityTurnIds: collectToolActivityTurnIds(orderedEvents),
912
825
  });
913
826
  if (replayPlan.items.length === 0 ||
@@ -1035,41 +948,6 @@ export class AiSdkCompaction {
1035
948
  return { messages: outcome.replacementMessages };
1036
949
  }
1037
950
  }
1038
- // -- moved helpers (defined in ai-sdk-backend, used only by cache write) -------
1039
- function incrementRecord(counts, key) {
1040
- counts[key] = (counts[key] ?? 0) + 1;
1041
- }
1042
- function mergeCountsInto(target, source) {
1043
- for (const [key, value] of Object.entries(source ?? {})) {
1044
- target[key] = (target[key] ?? 0) + value;
1045
- }
1046
- }
1047
- // -- moved helpers (prepare-step / signature / prune) ------------------------
1048
- /**
1049
- * Tool results from the newest completed step have not crossed the provider
1050
- * boundary yet: projection is invoked immediately before the first request
1051
- * that could show those results to the model. By default active pruning defers
1052
- * the newest step and archives only older completed steps, after the model has
1053
- * had one request in which to consume their exact output.
1054
- *
1055
- * `includeNewestStep` widens eligibility to every completed step, including the
1056
- * newest. The caller sets it when mid-turn capacity compaction is active: the
1057
- * final-payload verdict may need an oversized newest result pruned to a
1058
- * placeholder before declaring exhaustion, and capacity/recovery rebuilds
1059
- * re-materialize raw bodies from the ledger that must be re-archived.
1060
- */
1061
- function collectPrunableCompletedStepToolCallIds(steps, includeNewestStep) {
1062
- const out = new Set();
1063
- const prunableSteps = includeNewestStep ? steps : steps.slice(0, -1);
1064
- for (const step of prunableSteps) {
1065
- for (const call of step.toolCalls ?? []) {
1066
- if (typeof call.toolCallId === 'string' && call.toolCallId.length > 0) {
1067
- out.add(call.toolCallId);
1068
- }
1069
- }
1070
- }
1071
- return out;
1072
- }
1073
951
  function projectHistoricalImageOmissions(messages, omittedImageToolResults) {
1074
952
  if (omittedImageToolResults.size === 0)
1075
953
  return undefined;
@@ -1110,13 +988,6 @@ function stableStringifyForSignature(value) {
1110
988
  .map((key) => `${JSON.stringify(key)}:${stableStringifyForSignature(object[key])}`)
1111
989
  .join(',')}}`;
1112
990
  }
1113
- export function hasActiveToolResultPruneDiagnosticPatch(patch) {
1114
- return ((patch.activePrunedToolResults ?? 0) > 0 ||
1115
- (patch.activeSupersededToolResults ?? 0) > 0 ||
1116
- (patch.activeDuplicateToolResults ?? 0) > 0 ||
1117
- (patch.activeArchiveFailures ?? 0) > 0 ||
1118
- (patch.activeEstimatedTokensSaved ?? 0) > 0);
1119
- }
1120
991
  /**
1121
992
  * Per-send() state for the mid-turn capacity invariant. The coverage pool is
1122
993
  * NOT mirrored here: every trigger reads the current turn's persisted
@@ -444,18 +444,23 @@ export class AiSdkMessageProjection {
444
444
  }
445
445
  async materializeRuntimeReplayItem(budget, item) {
446
446
  if (item.role === 'user') {
447
+ // Both ordinary and steered replay materialize image attachments through
448
+ // the same path the original request used — a steering replay that kept
449
+ // only the envelope text would hand a recovery turn references without
450
+ // the native images the first request received.
451
+ const content = await this.appendImageParts(budget, item.content, item.attachments, item.steering ? `steering:${item.steering.eventId}` : `runtime-event:${item.eventId}`);
447
452
  if (item.steering) {
448
453
  // Already envelope-wrapped by the plan; carry the structured identity
449
454
  // so injection dedupe recognizes the replayed message.
450
455
  return {
451
456
  role: 'user',
452
- content: item.content,
457
+ content,
453
458
  providerOptions: steeringProviderOptions(item.steering.eventId),
454
459
  };
455
460
  }
456
461
  return {
457
462
  role: 'user',
458
- content: await this.appendImageParts(budget, item.content, item.attachments, `runtime-event:${item.eventId}`),
463
+ content,
459
464
  };
460
465
  }
461
466
  return {