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

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 (71) hide show
  1. package/dist/pi-transcript.js +3 -4
  2. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  3. package/node_modules/@maka/core/dist/agent-graph-schedule.js +15 -4
  4. package/node_modules/@maka/core/dist/executor-id.js +22 -0
  5. package/node_modules/@maka/core/dist/external-session.js +18 -0
  6. package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -13
  7. package/node_modules/@maka/core/dist/runtime-event.js +19 -3
  8. package/node_modules/@maka/core/dist/session-send-projection.js +6 -3
  9. package/node_modules/@maka/core/dist/session.js +8 -2
  10. package/node_modules/@maka/core/dist/shell-run-result.js +1 -0
  11. package/node_modules/@maka/core/dist/shell-run.js +4 -0
  12. package/node_modules/@maka/core/dist/work-board.js +94 -1
  13. package/node_modules/@maka/core/package.json +1 -0
  14. package/node_modules/@maka/eval/dist/fleet-simulation.js +270 -0
  15. package/node_modules/@maka/eval/dist/fleet-store.js +243 -0
  16. package/node_modules/@maka/eval/dist/fleet-worker.js +125 -0
  17. package/node_modules/@maka/eval/dist/fleet.js +329 -0
  18. package/node_modules/@maka/eval/dist/index.js +3 -0
  19. package/node_modules/@maka/runtime/dist/agent-run.js +2 -17
  20. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +6 -2
  21. package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +36 -24
  22. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +257 -330
  23. package/node_modules/@maka/runtime/dist/background-task-health-tool.js +104 -0
  24. package/node_modules/@maka/runtime/dist/history-compaction.js +3 -1
  25. package/node_modules/@maka/runtime/dist/local-web-fetch.js +1 -1
  26. package/node_modules/@maka/runtime/dist/model-adapter.js +62 -53
  27. package/node_modules/@maka/runtime/dist/model-history.js +1 -5
  28. package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +311 -0
  29. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +344 -0
  30. package/node_modules/@maka/runtime/dist/provider-error-classification.js +7 -1
  31. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +35 -8
  32. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +7 -1
  33. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +1 -0
  34. package/node_modules/@maka/runtime/dist/runtime-invocation-route.js +49 -0
  35. package/node_modules/@maka/runtime/dist/runtime-kernel.js +72 -150
  36. package/node_modules/@maka/runtime/dist/runtime-read-model.js +0 -2
  37. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
  38. package/node_modules/@maka/runtime/dist/session-manager.js +100 -58
  39. package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -3
  40. package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +1 -0
  41. package/node_modules/@maka/runtime/dist/stream-graph-schedule-reconcile.js +1 -0
  42. package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +26 -4
  43. package/node_modules/@maka/runtime/dist/subagent-tools.js +7 -0
  44. package/node_modules/@maka/runtime/dist/tool-runtime.js +45 -466
  45. package/node_modules/@maka/runtime/package.json +3 -0
  46. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +7 -2
  47. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +1 -0
  48. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +24 -3
  49. package/node_modules/@maka/runtime-host/dist/protocol/index.js +4 -1
  50. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +42 -5
  51. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +31 -3
  52. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +6 -0
  53. package/node_modules/@maka/runtime-host/dist/server/child-agent-composition.js +1 -0
  54. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +1 -22
  55. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +54 -8
  56. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +14 -1
  57. package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +10 -2
  58. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  59. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
  60. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +56 -16
  61. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +4 -3
  62. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
  63. package/node_modules/@maka/runtime-host/dist/server/web-fetch-tool.js +37 -1
  64. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +450 -234
  65. package/node_modules/@maka/storage/dist/claude-code-transcript-lineage.js +106 -77
  66. package/node_modules/@maka/storage/dist/legacy-run-header.js +2 -2
  67. package/node_modules/@maka/storage/dist/model-call-usage-sql.js +1 -1
  68. package/node_modules/@maka/storage/dist/session-store.js +12 -2
  69. package/node_modules/@maka/storage/dist/sqlite-long-term-memory-store.js +86 -12
  70. package/node_modules/@maka/storage/dist/work-board-store.js +40 -1
  71. package/package.json +1 -1
