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.
- package/README.md +38 -19
- package/README.zh-CN.md +34 -19
- package/dist/mcp-capability-provider.js +4 -1
- package/dist/pi-transcript-tools.js +6 -0
- package/dist/pi-tui-layout.js +56 -10
- package/dist/pi-tui-mcp-status.js +2 -2
- package/dist/pi-tui-pickers.js +121 -30
- package/dist/pi-tui-runner.js +156 -42
- package/dist/pi-tui-todo.js +221 -0
- package/dist/pi-tui-transcript-viewer.js +134 -7
- package/dist/runtime-host-cli-context.js +70 -13
- package/dist/runtime-host-handoff-surface.js +51 -26
- package/dist/runtime-host-local-handoff.js +11 -1
- package/dist/runtime-host-service-manager.js +0 -3
- package/dist/runtime-host-session-channel.js +13 -0
- package/dist/runtime-host-session-driver.js +42 -1
- package/dist/tui-autocomplete-layout.js +3 -1
- package/dist/tui-copy-catalog.js +85 -6
- package/dist/tui-editor-render.js +36 -0
- package/dist/tui-mcp-control.js +41 -6
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/agent-run.js +1 -0
- package/node_modules/@maka/core/dist/attachments.js +3 -0
- package/node_modules/@maka/core/dist/canonical-json.js +27 -0
- package/node_modules/@maka/core/dist/capabilities.js +35 -0
- package/node_modules/@maka/core/dist/computer-use.js +0 -1
- package/node_modules/@maka/core/dist/display-redaction.js +18 -0
- package/node_modules/@maka/core/dist/llm-connections.js +13 -0
- package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
- package/node_modules/@maka/core/dist/model-metadata.js +4 -0
- package/node_modules/@maka/core/dist/redaction.js +35 -0
- package/node_modules/@maka/core/dist/run-composition.js +77 -2
- package/node_modules/@maka/core/dist/runtime-event.js +3 -0
- package/node_modules/@maka/core/dist/session.js +57 -6
- package/node_modules/@maka/core/dist/slash-command-catalog.js +1 -0
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
- package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
- package/node_modules/@maka/core/package.json +3 -3
- package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
- package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
- package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
- package/node_modules/@maka/mcp/dist/index.js +337 -71
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
- package/node_modules/@maka/runtime/dist/agent-run.js +90 -2
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +16 -8
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +57 -50
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
- package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
- package/node_modules/@maka/runtime/dist/conversation-copy.js +153 -11
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
- package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +69 -1
- package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
- package/node_modules/@maka/runtime/dist/model-fetcher.js +42 -2
- package/node_modules/@maka/runtime/dist/model-history.js +0 -112
- package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +8 -1
- package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +1 -1
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +24 -1
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +127 -13
- package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
- package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
- package/node_modules/@maka/runtime/dist/test-connection.js +0 -1
- package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
- package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
- package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +52 -13
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
- package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +19 -0
- package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
- package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
- package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +9 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
- package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
- package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +86 -157
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
- package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
- package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
- package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +85 -25
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +42 -14
- package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
- package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +34 -4
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +51 -23
- package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +110 -14
- package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +59 -82
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +132 -139
- package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
- package/node_modules/@maka/storage/dist/agent-run-store.js +14 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
- package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -1
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -196
- package/node_modules/@maka/storage/dist/credential-store.js +12 -63
- package/node_modules/@maka/storage/dist/execution-stores.js +4 -1
- package/node_modules/@maka/storage/dist/git-exec.js +61 -0
- package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
- package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
- package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
- package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
- package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
- package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
- package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
- package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +2 -1
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
- package/node_modules/@maka/storage/dist/session-store.js +14 -2
- package/node_modules/@maka/storage/dist/settings-store.js +8 -4
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +1 -2
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +67 -3
- package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
- package/node_modules/@maka/storage/dist/storage-id.js +28 -0
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +31 -4
- package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
- package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
- 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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
}
|