maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.27.20260910

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 (62) hide show
  1. package/dist/cli-core.js +21 -0
  2. package/dist/runtime-host-cli.js +43 -3
  3. package/dist/runtime-host-handoff-surface.js +10 -8
  4. package/dist/runtime-host-lifecycle-transaction.js +28 -3
  5. package/dist/runtime-host-local-source-retirement.js +1 -1
  6. package/dist/runtime-host-setup-command.js +29 -6
  7. package/dist/runtime-host-update-command.js +49 -5
  8. package/dist/session-export-command.js +72 -0
  9. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  10. package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
  11. package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
  12. package/node_modules/@maka/core/package.json +2 -1
  13. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +7 -3
  14. package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
  15. package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
  16. package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
  17. package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
  18. package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
  19. package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
  20. package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
  21. package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
  22. package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
  23. package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
  24. package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
  25. package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
  26. package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
  27. package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
  28. package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
  29. package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
  30. package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
  31. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
  32. package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
  33. package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
  34. package/node_modules/@maka/runtime/dist/session-export.js +177 -0
  35. package/node_modules/@maka/runtime/package.json +18 -0
  36. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +67 -18
  37. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
  38. package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
  39. package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -1
  40. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
  41. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
  42. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
  43. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +452 -7
  44. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
  45. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
  46. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
  47. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +79 -16
  48. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +25 -4
  49. package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
  50. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  51. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
  52. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
  53. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
  54. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
  55. package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
  56. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +13 -6
  57. package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
  58. package/node_modules/@maka/storage/dist/session-bundle-policy.js +333 -30
  59. package/node_modules/@maka/storage/dist/session-store.js +2 -2
  60. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
  61. package/node_modules/@maka/storage/package.json +2 -0
  62. package/package.json +1 -1
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { copyWorkHubAttachmentsToTarget } from './workhub-message-attachments.js';
20
20
  import { createHash, randomUUID } from 'node:crypto';
21
- import { MAX_READ_IMAGE_BYTES } from '@maka/core/attachments';
21
+ import { attachmentKindFromMimeType, MAX_READ_IMAGE_BYTES } from '@maka/core/attachments';
22
22
  import { messageContentDigest, normalizeMessageContent } from '@maka/core/events';
23
23
  import { NO_REAL_CONNECTION_CODE } from '@maka/core/connection-error-copy';
24
24
  import { generalizedErrorMessage } from '@maka/core/redaction';
@@ -31,6 +31,7 @@ import { AgentGraphSupervisorWakeCoordinator } from '@maka/runtime/agent-graph-s
31
31
  import { BackendRegistry, SessionManager, workHubDirectStopAbortSource, } from '@maka/runtime/session-manager';
32
32
  import { buildToolsForAgentDefinition } from '@maka/runtime/agent-catalog';
33
33
  import { buildHistoryTools } from '@maka/runtime/history-tools';
34
+ import { buildBuiltinTools } from '@maka/runtime/builtin-tools';
34
35
  import { createLocalContinuationSafetyInspector } from '@maka/runtime/continuation-safety';
35
36
  import { createConfiguredSubagentCatalog } from '@maka/runtime/configured-subagent-catalog';
36
37
  import { buildHostCapabilitiesFromBinding } from '@maka/runtime/skills';
@@ -45,8 +46,24 @@ import { SessionActivityRegistry } from '@maka/runtime/goal-turn-lifecycle';
45
46
  import { ShellRunProcessManager } from '@maka/runtime/shell-run-manager';
46
47
  import { resolveShellPlan, resolveTurnShellPlan, validateShellPreference, } from '@maka/runtime/shell-detect';
47
48
  import { Context } from '@maka/runtime/plugin-kernel';
49
+ import { PluginAgentService } from '@maka/runtime/plugin-agent-service';
50
+ import { PluginAttachmentService } from '@maka/runtime/plugin-attachment-service';
51
+ import { PluginApprovalService } from '@maka/runtime/plugin-approval-service';
52
+ import { PluginFilesystemService } from '@maka/runtime/plugin-fs-service';
53
+ import { PluginLlmService } from '@maka/runtime/plugin-llm-service';
54
+ import { PluginShellService } from '@maka/runtime/plugin-shell-service';
55
+ import { PluginUserQuestionService } from '@maka/runtime/plugin-user-question-service';
56
+ import { PluginWebService } from '@maka/runtime/plugin-web-service';
48
57
  import { MakaCompositionLoader } from '@maka/runtime/plugin-composition-loader';
49
58
  import { PluginToolService } from '@maka/runtime/plugin-tool-service';
