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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/pi-transcript.js +3 -4
  2. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  3. package/node_modules/@maka/core/dist/agent-graph-schedule.js +15 -4
  4. package/node_modules/@maka/core/dist/executor-id.js +22 -0
  5. package/node_modules/@maka/core/dist/external-session.js +18 -0
  6. package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -13
  7. package/node_modules/@maka/core/dist/runtime-event.js +19 -3
  8. package/node_modules/@maka/core/dist/session-send-projection.js +6 -3
  9. package/node_modules/@maka/core/dist/session.js +8 -2
  10. package/node_modules/@maka/core/dist/shell-run-result.js +1 -0
  11. package/node_modules/@maka/core/dist/shell-run.js +4 -0
  12. package/node_modules/@maka/core/dist/work-board.js +94 -1
  13. package/node_modules/@maka/core/package.json +1 -0
  14. package/node_modules/@maka/eval/dist/fleet-simulation.js +270 -0
  15. package/node_modules/@maka/eval/dist/fleet-store.js +243 -0
  16. package/node_modules/@maka/eval/dist/fleet-worker.js +125 -0
  17. package/node_modules/@maka/eval/dist/fleet.js +329 -0
  18. package/node_modules/@maka/eval/dist/index.js +3 -0
  19. package/node_modules/@maka/runtime/dist/agent-run.js +2 -17
  20. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +6 -2
  21. package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +36 -24
  22. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +257 -330
  23. package/node_modules/@maka/runtime/dist/background-task-health-tool.js +104 -0
  24. package/node_modules/@maka/runtime/dist/history-compaction.js +3 -1
  25. package/node_modules/@maka/runtime/dist/local-web-fetch.js +1 -1
  26. package/node_modules/@maka/runtime/dist/model-adapter.js +62 -53
  27. package/node_modules/@maka/runtime/dist/model-history.js +1 -5
  28. package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +311 -0
  29. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +344 -0
  30. package/node_modules/@maka/runtime/dist/provider-error-classification.js +7 -1
  31. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +35 -8
  32. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +7 -1
  33. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +1 -0
  34. package/node_modules/@maka/runtime/dist/runtime-invocation-route.js +49 -0
  35. package/node_modules/@maka/runtime/dist/runtime-kernel.js +72 -150
  36. package/node_modules/@maka/runtime/dist/runtime-read-model.js +0 -2
  37. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
  38. package/node_modules/@maka/runtime/dist/session-manager.js +100 -58
  39. package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -3
  40. package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +1 -0
  41. package/node_modules/@maka/runtime/dist/stream-graph-schedule-reconcile.js +1 -0
  42. package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +26 -4
  43. package/node_modules/@maka/runtime/dist/subagent-tools.js +7 -0
  44. package/node_modules/@maka/runtime/dist/tool-runtime.js +45 -466
  45. package/node_modules/@maka/runtime/package.json +3 -0
  46. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +7 -2
  47. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +1 -0
  48. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +24 -3
  49. package/node_modules/@maka/runtime-host/dist/protocol/index.js +4 -1
  50. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +42 -5
  51. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +31 -3
  52. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +6 -0
  53. package/node_modules/@maka/runtime-host/dist/server/child-agent-composition.js +1 -0
  54. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +1 -22
  55. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +54 -8
  56. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +14 -1
  57. package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +10 -2
  58. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  59. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
  60. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +56 -16
  61. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +4 -3
  62. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
  63. package/node_modules/@maka/runtime-host/dist/server/web-fetch-tool.js +37 -1
  64. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +450 -234
  65. package/node_modules/@maka/storage/dist/claude-code-transcript-lineage.js +106 -77
  66. package/node_modules/@maka/storage/dist/legacy-run-header.js +2 -2
  67. package/node_modules/@maka/storage/dist/model-call-usage-sql.js +1 -1
  68. package/node_modules/@maka/storage/dist/session-store.js +12 -2
  69. package/node_modules/@maka/storage/dist/sqlite-long-term-memory-store.js +86 -12
  70. package/node_modules/@maka/storage/dist/work-board-store.js +40 -1
  71. package/package.json +1 -1
@@ -12,6 +12,7 @@
12
12
  "./builtin-tools": "./dist/builtin-tools.js",
13
13
  "./shell-tools": "./dist/shell-tools.js",
14
14
  "./shell-run-manager": "./dist/shell-run-manager.js",
15
+ "./background-task-health-tool": "./dist/background-task-health-tool.js",
15
16
  "./deep-research-tools": "./dist/deep-research-tools.js",
16
17
  "./durable-tool-result-projection": "./dist/durable-tool-result-projection.js",
17
18
  "./tool-artifacts": "./dist/tool-artifacts.js",
@@ -79,6 +80,8 @@
79
80
  "./plugin-composition-loader": "./dist/plugin-composition-loader.js",
80
81
  "./plugin-command-service": "./dist/plugin-command-service.js",
81
82
  "./plugin-data-services": "./dist/plugin-data-services.js",
83
+ "./plugin-executor-backend": "./dist/plugin-executor-backend.js",
84
+ "./plugin-executor-service": "./dist/plugin-executor-service.js",
82
85
  "./plugin-goal-service": "./dist/plugin-goal-service.js",
