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

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 (154) hide show
  1. package/README.md +38 -19
  2. package/README.zh-CN.md +34 -19
  3. package/dist/mcp-capability-provider.js +4 -1
  4. package/dist/pi-transcript-tools.js +6 -0
  5. package/dist/pi-tui-layout.js +56 -10
  6. package/dist/pi-tui-mcp-status.js +2 -2
  7. package/dist/pi-tui-pickers.js +121 -30
  8. package/dist/pi-tui-runner.js +156 -42
  9. package/dist/pi-tui-todo.js +221 -0
  10. package/dist/pi-tui-transcript-viewer.js +134 -7
  11. package/dist/runtime-host-cli-context.js +70 -13
  12. package/dist/runtime-host-handoff-surface.js +51 -26
  13. package/dist/runtime-host-local-handoff.js +11 -1
  14. package/dist/runtime-host-service-manager.js +0 -3
  15. package/dist/runtime-host-session-channel.js +13 -0
  16. package/dist/runtime-host-session-driver.js +42 -1
  17. package/dist/tui-autocomplete-layout.js +3 -1
  18. package/dist/tui-copy-catalog.js +85 -6
  19. package/dist/tui-editor-render.js +36 -0
  20. package/dist/tui-mcp-control.js +41 -6
  21. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  22. package/node_modules/@maka/core/dist/agent-run.js +1 -0
  23. package/node_modules/@maka/core/dist/attachments.js +3 -0
  24. package/node_modules/@maka/core/dist/canonical-json.js +27 -0
  25. package/node_modules/@maka/core/dist/capabilities.js +35 -0
  26. package/node_modules/@maka/core/dist/computer-use.js +0 -1
  27. package/node_modules/@maka/core/dist/display-redaction.js +18 -0
  28. package/node_modules/@maka/core/dist/llm-connections.js +13 -0
  29. package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
  30. package/node_modules/@maka/core/dist/model-metadata.js +4 -0
  31. package/node_modules/@maka/core/dist/redaction.js +35 -0
  32. package/node_modules/@maka/core/dist/run-composition.js +77 -2
  33. package/node_modules/@maka/core/dist/runtime-event.js +3 -0
  34. package/node_modules/@maka/core/dist/session.js +57 -6
  35. package/node_modules/@maka/core/dist/slash-command-catalog.js +1 -0
  36. package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
  37. package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
  38. package/node_modules/@maka/core/package.json +3 -3
  39. package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
  40. package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
  41. package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
  42. package/node_modules/@maka/mcp/dist/index.js +337 -71
  43. package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
  44. package/node_modules/@maka/runtime/dist/agent-run.js +90 -2
  45. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +16 -8
  46. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +57 -50
  47. package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
  48. package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
  49. package/node_modules/@maka/runtime/dist/conversation-copy.js +153 -11
  50. package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
  51. package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
  52. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +69 -1
  53. package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
  54. package/node_modules/@maka/runtime/dist/model-fetcher.js +42 -2
  55. package/node_modules/@maka/runtime/dist/model-history.js +0 -112
  56. package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
  57. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
  58. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +8 -1
  59. package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
  60. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +1 -1
  61. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +24 -1
  62. package/node_modules/@maka/runtime/dist/runtime-kernel.js +127 -13
  63. package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
  64. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
  65. package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
  66. package/node_modules/@maka/runtime/dist/test-connection.js +0 -1
  67. package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
  68. package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
  69. package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
  70. package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
  71. package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
  72. package/node_modules/@maka/runtime/package.json +1 -0
  73. package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
  74. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +52 -13
  75. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +2 -0
  76. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
  77. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
  78. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +19 -0
  79. package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
  80. package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
  81. package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
  82. package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
  83. package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
  84. package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
  85. package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -1
  86. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
  87. package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
  88. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
  89. package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
  90. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
  91. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +86 -157
  92. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
  93. package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
  94. package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
  95. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
  96. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
  97. package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
  98. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +85 -25
  99. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +42 -14
  100. package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
  101. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
  102. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
  103. package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
  104. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
  105. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
  106. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +51 -23
  107. package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
  108. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  109. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
  110. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
  111. package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
  112. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
  113. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
  114. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
  115. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
  116. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
  117. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
  118. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
  119. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +59 -82
  120. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +132 -139
  121. package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
  122. package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
  123. package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
  124. package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
  125. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -1
  126. package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -196
  127. package/node_modules/@maka/storage/dist/credential-store.js +12 -63
  128. package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
  129. package/node_modules/@maka/storage/dist/git-exec.js +61 -0
  130. package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
  131. package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
  132. package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
  133. package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
  134. package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
  135. package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
  136. package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
  137. package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
  138. package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
  139. package/node_modules/@maka/storage/dist/session-bundle-policy.js +2 -1
  140. package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
  141. package/node_modules/@maka/storage/dist/session-store.js +14 -2
  142. package/node_modules/@maka/storage/dist/settings-store.js +8 -4
  143. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
  144. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +1 -2
  145. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
  146. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
  147. package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
  148. package/node_modules/@maka/storage/dist/storage-id.js +28 -0
  149. package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +31 -4
  150. package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
  151. package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
  152. package/package.json +1 -1
  153. package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
  154. package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