59
+ import { PluginSystemPromptService } from '@maka/runtime/plugin-system-prompt-service';
60
+ import { PluginCommandService } from '@maka/runtime/plugin-command-service';
61
+ import { PluginAuthorizationService, PluginCredentialService, PluginSettingsService, PluginStorageService, } from '@maka/runtime/plugin-data-services';
62
+ import { PluginGoalService } from '@maka/runtime/plugin-goal-service';
63
+ import { PluginLspService } from '@maka/runtime/plugin-lsp-service';
64
+ import { PluginSessionQueryService } from '@maka/runtime/plugin-session-query-service';
65
+ import { PluginShellEnvService } from '@maka/runtime/plugin-shell-env-service';
66
+ import { PluginSkillService } from '@maka/runtime/plugin-skill-service';
50
67
  import { isHostedExecutionTerminal } from './hosted-execution-authority.js';
51
68
  import { createAgentGraphControlStore } from '@maka/storage/agent-graph-control-store';
52
69
  import { createArtifactAttachmentResourceReader } from '@maka/storage/artifact-stores';
@@ -59,6 +76,7 @@ import { createInteractiveContextOffloadReader } from '@maka/storage/context-off
59
76
  import { openStorageWriterComposition } from '@maka/storage/storage-writer-composition';
60
77
  import { resolveWorkspaceIdentity } from '@maka/storage/workspace-identity';
61
78
  import { CanonicalSessionProjectionReader } from './canonical-session-projection.js';
79
+ import { HostPluginDataRuntime } from './plugin-data-runtime.js';
62
80
  import { bindHostChildAgentBackend, createHostChildAgentToolComposition, } from './child-agent-composition.js';
63
81
  import { HostCanonicalPermissionOutcomeReader } from './canonical-permission-outcome-reader.js';
64
82
  import { HostArtifactCoordinator } from './artifact-coordinator.js';
@@ -75,7 +93,7 @@ import { HostDeepResearchCoordinator } from './deep-research-coordinator.js';
75
93
  import { HostDailyReviewCoordinator } from './daily-review-coordinator.js';
76
94
  import { prepareHostAiSdkBackend } from './execution-model-composition.js';
77
95
  import { createInteractiveRunComposer, createInteractiveRunComposerFactory, routeInteractiveRunToolSurface, } from './interactive-run-composer.js';
78
- import { createHostGoalEvaluator, createHostDailyReviewModel, createHostMemoryExtractionModel, createHostSessionEffectModel, } from './execution-model-authority.js';
96
+ import { createHostGoalEvaluator, createHostDailyReviewModel, createHostMemoryExtractionModel, createHostPluginModel, createHostSessionEffectModel, } from './execution-model-authority.js';
79
97
  import { HostExecutionInspectCoordinator } from './execution-inspect-coordinator.js';
80
98
  import { HostExternalSessionCoordinator } from './external-session-coordinator.js';
81
99
  import { HostGoalCoordinator } from './goal-coordinator.js';
@@ -88,7 +106,7 @@ import { HostInteractionCoordinator } from './interaction-coordinator.js';
88
106
  import { HostInteractiveTurnCoordinator } from './interactive-turn-coordinator.js';
89
107
  import { SessionTurnAccessRequestCoordinator } from './session-turn-access-request-coordinator.js';
90
108
  import { ensureBootstrapRuntimePolicy } from './bootstrap-runtime-policy.js';
91
- import { hostedExecutionRunProfile } from './hosted-execution-tool-profile.js';
109
+ import { bindWorkHubRoutingDecisionPrompt, hostedExecutionRunProfile, } from './hosted-execution-tool-profile.js';
92
110
  import { HostMemoryCoordinator } from './memory-coordinator.js';
93
111
  import { HostMemoryExtractionCoordinator } from './memory-extraction-coordinator.js';
94
112
  import { MemoryExtractionSessionLane } from './memory-extraction-session-lane.js';