83
86
  "./plugin-lsp-service": "./dist/plugin-lsp-service.js",
84
87
  "./plugin-session-query-service": "./dist/plugin-session-query-service.js",
@@ -58,12 +58,13 @@ export class RuntimeHostSessionProjector {
58
58
  replacing: false,
59
59
  });
60
60
  }
61
- if (message.text) {
61
+ if (message.text || message.interrupted) {
62
62
  this.#accumulators.set(accumulatorKey('text', message.id), {
63
63
  kind: 'text',
64
64
  turnId: root.turnId,
65
65
  messageId: message.id,
66
66
  text: message.text,
67
+ ...(message.interrupted ? { interrupted: true } : {}),
67
68
  complete: true,
68
69
  replacing: false,
69
70
  });
@@ -194,7 +195,7 @@ export class RuntimeHostSessionProjector {
194
195
  text: message.thinking.text,
195
196
  });
196
197
  }
197
- if (message.text) {
198
+ if (message.text || message.interrupted) {
198
199
  events.push({
199
200
  type: 'text_complete',
200
201
  id: `${terminal.id}:text:${message.id}`,
@@ -202,6 +203,7 @@ export class RuntimeHostSessionProjector {
202
203
  messageId: message.id,
203
204
  ts: terminal.ts,
204
205
  text: message.text,
206
+ ...(message.interrupted ? { interrupted: true } : {}),
205
207
  });
206
208
  }
207
209
  }
@@ -290,6 +292,7 @@ export class RuntimeHostSessionProjector {
290
292
  turnId: delta.turnId,
291
293
  messageId: delta.messageId,
292
294
  text: folded.text,
295
+ ...(delta.interrupted ? { interrupted: true } : {}),
293
296
  complete: delta.complete === true,
294
297
  replacing: delta.complete === true ? false : replacing,
295
298
  });
@@ -301,6 +304,7 @@ export class RuntimeHostSessionProjector {
301
304
  messageId: delta.messageId,
302
305
  ts: this.#now(),
303
306
  text: folded.text,
307
+ ...(delta.interrupted ? { interrupted: true } : {}),
304
308
  });
305
309
  }
306
310
  else if (folded.tail && !replacing) {
@@ -404,6 +408,7 @@ export class RuntimeHostSessionProjector {
404
408
  messageId: accumulator.messageId,
405
409
  ts: this.#now(),
406
410
  text: accumulator.text,
411
+ ...(accumulator.interrupted ? { interrupted: true } : {}),
407
412
  });
408
413
  }