@@ -18,9 +18,11 @@
18
18
  */
19
19
  import { createHash } from 'node:crypto';
20
20
  import { decodeDurableToolResultProjection } from '@maka/core/durable-tool-result-projection';
21
+ import { durableToolResultProjectionDigest, } from '@maka/core/model-projection-transition';
21
22
  import { decodeLedgerTransition, reduceEffectiveModelProjections, } from './model-projection-transition-ledger.js';
22
23
  import { isArchivedToolResultPlaceholder, } from './tool-result-archive.js';
23
24
  import { serializeToolResultProjectionV1 } from './tool-result-archive-encoding.js';
25
+ import { TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_BYTES, TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_TRANSITIONS, } from '@maka/core/tool-result-archive-evidence';
24
26
  /**
25
27
  * Read-only v1 reconstruction. It never calls a live tool projector, reads an
26
28
  * Artifact, changes history, or authorizes access from a hash alone.
@@ -80,7 +82,13 @@ export function createLedgerToolResultArchiveReader(evidence) {
80
82
  const replacement = transition.replacement;
81
83
  const placeholder = replacement.kind === 'json' ? replacement.value : undefined;
82
84
  if (isArchivedToolResultPlaceholder(placeholder) &&
83
- placeholder.artifactId === request.artifactId) {
85
+ ((placeholder.rewriteVersion === 1 &&
86
+ request.rewriteVersion === 1 &&
87
+ placeholder.artifactId === request.artifactId) ||
88
+ (placeholder.rewriteVersion === 2 &&
89
+ request.rewriteVersion === 2 &&
90
+ placeholder.sourceProjectionDigest === request.sourceProjectionDigest &&
91
+ placeholder.previousTransitionId === request.previousTransitionId))) {
84
92
  if (placeholder.runtimeEventId !== request.runtimeEventId ||
85
93
  placeholder.toolCallId !== request.toolCallId ||
86
94
  placeholder.toolName !== request.toolName ||
@@ -88,6 +96,10 @@ export function createLedgerToolResultArchiveReader(evidence) {
88
96
  placeholder.originalBytes !== request.originalBytes ||
89
97
  placeholder.rewriteVersion !== request.rewriteVersion)
90
98
  return { ok: false, reason: 'source_mismatch' };
99
+ if (placeholder.rewriteVersion === 2 &&
100
+ (placeholder.sourceProjectionDigest !== transition.sourceProjectionDigest ||
101
+ placeholder.previousTransitionId !== transition.previousTransitionId))
102
+ return { ok: false, reason: 'corrupt' };
91
103
  const serializedResult = serializeToolResultProjectionV1(source);
92
104
  if (Buffer.byteLength(serializedResult, 'utf8') !== request.originalBytes)
93
105
  return { ok: false, reason: 'size_mismatch' };
@@ -104,3 +116,59 @@ export function createLedgerToolResultArchiveReader(evidence) {
104
116
  }
105
117
  };
106
118
  }
119
+ export function createLedgerArchiveResourceReader(evidence) {
120
+ const reader = createLedgerToolResultArchiveReader(evidence);
121
+ return (input) => reader({
122
+ ...input,
123
+ kind: 'maka.archived_tool_result',
124
+ rewriteVersion: 2,
125
+ originalEstimatedTokens: 1,
126
+ reason: 'stale_tool_result_pruned_before_compact',
127
+ });
128
+ }
129
+ /** Verify reconstructibility before committing a replacement; does not write any payload. */
130
+ export function createLedgerArchivePreparer(evidence) {
131
+ return async (input) => {
132
+ const request = { ...input };
133
+ const loaded = await evidence.read({
134
+ sessionId: request.sessionId,
135
+ runtimeEventId: request.runtimeEventId,
136
+ });
137
+ if (!loaded.ok ||
138
+ loaded.transitions.length >= TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_TRANSITIONS ||
139
+ loaded.storedBytes === undefined ||
140
+ loaded.storedBytes > TOOL_RESULT_ARCHIVE_EVIDENCE_MAX_BYTES - 64 * 1024)
141
+ return;
142
+ const content = loaded.event.content;
143
+ if (loaded.event.sessionId !== request.sessionId ||
144
+ loaded.event.id !== request.runtimeEventId ||
145
+ content?.kind !== 'function_response' ||
146
+ content.providerExecuted ||
147
+ !content.modelProjection ||
148
+ content.id !== request.toolCallId ||
149
+ content.name !== request.toolName)
150
+ return;
151
+ const transitions = [];
152
+ for (const event of loaded.transitions) {
153
+ const decoded = decodeLedgerTransition(event, request.sessionId);
154
+ if (!decoded || decoded.target.runtimeEventId !== request.runtimeEventId)
155
+ return;
156
+ transitions.push(decoded);
157
+ }
158
+ const reduced = reduceEffectiveModelProjections([loaded.event], transitions);
159
+ const previous = reduced.applied.at(-1);
160
+ if (previous?.transitionId !== request.previousTransitionId)
161
+ return;
162
+ const source = previous?.replacement ?? content.modelProjection;
163
+ if (source.kind === 'json' && isArchivedToolResultPlaceholder(source.value))
164
+ return;
165
+ if (durableToolResultProjectionDigest(source) !== request.sourceProjectionDigest)
166
+ return;
167
+ const body = serializeToolResultProjectionV1(source);
168
+ if (body !== request.serializedResult ||
169
+ Buffer.byteLength(body) !== request.originalBytes ||
170
+ createHash('sha256').update(body).digest('hex') !== request.bodySha256)
171
+ return;
172
+ return { ledger: true };
173
+ };
174
+ }
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import { createHash } from 'node:crypto';
20
20
  import { jsonSchema } from 'ai';