@@ -177,10 +195,35 @@ export async function createExecutionRuntimeHostComposition(context, options = {
177
195
  let archiveEvidence;
178
196
  try {
179
197
  const pluginRoot = new Context();
180
- const pluginTools = new PluginToolService(pluginRoot);
198
+ const pluginAgents = new PluginAgentService(pluginRoot);
199
+ const pluginAttachments = new PluginAttachmentService(pluginRoot, pluginAgents);
200
+ new PluginApprovalService(pluginRoot, pluginAgents);
201
+ new PluginUserQuestionService(pluginRoot, pluginAgents);
202
+ const pluginFilesystem = new PluginFilesystemService(pluginRoot, pluginAgents);
203
+ const pluginLlm = new PluginLlmService(pluginRoot, pluginAgents);
204
+ const pluginShellEnv = new PluginShellEnvService(pluginRoot);
205
+ const pluginShell = new PluginShellService(pluginRoot, pluginAgents, pluginShellEnv);
206
+ const pluginWeb = new PluginWebService(pluginRoot, pluginAgents);
207
+ const pluginSessionQuery = new PluginSessionQueryService(pluginRoot, pluginAgents);
208
+ const pluginGoals = new PluginGoalService(pluginRoot, pluginAgents);
209
+ const pluginSkills = new PluginSkillService(pluginRoot);
210
+ const pluginCommands = new PluginCommandService(pluginRoot);
211
+ new PluginLspService(pluginRoot);
212
+ const pluginSettings = new PluginSettingsService(pluginRoot);
213
+ const pluginStorage = new PluginStorageService(pluginRoot);
214
+ const pluginCredentials = new PluginCredentialService(pluginRoot);
215
+ new PluginAuthorizationService(pluginRoot, pluginCredentials);
216
+ const pluginData = new HostPluginDataRuntime(context.owner.controlDirectory);
217
+ pluginSettings.bindRuntime(pluginData);
218
+ pluginStorage.bindRuntime(pluginData);
219
+ pluginCredentials.bindRuntime(pluginData);
220
+ const pluginTools = new PluginToolService(pluginRoot, { agents: pluginAgents });
221
+ const pluginSystemPrompt = new PluginSystemPromptService(pluginRoot);
181
222
  pluginPlatform = new HostPluginPlatform(context.owner.controlDirectory, {
182
223
  composition: new MakaCompositionLoader({ root: pluginRoot }),
183
224
  tools: pluginTools,
225
+ systemPrompt: pluginSystemPrompt,
226
+ commands: pluginCommands,
184
227
  });
185
228
  const pluginPlatformCoordinator = new HostPluginPlatformCoordinator(pluginPlatform);
186
229
  const openedProjectCatalog = storage.projectCatalog;
@@ -323,12 +366,94 @@ export async function createExecutionRuntimeHostComposition(context, options = {
323
366
  ...(sandboxManager ? { sandboxManager } : {}),
324
367
  ...(filesystemWorker ? { filesystemWorker } : {}),
325
368
  };
369
+ const invokeBuiltin = async (name, args, invocation, shellEnvironment) => {
370
+ if (!invocation.toolContext)
371
+ throw new Error(`${name} requires an active Tool invocation`);
372
+ const policy = await runtimePolicyStores.runtimePolicy.getSnapshot();
373
+ const tool = buildBuiltinTools({
374
+ ...builtinTools,
375
+ shell: resolveTurnShellPlan(policy.policy.shell),
376
+ ...(shellEnvironment ? { shellEnvironment } : {}),
377
+ }).find((candidate) => candidate.name === name);
378
+ if (!tool)
379
+ throw new Error(`Builtin capability is unavailable: ${name}`);
380
+ return tool.impl(args, invocation.toolContext);
381
+ };
382
+ pluginFilesystem.bindRuntime({
383
+ execute: (operation, invocation) => {
384
+ switch (operation.kind) {
385
+ case 'read':
386
+ return invokeBuiltin('Read', operation, invocation);
387
+ case 'write':
388
+ return invokeBuiltin('Write', operation, invocation);
389
+ case 'edit':
390
+ return invokeBuiltin('Edit', {
391
+ path: operation.path,
392
+ old_string: operation.oldString,
393
+ new_string: operation.newString,
394
+ }, invocation);
395
+ case 'glob':
396
+ return invokeBuiltin('Glob', { pattern: operation.pattern, cwd: operation.path }, invocation);
397
+ case 'grep':
398
+ return invokeBuiltin('Grep', { pattern: operation.pattern, path: operation.path, glob: operation.glob }, invocation);
399
+ case 'apply_patch':
400
+ return invokeBuiltin('apply_patch', operation.patch, invocation);
401
+ }
402
+ },
403
+ });
404
+ pluginShell.bindRuntime({
405
+ run: (options, invocation) => invokeBuiltin('Bash', {
406
+ command: options.command,
407
+ timeout_ms: options.timeoutMs,
408
+ run_in_background: options.background,
409
+ pty: options.pty,
410
+ }, invocation, options.environment),
411
+ read: (ref, invocation) => runtimeResources.readRuntimeResource(invocation.sessionId, ref, invocation.abortSignal),
412
+ write: (ref, input, invocation) => runtimeResources.writeStdin({
413
+ sessionId: invocation.sessionId,
414
+ ref,
415
+ input,
416
+ abortSignal: invocation.abortSignal,
417
+ caller: 'model',
418
+ }),
419
+ stop: (ref, invocation) => runtimeResources.stopBackgroundTask(invocation.sessionId, ref, invocation.abortSignal),
420
+ });
421
+ pluginAttachments.bindRuntime({
422
+ create: async (input, invocation) => {
423
+ const record = await openedArtifactStore.create({
424
+ sessionId: invocation.sessionId,
425
+ turnId: invocation.turnId,
426
+ name: input.name,
427
+ kind: pluginAttachmentArtifactKind(input.mimeType, input.name),
428
+ content: input.content,
429
+ mimeType: input.mimeType,
430
+ source: 'tool_result',
431
+ ...(input.summary ? { summary: input.summary } : {}),
432
+ });
433
+ return pluginAttachmentRef(record);
434
+ },
435
+ read: async (attachment, invocation) => {
436
+ if (attachment.ref.kind !== 'session_file' ||
437
+ attachment.ref.sessionId !== invocation.sessionId) {
438
+ throw new Error('Attachment is outside the current Session');
439
+ }
440
+ const result = await openedArtifactStore.readBinaryInSession(invocation.sessionId, attachment.ref.relativePath);
441
+ if (!result.ok)
442
+ throw new Error(`Attachment read failed: ${result.reason}`);
443
+ return Uint8Array.from(Buffer.from(result.base64, 'base64'));
444
+ },
445
+ list: async (invocation) => (await openedArtifactStore.listTurnArtifacts(invocation.sessionId, invocation.turnId)).map(pluginAttachmentRef),
446
+ });
326
447
  const webSearchService = createHostWebSearchService({
327
448
  policy: runtimePolicyStores.operations,
328
449
  });
329
450
  const webFetchService = createHostWebFetchService({
330
451
  policy: runtimePolicyStores.operations,
331
452
  });
453
+ pluginWeb.bindRuntime({
454
+ search: ({ query, limit, abortSignal }) => webSearchService.search({ query, limit, ...(abortSignal ? { abortSignal } : {}) }),
455
+ fetch: (input) => webFetchService.fetch(input),
456
+ });
332
457
  const historyTools = buildHistoryTools({
333
458
  listSessions: () => requireSessionManager(manager).listSessions(),
334
459
  readMessages: async (sessionId, abortSignal) => {
@@ -394,6 +519,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
394
519
  publish: (sessionId) => hostChanges.publishSessionCatalog(sessionId),
395
520
  }, projectMembership, context.requestDrain, new HostProjectDirectoryAuthority(options.projectDirectoryRoots));
396
521
  let rootCoordinator;
522
+ let workHubCoordination;
397
523
  let canonicalProjection;
398
524
  let memory;
399
525
  let clientCapabilities;
@@ -473,6 +599,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
473
599
  let recoveryTask;
474
600
  let rootCloseTask;
475
601
  let rootRecoveryCompleted = false;
602
+ let clientBoundRecovery = Promise.resolve();
476
603
  let closeTask;
477
604
  let backendInvalidationPoisoned = false;
478
605
  let domainModules;
@@ -539,6 +666,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
539
666
  oauthCredentials,
540
667
  createRunComposer: createInteractiveRunComposerFactory({
541
668
  skills,
669
+ pluginSkills,
542
670
  memory: requireMemory(memory),
543
671
  sessionTodo,
544
672
  clientCapabilities: requireClientCapabilities(clientCapabilities),
@@ -551,9 +679,29 @@ export async function createExecutionRuntimeHostComposition(context, options = {
551
679
  hostTools,
552
680
  resolveRootTools: (sessionId) => requireGraphCoordinator(graphCoordinator).toolsForSession(sessionId),
553
681
  resolvePluginTools: (sessionId, coreTools) => pluginTools.resolveContributions(sessionId, coreTools),
682
+ resolvePluginSystemPrompt: async (sessionId, promptContext, baseText) => {
683
+ const assembly = await pluginSystemPrompt.assemble({
684
+ sessionId,
685
+ turnId: promptContext.turnId,
686
+ cwd: promptContext.cwd,
687
+ }, baseText);
688
+ return {
689
+ text: assembly.text,
690
+ contexts: assembly.contexts,
691
+ sourceRevisions: assembly.sourceRevision ? [assembly.sourceRevision] : [],
692
+ };
693
+ },
554
694
  parentAgentTools: childAgentTools.parentTools,
555
695
  childTools: childAgentTools.childTools,
556
696
  worktreePatchWriteBackAvailable: true,
697
+ resolveProfileSystemPrompt: async (promptContext, basePrompt) => {
698
+ if (promptContext.sessionId !== WORKHUB_COORDINATION_SESSION_ID)
699
+ return basePrompt;
700
+ const admission = await stores.agentRunStore.readRootTurnAdmission(promptContext.sessionId, promptContext.turnId);
701
+ return bindWorkHubRoutingDecisionPrompt(basePrompt, admission?.execution.kind === 'workhub_coordination'
702
+ ? admission.execution.routingDecision
703
+ : undefined);
704
+ },
557
705
  }),
558
706
  ...(hostedExecutionRunProfile(backendContext.header.toolProfile)?.memoryExtraction === false
559
707
  ? {}
@@ -927,8 +1075,229 @@ export async function createExecutionRuntimeHostComposition(context, options = {
927
1075
  }, {
928
1076
  currentGraphId: (rootSessionId) => requireGraphCoordinator(graphCoordinator).currentGraphId(rootSessionId),
929
1077
  beginNextGraphEpoch: async (rootSessionId) => (await requireGraphCoordinator(graphCoordinator).beginNextGraphEpoch(rootSessionId, (operation) => requireGraphSupervisorWake(graphSupervisorWake).runWithSessionWakesSuppressed(rootSessionId, operation, 'agent_graph_epoch_advanced'))).graphId,
930
- }, (input) => sessionEffectCoordinator.nameSessionFromRootMessage(input), context.owner.capability.rootId);
1078
+ }, (input) => sessionEffectCoordinator.nameSessionFromRootMessage(input), context.owner.capability.rootId, dependencies.workHubRoutingModel
1079
+ ? (input) => workHubCoordination.prepareRoutingDecision(input)
1080
+ : undefined);
931
1081
  const coordinator = rootCoordinator;
1082
+ const pluginModel = createHostPluginModel({
1083
+ runtimePolicy: runtimePolicyStores,
1084
+ oauthCredentials,
1085
+ usage: openedUsageStores,
1086
+ requestDrain: context.requestDrain,
1087
+ readSessionHeader: (sessionId) => stores.sessionStore.readHeaderSnapshot(sessionId),
1088
+ });
1089
+ pluginLlm.bindRuntime({
1090
+ generate: (input, invocation) => pluginModel.generate({
1091
+ sessionId: invocation.sessionId,
1092
+ prompt: input.prompt,
1093
+ ...(input.system ? { system: input.system } : {}),
1094
+ ...(input.maxOutputTokens ? { maxOutputTokens: input.maxOutputTokens } : {}),
1095
+ abortSignal: input.signal ?? invocation.abortSignal,
1096
+ }),
1097
+ });
1098
+ const visibleAgentSessions = async (initiator) => {
1099
+ const sessions = await manager.listSessions();
1100
+ if (!initiator)
1101
+ return [];
1102
+ const visible = new Set([initiator.sessionId]);
1103
+ let changed = true;
1104
+ while (changed) {
1105
+ changed = false;
1106
+ for (const session of sessions) {
1107
+ if (!session.parentSessionId ||
1108
+ !visible.has(session.parentSessionId) ||
1109
+ visible.has(session.id))
1110
+ continue;
1111
+ visible.add(session.id);
1112
+ changed = true;
1113
+ }
1114
+ }
1115
+ return sessions.filter((session) => visible.has(session.id));
1116
+ };
1117
+ const pluginSessionSummary = (session) => Object.freeze({
1118
+ id: session.id,
1119
+ ...(session.name ? { title: session.name } : {}),
1120
+ ...(session.cwd ? { cwd: session.cwd } : {}),
1121
+ ...(session.status ? { status: session.status } : {}),
1122
+ ...(session.parentSessionId ? { parentSessionId: session.parentSessionId } : {}),
1123
+ ...(session.statusUpdatedAt || session.lastMessageAt
1124
+ ? { updatedAt: session.statusUpdatedAt ?? session.lastMessageAt }
1125
+ : {}),
1126
+ });
1127
+ const sessionQueryInitiator = (caller) => caller.invocation ??
1128
+ (caller.scopeSessionId ? Object.freeze({ sessionId: caller.scopeSessionId }) : undefined);
1129
+ pluginSessionQuery.bindRuntime({
1130
+ list: async (caller) => Object.freeze((await visibleAgentSessions(sessionQueryInitiator(caller))).map(pluginSessionSummary)),
1131
+ read: async (sessionId, caller) => {
1132
+ const session = (await visibleAgentSessions(sessionQueryInitiator(caller))).find(({ id }) => id === sessionId);
1133
+ if (!session)
1134
+ return undefined;
1135
+ return Object.freeze({
1136
+ session: pluginSessionSummary(session),
1137
+ messages: Object.freeze([
1138
+ ...(await requireSessionManager(manager).getMessages(sessionId)),
1139
+ ]),
1140
+ });
1141
+ },
1142
+ search: async (request, caller) => {
1143
+ const query = request.query.toLocaleLowerCase();
1144
+ const sessions = await visibleAgentSessions(sessionQueryInitiator(caller));
1145
+ const matches = [];
1146
+ for (const session of sessions) {
1147
+ const headerText = `${session.name}\n${session.cwd ?? ''}`.toLocaleLowerCase();
1148
+ if (headerText.includes(query)) {
1149
+ matches.push(session);
1150
+ continue;
1151
+ }
1152
+ const messages = await requireSessionManager(manager).getMessages(session.id);
1153
+ if (messages.some((message) => JSON.stringify(message).toLocaleLowerCase().includes(query))) {
1154
+ matches.push(session);
1155
+ }
1156
+ }
1157
+ const offset = request.cursor ? Number.parseInt(request.cursor, 10) : 0;
1158
+ if (!Number.isSafeInteger(offset) || offset < 0)
1159
+ throw new TypeError('Invalid Session query cursor');
1160
+ const limit = request.limit ?? 20;
1161
+ const page = matches.slice(offset, offset + limit);
1162
+ return Object.freeze({
1163
+ items: Object.freeze(page.map(pluginSessionSummary)),
1164
+ ...(offset + page.length < matches.length
1165
+ ? { cursor: String(offset + page.length) }
1166
+ : {}),
1167
+ });
1168
+ },
1169
+ });
1170
+ const describeAgent = (session) => ({
1171
+ id: session.id,
1172
+ sessionId: session.id,
1173
+ root: !session.parentSessionId,
1174
+ status: session.runningTurnIds?.length ? 'running' : session.status,
1175
+ ...(session.parentSessionId ? { ownerId: session.parentSessionId } : {}),
1176
+ });
1177
+ const submitAgentMessage = async (id, message, placement, initiator) => {
1178
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id)) {
1179
+ throw new Error('Agent is outside the current ownership tree');
1180
+ }
1181
+ const content = normalizeMessageContent(typeof message === 'string' ? { text: message } : message);
1182
+ const result = await messages.handlers['turn.message.submit']({
1183
+ originHostEpoch: context.hostEpoch,
1184
+ sessionId: id,
1185
+ messageId: randomUUID(),
1186
+ content,
1187
+ placement,
1188
+ }, {
1189
+ hostEpoch: context.hostEpoch,
1190
+ connectionId: 'plugin-agent',
1191
+ principal: 'runtime_host',
1192
+ acquireResidency: () => context.acquireResidency('plugin-agent'),
1193
+ });
1194
+ if (!result.ok)
1195
+ throw new Error(result.error.message);
1196
+ return result.result;
1197
+ };
1198
+ pluginAgents.bindRuntime({
1199
+ create: async (options, initiator) => {
1200
+ const spawn = initiator.toolContext?.spawnChildSession;
1201
+ if (!spawn)
1202
+ throw new Error('Agent creation requires an active Tool invocation');
1203
+ if (!options.prompt?.trim())
1204
+ throw new Error('Agent creation requires a prompt');
1205
+ return new Promise((resolve, reject) => {
1206
+ void spawn({
1207
+ agentProfile: options.agentProfile ?? 'implementation',
1208
+ prompt: options.prompt,
1209
+ ...(options.signal ? { abortSignal: options.signal } : {}),
1210
+ onReady: (ready) => resolve({
1211
+ id: ready.childSessionId,
1212
+ sessionId: ready.childSessionId,
1213
+ root: false,
1214
+ status: 'running',
1215
+ ownerId: initiator.sessionId,
1216
+ }),
1217
+ }).catch(reject);
1218
+ });
1219
+ },
1220
+ resume: async (options, initiator) => {
1221
+ if (options.prompt)
1222
+ await submitAgentMessage(options.sessionId, options.prompt, 'next_turn', initiator);
1223
+ const session = (await visibleAgentSessions(initiator)).find((item) => item.id === options.sessionId);
1224
+ if (!session)
1225
+ throw new Error('Agent was not found');
1226
+ return describeAgent(session);
1227
+ },
1228
+ get: async (id, initiator) => {
1229
+ const session = (await visibleAgentSessions(initiator)).find((item) => item.id === id);
1230
+ return session ? describeAgent(session) : undefined;
1231
+ },
1232
+ list: async (initiator) => (await visibleAgentSessions(initiator)).map(describeAgent),
1233
+ roots: async (initiator) => (await visibleAgentSessions(initiator))
1234
+ .filter((session) => !session.parentSessionId)
1235
+ .map(describeAgent),
1236
+ followup: (id, message, initiator) => submitAgentMessage(id, message, 'next_turn', initiator),
1237
+ steer: (id, message, initiator) => submitAgentMessage(id, message, 'current_turn', initiator),
1238
+ inject: (id, message, initiator) => submitAgentMessage(id, message, 'current_turn', initiator),
1239
+ cancel: async (id, initiator) => {
1240
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id)) {
1241
+ throw new Error('Agent is outside the current ownership tree');
1242
+ }
1243
+ await coordinator.stopSession(id, { source: 'stop_button' });
1244
+ },
1245
+ whenIdle: async (id, signal, initiator) => {
1246
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id)) {
1247
+ throw new Error('Agent is outside the current ownership tree');
1248
+ }
1249
+ const wait = coordinator.whenIdle(id);
1250
+ if (!wait)
1251
+ return;
1252
+ if (!signal)
1253
+ return wait;
1254
+ if (signal.aborted)
1255
+ throw signal.reason;
1256
+ let rejectAbort;
1257
+ const aborted = new Promise((_resolve, reject) => {
1258
+ rejectAbort = reject;
1259
+ });
1260
+ const onAbort = () => rejectAbort?.(signal.reason);
1261
+ signal.addEventListener('abort', onAbort, { once: true });
1262
+ try {
1263
+ await Promise.race([wait, aborted]);
1264
+ }
1265
+ finally {
1266
+ signal.removeEventListener('abort', onAbort);
1267
+ }
1268
+ },
1269
+ snapshot: async (id, initiator) => {
1270
+ const session = (await visibleAgentSessions(initiator)).find((item) => item.id === id);
1271
+ if (!session)
1272
+ throw new Error('Agent was not found');
1273
+ return { agent: describeAgent(session), root: coordinator.readRootState(id) };
1274
+ },
1275
+ inbox: async (id, initiator) => {
1276
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id))
1277
+ throw new Error('Agent was not found');
1278
+ return coordinator.readRootState(id);
1279
+ },
1280
+ result: async (id, initiator) => {
1281
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id))
1282
+ throw new Error('Agent was not found');
1283
+ return (await manager.getMessages(id)).at(-1);
1284
+ },
1285
+ artifacts: async (id, initiator) => {
1286
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id))
1287
+ throw new Error('Agent was not found');
1288
+ return (await openedArtifactStore.listPage(id, { offset: 0, limit: 100 })).records;
1289
+ },
1290
+ transcript: async (id, initiator) => {
1291
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id))
1292
+ throw new Error('Agent was not found');
1293
+ return manager.getMessages(id);
1294
+ },
1295
+ dispose: async (id, initiator) => {
1296
+ if (!(await visibleAgentSessions(initiator)).some((session) => session.id === id))
1297
+ throw new Error('Agent was not found');
1298
+ await coordinator.stopSession(id, { source: 'stop_button' });
1299
+ },
1300
+ });
932
1301
  const contextOperations = new HostContextCoordinator({
933
1302
  runtime: manager,
934
1303
  executions: coordinator,
@@ -1032,6 +1401,34 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1032
1401
  onProjectionChanged: (sessionId) => continuityCoordinator.enqueueCanonicalRefresh(sessionId),
1033
1402
  requestDrain: context.requestDrain,
1034
1403
  });
