@peers-app/peers-sdk 0.25.20 → 0.26.0
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/dist/contracts/__tests__/builder.test.js +0 -2
- package/dist/contracts/__tests__/contract-provider-router.test.js +51 -0
- package/dist/contracts/__tests__/contract-proxy.test.js +21 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +84 -0
- package/dist/contracts/__tests__/legacy-removal.test.d.ts +1 -0
- package/dist/contracts/__tests__/legacy-removal.test.js +90 -0
- package/dist/contracts/__tests__/system-contracts.test.js +88 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.js +44 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +446 -0
- package/dist/contracts/__tests__/tool-catalog.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog.test.js +649 -0
- package/dist/contracts/__tests__/workflow-context.test.d.ts +1 -0
- package/dist/contracts/__tests__/workflow-context.test.js +61 -0
- package/dist/contracts/builder.d.ts +9 -8
- package/dist/contracts/builder.js +34 -16
- package/dist/contracts/contract-provider-router.d.ts +12 -1
- package/dist/contracts/contract-provider-router.js +40 -2
- package/dist/contracts/contract-proxy.d.ts +20 -6
- package/dist/contracts/contract-proxy.js +23 -12
- package/dist/contracts/contract-resolvers.d.ts +22 -3
- package/dist/contracts/contract-resolvers.js +86 -7
- package/dist/contracts/extract.d.ts +7 -0
- package/dist/contracts/extract.js +3 -0
- package/dist/contracts/index.d.ts +11 -4
- package/dist/contracts/index.js +14 -2
- package/dist/contracts/registry.d.ts +3 -0
- package/dist/contracts/registry.js +2 -0
- package/dist/contracts/system/desktop-tools-contract.d.ts +5 -0
- package/dist/contracts/system/desktop-tools-contract.js +88 -0
- package/dist/contracts/system/host-tools-contract.d.ts +5 -0
- package/dist/contracts/system/host-tools-contract.js +76 -0
- package/dist/contracts/system/index.d.ts +3 -0
- package/dist/contracts/system/index.js +3 -0
- package/dist/contracts/system/tool-catalog-contract.d.ts +73 -0
- package/dist/contracts/system/tool-catalog-contract.js +70 -0
- package/dist/contracts/system-contracts.d.ts +3 -1
- package/dist/contracts/system-contracts.js +20 -6
- package/dist/contracts/tool-catalog-identity.d.ts +35 -0
- package/dist/contracts/tool-catalog-identity.js +88 -0
- package/dist/contracts/tool-catalog-migrations.d.ts +12 -0
- package/dist/contracts/tool-catalog-migrations.js +179 -0
- package/dist/contracts/tool-catalog-service.d.ts +30 -0
- package/dist/contracts/tool-catalog-service.js +287 -0
- package/dist/contracts/tool-catalog.d.ts +91 -0
- package/dist/contracts/tool-catalog.js +490 -0
- package/dist/contracts/types.d.ts +7 -5
- package/dist/data/messages.d.ts +3 -3
- package/dist/data/orm/sql.data-source.d.ts +5 -0
- package/dist/data/orm/sql.data-source.js +28 -10
- package/dist/data/orm/sql.data-source.test.d.ts +1 -1
- package/dist/data/orm/sql.data-source.test.js +160 -0
- package/dist/data/orm/table-container.d.ts +14 -0
- package/dist/data/orm/table-container.js +20 -0
- package/dist/data/tool-access-approvals.d.ts +6 -6
- package/dist/data/tool-catalog-fields.d.ts +31 -0
- package/dist/data/tool-catalog-fields.js +67 -0
- package/dist/data/tools.d.ts +38 -10
- package/dist/data/tools.js +31 -8
- package/dist/data/workflow-logs.d.ts +6 -2
- package/dist/data/workflow-logs.js +7 -5
- package/dist/data/workflow-runs.d.ts +6 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1 -3
- package/dist/package-installer/package-creator.d.ts +14 -5
- package/dist/package-installer/package-creator.js +92 -37
- package/dist/package-installer/package-creator.test.js +308 -23
- package/dist/package-installer/types.d.ts +8 -0
- package/dist/package-loader/contract-package-loader.d.ts +12 -3
- package/dist/package-loader/contract-package-loader.js +54 -50
- package/dist/package-loader/contract-package-loader.test.d.ts +1 -1
- package/dist/package-loader/contract-package-loader.test.js +172 -13
- package/dist/package-loader/host-authority-loading.test.js +16 -3
- package/dist/package-loader/isolated-artifact.d.ts +14 -2
- package/dist/package-loader/isolated-artifact.js +6 -0
- package/dist/package-loader/isolated-artifact.test.js +34 -0
- package/dist/package-loader/isolated-runtime.d.ts +14 -2
- package/dist/package-loader/isolated-table-gateway.d.ts +3 -1
- package/dist/package-loader/isolated-table-gateway.js +54 -5
- package/dist/package-loader/isolated-table-gateway.test.js +123 -0
- package/dist/package-loader/package-loader.d.ts +14 -2
- package/dist/package-loader/package-loader.js +184 -62
- package/dist/package-loader/package-loader.test.js +695 -2
- package/dist/package-loader/resolve-consumed-contracts.js +1 -1
- package/dist/system-ids.d.ts +6 -0
- package/dist/system-ids.js +7 -1
- package/dist/types/peers-package.d.ts +3 -2
- package/dist/types/workflow-run-context.d.ts +12 -2
- package/package.json +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -17
- package/dist/tools/tools-factory.d.ts +0 -5
- package/dist/tools/tools-factory.js +0 -34
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.workflowInvocableSystemContractIds = void 0;
|
|
4
|
+
exports.isWorkflowInvocableTarget = isWorkflowInvocableTarget;
|
|
5
|
+
exports.describeToolCatalogRow = describeToolCatalogRow;
|
|
6
|
+
exports.listToolCatalogEntries = listToolCatalogEntries;
|
|
7
|
+
exports.findToolCatalogEntry = findToolCatalogEntry;
|
|
8
|
+
exports.projectToolCatalogAccess = projectToolCatalogAccess;
|
|
9
|
+
exports.invokeToolCatalogTool = invokeToolCatalogTool;
|
|
10
|
+
const group_permissions_1 = require("../data/group-permissions");
|
|
11
|
+
const types_1 = require("../data/orm/types");
|
|
12
|
+
const tool_access_1 = require("../data/tool-access");
|
|
13
|
+
const tools_1 = require("../data/tools");
|
|
14
|
+
const system_ids_1 = require("../system-ids");
|
|
15
|
+
const field_type_1 = require("../types/field-type");
|
|
16
|
+
const utils_1 = require("../utils");
|
|
17
|
+
const contract_provider_router_1 = require("./contract-provider-router");
|
|
18
|
+
const contract_resolvers_1 = require("./contract-resolvers");
|
|
19
|
+
const system_contracts_1 = require("./system-contracts");
|
|
20
|
+
const tool_catalog_1 = require("./tool-catalog");
|
|
21
|
+
const tool_catalog_identity_1 = require("./tool-catalog-identity");
|
|
22
|
+
const tool_catalog_migrations_1 = require("./tool-catalog-migrations");
|
|
23
|
+
/** System tools admitted to workflows; administrative contracts remain excluded. */
|
|
24
|
+
exports.workflowInvocableSystemContractIds = new Set([
|
|
25
|
+
system_ids_1.hostToolsContractId,
|
|
26
|
+
system_ids_1.desktopToolsContractId,
|
|
27
|
+
]);
|
|
28
|
+
/** Whether a contract target may execute inside a workflow or assistant run. */
|
|
29
|
+
function isWorkflowInvocableTarget(target) {
|
|
30
|
+
return (!(0, system_contracts_1.isSystemContractId)(target.contractId) ||
|
|
31
|
+
exports.workflowInvocableSystemContractIds.has(target.contractId));
|
|
32
|
+
}
|
|
33
|
+
function liveResolver(dataContext, target, provider) {
|
|
34
|
+
const implementation = dataContext.packageLoader.contractRegistry.getProviderImplementation(target.contractId, target.version, provider) ?? target;
|
|
35
|
+
return (0, contract_resolvers_1.getContractResolver)(implementation.contractId, implementation.version, provider, dataContext);
|
|
36
|
+
}
|
|
37
|
+
/** Synchronously describe a row using registry state only. Callers await catalogReady first. */
|
|
38
|
+
function describeToolCatalogRow(dataContext, row) {
|
|
39
|
+
const entry = {
|
|
40
|
+
toolId: row.toolId,
|
|
41
|
+
aliases: [],
|
|
42
|
+
name: row.name,
|
|
43
|
+
usageDescription: row.usageDescription,
|
|
44
|
+
inputSchema: row.inputSchema,
|
|
45
|
+
outputSchema: row.outputSchema,
|
|
46
|
+
catalogState: row.catalogState,
|
|
47
|
+
catalogIdentity: row.catalogIdentity,
|
|
48
|
+
workflowInvocable: false,
|
|
49
|
+
availability: {
|
|
50
|
+
dispatch: "unavailable",
|
|
51
|
+
reason: row.packageId ? "legacy-package-upgrade-required" : "legacy-execution-removed",
|
|
52
|
+
message: row.packageId
|
|
53
|
+
? `Package ${row.packageId} must be upgraded to implement this tool through a contract resolver (docs/Packages/contracts.md#migrating-from-toolinstances).`
|
|
54
|
+
: "Legacy tool execution has been removed; implement this tool through a contract resolver (docs/Packages/contracts.md#migrating-from-toolinstances).",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
(0, tool_catalog_1.validateToolCatalogWrite)(undefined, row);
|
|
59
|
+
entry.target = (0, tool_catalog_1.toolCatalogTarget)(row) ?? (0, tool_catalog_migrations_1.resolveLegacyToolId)(row.toolId);
|
|
60
|
+
entry.aliases = entry.target ? (0, tool_catalog_migrations_1.toolCatalogIds)(entry.target) : [row.toolId];
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
entry.availability.reason = "invalid-identity";
|
|
64
|
+
entry.availability.message =
|
|
65
|
+
"Tool identity is invalid; inspect the catalog conflict and reinstall its provider.";
|
|
66
|
+
return entry;
|
|
67
|
+
}
|
|
68
|
+
if (row.catalogState === "quarantined") {
|
|
69
|
+
entry.availability.reason = "quarantined";
|
|
70
|
+
entry.availability.message =
|
|
71
|
+
"Tool identity is quarantined; resolve the catalog identity conflict before running it.";
|
|
72
|
+
return entry;
|
|
73
|
+
}
|
|
74
|
+
const target = entry.target;
|
|
75
|
+
if (target) {
|
|
76
|
+
const loader = dataContext.packageLoader;
|
|
77
|
+
const registry = loader.contractRegistry;
|
|
78
|
+
const provider = registry.getProviderPackageId(target.contractId, target.version);
|
|
79
|
+
entry.provider = {
|
|
80
|
+
activePackageId: provider,
|
|
81
|
+
registeredPackageIds: registry.getRegisteredProviderIds(target.contractId, target.version),
|
|
82
|
+
implementation: provider
|
|
83
|
+
? registry.getProviderImplementation(target.contractId, target.version, provider)
|
|
84
|
+
: undefined,
|
|
85
|
+
};
|
|
86
|
+
if (provider) {
|
|
87
|
+
const member = registry
|
|
88
|
+
.resolve(target.contractId, target.version)
|
|
89
|
+
?.tools.find((t) => t.name === target.toolName);
|
|
90
|
+
if (!member) {
|
|
91
|
+
entry.availability.reason = "member-removed";
|
|
92
|
+
entry.availability.message = `Provider ${provider} no longer declares ${target.toolName}; install a version that declares and implements it.`;
|
|
93
|
+
return entry;
|
|
94
|
+
}
|
|
95
|
+
if (loader.isolatedRuntime?.isInstalled(provider) ||
|
|
96
|
+
liveResolver(dataContext, target, provider)) {
|
|
97
|
+
entry.availability = {
|
|
98
|
+
dispatch: "contract",
|
|
99
|
+
requiresWorkflowRun: member.requiresWorkflowRun,
|
|
100
|
+
};
|
|
101
|
+
entry.workflowInvocable = isWorkflowInvocableTarget(target);
|
|
102
|
+
return entry;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
entry.availability.reason = provider ? "no-implementation" : "no-provider";
|
|
106
|
+
entry.availability.message = provider
|
|
107
|
+
? `Provider ${provider} declares ${target.toolName} but registers no implementation; install a version that implements it.`
|
|
108
|
+
: `No provider is installed for ${target.contractId}@${target.version}; install a package that implements ${target.toolName}.`;
|
|
109
|
+
}
|
|
110
|
+
return entry;
|
|
111
|
+
}
|
|
112
|
+
/** List bounded catalog rows after the context's catalog initialization. */
|
|
113
|
+
async function listToolCatalogEntries(dataContext, input = {}) {
|
|
114
|
+
await dataContext.packageLoader.catalogReady;
|
|
115
|
+
const rows = await (0, tools_1.Tools)(dataContext).list({}, { pageSize: Math.min(input.limit ?? 100, 500), textSearch: input.query });
|
|
116
|
+
return rows.map((row) => describeToolCatalogRow(dataContext, row));
|
|
117
|
+
}
|
|
118
|
+
/** Resolve one identity without letting unavailable IDs fall through to unrelated names. */
|
|
119
|
+
async function findToolCatalogEntry(dataContext, input, callerContext) {
|
|
120
|
+
await dataContext.packageLoader.catalogReady;
|
|
121
|
+
const table = (0, tools_1.Tools)(dataContext);
|
|
122
|
+
const exact = await table.get(input.toolIdOrName);
|
|
123
|
+
const choose = (rows) => {
|
|
124
|
+
const entries = rows
|
|
125
|
+
.map((row) => describeToolCatalogRow(dataContext, row))
|
|
126
|
+
.filter((entry) => !input.runnableOnly || entry.availability.dispatch !== "unavailable");
|
|
127
|
+
const groups = new Map(entries.map((entry) => [
|
|
128
|
+
entry.target ? (0, tool_catalog_identity_1.toolCatalogTargetKey)(entry.target) : entry.toolId,
|
|
129
|
+
entry,
|
|
130
|
+
]));
|
|
131
|
+
if (groups.size > 1)
|
|
132
|
+
throw new Error(`Ambiguous tool "${input.toolIdOrName}": matched ${groups.size} tools (${entries.map((entry) => entry.toolId).join(", ")}); use a tool ID`);
|
|
133
|
+
return entries[0];
|
|
134
|
+
};
|
|
135
|
+
const entry = exact
|
|
136
|
+
? describeToolCatalogRow(dataContext, exact)
|
|
137
|
+
: (choose(await table.list({ name: input.toolIdOrName })) ??
|
|
138
|
+
choose(await table.list({}, { textSearch: input.toolIdOrName })));
|
|
139
|
+
if (entry && callerContext)
|
|
140
|
+
await projectToolCatalogAccess(dataContext, entry, callerContext);
|
|
141
|
+
if (entry && input.runnableOnly && entry.availability.dispatch === "unavailable")
|
|
142
|
+
return undefined;
|
|
143
|
+
return entry;
|
|
144
|
+
}
|
|
145
|
+
/** Project live policy without resolving endpoints or starting isolated workers. */
|
|
146
|
+
async function projectToolCatalogAccess(dataContext, entry, callerContext) {
|
|
147
|
+
const userId = callerContext.callerUserId ?? callerContext.localUserId;
|
|
148
|
+
if (entry?.availability.dispatch === "contract" && entry.target && userId) {
|
|
149
|
+
const provider = entry.provider?.activePackageId;
|
|
150
|
+
if (!provider)
|
|
151
|
+
return entry;
|
|
152
|
+
const runtime = dataContext.packageLoader.isolatedRuntime;
|
|
153
|
+
const accessLevel = runtime?.isInstalled(provider)
|
|
154
|
+
? runtime.getEffectiveToolLevel?.(entry.target, provider)
|
|
155
|
+
: (0, tool_access_1.recordAccessLevel)((await liveResolver(dataContext, entry.target, provider)?.({ dataContext }))?.tools?.[entry.target.toolName]?.tool);
|
|
156
|
+
if (accessLevel !== undefined) {
|
|
157
|
+
const callerAccessLevel = await (0, group_permissions_1.getUserAccessLevel)(dataContext, userId);
|
|
158
|
+
entry.access = {
|
|
159
|
+
accessLevel,
|
|
160
|
+
callerAccessLevel,
|
|
161
|
+
allowed: (0, tool_access_1.decideToolAccess)({
|
|
162
|
+
userAccessLevel: callerAccessLevel,
|
|
163
|
+
toolAccessLevel: accessLevel,
|
|
164
|
+
assistantAccessLevel: callerContext?.authority?.assistantAccessLevel,
|
|
165
|
+
}).kind === "allow",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return entry;
|
|
170
|
+
}
|
|
171
|
+
function requirePlainObject(value, label) {
|
|
172
|
+
if (!value ||
|
|
173
|
+
typeof value !== "object" ||
|
|
174
|
+
(Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null))
|
|
175
|
+
throw new Error(`${label} requires a plain object`);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Validate and invoke a contract target through the selected provider with original authority.
|
|
179
|
+
* Never dispatches legacy code. Disposes every endpoint and retries only its own retirement once.
|
|
180
|
+
*/
|
|
181
|
+
async function invokeToolCatalogTool(dataContext, input, context, opts = {}) {
|
|
182
|
+
await dataContext.packageLoader.catalogReady;
|
|
183
|
+
requirePlainObject(input, "invokeTool");
|
|
184
|
+
for (const key of Reflect.ownKeys(input))
|
|
185
|
+
if (!["toolId", "args", "providerPackageId", "fallbackToDefaultProvider"].includes(String(key)) ||
|
|
186
|
+
typeof key !== "string")
|
|
187
|
+
throw new Error(`Unexpected invokeTool input key: ${String(key)}`);
|
|
188
|
+
if (context?.dataContext !== dataContext || !(context.callerUserId ?? context.localUserId))
|
|
189
|
+
throw new Error("Tool catalog requires an authenticated caller in the same data context");
|
|
190
|
+
if (context.callerDeviceId && !context.callerUserId)
|
|
191
|
+
throw new Error("Remote catalog invocation requires the caller user identity");
|
|
192
|
+
if (context.workflow && context.workflow.dataContextId !== dataContext.dataContextId)
|
|
193
|
+
throw new Error("Workflow data context does not match the catalog call");
|
|
194
|
+
if (context.callerPackageId || (context.callerDeviceId && !opts.allowRemoteCaller))
|
|
195
|
+
throw new Error("Tool catalog invocation requires a local user caller");
|
|
196
|
+
if (typeof input.toolId !== "string" ||
|
|
197
|
+
(input.providerPackageId !== undefined &&
|
|
198
|
+
(typeof input.providerPackageId !== "string" || !(0, utils_1.isid)(input.providerPackageId))) ||
|
|
199
|
+
(input.fallbackToDefaultProvider !== undefined &&
|
|
200
|
+
typeof input.fallbackToDefaultProvider !== "boolean"))
|
|
201
|
+
throw new Error("Invalid invokeTool input");
|
|
202
|
+
const row = await (0, tools_1.Tools)(dataContext).get(input.toolId);
|
|
203
|
+
if (!row)
|
|
204
|
+
throw new Error(`Tool not found: ${input.toolId}`);
|
|
205
|
+
(0, tool_catalog_1.validateToolCatalogWrite)(undefined, row);
|
|
206
|
+
const target = (0, tool_catalog_1.toolCatalogTarget)(row) ?? (0, tool_catalog_migrations_1.resolveLegacyToolId)(row.toolId);
|
|
207
|
+
if (row.catalogState === "quarantined" || !target || target.contractId === system_ids_1.toolCatalogContractId)
|
|
208
|
+
throw new Error("Tool catalog target is quarantined, unresolved, or the catalog itself");
|
|
209
|
+
if (opts.allowRemoteCaller && (0, contract_resolvers_1.isLocalOnlyContractId)(target.contractId))
|
|
210
|
+
throw new Error("Tool target is local-only");
|
|
211
|
+
if (context.workflow && !isWorkflowInvocableTarget(target))
|
|
212
|
+
throw new Error("Tool target is not available to workflows");
|
|
213
|
+
const loader = dataContext.packageLoader;
|
|
214
|
+
await loader.toolCatalog.assertTargetUsable(target);
|
|
215
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
216
|
+
const selected = loader.contractRegistry.selectProvider({
|
|
217
|
+
contractId: target.contractId,
|
|
218
|
+
version: target.version,
|
|
219
|
+
providerPackageIds: input.providerPackageId ? [input.providerPackageId] : undefined,
|
|
220
|
+
fallbackToDefaultProvider: input.fallbackToDefaultProvider,
|
|
221
|
+
});
|
|
222
|
+
if (!selected)
|
|
223
|
+
throw new Error(`Strict provider selection failed (no-provider) for ${target.contractId} v${target.version}`);
|
|
224
|
+
const providerPackageId = selected.providerPackageId;
|
|
225
|
+
const tool = selected.definition.tools.find((t) => t.name === target.toolName);
|
|
226
|
+
if (!tool)
|
|
227
|
+
throw new Error(`Tool member-removed: ${target.toolName}`);
|
|
228
|
+
if (tool.requiresWorkflowRun &&
|
|
229
|
+
(!context.workflow || loader.isolatedRuntime?.isInstalled(providerPackageId)))
|
|
230
|
+
throw new Error(`Tool ${target.toolName} requires a host workflow run`);
|
|
231
|
+
if (!loader.isolatedRuntime?.isInstalled(providerPackageId) &&
|
|
232
|
+
!liveResolver(dataContext, target, providerPackageId))
|
|
233
|
+
throw new Error(`Tool no-implementation: ${providerPackageId}`);
|
|
234
|
+
if (opts.maxAccessLevel !== undefined) {
|
|
235
|
+
const liveLevel = loader.isolatedRuntime?.isInstalled(providerPackageId)
|
|
236
|
+
? loader.isolatedRuntime.getEffectiveToolLevel?.(target, providerPackageId)
|
|
237
|
+
: (0, tool_access_1.recordAccessLevel)((await liveResolver(dataContext, target, providerPackageId)?.({ dataContext }))
|
|
238
|
+
?.tools?.[target.toolName]?.tool);
|
|
239
|
+
if (liveLevel === undefined || liveLevel > opts.maxAccessLevel)
|
|
240
|
+
throw new Error("Tool provider access level exceeds the authorized workflow call");
|
|
241
|
+
}
|
|
242
|
+
const source = input.args === undefined ? {} : input.args;
|
|
243
|
+
requirePlainObject(source, "Tool args");
|
|
244
|
+
const args = { ...source };
|
|
245
|
+
for (const field of tool.inputFields)
|
|
246
|
+
if (field.type === field_type_1.FieldType.Date && !field.isArray && typeof args[field.name] === "string")
|
|
247
|
+
args[field.name] = new Date(args[field.name]);
|
|
248
|
+
const validated = (0, types_1.fieldsToSchema)(tool.inputFields).passthrough().safeParse(args);
|
|
249
|
+
if (!validated.success)
|
|
250
|
+
throw new Error(`Tool argument validation failed: ${validated.error.message}`);
|
|
251
|
+
const dataContextId = dataContext.dataContextId;
|
|
252
|
+
const { workflow: _workflow, ...caller } = context;
|
|
253
|
+
const endpointContext = loader.isolatedRuntime?.isInstalled(providerPackageId)
|
|
254
|
+
? caller
|
|
255
|
+
: context;
|
|
256
|
+
const endpoint = await loader.resolveContractEndpoint({
|
|
257
|
+
contractId: target.contractId,
|
|
258
|
+
version: target.version,
|
|
259
|
+
providerPackageId,
|
|
260
|
+
dataContextId,
|
|
261
|
+
context: endpointContext,
|
|
262
|
+
notify: async () => undefined,
|
|
263
|
+
}, { providerPackageId, resolveVariable: opts.resolveVariable });
|
|
264
|
+
try {
|
|
265
|
+
return await endpoint.handleCall({
|
|
266
|
+
contractId: target.contractId,
|
|
267
|
+
version: target.version,
|
|
268
|
+
member: "tool",
|
|
269
|
+
operation: "run",
|
|
270
|
+
name: target.toolName,
|
|
271
|
+
args: [args],
|
|
272
|
+
providerPackageId,
|
|
273
|
+
dataContextId,
|
|
274
|
+
}, endpointContext);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
if (attempt !== 0 ||
|
|
278
|
+
!(error instanceof contract_provider_router_1.RetiredContractProviderEndpointError) ||
|
|
279
|
+
error.endpoint !== endpoint)
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
endpoint.dispose();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
throw new Error("Tool provider retired");
|
|
287
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { DataContext } from "../context/data-context";
|
|
2
|
+
import { type ITool } from "../data/tools";
|
|
3
|
+
import type { ContractRegistry, IContractProviderRegistration } from "./registry";
|
|
4
|
+
import { type ToolCatalogTarget } from "./tool-catalog-identity";
|
|
5
|
+
/** Supplemental metadata keyed by a declared public contract member, never by executable IDs. */
|
|
6
|
+
export type IToolCatalogMetadata = ToolCatalogTarget & Pick<Partial<ITool>, "isAssistantRunner" | "configSchema" | "detailedDescription">;
|
|
7
|
+
/** Read a complete canonical target; partial mappings are invalid, not legacy tools. */
|
|
8
|
+
export declare function toolCatalogTarget(row: Partial<ITool>): ToolCatalogTarget | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Mixed-version sync: runtimes that predate the catalog columns reconstruct and write
|
|
11
|
+
* full rows without them. When a synced candidate carries none of the catalog-owned
|
|
12
|
+
* fields, keep the established row's identity and state rather than rejecting the
|
|
13
|
+
* write as a retarget. A candidate carrying any catalog field is returned unchanged
|
|
14
|
+
* and validated as written, so partial stripping and retargeting still fail.
|
|
15
|
+
*
|
|
16
|
+
* @param existing - Materialized row, if any.
|
|
17
|
+
* @param candidate - Reconstructed row about to replace it.
|
|
18
|
+
* @returns The row to validate and write.
|
|
19
|
+
*/
|
|
20
|
+
export declare function retainToolCatalogFields<T extends Partial<ITool>>(existing: Partial<ITool> | undefined, candidate: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* Shared pre-persistence identity guard for local writes and reconstructed sync rows.
|
|
23
|
+
* Existing mappings cannot be retargeted, stripped or deleted. Rejected sync evidence
|
|
24
|
+
* belongs in the sync recovery queue; it must not replace the established row.
|
|
25
|
+
* Pass recordId for deletes so reserved identities are protected before materialization.
|
|
26
|
+
*
|
|
27
|
+
* Catalog identities are permanent. Unresolved legacy rows are a holdover until their
|
|
28
|
+
* tools move to contracts: they stay ordinary rows that local writers and older
|
|
29
|
+
* runtimes may delete.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateToolCatalogWrite(existing: Partial<ITool> | undefined, candidate: Partial<ITool> | undefined, recordId?: string | undefined): void;
|
|
32
|
+
/**
|
|
33
|
+
* Per-context runtime owner of permanent Tools identities. Construction is inert;
|
|
34
|
+
* ready() explicitly initializes storage after UserContext construction completes.
|
|
35
|
+
* No worker, resolver or executable ToolInstance is needed to index public members.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ToolCatalog {
|
|
38
|
+
readonly dataContext: DataContext;
|
|
39
|
+
readonly registry: ContractRegistry;
|
|
40
|
+
private tail;
|
|
41
|
+
private initialization?;
|
|
42
|
+
private subscription?;
|
|
43
|
+
private disposed;
|
|
44
|
+
private reconciliationQueued;
|
|
45
|
+
/** Bind to the registry shared by host and isolated providers in this context. */
|
|
46
|
+
constructor(dataContext: DataContext, registry: ContractRegistry);
|
|
47
|
+
private enqueue;
|
|
48
|
+
/** Await successful backfill and system publication. Failed initialization can be retried. */
|
|
49
|
+
ready(): Promise<void>;
|
|
50
|
+
private assertLive;
|
|
51
|
+
private validate;
|
|
52
|
+
/**
|
|
53
|
+
* Serialize validation, catalog persistence, package preparation and activation.
|
|
54
|
+
* A failed preparation never publishes the candidate or changes live tool policy.
|
|
55
|
+
* Successful catalog additions remain historical even if later preparation fails.
|
|
56
|
+
* Descriptive metadata of existing rows is refreshed only after preparation
|
|
57
|
+
* succeeds, so a failed replacement leaves the active provider's rows unchanged.
|
|
58
|
+
* Async activation must call the supplied guard in its final synchronous commit
|
|
59
|
+
* turn, before publishing providers or changing live bindings. The guard observes
|
|
60
|
+
* catalog changes immediately, independently of queued reconciliation.
|
|
61
|
+
*/
|
|
62
|
+
publish<T>(provider: string, registrations: readonly IContractProviderRegistration[], prepare: () => Promise<void>, activate: (assertCatalogCurrent: () => void) => T | Promise<T>, toolMetadata?: readonly IToolCatalogMetadata[]): Promise<T>;
|
|
63
|
+
private guardActivation;
|
|
64
|
+
private definitions;
|
|
65
|
+
/**
|
|
66
|
+
* Insert every public member's catalog rows, validating the batch first. With
|
|
67
|
+
* `refresh`, existing rows with absent or matching provider provenance also take the
|
|
68
|
+
* definition's name, usage description and IO schemas.
|
|
69
|
+
*/
|
|
70
|
+
private materialize;
|
|
71
|
+
/**
|
|
72
|
+
* Patch the listed descriptive fields whose stored values differ. A marked patch
|
|
73
|
+
* leaves every other column, including concurrently synced policy, untouched.
|
|
74
|
+
*/
|
|
75
|
+
private refresh;
|
|
76
|
+
private reconcile;
|
|
77
|
+
/**
|
|
78
|
+
* Patch supplemental metadata of existing system members, preserving identity,
|
|
79
|
+
* historical source and provenance, descriptions and all authorization policy.
|
|
80
|
+
* @throws If any entry is unresolved, quarantined or outside the system inventory.
|
|
81
|
+
*/
|
|
82
|
+
saveToolMetadata(entries: readonly (Partial<ITool> & {
|
|
83
|
+
toolId: string;
|
|
84
|
+
})[]): Promise<void>;
|
|
85
|
+
/** Reject quarantined or conflicting retained evidence before a contract tool call. */
|
|
86
|
+
assertTargetUsable(target: ToolCatalogTarget): Promise<void>;
|
|
87
|
+
/** Serialize host removal with pending publications while retaining all catalog rows. */
|
|
88
|
+
removeProvider(provider: string, deactivate: () => void): Promise<void>;
|
|
89
|
+
/** Stop subscriptions and queued lifecycle work; permanent rows are retained. */
|
|
90
|
+
dispose(): Promise<void>;
|
|
91
|
+
}
|