@warpgogol/werkstatt-shared 0.7.0 → 0.8.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warpgogol/werkstatt-shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -1353,22 +1353,25 @@
|
|
|
1353
1353
|
"ajv": "^8.20.0",
|
|
1354
1354
|
"bs58": "^6.0.0",
|
|
1355
1355
|
"expr-eval": "^2.0.2",
|
|
1356
|
+
"gray-matter": "^4.0.3",
|
|
1356
1357
|
"hls.js": "^1.6.16",
|
|
1357
1358
|
"lenis": "^1.3.25",
|
|
1358
1359
|
"plyr": "^3.8.4",
|
|
1359
|
-
"gray-matter": "^4.0.3",
|
|
1360
1360
|
"yaml": "^2.9.0",
|
|
1361
1361
|
"zod": "^4.4.3"
|
|
1362
1362
|
},
|
|
1363
1363
|
"optionalDependencies": {
|
|
1364
|
-
"@syrokomskyi/axiom-study": "
|
|
1364
|
+
"@syrokomskyi/axiom-study": "link:../../../pipelines/packages/axiom/axiom-study"
|
|
1365
1365
|
},
|
|
1366
1366
|
"devDependencies": {
|
|
1367
|
+
"@astrojs/markdown-remark": "^7.0.0",
|
|
1367
1368
|
"@cloudflare/workers-types": "^5.20260801.1",
|
|
1368
1369
|
"@types/node": "^24.0.0",
|
|
1370
|
+
"@warpgogol/werkstatt": "1.4.0",
|
|
1369
1371
|
"astro": "^7.2.0",
|
|
1370
1372
|
"eslint": "^10.8.0",
|
|
1371
1373
|
"fast-check": "^4.9.0",
|
|
1374
|
+
"gsap": "^3.15.0",
|
|
1372
1375
|
"typescript": "^6.0.3",
|
|
1373
1376
|
"typescript-eslint": "8.65.0",
|
|
1374
1377
|
"vitest": "^4.1.10"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
ARD (Agentic Resource Discovery) ai-catalog.json projection. Pure, dependency-free
|
|
5
|
+
formatter that projects a site's Agent Surface Manifest into the ai-catalog data model
|
|
6
|
+
for /.well-known/ai-catalog.json. No I/O — the kernel command loads the manifest and
|
|
7
|
+
passes it here.
|
|
8
|
+
</purpose>
|
|
9
|
+
<non-goals>
|
|
10
|
+
<item>Do not read files — callers load and pass the manifest.</item>
|
|
11
|
+
<item>Do not duplicate logic from api-catalog or mcp-card — this is a separate spec.</item>
|
|
12
|
+
</non-goals>
|
|
13
|
+
</MODULE_CONTRACT>
|
|
14
|
+
<CHANGE_SUMMARY>
|
|
15
|
+
<item>Initial ARD ai-catalog.json projection for isitagentready.com ARD check.</item>
|
|
16
|
+
</CHANGE_SUMMARY>
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { AgentSurfaceManifest } from "./manifest.ts";
|
|
20
|
+
|
|
21
|
+
/** ARD ai-catalog entry — one per discoverable resource. */
|
|
22
|
+
export interface ArdCatalogEntry {
|
|
23
|
+
identifier: string;
|
|
24
|
+
displayName: string;
|
|
25
|
+
type: string;
|
|
26
|
+
url: string;
|
|
27
|
+
representativeQueries: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** ARD ai-catalog host object. */
|
|
31
|
+
export interface ArdCatalogHost {
|
|
32
|
+
displayName: string;
|
|
33
|
+
identifier: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** ARD ai-catalog top-level document. */
|
|
37
|
+
export interface ArdCatalog {
|
|
38
|
+
specVersion: string;
|
|
39
|
+
host: ArdCatalogHost;
|
|
40
|
+
entries: ArdCatalogEntry[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Pure: project Agent Surface Manifest into ARD ai-catalog.json.
|
|
45
|
+
* Deterministic — entries are sorted by identifier for byte-identical output (DNA-58).
|
|
46
|
+
*/
|
|
47
|
+
export function buildArdCatalog(
|
|
48
|
+
manifest: AgentSurfaceManifest,
|
|
49
|
+
hostDisplayName?: string,
|
|
50
|
+
): ArdCatalog {
|
|
51
|
+
const domain = manifest.baseUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
52
|
+
const displayName = hostDisplayName ?? manifest.site;
|
|
53
|
+
|
|
54
|
+
const entries: ArdCatalogEntry[] = [];
|
|
55
|
+
|
|
56
|
+
// MCP server card entry
|
|
57
|
+
if (manifest.interfaces.mcp) {
|
|
58
|
+
entries.push({
|
|
59
|
+
identifier: `urn:air:${domain}:server:mcp`,
|
|
60
|
+
displayName: "MCP Server",
|
|
61
|
+
type: "application/mcp-server-card+json",
|
|
62
|
+
url: "/.well-known/mcp/server-card.json",
|
|
63
|
+
representativeQueries: [
|
|
64
|
+
"What tools are available on this MCP server",
|
|
65
|
+
"List MCP server capabilities and endpoints",
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A2A Agent Card entry
|
|
71
|
+
entries.push({
|
|
72
|
+
identifier: `urn:air:${domain}:agent:a2a`,
|
|
73
|
+
displayName: "A2A Agent Card",
|
|
74
|
+
type: "application/a2a+json",
|
|
75
|
+
url: "/.well-known/agent-card.json",
|
|
76
|
+
representativeQueries: [
|
|
77
|
+
"What agent capabilities does this site offer",
|
|
78
|
+
"Get the A2A agent card for this service",
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// OpenAPI entry
|
|
83
|
+
if (manifest.interfaces.openapi) {
|
|
84
|
+
entries.push({
|
|
85
|
+
identifier: `urn:air:${domain}:api:openapi`,
|
|
86
|
+
displayName: "OpenAPI Specification",
|
|
87
|
+
type: "application/vnd.oai.openapi+json",
|
|
88
|
+
url: manifest.interfaces.openapi,
|
|
89
|
+
representativeQueries: [
|
|
90
|
+
"What API endpoints are available on this site",
|
|
91
|
+
"Get the OpenAPI specification for agent integration",
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Agent Surface Manifest entry
|
|
97
|
+
entries.push({
|
|
98
|
+
identifier: `urn:air:${domain}:manifest:agent-surface`,
|
|
99
|
+
displayName: "Agent Surface Manifest",
|
|
100
|
+
type: "application/json",
|
|
101
|
+
url: "/.well-known/agent.json",
|
|
102
|
+
representativeQueries: [
|
|
103
|
+
"What agent discovery endpoints does this site provide",
|
|
104
|
+
"Get the agent surface manifest for this site",
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Knowledge domain entries
|
|
109
|
+
for (const ref of manifest.knowledge) {
|
|
110
|
+
entries.push({
|
|
111
|
+
identifier: `urn:air:${domain}:knowledge:${ref.domain}`,
|
|
112
|
+
displayName: `${ref.domain.charAt(0).toUpperCase() + ref.domain.slice(1)} Knowledge`,
|
|
113
|
+
type: "application/json",
|
|
114
|
+
url: ref.url,
|
|
115
|
+
representativeQueries: [
|
|
116
|
+
`What ${ref.domain} information is available on this site`,
|
|
117
|
+
`Get structured ${ref.domain} data for agent consumption`,
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Sort entries by identifier for deterministic output
|
|
123
|
+
entries.sort((a, b) => a.identifier.localeCompare(b.identifier));
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
specVersion: "1.0",
|
|
127
|
+
host: {
|
|
128
|
+
displayName,
|
|
129
|
+
identifier: `did:web:${domain}`,
|
|
130
|
+
},
|
|
131
|
+
entries,
|
|
132
|
+
};
|
|
133
|
+
}
|
package/src/share/agent/index.ts
CHANGED
|
@@ -205,6 +205,7 @@ export function resolveReferencesInString(
|
|
|
205
205
|
lang: string,
|
|
206
206
|
defaultLang: string,
|
|
207
207
|
sourceRef?: SourceRef,
|
|
208
|
+
onUnresolved?: (ref: string, error: string) => void,
|
|
208
209
|
): string {
|
|
209
210
|
// RFC-0731: Check for this. as a pure reference (entire text is this.field.path)
|
|
210
211
|
if (
|
|
@@ -219,6 +220,7 @@ export function resolveReferencesInString(
|
|
|
219
220
|
if (result.resolved) {
|
|
220
221
|
return formatValue(result.value);
|
|
221
222
|
}
|
|
223
|
+
onUnresolved?.(text.trim(), result.error ?? "unresolved this. reference");
|
|
222
224
|
}
|
|
223
225
|
}
|
|
224
226
|
THIS_SCAN_PATTERN.lastIndex = 0;
|
|
@@ -228,6 +230,7 @@ export function resolveReferencesInString(
|
|
|
228
230
|
if (result.resolved) {
|
|
229
231
|
return formatValue(result.value);
|
|
230
232
|
}
|
|
233
|
+
onUnresolved?.(text, result.error ?? "unresolved reference");
|
|
231
234
|
return text;
|
|
232
235
|
}
|
|
233
236
|
|
|
@@ -310,9 +313,12 @@ export async function resolveReferencesDeep(
|
|
|
310
313
|
lang: string,
|
|
311
314
|
defaultLang: string,
|
|
312
315
|
sourceRef?: SourceRef,
|
|
316
|
+
onUnresolved?: (ref: string, error: string) => void,
|
|
313
317
|
): Promise<unknown> {
|
|
314
318
|
return substituteRefsDeep(data, (value) =>
|
|
315
|
-
Promise.resolve(
|
|
319
|
+
Promise.resolve(
|
|
320
|
+
resolveReferencesInString(index, value, lang, defaultLang, sourceRef, onUnresolved),
|
|
321
|
+
),
|
|
316
322
|
);
|
|
317
323
|
}
|
|
318
324
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>ARD ai-catalog.json projection tests: determinism, shape, entry coverage.</purpose>
|
|
4
|
+
<keywords>ARD, ai-catalog, agent surface, test</keywords>
|
|
5
|
+
</MODULE_CONTRACT>
|
|
6
|
+
<CHANGE_SUMMARY>
|
|
7
|
+
<item>Initial ARD ai-catalog.json projection tests.</item>
|
|
8
|
+
</CHANGE_SUMMARY>
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { test, expect } from "vitest";
|
|
12
|
+
import { buildArdCatalog } from "../agent/ard-catalog.ts";
|
|
13
|
+
import { buildAgentSurfaceManifest } from "../agent/manifest.ts";
|
|
14
|
+
|
|
15
|
+
test("buildArdCatalog: produces specVersion, host, and entries array", () => {
|
|
16
|
+
const manifest = buildAgentSurfaceManifest({
|
|
17
|
+
site: "s",
|
|
18
|
+
baseUrl: "https://s.example",
|
|
19
|
+
languages: { default: "de", supported: ["de"] },
|
|
20
|
+
});
|
|
21
|
+
const catalog = buildArdCatalog(manifest, "My Site");
|
|
22
|
+
expect(catalog.specVersion).toBe("1.0");
|
|
23
|
+
expect(catalog.host.displayName).toBe("My Site");
|
|
24
|
+
expect(catalog.host.identifier).toBe("did:web:s.example");
|
|
25
|
+
expect(catalog.entries.length).toBeGreaterThan(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("buildArdCatalog: each entry has identifier, displayName, type, url, representativeQueries", () => {
|
|
29
|
+
const manifest = buildAgentSurfaceManifest({
|
|
30
|
+
site: "s",
|
|
31
|
+
baseUrl: "https://s.example",
|
|
32
|
+
languages: { default: "de", supported: ["de"] },
|
|
33
|
+
mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
|
|
34
|
+
knowledge: [
|
|
35
|
+
{ domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
const catalog = buildArdCatalog(manifest);
|
|
39
|
+
for (const entry of catalog.entries) {
|
|
40
|
+
expect(entry.identifier).toMatch(/^urn:air:s\.example:/);
|
|
41
|
+
expect(entry.displayName).toBeTruthy();
|
|
42
|
+
expect(entry.type).toBeTruthy();
|
|
43
|
+
expect(entry.url).toBeTruthy();
|
|
44
|
+
expect(entry.representativeQueries.length).toBeGreaterThanOrEqual(2);
|
|
45
|
+
expect(entry.representativeQueries.length).toBeLessThanOrEqual(5);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("buildArdCatalog: MCP interface adds MCP server entry", () => {
|
|
50
|
+
const manifest = buildAgentSurfaceManifest({
|
|
51
|
+
site: "s",
|
|
52
|
+
baseUrl: "https://s.example",
|
|
53
|
+
languages: { default: "de", supported: ["de"] },
|
|
54
|
+
mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
|
|
55
|
+
});
|
|
56
|
+
const catalog = buildArdCatalog(manifest);
|
|
57
|
+
const mcpEntry = catalog.entries.find((e) => e.identifier.includes(":server:mcp"));
|
|
58
|
+
expect(mcpEntry).toBeDefined();
|
|
59
|
+
expect(mcpEntry?.type).toBe("application/mcp-server-card+json");
|
|
60
|
+
expect(mcpEntry?.url).toBe("/.well-known/mcp/server-card.json");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("buildArdCatalog: knowledge refs produce entries", () => {
|
|
64
|
+
const manifest = buildAgentSurfaceManifest({
|
|
65
|
+
site: "s",
|
|
66
|
+
baseUrl: "https://s.example",
|
|
67
|
+
languages: { default: "de", supported: ["de"] },
|
|
68
|
+
knowledge: [
|
|
69
|
+
{ domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
|
|
70
|
+
{ domain: "team", url: "/api/agent/v1/team.json", schema: "gogol.agent.knowledge/team@1" },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
const catalog = buildArdCatalog(manifest);
|
|
74
|
+
const knowledgeEntries = catalog.entries.filter((e) => e.identifier.includes(":knowledge:"));
|
|
75
|
+
expect(knowledgeEntries).toHaveLength(2);
|
|
76
|
+
expect(knowledgeEntries[0].url).toBe("/api/agent/v1/offer.json");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("buildArdCatalog: entries are sorted by identifier", () => {
|
|
80
|
+
const manifest = buildAgentSurfaceManifest({
|
|
81
|
+
site: "s",
|
|
82
|
+
baseUrl: "https://s.example",
|
|
83
|
+
languages: { default: "de", supported: ["de"] },
|
|
84
|
+
knowledge: [
|
|
85
|
+
{ domain: "zeta", url: "/api/agent/v1/zeta.json", schema: "gogol.agent.knowledge/zeta@1" },
|
|
86
|
+
{ domain: "alpha", url: "/api/agent/v1/alpha.json", schema: "gogol.agent.knowledge/alpha@1" },
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
const catalog = buildArdCatalog(manifest);
|
|
90
|
+
const identifiers = catalog.entries.map((e) => e.identifier);
|
|
91
|
+
const sorted = [...identifiers].sort();
|
|
92
|
+
expect(identifiers).toEqual(sorted);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("buildArdCatalog: determinism — same input produces identical output", () => {
|
|
96
|
+
const input = {
|
|
97
|
+
site: "s",
|
|
98
|
+
baseUrl: "https://s.example",
|
|
99
|
+
languages: { default: "de", supported: ["de"] },
|
|
100
|
+
knowledge: [
|
|
101
|
+
{ domain: "offer", url: "/api/agent/v1/offer.json", schema: "gogol.agent.knowledge/offer@1" },
|
|
102
|
+
],
|
|
103
|
+
mcp: { url: "/api/agent/mcp", protocolVersion: "2025-06-18" },
|
|
104
|
+
};
|
|
105
|
+
const a = buildArdCatalog(buildAgentSurfaceManifest(input), "My Site");
|
|
106
|
+
const b = buildArdCatalog(buildAgentSurfaceManifest(input), "My Site");
|
|
107
|
+
expect(JSON.stringify(a)).toBe(JSON.stringify(b));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("buildArdCatalog: host identifier uses did:web format with domain", () => {
|
|
111
|
+
const manifest = buildAgentSurfaceManifest({
|
|
112
|
+
site: "warpgogol-com",
|
|
113
|
+
baseUrl: "https://warpgogol.com",
|
|
114
|
+
languages: { default: "de", supported: ["de"] },
|
|
115
|
+
});
|
|
116
|
+
const catalog = buildArdCatalog(manifest, "Warpgogol");
|
|
117
|
+
expect(catalog.host.identifier).toBe("did:web:warpgogol.com");
|
|
118
|
+
expect(catalog.host.displayName).toBe("Warpgogol");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("buildArdCatalog: default hostDisplayName falls back to site name", () => {
|
|
122
|
+
const manifest = buildAgentSurfaceManifest({
|
|
123
|
+
site: "my-site",
|
|
124
|
+
baseUrl: "https://example.com",
|
|
125
|
+
languages: { default: "de", supported: ["de"] },
|
|
126
|
+
});
|
|
127
|
+
const catalog = buildArdCatalog(manifest);
|
|
128
|
+
expect(catalog.host.displayName).toBe("my-site");
|
|
129
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module "@syrokomskyi/axiom-study" {
|
|
2
|
+
export interface Finding {
|
|
3
|
+
findingId: string;
|
|
4
|
+
semanticFingerprint: string;
|
|
5
|
+
methodologyId: string;
|
|
6
|
+
ruleId: string;
|
|
7
|
+
affectedSubjectId: string;
|
|
8
|
+
title: string;
|
|
9
|
+
severity: "info" | "low" | "medium" | "high" | "critical";
|
|
10
|
+
evidence: Array<{ evidenceId: string; digest: string }>;
|
|
11
|
+
uncertainty: unknown[];
|
|
12
|
+
extension: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"target": "ES2022",
|
|
4
4
|
"module": "preserve",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"isolatedModules": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
5
12
|
"noEmit": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
6
14
|
"declaration": true,
|
|
7
15
|
"rewriteRelativeImportExtensions": true,
|
|
8
|
-
"types": [
|
|
9
|
-
"node"
|
|
10
|
-
],
|
|
16
|
+
"types": ["node"],
|
|
11
17
|
"rootDir": ".",
|
|
12
|
-
"outDir": "dist"
|
|
13
|
-
"allowImportingTsExtensions": true
|
|
18
|
+
"outDir": "dist"
|
|
14
19
|
},
|
|
15
|
-
"include": [
|
|
16
|
-
"src/**/*.ts"
|
|
17
|
-
],
|
|
20
|
+
"include": ["src/**/*.ts"],
|
|
18
21
|
"exclude": [
|
|
19
22
|
"src/**/*.template.*",
|
|
20
23
|
"src/**/*.test.ts",
|