@aopslabs/aops-server 0.2.18 → 0.2.19
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/THIRD_PARTY_NOTICES +38 -222
- package/THIRD_PARTY_NOTICES.inventory.json +55 -129
- package/build/client/_app/immutable/chunks/{Duy8DcbU.js → DwNoiPzd.js} +1 -1
- package/build/client/_app/immutable/entry/{app.DE1eK-IO.js → app.BCeW6qfW.js} +2 -2
- package/build/client/_app/immutable/entry/start.zwNVELjE.js +1 -0
- package/build/client/_app/immutable/nodes/{1.DF4e4l4l.js → 1.DDA3xyX5.js} +1 -1
- package/build/client/_app/version.json +1 -1
- package/build/handler.js +4 -4
- package/build/index.js +4 -4
- package/build/server/chunks/chunks/agent-gateway-runtime.js-r4g4yskN.js +924 -0
- package/build/server/chunks/chunks/{aops-runtime-config.js-CHpDUCdE.js → aops-runtime-config.js-Cjj5F0t8.js} +6 -0
- package/build/server/chunks/chunks/{discovery.js-CZjXJ_ZV.js → discovery.js-DxJiP5Ke.js} +2 -2
- package/build/server/chunks/chunks/domain-dispatch.js-DUR9TD3Q.js +83 -0
- package/build/server/chunks/chunks/{internal.js-Cex9xk6X.js → internal.js-CQc6YaRD.js} +1 -1
- package/build/server/chunks/chunks/{plugin-error-normalize.js-DNeRsopQ.js → plugin-error-normalize.js-D5rNpava.js} +12 -1
- package/build/server/chunks/chunks/project-delete-preview.js-EPMmUQYW.js +64 -0
- package/build/server/chunks/chunks/{registry.js-DdOAgkkf.js → registry.js-W6rrS4yl.js} +1 -1
- package/build/server/chunks/chunks/{request-context.js-Y9Hk40z6.js → request-context.js-C7pKg6Do.js} +24 -2
- package/build/server/chunks/entries/endpoints/api/_domain_/_...path_/{_server.ts.js-DL2_Wh9Q.js → _server.ts.js-CrmOjNdW.js} +8 -9
- package/build/server/chunks/entries/endpoints/api/agent/discovery.json/{_server.ts.js-ILAncxy4.js → _server.ts.js-C_Bpk024.js} +5 -8
- package/build/server/chunks/entries/endpoints/api/agent/openapi.json/{_server.ts.js-Coo1MB-m.js → _server.ts.js-D_7ZwZLj.js} +4 -7
- package/build/server/chunks/entries/endpoints/api/agent/tools/{_server.ts.js-B99CDA4V.js → _server.ts.js-DsVOzB_s.js} +6 -9
- package/build/server/chunks/entries/endpoints/api/agent/tools/_toolId_/{_server.ts.js-Vdxe7_8Z.js → _server.ts.js-BhSRTRGX.js} +5 -8
- package/build/server/chunks/entries/endpoints/api/agent/tools/_toolId_/invoke/{_server.ts.js-D2N4Kt49.js → _server.ts.js-W45uKRo3.js} +6 -9
- package/build/server/chunks/entries/endpoints/api/aops/docman/flows/{_server.ts.js-97nMAeIY.js → _server.ts.js-B7NK3_ce.js} +2 -3
- package/build/server/chunks/entries/endpoints/api/aops/projectman/flows/{_server.ts.js-Cn1_Y84Z.js → _server.ts.js-QCAvkMnA.js} +2 -3
- package/build/server/chunks/entries/endpoints/api/aops/projects/_scopeId_/delete/_server.ts.js-2JTUKA0R.js +168 -0
- package/build/server/chunks/entries/endpoints/api/aops/projects/_scopeId_/delete-preview/_server.ts.js-6TwPYzNx.js +47 -0
- package/build/server/chunks/entries/endpoints/api/aops/settings/database/status/{_server.ts.js-DEHnxQc6.js → _server.ts.js-CysjqstH.js} +1 -1
- package/build/server/chunks/entries/endpoints/api/health/{_server.ts.js-BfazPOwp.js → _server.ts.js-BubTRJg_.js} +1 -1
- package/build/server/chunks/entries/endpoints/api/host-admin/plugins/{_server.ts.js-A9smz4KX.js → _server.ts.js-d549xH5s.js} +5 -8
- package/build/server/chunks/entries/endpoints/api/host-admin/runtime-config/{_server.ts.js-D8HIQFf4.js → _server.ts.js-Dj0PpCx9.js} +1 -1
- package/build/server/chunks/entries/endpoints/api/host-admin/runtime-config/test/{_server.ts.js-BHCFCWYZ.js → _server.ts.js-BmKesVSC.js} +1 -1
- package/build/server/chunks/entries/endpoints/api-info.json/{_server.ts.js-Cd-liJ6G.js → _server.ts.js-BlQ6o52L.js} +5 -8
- package/build/server/chunks/entries/endpoints/health/{_server.ts.js-D_H6vT6P.js → _server.ts.js-Dmp-dGYM.js} +2 -2
- package/build/server/chunks/entries/endpoints/openapi.json/{_server.ts.js-FJKIQip2.js → _server.ts.js-Bpzb9jLa.js} +4 -7
- package/build/server/chunks/{handler-7xD8haQQ.js → handler-BhSuXULO.js} +2 -2
- package/build/server/chunks/{index.js-oJov90e3.js → index.js-DaJ2rIzs.js} +1 -1
- package/build/server/chunks/{manifest.js-JcTVvkpO.js → manifest.js-BeuL7Ebk.js} +32 -18
- package/build/server/chunks/nodes/{1.js-C_yxDx3r.js → 1.js-CE_jApMe.js} +1 -1
- package/cockpit/.vite/manifest.json +2 -2
- package/cockpit/assets/{index-BSNHteK1.css → index-CNsNqqZP.css} +1 -1
- package/cockpit/assets/index-DuUFC3yd.js +17 -0
- package/cockpit/community.module-inventory.json +231 -191
- package/cockpit/index.html +2 -2
- package/migrations/agentspace/0001_vengeful_firestar.sql +4 -0
- package/migrations/agentspace/meta/_journal.json +7 -0
- package/migrations/projectman/0006_optional_sprint_task.sql +1 -0
- package/migrations/projectman/0007_board_column_delete_restrict.sql +15 -0
- package/migrations/projectman/meta/_journal.json +14 -0
- package/npm-shrinkwrap.json +86 -128
- package/package.json +20 -17
- package/runtime/agentspace-tooling.mjs +3 -10
- package/runtime-closure.package.json +33 -34
- package/scripts/community-migration-policy-package-v1.json +41 -216
- package/build/client/_app/immutable/entry/start.s0P-l96K.js +0 -1
- package/build/server/chunks/chunks/agent-gateway-runtime.js-De6jvAjX.js +0 -2642
- package/build/server/chunks/chunks/project-alias.js-BdnRxWft.js +0 -28
- package/cockpit/assets/index-BUaKAOXP.js +0 -18
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
import { dispatchDomainRequest } from '@aopslabs/host-core';
|
|
2
|
+
import { b as buildInternalDomainRequest } from './domain-dispatch.js-DUR9TD3Q.js';
|
|
3
|
+
import { buildAgentGatewayOpenApi, buildFederatedToolCatalog, validateManifest } from '@aopslabs/manifest';
|
|
4
|
+
import { g as getHostConfig, a as getKernelRuntimeSnapshot, b as getHostPluginRegistry, c as checkHostDomainAdmission, r as resetHostPluginRegistry } from './registry.js-W6rrS4yl.js';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
|
|
11
|
+
const MUTATING_ACTIVITY_OPERATION_PATTERN = /(^|[.])(create|update|remove|delete|archive|publish|add|save|upsert|write|link|attach|enqueue|start|stop|interrupt|cancel|approve|dispatch|operate|loop|work|restore|copy|rebase|zip|clean)([.-]|$)/;
|
|
12
|
+
function normalizeToolId$1(value) {
|
|
13
|
+
return String(value ?? "").trim().toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
function shouldAppendActivity(tool, args) {
|
|
16
|
+
const normalizedToolId = normalizeToolId$1(tool.toolId);
|
|
17
|
+
if (!normalizedToolId) return false;
|
|
18
|
+
if (args.preview === true) return false;
|
|
19
|
+
if (normalizedToolId.startsWith("agentspace.activity-item.")) return false;
|
|
20
|
+
if (args.apply !== true && args.confirm !== true) return false;
|
|
21
|
+
return MUTATING_ACTIVITY_OPERATION_PATTERN.test(normalizedToolId) || MUTATING_ACTIVITY_OPERATION_PATTERN.test(normalizeToolId$1(tool.operationId));
|
|
22
|
+
}
|
|
23
|
+
function describeActivityAppendFailure(tool, status, error) {
|
|
24
|
+
return {
|
|
25
|
+
toolId: normalizeToolId$1(tool.toolId) || "unknown_tool",
|
|
26
|
+
status,
|
|
27
|
+
message: error instanceof Error ? error.message : String(error ?? "unknown_error")
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function reportActivityAppendFailure(tool, status, error, warn = (message, detail) => console.warn(message, detail)) {
|
|
31
|
+
warn("[agent-gateway] durable activity append failed", describeActivityAppendFailure(tool, status, error));
|
|
32
|
+
}
|
|
33
|
+
function buildActivityInvokeArgs(toolId, envelope, context) {
|
|
34
|
+
return {
|
|
35
|
+
toolId,
|
|
36
|
+
...envelope.sourceId === void 0 ? {} : { sourceId: envelope.sourceId },
|
|
37
|
+
input: envelope.input,
|
|
38
|
+
...envelope.preview === void 0 ? {} : { preview: envelope.preview },
|
|
39
|
+
...envelope.apply === void 0 ? {} : { apply: envelope.apply },
|
|
40
|
+
...envelope.confirm === void 0 ? {} : { confirm: envelope.confirm },
|
|
41
|
+
...envelope.idempotencyKey === void 0 ? {} : { idempotencyKey: envelope.idempotencyKey },
|
|
42
|
+
...context === void 0 ? {} : { context }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function createLedgerSilenceWatch(options) {
|
|
46
|
+
const threshold = Math.max(1, 1);
|
|
47
|
+
const warn = ((message, detail) => console.warn(message, detail));
|
|
48
|
+
const now = (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
49
|
+
let mutatingInvokes = 0;
|
|
50
|
+
let appendAttempts = 0;
|
|
51
|
+
let appendFailures = 0;
|
|
52
|
+
let lastAppendAt = null;
|
|
53
|
+
let lastAppendStatus = null;
|
|
54
|
+
let warned = false;
|
|
55
|
+
const isSilent = () => appendAttempts === 0 && mutatingInvokes >= threshold;
|
|
56
|
+
const snapshot = () => ({
|
|
57
|
+
mutatingInvokes,
|
|
58
|
+
appendAttempts,
|
|
59
|
+
appendFailures,
|
|
60
|
+
lastAppendAt,
|
|
61
|
+
lastAppendStatus,
|
|
62
|
+
silent: isSilent(),
|
|
63
|
+
warned
|
|
64
|
+
});
|
|
65
|
+
return {
|
|
66
|
+
recordMutatingInvoke() {
|
|
67
|
+
if (!warned && appendAttempts === 0 && mutatingInvokes >= threshold) {
|
|
68
|
+
warned = true;
|
|
69
|
+
warn(
|
|
70
|
+
`[agent-gateway] activity ledger silent: ${String(mutatingInvokes)} mutating invokes, zero append attempts`,
|
|
71
|
+
snapshot()
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
mutatingInvokes += 1;
|
|
75
|
+
},
|
|
76
|
+
recordAppendAttempt(status, at) {
|
|
77
|
+
appendAttempts += 1;
|
|
78
|
+
lastAppendStatus = status;
|
|
79
|
+
lastAppendAt = at ?? now();
|
|
80
|
+
},
|
|
81
|
+
recordAppendFailure() {
|
|
82
|
+
appendFailures += 1;
|
|
83
|
+
},
|
|
84
|
+
snapshot
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function isRecord$2(value) {
|
|
88
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
89
|
+
}
|
|
90
|
+
function normalizeDomain(value) {
|
|
91
|
+
return String(value ?? "").trim().toLowerCase();
|
|
92
|
+
}
|
|
93
|
+
function normalizeToolId(value) {
|
|
94
|
+
return String(value ?? "").trim().toLowerCase();
|
|
95
|
+
}
|
|
96
|
+
function findTagValue(tags, prefix) {
|
|
97
|
+
if (!Array.isArray(tags)) return void 0;
|
|
98
|
+
const found = tags.find((tag) => typeof tag === "string" && tag.startsWith(prefix));
|
|
99
|
+
if (!found) return void 0;
|
|
100
|
+
const value = found.slice(prefix.length).trim().toLowerCase();
|
|
101
|
+
return value.length > 0 ? value : void 0;
|
|
102
|
+
}
|
|
103
|
+
function toFriendlyResourceName(tool) {
|
|
104
|
+
const tagged = findTagValue(tool.tags, "resource:");
|
|
105
|
+
const raw = tagged ?? tool.operationId.split(".")[0] ?? "resource";
|
|
106
|
+
return raw.replace(/[-_]+/g, " ").trim();
|
|
107
|
+
}
|
|
108
|
+
function resolveOperationKind(tool) {
|
|
109
|
+
return findTagValue(tool.tags, "kind:");
|
|
110
|
+
}
|
|
111
|
+
function buildToolSummary(tool) {
|
|
112
|
+
const resource = toFriendlyResourceName(tool);
|
|
113
|
+
const kind = resolveOperationKind(tool);
|
|
114
|
+
if (kind === "list") return `List ${resource} records with optional filter and options.`;
|
|
115
|
+
if (kind === "get") return `Get a ${resource} record by identifier.`;
|
|
116
|
+
if (kind === "create") return `Create a ${resource} record from data payload.`;
|
|
117
|
+
if (kind === "update") return `Update a ${resource} record.`;
|
|
118
|
+
if (kind === "delete") return `Delete a ${resource} record by identifier.`;
|
|
119
|
+
return `Run ${tool.operationId} on ${resource}.`;
|
|
120
|
+
}
|
|
121
|
+
function shouldRewriteSummary(summary) {
|
|
122
|
+
const normalized = summary.trim();
|
|
123
|
+
if (!normalized) return true;
|
|
124
|
+
if (normalized.length > 96) return false;
|
|
125
|
+
if (/[():]/.test(normalized)) return false;
|
|
126
|
+
if (/\b(alias|wrapper|route|compose|markdown|safe delete)\b/i.test(normalized)) return false;
|
|
127
|
+
return /^(list|get|create|update|remove|delete|add|set|link|unlink|record|attach|end|start|search)\b/i.test(
|
|
128
|
+
normalized
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function toCanonicalInvokeExamplePayload(_operationKind, payload) {
|
|
132
|
+
if (!isRecord$2(payload) || !hasInputEnvelope(payload)) return payload;
|
|
133
|
+
const envelopeParts = [payload.context, payload.query, payload.body, payload.params, payload.pathParams];
|
|
134
|
+
if (envelopeParts.some((part) => part !== void 0 && !isRecord$2(part))) return payload;
|
|
135
|
+
const flattened = Object.assign(
|
|
136
|
+
{},
|
|
137
|
+
...envelopeParts.filter((part) => isRecord$2(part))
|
|
138
|
+
);
|
|
139
|
+
return Object.keys(flattened).length > 0 ? flattened : payload;
|
|
140
|
+
}
|
|
141
|
+
function normalizeToolExample(example, operationKind) {
|
|
142
|
+
if (typeof example !== "string") return null;
|
|
143
|
+
const raw = example.trim();
|
|
144
|
+
if (!raw) return null;
|
|
145
|
+
let payload;
|
|
146
|
+
try {
|
|
147
|
+
payload = JSON.parse(raw);
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
if (payload !== void 0) {
|
|
151
|
+
return JSON.stringify(toCanonicalInvokeExamplePayload(operationKind, payload));
|
|
152
|
+
}
|
|
153
|
+
const inputMatch = raw.match(/--input\\s+'([^']+)'/) ?? raw.match(/--input\\s+\"([^\"]+)\"/) ?? raw.match(/--input\\s+([^\\s]+)/);
|
|
154
|
+
if (!inputMatch || typeof inputMatch[1] !== "string") return null;
|
|
155
|
+
const candidate = inputMatch[1].trim();
|
|
156
|
+
if (!candidate) return null;
|
|
157
|
+
try {
|
|
158
|
+
payload = JSON.parse(candidate);
|
|
159
|
+
return JSON.stringify(toCanonicalInvokeExamplePayload(operationKind, payload));
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function normalizeToolExamples(examples, operationKind) {
|
|
165
|
+
if (!Array.isArray(examples)) return void 0;
|
|
166
|
+
const normalized = examples.map((entry) => normalizeToolExample(entry, operationKind)).filter((entry) => Boolean(entry));
|
|
167
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
168
|
+
}
|
|
169
|
+
function normalizeToolIdShape(tool) {
|
|
170
|
+
const normalizedToolId = normalizeToolId(tool.toolId);
|
|
171
|
+
const domain = normalizeDomain(tool.domain);
|
|
172
|
+
if (!domain) return normalizedToolId;
|
|
173
|
+
const duplicatedDomainPrefix = `${domain}.${domain}.`;
|
|
174
|
+
if (normalizedToolId.startsWith(duplicatedDomainPrefix)) {
|
|
175
|
+
return `${domain}.${normalizedToolId.slice(duplicatedDomainPrefix.length)}`;
|
|
176
|
+
}
|
|
177
|
+
return normalizedToolId;
|
|
178
|
+
}
|
|
179
|
+
function normalizeCatalogTool(tool) {
|
|
180
|
+
const operationKind = resolveOperationKind(tool);
|
|
181
|
+
const fallbackExamplesByToolId = {
|
|
182
|
+
"jsonexcel.json.to-excel": ['{"jsonFile":"data.json","outputFile":"data.xlsx"}'],
|
|
183
|
+
"jsonexcel.excel.to-json": ['{"excelFile":"data.xlsx","outputFile":"data.json"}']
|
|
184
|
+
};
|
|
185
|
+
const normalizedToolId = normalizeToolIdShape(tool);
|
|
186
|
+
const examples = normalizeToolExamples(tool.examples, operationKind) ?? normalizeToolExamples(fallbackExamplesByToolId[normalizedToolId], operationKind);
|
|
187
|
+
const summaryCandidate = typeof tool.summary === "string" ? tool.summary : "";
|
|
188
|
+
const summary = shouldRewriteSummary(summaryCandidate) ? buildToolSummary(tool) : summaryCandidate;
|
|
189
|
+
const titleCandidate = typeof tool.title === "string" ? tool.title : "";
|
|
190
|
+
const title = shouldRewriteSummary(titleCandidate) ? summary : titleCandidate;
|
|
191
|
+
return {
|
|
192
|
+
...tool,
|
|
193
|
+
toolId: normalizedToolId,
|
|
194
|
+
...title ? { title } : {},
|
|
195
|
+
...summary ? { summary } : {},
|
|
196
|
+
...examples ? { examples } : {}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function hasInputEnvelope(input) {
|
|
200
|
+
const envelopeKeys = /* @__PURE__ */ new Set(["pathParams", "params", "query", "body", "context"]);
|
|
201
|
+
const keys = Object.keys(input);
|
|
202
|
+
if (keys.length === 0 || keys.some((key) => !envelopeKeys.has(key))) return false;
|
|
203
|
+
if (isRecord$2(input.pathParams) || isRecord$2(input.params) || isRecord$2(input.context)) return true;
|
|
204
|
+
if (isRecord$2(input.query)) return true;
|
|
205
|
+
return Object.prototype.hasOwnProperty.call(input, "body");
|
|
206
|
+
}
|
|
207
|
+
function toFederatedSourceFromRemote(source) {
|
|
208
|
+
return {
|
|
209
|
+
id: source.id,
|
|
210
|
+
kind: source.kind,
|
|
211
|
+
domain: source.domain,
|
|
212
|
+
enabled: source.enabled,
|
|
213
|
+
priority: source.priority,
|
|
214
|
+
baseUrl: source.baseUrl,
|
|
215
|
+
apiBasePath: source.apiBasePath
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function findDomainRouteByOperation(routes, operationId) {
|
|
219
|
+
return routes.find((route) => route.operation === operationId) ?? null;
|
|
220
|
+
}
|
|
221
|
+
const runtimeImport = new Function("specifier", "return import(specifier)");
|
|
222
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
223
|
+
promisify(execFile);
|
|
224
|
+
let manifestImportVersion = 0;
|
|
225
|
+
function withImportVersion(specifier) {
|
|
226
|
+
if (!specifier.startsWith("file://")) return specifier;
|
|
227
|
+
const url = new URL(specifier);
|
|
228
|
+
url.searchParams.set("v", String(manifestImportVersion));
|
|
229
|
+
return url.href;
|
|
230
|
+
}
|
|
231
|
+
function resolveManifestModuleSpecifier(moduleName, configPath) {
|
|
232
|
+
const trimmed = moduleName.trim();
|
|
233
|
+
if (!trimmed) return trimmed;
|
|
234
|
+
if (trimmed.startsWith("file://")) return trimmed;
|
|
235
|
+
if (trimmed.startsWith(".")) {
|
|
236
|
+
const baseDir = configPath ? path.dirname(configPath) : process.cwd();
|
|
237
|
+
return pathToFileURL(path.resolve(baseDir, trimmed)).href;
|
|
238
|
+
}
|
|
239
|
+
if (path.isAbsolute(trimmed)) {
|
|
240
|
+
return pathToFileURL(trimmed).href;
|
|
241
|
+
}
|
|
242
|
+
const resolvePaths = [process.cwd(), path.resolve(process.cwd(), "apps/aops-server")];
|
|
243
|
+
try {
|
|
244
|
+
const resolved = nodeRequire.resolve(trimmed, { paths: resolvePaths });
|
|
245
|
+
return pathToFileURL(resolved).href;
|
|
246
|
+
} catch {
|
|
247
|
+
return trimmed;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function toManifestErrorCode(provider, error) {
|
|
251
|
+
const message = error instanceof Error ? error.message : String(error ?? "unknown_error");
|
|
252
|
+
return {
|
|
253
|
+
sourceId: `manifest:${provider.id}`,
|
|
254
|
+
error: message
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async function loadManifestFromProvider(provider, configPath) {
|
|
258
|
+
const specifier = resolveManifestModuleSpecifier(provider.module, configPath);
|
|
259
|
+
const manifestRaw = await loadManifestFromCurrentProcess(specifier, provider);
|
|
260
|
+
const validation = validateManifest(manifestRaw);
|
|
261
|
+
if (!validation.ok) {
|
|
262
|
+
const firstSemantic = validation.issues[0]?.message;
|
|
263
|
+
const firstSchema = validation.zodIssues?.[0]?.message;
|
|
264
|
+
throw new Error(`manifest_validation_failed:${provider.id}:${firstSemantic ?? firstSchema ?? "invalid_manifest"}`);
|
|
265
|
+
}
|
|
266
|
+
if (normalizeDomain(validation.manifest.domain.id) !== normalizeDomain(provider.domain)) {
|
|
267
|
+
throw new Error(`manifest_domain_mismatch:${provider.id}:${validation.manifest.domain.id}:${provider.domain}`);
|
|
268
|
+
}
|
|
269
|
+
return validation.manifest;
|
|
270
|
+
}
|
|
271
|
+
async function loadManifestFromCurrentProcess(specifier, provider) {
|
|
272
|
+
const mod = await runtimeImport(withImportVersion(specifier));
|
|
273
|
+
const exportCandidate = mod[provider.exportName] ?? (provider.exportName === "default" ? mod.default : void 0);
|
|
274
|
+
if (exportCandidate === void 0) {
|
|
275
|
+
throw new Error(`manifest_export_not_found:${provider.id}:${provider.exportName}`);
|
|
276
|
+
}
|
|
277
|
+
return typeof exportCandidate === "function" ? await Promise.resolve(
|
|
278
|
+
exportCandidate(provider.options)
|
|
279
|
+
) : exportCandidate;
|
|
280
|
+
}
|
|
281
|
+
async function buildGatewayCatalogSnapshot(params) {
|
|
282
|
+
const { config } = params;
|
|
283
|
+
const errors = [];
|
|
284
|
+
const manifests = [];
|
|
285
|
+
const domains = /* @__PURE__ */ new Set();
|
|
286
|
+
if (config.catalog.enabled) {
|
|
287
|
+
for (const provider of config.catalog.manifestProviders) {
|
|
288
|
+
if (!provider.enabled) continue;
|
|
289
|
+
if (!checkHostDomainAdmission(provider.domain).allowed) continue;
|
|
290
|
+
try {
|
|
291
|
+
const manifest = await loadManifestFromProvider(provider, params.configPath);
|
|
292
|
+
manifests.push(manifest);
|
|
293
|
+
domains.add(normalizeDomain(manifest.domain.id));
|
|
294
|
+
} catch (error) {
|
|
295
|
+
errors.push(toManifestErrorCode(provider, error));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
for (const source of config.sources) {
|
|
300
|
+
if (!source.enabled) continue;
|
|
301
|
+
if (!checkHostDomainAdmission(source.domain).allowed) continue;
|
|
302
|
+
domains.add(normalizeDomain(source.domain));
|
|
303
|
+
}
|
|
304
|
+
const sources = config.sources.filter((source) => source.enabled && checkHostDomainAdmission(source.domain).allowed).map((source) => toFederatedSourceFromRemote(source));
|
|
305
|
+
if (config.includeLocal) {
|
|
306
|
+
for (const domain of domains) {
|
|
307
|
+
sources.push({
|
|
308
|
+
id: "local",
|
|
309
|
+
kind: "local-route",
|
|
310
|
+
domain,
|
|
311
|
+
enabled: true,
|
|
312
|
+
priority: 0
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const catalog = buildFederatedToolCatalog({
|
|
317
|
+
manifests,
|
|
318
|
+
sources
|
|
319
|
+
});
|
|
320
|
+
const normalizedCatalog = {
|
|
321
|
+
...catalog,
|
|
322
|
+
tools: catalog.tools.map((tool) => normalizeCatalogTool(tool)).filter(
|
|
323
|
+
(tool) => checkHostDomainAdmission(tool.domain, tool.operationId).allowed
|
|
324
|
+
)
|
|
325
|
+
};
|
|
326
|
+
const toolsById = /* @__PURE__ */ new Map();
|
|
327
|
+
for (const tool of normalizedCatalog.tools) {
|
|
328
|
+
toolsById.set(normalizeToolId(tool.toolId), tool);
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
catalog: normalizedCatalog,
|
|
332
|
+
manifests,
|
|
333
|
+
errors,
|
|
334
|
+
toolsById,
|
|
335
|
+
loadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
const INVOKE_REQUEST_DESCRIPTION = "Send either the raw tool input JSON directly, or use the canonical envelope { sourceId?, input, preview?, apply?, confirm?, idempotencyKey? }. Use preview for preflight, apply for guarded writes, confirm for destructive writes, and idempotencyKey for best-effort guarded-write deduplication.";
|
|
339
|
+
function isRecord$1(value) {
|
|
340
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
341
|
+
}
|
|
342
|
+
function isEnvelopeOnlyInvokeSchema(schema) {
|
|
343
|
+
if (!isRecord$1(schema)) return false;
|
|
344
|
+
if (Array.isArray(schema.oneOf)) return false;
|
|
345
|
+
if (schema.type !== "object") return false;
|
|
346
|
+
const properties = isRecord$1(schema.properties) ? schema.properties : null;
|
|
347
|
+
if (!properties || !Object.prototype.hasOwnProperty.call(properties, "input")) return false;
|
|
348
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
349
|
+
return required.includes("input");
|
|
350
|
+
}
|
|
351
|
+
function normalizeInvokeSchema(schema) {
|
|
352
|
+
if (!isEnvelopeOnlyInvokeSchema(schema)) return schema;
|
|
353
|
+
const properties = isRecord$1(schema.properties) ? schema.properties : {};
|
|
354
|
+
const rawInputSchema = properties.input ?? { type: "object", additionalProperties: true };
|
|
355
|
+
const envelopeSchema = {
|
|
356
|
+
...schema,
|
|
357
|
+
properties: {
|
|
358
|
+
...properties,
|
|
359
|
+
...Object.prototype.hasOwnProperty.call(properties, "preview") ? {} : { preview: { type: "boolean" } },
|
|
360
|
+
...Object.prototype.hasOwnProperty.call(properties, "apply") ? {} : { apply: { type: "boolean" } },
|
|
361
|
+
...Object.prototype.hasOwnProperty.call(properties, "confirm") ? {} : { confirm: { type: "boolean" } },
|
|
362
|
+
...Object.prototype.hasOwnProperty.call(properties, "idempotencyKey") ? {} : { idempotencyKey: { type: "string" } }
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
description: typeof schema.description === "string" && schema.description.trim().length > 0 ? schema.description : INVOKE_REQUEST_DESCRIPTION,
|
|
367
|
+
oneOf: [rawInputSchema, envelopeSchema]
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function normalizeAgentGatewayOpenApiDocument(document) {
|
|
371
|
+
const paths = isRecord$1(document.paths) ? document.paths : null;
|
|
372
|
+
if (!paths) return document;
|
|
373
|
+
let changed = false;
|
|
374
|
+
const normalizedPaths = {};
|
|
375
|
+
for (const [pathKey, pathValue] of Object.entries(paths)) {
|
|
376
|
+
if (!isRecord$1(pathValue) || !isRecord$1(pathValue.post)) {
|
|
377
|
+
normalizedPaths[pathKey] = pathValue;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const post = pathValue.post;
|
|
381
|
+
const requestBody = isRecord$1(post.requestBody) ? post.requestBody : null;
|
|
382
|
+
const content = requestBody && isRecord$1(requestBody.content) ? requestBody.content : null;
|
|
383
|
+
const jsonContent = content && isRecord$1(content["application/json"]) ? content["application/json"] : null;
|
|
384
|
+
const schema = jsonContent?.schema;
|
|
385
|
+
const nextSchema = normalizeInvokeSchema(schema);
|
|
386
|
+
if (nextSchema === schema) {
|
|
387
|
+
normalizedPaths[pathKey] = pathValue;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
changed = true;
|
|
391
|
+
normalizedPaths[pathKey] = {
|
|
392
|
+
...pathValue,
|
|
393
|
+
post: {
|
|
394
|
+
...post,
|
|
395
|
+
requestBody: {
|
|
396
|
+
...requestBody,
|
|
397
|
+
content: {
|
|
398
|
+
...content,
|
|
399
|
+
"application/json": {
|
|
400
|
+
...jsonContent,
|
|
401
|
+
schema: nextSchema
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
if (!changed) return document;
|
|
409
|
+
return {
|
|
410
|
+
...document,
|
|
411
|
+
paths: normalizedPaths
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const schemaCacheByDomain = /* @__PURE__ */ new Map();
|
|
415
|
+
function resetToolSchemaValidationCache(domain) {
|
|
416
|
+
const normalizedDomain = normalizeDomain("");
|
|
417
|
+
if (normalizedDomain) {
|
|
418
|
+
schemaCacheByDomain.delete(normalizedDomain);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
schemaCacheByDomain.clear();
|
|
422
|
+
}
|
|
423
|
+
function resolveGatewayConfig(config) {
|
|
424
|
+
return config ?? {
|
|
425
|
+
enabled: true,
|
|
426
|
+
includeLocal: true,
|
|
427
|
+
sources: [],
|
|
428
|
+
catalog: { enabled: false, manifestProviders: [] }
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function normalizeNonEmpty(value) {
|
|
432
|
+
if (typeof value !== "string") return void 0;
|
|
433
|
+
const normalized = value.trim();
|
|
434
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
435
|
+
}
|
|
436
|
+
function isRecord(value) {
|
|
437
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
438
|
+
}
|
|
439
|
+
function sanitizeActivityPayload(value, depth = 0) {
|
|
440
|
+
if (depth > 3) return "[truncated]";
|
|
441
|
+
if (typeof value === "string") {
|
|
442
|
+
const normalized = value.trim();
|
|
443
|
+
return normalized.length <= 400 ? normalized : `${normalized.slice(0, 397)}...`;
|
|
444
|
+
}
|
|
445
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null || value === void 0) {
|
|
446
|
+
return value;
|
|
447
|
+
}
|
|
448
|
+
if (Array.isArray(value)) {
|
|
449
|
+
return value.slice(0, 12).map((entry) => sanitizeActivityPayload(entry, depth + 1));
|
|
450
|
+
}
|
|
451
|
+
if (!isRecord(value)) return String(value);
|
|
452
|
+
const next = {};
|
|
453
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
454
|
+
const lower = key.toLowerCase();
|
|
455
|
+
if (lower.includes("token") || lower.includes("secret") || lower.includes("password") || lower.includes("authorization")) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
next[key] = sanitizeActivityPayload(entry, depth + 1);
|
|
459
|
+
}
|
|
460
|
+
return next;
|
|
461
|
+
}
|
|
462
|
+
function maybePushActivityRef(target, type, id, label) {
|
|
463
|
+
const normalizedId = normalizeNonEmpty(id);
|
|
464
|
+
if (!normalizedId) return;
|
|
465
|
+
if (target.some((entry) => entry.type === type && entry.id === normalizedId)) return;
|
|
466
|
+
target.push({ type, id: normalizedId });
|
|
467
|
+
}
|
|
468
|
+
function collectActivityRefs(value, refs) {
|
|
469
|
+
if (!isRecord(value)) return;
|
|
470
|
+
maybePushActivityRef(refs, "scope", value.scopeId);
|
|
471
|
+
maybePushActivityRef(refs, "project", value.projectId);
|
|
472
|
+
maybePushActivityRef(refs, "task", value.taskId);
|
|
473
|
+
maybePushActivityRef(refs, "sprint", value.sprintId);
|
|
474
|
+
maybePushActivityRef(refs, "microtask", value.microtaskId);
|
|
475
|
+
maybePushActivityRef(refs, "issue", value.issueId);
|
|
476
|
+
maybePushActivityRef(refs, "feedback", value.feedbackId);
|
|
477
|
+
maybePushActivityRef(refs, "prompt", value.promptId);
|
|
478
|
+
maybePushActivityRef(refs, "prompt-version", value.promptVersionId);
|
|
479
|
+
maybePushActivityRef(refs, "skill", value.skillId);
|
|
480
|
+
maybePushActivityRef(refs, "skill-version", value.skillVersionId);
|
|
481
|
+
maybePushActivityRef(refs, "resource", value.resourceId);
|
|
482
|
+
maybePushActivityRef(refs, "artifact", value.artifactId);
|
|
483
|
+
maybePushActivityRef(refs, "run", value.runId ?? value.runRecordId);
|
|
484
|
+
maybePushActivityRef(refs, "workflow", value.workflowId ?? value.workflowInstanceId);
|
|
485
|
+
maybePushActivityRef(refs, "workflow-definition", value.workflowDefinitionId);
|
|
486
|
+
maybePushActivityRef(refs, "document", value.documentId);
|
|
487
|
+
maybePushActivityRef(refs, "document-version", value.documentVersionId);
|
|
488
|
+
maybePushActivityRef(refs, "page", value.pageId);
|
|
489
|
+
maybePushActivityRef(refs, "page-version", value.pageVersionId);
|
|
490
|
+
maybePushActivityRef(refs, "section", value.sectionId);
|
|
491
|
+
if (isRecord(value.data)) collectActivityRefs(value.data, refs);
|
|
492
|
+
if (isRecord(value.input)) collectActivityRefs(value.input, refs);
|
|
493
|
+
if (isRecord(value.response)) collectActivityRefs(value.response, refs);
|
|
494
|
+
if (isRecord(value.result)) collectActivityRefs(value.result, refs);
|
|
495
|
+
}
|
|
496
|
+
function resolveGatewayActivityAction(tool) {
|
|
497
|
+
const segments = String(tool.operationId ?? "").split(".").filter(Boolean);
|
|
498
|
+
return segments[segments.length - 1] ?? "invoke";
|
|
499
|
+
}
|
|
500
|
+
function summarizeGatewayActivity(tool, status) {
|
|
501
|
+
const action = resolveGatewayActivityAction(tool);
|
|
502
|
+
const entity = String(tool.toolId ?? tool.operationId ?? "operation").replace(/^aops\./, "").replace(/^agentspace\./, "").replace(/^tasker\./, "").replace(/^projectman\./, "").replace(/^docman\./, "").replace(/^fileman\./, "").split(".").slice(0, 2).join(" ").replace(/[-_]+/g, " ").trim() || "operation";
|
|
503
|
+
const verbMap = {
|
|
504
|
+
create: "created",
|
|
505
|
+
add: "added",
|
|
506
|
+
update: "updated",
|
|
507
|
+
set: "updated",
|
|
508
|
+
publish: "published",
|
|
509
|
+
remove: "removed",
|
|
510
|
+
delete: "deleted",
|
|
511
|
+
restore: "restored",
|
|
512
|
+
copy: "copied",
|
|
513
|
+
rebase: "rebased",
|
|
514
|
+
zip: "archived",
|
|
515
|
+
clean: "cleaned",
|
|
516
|
+
link: "linked",
|
|
517
|
+
attach: "attached",
|
|
518
|
+
enqueue: "enqueued",
|
|
519
|
+
start: "started",
|
|
520
|
+
stop: "stopped",
|
|
521
|
+
cancel: "cancelled",
|
|
522
|
+
interrupt: "interrupted",
|
|
523
|
+
approve: "approved",
|
|
524
|
+
save: "saved",
|
|
525
|
+
write: "written",
|
|
526
|
+
record: "recorded",
|
|
527
|
+
store: "stored"
|
|
528
|
+
};
|
|
529
|
+
const actionKey = action.split("-")[0] ?? action;
|
|
530
|
+
const verb = verbMap[actionKey] ?? `${action.replace(/[-_]+/g, " ")} completed`;
|
|
531
|
+
return status === "success" ? `${entity} ${verb}` : `${entity} ${action.replace(/[-_]+/g, " ")} failed`;
|
|
532
|
+
}
|
|
533
|
+
function shouldAppendDurableGatewayActivity(tool, args) {
|
|
534
|
+
return shouldAppendActivity(
|
|
535
|
+
{ toolId: tool.toolId, ...tool.operationId === void 0 ? {} : { operationId: String(tool.operationId) } },
|
|
536
|
+
{ ...args.preview === void 0 ? {} : { preview: args.preview }, ...args.apply === void 0 ? {} : { apply: args.apply }, ...args.confirm === void 0 ? {} : { confirm: args.confirm } }
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
function resolveGatewayActivitySourceKind(context) {
|
|
540
|
+
const candidate = normalizeNonEmpty(context?.activitySourceKind);
|
|
541
|
+
if (candidate === "aops-cli" || candidate === "desktop" || candidate === "runner" || candidate === "system") {
|
|
542
|
+
return candidate;
|
|
543
|
+
}
|
|
544
|
+
return "system";
|
|
545
|
+
}
|
|
546
|
+
function cloneStringList(values) {
|
|
547
|
+
if (!Array.isArray(values)) return void 0;
|
|
548
|
+
const normalized = values.map((value) => String(value ?? "").trim()).filter(Boolean);
|
|
549
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
550
|
+
}
|
|
551
|
+
function mergeStringLists(...lists) {
|
|
552
|
+
const values = /* @__PURE__ */ new Set();
|
|
553
|
+
for (const list of lists) {
|
|
554
|
+
for (const entry of list ?? []) {
|
|
555
|
+
const normalized = String(entry ?? "").trim();
|
|
556
|
+
if (!normalized) continue;
|
|
557
|
+
values.add(normalized);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return values.size > 0 ? [...values] : void 0;
|
|
561
|
+
}
|
|
562
|
+
function toOperationDocs(value) {
|
|
563
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
564
|
+
const input = value;
|
|
565
|
+
const summary = typeof input.summary === "string" && input.summary.trim().length > 0 ? input.summary.trim() : void 0;
|
|
566
|
+
const notes = cloneStringList(input.notes);
|
|
567
|
+
const examples = cloneStringList(input.examples);
|
|
568
|
+
const antiPatterns = cloneStringList(input.antiPatterns);
|
|
569
|
+
const preconditions = cloneStringList(input.preconditions);
|
|
570
|
+
const postconditions = cloneStringList(input.postconditions);
|
|
571
|
+
const docs = {
|
|
572
|
+
...summary ? { summary } : {},
|
|
573
|
+
...notes ? { notes } : {},
|
|
574
|
+
...examples ? { examples } : {},
|
|
575
|
+
...antiPatterns ? { antiPatterns } : {},
|
|
576
|
+
...preconditions ? { preconditions } : {},
|
|
577
|
+
...postconditions ? { postconditions } : {}
|
|
578
|
+
};
|
|
579
|
+
return Object.keys(docs).length > 0 ? docs : null;
|
|
580
|
+
}
|
|
581
|
+
function toDomainDocs(value) {
|
|
582
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
583
|
+
const input = value;
|
|
584
|
+
const summary = typeof input.summary === "string" && input.summary.trim().length > 0 ? input.summary.trim() : void 0;
|
|
585
|
+
const notes = cloneStringList(input.notes);
|
|
586
|
+
const docs = {
|
|
587
|
+
...summary ? { summary } : {},
|
|
588
|
+
...notes ? { notes } : {}
|
|
589
|
+
};
|
|
590
|
+
return Object.keys(docs).length > 0 ? docs : null;
|
|
591
|
+
}
|
|
592
|
+
function buildDomainMetadataByDomain(snapshot, domain) {
|
|
593
|
+
const normalizedDomain = normalizeDomain(domain ?? "");
|
|
594
|
+
const metadataByDomain = {};
|
|
595
|
+
for (const manifest of snapshot.manifests) {
|
|
596
|
+
const manifestDomain = normalizeDomain(manifest.domain.id);
|
|
597
|
+
if (!manifestDomain) continue;
|
|
598
|
+
if (normalizedDomain.length > 0 && manifestDomain !== normalizedDomain) continue;
|
|
599
|
+
const existing = metadataByDomain[manifestDomain];
|
|
600
|
+
const domainDocs = toDomainDocs(manifest.docs?.domain);
|
|
601
|
+
const resources = manifest.capabilities.resources ?? [];
|
|
602
|
+
const resourceDocsMap = manifest.docs?.resources && typeof manifest.docs.resources === "object" && !Array.isArray(manifest.docs.resources) ? manifest.docs.resources : {};
|
|
603
|
+
const mergedResources = new Map(
|
|
604
|
+
(existing?.resources ?? []).map((resource) => [normalizeDomain(resource.resourceId), resource])
|
|
605
|
+
);
|
|
606
|
+
for (const resource of resources) {
|
|
607
|
+
const resourceId = String(resource.resourceId ?? "").trim().toLowerCase();
|
|
608
|
+
if (!resourceId) continue;
|
|
609
|
+
const docEntryRaw = resourceDocsMap[resource.resourceId] ?? resourceDocsMap[resourceId];
|
|
610
|
+
const docEntry = toDomainDocs(docEntryRaw);
|
|
611
|
+
const current = mergedResources.get(resourceId);
|
|
612
|
+
const title = current?.title ?? (String(resource.title ?? "").trim() || resourceId);
|
|
613
|
+
const kind = current?.kind ?? (typeof resource.kind === "string" && resource.kind.trim().length > 0 ? resource.kind.trim() : void 0);
|
|
614
|
+
const schemaRef = current?.schemaRef ?? (typeof resource.schemaRef === "string" && resource.schemaRef.trim().length > 0 ? resource.schemaRef.trim() : void 0);
|
|
615
|
+
mergedResources.set(resourceId, {
|
|
616
|
+
resourceId,
|
|
617
|
+
title,
|
|
618
|
+
kind,
|
|
619
|
+
schemaRef,
|
|
620
|
+
summary: current?.summary ?? docEntry?.summary,
|
|
621
|
+
notes: mergeStringLists(current?.notes, docEntry?.notes)
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
const description = existing?.description ?? (typeof manifest.domain.description === "string" && manifest.domain.description.trim().length > 0 ? manifest.domain.description.trim() : void 0);
|
|
625
|
+
const summary = existing?.summary ?? domainDocs?.summary ?? description;
|
|
626
|
+
const notes = mergeStringLists(existing?.notes, domainDocs?.notes);
|
|
627
|
+
metadataByDomain[manifestDomain] = {
|
|
628
|
+
domain: manifestDomain,
|
|
629
|
+
displayName: existing?.displayName ?? (typeof manifest.domain.displayName === "string" && manifest.domain.displayName.trim().length > 0 ? manifest.domain.displayName.trim() : void 0),
|
|
630
|
+
...description ? { description } : {},
|
|
631
|
+
...summary ? { summary } : {},
|
|
632
|
+
...notes ? { notes } : {},
|
|
633
|
+
...mergedResources.size > 0 ? {
|
|
634
|
+
resources: [...mergedResources.values()].sort((left, right) => left.title.localeCompare(right.title))
|
|
635
|
+
} : {}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
return Object.keys(metadataByDomain).length > 0 ? metadataByDomain : void 0;
|
|
639
|
+
}
|
|
640
|
+
function buildOperationDocsByOperationId(snapshot, domain) {
|
|
641
|
+
const normalizedDomain = normalizeDomain(domain ?? "");
|
|
642
|
+
const docsByOperationId = {};
|
|
643
|
+
for (const manifest of snapshot.manifests) {
|
|
644
|
+
if (normalizedDomain.length > 0 && normalizeDomain(manifest.domain.id) !== normalizedDomain) continue;
|
|
645
|
+
const operations = manifest.docs?.operations;
|
|
646
|
+
if (!operations || typeof operations !== "object" || Array.isArray(operations)) continue;
|
|
647
|
+
for (const [operationIdRaw, value] of Object.entries(operations)) {
|
|
648
|
+
const operationId = String(operationIdRaw ?? "").trim().toLowerCase();
|
|
649
|
+
if (!operationId) continue;
|
|
650
|
+
const docs = toOperationDocs(value);
|
|
651
|
+
if (!docs) continue;
|
|
652
|
+
docsByOperationId[operationId] = docs;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return Object.keys(docsByOperationId).length > 0 ? docsByOperationId : void 0;
|
|
656
|
+
}
|
|
657
|
+
function buildListResult(snapshot, domain) {
|
|
658
|
+
const normalizedDomain = normalizeDomain(domain ?? "");
|
|
659
|
+
const tools = normalizedDomain.length > 0 ? snapshot.catalog.tools.filter((tool) => normalizeDomain(tool.domain) === normalizedDomain) : snapshot.catalog.tools;
|
|
660
|
+
const operationDocsByOperationId = buildOperationDocsByOperationId(snapshot, domain);
|
|
661
|
+
const domainMetadataByDomain = buildDomainMetadataByDomain(snapshot, domain);
|
|
662
|
+
return {
|
|
663
|
+
catalogVersion: snapshot.catalog.catalogVersion,
|
|
664
|
+
generatedAt: snapshot.catalog.generatedAt,
|
|
665
|
+
tools,
|
|
666
|
+
errors: snapshot.errors,
|
|
667
|
+
...operationDocsByOperationId ? { operationDocsByOperationId } : {},
|
|
668
|
+
...domainMetadataByDomain ? { domainMetadataByDomain } : {}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
function findToolInputJsonSchemaInManifests(snapshot, tool) {
|
|
672
|
+
const inputSchemaRef = typeof tool.inputSchemaRef === "string" ? tool.inputSchemaRef.trim() : "";
|
|
673
|
+
if (!inputSchemaRef) return null;
|
|
674
|
+
const toolDomain = normalizeDomain(tool.domain);
|
|
675
|
+
for (const manifest of snapshot.manifests) {
|
|
676
|
+
if (normalizeDomain(manifest.domain.id) !== toolDomain) continue;
|
|
677
|
+
const schema = manifest.contracts?.schemas?.[inputSchemaRef];
|
|
678
|
+
if (schema && typeof schema === "object" && !Array.isArray(schema)) {
|
|
679
|
+
return schema;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
async function appendDurableGatewayActivityBestEffort(params) {
|
|
685
|
+
const scopeId = normalizeNonEmpty(params.args.context?.scopeId);
|
|
686
|
+
const projectId = normalizeNonEmpty(params.args.context?.projectId);
|
|
687
|
+
const resolvedScopeId = scopeId || projectId;
|
|
688
|
+
if (!resolvedScopeId) return;
|
|
689
|
+
const refs = [];
|
|
690
|
+
maybePushActivityRef(refs, "scope", resolvedScopeId);
|
|
691
|
+
maybePushActivityRef(refs, "project", projectId);
|
|
692
|
+
collectActivityRefs(params.args.input, refs);
|
|
693
|
+
collectActivityRefs(params.responseData, refs);
|
|
694
|
+
const requestHeaders = new Headers();
|
|
695
|
+
const tenantId = normalizeNonEmpty(params.args.context?.tenantId);
|
|
696
|
+
if (tenantId) requestHeaders.set("x-tenant-id", tenantId);
|
|
697
|
+
const payload = params.status === "error" ? {
|
|
698
|
+
input: params.args.input,
|
|
699
|
+
error: params.errorMessage ? { message: params.errorMessage } : void 0
|
|
700
|
+
} : {
|
|
701
|
+
input: params.args.input,
|
|
702
|
+
result: params.responseData
|
|
703
|
+
};
|
|
704
|
+
try {
|
|
705
|
+
await params.registry.ensureSetup("agentspace");
|
|
706
|
+
await dispatchDomainRequest({
|
|
707
|
+
registry: params.registry,
|
|
708
|
+
request: buildInternalDomainRequest({
|
|
709
|
+
method: "POST",
|
|
710
|
+
domain: "agentspace",
|
|
711
|
+
path: ["operations", "activity-item", "add-activity-item"],
|
|
712
|
+
scopeId: resolvedScopeId,
|
|
713
|
+
// The tenant header travelled with this call before it went through
|
|
714
|
+
// the shared builder, and dropping it would have been a silent change
|
|
715
|
+
// of who the append is attributed to.
|
|
716
|
+
headers: Object.fromEntries(requestHeaders.entries()),
|
|
717
|
+
body: {
|
|
718
|
+
data: {
|
|
719
|
+
scopeId: resolvedScopeId,
|
|
720
|
+
projectId,
|
|
721
|
+
sourceKind: resolveGatewayActivitySourceKind(params.args.context),
|
|
722
|
+
sourceId: params.tool.toolId,
|
|
723
|
+
action: resolveGatewayActivityAction(params.tool),
|
|
724
|
+
status: params.status,
|
|
725
|
+
summary: summarizeGatewayActivity(params.tool, params.status),
|
|
726
|
+
refs,
|
|
727
|
+
payload: sanitizeActivityPayload(payload)
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
context: {
|
|
731
|
+
...params.args.context ?? {},
|
|
732
|
+
...projectId ? { projectId } : {}
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
});
|
|
736
|
+
} catch (error) {
|
|
737
|
+
reportActivityAppendFailure({ toolId: params.tool.toolId }, params.status, error);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function createAgentGateway(options) {
|
|
741
|
+
const config = resolveGatewayConfig(options.config);
|
|
742
|
+
let catalogSnapshotPromise = null;
|
|
743
|
+
let latestCatalogSnapshot = null;
|
|
744
|
+
function invalidateCatalogSnapshot() {
|
|
745
|
+
catalogSnapshotPromise = null;
|
|
746
|
+
latestCatalogSnapshot = null;
|
|
747
|
+
resetToolSchemaValidationCache();
|
|
748
|
+
}
|
|
749
|
+
async function getCatalogSnapshot(params = {}) {
|
|
750
|
+
if (params.forceRefresh) invalidateCatalogSnapshot();
|
|
751
|
+
if (catalogSnapshotPromise) return catalogSnapshotPromise;
|
|
752
|
+
const snapshotPromise = buildGatewayCatalogSnapshot({
|
|
753
|
+
config,
|
|
754
|
+
configPath: options.configPath
|
|
755
|
+
}).then((snapshot) => {
|
|
756
|
+
latestCatalogSnapshot = snapshot;
|
|
757
|
+
return snapshot;
|
|
758
|
+
}).catch((error) => {
|
|
759
|
+
if (catalogSnapshotPromise === snapshotPromise) {
|
|
760
|
+
catalogSnapshotPromise = null;
|
|
761
|
+
latestCatalogSnapshot = null;
|
|
762
|
+
}
|
|
763
|
+
throw error;
|
|
764
|
+
});
|
|
765
|
+
catalogSnapshotPromise = snapshotPromise;
|
|
766
|
+
return catalogSnapshotPromise;
|
|
767
|
+
}
|
|
768
|
+
async function refreshCatalogSnapshot() {
|
|
769
|
+
return getCatalogSnapshot({ forceRefresh: true });
|
|
770
|
+
}
|
|
771
|
+
function getDiagnostics() {
|
|
772
|
+
return {
|
|
773
|
+
enabled: config.enabled !== false,
|
|
774
|
+
snapshotLoaded: latestCatalogSnapshot !== null,
|
|
775
|
+
loadedAt: latestCatalogSnapshot?.loadedAt,
|
|
776
|
+
catalogVersion: latestCatalogSnapshot?.catalog.catalogVersion,
|
|
777
|
+
generatedAt: latestCatalogSnapshot?.catalog.generatedAt,
|
|
778
|
+
toolCount: latestCatalogSnapshot?.catalog.tools.length ?? 0,
|
|
779
|
+
errorCount: latestCatalogSnapshot?.errors.length ?? 0
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
async function listTools(domain) {
|
|
783
|
+
if (config.enabled === false) {
|
|
784
|
+
return { catalogVersion: "1.0.0", generatedAt: (/* @__PURE__ */ new Date()).toISOString(), tools: [], errors: [] };
|
|
785
|
+
}
|
|
786
|
+
const snapshot = await getCatalogSnapshot();
|
|
787
|
+
return buildListResult(snapshot, domain);
|
|
788
|
+
}
|
|
789
|
+
async function getToolInputJsonSchema(toolId) {
|
|
790
|
+
if (config.enabled === false) return null;
|
|
791
|
+
const normalized = normalizeToolId(toolId);
|
|
792
|
+
const snapshot = await getCatalogSnapshot();
|
|
793
|
+
const tool = snapshot.catalog.tools.find((entry) => normalizeToolId(entry.toolId) === normalized);
|
|
794
|
+
if (!tool) return null;
|
|
795
|
+
const manifestSchema = findToolInputJsonSchemaInManifests(snapshot, tool);
|
|
796
|
+
if (manifestSchema) return manifestSchema;
|
|
797
|
+
const registry = await options.registryResolver();
|
|
798
|
+
const plugin = registry.get(normalizeDomain(tool.domain));
|
|
799
|
+
if (!plugin) return null;
|
|
800
|
+
const route = findDomainRouteByOperation(plugin.manifest.routes, tool.operationId);
|
|
801
|
+
if (!route) return null;
|
|
802
|
+
const schema = route.inputJsonSchema;
|
|
803
|
+
return schema && typeof schema === "object" && !Array.isArray(schema) ? schema : null;
|
|
804
|
+
}
|
|
805
|
+
async function getOpenApi(args = {}) {
|
|
806
|
+
const snapshot = await getCatalogSnapshot();
|
|
807
|
+
const normalizedDomain = normalizeDomain(args.domain ?? "");
|
|
808
|
+
const manifests = normalizedDomain.length > 0 ? snapshot.manifests.filter((manifest) => normalizeDomain(manifest.domain?.id ?? "") === normalizedDomain) : snapshot.manifests;
|
|
809
|
+
const tools = normalizedDomain.length > 0 ? snapshot.catalog.tools.filter((tool) => normalizeDomain(tool.domain) === normalizedDomain) : snapshot.catalog.tools;
|
|
810
|
+
const catalog = {
|
|
811
|
+
...snapshot.catalog,
|
|
812
|
+
tools
|
|
813
|
+
};
|
|
814
|
+
const document = buildAgentGatewayOpenApi({
|
|
815
|
+
manifests,
|
|
816
|
+
catalog,
|
|
817
|
+
serverBaseUrl: args.serverBaseUrl
|
|
818
|
+
});
|
|
819
|
+
return normalizeAgentGatewayOpenApiDocument(document);
|
|
820
|
+
}
|
|
821
|
+
return {
|
|
822
|
+
listTools,
|
|
823
|
+
getToolInputJsonSchema,
|
|
824
|
+
getOpenApi,
|
|
825
|
+
getDiagnostics,
|
|
826
|
+
refreshCatalogSnapshot,
|
|
827
|
+
invalidateCatalogSnapshot
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
let singleton = null;
|
|
831
|
+
const ledgerWatch = createLedgerSilenceWatch();
|
|
832
|
+
function getAgentGateway() {
|
|
833
|
+
if (singleton) return singleton;
|
|
834
|
+
const config = getHostConfig();
|
|
835
|
+
singleton = createAgentGateway({
|
|
836
|
+
registryResolver: getHostPluginRegistry,
|
|
837
|
+
registryResetter: resetHostPluginRegistry,
|
|
838
|
+
config: config.agentGateway
|
|
839
|
+
});
|
|
840
|
+
return singleton;
|
|
841
|
+
}
|
|
842
|
+
function getAgentGatewayDiagnostics() {
|
|
843
|
+
const base = singleton?.getDiagnostics() ?? {
|
|
844
|
+
enabled: getHostConfig().agentGateway?.enabled !== false,
|
|
845
|
+
snapshotLoaded: false,
|
|
846
|
+
toolCount: 0,
|
|
847
|
+
errorCount: 0
|
|
848
|
+
};
|
|
849
|
+
return { ...base, activityLedger: ledgerWatch.snapshot() };
|
|
850
|
+
}
|
|
851
|
+
function normalize(value) {
|
|
852
|
+
return String(value ?? "").trim().toLowerCase();
|
|
853
|
+
}
|
|
854
|
+
async function appendActivityForKernelInvoke(params) {
|
|
855
|
+
if (!params.owed) return;
|
|
856
|
+
ledgerWatch.recordAppendAttempt(params.status);
|
|
857
|
+
await appendDurableGatewayActivityBestEffort({
|
|
858
|
+
registry: await getHostPluginRegistry(),
|
|
859
|
+
tool: params.tool,
|
|
860
|
+
args: params.invokeArgs,
|
|
861
|
+
status: params.status,
|
|
862
|
+
...params.responseData === void 0 ? {} : { responseData: params.responseData },
|
|
863
|
+
...params.errorMessage === void 0 ? {} : { errorMessage: params.errorMessage }
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
async function invokeKernelAgentTool(args) {
|
|
867
|
+
const catalogGateway = getAgentGateway();
|
|
868
|
+
const catalog = await catalogGateway.listTools();
|
|
869
|
+
const catalogTool = catalog.tools.find((tool) => normalize(tool.toolId) === normalize(args.toolId));
|
|
870
|
+
if (!catalogTool) throw new Error(`tool_not_found:${args.toolId}`);
|
|
871
|
+
const snapshot = await getKernelRuntimeSnapshot();
|
|
872
|
+
const plugin = snapshot.registry.get(normalize(catalogTool.domain));
|
|
873
|
+
if (!plugin) throw new Error(`domain_not_registered:${catalogTool.domain}`);
|
|
874
|
+
const route = plugin.manifest.routes.find(
|
|
875
|
+
(candidate) => normalize(candidate.operation) === normalize(catalogTool.operationId)
|
|
876
|
+
);
|
|
877
|
+
if (!route) {
|
|
878
|
+
throw new Error(`operation_route_not_found:${catalogTool.domain}:${catalogTool.operationId}`);
|
|
879
|
+
}
|
|
880
|
+
const routeId = normalize(route.id);
|
|
881
|
+
const kernelToolId = routeId.startsWith(`${normalize(catalogTool.domain)}.`) ? routeId : `${normalize(catalogTool.domain)}.${routeId}`;
|
|
882
|
+
const invokeArgs = buildActivityInvokeArgs(args.toolId, args.envelope, args.context);
|
|
883
|
+
const owesActivityRecord = shouldAppendDurableGatewayActivity(catalogTool, invokeArgs);
|
|
884
|
+
if (owesActivityRecord) ledgerWatch.recordMutatingInvoke();
|
|
885
|
+
let result;
|
|
886
|
+
try {
|
|
887
|
+
result = await snapshot.gateway.invokeTool({
|
|
888
|
+
toolId: kernelToolId,
|
|
889
|
+
envelope: args.envelope,
|
|
890
|
+
context: args.context,
|
|
891
|
+
...args.requestHeaders ? { requestHeaders: args.requestHeaders } : {}
|
|
892
|
+
});
|
|
893
|
+
} catch (error) {
|
|
894
|
+
await appendActivityForKernelInvoke({
|
|
895
|
+
tool: catalogTool,
|
|
896
|
+
invokeArgs,
|
|
897
|
+
owed: owesActivityRecord,
|
|
898
|
+
status: "error",
|
|
899
|
+
errorMessage: error instanceof Error ? error.message : String(error ?? "unknown_error")
|
|
900
|
+
});
|
|
901
|
+
throw error;
|
|
902
|
+
}
|
|
903
|
+
const succeeded = result.status < 400 && result.data?.ok !== false;
|
|
904
|
+
await appendActivityForKernelInvoke({
|
|
905
|
+
tool: catalogTool,
|
|
906
|
+
invokeArgs,
|
|
907
|
+
owed: owesActivityRecord,
|
|
908
|
+
status: succeeded ? "success" : "error",
|
|
909
|
+
responseData: result.data,
|
|
910
|
+
...succeeded ? {} : { errorMessage: `invoke_status_${String(result.status)}` }
|
|
911
|
+
});
|
|
912
|
+
return {
|
|
913
|
+
tool: {
|
|
914
|
+
...catalogTool,
|
|
915
|
+
sourceId: result.tool.sourceId,
|
|
916
|
+
sourceKind: "local-route"
|
|
917
|
+
},
|
|
918
|
+
status: result.status,
|
|
919
|
+
data: result.data,
|
|
920
|
+
...result.headers ? { headers: result.headers } : {}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
export { normalizeToolId as a, getAgentGatewayDiagnostics as b, getAgentGateway as g, invokeKernelAgentTool as i, normalizeDomain as n, resolveOperationKind as r };
|