409
414
  if (root.status === 'completed') {
@@ -54,6 +54,7 @@ export function projectSessionCatalogSummary(session) {
54
54
  ...(session.revisionIndex === undefined ? {} : { revisionIndex: session.revisionIndex }),
55
55
  ...(session.revisionState === undefined ? {} : { revisionState: session.revisionState }),
56
56
  backend: session.backend,
57
+ ...(session.executorId ? { executorId: session.executorId } : {}),
57
58
  ...(session.llmConnectionId === null ? {} : { llmConnectionId: session.llmConnectionId }),
58
59
  llmConnectionSlug: session.llmConnectionSlug,
59
60
  connectionLocked: session.connectionLocked,
@@ -16,7 +16,8 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- import { requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireShapedRecord, requireUtf8String, } from './codec.js';
19
+ import { EXTERNAL_SESSION_LIMIT_KINDS, } from '@maka/core/external-session';
20
+ import { requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireRecord, requireShapedRecord, requireUtf8String, } from './codec.js';
20
21
  import { invalidProtocolFrame } from './errors.js';
21
22
  import { defineHostPathOperation, defineOperation } from './operation-spec.js';
22
23
  import { decodeSessionCatalogItem } from './session-catalog.js';
@@ -129,8 +130,28 @@ export function decodeExternalSessionImportInput(value) {
129
130
  return { adapterId: adapterId(input.adapterId), sourceSessionId };
130
131
  }
131
132
  export function decodeExternalSessionImportResult(value) {
132
- const result = requireExactRecord(value, 'external Session import result', ['session']);
133
- const decoded = { session: decodeSessionCatalogItem(result.session) };
133
+ const result = requireRecord(value, 'external Session import result');
134
+ if (result.kind === 'source_limit_exceeded') {
135
+ requireExactRecord(result, 'external Session import limit result', ['kind', 'limit']);
136
+ const limit = requireExactRecord(result.limit, 'external Session import limit', [
137
+ 'kind',
138
+ 'max',
139
+ ]);
140
+ if (!EXTERNAL_SESSION_LIMIT_KINDS.some((kind) => kind === limit.kind)) {
141
+ throw invalidProtocolFrame('Invalid external Session import limit kind');
142
+ }
143
+ const max = requireCount(limit.max, 'external Session import limit maximum');
144
+ if (max === 0)
145
+ throw invalidProtocolFrame('Invalid external Session import limit maximum');
146
+ return {
147
+ kind: 'source_limit_exceeded',
148
+ limit: { kind: limit.kind, max },
149
+ };
150
+ }
151
+ if (result.kind !== 'imported')
152
+ throw invalidProtocolFrame('Invalid external Session import result kind');
153
+ requireExactRecord(result, 'external Session import result', ['kind', 'session']);
154
+ const decoded = { kind: 'imported', session: decodeSessionCatalogItem(result.session) };
134
155
  requireEncodedByteLimit(decoded, 'external Session import result', EXTERNAL_SESSION_RESULT_MAX_BYTES);
135
156
  return decoded;
136
157
  }
@@ -62,7 +62,10 @@ export const RUNTIME_HOST_REGISTRATION_SCHEMA_VERSION = 1;
62
62
  export const RUNTIME_HOST_PROTOCOL_VERSION = 0;
63
63
  // Increment when the same protocol version no longer guarantees safe Client-Host
64
64
  // interoperability. Mismatches are rejected before domain commands are admitted.
65
- export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 151;
65
+ export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 154;
66
+ // 154: External Session import results distinguish committed Sessions from typed source limits.
67
+ // 153: Sessions may select plugin executors and Plugin Platform queries expose them.
68
+ // 152: Assistant completions and transcript rows preserve interrupted responses.
66
69
  // 151: WorkHub selects and delegates through a durable Host Form interaction.
67
70
  // 150: Message admission accepts an empty-text Message that carries a quote or
68
71
  // an attachment (#4804). Peers older than this epoch reject that frame at
@@ -108,7 +108,7 @@ export const PLUGIN_PLATFORM_OPERATION_SPECS = {
108
108
  };
109
109
  function decodePluginPlatformQueryInput(value) {
110
110
  const input = requireShapedRecord(value, 'Plugin Platform query input', ['view'], ['rootId', 'cursor', 'limit']);
111
- if (!['status', 'packages', 'entries', 'tools', 'commands', 'failures'].includes(input.view)) {
111
+ if (!['status', 'packages', 'entries', 'tools', 'commands', 'executors', 'failures'].includes(input.view)) {
112
112
  throw invalidProtocolFrame('Invalid Plugin Platform query view');
113
113
  }
114
114
  const view = input.view;
@@ -125,8 +125,12 @@ function decodePluginPlatformQueryInput(value) {
125
125
  (input.rootId !== undefined || cursor !== undefined || limit !== undefined)) {
126
126
  throw invalidProtocolFrame('Plugin Platform status query does not accept paging');
127
127
  }
128
- if (input.rootId !== undefined && view !== 'entries' && view !== 'tools' && view !== 'commands') {
129
- throw invalidProtocolFrame('Plugin root identity is only valid for Entry, Tool, and Command queries');
128
+ if (input.rootId !== undefined &&
129
+ view !== 'entries' &&
130
+ view !== 'tools' &&
131
+ view !== 'commands' &&
132
+ view !== 'executors') {
133
+ throw invalidProtocolFrame('Plugin root identity is only valid for Entry, Tool, Command, and Executor queries');
130
134
  }
131
135
  let rootId;
132
136
  if (input.rootId !== undefined) {
@@ -190,7 +194,7 @@ function decodePluginPlatformQueryResult(value) {
190
194
  ]);
191
195
  if (!Array.isArray(output.items) ||
192
196
  output.items.length > 64 ||
193
- !['packages', 'entries', 'tools', 'commands', 'failures'].includes(view)) {
197
+ !['packages', 'entries', 'tools', 'commands', 'executors', 'failures'].includes(view)) {
194
198
  throw invalidProtocolFrame('Invalid Plugin Platform page');
195
199
  }
196
200
  const nextCursor = output.nextCursor === null
@@ -205,7 +209,9 @@ function decodePluginPlatformQueryResult(value) {
205
209
  ? { view, items: output.items.map(decodeToolInspection), nextCursor }
206
210
  : view === 'commands'
207
211
  ? { view, items: output.items.map(decodeCommandInspection), nextCursor }
208
- : { view: 'failures', items: output.items.map(decodePlatformFailure), nextCursor };
212
+ : view === 'executors'
213
+ ? { view, items: output.items.map(decodeExecutorInspection), nextCursor }
214
+ : { view: 'failures', items: output.items.map(decodePlatformFailure), nextCursor };
209
215
  }
210
216
  requireEncodedByteLimit(decoded, 'Plugin Platform query result', PLUGIN_PLATFORM_QUERY_RESULT_MAX_BYTES);
211
217
  return decoded;
@@ -233,6 +239,37 @@ function decodeCommandInspection(value) {
233
239
  aliases: item.aliases.map((alias) => requireString(alias, 'Plugin Command alias', 128)),
234
240
  };
235
241
  }
242
+ function decodeExecutorInspection(value) {
243
+ const item = requireExactRecord(value, 'Plugin Executor inspection', [
244
+ 'entryId',
245
+ 'scopeId',
246
+ 'extensionId',
247
+ 'generation',
248
+ 'id',
249
+ 'displayName',
250
+ 'capabilities',
251
+ ]);
252
+ const capabilities = requireExactRecord(item.capabilities, 'Plugin Executor capabilities', [
253
+ 'thinking',
254
+ 'toolActivity',
255
+ ]);
256
+ if (typeof capabilities.thinking !== 'boolean' ||
257
+ typeof capabilities.toolActivity !== 'boolean') {
258
+ throw invalidProtocolFrame('Invalid Plugin Executor capabilities');
259
+ }
260
+ return {
261
+ entryId: requireId(item.entryId, 'Plugin Entry identity'),
262
+ scopeId: requireString(item.scopeId, 'Plugin scope identity', 256),
263
+ extensionId: requireId(item.extensionId, 'Plugin package identity'),
264
+ generation: requireCount(item.generation, 'Plugin generation'),
265
+ id: requireString(item.id, 'Plugin Executor id', 128),
266
+ displayName: requireString(item.displayName, 'Plugin Executor display name', 256),
267
+ capabilities: {
268
+ thinking: capabilities.thinking,
269
+ toolActivity: capabilities.toolActivity,
270
+ },
271
+ };
272
+ }
236
273
  function decodePlatformFailure(value) {
237
274
  const failure = requireShapedRecord(value, 'Plugin Platform failure', ['diagnostic'], ['entryId', 'extensionId']);
238
275
  if (failure.entryId === undefined && failure.extensionId === undefined) {
@@ -22,6 +22,7 @@ import { isPermissionMode } from '@maka/core/permission';
22
22
  import { isSessionStartMode } from '@maka/core/session-start-mode';
23
23
  import { isSessionBlockedReason, isSessionToolProfile, } from '@maka/core/session';
24
24
  import { isThinkingLevel } from '@maka/core/model-thinking';
25
+ import { isExecutorId } from '@maka/core/executor-id';
25
26
  import { assertAllowedKeys, requireCount, requireEncodedByteLimit, requireEntityId, requireExactRecord, requireRecord, requireShapedRecord, requireUtf8String, } from './codec.js';
26
27
  import { invalidProtocolFrame } from './errors.js';
27
28
  import { defineHostPathOperation, defineOperation } from './operation-spec.js';
@@ -92,6 +93,7 @@ const PROJECTION_FIELDS = [
92
93
  'revisionOfTurnId',
93
94
  'revisionIndex',
94
95
  'revisionState',
96
+ 'executorId',
95
97
  'thinkingLevel',
96
98
  'lastReadMessageId',
97
99
  'liveRunState',
@@ -243,23 +245,33 @@ export function decodeSessionCatalogQueryInput(value) {
243
245
  throw invalidProtocolFrame('Invalid Session catalog query kind');
244
246
  }
245
247
  export function decodeSessionCreateInput(value) {
246
- const input = requireShapedRecord(value, 'Session create input', ['sessionId', 'workspace', 'modelTarget'], [
248
+ const input = requireShapedRecord(value, 'Session create input', ['sessionId', 'workspace'], [
247
249
  'mode',
248
250
  'name',
249
251
  'labels',
252
+ 'modelTarget',
253
+ 'executorId',
250
254
  'thinkingLevel',
251
255
  'toolProfile',
252
256
  'permissionMode',
253
257
  'collaborationMode',
254
258
  'orchestrationMode',
255
259
  ]);
260
+ const executorId = Object.hasOwn(input, 'executorId')
261
+ ? executorIdValue(input.executorId)
262
+ : undefined;
263
+ const target = Object.hasOwn(input, 'modelTarget') ? modelTarget(input.modelTarget) : undefined;
264
+ if ((executorId === undefined) === (target === undefined)) {
265
+ throw invalidProtocolFrame('Session creation requires exactly one model target or executor id');
266
+ }
256
267
  return {
257
268
  sessionId: requireEntityId(input.sessionId, 'sessionId'),
258
269
  workspace: decodeWorkspaceTarget(input.workspace),
259
270
  ...(Object.hasOwn(input, 'mode') ? { mode: sessionStartMode(input.mode) } : {}),
260
271
  ...(Object.hasOwn(input, 'name') ? { name: sessionName(input.name) } : {}),
261
272
  ...(Object.hasOwn(input, 'labels') ? { labels: labels(input.labels) } : {}),
262
- modelTarget: modelTarget(input.modelTarget),
273
+ ...(target ? { modelTarget: target } : {}),
274
+ ...(executorId ? { executorId } : {}),
263
275
  ...(Object.hasOwn(input, 'thinkingLevel')
264
276
  ? { thinkingLevel: thinkingLevel(input.thinkingLevel) }
265
277
  : {}),
@@ -465,6 +477,7 @@ export function decodeSessionCatalogProjection(value) {
465
477
  ...optionalRevisionIndex(record),
466
478
  ...optionalRevisionState(record),
467
479
  backend: backend(record.backend),
480
+ ...optionalExecutorId(record),
468
481
  llmConnectionId: record.llmConnectionId === null
469
482
  ? null
470
483
  : requireEntityId(record.llmConnectionId, 'Session Connection id'),
@@ -476,6 +489,9 @@ export function decodeSessionCatalogProjection(value) {
476
489
  collaborationMode: collaborationMode(record.collaborationMode),
477
490
  orchestrationMode: orchestrationMode(record.orchestrationMode),
478
491
  };
492
+ if ((projection.backend === 'plugin-executor') !== (projection.executorId !== undefined)) {
493
+ throw invalidProtocolFrame('Session executor identity does not match its backend');
494
+ }
479
495
  requireEncodedByteLimit(projection, 'Session catalog projection', SESSION_CATALOG_RESULT_MAX_BYTES);
480
496
  return projection;
481
497
  }
@@ -631,11 +647,23 @@ function optionalThinkingLevel(record) {
631
647
  // header's durable backend, and rows written by builds that shipped
632
648
  // FakeBackend still hold it (#3211).
633
649
  function backend(value) {
634
- if (value !== 'ai-sdk' && value !== 'fake') {
650
+ if (value !== 'ai-sdk' && value !== 'plugin-executor' && value !== 'fake') {
635
651
  throw invalidProtocolFrame('Invalid Session backend');
636
652
  }
637
653
  return value;
638
654
  }
655
+ function executorIdValue(value) {
656
+ const id = requireUtf8String(value, 'Executor id', 128);
657
+ if (!isExecutorId(id)) {
658
+ throw invalidProtocolFrame('Invalid Executor id');
659
+ }
660
+ return id;
661
+ }
662
+ function optionalExecutorId(record) {
663
+ if (record.executorId === undefined)
664
+ return {};
665
+ return { executorId: executorIdValue(record.executorId) };
666
+ }
639
667
  function thinkingLevel(value) {
640
668
  if (!isThinkingLevel(value))
641
669
  throw invalidProtocolFrame('Invalid Session thinking level');
@@ -452,6 +452,7 @@ function decodeAssistantDelta(value) {
452
452
  'text',
453
453
  'reset',
454
454
  'complete',
455
+ 'interrupted',
455
456
  ]);
456
457
  assertRequiredKeys(record, 'Session assistant delta', [
457
458
  'kind',
@@ -467,6 +468,10 @@ function decodeAssistantDelta(value) {
467
468
  if (record.complete !== undefined && record.complete !== true) {
468
469
  throw invalidProtocolFrame('Invalid Session assistant delta completion');
469
470
  }
471
+ if (record.interrupted !== undefined &&
472
+ (record.interrupted !== true || record.complete !== true)) {
473
+ throw invalidProtocolFrame('Interrupted assistant delta must be complete');
474
+ }
470
475
  if (record.reset !== undefined && record.reset !== true) {
471
476
  throw invalidProtocolFrame('Invalid Session assistant delta reset');
472
477
  }
@@ -487,6 +492,7 @@ function decodeAssistantDelta(value) {
487
492
  : requireUtf8BoundedString(record.text, 'Session assistant delta text', SESSION_LIVE_DELTA_MAX_BYTES),
488
493
  ...(record.reset === true ? { reset: true } : {}),
489
494
  ...(record.complete === true ? { complete: true } : {}),
495
+ ...(record.interrupted === true ? { interrupted: true } : {}),
490
496
  };
491
497
  }
492
498
  function decodeSessionFrameEvent(value) {
@@ -43,6 +43,7 @@ export function bindHostChildAgentBackend(authority, parentSessionId) {
43
43
  },
44
44
  agentProfile: input.agentProfile,
45
45
  ...(input.subagentId ? { subagentId: input.subagentId } : {}),
46
+ ...(input.executorId ? { executorId: input.executorId } : {}),
46
47
  prompt: input.prompt,
47
48
  ...(input.swarm ? { swarm: input.swarm } : {}),
48
49
  abortSignal: input.abortSignal,
@@ -101,7 +101,7 @@ export function createHostExecutionArtifactServices(input) {
101
101
  archiveToolResult: prepareLedgerForCommit,
102
102
  readArchivedToolResultResource: (event) => event.storage === 'ledger' || event.storage === 'event'
103
103
  ? (readLedgerResource?.(event) ?? { ok: false, reason: 'read_failed' })
104
- : readArchive(input.artifacts, event),
104
+ : { ok: false, reason: 'read_failed' },
105
105
  }),
106
106
  };
107
107
  return Object.freeze(services);
@@ -160,24 +160,3 @@ async function readBoundedSourceFile(cwd, sourcePath) {
160
160
  function contentBytes(content) {
161
161
  return typeof content === 'string' ? Buffer.byteLength(content, 'utf8') : content.byteLength;
162
162
  }
163
- async function readArchive(artifacts, event) {
164
- const entry = await artifacts.getInSession(event.sessionId, event.artifactId);
165
- const record = entry.record;
166
- if (!record)
167
- return { ok: false, reason: 'not_found' };
168
- if (record.source !== 'tool_result_archive')
169
- return { ok: false, reason: 'source_mismatch' };
170
- if (record.sizeBytes !== event.originalBytes)
171
- return { ok: false, reason: 'size_mismatch' };
172
- const read = await artifacts.readTextInSession(event.sessionId, event.artifactId, {
173
- maxBytes: event.maxBytes ?? event.originalBytes,
174
- });
175
- if (!read.ok)
176
- return read;
177
- if (sha256(read.text) !== event.bodySha256)
178
- return { ok: false, reason: 'corrupt' };
179
- return { ok: true, serializedResult: read.text };
180
- }
181
- function sha256(text) {
182
- return createHash('sha256').update(text).digest('hex');
183
- }
@@ -51,6 +51,8 @@ import { PluginAttachmentService } from '@maka/runtime/plugin-attachment-service
51
51
  import { PluginApprovalService } from '@maka/runtime/plugin-approval-service';
52
52
  import { PluginFilesystemService } from '@maka/runtime/plugin-fs-service';
53
53
  import { PluginLlmService } from '@maka/runtime/plugin-llm-service';
54
+ import { PluginExecutorBackend } from '@maka/runtime/plugin-executor-backend';
55
+ import { PluginExecutorService } from '@maka/runtime/plugin-executor-service';
54
56
  import { PluginShellService } from '@maka/runtime/plugin-shell-service';
55
57
  import { PluginUserQuestionService } from '@maka/runtime/plugin-user-question-service';
56
58
  import { PluginWebService } from '@maka/runtime/plugin-web-service';
@@ -153,6 +155,7 @@ import { HostWorkHubCoordinationCoordinator } from './workhub-coordination-coord
153
155
  import { WorkHubActionEffectFailure, workHubResumedTurnId, } from './workhub-coordination-action-gate.js';
154
156
  import { createHostWebSearchService, createHostWebSearchToolFromService, resolveHostTavilyWebSearchReadiness, shouldResolveHostTavilyWebSearchReadiness, } from './web-search-tool.js';
155
157
  import { createHostWebFetchService, createHostWebFetchToolFromService } from './web-fetch-tool.js';
158
+ import { buildBackgroundTaskHealthTool } from '@maka/runtime/background-task-health-tool';
156
159
  import { createHostExecutionArtifactServices } from './execution-artifacts.js';
157
160
  import { openToolResultArchiveEvidenceReader } from '@maka/storage/tool-result-archive-evidence';
158
161
  import { createRuntimeHostWorkspaceExecutionComposition, RuntimeHostWorkspaceExecutionError, } from './workspace-execution-composition.js';
@@ -198,6 +201,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
198
201
  let goalExecutions;
199
202
  let pluginPlatform;
200
203
  let manager;
204
+ let invalidatePluginExecutorBackends = () => undefined;
201
205
  let modelMetadataRefresh;
202
206
  let archiveEvidence;
203
207
  try {
@@ -208,6 +212,11 @@ export async function createExecutionRuntimeHostComposition(context, options = {
208
212
  new PluginUserQuestionService(pluginRoot, pluginAgents);
209
213
  const pluginFilesystem = new PluginFilesystemService(pluginRoot, pluginAgents);
210
214
  const pluginLlm = new PluginLlmService(pluginRoot, pluginAgents);
215
+ const pluginExecutors = new PluginExecutorService(pluginRoot, {
216
+ // Retirement aborts its generation before backend disposal begins, so the
217
+ // terminal event preserves executor-retired instead of looking like Stop.
218
+ onChanged: () => queueMicrotask(invalidatePluginExecutorBackends),
219
+ });
211
220
  const pluginShellEnv = new PluginShellEnvService(pluginRoot);
212
221
  const pluginShell = new PluginShellService(pluginRoot, pluginAgents, pluginShellEnv);
213
222
  const pluginWeb = new PluginWebService(pluginRoot, pluginAgents);
@@ -231,6 +240,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
231
240
  tools: pluginTools,
232
241
  systemPrompt: pluginSystemPrompt,
233
242
  commands: pluginCommands,
243
+ executors: pluginExecutors,
234
244
  });
235
245
  const pluginPlatformCoordinator = new HostPluginPlatformCoordinator(pluginPlatform);
236
246
  const openedProjectCatalog = storage.projectCatalog;
@@ -457,6 +467,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
457
467
  const webFetchService = createHostWebFetchService({
458
468
  policy: runtimePolicyStores.operations,
459
469
  });
470
+ const backgroundTaskHealthTool = buildBackgroundTaskHealthTool(runtimeResources, webFetchService);
460
471
  pluginWeb.bindRuntime({
461
472
  search: ({ query, limit, abortSignal }) => webSearchService.search({ query, limit, ...(abortSignal ? { abortSignal } : {}) }),
462
473
  fetch: (input) => webFetchService.fetch(input),
@@ -479,6 +490,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
479
490
  const childHostTools = [
480
491
  createHostWebSearchToolFromService(webSearchService),
481
492
  createHostWebFetchToolFromService(webFetchService),
493
+ backgroundTaskHealthTool,
482
494
  ...runtimePolicy.modelTools,
483
495
  ];
484
496
  const hostTools = [...childHostTools, ...historyTools];
@@ -751,6 +763,23 @@ export async function createExecutionRuntimeHostComposition(context, options = {
751
763
  backends.register('ai-sdk', dependencies.primaryBackendFactory ?? {
752
764
  prepare: (backendContext) => prepareHostAiSdkBackend(hostAiSdkBackendInput(backendContext)),
753
765
  });
766
+ backends.register('plugin-executor', {
767
+ prepare: async (backendContext) => {
768
+ const executorId = backendContext.header.executorId;
769
+ if (!executorId)
770
+ throw new Error('Plugin executor Session is missing its executor id');
771
+ const binding = pluginExecutors.bind(backendContext.sessionId, executorId);
772
+ return {
773
+ providerStateIdentity: binding.providerStateIdentity,
774
+ build: (factoryContext) => new PluginExecutorBackend({
775
+ sessionId: factoryContext.sessionId,
776
+ cwd: factoryContext.header.cwd,
777
+ ...(factoryContext.systemPrompt ? { instructions: factoryContext.systemPrompt } : {}),
778
+ binding,
779
+ }),
780
+ };
781
+ },
782
+ });
754
783
  const runtimeAuthority = {
755
784
  bindRun: (identity) => messages.bindRun(identity),
756
785
  executeRoot: (input) => executeHostedExecutionToSettlement(requireRootCoordinator(rootCoordinator), input),
@@ -793,6 +822,8 @@ export async function createExecutionRuntimeHostComposition(context, options = {
793
822
  };
794
823
  resolveAvailableToolNames = async (sessionId) => {
795
824
  const header = await stores.sessionStore.readHeaderSnapshot(sessionId);
825
+ if (header.backend === 'plugin-executor')
826
+ return [];
796
827
  if (header.subagentRuntime) {
797
828
  if (!header.subagentParent) {
798
829
  throw new Error('Subagent runtime snapshot requires a linked child session');
@@ -970,6 +1001,12 @@ export async function createExecutionRuntimeHostComposition(context, options = {
970
1001
  toolBoundaryProtocol: stores.runtimeEventStore.toolBoundaryProtocol,
971
1002
  backends,
972
1003
  subagentCatalog,
1004
+ assertChildExecutorAvailable: (parentSessionId, executorId) => {
1005
+ const identity = pluginExecutors.identity(parentSessionId, executorId);
1006
+ if (identity.scopeId !== 'profile') {
1007
+ throw new Error(`Session-scoped executor cannot be inherited by a child Session: ${executorId}`);
1008
+ }
1009
+ },
973
1010
  newId: randomUUID,
974
1011
  now: Date.now,
975
1012
  safeBoundaryResumeEnabled: process.env.MAKA_RUNTIME_SAFE_BOUNDARY_RESUME === '1',
@@ -1052,6 +1089,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1052
1089
  const registerBackendInvalidation = () => {
1053
1090
  observeBackendInvalidation(requireSessionManager(manager).refreshIdleBackends());
1054
1091
  };
1092
+ invalidatePluginExecutorBackends = registerBackendInvalidation;
1055
1093
  const registerConfigurationMutation = () => {
1056
1094
  hostChanges.publishConfiguration();
1057
1095
  registerBackendInvalidation();
@@ -1263,6 +1301,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1263
1301
  return new Promise((resolve, reject) => {
1264
1302
  void spawn({
1265
1303
  agentProfile: options.agentProfile ?? 'implementation',
1304
+ ...(options.executorId ? { executorId: options.executorId } : {}),
1266
1305
  prompt: options.prompt,
1267
1306
  ...(options.signal ? { abortSignal: options.signal } : {}),
1268
1307
  onReady: (ready) => resolve({
@@ -1512,6 +1551,9 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1512
1551
  continuity: continuityCoordinator,
1513
1552
  workspaceResolver,
1514
1553
  requestDrain: context.requestDrain,
1554
+ assertExecutorAvailable: (sessionId, executorId) => {
1555
+ pluginExecutors.identity(sessionId, executorId);
1556
+ },
1515
1557
  ...(context.sessionAccessAuthority
1516
1558
  ? { sessionAccessAuthority: context.sessionAccessAuthority }
1517
1559
  : {}),
@@ -1659,14 +1701,18 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1659
1701
  sessionId: input.targetSessionId,
1660
1702
  workspace: input.create.workspace,
1661
1703
  name: input.create.title,
1662
- modelTarget: input.create.defaults?.model
1663
- ? {
1664
- kind: 'explicit',
1665
- connectionId: input.create.defaults.model.llmConnectionId,
1666
- connectionSlug: input.create.defaults.model.llmConnectionSlug,
1667
- model: input.create.defaults.model.model,
1668
- }
1669
- : { kind: 'default' },
1704
+ ...(input.create.defaults?.executorId
1705
+ ? { executorId: input.create.defaults.executorId }
1706
+ : {
1707
+ modelTarget: input.create.defaults?.model
1708
+ ? {
1709
+ kind: 'explicit',
1710
+ connectionId: input.create.defaults.model.llmConnectionId,
1711
+ connectionSlug: input.create.defaults.model.llmConnectionSlug,
1712
+ model: input.create.defaults.model.model,
1713
+ }
1714
+ : { kind: 'default' },
1715
+ }),
1670
1716
  ...(input.create.defaults?.permissionMode
1671
1717
  ? { permissionMode: input.create.defaults.permissionMode }
1672
1718
  : {}),
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { JsonArrayPageBudget } from './json-array-page-budget.js';
20
+ import { ExternalSessionLimitError, } from '@maka/core/external-session';
20
21
  import { ExternalSessionImporter } from '@maka/storage/external-sessions';
21
22
  import { EXTERNAL_SESSION_CWD_MAX_BYTES, EXTERNAL_SESSION_IMPORTED_SESSION_IDS_MAX_ITEMS, EXTERNAL_SESSION_NAME_MAX_BYTES, EXTERNAL_SESSION_PAGE_MAX_ITEMS, EXTERNAL_SESSION_RESULT_MAX_BYTES, EXTERNAL_SESSION_SOURCE_MAX_ITEMS, EXTERNAL_SESSION_SOURCE_SESSION_ID_MAX_BYTES, } from '../protocol/index.js';
22
23
  import { projectSessionCatalogRecord, SessionOperationFailure, NoUsableImportModelError, } from './session-catalog-coordinator.js';
@@ -198,6 +199,15 @@ export class HostExternalSessionCoordinator {
198
199
  }
199
200
  catch (error) {
200
201
  if (!commitAttempted) {
202
+ if (error instanceof ExternalSessionLimitError) {
203
+ return {
204
+ ok: true,
205
+ result: {
206
+ kind: 'source_limit_exceeded',
207
+ limit: { kind: error.limit.kind, max: error.limit.max },
208
+ },
209
+ };
210
+ }
201
211
  return importFailure(isSourceSessionNotFound(error) ? 'not_found' : 'source_unreadable', isSourceSessionNotFound(error)
202
212
  ? 'External Session does not exist'
203
213
  : 'External Session could not be read or converted');
@@ -218,7 +228,10 @@ export class HostExternalSessionCoordinator {
218
228
  }
219
229
  try {
220
230
  const record = await this.#sessions.readCatalogRecord(header.id);
221
- return { ok: true, result: { session: projectSessionCatalogRecord(record) } };
231
+ return {
232
+ ok: true,
233
+ result: { kind: 'imported', session: projectSessionCatalogRecord(record) },
234
+ };
222
235
  }
223
236
  catch {
224
237
  this.#requestDrain();
@@ -23,6 +23,11 @@ const IMPORT_STAGING_UNAVAILABLE_REASON = 'Imported Session history is still bei
23
23
  export const WORKHUB_COORDINATION_EXECUTION_UNAVAILABLE_REASON = 'WorkHub Coordination Session execution requires WorkHub authority';
24
24
  export const WORKHUB_COORDINATION_TARGET_UNAVAILABLE_REASON = 'WorkHub Coordination execution requires the reserved Coordination Session';
25
25
  export const LEGACY_CONNECTION_IDENTITY_EXECUTION_UNAVAILABLE_REASON = 'This Session requires an explicit account selection before it can run.';
26
+ export const PLUGIN_EXECUTOR_CONTINUATION_UNAVAILABLE_REASON = 'Plugin executor Sessions cannot resume without provider-owned durable continuation support.';
27
+ export const PLUGIN_EXECUTOR_COPY_UNAVAILABLE_REASON = 'Plugin executor Sessions cannot be copied without provider-owned conversation cloning';
28
+ export function runtimeHostConversationCopyUnavailableReason(header) {
29
+ return header.backend === 'plugin-executor' ? PLUGIN_EXECUTOR_COPY_UNAVAILABLE_REASON : undefined;
30
+ }
26
31
  export function runtimeHostExternalTurnUnavailableReason(header) {
27
32
  return runtimeHostExecutionUnavailableReason(header, { kind: 'external_message' });
28
33
  }
@@ -31,9 +36,12 @@ export function runtimeHostSafeBoundaryContinuationUnavailableReason(header) {
31
36
  ? WORKHUB_COORDINATION_EXECUTION_UNAVAILABLE_REASON
32
37
  : undefined) ??
33
38
  (header.transcriptLedgerVersion === 0 ? IMPORT_STAGING_UNAVAILABLE_REASON : undefined) ??
34
- (header.llmConnectionId === undefined && header.backend !== 'fake'
39
+ (header.llmConnectionId === undefined && header.backend === 'ai-sdk'
35
40
  ? LEGACY_CONNECTION_IDENTITY_EXECUTION_UNAVAILABLE_REASON
36
41
  : undefined) ??
42
+ (header.backend === 'plugin-executor'
43
+ ? PLUGIN_EXECUTOR_CONTINUATION_UNAVAILABLE_REASON
44
+ : undefined) ??
37
45
  (header.subagentParent ? CHILD_CONTINUATION_UNAVAILABLE_REASON : undefined));
38
46
  }
39
47
  export function runtimeHostExecutionUnavailableReason(header, execution) {
@@ -58,7 +66,7 @@ export function runtimeHostExecutionUnavailableReason(header, execution) {
58
66
  ? WORKHUB_COORDINATION_EXECUTION_UNAVAILABLE_REASON
59
67
  : undefined) ??
60
68
  (header.transcriptLedgerVersion === 0 ? IMPORT_STAGING_UNAVAILABLE_REASON : undefined) ??
61
- (header.llmConnectionId === undefined && header.backend !== 'fake'
69
+ (header.llmConnectionId === undefined && header.backend === 'ai-sdk'
62
70
  ? LEGACY_CONNECTION_IDENTITY_EXECUTION_UNAVAILABLE_REASON
63
71
  : undefined) ??
64
72
  (header.collaborationMode === 'plan' &&
@@ -69,6 +69,12 @@ export class HostPluginPlatformCoordinator {
69
69
  result: boundedPage('commands', this.platform.inspectCommands(input.rootId), input),
70
70
  };
71
71
  }
72
+ if (input.view === 'executors') {
73
+ return {
74
+ ok: true,
75
+ result: boundedPage('executors', this.platform.inspectExecutors(input.rootId), input),
76
+ };
77
+ }
72
78
  if (input.view === 'failures') {
73
79
  return { ok: true, result: boundedPage('failures', failures, input) };
74
80
  }