maka-agent 0.2.0-dev.24.20260907 → 0.2.0-dev.25.20260908

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 (105) hide show
  1. package/README.md +38 -19
  2. package/README.zh-CN.md +34 -19
  3. package/dist/pi-transcript-tools.js +6 -0
  4. package/dist/pi-tui-layout.js +56 -10
  5. package/dist/pi-tui-pickers.js +102 -1
  6. package/dist/pi-tui-runner.js +156 -42
  7. package/dist/pi-tui-todo.js +221 -0
  8. package/dist/pi-tui-transcript-viewer.js +134 -7
  9. package/dist/runtime-host-cli-context.js +70 -13
  10. package/dist/runtime-host-handoff-surface.js +51 -26
  11. package/dist/runtime-host-local-handoff.js +11 -1
  12. package/dist/runtime-host-service-manager.js +0 -3
  13. package/dist/runtime-host-session-channel.js +13 -0
  14. package/dist/runtime-host-session-driver.js +42 -1
  15. package/dist/tui-copy-catalog.js +79 -3
  16. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  17. package/node_modules/@maka/core/dist/agent-run.js +1 -0
  18. package/node_modules/@maka/core/dist/attachments.js +3 -0
  19. package/node_modules/@maka/core/dist/canonical-json.js +27 -0
  20. package/node_modules/@maka/core/dist/capabilities.js +35 -0
  21. package/node_modules/@maka/core/dist/computer-use.js +0 -1
  22. package/node_modules/@maka/core/dist/display-redaction.js +18 -0
  23. package/node_modules/@maka/core/dist/llm-connections.js +13 -0
  24. package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
  25. package/node_modules/@maka/core/dist/model-metadata.js +4 -0
  26. package/node_modules/@maka/core/dist/redaction.js +35 -0
  27. package/node_modules/@maka/core/dist/run-composition.js +77 -2
  28. package/node_modules/@maka/core/dist/session.js +30 -4
  29. package/node_modules/@maka/core/dist/slash-command-catalog.js +1 -0
  30. package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
  31. package/node_modules/@maka/core/package.json +1 -0
  32. package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
  33. package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
  34. package/node_modules/@maka/runtime/dist/agent-run.js +80 -1
  35. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +16 -8
  36. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +57 -50
  37. package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
  38. package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
  39. package/node_modules/@maka/runtime/dist/conversation-copy.js +153 -11
  40. package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
  41. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +69 -1
  42. package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
  43. package/node_modules/@maka/runtime/dist/model-fetcher.js +42 -2
  44. package/node_modules/@maka/runtime/dist/model-history.js +0 -112
  45. package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
  46. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
  47. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +8 -1
  48. package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
  49. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +1 -1
  50. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +4 -0
  51. package/node_modules/@maka/runtime/dist/runtime-kernel.js +7 -0
  52. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
  53. package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
  54. package/node_modules/@maka/runtime/dist/test-connection.js +0 -1
  55. package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
  56. package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
  57. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
  58. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
  59. package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
  60. package/node_modules/@maka/runtime/package.json +1 -0
  61. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +35 -12
  62. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +2 -0
  63. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +19 -0
  64. package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
  65. package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
  66. package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
  67. package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
  68. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
  69. package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
  70. package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
  71. package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
  72. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
  73. package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
  74. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +31 -2
  75. package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
  76. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
  77. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +44 -9
  78. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +42 -14
  79. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +47 -22
  80. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  81. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
  82. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
  83. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
  84. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +20 -2
  85. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +13 -3
  86. package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
  87. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -1
  88. package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -196
  89. package/node_modules/@maka/storage/dist/git-exec.js +61 -0
  90. package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
  91. package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
  92. package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
  93. package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
  94. package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
  95. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
  96. package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
  97. package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
  98. package/node_modules/@maka/storage/dist/session-bundle-policy.js +2 -1
  99. package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
  100. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +1 -2
  101. package/node_modules/@maka/storage/dist/storage-id.js +28 -0
  102. package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +31 -4
  103. package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
  104. package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
  105. package/package.json +1 -1