21
+ import { REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH } from '@maka/core/run-composition';
21
22
  import { truncateUtf16Safe } from '@maka/core/text-sanitize';
22
23
  const MAX_PROVIDER_TOOL_NAME = 64;
23
24
  const HASH_CHARS = 10;
@@ -49,8 +50,7 @@ export function buildMcpToolsWithIdentities(provider, options = {}) {
49
50
  toolName: descriptor.name,
50
51
  tool: {
51
52
  name,
52
- description: descriptor.description?.trim() ||
53
- `MCP tool ${descriptor.name} provided by ${descriptor.serverId}`,
53
+ description: mcpToolDescription(descriptor),
54
54
  displayName: descriptor.annotations?.title?.trim() || descriptor.name,
55
55
  activityKind: options.activityKindForDescriptor?.(descriptor) ?? 'tool',
56
56
  // MCP annotations are advisory provider claims, not a security boundary.
@@ -129,6 +129,11 @@ export function buildMcpToolsWithIdentities(provider, options = {}) {
129
129
  };
130
130
  });
131
131
  }
132
+ function mcpToolDescription(descriptor) {
133
+ const description = descriptor.description?.trim() ||
134
+ `MCP tool ${descriptor.name} provided by ${descriptor.serverId}`;
135
+ return description.slice(0, REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH);
136
+ }
132
137
  export function mcpProxyToolName(serverId, toolName) {
133
138
  const raw = `mcp__${sanitizeNamePart(serverId)}__${sanitizeNamePart(toolName)}`;
134
139
  if (raw.length <= MAX_PROVIDER_TOOL_NAME)
@@ -257,6 +257,12 @@ function normalizeConnectionEffectModels(models) {
257
257
  });
258
258
  }
259
259
  }