1404
+ pluginGoals.bindRuntime({
1405
+ execute: async (operation, invocation) => {
1406
+ const coordinator = requireGoal(goal);
1407
+ if (operation.kind === 'get')
1408
+ return coordinator.readProjection(invocation.sessionId);
1409
+ if (!invocation.toolContext)
1410
+ throw new Error('Goal mutation requires an active Tool invocation');
1411
+ const toolName = operation.kind === 'create'
1412
+ ? 'GoalSet'
1413
+ : operation.kind === 'clear'
1414
+ ? 'GoalClear'
1415
+ : operation.kind === 'pause'
1416
+ ? 'GoalPause'
1417
+ : 'GoalResume';
1418
+ const tool = coordinator.tools.find(({ name }) => name === toolName);
1419
+ if (!tool)
1420
+ throw new Error(`Goal capability is unavailable: ${toolName}`);
1421
+ const args = operation.kind === 'create'
1422
+ ? {
1423
+ condition: operation.objective,
1424
+ ...(operation.maxIterations ? { max_iterations: operation.maxIterations } : {}),
1425
+ ...(operation.blockCap ? { block_cap: operation.blockCap } : {}),
1426
+ ...(operation.tokenBudget ? { token_budget: operation.tokenBudget } : {}),
1427
+ }
1428
+ : {};
1429
+ return await tool.impl(args, invocation.toolContext);
1430
+ },
1431
+ });
1035
1432
  async function applyRuntimePolicyMutationEffects() {
1036
1433
  try {
1037
1434
  await requireMemory(memory).refreshAfterPolicyMutation();
@@ -1061,7 +1458,8 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1061
1458
  ? { sessionAccessAuthority: context.sessionAccessAuthority }
1062
1459
  : {}),
1063
1460
  });
