maka-agent 0.2.0-dev.46.20260922 → 0.2.0-dev.48.20260923

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 (131) hide show
  1. package/DISCLAIMER-WIP +6 -1
  2. package/README.md +6 -1
  3. package/dist/acp/maka-acp-agent.js +1 -1
  4. package/dist/acp/session-mcp.js +212 -0
  5. package/dist/acp/session-registry.js +52 -8
  6. package/dist/acp/stdio-server.js +3 -0
  7. package/dist/cli-core.js +1 -1
  8. package/dist/mcp-capability-provider.js +6 -1
  9. package/dist/mcp-capability-publication.js +139 -0
  10. package/dist/pi-tui-layout.js +46 -17
  11. package/dist/pi-tui-pickers.js +25 -14
  12. package/dist/pi-tui-runner.js +24 -15
  13. package/dist/runtime-host-capability-provider-command.js +25 -6
  14. package/dist/runtime-host-service-manager.js +26 -2
  15. package/dist/tui-context-refresh.js +15 -26
  16. package/dist/tui-mcp-control.js +66 -112
  17. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  18. package/node_modules/@maka/acp-executor-plugin/dist/acp-errors.js +26 -0
  19. package/node_modules/@maka/acp-executor-plugin/dist/acp-filesystem.js +103 -0
  20. package/node_modules/@maka/acp-executor-plugin/dist/acp-process.js +119 -0
  21. package/node_modules/@maka/acp-executor-plugin/dist/acp-projection.js +146 -0
  22. package/node_modules/@maka/acp-executor-plugin/dist/index.js +653 -0
  23. package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +24446 -0
  24. package/node_modules/@maka/acp-executor-plugin/package.json +15 -0
  25. package/node_modules/@maka/antigravity-acp-plugin/dist/index.js +88 -0
  26. package/node_modules/@maka/antigravity-acp-plugin/dist/model-presentation.js +53 -0
  27. package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +115 -0
  28. package/node_modules/@maka/antigravity-acp-plugin/package.json +15 -0
  29. package/node_modules/@maka/core/dist/client-capability-grant.js +2 -2
  30. package/node_modules/@maka/core/dist/executor-catalog.js +106 -0
  31. package/node_modules/@maka/core/dist/mcp.js +4 -1
  32. package/node_modules/@maka/core/dist/model-metadata.js +28 -1
  33. package/node_modules/@maka/core/dist/model-thinking.js +36 -1
  34. package/node_modules/@maka/core/dist/provider-registry.js +0 -1
  35. package/node_modules/@maka/core/dist/refresh-read-coordinator.js +62 -0
  36. package/node_modules/@maka/core/dist/runtime-event-store.js +25 -0
  37. package/node_modules/@maka/core/dist/runtime-invocation.js +5 -0
  38. package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +4 -0
  39. package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +6 -0
  40. package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +21 -0
  41. package/node_modules/@maka/core/dist/session.js +19 -1
  42. package/node_modules/@maka/core/dist/settings.js +3 -0
  43. package/node_modules/@maka/core/dist/tool-ledger-scanner.js +365 -104
  44. package/node_modules/@maka/core/dist/turn-origin.js +14 -0
  45. package/node_modules/@maka/core/package.json +3 -0
  46. package/node_modules/@maka/mcp/dist/credential-coordinator.js +6 -1
  47. package/node_modules/@maka/mcp/dist/index.js +31 -5
  48. package/node_modules/@maka/mcp/dist/oauth.js +54 -6
  49. package/node_modules/@maka/runtime/dist/agent-run-inspect.js +1 -1
  50. package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +15 -60
  51. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +18 -0
  52. package/node_modules/@maka/runtime/dist/apply-patch-profile.js +64 -14
  53. package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -2
  54. package/node_modules/@maka/runtime/dist/codex-v4a-patch.js +18 -0
  55. package/node_modules/@maka/runtime/dist/conversation-copy.js +56 -1
  56. package/node_modules/@maka/runtime/dist/mcp-tools.js +9 -2
  57. package/node_modules/@maka/runtime/dist/model-adapter.js +80 -3
  58. package/node_modules/@maka/runtime/dist/model-runtime.js +6 -0
  59. package/node_modules/@maka/runtime/dist/openai-apply-patch.js +5 -0
  60. package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +122 -3
  61. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +212 -16
  62. package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +2 -2
  63. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
  64. package/node_modules/@maka/runtime/dist/session-manager.js +184 -126
  65. package/node_modules/@maka/runtime/dist/shell-run-manager.js +3 -0
  66. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +29 -4
  67. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +8 -5
  68. package/node_modules/@maka/runtime-host/dist/client/capability-provider-service.js +2 -0
  69. package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +59 -20
  70. package/node_modules/@maka/runtime-host/dist/client/connection.js +6 -4
  71. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  72. package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +8 -1
  73. package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +4 -4
  74. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -1
  75. package/node_modules/@maka/runtime-host/dist/composition-identity.js +19 -0
  76. package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +20 -3
  77. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +8 -5
  78. package/node_modules/@maka/runtime-host/dist/protocol/index.js +20 -2
  79. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +34 -0
  80. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +75 -5
  81. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +1 -5
  82. package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +188 -0
  83. package/node_modules/@maka/runtime-host/dist/server/candidate.js +12 -7
  84. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +234 -70
  85. package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +14 -8
  86. package/node_modules/@maka/runtime-host/dist/server/client-capability-recovery.js +44 -46
  87. package/node_modules/@maka/runtime-host/dist/server/context-coordinator.js +2 -0
  88. package/node_modules/@maka/runtime-host/dist/server/execution-composition-factory.js +1 -1
  89. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +188 -21
  90. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
  91. package/node_modules/@maka/runtime-host/dist/server/extension-bundle.js +4 -0
  92. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +29 -7
  93. package/node_modules/@maka/runtime-host/dist/server/host-composition-source.js +47 -0
  94. package/node_modules/@maka/runtime-host/dist/server/host-composition.js +1 -29
  95. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +2 -9
  96. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +75 -9
  97. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +5 -1
  98. package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +45 -0
  99. package/node_modules/@maka/runtime-host/dist/server/jev-routing-model.js +157 -0
  100. package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -2
  101. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -0
  102. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +11 -1
  103. package/node_modules/@maka/runtime-host/dist/server/process-lifecycle.js +1 -1
  104. package/node_modules/@maka/runtime-host/dist/server/process-termination-error.js +27 -0
  105. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +91 -12
  106. package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +2 -0
  107. package/node_modules/@maka/runtime-host/dist/server/session-bundle-coordinator.js +4 -1
  108. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +128 -13
  109. package/node_modules/@maka/runtime-host/dist/server/session-effect-coordinator.js +12 -6
  110. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +2 -2
  111. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +33 -0
  112. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +4 -0
  113. package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +173 -0
  114. package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +293 -0
  115. package/node_modules/@maka/runtime-host/package.json +2 -0
  116. package/node_modules/@maka/storage/dist/agent-run-store-contract.js +8 -0
  117. package/node_modules/@maka/storage/dist/execution-stores.js +15 -3
  118. package/node_modules/@maka/storage/dist/mcp-config-store.js +91 -2
  119. package/node_modules/@maka/storage/dist/operational-state-store.js +15 -1
  120. package/node_modules/@maka/storage/dist/plan-authority.js +2 -0
  121. package/node_modules/@maka/storage/dist/plan-store.js +30 -0
  122. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  123. package/node_modules/@maka/storage/dist/runtime-policy/credential-vault-document.js +4 -1
  124. package/node_modules/@maka/storage/dist/runtime-policy/policy-document.js +2 -0
  125. package/node_modules/@maka/storage/dist/session-store-values.js +16 -4
  126. package/node_modules/@maka/storage/dist/shell-run-authority.js +1 -0
  127. package/node_modules/@maka/storage/dist/shell-run-store.js +15 -0
  128. package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +74 -1
  129. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +724 -84
  130. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +16 -0
  131. package/package.json +1 -1