@@ -0,0 +1,329 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { stableJsonStringify } from '@maka/core/canonical-json';
20
+ import { expandExperiment } from './experiment.js';
21
+ import { decodeEvalResult } from './result.js';
22
+ import { parseExperimentSpec } from './spec.js';
23
+ export function createFleetState(spec, policy) {
24
+ const frozenSpec = parseExperimentSpec(spec);
25
+ for (const key of ['runId', 'environmentId'])
26
+ nonempty(policy[key], key);
27
+ for (const key of [
28
+ 'groupCpus',
29
+ 'groupMemoryMb',
30
+ 'leaseMs',
31
+ 'recoveryGraceMs',
32
+ 'maxAttemptsPerCell',
33
+ ])
34
+ positive(policy[key], key);
35
+ if (!Number.isSafeInteger(policy.retryBackoffMs) || policy.retryBackoffMs < 0) {
36
+ throw new Error('invalid retryBackoffMs');
37
+ }
38
+ const groups = new Map();
39
+ const ids = new Set();
40
+ for (const cell of expandExperiment(frozenSpec)) {
41
+ if (ids.has(cell.id))
42
+ throw new Error('ambiguous experiment cell identity');
43
+ ids.add(cell.id);
44
+ const id = JSON.stringify([cell.task.id, cell.repetition]);
45
+ const group = groups.get(id) ?? [];
46
+ group.push(cell.id);
47
+ groups.set(id, group);
48
+ }
49
+ return {
50
+ version: 'maka.eval.fleet.v1',
51
+ spec: frozenSpec,
52
+ policy: structuredClone(policy),
53
+ clock: 0,
54
+ nextAssignment: 1,
55
+ workers: [],
56
+ assignments: [],
57
+ attempts: [],
58
+ blocked: [],
59
+ paused: false,
60
+ groups: [...groups].map(([id, cellIds]) => ({ id, cellIds, generation: 0, readyAt: 0 })),
61
+ };
62
+ }
63
+ /** Pure deterministic transition. Persist the returned state before delivering its reply. */
64
+ export function transitionFleet(previous, command, now) {
65
+ if (!Number.isSafeInteger(now) || now < 0)
66
+ throw new Error('invalid fleet clock');
67
+ const state = structuredClone(previous);
68
+ state.clock = Math.max(state.clock, now);
69
+ if (command.kind === 'pause') {
70
+ state.paused = true;
71
+ return { state, reply: {} };
72
+ }
73
+ // Recovery must run BEFORE expiry: a coordinator outage is not evidence of VM loss.
74
+ if (command.kind === 'recover') {
75
+ state.paused = false;
76
+ for (const worker of state.workers)
77
+ worker.reconciled = false;
78
+ for (const assignment of active(state)) {
79
+ assignment.expiresAt = Math.max(assignment.expiresAt, state.clock + state.policy.recoveryGraceMs);
80
+ }
81
+ return { state, reply: {} };
82
+ }
83
+ for (const assignment of active(state)) {
84
+ if (!state.paused && assignment.expiresAt <= state.clock)
85
+ settle(state, assignment, 'expired');
86
+ }
87
+ const reply = {};
88
+ switch (command.kind) {
89
+ case 'register': {
90
+ const description = command.worker;
91
+ nonempty(description.id, 'worker id');
92
+ positive(description.groupSlots, 'groupSlots');
93
+ positive(description.cpus, 'cpus');
94
+ positive(description.memoryMb, 'memoryMb');
95
+ if (description.environmentId !== state.policy.environmentId ||
96
+ description.cpus < description.groupSlots * state.policy.groupCpus ||
97
+ description.memoryMb < description.groupSlots * state.policy.groupMemoryMb) {
98
+ throw new Error('worker does not satisfy execution and verifier admission');
99
+ }
100
+ const existing = state.workers.find((w) => w.description.id === description.id);
101
+ if (existing &&
102
+ stableJsonStringify(existing.description) !== stableJsonStringify(description)) {
103
+ throw new Error('worker incarnation changed description');
104
+ }
105
+ if (!existing)
106
+ state.workers.push({ description: structuredClone(description), reconciled: false });
107
+ break;
108
+ }
109
+ case 'heartbeat': {
110
+ const worker = requireWorker(state, command.workerId);
111
+ if (new Set(command.assignmentIds).size !== command.assignmentIds.length) {
112
+ throw new Error('duplicate heartbeat assignment');
113
+ }
114
+ for (const id of command.assignmentIds) {
115
+ const assignment = requireAssignment(state, command.workerId, id);
116
+ if (assignment.status === 'active')
117
+ assignment.expiresAt = state.clock + state.policy.leaseMs;
118
+ }
119
+ // Omitted assignments are allowed to expire; they are not silently renewed.
120
+ worker.reconciled = true;
121
+ reply.activeAssignmentIds = active(state)
122
+ .filter((a) => a.workerId === command.workerId)
123
+ .map((a) => a.id);
124
+ // Includes a claim whose reply was lost. The worker can adopt it rather than leak a slot.
125
+ reply.work = active(state)
126
+ .filter((a) => a.workerId === command.workerId)
127
+ .map((a) => workFor(state, a));
128
+ break;
129
+ }
130
+ case 'claim': {
131
+ const worker = requireWorker(state, command.workerId);
132
+ const running = active(state);
133
+ if (state.paused ||
134
+ !worker.reconciled ||
135
+ state.blocked.length ||
136
+ running.length >= state.spec.execution.maxConcurrentTaskGroups ||
137
+ running.filter((a) => a.workerId === command.workerId).length >=
138
+ worker.description.groupSlots)
139
+ break;
140
+ const group = state.groups.find((g) => g.readyAt <= state.clock &&
141
+ !running.some((a) => a.groupId === g.id) &&
142
+ pendingCells(state, g.cellIds).length > 0);
143
+ if (!group)
144
+ break;
145
+ const id = `${state.policy.runId}/assignment-${state.nextAssignment++}`;
146
+ const attempts = pendingCells(state, group.cellIds).map((cellId, index) => ({
147
+ id: `${id}/attempt-${index + 1}`,
148
+ cellId,
149
+ sequence: state.attempts.filter((a) => a.cellId === cellId).length + 1,
150
+ assignmentId: id,
151
+ report: null,
152
+ disposition: 'pending',
153
+ }));
154
+ const assignment = {
155
+ id,
156
+ groupId: group.id,
157
+ workerId: command.workerId,
158
+ generation: ++group.generation,
159
+ attemptIds: attempts.map((a) => a.id),
160
+ status: 'active',
161
+ expiresAt: state.clock + state.policy.leaseMs,
162
+ };
163
+ state.attempts.push(...attempts);
164
+ state.assignments.push(assignment);
165
+ reply.assignment = structuredClone(assignment);
166
+ reply.work = [workFor(state, assignment)];
167
+ break;
168
+ }
169
+ case 'report': {
170
+ const attempt = state.attempts.find((a) => a.id === command.attemptId);
171
+ if (!attempt)
172
+ throw new Error('unknown attempt');
173
+ const assignment = requireAssignment(state, command.workerId, attempt.assignmentId);
174
+ const report = validateFleetReport(command.report);
175
+ if (attempt.report) {
176
+ if (stableJsonStringify(attempt.report) !== stableJsonStringify(report)) {
177
+ throw new Error('conflicting attempt report');
178
+ }
179
+ }
180
+ else {
181
+ attempt.report = report;
182
+ attempt.disposition =
183
+ assignment.status !== 'active' ? 'late' : selectable(report) ? 'selected' : 'retryable';
184
+ if (attempt.disposition !== 'late' &&
185
+ report.environmentFailure &&
186
+ !state.blocked.includes(report.environmentFailure))
187
+ state.blocked.push(report.environmentFailure);
188
+ }
189
+ reply.disposition = attempt.disposition;
190
+ break;
191
+ }
192
+ case 'finish': {
193
+ const assignment = requireAssignment(state, command.workerId, command.assignmentId);
194
+ if (assignment.status !== 'active')
195
+ break;
196
+ if (assignment.attemptIds.some((id) => !state.attempts.find((a) => a.id === id)?.report)) {
197
+ throw new Error('cannot finish before all cell reports are committed');
198
+ }
199
+ settle(state, assignment, 'finished');
200
+ break;
201
+ }
202
+ case 'repair':
203
+ state.blocked = state.blocked.filter((reason) => reason !== command.reason);
204
+ break;
205
+ case 'tick':
206
+ break;
207
+ }
208
+ return { state, reply };
209
+ }
210
+ export function summarizeFleet(state) {
211
+ const selected = state.attempts.filter((a) => a.disposition === 'selected');
212
+ const cells = state.groups.flatMap((g) => g.cellIds);
213
+ const running = active(state);
214
+ const exhausted = cells.filter((id) => !selected.some((a) => a.cellId === id) &&
215
+ !running.some((a) => a.attemptIds.some((attemptId) => state.attempts.some((attempt) => attempt.id === attemptId && attempt.cellId === id))) &&
216
+ state.attempts.filter((a) => a.cellId === id).length >= state.policy.maxAttemptsPerCell);
217
+ const reported = state.attempts.filter((a) => a.report);
218
+ return {
219
+ runId: state.policy.runId,
220
+ totalCells: cells.length,
221
+ completedCells: selected.length,
222
+ incompleteCells: cells.length - selected.length,
223
+ exhaustedCells: exhausted.length,
224
+ activeGroups: running.length,
225
+ blocked: [...state.blocked],
226
+ paused: state.paused,
227
+ settled: running.length === 0 && cells.length === selected.length + exhausted.length,
228
+ observedCostUsd: reported.reduce((sum, a) => sum + (a.report.result.costUsd ?? 0), 0),
229
+ usageComplete: state.attempts.every((a) => a.report?.usage === 'complete'),
230
+ results: selected.map((a) => ({
231
+ cellId: a.cellId,
232
+ attemptId: a.id,
233
+ result: selectedResult(a.report),
234
+ artifacts: structuredClone(a.report.artifacts),
235
+ })),
236
+ };
237
+ }
238
+ export function validateFleetReport(value) {
239
+ const report = structuredClone(value);
240
+ report.result = decodeEvalResult(value.result);
241
+ member(report.execution, ['completed', 'subject_failed', 'not_started', 'unknown'], 'execution');
242
+ member(report.verification, ['valid', 'invalid', 'not_run'], 'verification');
243
+ member(report.usage, ['complete', 'partial', 'missing'], 'usage');
244
+ member(report.cleanup, ['confirmed', 'unknown'], 'cleanup');
245
+ if (report.environmentFailure !== null)
246
+ nonempty(report.environmentFailure, 'environmentFailure');
247
+ if (!Array.isArray(report.artifacts))
248
+ throw new Error('invalid artifacts');
249
+ for (const artifact of report.artifacts) {
250
+ if (!/^[a-f0-9]{64}$/.test(artifact.sha256) ||
251
+ !Number.isSafeInteger(artifact.bytes) ||
252
+ artifact.bytes < 0) {
253
+ throw new Error('invalid artifact reference');
254
+ }
255
+ }
256
+ if (report.usage === 'missing' && report.result.usage !== null)
257
+ throw new Error('missing usage has tokens');
258
+ if (report.usage === 'complete' && report.result.usage === null)
259
+ throw new Error('complete usage lacks tokens');
260
+ if (report.usage !== 'complete' && report.result.costUsd !== null)
261
+ throw new Error('incomplete usage has settled cost');
262
+ if (report.verification === 'valid' &&
263
+ (report.result.score === null || !['completed', 'subject_failed'].includes(report.execution)))
264
+ throw new Error('invalid verification evidence');
265
+ return report;
266
+ }
267
+ function selectedResult(report) {
268
+ // Keep raw execution evidence in the attempt; project settled status only in selected results.
269
+ return {
270
+ ...report.result,
271
+ status: report.execution === 'subject_failed' || report.result.status === 'subject_failed'
272
+ ? 'subject_failed'
273
+ : 'completed',
274
+ };
275
+ }
276
+ function selectable(report) {
277
+ return (['completed', 'subject_failed'].includes(report.execution) &&
278
+ report.verification === 'valid' &&
279
+ report.cleanup === 'confirmed' &&
280
+ report.environmentFailure === null);
281
+ }
282
+ function active(state) {
283
+ return state.assignments.filter((a) => a.status === 'active');
284
+ }
285
+ function workFor(state, assignment) {
286
+ const cells = expandExperiment(state.spec);
287
+ return {
288
+ assignment: structuredClone(assignment),
289
+ cells: assignment.attemptIds.flatMap((id) => {
290
+ const attempt = state.attempts.find((a) => a.id === id);
291
+ return attempt.report
292
+ ? []
293
+ : [{ attemptId: id, cell: cells.find((c) => c.id === attempt.cellId) }];
294
+ }),
295
+ };
296
+ }
297
+ function pendingCells(state, ids) {
298
+ return ids.filter((id) => !state.attempts.some((a) => a.cellId === id && a.disposition === 'selected') &&
299
+ state.attempts.filter((a) => a.cellId === id).length < state.policy.maxAttemptsPerCell);
300
+ }
301
+ function requireWorker(state, id) {
302
+ const worker = state.workers.find((w) => w.description.id === id);
303
+ if (!worker)
304
+ throw new Error('unknown worker incarnation');
305
+ return worker;
306
+ }
307
+ function requireAssignment(state, workerId, id) {
308
+ const assignment = state.assignments.find((a) => a.id === id);
309
+ if (!assignment || assignment.workerId !== workerId)
310
+ throw new Error('unknown or foreign assignment');
311
+ return assignment;
312
+ }
313
+ function settle(state, assignment, status) {
314
+ assignment.status = status;
315
+ state.groups.find((g) => g.id === assignment.groupId).readyAt =
316
+ state.clock + state.policy.retryBackoffMs;
317
+ }
318
+ function positive(value, name) {
319
+ if (!Number.isSafeInteger(value) || value < 1)
320
+ throw new Error(`invalid ${name}`);
321
+ }
322
+ function nonempty(value, name) {
323
+ if (typeof value !== 'string' || !value.trim())
324
+ throw new Error(`invalid ${name}`);
325
+ }
326
+ function member(value, values, name) {
327
+ if (!values.includes(value))
328
+ throw new Error(`invalid ${name}`);
329
+ }
@@ -21,6 +21,9 @@ export * from './cli.js';
21
21
  export * from './experiment-directory.js';
