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

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 (148) hide show
  1. package/dist/acp/maka-acp-agent.js +2 -1
  2. package/dist/acp/session-configuration.js +143 -0
  3. package/dist/acp/session-registry.js +90 -4
  4. package/dist/cli-core.js +2 -0
  5. package/dist/pi-transcript.js +5 -12
  6. package/dist/pi-tui-runner.js +74 -4
  7. package/dist/runtime-host-cli-context.js +167 -78
  8. package/dist/runtime-host-cli-installation.js +8 -2
  9. package/dist/runtime-host-cli.js +31 -1
  10. package/dist/runtime-host-handoff-surface.js +81 -0
  11. package/dist/runtime-host-installed-update-bootstrap.js +13 -0
  12. package/dist/runtime-host-installed-update-coordinator.js +33 -0
  13. package/dist/runtime-host-lifecycle-transaction.js +9 -1
  14. package/dist/runtime-host-local-handoff.js +37 -4
  15. package/dist/runtime-host-local-managed-activation.js +84 -0
  16. package/dist/runtime-host-local-source-retirement.js +18 -2
  17. package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
  18. package/dist/runtime-host-operator-retirement.js +50 -0
  19. package/dist/runtime-host-service-manager.js +3 -0
  20. package/dist/runtime-host-session-driver.js +4 -25
  21. package/dist/runtime-host-session-update.js +62 -0
  22. package/dist/runtime-host-tui-command.js +38 -73
  23. package/dist/runtime-host-tui-context.js +1 -0
  24. package/dist/runtime-host-tui-owner.js +226 -0
  25. package/dist/tui-copy-catalog.js +140 -3
  26. package/dist/tui-host-owner-copy.js +24 -0
  27. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  28. package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
  29. package/node_modules/@maka/core/dist/model-failure.js +40 -0
  30. package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
  31. package/node_modules/@maka/core/dist/model-metadata.js +3 -3
  32. package/node_modules/@maka/core/dist/model-web-search.js +5 -4
  33. package/node_modules/@maka/core/dist/provider-registry.js +45 -4
  34. package/node_modules/@maka/core/dist/record-schema.js +9 -2
  35. package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
  36. package/node_modules/@maka/core/dist/runtime-event.js +37 -6
  37. package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
  38. package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
  39. package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
  40. package/node_modules/@maka/core/dist/session.js +54 -52
  41. package/node_modules/@maka/core/dist/slash-command-catalog.js +2 -0
  42. package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
  43. package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
  44. package/node_modules/@maka/core/package.json +4 -0
  45. package/node_modules/@maka/mcp/dist/index.js +131 -1
  46. package/node_modules/@maka/runtime/dist/agent-run.js +272 -105
  47. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +13 -5
  48. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
  49. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +127 -133
  50. package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
  51. package/node_modules/@maka/runtime/dist/conversation-copy.js +5 -4
  52. package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
  53. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
  54. package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
  55. package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
  56. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +106 -0
  57. package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
  58. package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
  59. package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
  60. package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
  61. package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
  62. package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
  63. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
  64. package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
  65. package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
  66. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +68 -108
  67. package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
  68. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +82 -54
  69. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +117 -159
  70. package/node_modules/@maka/runtime/dist/runtime-kernel.js +150 -112
  71. package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
  72. package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
  73. package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
  74. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
  75. package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
  76. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
  77. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
  78. package/node_modules/@maka/runtime/dist/test-connection.js +16 -16
  79. package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
  80. package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
  81. package/node_modules/@maka/runtime/package.json +1 -0
  82. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
  83. package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
  84. package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
  85. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +156 -0
  86. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +273 -0
  87. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
  88. package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
  89. package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
  90. package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
  91. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +27 -5
  92. package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
  93. package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
  94. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
  95. package/node_modules/@maka/runtime-host/dist/protocol/index.js +25 -1
  96. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  97. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
  98. package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
  99. package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
  100. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
  101. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
  102. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
  103. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
  104. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +100 -24
  105. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
  106. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +3 -2
  107. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
  108. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
  109. package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
  110. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
  111. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
  112. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
  113. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
  114. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
  115. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
  116. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +50 -7
  117. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
  118. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +8 -55
  119. package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
  120. package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
  121. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
  122. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
  123. package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
  124. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +0 -1
  125. package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
  126. package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
  127. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -4
  128. package/node_modules/@maka/storage/dist/codex-session-adapter.js +0 -2
  129. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
  130. package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
  131. package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
  132. package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
  133. package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
  134. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  135. package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
  136. package/node_modules/@maka/storage/dist/session-bundle-policy.js +24 -4
  137. package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
  138. package/node_modules/@maka/storage/dist/session-repository.js +767 -0
  139. package/node_modules/@maka/storage/dist/session-store.js +25 -99
  140. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
  141. package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
  142. package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +7 -1
  143. package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
  144. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +129 -25
  145. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
  146. package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +111 -0
  147. package/node_modules/@maka/storage/package.json +2 -0
  148. package/package.json +1 -1