@@ -16,9 +16,10 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ import { watch } from 'node:fs';
19
20
  import { readFile } from 'node:fs/promises';
20
- import { dirname, join } from 'node:path';
21
- import { MCP_CONFIG_VERSION, createDefaultMcpConfig, isNonLoopbackCleartextHttp, } from '@maka/core/mcp';
21
+ import { basename, dirname, join } from 'node:path';
22
+ import { MCP_CONFIG_VERSION, createDefaultMcpConfig, isNonLoopbackCleartextHttp, mcpConfigChangeRetiresCredentials, } from '@maka/core/mcp';
22
23
  import { writeAtomicFile } from './atomic-file-write.js';
23
24
  import { withProcessLifetimeFileUpdateLock } from './process-lifetime-file-update-lock.js';
24
25
  import { hardenDirectory } from './stable-storage.js';
@@ -30,6 +31,9 @@ const MAX_ID_LENGTH = 128;
30
31
  const MAX_STRING_LENGTH = 8_192;
31
32
  const MAX_CONFIG_BYTES = 1_048_576;
32
33
  const FORBIDDEN_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
34
+ // An atomic replace is a temp write plus a rename; one settle window turns
35
+ // that burst into one notification.
36
+ const CHANGE_SETTLE_MS = 150;
33
37
  export class McpConfigSourceError extends Error {
34
38
  reason;
35
39
  version;
@@ -54,6 +58,64 @@ export class McpServerExistsError extends Error {
54
58
  export function createMcpConfigStore(workspaceRoot) {
55
59
  return new FileMcpConfigStore(join(workspaceRoot, 'mcp.json'));
56
60
  }
61
+ /** Watches the directory, not the file: an atomic replace renames a new
62
+ * inode over the old one, which ends a watch on the file itself. */
63
+ export function subscribeMcpConfigFileChanges(path, listener) {
64
+ const name = basename(path);
65
+ let timer;
66
+ let watcher;
67
+ const settle = () => {
68
+ clearTimeout(timer);
69
+ timer = setTimeout(() => listener(), CHANGE_SETTLE_MS);
70
+ };
71
+ try {
72
+ watcher = watch(dirname(path), { persistent: false }, (_event, filename) => {
73
+ if (filename === null || filename.toString() === name)
74
+ settle();
75
+ });
76
+ }
77
+ catch (error) {
78
+ queueMicrotask(() => listener(error));
79
+ return () => { };
80
+ }
81
+ settle();
82
+ const stop = () => {
83
+ clearTimeout(timer);
84
+ watcher.close();
85
+ };
86
+ watcher.on('error', (error) => {
87
+ stop();
88
+ listener(error);
89
+ });
90
+ return stop;
91
+ }
92
+ export class McpConfigurationValidationError extends Error {
93
+ constructor(cause) {
94
+ super(cause instanceof Error ? cause.message : 'Invalid MCP configuration', { cause });
95
+ this.name = 'McpConfigurationValidationError';
96
+ }
97
+ }
98
+ /** Validate before retiring credentials, and retire before publishing the
99
+ * replacement. Both effects run under the config file's cross-process lock. */
100
+ export function updateMcpConfiguration(store, prepare, retireCredentials) {
101
+ return store.transform(async (current) => {
102
+ const proposed = prepare(current);
103
+ let next;
104
+ try {
105
+ next = normalizeMcpConfig(proposed);
106
+ assertMcpEndpointPolicyOnChanges(current, next);
107
+ }
108
+ catch (error) {
109
+ throw new McpConfigurationValidationError(error);
110
+ }
111
+ for (const [serverId, previous] of Object.entries(current.mcpServers)) {
112
+ if (mcpConfigChangeRetiresCredentials(previous, next.mcpServers[serverId])) {
113
+ await retireCredentials(serverId, previous);
114
+ }
115
+ }
116
+ return next;
117
+ });
118
+ }
57
119
  export function normalizeMcpConfig(value) {
58
120
  if (!isRecord(value))
59
121
  throw new Error('MCP config must be an object');
@@ -114,6 +176,21 @@ class FileMcpConfigStore {
114
176
  return this.withUpdateLock(() => this.readOrCreate());
115
177
  }
116
178
  }
179
+ subscribeChanges(listener) {
180
+ let stop;
181
+ let stopped = false;
182
+ void this.ensureDirectory().then(() => {
183
+ if (!stopped)
184
+ stop = subscribeMcpConfigFileChanges(this.path, listener);
185
+ }, (error) => {
186
+ if (!stopped)
187
+ listener(error);
188
+ });
189
+ return () => {
190
+ stopped = true;
191
+ stop?.();
192
+ };
193
+ }
117
194
  async transform(apply) {
118
195
  return this.withUpdateLock(async () => {
119
196
  const current = await this.readOrCreate();
@@ -280,6 +357,18 @@ function normalizeOAuth(value, serverId) {
280
357
  if (!isRecord(value))
281
358
  throw new Error(`${serverId}.oauth must be an object`);
282
359
  const result = {};
360
+ if (value.issuer !== undefined) {
361
+ const issuer = nonEmptyString(value.issuer, `${serverId}.oauth.issuer`);
362
+ const parsed = new URL(issuer);
363
+ if (!['https:', 'http:'].includes(parsed.protocol) ||
364
+ parsed.username ||
365
+ parsed.password ||
366
+ parsed.search ||
367
+ parsed.hash) {
368
+ throw new Error(`${serverId}.oauth.issuer must be an HTTP(S) issuer URL without credentials, query or fragment`);
369
+ }
370
+ result.issuer = issuer;
371
+ }
283
372
  if (value.clientId !== undefined) {
284
373
  result.clientId = nonEmptyString(value.clientId, `${serverId}.oauth.clientId`);
285
374
  }
@@ -138,6 +138,8 @@ class OperationalStateDatabaseOwner {
138
138
  references = 0;
139
139
  closed = false;
140
140
  transactionDepth = 0;
141
+ transactionWrote = false;
142
+ writeViewRevisionValue = 0;
141
143
  settledCallbacks = [];
142
144
  constructor(databasePath, options) {
143
145
  this.databasePath = databasePath;
@@ -173,6 +175,7 @@ class OperationalStateDatabaseOwner {
173
175
  return {
174
176
  database: this.database,
175
177
  databasePath: this.databasePath,
178
+ writeViewRevision: () => this.writeViewRevisionValue,
176
179
  transaction: (mode, operation) => this.transaction(mode, operation),
177
180
  onTransactionSettled: (callback) => {
178
181
  if (this.transactionDepth === 0)
@@ -220,22 +223,33 @@ class OperationalStateDatabaseOwner {
220
223
  transaction(mode, operation) {
221
224
  if (this.closed)
222
225
  throw new Error('Operational state database is closed');
223
- if (this.transactionDepth > 0)
226
+ if (this.transactionDepth > 0) {
227
+ if (mode === 'write')
228
+ this.transactionWrote = true;
224
229
  return operation();
230
+ }
225
231
  this.database.exec(mode === 'write' ? 'BEGIN IMMEDIATE' : 'BEGIN');
226
232
  this.transactionDepth += 1;
233
+ this.transactionWrote = mode === 'write';
227
234
  let result;
228
235
  try {
229
236
  result = operation();
230
237
  this.database.exec('COMMIT');
231
238
  }
232
239
  catch (error) {
240
+ const transactionWrote = this.transactionWrote;
233
241
  rollback(this.database);
234
242
  this.transactionDepth -= 1;
243
+ this.transactionWrote = false;
244
+ if (transactionWrote)
245
+ this.writeViewRevisionValue += 1;
235
246
  this.settle(false);
236
247
  throw error;
237
248
  }
238
249
  this.transactionDepth -= 1;
250
+ if (this.transactionWrote)
251
+ this.writeViewRevisionValue += 1;
252
+ this.transactionWrote = false;
239
253
  this.settle(true);
240
254
  return result;
241
255
  }
@@ -53,6 +53,7 @@ export function observeInteractivePlanStoreWriter(source, onChanged, onPublicati
53
53
  access: writer.access,
54
54
  [writerBrand]: true,
55
55
  readState: (sessionId) => writer.readState(sessionId),
56
+ listPlanRecoverySessionIds: () => writer.listPlanRecoverySessionIds(),
56
57
  readOperationReceipt: (sessionId, operationId, operationInput) => writer.readOperationReceipt(sessionId, operationId, operationInput),
57
58
  submitProposal: (input) => publish(input.sessionId, () => writer.submitProposal(input)),
58
59
  requestRevision: (input) => publish(input.sessionId, () => writer.requestRevision(input)),
@@ -136,6 +137,7 @@ function createWriterFacade(lease, store) {
136
137
  access: 'write',
137
138
  [writerBrand]: true,
138
139
  readState: (sessionId) => run(() => store.readState(sessionId)),
140
+ listPlanRecoverySessionIds: () => run(() => store.listPlanRecoverySessionIds()),
139
141
  readOperationReceipt: (sessionId, operationId, operationInput) => run(() => store.readOperationReceipt(sessionId, operationId, structuredClone(operationInput))),
140
142
  submitProposal: (input) => run(() => store.submitProposal(cloneSubmit(input))),
141
143
  requestRevision: (input) => run(() => store.requestRevision(cloneInput(input))),
@@ -43,6 +43,36 @@ class SqlitePlanStoreImpl {
43
43
  async readState(sessionId) {
44
44
  return (await this.readLedger(sessionId)).state;
45
45
  }
46
+ async listPlanRecoverySessionIds() {
47
+ const rows = this.#lease.database
48
+ .prepare(`
49
+ WITH lifecycle AS (
50
+ SELECT
51
+ session_id,
52
+ json_extract(record_json, '$.type') AS event_type,
53
+ ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY sequence DESC) AS position
54
+ FROM workflow_plan_events
55
+ WHERE json_extract(record_json, '$.type') IN (
56
+ 'plan_approved',
57
+ 'plan_execution_completed',
58
+ 'plan_execution_cancelled',
59
+ 'plan_execution_interrupted',
60
+ 'plan_execution_resumed'
61
+ )
62
+ )
63
+ SELECT session_id
64
+ FROM lifecycle
65
+ WHERE position = 1
66
+ AND event_type IN ('plan_approved', 'plan_execution_resumed')
67
+ ORDER BY session_id
68
+ `)
69
+ .all();
70
+ return rows.map((row) => {
71
+ if (typeof row.session_id !== 'string')
72
+ throw new Error('Invalid SQLite Plan row');
73
+ return row.session_id;
74
+ });
75
+ }
46
76
  async readOperationReceipt(sessionId, operationId, operationInput) {
47
77
  assertSafeId(sessionId);
48
78
  assertSafeId(operationId);
@@ -40,6 +40,7 @@ export async function openRuntimeEventReadPersistence(input) {
40
40
  kind: 'sqlite',
41
41
  runtimeEventStore: Object.freeze({
42
42
  listSessionInvocations: (sessionId) => store.listSessionInvocations(sessionId),
43
+ listInvocationRecoveryInventory: (sessionIds) => store.listInvocationRecoveryInventory(sessionIds),
43
44
  readRunInvocation: (sessionId, runId) => store.readRunInvocation(sessionId, runId),
44
45
  listSessionInvocationsBounded: (sessionId, limit) => store.listSessionInvocationsBounded(sessionId, limit),
45
46
  listSessionInvocationsPage: (sessionId, input) => store.listSessionInvocationsPage(sessionId, input),
@@ -237,7 +237,8 @@ function sameLocator(left, right) {
237
237
  if (left.scope === 'web_search' && right.scope === 'web_search') {
238
238
  return left.provider === right.provider;
239
239
  }
240
- return left.scope === 'network_proxy' && right.scope === 'network_proxy';
240
+ return ((left.scope === 'network_proxy' && right.scope === 'network_proxy') ||
241
+ (left.scope === 'jev' && right.scope === 'jev'));
241
242
  }
242
243
  function locatorKey(locator) {
243
244
  switch (locator.scope) {
@@ -245,6 +246,8 @@ function locatorKey(locator) {
245
246
  return `connection:${locator.connectionId}:${locator.kind}`;
246
247
  case 'web_search':
247
248
  return `web_search:${locator.provider}:api_key`;
249
+ case 'jev':
250
+ return 'jev:api_key';
248
251
  case 'network_proxy':
249
252
  return 'network_proxy:password';
250
253
  }
@@ -84,6 +84,8 @@ export function policySnapshot(document) {
84
84
  }
85
85
  function applyMutation(policy, operation) {
86
86
  switch (operation.kind) {
87
+ case 'set_jev':
88
+ return { ...policy, jev: operation.value };
87
89
  case 'set_network_proxy':
88
90
  return { ...policy, networkProxy: operation.value };
89
91
  case 'set_personalization':
@@ -16,6 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ import { isExecutorConfiguration } from '@maka/core/executor-catalog';
19
20
  import { randomUUID } from 'node:crypto';
20
21
  import { DEFAULT_SESSION_NAME, normalizeUserSessionName } from '@maka/core/session-name';
21
22
  import { isExecutorId } from '@maka/core/executor-id';
@@ -72,7 +73,12 @@ export function buildSessionHeader(workspaceRoot, input, sessionId = randomUUID(
72
73
  ...(input.revisionState ? { revisionState: input.revisionState } : {}),
73
74
  hasUnread: false,
74
75
  backend: input.executorId ? 'plugin-executor' : 'ai-sdk',
75
- ...(input.executorId ? { executorId: input.executorId } : {}),
76
+ ...(input.executorId
77
+ ? {
78
+ executorId: input.executorId,
79
+ ...(input.executorConfig ? { executorConfig: input.executorConfig } : {}),
80
+ }
81
+ : {}),
76
82
  ...(input.llmConnectionId === undefined ? {} : { llmConnectionId: input.llmConnectionId }),
77
83
  llmConnectionSlug: input.llmConnectionSlug,
78
84
  // A subagent Session's route is chosen by the spawn that created it and is
@@ -273,9 +279,10 @@ function isPersistedBackendKind(value) {
273
279
  }
274
280
  function isValidExecutorSelection(header) {
275
281
  if (header.backend === 'plugin-executor') {
276
- return isExecutorId(header.executorId);
282
+ return (isExecutorId(header.executorId) &&
283
+ (header.executorConfig === undefined || isExecutorConfiguration(header.executorConfig)));
277
284
  }
278
- return header.executorId === undefined;
285
+ return header.executorId === undefined && header.executorConfig === undefined;
279
286
  }
280
287
  function isFiniteNumber(value) {
281
288
  return typeof value === 'number' && Number.isFinite(value);
@@ -314,7 +321,12 @@ export function toSummary(header) {
314
321
  ...(header.revisionIndex !== undefined ? { revisionIndex: header.revisionIndex } : {}),
315
322
  ...(header.revisionState ? { revisionState: header.revisionState } : {}),
316
323
  backend: header.backend,
317
- ...(header.executorId ? { executorId: header.executorId } : {}),
324
+ ...(header.executorId
325
+ ? {
326
+ executorId: header.executorId,
327
+ ...(header.executorConfig ? { executorConfig: header.executorConfig } : {}),
328
+ }
329
+ : {}),
318
330
  ...(header.llmConnectionId === undefined ? {} : { llmConnectionId: header.llmConnectionId }),
319
331
  llmConnectionSlug: header.llmConnectionSlug,
320
332
  connectionLocked: header.connectionLocked,
@@ -97,6 +97,7 @@ function createWriterFacade(lease, store) {
97
97
  },
98
98
  readShellRun: (sessionId, shellRunId) => run(() => store.readShellRun(sessionId, shellRunId)),
99
99
  listSessionShellRuns: (sessionId) => run(() => store.listSessionShellRuns(sessionId)),
100
+ listShellRunRecoverySessionIds: () => run(() => store.listShellRunRecoverySessionIds()),
100
101
  close: () => {
101
102
  if (closed)
102
103
  return;
@@ -90,6 +90,21 @@ class SqliteShellRunStore {
90
90
  return normalizeShellRunRecord(JSON.parse(row.record_json), sessionId, row.shell_run_id);
91
91
  });
92
92
  }
93
+ async listShellRunRecoverySessionIds() {
94
+ const rows = this.#lease.database
95
+ .prepare(`
96
+ SELECT DISTINCT session_id
97
+ FROM core_shell_runs
98
+ WHERE json_extract(record_json, '$.status') IN ('starting', 'running')
99
+ ORDER BY session_id
100
+ `)
101
+ .all();
102
+ return rows.map((row) => {
103
+ if (typeof row.session_id !== 'string')
104
+ throw new Error('Invalid SQLite ShellRun row');
105
+ return row.session_id;
106
+ });
107
+ }
93
108
  close() {
94
109
  this.#lease.close();
95
110
  }
@@ -20,7 +20,7 @@ import { decodePersistedLegacyRunHeader, invocationOpeningFromLegacyRunHeader, }
20
20
  import { encodeCanonicalRuntimeEvent } from '@maka/core/canonical-runtime-event';
21
21
  import { rebuildTranscriptTurnExtents, TERMINAL_RUNTIME_EVENT_SQL, } from './runtime-transcript-query.js';
22
22
  import { buildInvocationOpenedEvent, buildSyntheticTerminalRuntimeEvent, } from '@maka/core/runtime-invocation';
23
- export const SQLITE_RUNTIME_SCHEMA_VERSION = 19;
23
+ export const SQLITE_RUNTIME_SCHEMA_VERSION = 20;
24
24
  export const RUNTIME_RECOVERY_AUTHORITY_CAPABILITY = 'runtime_recovery_authority';
25
25
  export const RUNTIME_RECOVERY_AUTHORITY_CAPABILITY_VERSION = 1;
26
26
  export const RUNTIME_CONTINUATION_AUTHORITY_CAPABILITY = 'runtime_continuation_authority';
@@ -624,6 +624,79 @@ const MIGRATIONS = new Map([
624
624
  ON runtime_session_turn_extents(session_id, first_ordinal, last_ordinal);
625
625
  `,
626
626
  ],
627
+ [
628
+ 20,
629
+ `
630
+ CREATE INDEX runtime_events_recovery_user_message
631
+ ON runtime_events(
632
+ session_id,
633
+ turn_id,
634
+ event_id,
635
+ event_kind,
636
+ CASE
637
+ WHEN json_valid(payload_json)
638
+ THEN json_extract(payload_json, '$.partial')
639
+ END,
640
+ CASE
641
+ WHEN json_valid(payload_json)
642
+ THEN json_extract(payload_json, '$.role')
643
+ END,
644
+ coalesce(CASE
645
+ WHEN json_valid(payload_json)
646
+ THEN json_extract(payload_json, '$.content.steering')
647
+ END, 0)
648
+ )
649
+ WHERE event_kind = 'text'
650
+ AND CASE
651
+ WHEN json_valid(payload_json)
652
+ THEN json_extract(payload_json, '$.partial')
653
+ END = 0
654
+ AND CASE
655
+ WHEN json_valid(payload_json)
656
+ THEN json_extract(payload_json, '$.role')
657
+ END = 'user'
658
+ AND coalesce(CASE
659
+ WHEN json_valid(payload_json)
660
+ THEN json_extract(payload_json, '$.content.steering')
661
+ END, 0) = 0;
662
+
663
+ -- Historical duplicates must remain readable so the affected identity can
664
+ -- fail closed. This is intentionally non-unique. CASE keeps a malformed
665
+ -- legacy payload from aborting the whole schema migration.
666
+ CREATE INDEX runtime_events_steering_message
667
+ ON runtime_events(
668
+ session_id,
669
+ CASE
670
+ WHEN json_valid(payload_json)
671
+ THEN json_extract(payload_json, '$.refs.providerEventId')
672
+ END
673
+ )
674
+ WHERE event_kind = 'text'
675
+ AND CASE
676
+ WHEN json_valid(payload_json)
677
+ THEN json_extract(payload_json, '$.partial')
678
+ END = 0
679
+ AND CASE
680
+ WHEN json_valid(payload_json)
681
+ THEN json_extract(payload_json, '$.content.steering')
682
+ END = 1;
683
+
684
+ CREATE INDEX runtime_events_tool_dispatch_operation
685
+ ON runtime_events(
686
+ CASE
687
+ WHEN json_valid(payload_json)
688
+ THEN json_extract(payload_json, '$.actions.toolDispatch.operationId')
689
+ END
690
+ )
691
+ WHERE event_kind = 'tool_dispatch';
692
+
693
+ CREATE INDEX tool_operations_unsettled
694
+ ON tool_operations(invocation_id, operation_id)
695
+ WHERE current_state = 'prepared'
696
+ AND result_event_id IS NULL
697
+ AND dispatch_event_id IS NOT NULL;
698
+ `,
699
+ ],
627
700
  ]);
628
701
  /**
629
702
  * Data migrations that a SQL statement cannot express, applied inside the same