22
22
  export * from './experiment.js';
23
23
  export * from './external-subject.js';
24
+ export * from './fleet.js';
25
+ export * from './fleet-store.js';
26
+ export * from './fleet-worker.js';
24
27
  export * from './harness-executor.js';
25
28
  export * from './maka-subject.js';
26
29
  export * from './result.js';
@@ -20,6 +20,7 @@ import { RUN_COMPOSITION_RECORDED_EVENT_TYPE } from '@maka/core/agent-run';
20
20
  import { isRuntimeHandoffPause } from '@maka/core/runtime-handoff';
21
21
  import { RunHandoffGate } from './run-handoff-gate.js';
22
22
  import { preserveHandoffOpening } from './runtime-resume.js';
23
+ import { runtimeInvocationRouteForHeader } from './runtime-invocation-route.js';
23
24
  import { createRequestCompositionSnapshot, decodeRequestCompositionSnapshot, decodeRunCompositionSnapshot, } from '@maka/core/run-composition';
24
25
  import { DurableStoreWriteError, RunSealedError } from '@maka/core/runtime-event-store';
25
26
  import { buildInvocationOpenedEvent, buildSyntheticTerminalRuntimeEvent, isSessionInlineInvocation, } from '@maka/core/runtime-invocation';
@@ -86,7 +87,6 @@ export class AgentRun {
86
87
  failureClass;
87
88
  failureMessage;
88
89
  lastTs = 0;
89
- sawCompletion = false;
90
90
  finalStatus;
91
91
  turnFailed = false;
92
92
  finalized = false;
@@ -877,7 +877,6 @@ export class AgentRun {
877
877
  const terminalSessionEvent = (ev.type === 'complete' || ev.type === 'abort') && !this.turnFailed;
878
878
  const turnStatus = terminalSessionEvent ? turnStatusFromEvent(ev) : undefined;
879
879
  if (terminalSessionEvent) {
880
- this.sawCompletion = true;
881
880
  if (ev.type === 'abort' && !this.abortSource)
882
881
  this.abortSource = ev.reason;
883
882
  if (ev.type === 'complete' && ev.stopReason === 'user_stop' && !this.abortSource)
@@ -1128,21 +1127,7 @@ export class AgentRun {
1128
1127
  const opening = {
1129
1128
  kind: 'invocation_opened',
1130
1129
  protocol: 'invocation_opened_v1',
1131
- route: this.header.llmConnectionId === undefined
1132
- ? {
1133
- provenance: 'unknown',
1134
- backendKind: this.header.backend,
1135
- llmConnectionSlug: this.header.llmConnectionSlug,
1136
- modelId: this.header.model,
1137
- }
1138
- : {
1139
- provenance: 'runtime',
1140
- backendKind: this.header.backend,
1141
- llmConnectionId: this.header.llmConnectionId,
1142
- llmConnectionSlug: this.header.llmConnectionSlug,
1143
- modelId: this.header.model,
1144
- ...(providerStateIdentity ? { providerStateIdentity } : {}),
1145
- },
1130
+ route: runtimeInvocationRouteForHeader(this.header, providerStateIdentity),
1146
1131
  configuration: {
1147
1132
  cwd: this.header.cwd,
1148
1133
  permissionMode: this.header.permissionMode,
@@ -285,7 +285,9 @@ export class AiSdkCompaction {
285
285
  const route = invocation.opening.route;
286
286
  return {
287
287
  runId: invocation.runId,
288
- ...(route.provenance === 'runtime' ? { connectionId: route.llmConnectionId } : {}),
288
+ ...(route.provenance === 'runtime' && route.backendKind !== 'plugin-executor'
289
+ ? { connectionId: route.llmConnectionId }
290
+ : {}),
289
291
  modelId: route.modelId,
290
292
  };
291
293
  })
@@ -882,8 +884,9 @@ export class AiSdkCompaction {
882
884
  */
883
885
  async recoverFromOverflowError(input) {
884
886
  const state = input.midTurnState;
885
- if (input.retryAlreadyUsed || !state)
887
+ if (!state || state.compactionAttemptedThisSend || state.omittedImageToolResults.size > 0) {
886
888
  return undefined;
889
+ }
887
890
  if (this.modelAdapter.classifyError(input.error) !== 'context_overflow')
888
891
  return undefined;
889
892
  const eligibleImages = collectHistoricalImageToolResults(state.priorContentEvents);
@@ -1136,6 +1139,7 @@ function persistedRequestAnchor(events, invocations, modelId, connectionId) {
1136
1139
  continue;
1137
1140
  const route = invocations.find((candidate) => candidate.runId === event?.runId)?.opening.route;
1138
1141
  if (route?.provenance !== 'runtime' ||
1142
+ route.backendKind === 'plugin-executor' ||
1139
1143
  route.modelId !== modelId ||
1140
1144
  route.llmConnectionId !== connectionId) {
1141
1145
  return undefined;
@@ -24,6 +24,31 @@ import { admitProviderReasoningReplayItems, buildRuntimeEventReplayTimeline, for
24
24
  import { openAiChatReasoningFieldFromProviderOptions } from './openai-chat-reasoning-transport.js';
25
25
  import { decodePlaintextResponsesReasoningState, replayPlaintextResponsesProviderOptions, } from './responses-reasoning-state.js';
26
26
  import { toolResultOutput } from './tool-result-output.js';
27
+ function isRedactedThinking(providerOptions) {
28
+ const anthropic = providerOptions?.anthropic;
29
+ return (!!anthropic &&
30
+ typeof anthropic === 'object' &&
31
+ !Array.isArray(anthropic) &&
32
+ typeof anthropic.redactedData === 'string');
33
+ }
34
+ function encryptedResponsesReasoning(providerOptions) {
35
+ const openai = providerOptions?.openai;
36
+ if (!openai || typeof openai !== 'object' || Array.isArray(openai))
37
+ return undefined;
38
+ const { itemId, reasoningEncryptedContent } = openai;
39
+ return typeof itemId === 'string' &&
40
+ itemId.length > 0 &&
41
+ typeof reasoningEncryptedContent === 'string' &&
42
+ reasoningEncryptedContent.length > 0
43
+ ? { itemId, reasoningEncryptedContent }
44
+ : undefined;
45
+ }
46
+ export function hasFinalizedReasoning(part) {
47
+ return (!!part.signature ||
48
+ isRedactedThinking(part.providerOptions) ||
49
+ decodePlaintextResponsesReasoningState(part.providerOptions).kind === 'valid' ||
50
+ encryptedResponsesReasoning(part.providerOptions) !== undefined);
51
+ }
27
52
  function isImageToolResult(value) {
28
53
  if (!value || typeof value !== 'object')
29
54
  return false;
@@ -167,11 +192,7 @@ export class AiSdkMessageProjection {
167
192
  }
168
193
  : undefined;
169
194
  }
170
- const anthropic = item.providerOptions?.anthropic;
171
- if (anthropic &&
172
- typeof anthropic === 'object' &&
173
- !Array.isArray(anthropic) &&
174
- typeof anthropic.redactedData === 'string') {
195
+ if (isRedactedThinking(item.providerOptions)) {
175
196
  return replaySupport.signedThinking
176
197
  ? {
177
198
  part: {
@@ -218,26 +239,17 @@ export class AiSdkMessageProjection {
218
239
  return { part: { type: 'reasoning', text: item.text } };
219
240
  }
220
241
  if (replaySupport.responsesReasoning === 'encrypted-content') {
221
- const openai = item.providerOptions?.openai;
222
- if (openai && typeof openai === 'object' && !Array.isArray(openai)) {
223
- const { itemId, reasoningEncryptedContent } = openai;
224
- if (typeof itemId === 'string' &&
225
- itemId.length > 0 &&
226
- typeof reasoningEncryptedContent === 'string' &&
227
- reasoningEncryptedContent.length > 0) {
228
- return {
229
- part: {
230
- type: 'reasoning',
231
- text: item.text,
232
- providerOptions: {
233
- openai: {
234
- itemId,
235
- reasoningEncryptedContent,
236
- },
237
- },
242
+ const encrypted = encryptedResponsesReasoning(item.providerOptions);
243
+ if (encrypted) {
244
+ return {
245
+ part: {
246
+ type: 'reasoning',
247
+ text: item.text,
248
+ providerOptions: {
249
+ openai: encrypted,
238
250
  },
239
- };
240
- }
251
+ },
252
+ };
241
253
  }
242
254
  }
243
255
  if (!replaySupport.unsignedThinking)