@@ -116,12 +116,12 @@ if (zenmux.api !== 'https://zenmux.ai/api/v1') {
116
116
  throw new Error('models.dev ZenMux provider facts are missing the official OpenAI-compatible API');
117
117
  }
118
118
  const zenmuxModelProviderOverrides = GENERATED_MODELS_DEV_MODEL_PROVIDER_OVERRIDES.zenmux;
119
- if (zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.npm !== '@ai-sdk/anthropic' ||
120
- zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.api !==
119
+ if (zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.adapter.kind !== 'anthropic' ||
120
+ zenmuxModelProviderOverrides['anthropic/claude-sonnet-4.6']?.baseUrl !==
121
121
  'https://zenmux.ai/api/anthropic/v1') {
122
122
  throw new Error('models.dev ZenMux snapshot is missing its Anthropic model-level protocol override');
123
123
  }
124
- if (zenmuxModelProviderOverrides['openai/gpt-5.4']?.npm !== '@ai-sdk/openai') {
124
+ if (zenmuxModelProviderOverrides['openai/gpt-5.4']?.adapter.kind !== 'openai') {
125
125
  throw new Error('models.dev ZenMux snapshot is missing its native OpenAI model-level protocol override');
126
126
  }
127
127
  const zenmuxOpenAICompatibleMetadata = Object.fromEntries(Object.entries(GENERATED_MODELS_DEV_METADATA.zenmux).filter(([id]) => zenmuxModelProviderOverrides[id] === undefined));
@@ -505,6 +505,14 @@ const providerRegistry = {
505
505
  fallbackModels: [...kimiCodingPlanModelIds],
506
506
  status: 'ready',
507
507
  runtimeAdapter: { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
508
+ protocolAdapters: {
509
+ 'openai-chat': {
510
+ kind: 'openai-compatible',
511
+ name: 'provider',
512
+ normalizeUsage: true,
513
+ normalizeBaseUrl: true,
514
+ },
515
+ },
508
516
  modelDiscovery: { kind: 'protocol' },
509
517
  category: 'domestic',
510
518
  catalogGroup: 'plans',
@@ -891,6 +899,10 @@ const providerRegistry = {
891
899
  fallbackModels: opencodeModelIds,
892
900
  status: 'ready',
893
901
  runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
902
+ protocolAdapters: {
903
+ 'anthropic-messages': { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
904
+ 'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
905
+ },
894
906
  modelDiscovery: { kind: 'protocol' },
895
907
  category: 'overseas',
896
908
  catalogGroup: 'plans',
@@ -904,6 +916,10 @@ const providerRegistry = {
904
916
  fallbackModels: opencodeGoModelIds,
905
917
  status: 'ready',
906
918
  runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
919
+ protocolAdapters: {
920
+ 'anthropic-messages': { kind: 'anthropic', auth: 'api-key', normalizeBaseUrl: true },
921
+ 'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
922
+ },
907
923
  modelDiscovery: { kind: 'protocol' },
908
924
  category: 'overseas',
909
925
  catalogGroup: 'plans',
@@ -1182,6 +1198,22 @@ const providerRegistry = {
1182
1198
  signupUrl: 'https://modelstudio.console.alibabacloud.com/',
1183
1199
  catalogOrder: 41.4,
1184
1200
  },
1201
+ commandcode: {
1202
+ label: 'Command Code',
1203
+ baseUrl: 'https://api.commandcode.ai/provider/v1',
1204
+ authKind: 'api_key',
1205
+ fallbackModels: [],
1206
+ status: 'ready',
1207
+ runtimeAdapter: { kind: 'openai-compatible', name: 'provider' },
1208
+ protocolAdapters: {
1209
+ 'anthropic-messages': { kind: 'anthropic', auth: 'bearer', normalizeBaseUrl: true },
1210
+ },
1211
+ modelDiscovery: { kind: 'protocol', modelProtocols: 'commandcode' },
1212
+ category: 'overseas',
1213
+ catalogGroup: 'plans',
1214
+ signupUrl: 'https://commandcode.ai/docs/plans/goat',
1215
+ catalogOrder: 41.5,
1216
+ },
1185
1217
  'cloudflare-workers-ai': {
1186
1218
  label: cloudflareWorkersAi.name,
1187
1219
  baseUrl: '',
@@ -1299,7 +1331,16 @@ const providerRegistry = {
1299
1331
  authKind: 'oauth_token',
1300
1332
  fallbackModels: githubCopilotModelIds,
1301
1333
  status: 'ready',
1302
- runtimeAdapter: { kind: 'github-copilot' },
1334
+ runtimeAdapter: { kind: 'openai-compatible', name: 'provider', includeUsage: false },
1335
+ protocolAdapters: {
1336
+ 'anthropic-messages': {
1337
+ kind: 'anthropic',
1338
+ auth: 'bearer',
1339
+ normalizeBaseUrl: true,
1340
+ includeBetaHeaders: false,
1341
+ },
1342
+ 'openai-responses': { kind: 'openai', apiProtocol: 'openai-responses' },
1343
+ },
1303
1344
  modelDiscovery: { kind: 'protocol', auth: 'github-copilot' },
1304
1345
  category: 'oauth',
1305
1346
  signupUrl: 'https://github.com/features/copilot/plans',
@@ -19,11 +19,18 @@
19
19
  /**
20
20
  * Defines a JSON object shape while making schema additions a type error until
21
21
  * both the required and optional key lists are updated.
22
+ *
23
+ * `retired` names keys older writers persisted that this type no longer has.
24
+ * They are accepted on read and dropped by {@link pickShape}, so what the shape
25
+ * emits may shrink freely while what it accepts only grows. Removing a key from
26
+ * `optional` without listing it here makes every stored record carrying it fail
27
+ * validation outright.
22
28
  */
23
29
  export function defineObjectShape() {
24
- return (required, optional) => ({
30
+ return (required, optional, retired = []) => ({
25
31
  required,
26
32
  allowed: new Set([...required, ...optional]),
33
+ retired: new Set(retired),
27
34
  });
28
35
  }
29
36
  export function isRecord(value) {
@@ -31,7 +38,7 @@ export function isRecord(value) {
31
38
  }
32
39
  export function hasExactShape(value, shape) {
33
40
  return (shape.required.every((key) => Object.hasOwn(value, key)) &&
34
- Object.keys(value).every((key) => shape.allowed.has(key)));
41
+ Object.keys(value).every((key) => shape.allowed.has(key) || shape.retired?.has(key) === true));
35
42
  }
36
43
  /**
37
44
  * Narrows a record to the keys a shape allows. `undefined` entries are dropped
@@ -65,6 +65,8 @@ export function createRuntimeBoundaryCursor(segments) {
65
65
  const invocationIds = new Set();
66
66
  const runIds = new Set();
67
67
  const turnIds = new Set();
68
+ let previousTurnId;
69
+ let protocol = 'runtime_boundary_cursor_v1';
68
70
  for (const segment of canonicalSegments) {
69
71
  if (segment.identity.sessionId !== sessionId) {
70
72
  throw new Error('Runtime boundary segments must belong to the same session');
@@ -78,24 +80,30 @@ export function createRuntimeBoundaryCursor(segments) {
78
80
  }
79
81
  invocationIds.add(segment.identity.invocationId);
80
82
  if (turnIds.has(segment.identity.turnId)) {
81
- throw new Error('Runtime boundary lineage contains a duplicate turnId');
83
+ if (previousTurnId !== segment.identity.turnId) {
84
+ throw new Error('Runtime boundary lineage returns to a previous turnId');
85
+ }
86
+ protocol = 'runtime_boundary_cursor_v2';
82
87
  }
83
88
  turnIds.add(segment.identity.turnId);
89
+ previousTurnId = segment.identity.turnId;
84
90
  }
85
91
  return {
86
- protocol: 'runtime_boundary_cursor_v1',
92
+ protocol,
87
93
  segments: canonicalSegments,
88
- manifestDigest: digestRuntimeBoundaryManifest(canonicalSegments),
94
+ manifestDigest: digestRuntimeBoundaryManifest(canonicalSegments, protocol),
89
95
  };
90
96
  }
91
- export function digestRuntimeBoundaryManifest(segments) {
97
+ export function digestRuntimeBoundaryManifest(segments, protocol = 'runtime_boundary_cursor_v1') {
92
98
  const canonicalSegments = segments.map(decodeRuntimePrefixSegment);
93
99
  const json = stableJsonStringify({
94
- protocol: 'runtime_boundary_cursor_v1',
100
+ protocol,
95
101
  segments: canonicalSegments,
96
102
  });
97
103
  const hash = nodeCrypto.createHash('sha256');
98
- updateLengthPrefixed(hash, Buffer.from('maka.runtime-boundary-manifest.v1', 'utf8'));
104
+ updateLengthPrefixed(hash, Buffer.from(protocol === 'runtime_boundary_cursor_v1'
105
+ ? 'maka.runtime-boundary-manifest.v1'
106
+ : 'maka.runtime-boundary-manifest.v2', 'utf8'));
99
107
  updateLengthPrefixed(hash, Buffer.from(json, 'utf8'));
100
108
  return `sha256:${hash.digest('hex')}`;
101
109
  }
@@ -115,13 +123,16 @@ export function decodeRuntimePrefixSegment(value) {
115
123
  export function decodeRuntimeBoundaryCursor(value) {
116
124
  if (!isRecord(value) ||
117
125
  !hasExactKeys(value, ['protocol', 'segments', 'manifestDigest']) ||
118
- value.protocol !== 'runtime_boundary_cursor_v1' ||
126
+ (value.protocol !== 'runtime_boundary_cursor_v1' &&
127
+ value.protocol !== 'runtime_boundary_cursor_v2') ||
119
128
  !Array.isArray(value.segments) ||
120
129
  value.segments.length === 0) {
121
130
  throw new Error('Invalid RuntimeEvent boundary cursor');
122
131
  }
123
132
  const segments = value.segments.map(decodeRuntimePrefixSegment);
124
133
  const cursor = createRuntimeBoundaryCursor(segments);
134
+ if (cursor.protocol !== value.protocol)
135
+ throw new Error('RuntimeEvent boundary cursor version mismatch');
125
136
  const manifestDigest = decodeBoundaryDigest(value.manifestDigest);
126
137
  if (cursor.manifestDigest !== manifestDigest) {
127
138
  throw new Error('RuntimeEvent boundary manifest digest mismatch');
@@ -173,12 +184,21 @@ export function decodeContinuationClaim(value) {
173
184
  throw new Error('Continuation claim target invocationId reuses source identity');
174
185
  }
175
186
  const targetTurnId = value.target.turnId;
176
- if (boundary.segments.some((segment) => segment.identity.turnId === targetTurnId)) {
177
- throw new Error('Continuation claim target turnId reuses source identity');
178
- }
179
187
  const targetOpening = decodeRuntimeInvocationOpened(value.targetOpening);
180
188
  const openSource = targetOpening.source;
181
- if (openSource.kind !== 'continuation' ||
189
+ if (openSource.kind === 'handoff') {
190
+ if (targetTurnId !== source.identity.turnId) {
191
+ throw new Error('Handoff claim must preserve the logical turnId');
192
+ }
193
+ const root = boundary.segments.find((segment) => segment.identity.turnId === targetTurnId);
194
+ if (root?.identity.runId !== openSource.rootRunId) {
195
+ throw new Error('Handoff claim logical root mismatch');
196
+ }
197
+ }
198
+ else if (boundary.segments.some((segment) => segment.identity.turnId === targetTurnId)) {
199
+ throw new Error('Continuation claim target turnId reuses source identity');
200
+ }
201
+ if ((openSource.kind !== 'continuation' && openSource.kind !== 'handoff') ||
182
202
  openSource.claimId !== value.claimId ||
183
203
  openSource.boundaryDigest !== boundaryDigest ||
184
204
  openSource.sourceInvocationId !== source.identity.invocationId ||
@@ -31,12 +31,15 @@
31
31
  * Phase 1 scope: types + small pure helpers only. No storage, runner,
32
32
  * projection, or ledger logic lives here. Those arrive in later nodes.
33
33
  */
34
+ import { isModelRetryDecision } from './model-failure.js';
35
+ import { isRuntimeHandoffPause, runtimeHandoffPause, } from './runtime-handoff.js';
34
36
  import { isMessageContent, normalizeMessageContent, } from './events.js';
35
37
  import { INTERACTION_ID_MAX_BYTES, INTERACTION_TOOL_NAME_MAX_BYTES, decodeInteractionRequest, } from './interaction.js';
36
38
  import { isPermissionMode, } from './permission.js';
37
39
  import { isCollaborationMode } from './collaboration.js';
38
40
  import { isAgentSwarmAuthorizationSource, isEffectiveOrchestrationSource, isOrchestrationMode, } from './orchestration.js';
39
41
  import { isToolMode } from './tool-mode.js';
42
+ import { isRuntimeSystemNoteKind, } from './session.js';
40
43
  import { decodeTurnOrigin } from './turn-origin.js';
41
44
  import { defineObjectShape, hasExactShape, isFiniteNumber, isOptionalMember, isOptionalString, isRecord, isStringArray, } from './record-schema.js';
42
45
  import { isPermissionDecisionFields, isPermissionRequestPayload, isUserQuestionRequest, } from './interaction-record-schema.js';
@@ -106,6 +109,7 @@ export const RUNTIME_EVENT_CONTENT_KINDS = [
106
109
  'function_call',
107
110
  'function_response',
108
111
  'error',
112
+ 'system_note',
109
113
  'invocation_opened',
110
114
  ];
111
115
  export const TOOL_BOUNDARY_PROTOCOL_V1 = 't1_after_preflight_v1';
@@ -178,7 +182,8 @@ const TEXT_CONTENT_SHAPE = defineObjectShape()(['kind', 'text'], [
178
182
  const THINKING_CONTENT_SHAPE = defineObjectShape()(['kind', 'text'], ['signature', 'providerOptions']);
179
183
  const FUNCTION_CALL_CONTENT_SHAPE = defineObjectShape()(['kind', 'id', 'name', 'args'], ['providerOptions', 'providerExecuted']);
180
184
  const FUNCTION_RESPONSE_CONTENT_SHAPE = defineObjectShape()(['kind', 'id', 'name', 'result'], ['isError', 'providerExecuted', 'providerOutput', 'modelProjection']);
181
- const ERROR_CONTENT_SHAPE = defineObjectShape()(['kind', 'message'], ['code', 'reason', 'details']);
185
+ const ERROR_CONTENT_SHAPE = defineObjectShape()(['kind', 'message'], ['code', 'reason', 'details', 'retry']);
186
+ const SYSTEM_NOTE_CONTENT_SHAPE = defineObjectShape()(['kind', 'note'], ['data']);
182
187
  const INVOCATION_OPENED_CONTENT_SHAPE = defineObjectShape()(['kind', 'protocol', 'route', 'configuration', 'root', 'source'], ['lineage']);
183
188
  const INVOCATION_ROUTE_RUNTIME_SHAPE = defineObjectShape()(['provenance', 'backendKind', 'llmConnectionId', 'llmConnectionSlug', 'modelId'], ['providerStateIdentity']);
184
189
  const INVOCATION_ROUTE_UNKNOWN_SHAPE = defineObjectShape()(['provenance', 'backendKind', 'llmConnectionSlug', 'modelId'], []);
@@ -204,6 +209,16 @@ const INVOCATION_LINEAGE_SHAPE = defineObjectShape()([], [
204
209
  ]);
205
210
  const INVOCATION_CONTINUATION_SOURCE_SHAPE = defineObjectShape()(['kind', 'sourceInvocationId', 'sourceRunId', 'sourceTurnId', 'sourceRuntimeEventHighWater'], ['claimId', 'boundaryDigest']);
206
211
  const INVOCATION_FRESH_SOURCE_SHAPE = defineObjectShape()(['kind'], []);
212
+ const INVOCATION_HANDOFF_SOURCE_SHAPE = defineObjectShape()([
213
+ 'kind',
214
+ 'rootRunId',
215
+ 'sourceInvocationId',
216
+ 'sourceRunId',
217
+ 'sourceTurnId',
218
+ 'sourceRuntimeEventHighWater',
219
+ 'claimId',
220
+ 'boundaryDigest',
221
+ ], []);
207
222
  const INVOCATION_ROOT_SHAPES = {
208
223
  user: defineObjectShape()(['kind'], []),
209
224
  context_compact: defineObjectShape()(['kind'], []),
@@ -213,6 +228,7 @@ const INVOCATION_ROOT_SHAPES = {
213
228
  legacy_automation: defineObjectShape()(['kind', 'legacyAutomationId'], []),
214
229
  };
215
230
  const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
231
+ 'handoffPause',
216
232
  'stateDelta',
217
233
  'artifactDelta',
218
234
  'permissionRequest',
@@ -236,7 +252,7 @@ const RUNTIME_ACTIONS_SHAPE = defineObjectShape()([], [
236
252
  const RUNTIME_MANAGED_MUTATION_TERMINAL_SHAPE = defineObjectShape()(['protocol', 'operationId', 'dispatchEventId', 'workspaceInstanceId', 'terminalKind'], []);
237
253
  const ANSWER_ACCEPTED_IDENTITY_SHAPE = defineObjectShape()(['requestId'], []);
238
254
  const PERMISSION_CLOSURE_ACCEPTED_SHAPE = defineObjectShape()(['requestId', 'reason'], []);
239
- const RUNTIME_PERMISSION_DECISION_SHAPE = defineObjectShape()(['requestId', 'decision'], ['rememberForTurn', 'reviewer', 'rationale', 'riskLevel', 'toolName']);
255
+ const RUNTIME_PERMISSION_DECISION_SHAPE = defineObjectShape()(['requestId', 'decision'], ['rememberForTurn', 'reviewer', 'rationale', 'riskLevel', 'toolName', 'hint']);
240
256
  const UTF8 = new TextEncoder();
241
257
  const RUNTIME_TOOL_DISPATCH_SHAPE = defineObjectShape()([
242
258
  'protocol',
@@ -341,6 +357,7 @@ export function decodeRuntimeEvent(value) {
341
357
  (value.refs !== undefined && !isRuntimeEventRefs(value.refs))) {
342
358
  throw new Error('Invalid RuntimeEvent schema');
343
359
  }
360
+ runtimeHandoffPause(value);
344
361
  if (isRecord(value.content) && value.content.kind === 'text') {
345
362
  return {
346
363
  ...value,
@@ -415,8 +432,13 @@ function isRuntimeEventContent(value) {
415
432
  return (hasExactShape(value, ERROR_CONTENT_SHAPE) &&
416
433
  isOptionalString(value.code) &&
417
434
  isOptionalString(value.reason) &&
435
+ (value.retry === undefined || isModelRetryDecision(value.retry)) &&
418
436
  typeof value.message === 'string' &&
419
437
  (value.details === undefined || isStringArray(value.details) || isRecord(value.details)));
438
+ case 'system_note':
439
+ return (hasExactShape(value, SYSTEM_NOTE_CONTENT_SHAPE) &&
440
+ typeof value.note === 'string' &&
441
+ isRuntimeSystemNoteKind(value.note));
420
442
  case 'invocation_opened':
421
443
  return isRuntimeInvocationOpened(value);
422
444
  default:
@@ -508,8 +530,14 @@ function isRuntimeInvocationOpenSource(value) {
508
530
  return false;
509
531
  if (value.kind === 'fresh')
510
532
  return hasExactShape(value, INVOCATION_FRESH_SOURCE_SHAPE);
511
- return (value.kind === 'continuation' &&
512
- hasExactShape(value, INVOCATION_CONTINUATION_SOURCE_SHAPE) &&
533
+ return (((value.kind === 'continuation' &&
534
+ hasExactShape(value, INVOCATION_CONTINUATION_SOURCE_SHAPE)) ||
535
+ (value.kind === 'handoff' &&
536
+ hasExactShape(value, INVOCATION_HANDOFF_SOURCE_SHAPE) &&
537
+ isNonEmptyString(value.rootRunId) &&
538
+ isNonEmptyString(value.claimId) &&
539
+ isSha256Digest(value.boundaryDigest) &&
540
+ value.sourceRuntimeEventHighWater > 0)) &&
513
541
  isNonEmptyString(value.sourceInvocationId) &&
514
542
  isNonEmptyString(value.sourceRunId) &&
515
543
  isNonEmptyString(value.sourceTurnId) &&
@@ -554,7 +582,8 @@ function isRuntimeEventActions(value) {
554
582
  Object.keys(value).some((key) => key !== 'permissionClosureAccepted')) {
555
583
  return false;
556
584
  }
557
- return ((value.stateDelta === undefined || isRecord(value.stateDelta)) &&
585
+ return ((value.handoffPause === undefined || isRuntimeHandoffPause(value.handoffPause)) &&
586
+ (value.stateDelta === undefined || isRecord(value.stateDelta)) &&
558
587
  (value.artifactDelta === undefined ||
559
588
  (isRecord(value.artifactDelta) &&
560
589
  Object.values(value.artifactDelta).every((item) => typeof item === 'string' || typeof item === 'boolean' || isFiniteNumber(item)))) &&
@@ -621,7 +650,8 @@ function isRuntimeEventPermissionDecision(value) {
621
650
  (value.toolName === undefined ||
622
651
  (typeof value.toolName === 'string' &&
623
652
  value.toolName.length > 0 &&
624
- UTF8.encode(value.toolName).byteLength <= INTERACTION_TOOL_NAME_MAX_BYTES)));
653
+ UTF8.encode(value.toolName).byteLength <= INTERACTION_TOOL_NAME_MAX_BYTES)) &&
654
+ isOptionalString(value.hint));
625
655
  }
626
656
  function isRuntimeEventAnswerAcceptedIdentity(value) {
627
657
  return (isRecord(value) &&
@@ -804,6 +834,7 @@ export function runtimeEventHasModelVisibleContent(event) {
804
834
  case 'function_response':
805
835
  return true;
806
836
  case 'error':
837
+ case 'system_note':
807
838
  case 'invocation_opened':
808
839
  return false;
809
840
  }
@@ -0,0 +1,98 @@
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 { defineObjectShape, hasExactShape, isRecord } from './record-schema.js';
20
+ import { stableJsonStringify } from './tool-args-identity.js';
21
+ const PAUSE_SHAPE = defineObjectShape()([
22
+ 'protocol',
23
+ 'handoffId',
24
+ 'hostEpoch',
25
+ 'rootRunId',
26
+ 'successorRunId',
27
+ 'successorInvocationId',
28
+ 'claimId',
29
+ 'remainingSteps',
30
+ ], []);
31
+ export function isRuntimeHandoffPause(value) {
32
+ return (isRecord(value) &&
33
+ hasExactShape(value, PAUSE_SHAPE) &&
34
+ value.protocol === 'runtime_handoff_pause_v1' &&
35
+ (value.remainingSteps === null ||
36
+ (Number.isSafeInteger(value.remainingSteps) && value.remainingSteps > 0)) &&
37
+ [
38
+ value.handoffId,
39
+ value.hostEpoch,
40
+ value.rootRunId,
41
+ value.successorRunId,
42
+ value.successorInvocationId,
43
+ value.claimId,
44
+ ].every((id) => typeof id === 'string' &&
45
+ id.length > 0 &&
46
+ id.length <= 256 &&
47
+ !/[\u0000-\u001f\u007f]/u.test(id)));
48
+ }
49
+ /** The seal cannot simultaneously publish content or claim a logical outcome. */
50
+ export function runtimeHandoffPause(event) {
51
+ const pause = event.actions?.handoffPause;
52
+ if (pause === undefined)
53
+ return undefined;
54
+ if (!isRuntimeHandoffPause(pause) ||
55
+ event.partial !== false ||
56
+ event.role !== 'system' ||
57
+ (event.author !== 'host' && event.author !== 'system') ||
58
+ event.status !== undefined ||
59
+ event.content !== undefined ||
60
+ event.actions?.endInvocation !== true ||
61
+ Object.keys(event.actions).some((key) => key !== 'endInvocation' && key !== 'handoffPause') ||
62
+ pause.successorRunId === event.runId ||
63
+ pause.successorRunId === event.invocationId ||
64
+ pause.successorInvocationId === event.invocationId ||
65
+ pause.successorRunId === pause.rootRunId) {
66
+ throw new Error('Invalid Runtime handoff pause seal');
67
+ }
68
+ return pause;
69
+ }
70
+ /** Verify against source facts, not the proposed claim's description of them. */
71
+ export function assertHandoffClaimSource(claim, prefix) {
72
+ const last = prefix.events.at(-1);
73
+ const pause = last && runtimeHandoffPause(last);
74
+ const source = claim.targetOpening.source;
75
+ if (source.kind !== 'handoff') {
76
+ if (pause)
77
+ throw new Error('A handoff pause is reserved for its authorized successor');
78
+ return;
79
+ }
80
+ const opening = prefix.events[0]?.content;
81
+ if (!pause ||
82
+ opening?.kind !== 'invocation_opened' ||
83
+ prefix.identity.sessionId !== claim.target.sessionId ||
84
+ prefix.identity.turnId !== claim.target.turnId ||
85
+ pause.claimId !== claim.claimId ||
86
+ pause.successorRunId !== claim.target.runId ||
87
+ pause.successorInvocationId !== claim.target.invocationId ||
88
+ pause.rootRunId !== source.rootRunId ||
89
+ pause.rootRunId !==
90
+ (opening.source.kind === 'handoff' ? opening.source.rootRunId : prefix.identity.runId) ||
91
+ stableJsonStringify(opening.root) !== stableJsonStringify(claim.targetOpening.root) ||
92
+ stableJsonStringify(opening.configuration) !==
93
+ stableJsonStringify(claim.targetOpening.configuration) ||
94
+ stableJsonStringify(opening.lineage ?? null) !==
95
+ stableJsonStringify(claim.targetOpening.lineage ?? null)) {
96
+ throw new Error('Handoff claim does not preserve the sealed source authority');
97
+ }
98
+ }
@@ -149,7 +149,7 @@ export function runtimeInvocationOutcome(record) {
149
149
  export function isSessionInlineInvocation(opening) {
150
150
  const lineage = opening.lineage;
151
151
  return (lineage?.parentRunId === undefined ||
152
- (opening.source.kind === 'continuation' && lineage.agentId === undefined));
152
+ (opening.source.kind !== 'fresh' && lineage.agentId === undefined));
153
153
  }
154
154
  /**
155
155
  * Is this invocation the one the Host admitted for that root execution?
@@ -0,0 +1,112 @@
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 { continuationStartEventMatchesClaim, createRuntimeBoundaryCursor, invocationMatchesClaimTarget, runtimePrefixSegment, } from './runtime-boundary.js';
20
+ import { readRunInvocation, } from './runtime-event-store.js';
21
+ import { assertHandoffClaimSource, runtimeHandoffPause, } from './runtime-handoff.js';
22
+ /** All logical snapshot and command routing paths share these edge checks. */
23
+ export async function readLogicalRuntimeExecution(store, identity, knownRoot) {
24
+ const root = knownRoot ?? (await readRunInvocation(store, identity.sessionId, identity.runId));
25
+ if (!root)
26
+ return undefined;
27
+ if (root.sessionId !== identity.sessionId ||
28
+ root.runId !== identity.runId ||
29
+ root.turnId !== identity.turnId ||
30
+ root.opening.source.kind === 'handoff') {
31
+ throw new Error('Logical execution reference does not name its original admission');
32
+ }
33
+ let tip = root;
34
+ let segments;
35
+ const seen = new Set();
36
+ for (;;) {
37
+ if (seen.has(tip.runId) || seen.size >= 64)
38
+ throw new Error('Invalid logical execution handoff lineage');
39
+ seen.add(tip.runId);
40
+ const events = await store.readImmutableRuntimeEvents(identity.sessionId, tip.runId);
41
+ const last = events.at(-1);
42
+ const pause = last && runtimeHandoffPause(last);
43
+ if (!pause)
44
+ return { root, tip, events, runIds: [...seen] };
45
+ if (pause.rootRunId !== root.runId)
46
+ throw new Error('Handoff seal changes the logical root');
47
+ const prefix = await store.readImmutableRuntimePrefix({
48
+ sessionId: identity.sessionId,
49
+ runId: tip.runId,
50
+ });
51
+ if (prefix.position.lastEventId !== last.id)
52
+ throw new Error('Handoff source changed after its seal');
53
+ if (!segments) {
54
+ // A manual continuation may itself become a handoff root. Preserve its
55
+ // earlier replay boundary without treating those earlier Turns as this Turn.
56
+ const source = root.opening.source;
57
+ if (source.kind === 'continuation') {
58
+ if (!source.boundaryDigest)
59
+ throw new Error('Handoff requires authenticated source lineage');
60
+ const initial = await store.readContinuationClaimStateByBoundary(source.boundaryDigest);
61
+ if (!initial ||
62
+ initial.startEventId !== prefix.events[0]?.id ||
63
+ !invocationMatchesClaimTarget(root, initial.claim) ||
64
+ !continuationStartEventMatchesClaim(prefix.events[0], initial.claim, initial.startKind)) {
65
+ throw new Error('Handoff root continuation is not authenticated');
66
+ }
67
+ segments = [...initial.claim.boundary.segments];
68
+ }
69
+ else
70
+ segments = [];
71
+ }
72
+ segments.push(runtimePrefixSegment(prefix));
73
+ const boundary = createRuntimeBoundaryCursor(segments);
74
+ let state = await store.readContinuationClaimStateByBoundary(boundary.manifestDigest);
75
+ const next = await readRunInvocation(store, identity.sessionId, pause.successorRunId);
76
+ // A start can commit between the two reads. Authority is monotonic; refresh
77
+ // it once when the later opening read proves the earlier snapshot was old.
78
+ if (next && !state?.startEventId) {
79
+ state = await store.readContinuationClaimStateByBoundary(boundary.manifestDigest);
80
+ }
81
+ if (!state) {
82
+ if (next)
83
+ throw new Error('Handoff successor opened without its continuation claim');
84
+ return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
85
+ }
86
+ assertHandoffClaimSource(state.claim, prefix);
87
+ if (!next) {
88
+ if (state.startEventId)
89
+ throw new Error('Handoff successor opening is missing');
90
+ return { root, tip, events, runIds: [...seen], pendingHandoff: pause };
91
+ }
92
+ const first = (await store.readImmutableRuntimeEvents(identity.sessionId, next.runId))[0];
93
+ if (state.startEventId !== first?.id ||
94
+ !invocationMatchesClaimTarget(next, state.claim) ||
95
+ !continuationStartEventMatchesClaim(first, state.claim, state.startKind)) {
96
+ throw new Error('Handoff successor does not match its authenticated start');
97
+ }
98
+ tip = next;
99
+ }
100
+ }
101
+ /** Resolve a durable physical proof back to its root, then authenticate membership. */
102
+ export async function readLogicalRuntimeExecutionForRun(store, identity) {
103
+ const run = await readRunInvocation(store, identity.sessionId, identity.runId);
104
+ if (!run)
105
+ return undefined;
106
+ const rootRunId = run.opening.source.kind === 'handoff' ? run.opening.source.rootRunId : run.runId;
107
+ const logical = await readLogicalRuntimeExecution(store, { ...identity, runId: rootRunId });
108
+ if (!logical?.runIds.includes(identity.runId)) {
109
+ throw new Error('Physical proof is not an authenticated member of its logical execution');
110
+ }
111
+ return logical;
112
+ }