@reqvire-org/ads 0.3.16 → 0.3.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/check.mjs +13 -8
- package/dist/evidence.mjs +18 -0
- package/dist/investigations.mjs +70 -0
- package/dist/mcp/context.mjs +1 -1
- package/dist/mcp/mutations.mjs +34 -17
- package/dist/mcp/preview-widget.bundle.js +12 -12
- package/dist/mcp/resource-loader.js +3 -3
- package/dist/mcp/server.mjs +30 -8
- package/dist/scenario-selectors.mjs +140 -0
- package/dist/selector-match.mjs +94 -0
- package/dist/showcase/client.bundle.js +2 -2
- package/dist/skills/ads-break-ui/SKILL.md +3 -0
- package/dist/skills/ads-break-ui/references/evidence.md +73 -0
- package/dist/skills/ads-break-ui/references/scenarios.md +32 -0
- package/dist/skills/ads-designer/SKILL.md +3 -0
- package/dist/skills/ads-designer/references/evidence.md +73 -0
- package/dist/skills/ads-designer/references/scenarios.md +32 -0
- package/dist/skills/ads-import/SKILL.md +3 -0
- package/dist/skills/ads-import/references/evidence.md +73 -0
- package/dist/skills/ads-import/references/scenarios.md +32 -0
- package/dist/skills/ads-migrate/SKILL.md +3 -0
- package/dist/skills/ads-migrate/references/evidence.md +73 -0
- package/dist/skills/ads-migrate/references/scenarios.md +32 -0
- package/dist/skills/ads-motion/SKILL.md +3 -0
- package/dist/skills/ads-motion/references/evidence.md +73 -0
- package/dist/skills/ads-motion/references/scenarios.md +32 -0
- package/dist/verification/browser.mjs +22 -7
- package/dist/verification/playwright-reporter.cjs +23 -5
- package/dist/verification.mjs +31 -9
- package/docs/ARCHITECTURE.md +20 -3
- package/docs/BEHAVIORS.md +18 -0
- package/docs/CHANGELOG.md +13 -0
- package/docs/MCP.md +13 -0
- package/package.json +1 -1
|
@@ -31,14 +31,14 @@ export class PreviewResourceLoader {
|
|
|
31
31
|
if (!record) throw new Error(`Unknown preview dependency: ${uri}`);
|
|
32
32
|
let response;
|
|
33
33
|
try { response = await this.read(uri); }
|
|
34
|
-
catch (error) { throw new Error(`Preview resource read failed for ${uri}: ${error.message}`, { cause: error }); }
|
|
34
|
+
catch (error) { throw Object.assign(new Error(`Preview resource read failed for ${uri}: ${error.message}`, { cause: error }), { code: "ADS_RESOURCE_READ", category: "transport", resourceUri: uri }); }
|
|
35
35
|
if (this.closed) throw new Error("The preview App was closed.");
|
|
36
36
|
const content = response.contents.find(item => item.uri === uri);
|
|
37
|
-
if (!content) throw new Error(`Missing preview dependency: ${uri}`);
|
|
37
|
+
if (!content) throw Object.assign(new Error(`Missing preview dependency: ${uri}`), { code: "ADS_RESOURCE_CONTENT", category: "build", resourceUri: uri });
|
|
38
38
|
const bytes = typeof content.text === "string" ? new TextEncoder().encode(content.text) : Uint8Array.from(atob(content.blob), letter => letter.charCodeAt(0));
|
|
39
39
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
40
40
|
const actual = "sha256:" + [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
41
|
-
if (bytes.length !== record.size || actual !== record.digest) throw new Error("Preview resource integrity check failed. Rebuild and reopen the preview.");
|
|
41
|
+
if (bytes.length !== record.size || actual !== record.digest) throw Object.assign(new Error("Preview resource integrity check failed. Rebuild and reopen the preview."), { code: "ADS_RESOURCE_INTEGRITY", category: "build", resourceUri: uri });
|
|
42
42
|
return bytes;
|
|
43
43
|
})();
|
|
44
44
|
this.bytes.set(uri, task);
|
package/dist/mcp/server.mjs
CHANGED
|
@@ -6,6 +6,8 @@ import { z } from "zod";
|
|
|
6
6
|
import { checkProject } from "../check.mjs";
|
|
7
7
|
import { loadConfig, sourceFiles } from "../config.mjs";
|
|
8
8
|
import { behaviorStatus, createVerificationController, BEHAVIOR_URI } from "../verification.mjs";
|
|
9
|
+
import { INVESTIGATIONS_URI, investigationIndex, readInvestigation, readInvestigationResource } from "../investigations.mjs";
|
|
10
|
+
import { failure } from "../evidence.mjs";
|
|
9
11
|
import { inspectTokens } from "../tokens.mjs";
|
|
10
12
|
import { inspectScenarios } from "../scenarios.mjs";
|
|
11
13
|
import { startShowcaseHost } from "./showcase-host.mjs";
|
|
@@ -108,6 +110,7 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
108
110
|
return [
|
|
109
111
|
{ uri: GUIDE_URI, name: "Start here: design-system workflow", description: "Read first for UI/ADS tasks, especially when only resource access is exposed. Explains the exact workspace, tool sequence, skill selection, previews, project commands and fallback.", mimeType: "text/markdown" },
|
|
110
112
|
{ uri: "ads://guides/behaviors", name: "Controller and behavioral verification guide", mimeType: "text/markdown" },
|
|
113
|
+
{ uri: "ads://guides/evidence", name: "Failure boundaries and compact investigations", mimeType: "text/markdown" },
|
|
111
114
|
{ uri: "ads://guides/atomic-styles", name: "Atomic styles: composition conflicts and custom-property extraction", mimeType: "text/markdown" },
|
|
112
115
|
{ uri: "ads://guides/scenarios", name: "Scenario coverage and public class-helper contracts", mimeType: "text/markdown" },
|
|
113
116
|
{ uri: "ads://guides/captures", name: "On-demand captures and candidate visual review", mimeType: "text/markdown" },
|
|
@@ -117,15 +120,17 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
117
120
|
{ uri: `${PROJECT_URI}/catalog`, name: "Public components and patterns", mimeType: "application/json" },
|
|
118
121
|
{ uri: `${PROJECT_URI}/scenarios`, name: "Independently mountable scenarios", mimeType: "application/json" },
|
|
119
122
|
{ uri: BEHAVIOR_URI, name: "Behavior contracts, demonstrations and current verification evidence", mimeType: "application/json" },
|
|
123
|
+
{ uri: INVESTIGATIONS_URI, name: "Latest per-check investigation evidence", mimeType: "application/json" },
|
|
120
124
|
{ uri: CAPTURES_URI, name: "On-demand current and candidate screenshots", mimeType: "application/json" },
|
|
121
125
|
{ uri: `${PROJECT_URI}/checks`, name: "Current ADS architecture diagnostics", mimeType: "application/json" },
|
|
126
|
+
// Keep fallback workflow metadata discoverable in the bounded first page.
|
|
127
|
+
...skills.skills.map(({ uri, frontmatter }) => ({ uri, name: frontmatter.name, description: frontmatter.description, mimeType: "text/markdown" })),
|
|
122
128
|
...documents.filter(document => promoted.has(document.uri)).sort((a, b) => Number(b.uri === context.publicEntryUri) - Number(a.uri === context.publicEntryUri)).map(({ file: _file, ...document }) => document),
|
|
123
129
|
{ uri: TOKENS_APP_URI, name: "Experimental token widget", mimeType: APP_MIME },
|
|
124
130
|
{ uri: SCENARIO_APP_URI, name: "Experimental scenario widget", mimeType: APP_MIME },
|
|
125
131
|
...(options.config.mcp.previewManifest ? [{ uri: PREVIEW_APP_URI, name: "Shared-resource preview App", mimeType: APP_MIME }, { uri: PREVIEW_MANIFEST_URI, name: "Preview dependency graph", mimeType: "application/json" }] : []),
|
|
126
132
|
...(options.config.mcp.previewManifest ? resourceAssets() : []),
|
|
127
133
|
...allApps().map((app) => ({ uri: appUri(app.id), name: app.title, description: app.description, mimeType: APP_MIME })),
|
|
128
|
-
...skills.skills.map(({ uri, frontmatter }) => ({ uri, name: frontmatter.name, description: frontmatter.description, mimeType: "text/markdown" })),
|
|
129
134
|
...documents.filter((document) => !promoted.has(document.uri)).map(({ file: _file, ...document }) => document),
|
|
130
135
|
...[...skills.files.values()].filter((file) => !skillEntries.has(file.uri)).map(({ uri, name, mimeType }) => ({ uri, name, mimeType })),
|
|
131
136
|
];
|
|
@@ -142,13 +147,14 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
142
147
|
build: summarizeBuild(builds()),
|
|
143
148
|
previews: previews(), skills: skills.skills.map(({ uri, frontmatter }) => ({ uri, name: frontmatter.name, description: frontmatter.description })),
|
|
144
149
|
scenarios: { count: getRegistry().scenarios.length, diagnostics: getRegistry().diagnostics, resourceUri: `${PROJECT_URI}/scenarios`, tool: "ads_show_scenario" },
|
|
145
|
-
behaviors: { resourceUri: BEHAVIOR_URI, discoveryTool: "ads_behaviors", verificationTool: "ads_verify", ...verification.status() },
|
|
150
|
+
behaviors: { resourceUri: BEHAVIOR_URI, discoveryTool: "ads_behaviors", verificationTool: "ads_verify", investigationTool: "ads_investigate", investigationResourceUri: INVESTIGATIONS_URI, ...verification.status() },
|
|
146
151
|
captures: { resourceUri: CAPTURES_URI, tool: "ads_capture", clearTool: "ads_capture_clear", ...captures.status() },
|
|
147
152
|
experimental: { apps: true, nativeSkills: era === "modern", nativeSkillActivation: "host-controlled" } };
|
|
148
153
|
}
|
|
149
154
|
async function read(uri, signal) {
|
|
150
155
|
if (uri === GUIDE_URI) return { contents: [{ uri, mimeType: "text/markdown", text: projectGuide(project(), [...previews(), ...scenarioApps()]) }] };
|
|
151
156
|
if (uri === "ads://guides/behaviors") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../../docs/BEHAVIORS.md", import.meta.url), "utf8") }] };
|
|
157
|
+
if (uri === "ads://guides/evidence") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/evidence.md", import.meta.url), "utf8") }] };
|
|
152
158
|
if (uri === "ads://guides/atomic-styles") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/atomic-styles.md", import.meta.url), "utf8") }] };
|
|
153
159
|
if (uri === "ads://guides/scenarios") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/scenarios.md", import.meta.url), "utf8") }] };
|
|
154
160
|
if (uri === "ads://guides/captures") return { contents: [{ uri, mimeType: "text/markdown", text: readFileSync(new URL("../skills/ads-designer/references/captures.md", import.meta.url), "utf8") }] };
|
|
@@ -161,12 +167,17 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
161
167
|
else if (uri === `${PROJECT_URI}/scenarios`) value = getRegistry();
|
|
162
168
|
else if (uri === `${PROJECT_URI}/checks`) value = checkProject(root);
|
|
163
169
|
else if (uri === BEHAVIOR_URI) value = behaviorStatus(options);
|
|
170
|
+
else if (uri === INVESTIGATIONS_URI) value = investigationIndex(root);
|
|
164
171
|
else if (uri === CAPTURES_URI) value = captureIndex();
|
|
165
172
|
else if (uri === PREVIEW_MANIFEST_URI && options.config.mcp.previewManifest) {
|
|
166
173
|
value = readPreviewManifest(previewOptions());
|
|
167
174
|
if (!value) throw new Error("Preview resources are not built yet. Invoke ads_show_scenario to start the managed compiler.");
|
|
168
175
|
}
|
|
169
176
|
if (value !== undefined) return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(value) }] };
|
|
177
|
+
if (uri.startsWith("ads://investigations/") || uri.startsWith("ads://verification-artifacts/")) {
|
|
178
|
+
try { return {contents:[readInvestigationResource(root,uri)]}; }
|
|
179
|
+
catch(error){throw new ResourceNotFoundError(uri,error.message);}
|
|
180
|
+
}
|
|
170
181
|
if (uri.startsWith("ads://captures/")) {
|
|
171
182
|
try { const inventory = captureInventory(); return { contents: [captures.store.readResource(uri, inventory.scenarios, inventory.revision, { allowUnknownTargets: !!inventory.diagnostics.length })] }; }
|
|
172
183
|
catch (error) { if (error.code === "ADS_CAPTURE_NOT_FOUND") throw new ResourceNotFoundError(uri, error.message); throw error; }
|
|
@@ -244,7 +255,7 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
244
255
|
} },
|
|
245
256
|
{ name: "ads_check", description: "Run read-only core ADS architecture checks; returns blocking diagnostics separately from nonblocking advisories, with warnings before informational limits. Atomic warnings include helper/forwarded-class paths and ads://guides/atomic-styles guidance; unresolved paths do not establish safe composition. Project Oxlint, typecheck, build and browser tests remain separate.", schema: z.object({ limit: z.number().int().min(1).max(50).default(20) }).strict(), call: ({ limit }) => {
|
|
246
257
|
const result = checkProject(root);
|
|
247
|
-
return json({ ok: result.ok, total: result.diagnostics.length, diagnostics: result.diagnostics.slice(0, limit), advisoryCount: result.advisories?.length ?? 0, advisories: (result.advisories ?? []).slice(0, limit), resourceUri: `${PROJECT_URI}/checks` });
|
|
258
|
+
return json({ ok: result.ok, acceptance:result.acceptance, total: result.diagnostics.length, diagnostics: result.diagnostics.slice(0, limit), advisoryCount: result.advisories?.length ?? 0, advisories: (result.advisories ?? []).slice(0, limit), resourceUri: `${PROJECT_URI}/checks` });
|
|
248
259
|
} },
|
|
249
260
|
{ name: "ads_skills", description: "Select one ADS workflow after ads_status: ads-designer for building/refining/debugging UI; ads-migrate for a library reference; ads-import for another project's ADS; ads-motion for animation; ads-break-ui for stress testing. Then read that workflow with ads_read_skill. Listing does not activate a native skill.", schema: empty, call: () => json({ skills: skills.skills.map(({ uri, frontmatter }) => ({ uri, ...frontmatter })) }) },
|
|
250
261
|
{ name: "ads_read_skill", description: "Read a selected skill or relative supporting text file in bounded chunks. This is explicit instruction access, not automatic native skill activation/install.", schema: z.object({ name: z.string().max(64), file: z.string().max(300).default("SKILL.md"), offset: z.number().int().min(0).default(0), limit: z.number().int().min(1).max(8000).default(6000) }).strict(), call: ({ name, file, offset, limit }) => {
|
|
@@ -284,7 +295,8 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
284
295
|
demonstrations: demonstrations.slice(0, 10), demonstrationCount: demonstrations.length, checkCount: Object.keys(item.checks).length };
|
|
285
296
|
}), diagnostics: inventory.diagnostics.slice(0, 50), resourceUri: BEHAVIOR_URI });
|
|
286
297
|
} },
|
|
287
|
-
{ name: "
|
|
298
|
+
{ name: "ads_investigate", title: "Inspect check evidence", description: "Read the latest compact investigation for an exact registered unit/check, including candidate runs. Returns failure boundary, exact input/observed preview revisions, source/failing step, observed viewport, failed startup requests and available artifact resource URIs. Does not rerun tests or infer root cause. Bundles replace the previous entry per check; compare revisions with ads_behaviors before claiming freshness.", schema: z.object({unit:z.string().min(1).max(256),check:z.string().min(1).max(100)}).strict(), call: args => json(readInvestigation(root,args)) },
|
|
299
|
+
{ name: "ads_verify", description: "Run one owner's registered development behavior checks using its configured runner/profile. Executes trusted project commands with bounded output, cancellation and process-tree cleanup; installs nothing. Progress and per-check timings are available through ads_status; a profile timeout bounds the whole run. Browser profiles use an isolated managed showcase/MCP App. React StrictMode requires a project development harness, not a production widget. Missing/skipped cases, stale inputs and process exit alone cannot produce passing evidence. Results separate failure boundaries and explicit acceptance scope; inspect retained source/request/artifact evidence with ads_investigate. Discover exact unit/check IDs with ads_behaviors first.", schema: z.object({ unit: z.string().min(1).max(256), checks: z.array(z.string().min(1).max(100)).min(1).max(100).optional() }).strict(), annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }, call: async (args, signal, progress) => {
|
|
288
300
|
const result = await verification.run(args, signal, progress);
|
|
289
301
|
await refresh();
|
|
290
302
|
return { ...json(result), ...(!result.ok ? { isError: true } : {}) };
|
|
@@ -330,7 +342,7 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
330
342
|
void context.mcpReq.notify({ method: "notifications/progress", params: { progressToken: token, progress: ++progress, message: value.message ?? `${value.profile}: ${value.completed}/${value.total} completed; ${value.active.map(item => `${item.unit}#${item.check}`).join(", ") || "no active check reported"}; ${value.elapsedMs} ms elapsed` } }).catch(() => {});
|
|
331
343
|
};
|
|
332
344
|
try { return await tool.call(input.data, context.mcpReq.signal, notify); }
|
|
333
|
-
catch (error) { return { isError: true, content: [{ type: "text", text: error.message }],
|
|
345
|
+
catch (error) { return { isError: true, content: [{ type: "text", text: error.message }], structuredContent: {ok:false,message:error.message,failure:failure(error.build?"build":"runner",error.build?"preview-build":"tool-execution",error.build?.code??"ADS_TOOL_ERROR",{certainty:"observed-boundary"}),...(error.build?{build:error.build}:{})} }; }
|
|
334
346
|
});
|
|
335
347
|
server.setRequestHandler("resources/list", ({ params = {} }) => {
|
|
336
348
|
previewWatch?.start();
|
|
@@ -338,10 +350,16 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
338
350
|
const listing = page(resources(), cursor, "resources");
|
|
339
351
|
return { resources: listing.items, ...(listing.nextCursor ? { nextCursor: listing.nextCursor } : {}) };
|
|
340
352
|
});
|
|
341
|
-
server.setRequestHandler("resources/templates/list", () => ({ resourceTemplates: [
|
|
353
|
+
server.setRequestHandler("resources/templates/list", () => ({ resourceTemplates: [
|
|
354
|
+
{ uriTemplate: "ads://captures/{key}/{slot}", name: "Current/candidate screenshot", description: "Exact keys and available slots come from ads://project/captures. Slots are replaced in place, not revision history.", mimeType: "image/png" },
|
|
355
|
+
{ uriTemplate: "ads://investigations/{unit}/{check}", name: "Latest check investigation", description: "Use encoded unit/check segments from ads://project/investigations. One latest bundle per check, including candidate verification.", mimeType: "application/json" },
|
|
356
|
+
] }));
|
|
342
357
|
server.setRequestHandler("resources/read", ({ params: { uri } }, context) => read(uri, context.mcpReq.signal));
|
|
343
358
|
server.setRequestHandler("resources/subscribe", ({ params: { uri } }) => {
|
|
344
359
|
if (!resources().some((entry) => entry.uri === uri)) {
|
|
360
|
+
if(uri.startsWith("ads://investigations/")) {
|
|
361
|
+
try {readInvestigationResource(root,uri);subscribed.add(uri);return {};}catch{throw new ResourceNotFoundError(uri,"Unknown check investigation.");}
|
|
362
|
+
}
|
|
345
363
|
if (!uri.startsWith("ads://captures/")) throw new ResourceNotFoundError(uri, "Unknown ADS resource subscription.");
|
|
346
364
|
try { const inventory = captureInventory(); captures.store.readResource(uri, inventory.scenarios, inventory.revision, { allowUnknownTargets: !!inventory.diagnostics.length }); } catch { throw new ResourceNotFoundError(uri, "Unknown or absent screenshot slot."); }
|
|
347
365
|
}
|
|
@@ -382,6 +400,8 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
382
400
|
let previousBuild = JSON.stringify(builds());
|
|
383
401
|
let previousCaptures = captures.store.fingerprint();
|
|
384
402
|
let previousCaptureUris = captures.store.resourceUris();
|
|
403
|
+
const investigationFingerprint=()=>fileFingerprint([path.join(root,".ds/investigations.json")]);
|
|
404
|
+
let previousInvestigations=investigationFingerprint();
|
|
385
405
|
async function refresh(force = false) {
|
|
386
406
|
// Polling covers replaced files and new directories consistently across OSes.
|
|
387
407
|
cache.documents = undefined;
|
|
@@ -394,7 +414,8 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
394
414
|
const artifactsChanged = artifacts !== previousArtifacts;
|
|
395
415
|
const assetsChanged = assets !== previousAssets;
|
|
396
416
|
const capturesChanged = captures.store.fingerprint() !== previousCaptures;
|
|
397
|
-
|
|
417
|
+
const investigationsChanged=investigationFingerprint()!==previousInvestigations;
|
|
418
|
+
if (!filesChanged && !buildChanged && !artifactsChanged && !assetsChanged && !capturesChanged && !investigationsChanged) return false;
|
|
398
419
|
if (filesChanged) {
|
|
399
420
|
const updated = loadConfig(root);
|
|
400
421
|
await previewWatch?.reconfigure?.(updated.config.mcp);
|
|
@@ -411,7 +432,8 @@ export function createAdsMcp(directory, { era = "legacy", skillDirectory, pollIn
|
|
|
411
432
|
previousAssets = assetFingerprint();
|
|
412
433
|
previousBuild = JSON.stringify(builds());
|
|
413
434
|
const targets = filesChanged ? era === "modern" ? resources().map((entry) => entry.uri).filter((uri) => !skills.files.has(uri)) : [...subscribed]
|
|
414
|
-
: [BUILD_URI, BEHAVIOR_URI, `${PROJECT_URI}/status`, ...(options.config.mcp.previewManifest ? [PREVIEW_MANIFEST_URI] : []), ...(artifactsChanged ? allApps().map(app => appUri(app.id)) : [])].filter((uri) => era === "modern" || subscribed.has(uri));
|
|
435
|
+
: [BUILD_URI, BEHAVIOR_URI, INVESTIGATIONS_URI, `${PROJECT_URI}/status`, ...(options.config.mcp.previewManifest ? [PREVIEW_MANIFEST_URI] : []), ...(artifactsChanged ? allApps().map(app => appUri(app.id)) : [])].filter((uri) => era === "modern" || subscribed.has(uri));
|
|
436
|
+
if(investigationsChanged){targets.push(...[INVESTIGATIONS_URI,...investigationIndex(root).entries.map(entry=>entry.resourceUri),...[...subscribed].filter(uri=>uri.startsWith("ads://investigations/"))].filter(uri=>era==="modern"||subscribed.has(uri)));previousInvestigations=investigationFingerprint();}
|
|
415
437
|
if (capturesChanged || filesChanged) {
|
|
416
438
|
const current = (captures.store.fingerprint() ? captureIndex().captures : []).flatMap(record => [record.current?.resourceUri, record.candidate?.resourceUri].filter(Boolean));
|
|
417
439
|
targets.push(...[CAPTURES_URI, ...previousCaptureUris, ...current].filter(uri => era === "modern" || subscribed.has(uri)));
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import postcss from "postcss";
|
|
4
|
+
import { isDevelopmentFile, isInside } from "./config.mjs";
|
|
5
|
+
import { publicApi } from "./public-api.mjs";
|
|
6
|
+
import { createSourceValues, reference, literal, isFunction } from "./source-values.mjs";
|
|
7
|
+
import { uiOperation } from "./ui-framework.mjs";
|
|
8
|
+
import { nestedSelectors, selectorWitness } from "./selector-match.mjs";
|
|
9
|
+
|
|
10
|
+
/** Native semantics may be composed through actual public runtime styles.
|
|
11
|
+
* A matching tag relationship is evidence of ownership, not visual correctness. */
|
|
12
|
+
export function createScenarioSelectors(program, options) {
|
|
13
|
+
const checker = program.getTypeChecker(), templates = new WeakMap(), cache = new WeakMap();
|
|
14
|
+
function imported(node, name, seen = new Set()) {
|
|
15
|
+
if (!node || seen.has(node)) return false;
|
|
16
|
+
seen.add(node);
|
|
17
|
+
const base = ts.isPropertyAccessExpression(node) ? node.expression : node;
|
|
18
|
+
return checker.getSymbolAtLocation(base)?.declarations?.some(declaration => {
|
|
19
|
+
if (ts.isImportSpecifier(declaration)) return (declaration.propertyName ?? declaration.name).text === name && ["@linaria/atomic", "@linaria/core"].includes(declaration.parent.parent.parent.moduleSpecifier.text);
|
|
20
|
+
if (ts.isNamespaceImport(declaration) && ts.isPropertyAccessExpression(node)) return node.name.text === name && ["@linaria/atomic", "@linaria/core"].includes(declaration.parent.parent.moduleSpecifier.text);
|
|
21
|
+
return ts.isVariableDeclaration(declaration) && imported(declaration.initializer, name, seen);
|
|
22
|
+
}) ?? false;
|
|
23
|
+
}
|
|
24
|
+
const isCx = node => imported(node, "cx");
|
|
25
|
+
const values = createSourceValues(program, options, { strict: true, preserveCall: node => isCx(node) || uiOperation(node, checker) === "createElement" });
|
|
26
|
+
const { state, next, resolve, get, keys, props, returns } = values;
|
|
27
|
+
const publicNodes = new Set(publicApi(program, options).filter(item => item.renderable).map(item => resolve(reference(item.declaration.name ?? item.declaration), state())).filter(value => value.kind === "node").map(value => value.node));
|
|
28
|
+
const runtime = node => !isDevelopmentFile(node.getSourceFile().fileName, options) && ["components", "patterns"].some(folder => isInside(path.join(options.dsRoot, folder), node.getSourceFile().fileName));
|
|
29
|
+
function selectors(node) {
|
|
30
|
+
if (templates.has(node)) return templates.get(node);
|
|
31
|
+
const result = []; templates.set(node, result);
|
|
32
|
+
if (!runtime(node) || !ts.isNoSubstitutionTemplateLiteral(node.template)) return result;
|
|
33
|
+
try {
|
|
34
|
+
function walk(container, parents) {
|
|
35
|
+
for (const child of container.nodes ?? []) {
|
|
36
|
+
if (child.type === "rule") {
|
|
37
|
+
const selected = nestedSelectors(parents, child.selector);
|
|
38
|
+
if (child.nodes.some(value => value.type === "decl" && !value.prop.startsWith("--"))) result.push(...selected);
|
|
39
|
+
walk(child, selected);
|
|
40
|
+
} else if (child.type === "atrule" && !/(?:^|-)keyframes$/.test(child.name)) walk(child, parents);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
walk(postcss.parse(node.template.text), ["&"]);
|
|
44
|
+
} catch { /* The CSS syntax gate owns malformed templates. */ }
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function classes(input, context) {
|
|
48
|
+
const value = resolve(input, next(context));
|
|
49
|
+
if (value.kind === "union") {
|
|
50
|
+
const all = value.values.map(branch => classes(branch, next(context)));
|
|
51
|
+
return all[0]?.filter(entry => all.every(branch => branch.includes(entry))) ?? [];
|
|
52
|
+
}
|
|
53
|
+
if (value.kind !== "node") return [];
|
|
54
|
+
if (ts.isTaggedTemplateExpression(value.node) && imported(value.node.tag, "css")) return [value.node];
|
|
55
|
+
if (ts.isCallExpression(value.node) && isCx(value.node.expression)) return value.node.arguments.flatMap(argument => classes(reference(argument, value.env, value.via), next(context)));
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
function element(node, env, via) {
|
|
59
|
+
if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
60
|
+
const opening = ts.isJsxElement(node) ? node.openingElement : node;
|
|
61
|
+
const attributes = props(opening, env, via);
|
|
62
|
+
const children = ts.isJsxElement(node) ? node.children.filter(child => !ts.isJsxText(child) || child.text.trim()).filter(child => !ts.isJsxExpression(child) || child.expression) : [];
|
|
63
|
+
if (children.length) attributes.layers.push(new Map([["children", children.length === 1 ? reference(children[0], env, via) : { kind: "array", values: children.map(child => reference(child, env, via)) }]]));
|
|
64
|
+
return { tag: reference(opening.tagName, env, via), attributes, intrinsic: ts.isIdentifier(opening.tagName) && /^[a-z]/.test(opening.tagName.text) ? opening.tagName.text : null };
|
|
65
|
+
}
|
|
66
|
+
if (ts.isCallExpression(node) && uiOperation(node.expression, checker) === "createElement") {
|
|
67
|
+
const attributes = { kind: "object", layers: node.arguments[1] && node.arguments[1].kind !== ts.SyntaxKind.NullKeyword ? [reference(node.arguments[1], env, via)] : [] };
|
|
68
|
+
const children = node.arguments.slice(2).map(child => reference(child, env, via));
|
|
69
|
+
if (children.length) attributes.layers.push(new Map([["children", children.length === 1 ? children[0] : { kind: "array", values: children }]]));
|
|
70
|
+
return { tag: reference(node.arguments[0], env, via), attributes, intrinsic: ts.isStringLiteralLike(node.arguments[0]) ? node.arguments[0].text : null };
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return site => {
|
|
75
|
+
if (cache.has(site)) return cache.get(site);
|
|
76
|
+
const native = ts.isJsxOpeningElement(site) ? site.parent : site;
|
|
77
|
+
let steps = 0, limited = false;
|
|
78
|
+
const witnesses = [];
|
|
79
|
+
function render(input, parent, context, owned = false) {
|
|
80
|
+
if (++steps > 700 || context.depth > 40) { limited = true; return []; }
|
|
81
|
+
const value = resolve(input, next(context));
|
|
82
|
+
context = value.context ?? input.context ?? context;
|
|
83
|
+
if (value.kind === "union") {
|
|
84
|
+
// Unknown render alternatives cannot silently authorize a native control.
|
|
85
|
+
limited = true; return [];
|
|
86
|
+
}
|
|
87
|
+
if (value.kind === "array") return value.values.flatMap(child => render(child, parent, next(context), owned));
|
|
88
|
+
if (value.kind !== "node") return [];
|
|
89
|
+
const {node, env, via} = value;
|
|
90
|
+
if (ts.isJsxExpression(node)) return render(reference(node.expression, env, via), parent, next(context), owned);
|
|
91
|
+
if (ts.isJsxFragment(node)) return node.children.flatMap(child => render(reference(child, env, via), parent, next(context), owned));
|
|
92
|
+
if (ts.isArrayLiteralExpression(node)) return node.elements.flatMap(child => render(reference(child, env, via), parent, next(context), owned));
|
|
93
|
+
const view = element(node, env, via);
|
|
94
|
+
if (!view) return [];
|
|
95
|
+
const target = view.intrinsic ? literal(view.intrinsic) : resolve(view.tag, next(context));
|
|
96
|
+
if (["provider", "context"].includes(target.kind)) return render(get(view.attributes, "children", next(context)), parent, next(context), owned);
|
|
97
|
+
if (target.kind === "node" && isFunction(target.node)) {
|
|
98
|
+
const outputs = returns(target, [view.attributes], next(context), "component");
|
|
99
|
+
if (outputs.length !== 1) { limited = true; return []; }
|
|
100
|
+
return render(outputs[0], parent, next(context), owned || publicNodes.has(target.node));
|
|
101
|
+
}
|
|
102
|
+
if (target.kind !== "literal" || typeof target.value !== "string") return [];
|
|
103
|
+
const attributes = new Map(),ownedAttributes=new Set();
|
|
104
|
+
for (const name of keys(view.attributes, next(context))) {
|
|
105
|
+
const input=get(view.attributes,name,next(context));
|
|
106
|
+
const value = resolve(input, next(context));
|
|
107
|
+
attributes.set(name, value.kind === "literal" ? value.value : undefined);
|
|
108
|
+
if(owned&&runtime(node)&&input.kind==="node"&&ts.isStringLiteralLike(input.node)&&runtime(input.node))ownedAttributes.add(name);
|
|
109
|
+
}
|
|
110
|
+
const result = {tag:target.value.toLowerCase(),attributes,ownedAttributes,parent,children:[],site:node,styles:owned ? classes(get(view.attributes,"className",next(context)),next(context)) : []};
|
|
111
|
+
if (node === native) witnesses.push(result);
|
|
112
|
+
result.children = render(get(view.attributes,"children",next(context)),result,next(context),owned);
|
|
113
|
+
return [result];
|
|
114
|
+
}
|
|
115
|
+
function proves(roots) {
|
|
116
|
+
function all(node) { return [node,...node.children.flatMap(all)]; }
|
|
117
|
+
const nodes = roots.flatMap(all);
|
|
118
|
+
return witnesses.some(witness => nodes.some(anchor => anchor.styles.some(template => selectors(template).some(selector => nodes.some(target => {
|
|
119
|
+
// Styles must target the native node itself or public UI inside it.
|
|
120
|
+
let inside = target === witness;
|
|
121
|
+
for (let parent = target.parent; parent && !inside; parent = parent.parent) inside = parent === witness;
|
|
122
|
+
return inside && selectorWitness(selector,target,anchor,witness);
|
|
123
|
+
})))));
|
|
124
|
+
}
|
|
125
|
+
// First follow the native wrapper into its public contents. This also works
|
|
126
|
+
// when a scenario helper returns it and is invoked under a public container.
|
|
127
|
+
let roots = render(reference(native),null,state());
|
|
128
|
+
let ok = proves(roots);
|
|
129
|
+
if (!ok) {
|
|
130
|
+
let parent = native.parent;
|
|
131
|
+
while (parent && (ts.isJsxExpression(parent) || ts.isParenthesizedExpression(parent))) parent = parent.parent;
|
|
132
|
+
if (parent && element(parent,new Map(),[])) {
|
|
133
|
+
const view = element(parent,new Map(),[]), target = resolve(view.tag,state());
|
|
134
|
+
if (target.kind === "node" && publicNodes.has(target.node)) { witnesses.length=0; steps=0; limited=false; roots=render(reference(parent),null,state()); ok=proves(roots); }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const result = {ok, reasons:ok ? [] : [limited ? "Selector composition reached an unresolved render branch or source work limit." : "No attached public-runtime Linaria selector proves this native tag or runtime-part relationship."], evidence:ok ? {kind:"selector-composition", established:["public-runtime-style-provenance","native-position-ownership"], unverified:["computed-styles","interaction","accessibility"]} : undefined};
|
|
138
|
+
cache.set(site,result);return result;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import selectorParser from "postcss-selector-parser";
|
|
2
|
+
|
|
3
|
+
const states = new Set([":hover", ":active", ":focus", ":focus-visible", ":focus-within", ":visited", ":link", ":any-link", ":enabled", ":disabled", ":checked", ":indeterminate", ":required", ":optional", ":valid", ":invalid", ":placeholder-shown", ":open"]);
|
|
4
|
+
|
|
5
|
+
/** Match bounded source DOM witnesses, not a browser cascade. Ownership requires
|
|
6
|
+
* a positive native tag or a :has binding to a real runtime-owned child marker. */
|
|
7
|
+
export function selectorWitness(selector, target, anchor, native) {
|
|
8
|
+
let steps = 0;
|
|
9
|
+
const yes = typed => ({ typed });
|
|
10
|
+
const and = (a, b) => a === false || b === false ? false : !a || !b ? undefined : yes(a.typed || b.typed);
|
|
11
|
+
const any = results => results.find(value => value?.typed) ?? results.find(Boolean) ?? (results.includes(undefined) ? undefined : false);
|
|
12
|
+
const descendants = (element, found = []) => {
|
|
13
|
+
if (found.length > 128) return found;
|
|
14
|
+
for (const child of element.children) { found.push(child); descendants(child, found); }
|
|
15
|
+
return found;
|
|
16
|
+
};
|
|
17
|
+
function simple(node, element, scope, binding) {
|
|
18
|
+
if (++steps > 2000 || !element) return undefined;
|
|
19
|
+
if (node.type === "comment") return yes(false);
|
|
20
|
+
if (node.type === "nesting") return element === anchor ? yes(false) : false;
|
|
21
|
+
if (node.type === "universal") return yes(false);
|
|
22
|
+
if (node.type === "tag") return !node.namespace && node.value.toLowerCase() === element.tag ? yes(element === native) : false;
|
|
23
|
+
if (node.type === "attribute") {
|
|
24
|
+
if (node.namespace || !element.attributes.has(node.attribute)) return false;
|
|
25
|
+
const actual = element.attributes.get(node.attribute);
|
|
26
|
+
if (actual === undefined) return undefined;
|
|
27
|
+
const owned=binding&&element.parent===native&&element.ownedAttributes?.has(node.attribute);
|
|
28
|
+
if (!node.operator) return yes(owned);
|
|
29
|
+
if (node.operator !== "=" || node.insensitive) return undefined;
|
|
30
|
+
return String(actual) === node.value ? yes(owned) : false;
|
|
31
|
+
}
|
|
32
|
+
if (node.type === "class" || node.type === "id") {
|
|
33
|
+
const value = element.attributes.get(node.type === "class" ? "className" : "id");
|
|
34
|
+
if (typeof value !== "string") return undefined;
|
|
35
|
+
return (node.type === "class" ? value.split(/\s+/).includes(node.value) : value === node.value) ? yes(false) : false;
|
|
36
|
+
}
|
|
37
|
+
if (node.type !== "pseudo" || node.value.startsWith("::")) return undefined;
|
|
38
|
+
if (node.value === ":scope") return element === scope ? yes(false) : false;
|
|
39
|
+
if (states.has(node.value) && !node.nodes?.length) return yes(false);
|
|
40
|
+
if ([":is", ":where"].includes(node.value)) return any(node.nodes.map(branch => match(branch.nodes, element, scope, binding)));
|
|
41
|
+
if (node.value === ":not") {
|
|
42
|
+
const matched = any(node.nodes.map(branch => match(branch.nodes, element, scope)));
|
|
43
|
+
return matched === false ? yes(false) : matched ? false : undefined;
|
|
44
|
+
}
|
|
45
|
+
if (node.value === ":has") {
|
|
46
|
+
const children = descendants(element);
|
|
47
|
+
if (children.length > 128) return undefined;
|
|
48
|
+
return any(node.nodes.flatMap(branch => {
|
|
49
|
+
const nodes = [selectorParser.pseudo({ value: ":scope" }), ...(branch.nodes[0]?.type === "combinator" ? [] : [selectorParser.combinator({ value: " " })]), ...branch.nodes];
|
|
50
|
+
return children.map(child => match(nodes, child, element,element===native));
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
function match(nodes, element, scope, binding=false) {
|
|
56
|
+
if (++steps > 2000 || !element) return undefined;
|
|
57
|
+
const groups = [[]], combinators = [];
|
|
58
|
+
for (const node of nodes) {
|
|
59
|
+
if (node.type === "combinator") { combinators.push(node.value.trim() || " "); groups.push([]); }
|
|
60
|
+
else groups.at(-1).push(node);
|
|
61
|
+
}
|
|
62
|
+
if (groups.some(group => !group.length)) return undefined;
|
|
63
|
+
function part(index, current) {
|
|
64
|
+
if (!current || ++steps > 2000) return current ? undefined : false;
|
|
65
|
+
let result = yes(false);
|
|
66
|
+
for (const node of groups[index]) result = and(result, simple(node, current, scope,binding));
|
|
67
|
+
if (!result || !index) return result;
|
|
68
|
+
const candidates = [];
|
|
69
|
+
if (combinators[index - 1] === ">") candidates.push(current.parent);
|
|
70
|
+
else if (combinators[index - 1] === " ") { for (let parent = current.parent; parent && candidates.length < 32; parent = parent.parent) candidates.push(parent); }
|
|
71
|
+
else if (["+", "~"].includes(combinators[index - 1])) {
|
|
72
|
+
const siblings = current.parent?.children ?? [], position = siblings.indexOf(current);
|
|
73
|
+
candidates.push(...siblings.slice(combinators[index - 1] === "+" ? Math.max(0, position - 1) : 0, position));
|
|
74
|
+
} else return undefined;
|
|
75
|
+
return and(result, any(candidates.map(candidate => part(index - 1, candidate))));
|
|
76
|
+
}
|
|
77
|
+
return part(groups.length - 1, element);
|
|
78
|
+
}
|
|
79
|
+
try { return selectorParser().astSync(selector).nodes.some(branch => match(branch.nodes, target, anchor)?.typed); }
|
|
80
|
+
catch { return false; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Resolve CSS nesting with selector ASTs; never substitute project names. */
|
|
84
|
+
export function nestedSelectors(parents, selector) {
|
|
85
|
+
const children = selectorParser().astSync(selector).nodes;
|
|
86
|
+
const result = [];
|
|
87
|
+
for (const parent of parents) for (const child of children) {
|
|
88
|
+
if (result.length >= 32) return [];
|
|
89
|
+
const copy = child.clone(); let nested = false;
|
|
90
|
+
copy.walkNesting(node => { nested = true; node.replaceWith(selectorParser.pseudo({ value: ":is", nodes: selectorParser().astSync(parent).nodes.map(value => value.clone()) })); });
|
|
91
|
+
result.push(nested ? copy.toString() : `${parent} ${copy}`);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
@@ -112,8 +112,8 @@ Boolean requesting whether a visible border and background is provided by the ho
|
|
|
112
112
|
- omitted: host decides border`)}),My=p.object({method:p.literal("ui/request-display-mode"),params:p.object({mode:Fa.describe("The display mode being requested.")})}),Ay=p.object({mode:Fa.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),DP=p.union([p.literal("model"),p.literal("app")]).describe("Tool visibility scope - who can access the tool."),kk=p.object({resourceUri:p.string().optional(),visibility:p.array(DP).optional().describe(`Who can access this tool. Default: ["model", "app"]
|
|
113
113
|
- "model": Tool visible to and callable by the agent
|
|
114
114
|
- "app": Tool callable by the app from this server only`),csp:p.never().optional(),permissions:p.never().optional()}),$k=p.object({mimeTypes:p.array(p.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')}),FP=p.object({method:p.literal("ui/download-file"),params:p.object({contents:p.array(p.union([or,rr])).describe("Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})}),HP=p.object({method:p.literal("ui/message"),params:p.object({role:p.literal("user").describe('Message role, currently only "user" is supported.'),content:p.array(Nt).describe("Message content blocks (text, image, etc.).")})}),Ck=p.object({method:p.literal("ui/notifications/sandbox-resource-ready"),params:p.object({html:p.string().describe("HTML content to load into the inner iframe."),sandbox:p.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:Tg.optional().describe("CSP configuration from resource metadata."),permissions:Eg.optional().describe("Sandbox permissions from resource metadata.")})}),Ik=p.object({method:p.literal("ui/notifications/tool-result"),params:Po.describe("Standard MCP tool execution result.")}),Ly=p.object({toolInfo:p.object({id:pt.optional().describe("JSON-RPC id of the tools/call request."),tool:xo.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:PP.optional().describe("Current color theme preference."),styles:MP.optional().describe("Style configuration for theming the app."),displayMode:Fa.optional().describe("How the UI is currently displayed."),availableDisplayModes:p.array(Fa).optional().describe("Display modes the host supports."),containerDimensions:p.union([p.object({height:p.number().describe("Fixed container height in pixels.")}),p.object({maxHeight:p.union([p.number(),p.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(p.union([p.object({width:p.number().describe("Fixed container width in pixels.")}),p.object({maxWidth:p.union([p.number(),p.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
115
|
-
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ok=p.object({method:p.literal("ui/notifications/host-context-changed"),params:Ly.describe("Partial context update containing only changed fields.")}),VP=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Nt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),JP=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:It.describe("App identification (name and version)."),appCapabilities:UP.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),BP=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:It.describe("Host application identification and version."),hostCapabilities:LP.describe("Features and capabilities provided by the host."),hostContext:Ly.describe("Rich context about the host environment.")}).passthrough();var Ou=class{eventTarget;eventSource;messageListener;constructor(t=window.parent,o){this.eventTarget=t,this.eventSource=o,this.messageListener=r=>{if(o&&r.source!==this.eventSource){console.debug("Ignoring message from unknown source",r);return}let n=ht.safeParse(r.data);n.success?(console.debug("Parsed message",n.data),this.onmessage?.(n.data)):r.data?.jsonrpc!=="2.0"?console.debug("Ignoring non-JSON-RPC message",n.error.message,r):(console.error("Failed to parse message",n.error.message,r),this.onerror?.(Error("Invalid JSON-RPC message received: "+n.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(t,o){t.method!==xP&&console.debug("Sending message",t),this.eventTarget.postMessage(t,"*")}async close(){window.removeEventListener("message",this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion};var WP=[jy],Nu=class extends Tu{_client;_hostInfo;_hostCapabilities;_appCapabilities;_hostContext={};_appInfo;_initializedReceived=!1;_registeredEvents=new Set;_events=new xg;_methods=new Pg;setRequestHandler=(...t)=>{let[o,...r]=Ny("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=Ny("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_wrapHandler(t,o){let r=super._wrapHandler(t,o);return t==="ui/initialize"||t==="ping"?r:async(n,s)=>(this._initializedReceived||console.warn(`[ext-apps] AppBridge received '${t}' before ui/notifications/initialized. The View is calling host methods before completing the handshake; it should await app.connect() first.`),r(n,s))}eventSchemas={sizechange:NP,sandboxready:OP,initialized:ZP,requestteardown:jP,loggingmessage:nr};setEventHandler(t,o){this._ensureEventSlot(t),this._events.setHandler(t,o)}getEventHandler(t){return this._events.getHandler(t)}addEventListener(t,o){this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);let r=n=>{t==="initialized"&&(this._initializedReceived=!0),this._events.dispatch(t,n)};t==="loggingmessage"?this.setNotificationHandler("notifications/message",n=>{r(n.params)}):this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},n=>r(n)),this._registeredEvents.add(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o,r,n){super(n),this._client=t,this._hostInfo=o,this._hostCapabilities=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),this._ensureEventSlot("initialized"),this._hostContext=n?.hostContext||{},this.replaceRequestHandler("ui/initialize",{params:JP.shape.params,result:BP},s=>this._onAppsInitialize(s)),this.replaceRequestHandler("ping",(s,i)=>(this.onping?.(s.params,i),{})),this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},()=>({mode:this._hostContext.displayMode??"inline"}))}buildContext(t){return t}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){}assertNotificationCapability(t){}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler("sizechange")}set onsizechange(t){this.setEventHandler("sizechange",t)}get onsandboxready(){return this.getEventHandler("sandboxready")}set onsandboxready(t){this.setEventHandler("sandboxready",t)}get oninitialized(){return this.getEventHandler("initialized")}set oninitialized(t){this.setEventHandler("initialized",t)}_onmessage;get onmessage(){return this._onmessage}set onmessage(t){this.warnIfRequestHandlerReplaced("onmessage",this._onmessage,t),this._onmessage=t,this.replaceRequestHandler("ui/message",{params:HP.shape.params,result:IP},async(o,r)=>{if(!this._onmessage)throw Error("No onmessage handler set");return this._onmessage(o,r)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(t){this.warnIfRequestHandlerReplaced("onopenlink",this._onopenlink,t),this._onopenlink=t,this.replaceRequestHandler("ui/open-link",{params:kP.shape.params,result:$P},async(o,r)=>{if(!this._onopenlink)throw Error("No onopenlink handler set");return this._onopenlink(o,r)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(t){this.warnIfRequestHandlerReplaced("ondownloadfile",this._ondownloadfile,t),this._ondownloadfile=t,this.replaceRequestHandler("ui/download-file",{params:FP.shape.params,result:CP},async(o,r)=>{if(!this._ondownloadfile)throw Error("No ondownloadfile handler set");return this._ondownloadfile(o,r)})}get onrequestteardown(){return this.getEventHandler("requestteardown")}set onrequestteardown(t){this.setEventHandler("requestteardown",t)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(t){this.warnIfRequestHandlerReplaced("onrequestdisplaymode",this._onrequestdisplaymode,t),this._onrequestdisplaymode=t,this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},async(o,r)=>{if(!this._onrequestdisplaymode)throw Error("No onrequestdisplaymode handler set");return this._onrequestdisplaymode(o,r)})}get onloggingmessage(){return this.getEventHandler("loggingmessage")}set onloggingmessage(t){this.setEventHandler("loggingmessage",t)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(t){this.warnIfRequestHandlerReplaced("onupdatemodelcontext",this._onupdatemodelcontext,t),this._onupdatemodelcontext=t,this.replaceRequestHandler("ui/update-model-context",{params:VP.shape.params,result:Ro},async(o,r)=>{if(!this._onupdatemodelcontext)throw Error("No onupdatemodelcontext handler set");return this._onupdatemodelcontext(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",async(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_oncreatesamplingmessage;get oncreatesamplingmessage(){return this._oncreatesamplingmessage}set oncreatesamplingmessage(t){this.warnIfRequestHandlerReplaced("oncreatesamplingmessage",this._oncreatesamplingmessage,t),this._oncreatesamplingmessage=t,this.replaceRequestHandler("sampling/createMessage",async(o,r)=>{if(!this._oncreatesamplingmessage)throw Error("No oncreatesamplingmessage handler set");return this._oncreatesamplingmessage(o.params,r)})}sendToolListChanged(t={}){return this.notification({method:"notifications/tools/list_changed",params:t})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(t){this.warnIfRequestHandlerReplaced("onlistresources",this._onlistresources,t),this._onlistresources=t,this.replaceRequestHandler("resources/list",async(o,r)=>{if(!this._onlistresources)throw Error("No onlistresources handler set");return this._onlistresources(o.params,r)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(t){this.warnIfRequestHandlerReplaced("onlistresourcetemplates",this._onlistresourcetemplates,t),this._onlistresourcetemplates=t,this.replaceRequestHandler("resources/templates/list",async(o,r)=>{if(!this._onlistresourcetemplates)throw Error("No onlistresourcetemplates handler set");return this._onlistresourcetemplates(o.params,r)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(t){this.warnIfRequestHandlerReplaced("onreadresource",this._onreadresource,t),this._onreadresource=t,this.replaceRequestHandler("resources/read",async(o,r)=>{if(!this._onreadresource)throw Error("No onreadresource handler set");return this._onreadresource(o.params,r)})}sendResourceListChanged(t={}){return this.notification({method:"notifications/resources/list_changed",params:t})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(t){this.warnIfRequestHandlerReplaced("onlistprompts",this._onlistprompts,t),this._onlistprompts=t,this.replaceRequestHandler("prompts/list",async(o,r)=>{if(!this._onlistprompts)throw Error("No onlistprompts handler set");return this._onlistprompts(o.params,r)})}sendPromptListChanged(t={}){return this.notification({method:"notifications/prompts/list_changed",params:t})}getCapabilities(){return this._hostCapabilities}async _onAppsInitialize(t){let o=t.protocolVersion;return this._appInfo!==void 0&&console.warn("[ext-apps] AppBridge received a second ui/initialize. The View may be double-mounting (e.g. React StrictMode in dev) without closing the previous App instance. Responding normally; the latest appInfo/appCapabilities replace the previous values."),this._appCapabilities=t.appCapabilities,this._appInfo=t.appInfo,{protocolVersion:WP.includes(o)?o:jy,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(t){let o={},r=!1;for(let n of Object.keys(t)){let s=this._hostContext[n],i=t[n];KP(s,i)||(o[n]=i,r=!0)}r&&(this._hostContext=t,this.sendHostContextChange(o))}sendHostContextChange(t){return this.notification({method:"ui/notifications/host-context-changed",params:t})}sendToolInput(t){return this.notification({method:"ui/notifications/tool-input",params:t})}sendToolInputPartial(t){return this.notification({method:"ui/notifications/tool-input-partial",params:t})}sendToolResult(t){return this.notification({method:"ui/notifications/tool-result",params:t})}sendToolCancelled(t){return this.notification({method:"ui/notifications/tool-cancelled",params:t})}sendSandboxResourceReady(t){return this.notification({method:"ui/notifications/sandbox-resource-ready",params:t})}teardownResource(t,o){return this.request({method:"ui/resource-teardown",params:t},AP,o)}sendResourceTeardown=this.teardownResource;callTool(t,o){return this.request({method:"tools/call",params:t},o)}listTools(t,o){return this.request({method:"tools/list",params:t},o)}async connect(t){if(this.transport)throw Error("AppBridge is already connected. Call close() before connecting again.");if(this._initializedReceived=!1,this._client){let o=this._client.getServerCapabilities();if(!o)throw Error("Client server capabilities not available");o.tools&&(this.oncalltool=async(r,n)=>this._client.request({method:"tools/call",params:r},{signal:n.mcpReq.signal}),o.tools.listChanged&&this._client.setNotificationHandler("notifications/tools/list_changed",r=>this.sendToolListChanged(r.params))),o.resources&&(this.onlistresources=async(r,n)=>this._client.request({method:"resources/list",params:r},{signal:n.mcpReq.signal}),this.onlistresourcetemplates=async(r,n)=>this._client.request({method:"resources/templates/list",params:r},{signal:n.mcpReq.signal}),this.onreadresource=async(r,n)=>this._client.request({method:"resources/read",params:r},{signal:n.mcpReq.signal}),o.resources.listChanged&&this._client.setNotificationHandler("notifications/resources/list_changed",r=>this.sendResourceListChanged(r.params))),o.prompts&&(this.onlistprompts=async(r,n)=>this._client.request({method:"prompts/list",params:r},{signal:n.mcpReq.signal}),o.prompts.listChanged&&this._client.setNotificationHandler("notifications/prompts/list_changed",r=>this.sendPromptListChanged(r.params)))}return super.connect(t)}};function KP(e,t){return JSON.stringify(e)===JSON.stringify(t)}var Uy=[200,800],GP=e=>e?.name==="TypeError"&&/^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.)$/.test(e.message);function YP(e,t){return new Promise((o,r)=>{t?.throwIfAborted();let s=setTimeout(()=>{t?.removeEventListener("abort",i),o()},e),i=()=>{clearTimeout(s),t.removeEventListener("abort",i),r(t.reason)};t?.addEventListener("abort",i,{once:!0})})}function Zy(e,{signal:t,onDiagnostic:o,wait:r=YP}={}){return async n=>{for(let s=1;;s++){t?.throwIfAborted();try{let i=await e(n);return t?.throwIfAborted(),s>1&&o?.({state:"recovered",uri:n.uri,attempt:s}),i}catch(i){if(t?.throwIfAborted(),!GP(i))throw i;if(s>Uy.length)throw o?.({state:"exhausted",uri:n.uri,attempt:s,message:i.message}),Object.assign(new Error(`MCP resource transport failed for ${n.uri} after ${s} attempts: ${i.message}. Inspect the host connection and browser network diagnostics; this resource-loading error does not establish a component assertion failure.`,{cause:i}),{code:"ADS_RESOURCE_TRANSPORT"});let a=Uy[s-1];o?.({state:"retry",uri:n.uri,attempt:s,nextAttempt:s+1,delayMs:a,message:i.message}),await r(a,t)}}}}var re=e=>document.getElementById(e),Ha=new URLSearchParams(location.search),XP=Ha.get("scenario")??Ha.get("category"),QP=["tokens","components","patterns","composition"],we="tokens",oo={},kg=!0,Ng=[],qg=[],jg=[],$g,gt,ft,Zn,Dn=0,fr=Ha.get("capture")==="1"&&Ha.get("theme")==="dark"?"dark":"light";Ha.get("capture")==="1"&&(document.documentElement.dataset.capture="true");document.documentElement.dataset.theme=fr;var Cg,Ig=!1,Au,Mg,Lu=!1,Va=!1,Fn,mr,Og=0,qu,ju=new AbortController,St=new Cy({name:"ads-showcase",version:"1.0.0"},{versionNegotiation:{mode:"auto"},capabilities:{extensions:{"io.modelcontextprotocol/ui":{mimeTypes:["text/html;profile=mcp-app"]}}}}),Mu=(e,t={})=>St.callTool({name:e,arguments:t}),Ag=Zy(e=>St.readResource(e,{signal:ju.signal}),{signal:ju.signal,onDiagnostic:e=>{e.state==="retry"&&(document.documentElement.dataset.adsResourceRetries=String(Number(document.documentElement.dataset.adsResourceRetries??0)+1)),console.warn("ADS resource transport",e)}}),eT=async e=>JSON.parse((await Ag({uri:e})).contents.find(t=>typeof t.text=="string").text);function ro(e,t=!1){re("loader").hidden=!0,re("preview").removeAttribute("aria-busy"),re("status").hidden=!e,re("status").textContent=e,re("status").dataset.error=String(t),re("status").setAttribute("role",t?"alert":"status")}function Fy(){re("status").hidden=!0,re("loader").hidden=!1,re("preview").setAttribute("aria-busy","true")}function Dy(e){let t=new URL(location.href);return t.searchParams.delete("scenario"),t.searchParams.delete("category"),e&&t.searchParams.set("scenario",e),t.pathname+t.search+t.hash}function tT(){return we==="tokens"?Ng:qg.filter(e=>e.kind===we)}function Lg(){return{theme:fr,displayMode:"inline",containerDimensions:{width:re("frame-slot").clientWidth,height:re("frame-slot").clientHeight}}}function Ug(){let e=tT();e.some(o=>o.id===oo[we])||(oo[we]=e[0]?.id??null),history.replaceState(null,"",Dy(oo[we]));let t=re("sidebar").scrollTop;re("entries").setAttribute("aria-label",we[0].toUpperCase()+we.slice(1)),re("entries").replaceChildren(...e.map(o=>{let r=document.createElement("a");return r.textContent=o.title,r.href=Dy(o.id),o.id===oo[we]&&r.setAttribute("aria-current","page"),r.addEventListener("click",n=>{n.button||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||(n.preventDefault(),oo[we]=o.id,Ug(),Vn().catch(gr))}),r})),re("sidebar").scrollTop=t;for(let o of re("sections").querySelectorAll("button"))o.dataset.section===we?o.setAttribute("aria-current","page"):o.removeAttribute("aria-current")}function gr(e){!Lu&&!Va&&ro(`${e.message}${gt?`
|
|
116
|
-
The retained preview is not verified against the current source.`:""}`,!0)}async function Hn(e){if(e){try{await e.teardownResource({},{timeout:500})}catch{}await e.close()}}function Hy(){if(Lu||Va)return;Va=!0,ju.abort(),++Dn,clearTimeout(Au),ro("MCP disconnected. The retained preview is stale. Refresh the page after restarting ADS.",!0);let e=ft;ft=void 0,Hn(e)}async function Vy(){$g=(await Mu("ads_status")).structuredContent,re("title").textContent=$g.project.showcase.title,re("title").title=$g.projectRoot;let e=[],t;do{let i=await St.listTools(t?{cursor:t}:{});e.push(...i.tools),t=i.nextCursor}while(t);jg=e,Ng=(await Mu("ads_tokens",{limit:1})).structuredContent.categories;let o=[],r=0;do{let i=(await Mu("ads_scenarios",{limit:50,offset:r})).structuredContent;o.push(...i.scenarios),r=i.nextOffset}while(r!==void 0);qg=o;let n=kg?XP:oo[we],s=qg.find(i=>i.id===n);s&&QP.includes(s.kind)?(we=s.kind,oo[we]=s.id):kg&&Ng.some(i=>i.id===n)&&(oo.tokens=n),kg=!1,Ug()}function Vn({force:e=!1}={}){if(Va)return Promise.resolve();let t=oo[we],o=`${we}:${t}`;if(Zn?.key===o)return Zn.promise;if(!e&&!Zn&>?.key===o)return Promise.resolve();let r={key:o};return Zn=r,r.promise=oT(t,o,we==="tokens").finally(()=>{Zn===r&&(Zn=void 0)}),r.promise}async function oT(e,t,o){let r=++Dn;try{if(!e&&!o){if(await Hn(ft),r!==Dn)return;ft=void 0,gt=void 0,Fn=void 0,mr=void 0,re("frame-slot").replaceChildren(),ro("No scenarios discovered. Colocate an independently mountable scenario and fixture with the public implementation.");return}let n=jg.find(l=>l.name===(o?"ads_show_tokens":"ads_show_scenario"));if(!n?._meta?.ui?.resourceUri)throw new Error("The server did not expose a preview App tool.");let s=o?e?{category:e}:{}:{id:e};Fy();let i=await Mu(n.name,s);if(r!==Dn)return;if(i.isError)throw new Error(i.content?.find(l=>l.type==="text")?.text??"Preview build failed.");gt={key:t,id:e,token:o&&!i.structuredContent?.scenario,build:i.structuredContent?.build,manifestRevision:i.structuredContent?.manifestRevision};let a=await rT(n._meta.ui.resourceUri);if(r!==Dn)return;if(Fn==="ui://ads/preview"&&!gt.manifestRevision)throw new Error("The resource preview has no manifest revision.");await a.sendToolInput({arguments:s}),await a.sendToolResult(i),i.structuredContent?.manifestUri||ro("")}catch(n){r===Dn&&gr(n)}}async function rT(e){if(Fn===e&&mr)return mr;let t=++Og,o=ft;ft=void 0,Fn=e,mr=(async()=>{await Hn(o);let r=await Ag({uri:e});if(t!==Og||Lu||Va)throw new Error("Preview opening was superseded.");let n=r.contents.find(u=>u.mimeType==="text/html;profile=mcp-app"&&typeof u.text=="string");if(!n)throw new Error("The preview resource contains no MCP App.");let s=document.createElement("iframe");s.title="Design-system preview",s.setAttribute("sandbox","allow-scripts allow-same-origin"),s.src=qu,re("frame-slot").replaceChildren(s);let i=new Nu(null,{name:"ADS showcase",version:"1.0.0"},{serverResources:{},serverTools:{},logging:{}},{hostContext:Lg()});ft=i,i.onreadresource=u=>Ag(u),i.oncalltool=u=>{let d=jg.find(h=>h.name===u.name);if(!d||!(d._meta?.ui?.visibility??["model","app"]).includes("app"))throw new Error("This tool is not callable from an App.");return St.callTool(u)},i.onloggingmessage=u=>{u.logger!=="ads-preview"||u.data?.id!==gt?.id||(u.data.state==="ready"&&u.data.revision===gt.manifestRevision?ro(""):u.data.code==="ADS_PREVIEW_REOPEN"?(ft=void 0,Fn=void 0,mr=void 0,Fy(),Hn(i).then(()=>Vn({force:!0})).catch(gr)):u.data.state==="error"&&ro(u.data.message,!0))},i.onerror=gr;let a,l=new Promise((u,d)=>{a=setTimeout(()=>d(new Error("MCP App initialization timed out.")),15e3),i.oninitialized=()=>{clearTimeout(a),u()}});i.onsandboxready=()=>i.sendSandboxResourceReady({html:n.text,csp:n._meta?.ui?.csp,sandbox:"allow-scripts"});try{await Promise.all([i.connect(new Ou(s.contentWindow,s.contentWindow)),l])}catch(u){throw await Hn(i),u}finally{clearTimeout(a)}return i})();try{return await mr}catch(r){throw t===Og&&(Fn=void 0,mr=void 0),r}}async function nT(){let e=gt;if(!e||e.token)return;let t=await eT("ads://project/build");if(gt!==e)return;let o=t.previews.find(r=>r.id===e.id);if(!o){ro("The selected scenario is no longer registered.",!0);return}if(!(["idle","building"].includes(o.state)&&e.build?.current&&o.session===e.build.session&&o.revision<=e.build.revision)){if(o.state==="error"){ro(`Preview build failed. Showing an earlier view.
|
|
115
|
+
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),Ok=p.object({method:p.literal("ui/notifications/host-context-changed"),params:Ly.describe("Partial context update containing only changed fields.")}),VP=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Nt).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),JP=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:It.describe("App identification (name and version)."),appCapabilities:UP.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),BP=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:It.describe("Host application identification and version."),hostCapabilities:LP.describe("Features and capabilities provided by the host."),hostContext:Ly.describe("Rich context about the host environment.")}).passthrough();var Ou=class{eventTarget;eventSource;messageListener;constructor(t=window.parent,o){this.eventTarget=t,this.eventSource=o,this.messageListener=r=>{if(o&&r.source!==this.eventSource){console.debug("Ignoring message from unknown source",r);return}let n=ht.safeParse(r.data);n.success?(console.debug("Parsed message",n.data),this.onmessage?.(n.data)):r.data?.jsonrpc!=="2.0"?console.debug("Ignoring non-JSON-RPC message",n.error.message,r):(console.error("Failed to parse message",n.error.message,r),this.onerror?.(Error("Invalid JSON-RPC message received: "+n.error.message)))}}async start(){window.addEventListener("message",this.messageListener)}async send(t,o){t.method!==xP&&console.debug("Sending message",t),this.eventTarget.postMessage(t,"*")}async close(){window.removeEventListener("message",this.messageListener),this.onclose?.()}onclose;onerror;onmessage;sessionId;setProtocolVersion};var WP=[jy],Nu=class extends Tu{_client;_hostInfo;_hostCapabilities;_appCapabilities;_hostContext={};_appInfo;_initializedReceived=!1;_registeredEvents=new Set;_events=new xg;_methods=new Pg;setRequestHandler=(...t)=>{let[o,...r]=Ny("request",t)??t;this._methods.claim(o,"setRequestHandler"),super.setRequestHandler.call(this,o,...r)};setNotificationHandler=(...t)=>{let[o,...r]=Ny("notification",t)??t;this._methods.claim(o,"setNotificationHandler"),super.setNotificationHandler.call(this,o,...r)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...o)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...o)};_wrapHandler(t,o){let r=super._wrapHandler(t,o);return t==="ui/initialize"||t==="ping"?r:async(n,s)=>(this._initializedReceived||console.warn(`[ext-apps] AppBridge received '${t}' before ui/notifications/initialized. The View is calling host methods before completing the handshake; it should await app.connect() first.`),r(n,s))}eventSchemas={sizechange:NP,sandboxready:OP,initialized:ZP,requestteardown:jP,loggingmessage:nr};setEventHandler(t,o){this._ensureEventSlot(t),this._events.setHandler(t,o)}getEventHandler(t){return this._events.getHandler(t)}addEventListener(t,o){this._ensureEventSlot(t),this._events.addEventListener(t,o)}removeEventListener(t,o){this._events.removeEventListener(t,o)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let o=this.eventSchemas[t];if(!o)throw Error(`Unknown event: ${String(t)}`);let r=n=>{t==="initialized"&&(this._initializedReceived=!0),this._events.dispatch(t,n)};t==="loggingmessage"?this.setNotificationHandler("notifications/message",n=>{r(n.params)}):this.setNotificationHandler(o.shape.method.value,{params:o.shape.params},n=>r(n)),this._registeredEvents.add(t)}warnIfRequestHandlerReplaced(t,o,r){o&&r&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,o,r,n){super(n),this._client=t,this._hostInfo=o,this._hostCapabilities=r,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),this._ensureEventSlot("initialized"),this._hostContext=n?.hostContext||{},this.replaceRequestHandler("ui/initialize",{params:JP.shape.params,result:BP},s=>this._onAppsInitialize(s)),this.replaceRequestHandler("ping",(s,i)=>(this.onping?.(s.params,i),{})),this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},()=>({mode:this._hostContext.displayMode??"inline"}))}buildContext(t){return t}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){}assertNotificationCapability(t){}getAppCapabilities(){return this._appCapabilities}getAppVersion(){return this._appInfo}onping;get onsizechange(){return this.getEventHandler("sizechange")}set onsizechange(t){this.setEventHandler("sizechange",t)}get onsandboxready(){return this.getEventHandler("sandboxready")}set onsandboxready(t){this.setEventHandler("sandboxready",t)}get oninitialized(){return this.getEventHandler("initialized")}set oninitialized(t){this.setEventHandler("initialized",t)}_onmessage;get onmessage(){return this._onmessage}set onmessage(t){this.warnIfRequestHandlerReplaced("onmessage",this._onmessage,t),this._onmessage=t,this.replaceRequestHandler("ui/message",{params:HP.shape.params,result:IP},async(o,r)=>{if(!this._onmessage)throw Error("No onmessage handler set");return this._onmessage(o,r)})}_onopenlink;get onopenlink(){return this._onopenlink}set onopenlink(t){this.warnIfRequestHandlerReplaced("onopenlink",this._onopenlink,t),this._onopenlink=t,this.replaceRequestHandler("ui/open-link",{params:kP.shape.params,result:$P},async(o,r)=>{if(!this._onopenlink)throw Error("No onopenlink handler set");return this._onopenlink(o,r)})}_ondownloadfile;get ondownloadfile(){return this._ondownloadfile}set ondownloadfile(t){this.warnIfRequestHandlerReplaced("ondownloadfile",this._ondownloadfile,t),this._ondownloadfile=t,this.replaceRequestHandler("ui/download-file",{params:FP.shape.params,result:CP},async(o,r)=>{if(!this._ondownloadfile)throw Error("No ondownloadfile handler set");return this._ondownloadfile(o,r)})}get onrequestteardown(){return this.getEventHandler("requestteardown")}set onrequestteardown(t){this.setEventHandler("requestteardown",t)}_onrequestdisplaymode;get onrequestdisplaymode(){return this._onrequestdisplaymode}set onrequestdisplaymode(t){this.warnIfRequestHandlerReplaced("onrequestdisplaymode",this._onrequestdisplaymode,t),this._onrequestdisplaymode=t,this.replaceRequestHandler("ui/request-display-mode",{params:My.shape.params,result:Ay},async(o,r)=>{if(!this._onrequestdisplaymode)throw Error("No onrequestdisplaymode handler set");return this._onrequestdisplaymode(o,r)})}get onloggingmessage(){return this.getEventHandler("loggingmessage")}set onloggingmessage(t){this.setEventHandler("loggingmessage",t)}_onupdatemodelcontext;get onupdatemodelcontext(){return this._onupdatemodelcontext}set onupdatemodelcontext(t){this.warnIfRequestHandlerReplaced("onupdatemodelcontext",this._onupdatemodelcontext,t),this._onupdatemodelcontext=t,this.replaceRequestHandler("ui/update-model-context",{params:VP.shape.params,result:Ro},async(o,r)=>{if(!this._onupdatemodelcontext)throw Error("No onupdatemodelcontext handler set");return this._onupdatemodelcontext(o,r)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",async(o,r)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(o.params,r)})}_oncreatesamplingmessage;get oncreatesamplingmessage(){return this._oncreatesamplingmessage}set oncreatesamplingmessage(t){this.warnIfRequestHandlerReplaced("oncreatesamplingmessage",this._oncreatesamplingmessage,t),this._oncreatesamplingmessage=t,this.replaceRequestHandler("sampling/createMessage",async(o,r)=>{if(!this._oncreatesamplingmessage)throw Error("No oncreatesamplingmessage handler set");return this._oncreatesamplingmessage(o.params,r)})}sendToolListChanged(t={}){return this.notification({method:"notifications/tools/list_changed",params:t})}_onlistresources;get onlistresources(){return this._onlistresources}set onlistresources(t){this.warnIfRequestHandlerReplaced("onlistresources",this._onlistresources,t),this._onlistresources=t,this.replaceRequestHandler("resources/list",async(o,r)=>{if(!this._onlistresources)throw Error("No onlistresources handler set");return this._onlistresources(o.params,r)})}_onlistresourcetemplates;get onlistresourcetemplates(){return this._onlistresourcetemplates}set onlistresourcetemplates(t){this.warnIfRequestHandlerReplaced("onlistresourcetemplates",this._onlistresourcetemplates,t),this._onlistresourcetemplates=t,this.replaceRequestHandler("resources/templates/list",async(o,r)=>{if(!this._onlistresourcetemplates)throw Error("No onlistresourcetemplates handler set");return this._onlistresourcetemplates(o.params,r)})}_onreadresource;get onreadresource(){return this._onreadresource}set onreadresource(t){this.warnIfRequestHandlerReplaced("onreadresource",this._onreadresource,t),this._onreadresource=t,this.replaceRequestHandler("resources/read",async(o,r)=>{if(!this._onreadresource)throw Error("No onreadresource handler set");return this._onreadresource(o.params,r)})}sendResourceListChanged(t={}){return this.notification({method:"notifications/resources/list_changed",params:t})}_onlistprompts;get onlistprompts(){return this._onlistprompts}set onlistprompts(t){this.warnIfRequestHandlerReplaced("onlistprompts",this._onlistprompts,t),this._onlistprompts=t,this.replaceRequestHandler("prompts/list",async(o,r)=>{if(!this._onlistprompts)throw Error("No onlistprompts handler set");return this._onlistprompts(o.params,r)})}sendPromptListChanged(t={}){return this.notification({method:"notifications/prompts/list_changed",params:t})}getCapabilities(){return this._hostCapabilities}async _onAppsInitialize(t){let o=t.protocolVersion;return this._appInfo!==void 0&&console.warn("[ext-apps] AppBridge received a second ui/initialize. The View may be double-mounting (e.g. React StrictMode in dev) without closing the previous App instance. Responding normally; the latest appInfo/appCapabilities replace the previous values."),this._appCapabilities=t.appCapabilities,this._appInfo=t.appInfo,{protocolVersion:WP.includes(o)?o:jy,hostCapabilities:this.getCapabilities(),hostInfo:this._hostInfo,hostContext:this._hostContext}}setHostContext(t){let o={},r=!1;for(let n of Object.keys(t)){let s=this._hostContext[n],i=t[n];KP(s,i)||(o[n]=i,r=!0)}r&&(this._hostContext=t,this.sendHostContextChange(o))}sendHostContextChange(t){return this.notification({method:"ui/notifications/host-context-changed",params:t})}sendToolInput(t){return this.notification({method:"ui/notifications/tool-input",params:t})}sendToolInputPartial(t){return this.notification({method:"ui/notifications/tool-input-partial",params:t})}sendToolResult(t){return this.notification({method:"ui/notifications/tool-result",params:t})}sendToolCancelled(t){return this.notification({method:"ui/notifications/tool-cancelled",params:t})}sendSandboxResourceReady(t){return this.notification({method:"ui/notifications/sandbox-resource-ready",params:t})}teardownResource(t,o){return this.request({method:"ui/resource-teardown",params:t},AP,o)}sendResourceTeardown=this.teardownResource;callTool(t,o){return this.request({method:"tools/call",params:t},o)}listTools(t,o){return this.request({method:"tools/list",params:t},o)}async connect(t){if(this.transport)throw Error("AppBridge is already connected. Call close() before connecting again.");if(this._initializedReceived=!1,this._client){let o=this._client.getServerCapabilities();if(!o)throw Error("Client server capabilities not available");o.tools&&(this.oncalltool=async(r,n)=>this._client.request({method:"tools/call",params:r},{signal:n.mcpReq.signal}),o.tools.listChanged&&this._client.setNotificationHandler("notifications/tools/list_changed",r=>this.sendToolListChanged(r.params))),o.resources&&(this.onlistresources=async(r,n)=>this._client.request({method:"resources/list",params:r},{signal:n.mcpReq.signal}),this.onlistresourcetemplates=async(r,n)=>this._client.request({method:"resources/templates/list",params:r},{signal:n.mcpReq.signal}),this.onreadresource=async(r,n)=>this._client.request({method:"resources/read",params:r},{signal:n.mcpReq.signal}),o.resources.listChanged&&this._client.setNotificationHandler("notifications/resources/list_changed",r=>this.sendResourceListChanged(r.params))),o.prompts&&(this.onlistprompts=async(r,n)=>this._client.request({method:"prompts/list",params:r},{signal:n.mcpReq.signal}),o.prompts.listChanged&&this._client.setNotificationHandler("notifications/prompts/list_changed",r=>this.sendPromptListChanged(r.params)))}return super.connect(t)}};function KP(e,t){return JSON.stringify(e)===JSON.stringify(t)}var Uy=[200,800],GP=e=>e?.name==="TypeError"&&/^(Failed to fetch|fetch failed|Load failed|NetworkError when attempting to fetch resource\.)$/.test(e.message);function YP(e,t){return new Promise((o,r)=>{t?.throwIfAborted();let s=setTimeout(()=>{t?.removeEventListener("abort",i),o()},e),i=()=>{clearTimeout(s),t.removeEventListener("abort",i),r(t.reason)};t?.addEventListener("abort",i,{once:!0})})}function Zy(e,{signal:t,onDiagnostic:o,wait:r=YP}={}){return async n=>{for(let s=1;;s++){t?.throwIfAborted();try{let i=await e(n);return t?.throwIfAborted(),s>1&&o?.({state:"recovered",uri:n.uri,attempt:s}),i}catch(i){if(t?.throwIfAborted(),!GP(i))throw i;if(s>Uy.length)throw o?.({state:"exhausted",uri:n.uri,attempt:s,message:i.message}),Object.assign(new Error(`MCP resource transport failed for ${n.uri} after ${s} attempts: ${i.message}. Inspect the host connection and browser network diagnostics; this resource-loading error does not establish a component assertion failure.`,{cause:i}),{code:"ADS_RESOURCE_TRANSPORT"});let a=Uy[s-1];o?.({state:"retry",uri:n.uri,attempt:s,nextAttempt:s+1,delayMs:a,message:i.message}),await r(a,t)}}}}var re=e=>document.getElementById(e),Ha=new URLSearchParams(location.search),XP=Ha.get("scenario")??Ha.get("category"),QP=["tokens","components","patterns","composition"],we="tokens",oo={},kg=!0,Ng=[],qg=[],jg=[],$g,gt,ft,Zn,Dn=0,fr=Ha.get("capture")==="1"&&Ha.get("theme")==="dark"?"dark":"light";Ha.get("capture")==="1"&&(document.documentElement.dataset.capture="true");document.documentElement.dataset.theme=fr;var Cg,Ig=!1,Au,Mg,Lu=!1,Va=!1,Fn,mr,Og=0,qu,ju=new AbortController,St=new Cy({name:"ads-showcase",version:"1.0.0"},{versionNegotiation:{mode:"auto"},capabilities:{extensions:{"io.modelcontextprotocol/ui":{mimeTypes:["text/html;profile=mcp-app"]}}}}),Mu=(e,t={})=>St.callTool({name:e,arguments:t}),Ag=Zy(e=>St.readResource(e,{signal:ju.signal}),{signal:ju.signal,onDiagnostic:e=>{e.state==="retry"&&(document.documentElement.dataset.adsResourceRetries=String(Number(document.documentElement.dataset.adsResourceRetries??0)+1)),console.warn("ADS resource transport",e)}}),eT=async e=>JSON.parse((await Ag({uri:e})).contents.find(t=>typeof t.text=="string").text);function ro(e,t=!1){re("loader").hidden=!0,re("preview").removeAttribute("aria-busy"),re("status").hidden=!e,re("status").textContent=e,re("status").dataset.error=String(t),re("status").setAttribute("role",t?"alert":"status")}function Fy(){re("status").hidden=!0,re("loader").hidden=!1,re("preview").setAttribute("aria-busy","true")}function Dy(e){let t=new URL(location.href);return t.searchParams.delete("scenario"),t.searchParams.delete("category"),e&&t.searchParams.set("scenario",e),t.pathname+t.search+t.hash}function tT(){return we==="tokens"?Ng:qg.filter(e=>e.kind===we)}function Lg(){return{theme:fr,displayMode:"inline",containerDimensions:{width:re("frame-slot").clientWidth,height:re("frame-slot").clientHeight}}}function Ug(){let e=tT();e.some(o=>o.id===oo[we])||(oo[we]=e[0]?.id??null),history.replaceState(null,"",Dy(oo[we]));let t=re("sidebar").scrollTop;re("entries").setAttribute("aria-label",we[0].toUpperCase()+we.slice(1)),re("entries").replaceChildren(...e.map(o=>{let r=document.createElement("a");return r.textContent=o.title,r.href=Dy(o.id),o.id===oo[we]&&r.setAttribute("aria-current","page"),r.addEventListener("click",n=>{n.button||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||(n.preventDefault(),oo[we]=o.id,Ug(),Vn().catch(gr))}),r})),re("sidebar").scrollTop=t;for(let o of re("sections").querySelectorAll("button"))o.dataset.section===we?o.setAttribute("aria-current","page"):o.removeAttribute("aria-current")}function gr(e){!Lu&&!Va&&(re("status").dataset.errorCategory=e.category??(e.code==="ADS_RESOURCE_TRANSPORT"?"transport":"runner"),re("status").dataset.errorCode=e.code??"ADS_HOST_STARTUP",ro(`${e.message}${gt?`
|
|
116
|
+
The retained preview is not verified against the current source.`:""}`,!0))}async function Hn(e){if(e){try{await e.teardownResource({},{timeout:500})}catch{}await e.close()}}function Hy(){if(Lu||Va)return;Va=!0,ju.abort(),++Dn,clearTimeout(Au),ro("MCP disconnected. The retained preview is stale. Refresh the page after restarting ADS.",!0);let e=ft;ft=void 0,Hn(e)}async function Vy(){$g=(await Mu("ads_status")).structuredContent,re("title").textContent=$g.project.showcase.title,re("title").title=$g.projectRoot;let e=[],t;do{let i=await St.listTools(t?{cursor:t}:{});e.push(...i.tools),t=i.nextCursor}while(t);jg=e,Ng=(await Mu("ads_tokens",{limit:1})).structuredContent.categories;let o=[],r=0;do{let i=(await Mu("ads_scenarios",{limit:50,offset:r})).structuredContent;o.push(...i.scenarios),r=i.nextOffset}while(r!==void 0);qg=o;let n=kg?XP:oo[we],s=qg.find(i=>i.id===n);s&&QP.includes(s.kind)?(we=s.kind,oo[we]=s.id):kg&&Ng.some(i=>i.id===n)&&(oo.tokens=n),kg=!1,Ug()}function Vn({force:e=!1}={}){if(Va)return Promise.resolve();let t=oo[we],o=`${we}:${t}`;if(Zn?.key===o)return Zn.promise;if(!e&&!Zn&>?.key===o)return Promise.resolve();let r={key:o};return Zn=r,r.promise=oT(t,o,we==="tokens").finally(()=>{Zn===r&&(Zn=void 0)}),r.promise}async function oT(e,t,o){let r=++Dn;try{if(!e&&!o){if(await Hn(ft),r!==Dn)return;ft=void 0,gt=void 0,Fn=void 0,mr=void 0,re("frame-slot").replaceChildren(),ro("No scenarios discovered. Colocate an independently mountable scenario and fixture with the public implementation.");return}let n=jg.find(l=>l.name===(o?"ads_show_tokens":"ads_show_scenario"));if(!n?._meta?.ui?.resourceUri)throw new Error("The server did not expose a preview App tool.");let s=o?e?{category:e}:{}:{id:e};Fy();let i=await Mu(n.name,s);if(r!==Dn)return;if(i.isError)throw Object.assign(new Error(i.content?.find(l=>l.type==="text")?.text??"Preview build failed."),i.structuredContent?.failure);gt={key:t,id:e,token:o&&!i.structuredContent?.scenario,build:i.structuredContent?.build,manifestRevision:i.structuredContent?.manifestRevision};let a=await rT(n._meta.ui.resourceUri);if(r!==Dn)return;if(Fn==="ui://ads/preview"&&!gt.manifestRevision)throw new Error("The resource preview has no manifest revision.");await a.sendToolInput({arguments:s}),await a.sendToolResult(i),i.structuredContent?.manifestUri||ro("")}catch(n){r===Dn&&gr(n)}}async function rT(e){if(Fn===e&&mr)return mr;let t=++Og,o=ft;ft=void 0,Fn=e,mr=(async()=>{await Hn(o);let r=await Ag({uri:e});if(t!==Og||Lu||Va)throw new Error("Preview opening was superseded.");let n=r.contents.find(u=>u.mimeType==="text/html;profile=mcp-app"&&typeof u.text=="string");if(!n)throw new Error("The preview resource contains no MCP App.");let s=document.createElement("iframe");s.title="Design-system preview",s.setAttribute("sandbox","allow-scripts allow-same-origin"),s.src=qu,re("frame-slot").replaceChildren(s);let i=new Nu(null,{name:"ADS showcase",version:"1.0.0"},{serverResources:{},serverTools:{},logging:{}},{hostContext:Lg()});ft=i,i.onreadresource=u=>Ag(u),i.oncalltool=u=>{let d=jg.find(h=>h.name===u.name);if(!d||!(d._meta?.ui?.visibility??["model","app"]).includes("app"))throw new Error("This tool is not callable from an App.");return St.callTool(u)},i.onloggingmessage=u=>{u.logger!=="ads-preview"||u.data?.id!==gt?.id||(u.data.state==="ready"&&u.data.revision===gt.manifestRevision?ro(""):u.data.code==="ADS_PREVIEW_REOPEN"?(ft=void 0,Fn=void 0,mr=void 0,Fy(),Hn(i).then(()=>Vn({force:!0})).catch(gr)):u.data.state==="error"&&ro(u.data.message,!0))},i.onerror=gr;let a,l=new Promise((u,d)=>{a=setTimeout(()=>d(new Error("MCP App initialization timed out.")),15e3),i.oninitialized=()=>{clearTimeout(a),u()}});i.onsandboxready=()=>i.sendSandboxResourceReady({html:n.text,csp:n._meta?.ui?.csp,sandbox:"allow-scripts"});try{await Promise.all([i.connect(new Ou(s.contentWindow,s.contentWindow)),l])}catch(u){throw await Hn(i),u}finally{clearTimeout(a)}return i})();try{return await mr}catch(r){throw t===Og&&(Fn=void 0,mr=void 0),r}}async function nT(){let e=gt;if(!e||e.token)return;let t=await eT("ads://project/build");if(gt!==e)return;let o=t.previews.find(r=>r.id===e.id);if(!o){ro("The selected scenario is no longer registered.",!0);return}if(!(["idle","building"].includes(o.state)&&e.build?.current&&o.session===e.build.session&&o.revision<=e.build.revision)){if(o.state==="error"){ro(`Preview build failed. Showing an earlier view.
|
|
117
117
|
${o.diagnostics.map(r=>`${r.file??""}${r.line?`:${r.line}`:""}: ${r.message}`).join(`
|
|
118
118
|
`)}`,!0);return}if(["idle","building"].includes(o.state)){ro("Rebuilding\u2026 The current view is the previous preview.");return}o.current&&(o.revision!==e.build?.revision||o.session!==e.build?.session)&&await Vn({force:!0})}}function Uu(e=!1,t=!1){Ig||=e,t&>?.token&&(gt.key="invalidated"),clearTimeout(Au),Au=setTimeout(()=>{if(Cg){Uu(e,t);return}Cg=(async()=>{Ig&&(Ig=!1,await Vy()),await Vn(),await nT()})().catch(gr).finally(()=>{Cg=void 0})},150)}for(let e of re("sections").querySelectorAll("button"))e.addEventListener("click",()=>{we=e.dataset.section,Ug(),Vn().catch(gr)});re("pane-toggle").addEventListener("click",()=>{let e=document.body.classList.toggle("collapsed");re("pane-toggle").setAttribute("aria-expanded",String(!e)),re("pane-toggle").setAttribute("aria-label",e?"Show navigation pane":"Hide navigation pane"),re("pane-toggle").title=e?"Show navigation pane":"Hide navigation pane",re("pane-toggle").querySelector("path").setAttribute("d",e?"M6 4l4 4-4 4":"M10 4l-4 4 4 4")});re("theme").addEventListener("click",()=>{fr=fr==="light"?"dark":"light",document.documentElement.dataset.theme=fr,re("theme").setAttribute("aria-label",fr==="dark"?"Use light theme":"Use dark theme"),re("theme").title=re("theme").getAttribute("aria-label"),re("theme").querySelector("svg").innerHTML=fr==="dark"?'<circle cx="8" cy="8" r="3"/><path d="M8 1v1M8 14v1M1 8h1M14 8h1M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/>':'<path d="M13.5 9a5.5 5.5 0 0 1-6.5-6.5A5.5 5.5 0 1 0 13.5 9Z"/>',ft?.setHostContext(Lg())});re("minimize").addEventListener("click",()=>{re("header").hidden=!0,document.body.classList.add("collapsed")});var Jy=new ResizeObserver(()=>ft?.setHostContext(Lg()));Jy.observe(re("frame-slot"));window.addEventListener("pagehide",()=>{Lu=!0,ju.abort(),clearTimeout(Au),Jy.disconnect(),Hn(ft),Mg?.close(),St.close()},{once:!0});St.onclose=Hy;St.setNotificationHandler("notifications/resources/list_changed",()=>Uu(!0));St.setNotificationHandler("notifications/tools/list_changed",()=>Uu(!0));St.setNotificationHandler("notifications/resources/updated",({params:e})=>Uu(!1,e.uri==="ads://project/tokens"));try{if(qu=(await fetch("/host.json").then(e=>e.json())).sandboxUrl,!qu||new URL(qu).origin===location.origin)throw new Error("The separate-origin MCP App sandbox is unavailable.");await St.connect(new Iy(new URL("/mcp",location.href))),Mg=await St.listen({resourcesListChanged:!0,toolsListChanged:!0,resourceSubscriptions:["ads://project/build","ads://project/tokens","ads://project/scenarios","ads://project/status"]}),Mg.closed.then(e=>{e!=="local"&&Hy()}),await Vy(),await Vn()}catch(e){gr(e)}
|
|
119
119
|
/*! Bundled license information:
|
|
@@ -52,6 +52,9 @@ For owner drafts, token-sample reconciliation, coupled transactions and local
|
|
|
52
52
|
constant namespaces, read [guarded-editing.md](references/guarded-editing.md).
|
|
53
53
|
For on-demand current/candidate screenshots and visual dry-run review, read
|
|
54
54
|
[captures.md](references/captures.md).
|
|
55
|
+
For a rejected patch, failed check or loading error, read
|
|
56
|
+
[evidence and diagnosis](references/evidence.md); inspect the failing boundary
|
|
57
|
+
and `ads_investigate` bundle before changing APIs or verification policy.
|
|
55
58
|
|
|
56
59
|
|
|
57
60
|
For parent/child `covers`, conditional-control review, reference comparisons,
|