260
+ export class GitHubCopilotModelPolicyError extends Error {
261
+ constructor() {
262
+ super('GitHub Copilot model policy is not enabled');
263
+ this.name = 'GitHubCopilotModelPolicyError';
264
+ }
265
+ }
260
266
  export async function fetchGitHubCopilotModels(baseUrl, accessToken, fetchFn) {
261
267
  const response = await fetchForConnectionEffect(fetchFn, `${stripTrailing(baseUrl)}/models`, {
262
268
  headers: {
@@ -272,7 +278,12 @@ export async function fetchGitHubCopilotModels(baseUrl, accessToken, fetchFn) {
272
278
  throw new ConnectionEffectHttpError(response.status);
273
279
  }
274
280
  const payload = await readProviderJson(response);
275
- return providerObjectArray(payload.data, 'GitHub Copilot models', true).flatMap(toGitHubCopilotModelInfo);
281
+ const rawModels = providerObjectArray(payload.data, 'GitHub Copilot models', true);
282
+ const models = rawModels.flatMap(toGitHubCopilotModelInfo);
283
+ if (models.length === 0 && rawModels.some(isGitHubCopilotModelBlockedByPolicy)) {
284
+ throw new GitHubCopilotModelPolicyError();
285
+ }
286
+ return models;
276
287
  }
277
288
  /**
278
289
  * Discovery error carrying the HTTP status, so callers (syncOpenAiCodexConnection)
@@ -347,7 +358,12 @@ function toGitHubCopilotModelInfo(model) {
347
358
  if (typeof model.id !== 'string' ||
348
359
  !model.id ||
349
360
  model.model_picker_enabled !== true ||
350
- model.policy?.state === 'disabled' ||
361
+ // GitHub historically returned enabled/disabled/unconfigured policy gates.
362
+ // A policy-free model needs no acknowledgement; when the gate is present,
363
+ // Maka can use the model only after another client has enabled it. Maka has
364
+ // no policy-acceptance flow, so fail closed over unconfigured and unknown
365
+ // states instead of advertising a model that inference will reject.
366
+ !isGitHubCopilotModelPolicyEnabled(model.policy) ||
351
367
  model.capabilities?.supports?.tool_calls !== true)
352
368
  return [];
353
369
  assertOptionalArray(model.supported_endpoints, 'model supported_endpoints');
@@ -385,6 +401,28 @@ function toGitHubCopilotModelInfo(model) {
385
401
  },
386
402
  ];
387
403
  }
404
+ function isGitHubCopilotModelPolicyEnabled(policy) {
405
+ if (policy === undefined)
406
+ return true;
407
+ if (!policy || typeof policy !== 'object' || Array.isArray(policy))
408
+ return false;
409
+ return policy.state === 'enabled';
410
+ }
411
+ function isGitHubCopilotModelBlockedByPolicy(model) {
412
+ if (typeof model.id !== 'string' ||
413
+ !model.id ||
414
+ model.model_picker_enabled !== true ||
415
+ model.capabilities?.supports?.tool_calls !== true ||
416
+ !Array.isArray(model.supported_endpoints) ||
417
+ !model.supported_endpoints.some((endpoint) => ['/v1/messages', '/responses', '/chat/completions'].includes(endpoint))) {
418
+ return false;
419
+ }
420
+ if (!model.policy || typeof model.policy !== 'object' || Array.isArray(model.policy)) {
421
+ return false;
422
+ }
423
+ const state = model.policy.state;
424
+ return state === 'disabled' || state === 'unconfigured';
425
+ }
388
426
  async function fetchCohereModels(baseUrl, apiKey, fetchFn) {
389
427
  const root = stripTrailing(baseUrl).replace(/\/v2$/, '');
390
428
  const models = [];
@@ -655,6 +693,8 @@ function nextProviderPageToken(value) {
655
693
  return value;
656
694
  }
657
695
  function classifyDiscoveryError(error) {
696
+ if (error instanceof GitHubCopilotModelPolicyError)
697
+ return { kind: 'auth' };
658
698
  if (error instanceof ConnectionEffectFetchError)
659
699
  return { kind: error.kind };
660
700
  if (error instanceof ConnectionEffectHttpError) {
@@ -278,56 +278,6 @@ export function buildRuntimeEventReplayTimeline(items) {
278
278
  function replayToolIdentity(invocationId, toolCallId) {
279
279
  return JSON.stringify([invocationId, toolCallId]);
280
280
  }
281
- // ============================================================================
282
- // Projection
283
- // ============================================================================
284
- /**
285
- * Build the model-visible history from a RuntimeEvent stream.
286
- *
287
- * Events SHOULD be supplied in causal order; the projection preserves
288
- * input order. Partial events are always excluded — callers MUST NOT
289
- * replay transient streaming chunks into the next model call.
290
- *
291
- * The default options match the durable-history policy: user/model text
292
- * and tool calls/responses are kept; thinking, system notes, token usage,
293
- * permission acks, and diagnostics are dropped.
294
- */
295
- export function buildModelHistoryFromRuntimeEvents(events, options = {}) {
296
- const includeToolEvents = options.includeToolEvents ?? true;
297
- const includeSystemEvents = options.includeSystemEvents ?? false;
298
- const includeThinking = options.includeThinking ?? false;
299
- const out = [];
300
- for (const event of events) {
301
- // 1. Never replay transient streaming chunks.
302
- if (isPartialRuntimeEvent(event))
303
- continue;
304
- // 2. Only model-visible content kinds (text/thinking/function_*).
305
- if (!runtimeEventHasModelVisibleContent(event))
306
- continue;
307
- const content = event.content;
308
- if (!content)
309
- continue;
310
- // 3. System-role events are UI notes by default; opt in for
311
- // model-injected system instructions.
312
- if (event.role === 'system' && !includeSystemEvents)
313
- continue;
314
- // 4. Thinking replay is provider-specific; opt in.
315
- if (content.kind === 'thinking' && !includeThinking)
316
- continue;
317
- // 5. Tool function_call / function_response; opt out for text-only.
318
- if (!includeToolEvents &&
319
- (content.kind === 'function_call' || content.kind === 'function_response')) {
320
- continue;
321
- }
322
- out.push({
323
- role: event.role,
324
- content,
325
- ts: event.ts,
326
- eventId: event.id,
327
- });
328
- }
329
- return out;
330
- }
331
281
  /**
332
282
  * Collect the turn ids that contain tool activity (function_call /
333
283
  * function_response) from a RuntimeEvent ledger. Pass the result as
@@ -645,46 +595,6 @@ export function buildRuntimeEventModelReplayPlan(events, options = {}) {
645
595
  semanticKinds.includes('tool_result'),
646
596
  };
647
597
  }
648
- /**
649
- * Convert projected RuntimeEvent history into the current AI SDK text-only
650
- * message shape. Tool/function and thinking entries are intentionally skipped.
651
- */
652
- export function buildTextModelMessagesFromRuntimeEvents(events, options = {}) {
653
- const history = buildModelHistoryFromRuntimeEvents(events, {
654
- includeToolEvents: false,
655
- includeSystemEvents: options.includeSystemEvents ?? false,
656
- includeThinking: false,
657
- });
658
- const out = [];
659
- for (const entry of history) {
660
- if (entry.content.kind !== 'text')
661
- continue;
662
- if (entry.role === 'tool')
663
- continue;
664
- if (entry.role === 'system' && !options.includeSystemEvents)
665
- continue;
666
- const role = entry.role === 'model'
667
- ? 'assistant'
668
- : entry.role === 'user'
669
- ? 'user'
670
- : entry.role === 'system'
671
- ? 'system'
672
- : undefined;
673
- if (!role)
674
- continue;
675
- const steering = entry.content.steering === true && role === 'user';
676
- out.push({
677
- role,
678
- content: steering
679
- ? buildSteeringEnvelope(formatTextWithInlineRefs(entry.content))
680
- : formatTextWithInlineRefs(entry.content),
681
- // Keep the structured identity even in the text-only shape: dedupe
682
- // against the live injection set works by ledger event id.
683
- ...(steering ? { providerOptions: steeringProviderOptions(entry.eventId) } : {}),
684
- });
685
- }
686
- return out;
687
- }
688
598
  function modelTextRole(role) {
689
599
  switch (role) {
690
600
  case 'user':
@@ -770,28 +680,6 @@ export function steeringMessagesMissingFromBase(injected, baseMessages) {
770
680
  return eventId === undefined || !present.has(eventId);
771
681
  });
772
682
  }
773
- /**
774
- * The messages with THIS TURN'S injected steering removed (transport-retry
775
- * base). Only the injected set may be stripped: the retry attempt's own
776
- * request projection re-appends exactly that accumulator, while a historical,
777
- * ledger-replayed steering message (same marker, different event id) is part
778
- * of the base that nothing re-appends — stripping it would erase it from
779
- * every post-retry request.
780
- */
781
- export function stripSteeringMessages(messages, injected) {
782
- const ids = new Set();
783
- for (const message of injected) {
784
- const eventId = steeringEventIdOf(message);
785
- if (eventId !== undefined)
786
- ids.add(eventId);
787
- }
788
- if (ids.size === 0)
789
- return [...messages];
790
- return messages.filter((message) => {
791
- const eventId = steeringEventIdOf(message);
792
- return eventId === undefined || !ids.has(eventId);
793
- });
794
- }
795
683
  /**
796
684
  * Fold a user turn's inline references into its model-facing text. Attachments
797
685
  * render as a name/type block with exact Read instructions when the reference
@@ -269,10 +269,9 @@ function registerPluginEffect(ctx, label, register) {
269
269
  }
270
270
  export function registerPluginContribution(ctx, label, register) {
271
271
  if (ctx.makaTransaction) {
272
- ctx.makaTransaction.stage(label, register, ctx);
273
- return;
272
+ return ctx.makaTransaction.stage(label, register, ctx);
274
273
  }
275
- registerPluginEffect(ctx, label, register);
274
+ return registerPluginEffect(ctx, label, register);
276
275
  }
277
276
  export class MakaPluginTransactionBuffer {
278
277
  context;
@@ -283,13 +282,22 @@ export class MakaPluginTransactionBuffer {
283
282
  }
284
283
  stage(label, register, owner = this.context) {
285
284
  if (this.#state === 'committed') {
286
- registerPluginEffect(owner, label, register);
287
- return;
285
+ return registerPluginEffect(owner, label, register);
288
286
  }
289
287
  if (this.#state === 'rolled_back') {
290
288
  throw new MakaPluginRuntimeError('activation_failed', `Cannot stage contribution after transaction is ${this.#state}`);
291
289
  }
292
- this.#registrations.push({ label, register, owner });
290
+ const item = {
291
+ label,
292
+ register,
293
+ owner,
294
+ cancelled: false,
295
+ };
296
+ this.#registrations.push(item);
297
+ return async () => {
298
+ item.cancelled = true;
299
+ await item.release?.();
300
+ };
293
301
  }
294
302
  async commit() {
295
303
  if (this.#state === 'committed')
@@ -300,7 +308,10 @@ export class MakaPluginTransactionBuffer {
300
308
  const registered = [];
301
309
  try {
302
310
  for (const item of this.#registrations) {
303
- registered.push(registerPluginEffect(item.owner, item.label, item.register));
311
+ if (item.cancelled)
312
+ continue;
313
+ item.release = registerPluginEffect(item.owner, item.label, item.register);
314
+ registered.push(item.release);
304
315
  }
305
316
  this.#state = 'committed';
306
317
  this.#registrations.length = 0;
@@ -333,10 +344,9 @@ export class MakaPluginTransactionBuffer {
333
344
  export function fiberStateName(state) {
334
345
  return ['pending', 'loading', 'active', 'failed', 'disposed', 'unloading'][state];
335
346
  }
336
- export function isCanonicalPluginId(value) {
347
+ export function isCanonicalExtensionId(value) {
337
348
  return typeof value === 'string' && value.length <= 128 && ID_PATTERN.test(value);
338
349
  }
339
- export const isCanonicalExtensionId = isCanonicalPluginId;
340
350
  function cloneCompositionEntry(entry) {
341
351
  return {
342
352
  ...entry,
@@ -384,7 +394,7 @@ export function isCanonicalExtensionScopeId(value) {
384
394
  return (typeof value === 'string' && value.length <= 128 && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(value));
385
395
  }
386
396
  function validatePluginId(value, label) {
387
- if (!isCanonicalPluginId(value)) {
397
+ if (!isCanonicalExtensionId(value)) {
388
398
  throw new MakaPluginRuntimeError('invalid_entry', `Invalid ${label}`);
389
399
  }
390
400
  }
@@ -0,0 +1,205 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH } from '@maka/core/run-composition';
20
+ import { Service } from './plugin-kernel.js';
21
+ import { MakaPluginRuntimeError, pluginIdentity, registerPluginContribution, } from './plugin-runtime.js';
22
+ import { bindToolActivationIdentity } from './tool-activation-identity.js';
23
+ import { TOOL_SEARCH_NAME, TOOL_SEARCH_PROVIDER_NAME } from './tool-availability.js';
24
+ /**
25
+ * Context-scoped Tool contribution registry for trusted Host plugins.
26
+ *
27
+ * Registration is staged by the Plugin Platform transaction and owned by the
28
+ * registering Fiber. Profile registrations are inherited by Session roots;
29
+ * an exact Session registration shadows the Profile registration. Core tools
30
+ * remain Host-owned and cannot be shadowed.
31
+ */
32
+ export class PluginToolService extends Service {
33
+ layers = new Map();
34
+ onChanged;
35
+ constructor(ctx, options = {}) {
36
+ super(ctx, 'tools');
37
+ this.onChanged = options.onChanged;
38
+ }
39
+ register(definition) {
40
+ const identity = pluginIdentity(this.ctx);
41
+ if (identity.scopeId === 'desktop-ui') {
42
+ throw new MakaPluginRuntimeError('activation_failed', 'desktop-ui plugins cannot register Host tools');
43
+ }
44
+ validateTool(definition);
45
+ return registerPluginContribution(this.ctx, `tools.register(${JSON.stringify(definition.name)})`, () => this.publish(identity, definition));
46
+ }
47
+ resolve(sessionId, coreTools) {
48
+ const contributions = this.resolveContributions(sessionId, coreTools);
49
+ return Object.freeze({
50
+ tools: Object.freeze([...coreTools, ...contributions.tools]),
51
+ });
52
+ }
53
+ /** Resolve only Plugin-owned additions after validating them against the Host binding. */
54
+ resolveContributions(sessionId, coreTools) {
55
+ if (!sessionId || /[\r\n\0]/u.test(sessionId))
56
+ throw new Error('Invalid Tool Session scope');
57
+ const visible = new Map();
58
+ for (const entry of this.layers.get('profile')?.values() ?? []) {
59
+ visible.set(entry.definition.name, entry);
60
+ }
61
+ const sessionRoot = `session:${sessionId}`;
62
+ for (const entry of this.layers.get(sessionRoot)?.values() ?? []) {
63
+ visible.set(entry.definition.name, entry);
64
+ }
65
+ const coreNames = new Set(coreTools.map(({ name }) => name));
66
+ for (const name of visible.keys()) {
67
+ if (coreNames.has(name)) {
68
+ throw new MakaPluginRuntimeError('activation_failed', `Plugin Tool ${JSON.stringify(name)} conflicts with a Host-owned Tool`);
69
+ }
70
+ }
71
+ const entries = [...visible.values()].sort(compareRegistration);
72
+ return Object.freeze({
73
+ tools: Object.freeze(entries.map(({ exposed }) => exposed)),
74
+ });
75
+ }
76
+ inspect(rootId) {
77
+ const layers = rootId
78
+ ? [[rootId, this.layers.get(rootId)]]
79
+ : [...this.layers.entries()];
80
+ return Object.freeze(layers
81
+ .flatMap(([, layer]) => [...(layer?.values() ?? [])])
82
+ .sort(compareRegistration)
83
+ .map((entry) => Object.freeze({
84
+ entryId: entry.entryId,
85
+ scopeId: entry.scopeId,
86
+ extensionId: entry.extensionId,
87
+ generation: entry.generation,
88
+ toolName: entry.definition.name,
89
+ activeCalls: entry.activeCalls,
90
+ retired: entry.retired,
91
+ })));
92
+ }
93
+ publish(identity, definition) {
94
+ const rootId = identity.scopeId;
95
+ let layer = this.layers.get(rootId);
96
+ if (!layer) {
97
+ layer = new Map();
98
+ this.layers.set(rootId, layer);
99
+ }
100
+ const existing = layer.get(definition.name);
101
+ if (existing && existing.entryId !== identity.entryId) {
102
+ throw new MakaPluginRuntimeError('activation_failed', `Plugin Tool ${JSON.stringify(definition.name)} is already registered by ${existing.entryId}`);
103
+ }
104
+ let entry;
105
+ const exposed = bindToolActivationIdentity({
106
+ ...definition,
107
+ impl: async (args, context) => {
108
+ if (entry.retired) {
109
+ throw new Error(`Plugin Tool ${JSON.stringify(definition.name)} is no longer active`);
110
+ }
111
+ entry.activeCalls += 1;
112
+ try {
113
+ return await definition.impl(args, context);
114
+ }
115
+ finally {
116
+ entry.activeCalls -= 1;
117
+ if (entry.activeCalls === 0) {
118
+ for (const resolve of entry.drainWaiters)
119
+ resolve();
120
+ entry.drainWaiters.clear();
121
+ }
122
+ }
123
+ },
124
+ }, {
125
+ kind: 'plugin',
126
+ scopeId: identity.scopeId,
127
+ entryId: identity.entryId,
128
+ extensionId: identity.extensionId,
129
+ generation: identity.generation,
130
+ toolName: definition.name,
131
+ });
132
+ entry = {
133
+ ...identity,
134
+ definition,
135
+ exposed,
136
+ token: Symbol(definition.name),
137
+ activeCalls: 0,
138
+ retired: false,
139
+ drainWaiters: new Set(),
140
+ };
141
+ layer.set(definition.name, entry);
142
+ try {
143
+ this.notifyChanged(rootId);
144
+ }
145
+ catch (error) {
146
+ if (existing)
147
+ layer.set(definition.name, existing);
148
+ else
149
+ layer.delete(definition.name);
150
+ if (layer.size === 0)
151
+ this.layers.delete(rootId);
152
+ throw error;
153
+ }
154
+ return async () => {
155
+ entry.retired = true;
156
+ const currentLayer = this.layers.get(rootId);
157
+ if (currentLayer?.get(definition.name)?.token === entry.token) {
158
+ if (existing && !existing.retired)
159
+ currentLayer.set(definition.name, existing);
160
+ else
161
+ currentLayer.delete(definition.name);
162
+ if (currentLayer.size === 0)
163
+ this.layers.delete(rootId);
164
+ this.notifyChanged(rootId);
165
+ }
166
+ if (entry.activeCalls > 0) {
167
+ await new Promise((resolve) => entry.drainWaiters.add(resolve));
168
+ }
169
+ };
170
+ }
171
+ notifyChanged(rootId) {
172
+ this.ctx.emit('tools/change');
173
+ this.onChanged?.(rootId);
174
+ }
175
+ }
176
+ function validateTool(tool) {
177
+ if (!tool || typeof tool !== 'object')
178
+ throw new TypeError('Tool definition is required');
179
+ if (typeof tool.name !== 'string' ||
180
+ tool.name.length === 0 ||
181
+ tool.name.length > 128 ||
182
+ /[\r\n\0]/u.test(tool.name)) {
183
+ throw new TypeError('Tool requires a valid name');
184
+ }
185
+ if (typeof tool.description !== 'string' ||
186
+ tool.description.length === 0 ||
187
+ tool.description.length > REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH ||
188
+ typeof tool.impl !== 'function') {
189
+ throw new TypeError(`Tool ${JSON.stringify(tool.name)} requires a description of at most ${REQUEST_COMPOSITION_MAX_TOOL_DESCRIPTION_LENGTH} characters and an implementation`);
190
+ }
191
+ if (tool.parameters === undefined) {
192
+ throw new TypeError(`Tool ${JSON.stringify(tool.name)} requires an input schema`);
193
+ }
194
+ if (tool.providerTool) {
195
+ throw new TypeError(`Plugin Tool ${JSON.stringify(tool.name)} cannot claim a provider protocol`);
196
+ }
197
+ if (tool.name === TOOL_SEARCH_NAME || tool.name === TOOL_SEARCH_PROVIDER_NAME) {
198
+ throw new TypeError(`Plugin Tool name ${JSON.stringify(tool.name)} is reserved by Runtime`);
199
+ }
200
+ }
201
+ function compareRegistration(left, right) {
202
+ return (left.definition.name.localeCompare(right.definition.name) ||
203
+ left.scopeId.localeCompare(right.scopeId) ||
204
+ left.entryId.localeCompare(right.entryId));
205
+ }
@@ -199,14 +199,18 @@ export class ProviderRequestTracker {
199
199
  * that call, not new calls, so they share this id.
200
200
  */
201
201
  logicalCallIdByStep = new Map();
202
+ requestCompositionIdByStep = new Map();
202
203
  constructor(input) {
203
204
  this.input = input;
204
205
  }
205
206
  get traceId() {
206
207
  return this.input.traceId;
207
208
  }
208
- setStep(step) {
209
+ setStep(step, requestCompositionId) {
209
210
  this.step = step;
211
+ if (requestCompositionId !== undefined) {
212
+ this.requestCompositionIdByStep.set(step, requestCompositionId);
213
+ }
210
214
  }
211
215
  async trackStream(input) {
212
216
  throwIfAbortedBeforeDispatch(input.abortSignal);
@@ -356,6 +360,9 @@ export class ProviderRequestTracker {
356
360
  ? { connectionSlug: accounting.connectionSlug }
357
361
  : {}),
358
362
  step: Math.max(0, step),
363
+ ...(this.requestCompositionIdByStep.get(step)
364
+ ? { requestCompositionId: this.requestCompositionIdByStep.get(step) }
365
+ : {}),
359
366
  attempt: Math.max(0, attempt - 1),
360
367
  callKind: accounting.callKind,
361
368
  ...((input.historyCompactRoute ?? accounting.historyCompactRoute) !== undefined
@@ -152,6 +152,29 @@ export function toolCatalogHash(tools) {
152
152
  .sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0))
153
153
  .map(toolShapeForDiagnostics));
154
154
  }
155
+ /** Canonical, JSON-safe schemas for the exact provider-visible tool subset. */
156
+ export function requestCompositionToolSchemas(tools, activeToolNames) {
157
+ const active = new Set(activeToolNames);
158
+ return tools
159
+ .filter((tool) => active.has(tool.name))
160
+ .map((tool) => {
161
+ const inputSchema = JSON.parse(stableStringify(schemaShapeForHash(tool.parameters)));
162
+ if (!isPlainObject(inputSchema)) {
163
+ throw new Error(`Tool ${JSON.stringify(tool.name)} produced a non-object input schema`);
164
+ }
165
+ return {
166
+ name: tool.name,
167
+ description: tool.description,
168
+ inputSchema,
169
+ ...(tool.providerTool
170
+ ? {
171
+ providerTool: JSON.parse(stableStringify(tool.providerTool)),
172
+ }
173
+ : {}),
174
+ };
175
+ })
176
+ .sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
177
+ }
155
178
  export function stableStringify(value) {
156
179
  return JSON.stringify(canonicalize(value));
157
180
  }