1064
- const workHubCoordination = new HostWorkHubCoordinationCoordinator({
1461
+ workHubCoordination = new HostWorkHubCoordinationCoordinator({
1462
+ routingModel: dependencies.workHubRoutingModel,
1065
1463
  configureModel: (input) => sessionCatalog.configureWorkHubModel(input),
1066
1464
  transitionConfiguration: (input) => requireSessionManager(manager).transitionSessionConfiguration(WORKHUB_COORDINATION_SESSION_ID, input),
1067
1465
  stateRoot: context.owner.capability.canonicalPath,
@@ -1556,7 +1954,35 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1556
1954
  }),
1557
1955
  createRuntimeHostDomainModule({
1558
1956
  id: 'client-capability',
1559
- handlers: [clientCapabilities.handlers],
1957
+ handlers: [
1958
+ {
1959
+ ...clientCapabilities.handlers,
1960
+ 'client.capability.replace': async (input, operationContext) => {
1961
+ const outcome = await clientCapabilities.handlers['client.capability.replace'](input, operationContext);
1962
+ if (outcome.ok && rootRecoveryCompleted && !draining) {
1963
+ // Registration completes outside the policy mutation gate before
1964
+ // recovery selects bindings or composes a backend. Serialize
1965
+ // reconnects and retain the task for the shutdown barrier.
1966
+ clientBoundRecovery = clientBoundRecovery.then(async () => {
1967
+ if (draining)
1968
+ return;
1969
+ await coordinator.recover();
1970
+ await messages.consumePendingAdmissions(recoverySessions
1971
+ .filter((session) => session.toolProfile === 'workhub-coordination-v2')
1972
+ .map((session) => session.id));
1973
+ });
1974
+ try {
1975
+ await clientBoundRecovery;
1976
+ }
1977
+ catch (error) {
1978
+ context.requestDrain();
1979
+ throw error;
1980
+ }
1981
+ }
1982
+ return outcome;
1983
+ },
1984
+ },
1985
+ ],
1560
1986
  recovery: {
1561
1987
  resources: async () => {
1562
1988
  await recoverClientCapabilityOutcomes(stores.runtimeEventStore, recoverySessions.map((session) => session.id));
@@ -1630,6 +2056,7 @@ export async function createExecutionRuntimeHostComposition(context, options = {
1630
2056
  async () => {
1631
2057
  if (!rootRecoveryCompleted || poisonFailure)
1632
2058
  return;
2059
+ await clientBoundRecovery.catch(() => undefined);
1633
2060
  rootCloseTask ??= coordinator.close();
1634
2061
  await rootCloseTask;
1635
2062
  },
@@ -2028,6 +2455,24 @@ function requireGoal(coordinator) {
2028
2455
  throw new Error('Runtime Host Goal coordinator is not composed');
2029
2456
  return coordinator;
2030
2457
  }
2458
+ function pluginAttachmentArtifactKind(mimeType, name) {
2459
+ const kind = attachmentKindFromMimeType(mimeType, name);
2460
+ return kind === 'image' || kind === 'pdf' ? kind : 'file';
2461
+ }
2462
+ function pluginAttachmentRef(record) {
2463
+ const mimeType = record.mimeType ?? 'application/octet-stream';
2464
+ return {
2465
+ kind: attachmentKindFromMimeType(mimeType, record.name),
2466
+ name: record.name,
2467
+ mimeType,
2468
+ bytes: record.sizeBytes,
2469
+ ref: {
2470
+ kind: 'session_file',
2471
+ sessionId: record.sessionId,
2472
+ relativePath: record.relativePath,
2473
+ },
2474
+ };
2475
+ }
2031
2476
  /** Every run this Session has opened, named by the event spine that defines it. */
2032
2477
  async function sessionRunIds(runtimeEventStore, sessionId) {
2033
2478
  return (await runtimeEventStore.listSessionInvocations(sessionId)).map((invocation) => invocation.runId);