@@ -19,13 +19,14 @@
19
19
  import { randomUUID } from 'node:crypto';
20
20
  import { link, mkdir, open, readFile, unlink } from 'node:fs/promises';
21
21
  import { join } from 'node:path';
22
+ import { stableJsonStringify } from '@maka/core/canonical-json';
22
23
  import { FileAttemptStore } from './attempt-store.js';
23
24
  import { parseExperimentSpec } from './spec.js';
24
25
  export async function openExperimentDirectory(root, spec) {
25
26
  await mkdir(root, { recursive: true });
26
27
  const specPath = join(root, 'experiment.json');
27
28
  const temporaryPath = join(root, `.experiment-${randomUUID()}.tmp`);
28
- const canonical = `${canonicalJson(spec)}\n`;
29
+ const canonical = `${stableJsonStringify(spec)}\n`;
29
30
  const temporary = await open(temporaryPath, 'wx', 0o600);
30
31
  try {
31
32
  await temporary.writeFile(canonical);
@@ -37,8 +38,9 @@ export async function openExperimentDirectory(root, spec) {
37
38
  if (error.code !== 'EEXIST')
38
39
  throw error;
39
40
  const existing = parseExperimentSpec(JSON.parse(await readFile(specPath, 'utf8')));
40
- if (canonicalJson(existing) !== canonicalJson(spec))
41
+ if (stableJsonStringify(existing) !== stableJsonStringify(spec)) {
41
42
  throw new Error('experiment spec differs');
43
+ }
42
44
  }
43
45
  finally {
44
46
  await temporary.close().catch(() => undefined);
@@ -46,14 +48,3 @@ export async function openExperimentDirectory(root, spec) {
46
48
  }
47
49
  return { root, specPath, attempts: new FileAttemptStore(join(root, 'attempts')) };
48
50
  }
49
- function canonicalJson(value) {
50
- if (Array.isArray(value))
51
- return `[${value.map(canonicalJson).join(',')}]`;
52
- if (value && typeof value === 'object') {
53
- return `{${Object.entries(value)
54
- .sort(([left], [right]) => left.localeCompare(right))
55
- .map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
56
- .join(',')}}`;
57
- }
58
- return JSON.stringify(value);
59
- }
@@ -61,19 +61,6 @@ export async function inspectAgentRunReadModel(runStore, runtimeEventStore, opti
61
61
  diagnostics,
62
62
  };
63
63
  }
64
- export async function inspectSessionRunReadModels(runStore, runtimeEventStore, sessionId, options = {}) {
65
- const invocations = await runtimeEventStore.listSessionInvocations(sessionId);
66
- const models = [];
67
- for (const invocation of invocations) {
68
- models.push(await inspectAgentRunReadModel(runStore, runtimeEventStore, {
69
- sessionId,
70
- runId: invocation.runId,
71
- invocation,
72
- ...(options.isFatalReadError ? { isFatalReadError: options.isFatalReadError } : {}),
73
- }));
74
- }
75
- return models;
76
- }
77
64
  // A run is not its invocation: a continuation is a new run on the invocation it
78
65
  // resumes. This reader is addressed by run, so it looks the invocation up by the
79
66
  // id it was actually given.
@@ -20,12 +20,13 @@ 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 { decodeRunCompositionSnapshot } from '@maka/core/run-composition';
23
+ import { createRequestCompositionSnapshot, decodeRequestCompositionSnapshot, decodeRunCompositionSnapshot, } from '@maka/core/run-composition';
24
24
  import { DurableStoreWriteError, RunSealedError } from '@maka/core/runtime-event-store';
25
25
  import { buildInvocationOpenedEvent, buildSyntheticTerminalRuntimeEvent, isSessionInlineInvocation, } from '@maka/core/runtime-invocation';
26
26
  import { MODEL_PROJECTION_TRANSITION_EVENT_TYPE, } from '@maka/core/model-projection-transition';
27
27
  import { isTerminalRuntimeEvent } from '@maka/core/runtime-event';
28
28
  import { ToolLedgerCorruptionError, ToolLedgerRejectionError, } from '@maka/core/tool-ledger-scanner';
29
+ import { stableHash } from './request-shape.js';
29
30
  import { Buffer } from 'node:buffer';
30
31
  import { isDeepStrictEqual } from 'node:util';
31
32
  import { redactSecrets } from '@maka/core/redaction';
@@ -79,6 +80,9 @@ export class AgentRun {
79
80
  runComposition;
80
81
  runCompositionWrite;
81
82
  runCompositionCommitted = false;
83
+ requestComposition;
84
+ requestCompositionIndex;
85
+ requestCompositionIndexRead;
82
86
  failureClass;
83
87
  failureMessage;
84
88
  lastTs = 0;
@@ -418,6 +422,72 @@ export class AgentRun {
418
422
  throw new Error('Cooperative handoff requires a durably committed Run Composition');
419
423
  }
420
424
  }
425
+ /**
426
+ * Durably binds one logical model step to its effective request surface.
427
+ * Unchanged steps reuse the latest snapshot; a changed surface appends a full
428
+ * replacement before provider dispatch, matching DSH request/header epochs.
429
+ */
430
+ async recordRequestComposition(input) {
431
+ if (!this.input.runStore) {
432
+ throw new Error('AgentRun store is not configured');
433
+ }
434
+ await this.loadRequestCompositionIndex();
435
+ const snapshot = createRequestCompositionSnapshot(input, this.requestCompositionIndex?.size ? 'change' : 'initial');
436
+ const surfaceHash = requestCompositionSurfaceHash(snapshot);
437
+ const existing = this.requestCompositionIndex?.get(surfaceHash);
438
+ if (existing) {
439
+ if (!sameRequestCompositionSurface(existing, snapshot)) {
440
+ throw new Error(`Request Composition surface hash collision: ${surfaceHash}`);
441
+ }
442
+ this.requestComposition = existing;
443
+ return existing.compositionId;
444
+ }
445
+ await this.enqueueRequiredRunStoreWrite('append request composition', async () => {
446
+ await this.input.runStore?.appendEvent(this.sessionId, this.runId, {
447
+ type: 'request_composition_resolved',
448
+ id: snapshot.compositionId,
449
+ runId: this.runId,
450
+ sessionId: this.sessionId,
451
+ turnId: this.turnId,
452
+ ts: this.input.now(),
453
+ data: { snapshot },
454
+ }, { durable: true });
455
+ });
456
+ this.requestComposition = snapshot;
457
+ this.requestCompositionIndex?.set(surfaceHash, snapshot);
458
+ return snapshot.compositionId;
459
+ }
460
+ async loadRequestCompositionIndex() {
461
+ if (this.requestCompositionIndex)
462
+ return;
463
+ if (this.requestCompositionIndexRead)
464
+ return await this.requestCompositionIndexRead;
465
+ const read = (async () => {
466
+ const index = new Map();
467
+ const events = await this.input.runStore?.readEvents(this.sessionId, this.runId);
468
+ for (const event of events ?? []) {
469
+ if (event.type !== 'request_composition_resolved')
470
+ continue;
471
+ const snapshot = decodeRequestCompositionSnapshot(event.data?.snapshot);
472
+ const surfaceHash = requestCompositionSurfaceHash(snapshot);
473
+ const existing = index.get(surfaceHash);
474
+ if (existing && !sameRequestCompositionSurface(existing, snapshot)) {
475
+ throw new Error(`Request Composition surface hash collision: ${surfaceHash}`);
476
+ }
477
+ index.set(surfaceHash, existing ?? snapshot);
478
+ this.requestComposition = snapshot;
479
+ }
480
+ this.requestCompositionIndex = index;
481
+ })();
482
+ this.requestCompositionIndexRead = read;
483
+ try {
484
+ await read;
485
+ }
486
+ finally {
487
+ if (this.requestCompositionIndexRead === read)
488
+ this.requestCompositionIndexRead = undefined;
489
+ }
490
+ }
421
491
  /**
422
492
  * Canonical accounting record for one physical provider request (#1679).
423
493
  *
@@ -1573,6 +1643,15 @@ function redactTraceString(value) {
1573
1643
  function errorMessage(error) {
1574
1644
  return redactTraceString(error instanceof Error ? error.message : String(error));
1575
1645
  }
1646
+ function sameRequestCompositionSurface(current, candidate) {
1647
+ const { schemaVersion: _schemaVersion, compositionId: _compositionId, step: _step, reason: _reason, ...currentSurface } = current;
1648
+ const { schemaVersion: _candidateSchemaVersion, compositionId: _candidateCompositionId, step: _candidateStep, reason: _candidateReason, ...candidateSurface } = candidate;
1649
+ return isDeepStrictEqual(currentSurface, candidateSurface);
1650
+ }
1651
+ function requestCompositionSurfaceHash(snapshot) {
1652
+ const { schemaVersion: _schemaVersion, compositionId: _compositionId, step: _step, reason: _reason, ...surface } = snapshot;
1653
+ return stableHash(surface);
1654
+ }
1576
1655
  function isInteractionResumeAck(event) {
1577
1656
  return (event.type === 'sandbox_boundary_decision_ack' ||
1578
1657
  event.type === 'user_question_answer_ack' ||
@@ -71,7 +71,8 @@ export class AiSdkBackend {
71
71
  messageProjection;
72
72
  providerTelemetry;
73
73
  resolvedProviderOptions;
74
- toolAvailabilityRuntime;
74
+ memoryTools;
75
+ applyPatchProfile;
75
76
  /** Bounds outstanding Code Mode cells on this backend. */
76
77
  codeCellAdmission = new AdmissionLimiter(MAX_ACTIVE_CODE_MODE_CELLS);
77
78
  /**
@@ -147,6 +148,7 @@ export class AiSdkBackend {
147
148
  beforeRunProviderDispatch: input.beforeRunProviderDispatch,
148
149
  });
149
150
  const applyPatchProfile = runtime.applyPatchProfile;
151
+ this.applyPatchProfile = applyPatchProfile;
150
152
  this.messageProjection = new AiSdkMessageProjection({
151
153
  modelAdapter: this.modelAdapter,
152
154
  applyPatchProfile,
@@ -168,7 +170,7 @@ export class AiSdkBackend {
168
170
  if (input.tools.some((tool) => tool.name === MEMORY_REMEMBER_TOOL_NAME || tool.name === MEMORY_EXTRACT_TOOL_NAME)) {
169
171
  throw new Error('Long-term Memory trigger tool names are reserved by Runtime');
170
172
  }
171
- const memoryTools = input.memoryExtraction
173
+ this.memoryTools = input.memoryExtraction
172
174
  ? buildMemoryExtractionTriggerTools({
173
175
  capabilities: input.memoryExtraction,
174
176
  snapshot: (trigger, context) => this.memorySourceSnapshot(trigger, context),
@@ -182,11 +184,17 @@ export class AiSdkBackend {
182
184
  : {}),
183
185
  })
184
186
  : [];
185
- const modelTools = routeApplyPatchTools(input.tools, applyPatchProfile);
186
- this.toolAvailabilityRuntime = new ToolAvailabilityRuntime(
187
- // The archive decoder is a runtime protocol tool, not a host binding:
188
- // this session's placeholders name it, so this session advertises it.
189
- bindToolResultArchiveDecoder([...modelTools, ...memoryTools], input.toolResultArchive), input.toolAvailability, buildInvalidMakaTool());
187
+ }
188
+ snapshotToolAvailability() {
189
+ const hostTools = Object.freeze([...(this.input.resolveTools?.() ?? this.input.tools)]);
190
+ if (hostTools.some((tool) => tool.name === MEMORY_REMEMBER_TOOL_NAME || tool.name === MEMORY_EXTRACT_TOOL_NAME)) {
191
+ throw new Error('Long-term Memory trigger tool names are reserved by Runtime');
192
+ }
193
+ const modelTools = routeApplyPatchTools(hostTools, this.applyPatchProfile);
194
+ return {
195
+ hostTools,
196
+ runtime: new ToolAvailabilityRuntime(bindToolResultArchiveDecoder([...modelTools, ...this.memoryTools], this.input.toolResultArchive), this.input.toolAvailability, buildInvalidMakaTool()),
197
+ };
190
198
  }
191
199
  memorySourceSnapshot(trigger, context) {
192
200
  if (trigger !== 'remember')
@@ -255,7 +263,7 @@ export class AiSdkBackend {
255
263
  messageProjection: this.messageProjection,
256
264
  providerTelemetry: this.providerTelemetry,
257
265
  compaction: this.compaction,
258
- toolAvailabilityRuntime: this.toolAvailabilityRuntime,
266
+ snapshotToolAvailability: () => this.snapshotToolAvailability(),
259
267
  codeCellAdmission: this.codeCellAdmission,
260
268
  resolvedProviderOptions: this.resolvedProviderOptions,
261
269
  session: this.turnSessionState,
@@ -37,7 +37,8 @@ import { hasActiveToolResultPruneDiagnosticPatch, hasBlockingReplayDiagnostics,
37
37
  import { RunTrace } from './run-trace.js';
38
38
  import { REQUEST_SANDBOX_BOUNDARY_TOOL_NAME, SANDBOX_BOUNDARY_DENIED_FOR_TURN, SANDBOX_BOUNDARY_FINALIZATION_PROMPT, } from './sandbox-boundary-tool.js';
39
39
  import { buildRuntimeEventModelReplayPlan, buildSteeringEnvelope, collectToolActivityTurnIds, compatibleProviderReasoningReplayEventIds, formatTextWithInlineRefs, steeringMessagesMissingFromBase, steeringModelMessage, } from './model-history.js';
40
- import { toolSchemaCharsForDiagnostics } from './request-shape.js';
40
+ import { toolSchemaCharsForDiagnostics, requestCompositionToolSchemas, stableHash, toolCatalogHash, } from './request-shape.js';
41
+ import { toolAvailabilityHash } from './tool-availability.js';
41
42
  import { renderSwarmModePrompt } from './swarm-mode.js';
42
43
  import { renderGraphModePrompt } from './graph-mode.js';
43
44
  import { modelUsesNativeOpenAiResponses } from './model-runtime.js';
@@ -800,8 +801,8 @@ export class AiSdkTurn {
800
801
  return;
801
802
  }
802
803
  // --- Build the provider-visible schema set. Tool execution stays in Runtime. ---
803
- // One immutable runtime owns the bound search catalog and cached index.
804
- // Mutable activation belongs to this turn.
804
+ // Each logical step freezes its own scoped catalog and search projection.
805
+ // Mutable activation belongs to this turn and follows contribution identity.
805
806
  const requiredOrchestrationTools = this.orchestration.mode === 'swarm'
806
807
  ? new Set([
807
808
  'agent_list',
@@ -825,11 +826,22 @@ export class AiSdkTurn {
825
826
  throw new Error(`Invalid tool mode: ${String(requestedToolMode)}`);
826
827
  }
827
828
  const toolMode = requestedToolMode;
828
- if (toolMode === 'code_mode' && this.deps.backend.tools.some((tool) => tool.name === 'exec')) {
829
- throw new Error('Tool name "exec" is reserved for Code Mode.');
830
- }
831
- const plan = projectToolModePlan(this.deps.toolAvailabilityRuntime.prepare(this.activeTools, requiredOrchestrationTools), toolMode, codeModeExecTool);
832
- const providerTools = plan.providerTools;
829
+ const snapshotStepTools = () => {
830
+ const snapshot = this.deps.snapshotToolAvailability();
831
+ if (toolMode === 'code_mode' && snapshot.hostTools.some((tool) => tool.name === 'exec')) {
832
+ throw new Error('Tool name "exec" is reserved for Code Mode.');
833
+ }
834
+ const plan = projectToolModePlan(snapshot.runtime.prepare(this.activeTools, requiredOrchestrationTools), toolMode, codeModeExecTool);
835
+ const modelTools = {};
836
+ for (const tool of plan.providerTools) {
837
+ modelTools[tool.name] = tool.providerTool
838
+ ? { kind: 'provider', providerTool: tool.providerTool }
839
+ : { kind: 'function', description: tool.description, inputSchema: tool.parameters };
840
+ }
841
+ toolRuntime.setGating(plan.gating);
842
+ return { plan, providerTools: plan.providerTools, modelTools };
843
+ };
844
+ let { plan, providerTools, modelTools } = snapshotStepTools();
833
845
  let activeToolResultPruneDiagnosticPatch = {};
834
846
  let midTurnCompactDiagnosticPatch;
835
847
  // Tool names the repair path matches a mis-cased call against — follows the
@@ -843,44 +855,8 @@ export class AiSdkTurn {
843
855
  : [...names];
844
856
  };
845
857
  const currentRepairToolNames = () => boundaryAwareToolNames(plan.currentRepairToolNames());
846
- if (plan.gating) {
847
- toolRuntime.setGating(plan.gating);
848
- }
849
- const modelTools = {};
850
- for (const t of providerTools) {
851
- modelTools[t.name] = t.providerTool
852
- ? { kind: 'provider', providerTool: t.providerTool }
853
- : {
854
- kind: 'function',
855
- description: t.description,
856
- inputSchema: t.parameters,
857
- };
858
- }
859
- // Resolve the stable Provider envelope before automatic Compaction freezes
860
- // its source. The same value is reused by the primary request; Memory does
861
- // not resolve or mutate Agent configuration after the checkpoint commits.
858
+ let resolvedSystemPrompt = { sourceRevisions: [] };
862
859
  let systemPrompt;
863
- try {
864
- systemPrompt = joinPromptFragments([
865
- await this.resolveSystemPrompt(),
866
- this.orchestration?.mode === 'swarm' ? renderSwarmModePrompt() : undefined,
867
- this.orchestration?.mode === 'graph' ? renderGraphModePrompt() : undefined,
868
- ]);
869
- }
870
- catch (err) {
871
- trace.modelStreamFailed(this.deps.modelAdapter.classifyError(err), err);
872
- queue.push(this.makeErrorEvent(turnId, err));
873
- queue.push({
874
- type: 'complete',
875
- id: this.deps.newId(),
876
- turnId,
877
- ts: this.deps.now(),
878
- stopReason: 'error',
879
- });
880
- queue.close();
881
- yield* this.drain(queue);
882
- return;
883
- }
884
860
  // --- Build messages from RuntimeEvent history and its compatibility projection. ---
885
861
  const priorReplayResult = await this.buildPriorMessages(input);
886
862
  if (this.aborted) {
@@ -1083,7 +1059,8 @@ export class AiSdkTurn {
1083
1059
  const onMidTurnDiagnosticPatch = (patch) => {
1084
1060
  midTurnCompactDiagnosticPatch = mergeContextBudgetDiagnosticPatches(midTurnCompactDiagnosticPatch, patch);
1085
1061
  };
1086
- const midTurnCapacityHook = this.deps.compaction.buildMidTurnCapacityCompactProjection(turnId, midTurnState, queue, providerTools, onMidTurnDiagnosticPatch, this, this.automaticMemoryCompactionSupported()
1062
+ const capacityProviderTools = [...providerTools];
1063
+ const midTurnCapacityHook = this.deps.compaction.buildMidTurnCapacityCompactProjection(turnId, midTurnState, queue, capacityProviderTools, onMidTurnDiagnosticPatch, this, this.automaticMemoryCompactionSupported()
1087
1064
  ? () => this.automaticMemoryCompactionDecision()
1088
1065
  : undefined, this.automaticMemoryCompactionSupported()
1089
1066
  ? (dispatch) => this.dispatchAutomaticMemoryCompaction(dispatch)
@@ -1095,7 +1072,8 @@ export class AiSdkTurn {
1095
1072
  pruneAppliedAtStep = runtimeSteps;
1096
1073
  activeToolResultPruneDiagnosticPatch = mergeActiveToolResultPruneDiagnosticPatches(activeToolResultPruneDiagnosticPatch, patch);
1097
1074
  });
1098
- const shapedProjection = composeRequestProjection(plan.projectActiveTools, midTurnCapacityHook, activeToolResultPruneHook);
1075
+ const projectCurrentToolAvailability = (options) => plan.projectActiveTools?.(options);
1076
+ const shapedProjection = composeRequestProjection(projectCurrentToolAvailability, midTurnCapacityHook, activeToolResultPruneHook);
1099
1077
  // Hooks shape; nothing measures the final payload. Whether it fits is
1100
1078
  // the provider's answer (#4559).
1101
1079
  const requestProjection = shapedProjection;
@@ -1111,6 +1089,14 @@ export class AiSdkTurn {
1111
1089
  let finishReason = 'stop';
1112
1090
  let terminalProviderError;
1113
1091
  agentLoop: for (;;) {
1092
+ ({ plan, providerTools, modelTools } = snapshotStepTools());
1093
+ resolvedSystemPrompt = await this.resolveSystemPrompt();
1094
+ systemPrompt = joinPromptFragments([
1095
+ resolvedSystemPrompt.text,
1096
+ this.orchestration.mode === 'swarm' ? renderSwarmModePrompt() : undefined,
1097
+ this.orchestration.mode === 'graph' ? renderGraphModePrompt() : undefined,
1098
+ ]);
1099
+ capacityProviderTools.splice(0, capacityProviderTools.length, ...providerTools);
1114
1100
  await this.drainSteeringInto(input, queue);
1115
1101
  if (this.deps.backend.loadTurnRuntimeEvents) {
1116
1102
  requestMessages = await loadDurableTurnProjection();
@@ -1159,7 +1145,20 @@ export class AiSdkTurn {
1159
1145
  : undefined;
1160
1146
  const projectedMessages = shaped?.messages ?? requestMessages;
1161
1147
  const activeToolsForRequest = resolveDispatch(shaped?.activeTools).activeTools;
1162
- providerRequestTracker?.setStep(runtimeSteps);
1148
+ const requestCompositionId = this.runId && this.deps.backend.recordRequestComposition
1149
+ ? await this.deps.backend.recordRequestComposition(this.runId, {
1150
+ compositionId: this.deps.newId(),
1151
+ step: runtimeSteps,
1152
+ sourceRevisions: resolvedSystemPrompt.sourceRevisions,
1153
+ systemPromptHash: stableHash(requestSystemPrompt ?? ''),
1154
+ toolCatalogHash: toolCatalogHash(providerTools),
1155
+ toolAvailabilityHash: toolAvailabilityHash(this.deps.backend.toolAvailability),
1156
+ providerOptionsHash: stableHash(this.deps.resolvedProviderOptions),
1157
+ toolNames: activeToolsForRequest,
1158
+ toolSchemas: requestCompositionToolSchemas(providerTools, activeToolsForRequest),
1159
+ })
1160
+ : undefined;
1161
+ providerRequestTracker?.setStep(runtimeSteps, requestCompositionId);
1163
1162
  let attemptMessages = projectedMessages;
1164
1163
  let providerAttempt = 1;
1165
1164
  let idleWatchdogRetryCount = 0;
@@ -2488,14 +2487,22 @@ export class AiSdkTurn {
2488
2487
  async resolveSystemPrompt() {
2489
2488
  const turnId = this.turnId;
2490
2489
  if (typeof this.deps.backend.systemPrompt === 'function') {
2491
- return await this.deps.backend.systemPrompt({
2490
+ const resolved = await this.deps.backend.systemPrompt({
2492
2491
  sessionId: this.deps.backend.sessionId,
2493
2492
  turnId,
2494
2493
  cwd: this.deps.backend.header.cwd,
2495
2494
  emitSkillCatalogTrace: (message, data) => this.runTrace?.emit('skill', 'skill_catalog_built', message, data),
2496
2495
  });
2496
+ return typeof resolved === 'string' || resolved === undefined
2497
+ ? { ...(resolved === undefined ? {} : { text: resolved }), sourceRevisions: [] }
2498
+ : resolved;
2497
2499
  }
2498
- return this.deps.backend.systemPrompt;
2500
+ return {
2501
+ ...(this.deps.backend.systemPrompt === undefined
2502
+ ? {}
2503
+ : { text: this.deps.backend.systemPrompt }),
2504
+ sourceRevisions: [],
2505
+ };
2499
2506
  }
2500
2507
  async *drain(queue) {
2501
2508
  try {
@@ -25,7 +25,7 @@ export function buildArchiveReadTool(reader) {
25
25
  .object({
26
26
  ref: z
27
27
  .string()
28
- .describe('A maka://archive/... ref returned in an archived tool-result placeholder'),
28
+ .describe('The exact resourceRef returned in an archived tool-result placeholder (ledger or legacy archive)'),
29
29
  operation: z
30
30
  .enum(['inspect', 'read', 'query', 'search'])
31
31
  .default('inspect')
@@ -82,7 +82,7 @@ export function buildArchiveReadTool(reader) {
82
82
  name: ARCHIVE_READ_TOOL_NAME,
83
83
  displayName: 'Read archived result',
84
84
  activityKind: 'read',
85
- description: 'Inspect, search, or page through a tool-result archive returned as a maka://archive/... ref. Start with inspect for a preview and the char/line coordinate space. Use operation "search" with a pattern to locate text, operation "read" with unit "line" for line-oriented terminal output, or operation "query" with an itemId for one agent_swarm item. Results are strictly bounded so reading an archive cannot immediately trigger another archive.',
85
+ description: 'Inspect, search, or page through a tool-result archive using its exact resourceRef. Both ledger and legacy archive references are supported. Start with inspect for a preview and the char/line coordinate space. Use operation "search" with a pattern to locate text, operation "read" with unit "line" for line-oriented terminal output, or operation "query" with an itemId for one agent_swarm item. Results are strictly bounded so reading an archive cannot immediately trigger another archive.',
86
86
  parameters: jsonSchema(async () => await providerSchema.jsonSchema, {
87
87
  validate: async (value) => {
88
88
  const result = await parameters.safeParseAsync(value);
@@ -46,22 +46,9 @@ export function stableJsonLength(value) {
46
46
  export function turnKey(event) {
47
47
  return event.turnId || '<unknown-turn>';
48
48
  }
49
- export function uniqueSorted(values) {
50
- return [...new Set(values.filter((value) => value.length > 0))].sort();
51
- }
52
49
  export function sha256(text) {
53
50
  return createHash('sha256').update(text).digest('hex');
54
51
  }
55
- export function stableStringify(value) {
56
- if (value === undefined)
57
- return '';
58
- try {
59
- return JSON.stringify(value) ?? '';
60
- }
61
- catch {
62
- return String(value);
63
- }
64
- }
65
52
  export function finitePositive(value) {
66
53
  return typeof value === 'number' && Number.isFinite(value) && value > 0
67
54
  ? Math.floor(value)
@@ -70,6 +57,3 @@ export function finitePositive(value) {
70
57
  export function utf8ByteLength(text) {
71
58
  return Buffer.byteLength(text, 'utf8');
72
59
  }
73
- export function optionalNonNegativeFiniteNumber(value) {
74
- return value === undefined || (typeof value === 'number' && Number.isFinite(value) && value >= 0);
75
- }