@xnetjs/plugins 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4876,6 +4876,86 @@ function hasWriteScope(plan) {
4876
4876
  );
4877
4877
  }
4878
4878
 
4879
+ // src/ai-surface/args.ts
4880
+ function readRequiredString(record, key) {
4881
+ const value = record[key];
4882
+ if (typeof value !== "string" || !value.trim()) {
4883
+ throw new Error(`${key} must be a non-empty string`);
4884
+ }
4885
+ return value;
4886
+ }
4887
+ function readRequiredRecord(record, key) {
4888
+ const value = record[key];
4889
+ if (!isRecord3(value)) {
4890
+ throw new Error(`${key} must be an object`);
4891
+ }
4892
+ return value;
4893
+ }
4894
+ function readRequiredStringArray(value, key) {
4895
+ const result = readStringArray(value);
4896
+ if (result.length === 0) {
4897
+ throw new Error(`${key} must contain at least one string`);
4898
+ }
4899
+ return result;
4900
+ }
4901
+ function readStringArray(value) {
4902
+ if (!Array.isArray(value)) return [];
4903
+ return value.filter((item) => typeof item === "string" && item.trim() !== "");
4904
+ }
4905
+ function readCsvStringArray(value) {
4906
+ if (!value) return [];
4907
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
4908
+ }
4909
+ function readOptionalString(record, key) {
4910
+ const value = record[key];
4911
+ return typeof value === "string" && value.trim() ? value : void 0;
4912
+ }
4913
+ function readOptionalRecord(record, key) {
4914
+ return readRecord(record, key);
4915
+ }
4916
+ function readOptionalNumber(record, key) {
4917
+ const value = record[key];
4918
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4919
+ }
4920
+ function readOptionalBoolean(record, key) {
4921
+ const value = record[key];
4922
+ return typeof value === "boolean" ? value : void 0;
4923
+ }
4924
+ function readUrlNumber(params, key) {
4925
+ const value = params.get(key);
4926
+ if (value === null) return void 0;
4927
+ const parsed = Number(value);
4928
+ return Number.isFinite(parsed) ? parsed : void 0;
4929
+ }
4930
+ function readRecordString(record, key) {
4931
+ const value = record[key];
4932
+ return typeof value === "string" && value.trim() ? value : void 0;
4933
+ }
4934
+ function readRecord(record, key) {
4935
+ const value = record[key];
4936
+ return isRecord3(value) ? value : void 0;
4937
+ }
4938
+ function readRecordNumber(record, key) {
4939
+ const value = record[key];
4940
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4941
+ }
4942
+ function readRecordBoolean(record, key) {
4943
+ const value = record[key];
4944
+ return typeof value === "boolean" ? value : void 0;
4945
+ }
4946
+ function isRecord3(value) {
4947
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4948
+ }
4949
+ function readContextSeeds(value) {
4950
+ if (!Array.isArray(value)) return [];
4951
+ return value.map((seed) => {
4952
+ if (!isRecord3(seed)) return null;
4953
+ const kind = typeof seed.kind === "string" ? seed.kind : null;
4954
+ const id = typeof seed.id === "string" ? seed.id : null;
4955
+ return kind && id ? { kind, id } : null;
4956
+ }).filter((seed) => seed !== null);
4957
+ }
4958
+
4879
4959
  // src/ai-surface/page-markdown.ts
4880
4960
  var XNET_MARKDOWN_DIRECTIVE_SPECS = [
4881
4961
  {
@@ -5111,7 +5191,7 @@ function findJsonPayloadEnd(src, startIndex) {
5111
5191
  function parseJsonObject(rawPayload, label, errors) {
5112
5192
  try {
5113
5193
  const parsed = JSON.parse(rawPayload);
5114
- if (isRecord3(parsed)) return parsed;
5194
+ if (isRecord4(parsed)) return parsed;
5115
5195
  errors.push(`${label} payload must be a JSON object`);
5116
5196
  return null;
5117
5197
  } catch {
@@ -5145,7 +5225,7 @@ function unquoteYamlScalar(value) {
5145
5225
  return trimmed.slice(1, -1);
5146
5226
  }
5147
5227
  }
5148
- function isRecord3(value) {
5228
+ function isRecord4(value) {
5149
5229
  return typeof value === "object" && value !== null && !Array.isArray(value);
5150
5230
  }
5151
5231
  function markdownDiffPrefix(kind) {
@@ -5154,900 +5234,1094 @@ function markdownDiffPrefix(kind) {
5154
5234
  return " ";
5155
5235
  }
5156
5236
 
5157
- // src/ai-surface/service.ts
5158
- var DEFAULT_LIMITS = {
5159
- maxListLimit: 100,
5160
- maxSearchScan: 500,
5161
- maxSearchResults: 20,
5162
- maxContextResources: 12,
5163
- maxCharactersPerResource: 12e3,
5164
- maxJsonCharacters: 24e3,
5165
- maxCanvasObjects: 200,
5166
- maxDatabaseRows: 100
5167
- };
5168
- var AiSurfaceService = class {
5169
- constructor(config) {
5170
- this.config = config;
5171
- this.limits = { ...DEFAULT_LIMITS, ...config.limits };
5172
- this.clock = config.clock ?? (() => /* @__PURE__ */ new Date());
5173
- for (const tool of config.extraTools ?? []) {
5174
- if (!this.extraTools.has(tool.name)) this.extraTools.set(tool.name, tool);
5237
+ // src/ai-surface/resources/router.ts
5238
+ var XNET_URI_PREFIX = "xnet://";
5239
+ var PARAM_SEGMENT_PATTERN = /^\{([a-zA-Z][a-zA-Z0-9]*)\}(.*)$/;
5240
+ function createAiResourceRouter() {
5241
+ const routes = [];
5242
+ const router = {
5243
+ register(template, handler) {
5244
+ routes.push(compileRoute(template, handler));
5245
+ return router;
5246
+ },
5247
+ async resolve(host, uri) {
5248
+ const parsed = parseXNetUri(uri);
5249
+ for (const route of routes) {
5250
+ const params = matchRoute(route, parsed);
5251
+ if (params) {
5252
+ return await route.handler(host, { uri, params, searchParams: parsed.searchParams });
5253
+ }
5254
+ }
5255
+ throw new Error(`Resource not found: ${uri}`);
5175
5256
  }
5257
+ };
5258
+ return router;
5259
+ }
5260
+ function parseXNetUri(uri) {
5261
+ let parsed;
5262
+ try {
5263
+ parsed = new URL(uri);
5264
+ } catch {
5265
+ throw new Error(`Invalid xNet resource URI: ${uri}`);
5176
5266
  }
5177
- limits;
5178
- clock;
5179
- sequence = 0;
5180
- auditEvents = [];
5181
- rollbackSnapshots = /* @__PURE__ */ new Map();
5182
- /** Contributed tools by name (exploration 0196), de-duped at construction. */
5183
- extraTools = /* @__PURE__ */ new Map();
5184
- /** The contributed (non-built-in) tools, with built-in name collisions removed. */
5185
- getExtraTools() {
5186
- const builtIn = new Set(this.builtInTools().map((t) => t.name));
5187
- return [...this.extraTools.values()].filter((t) => !builtIn.has(t.name));
5267
+ if (parsed.protocol !== "xnet:") {
5268
+ throw new Error(`Invalid xNet resource URI: ${uri}`);
5188
5269
  }
5189
- getResources() {
5190
- return [
5191
- createResource(
5192
- "xnet://workspace/summary",
5193
- "Workspace Summary",
5194
- "High-level schema counts, recent nodes, and AI surface capabilities.",
5195
- "application/json",
5196
- "low",
5197
- ["workspace.read"]
5198
- ),
5199
- createResource(
5200
- "xnet://workspace/recent",
5201
- "Recent Workspace Nodes",
5202
- "Recent nodes with ids, schemas, titles, and revisions.",
5203
- "application/json",
5204
- "low",
5205
- ["workspace.read"]
5206
- ),
5207
- createResource(
5208
- "xnet://nodes",
5209
- "All Nodes",
5210
- "Limited list of nodes in the local store.",
5211
- "application/json",
5212
- "low",
5213
- ["workspace.read"]
5214
- ),
5215
- createResource(
5216
- "xnet://schemas",
5217
- "All Schemas",
5218
- "List of available schemas and property metadata.",
5219
- "application/json",
5220
- "low",
5221
- ["workspace.read"]
5222
- ),
5223
- createResource(
5224
- "xnet://page/{pageId}.md",
5225
- "Page Markdown",
5226
- "Markdown projection for a page, including xNet frontmatter identity.",
5227
- "text/markdown",
5228
- "low",
5229
- ["page.read"],
5230
- true
5231
- ),
5232
- createResource(
5233
- "xnet://page/{pageId}/outline",
5234
- "Page Outline",
5235
- "Page heading outline extracted from the Markdown projection.",
5236
- "application/json",
5237
- "low",
5238
- ["page.read"],
5239
- true
5240
- ),
5241
- createResource(
5242
- "xnet://database/{databaseId}/schema",
5243
- "Database Schema",
5244
- "Database node metadata, declared properties, and known column descriptors.",
5245
- "application/json",
5246
- "low",
5247
- ["database.read"],
5248
- true
5249
- ),
5250
- createResource(
5251
- "xnet://database/{databaseId}/views",
5252
- "Database Views",
5253
- "Known database view descriptors from the database node projection.",
5254
- "application/json",
5255
- "low",
5256
- ["database.read"],
5257
- true
5258
- ),
5259
- createResource(
5260
- "xnet://database/{databaseId}/sample?limit=10",
5261
- "Database Sample Rows",
5262
- "Bounded sample of rows for a database using NodeQueryDescriptor semantics.",
5263
- "application/json",
5264
- "low",
5265
- ["database.read", "database.query"],
5266
- true
5267
- ),
5268
- createResource(
5269
- "xnet://canvas/{canvasId}/viewport?x=0&y=0&w=1000&h=800",
5270
- "Canvas Viewport",
5271
- "Viewport-scoped canvas objects and edges.",
5272
- "application/json",
5273
- "low",
5274
- ["canvas.read"],
5275
- true
5276
- ),
5277
- createResource(
5278
- "xnet://canvas/{canvasId}/objects",
5279
- "Canvas Objects",
5280
- "Bounded list of canvas objects and connectors.",
5281
- "application/json",
5282
- "low",
5283
- ["canvas.read"],
5284
- true
5285
- ),
5286
- createResource(
5287
- "xnet://canvas/{canvasId}/selection?ids=object-1,object-2",
5288
- "Canvas Selection",
5289
- "Selection-scoped canvas objects, edges, and source previews.",
5290
- "application/json",
5291
- "low",
5292
- ["canvas.read"],
5293
- true
5294
- ),
5295
- createResource(
5296
- "xnet://canvas/{canvasId}/json-canvas",
5297
- "Canvas JSON Canvas",
5298
- "JSON Canvas projection with xNet source metadata sidecars.",
5299
- "application/json",
5300
- "low",
5301
- ["canvas.read"],
5302
- true
5303
- )
5304
- ];
5270
+ return {
5271
+ host: parsed.hostname,
5272
+ parts: parsed.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part)),
5273
+ searchParams: parsed.searchParams
5274
+ };
5275
+ }
5276
+ function compileRoute(template, handler) {
5277
+ if (!template.startsWith(XNET_URI_PREFIX)) {
5278
+ throw new Error(`Resource route template must start with ${XNET_URI_PREFIX}: ${template}`);
5305
5279
  }
5306
- /** The full tool surface: built-in `xnet_*` tools plus contributed extras. */
5307
- getTools() {
5308
- const extras = this.getExtraTools().map(
5309
- ({ invoke: _invoke, ...def }) => def
5310
- );
5311
- return [...this.builtInTools(), ...extras];
5280
+ const [path] = template.slice(XNET_URI_PREFIX.length).split("?");
5281
+ const [host, ...rawSegments] = path.split("/").filter(Boolean);
5282
+ if (!host) {
5283
+ throw new Error(`Resource route template must include a host: ${template}`);
5312
5284
  }
5313
- builtInTools() {
5314
- return [
5315
- {
5316
- name: "xnet_search",
5317
- title: "Search xNet workspace",
5318
- description: "Search node titles and searchable properties with pagination and limits.",
5319
- risk: "low",
5320
- requiredScopes: ["workspace.search"],
5321
- inputSchema: {
5322
- type: "object",
5323
- properties: {
5324
- query: { type: "string", description: "Search text." },
5325
- schemaId: { type: "string", description: "Optional schema IRI filter." },
5326
- limit: { type: "number", description: "Maximum result count." },
5327
- offset: { type: "number", description: "Result offset for pagination." }
5328
- },
5329
- required: ["query"]
5330
- }
5331
- },
5332
- {
5333
- name: "xnet_graph_expand",
5334
- title: "Expand a node along its relations",
5335
- description: "Walk typed relation edges out from a node to its connected neighbors (bounded by hops and a result limit). Use for just-in-time expansion: fetch a specific node\u2019s connections only when you need them, instead of pulling the whole graph into context.",
5336
- risk: "low",
5337
- requiredScopes: ["workspace.read"],
5338
- inputSchema: {
5339
- type: "object",
5340
- properties: {
5341
- nodeId: { type: "string", description: "The node to expand from." },
5342
- hops: {
5343
- type: "number",
5344
- description: "How many relation hops to walk (1\u20132, default 1)."
5345
- },
5346
- limit: { type: "number", description: "Maximum neighbors to return." }
5347
- },
5348
- required: ["nodeId"]
5349
- }
5285
+ return {
5286
+ host,
5287
+ segments: rawSegments.map((segment) => {
5288
+ const param = PARAM_SEGMENT_PATTERN.exec(segment);
5289
+ return param ? { kind: "param", name: param[1], suffix: param[2] } : { kind: "literal", value: segment };
5290
+ }),
5291
+ handler
5292
+ };
5293
+ }
5294
+ function matchRoute(route, parsed) {
5295
+ if (parsed.host !== route.host) return null;
5296
+ if (parsed.parts.length !== route.segments.length) return null;
5297
+ const params = {};
5298
+ for (const [index, segment] of route.segments.entries()) {
5299
+ const part = parsed.parts[index];
5300
+ if (segment.kind === "literal") {
5301
+ if (part !== segment.value) return null;
5302
+ continue;
5303
+ }
5304
+ if (segment.suffix && !part.endsWith(segment.suffix)) return null;
5305
+ params[segment.name] = segment.suffix ? part.slice(0, -segment.suffix.length) : part;
5306
+ }
5307
+ return params;
5308
+ }
5309
+
5310
+ // src/ai-surface/resources/routes.ts
5311
+ function createBuiltInResourceRouter() {
5312
+ return createAiResourceRouter().register(
5313
+ "xnet://nodes",
5314
+ async (host, { uri }) => host.jsonResource(uri, await host.listNodes())
5315
+ ).register(
5316
+ "xnet://schemas",
5317
+ async (host, { uri }) => host.jsonResource(uri, await host.listSchemas())
5318
+ ).register(
5319
+ "xnet://workspace/summary",
5320
+ async (host, { uri }) => host.jsonResource(uri, await host.getWorkspaceSummary())
5321
+ ).register(
5322
+ "xnet://workspace/recent",
5323
+ async (host, { uri }) => host.jsonResource(uri, await host.getRecentNodes())
5324
+ ).register(
5325
+ "xnet://workspace/search",
5326
+ async (host, { uri, searchParams }) => host.jsonResource(
5327
+ uri,
5328
+ await host.search({
5329
+ query: searchParams.get("q") ?? "",
5330
+ schemaId: searchParams.get("schema") ?? void 0,
5331
+ limit: readUrlNumber(searchParams, "limit"),
5332
+ offset: readUrlNumber(searchParams, "offset")
5333
+ })
5334
+ )
5335
+ ).register(
5336
+ "xnet://node/{nodeId}",
5337
+ async (host, { uri, params }) => host.jsonResource(uri, await host.getNodeProjection(params.nodeId))
5338
+ ).register(
5339
+ "xnet://page/{pageId}.md",
5340
+ async (host, { uri, params }) => await host.readPageMarkdown(params.pageId, true, uri)
5341
+ ).register(
5342
+ "xnet://page/{pageId}",
5343
+ async (host, { uri, params }) => await host.readPageMarkdown(params.pageId, true, uri)
5344
+ ).register(
5345
+ "xnet://page/{pageId}/outline",
5346
+ async (host, { uri, params }) => host.jsonResource(uri, await host.readPageOutline(params.pageId))
5347
+ ).register(
5348
+ "xnet://page/{pageId}/context-pack",
5349
+ async (host, { uri, params }) => host.jsonResource(
5350
+ uri,
5351
+ await host.createContextPack({ seeds: [{ kind: "page", id: params.pageId }] })
5352
+ )
5353
+ ).register(
5354
+ "xnet://database/{databaseId}/schema",
5355
+ async (host, { uri, params }) => host.jsonResource(uri, await host.describeDatabase(params.databaseId))
5356
+ ).register(
5357
+ "xnet://database/{databaseId}/views",
5358
+ async (host, { uri, params }) => host.jsonResource(uri, await host.readDatabaseViews(params.databaseId))
5359
+ ).register(
5360
+ "xnet://database/{databaseId}/sample",
5361
+ async (host, { uri, params, searchParams }) => host.jsonResource(
5362
+ uri,
5363
+ await host.sampleDatabase({
5364
+ databaseId: params.databaseId,
5365
+ sampleSize: readUrlNumber(searchParams, "limit")
5366
+ })
5367
+ )
5368
+ ).register(
5369
+ "xnet://database/{databaseId}/query",
5370
+ async (host, { uri, params, searchParams }) => host.jsonResource(
5371
+ uri,
5372
+ await host.queryDatabase({
5373
+ databaseId: params.databaseId,
5374
+ schemaId: searchParams.get("schema") ?? void 0,
5375
+ search: searchParams.get("q") ?? void 0,
5376
+ materializedView: searchParams.get("view") ? { viewId: searchParams.get("view") ?? "" } : void 0,
5377
+ limit: readUrlNumber(searchParams, "limit"),
5378
+ offset: readUrlNumber(searchParams, "offset")
5379
+ })
5380
+ )
5381
+ ).register(
5382
+ "xnet://canvas/{canvasId}/viewport",
5383
+ async (host, { uri, params, searchParams }) => host.jsonResource(
5384
+ uri,
5385
+ await host.readCanvasViewport({
5386
+ canvasId: params.canvasId,
5387
+ x: readUrlNumber(searchParams, "x"),
5388
+ y: readUrlNumber(searchParams, "y"),
5389
+ w: readUrlNumber(searchParams, "w"),
5390
+ h: readUrlNumber(searchParams, "h"),
5391
+ tileSize: readUrlNumber(searchParams, "tileSize"),
5392
+ tileIds: readCsvStringArray(searchParams.get("tileIds")),
5393
+ includeSourcePreviews: searchParams.get("includeSourcePreviews") === "true"
5394
+ })
5395
+ )
5396
+ ).register(
5397
+ "xnet://canvas/{canvasId}/objects",
5398
+ async (host, { uri, params }) => host.jsonResource(uri, await host.readCanvasObjects(params.canvasId))
5399
+ ).register(
5400
+ "xnet://canvas/{canvasId}/selection",
5401
+ async (host, { uri, params, searchParams }) => host.jsonResource(
5402
+ uri,
5403
+ await host.readCanvasSelection({
5404
+ canvasId: params.canvasId,
5405
+ objectIds: readCsvStringArray(searchParams.get("ids")),
5406
+ includeSourcePreviews: searchParams.get("includeSourcePreviews") !== "false"
5407
+ })
5408
+ )
5409
+ ).register(
5410
+ "xnet://canvas/{canvasId}/json-canvas",
5411
+ async (host, { uri, params, searchParams }) => host.jsonResource(
5412
+ uri,
5413
+ await host.exportCanvasJsonCanvas({
5414
+ canvasId: params.canvasId,
5415
+ includeXNetMetadata: searchParams.get("includeXNetMetadata") !== "false"
5416
+ })
5417
+ )
5418
+ ).register(
5419
+ "xnet://canvas/{canvasId}/object/{objectId}",
5420
+ async (host, { uri, params }) => host.jsonResource(uri, await host.readCanvasObject(params.canvasId, params.objectId))
5421
+ );
5422
+ }
5423
+
5424
+ // src/ai-surface/tools/audit.ts
5425
+ var getAuditLogTool = {
5426
+ definition: {
5427
+ name: "xnet_get_audit_log",
5428
+ title: "Read AI audit log",
5429
+ description: "Read recent AI mutation audit events with optional plan filtering.",
5430
+ risk: "low",
5431
+ requiredScopes: ["workspace.read"],
5432
+ inputSchema: {
5433
+ type: "object",
5434
+ properties: {
5435
+ planId: { type: "string", description: "Optional mutation plan id filter." },
5436
+ limit: { type: "number", description: "Maximum audit events to return." }
5437
+ }
5438
+ }
5439
+ },
5440
+ execute: (host, args) => host.getAuditLog({
5441
+ planId: readOptionalString(args, "planId"),
5442
+ limit: readOptionalNumber(args, "limit")
5443
+ })
5444
+ };
5445
+ var validateMutationPlanTool = {
5446
+ definition: {
5447
+ name: "xnet_validate_mutation_plan",
5448
+ title: "Validate mutation plan",
5449
+ description: "Validate a serialized mutation plan and return errors or warnings.",
5450
+ risk: "medium",
5451
+ requiredScopes: ["workspace.read"],
5452
+ inputSchema: {
5453
+ type: "object",
5454
+ properties: {
5455
+ plan: { type: "object", description: "Mutation plan object to validate." }
5350
5456
  },
5351
- {
5352
- name: "xnet_create_context_pack",
5353
- title: "Create context pack",
5354
- description: "Create a bounded context pack from seeds and optional search results.",
5355
- risk: "low",
5356
- requiredScopes: ["workspace.read", "workspace.search"],
5357
- inputSchema: {
5358
- type: "object",
5359
- properties: {
5360
- query: { type: "string", description: "Optional search query." },
5361
- seeds: {
5362
- type: "array",
5363
- description: "Seed resources such as pages, databases, canvases, or nodes.",
5364
- items: {
5365
- type: "object",
5366
- properties: {
5367
- kind: { type: "string", description: "Seed kind." },
5368
- id: { type: "string", description: "Seed id." }
5369
- }
5370
- }
5371
- },
5372
- limit: { type: "number", description: "Maximum resources to include." }
5373
- }
5457
+ required: ["plan"]
5458
+ }
5459
+ },
5460
+ execute: (_host, args) => {
5461
+ const validation = validateAiMutationPlan(args.plan);
5462
+ return { validation };
5463
+ }
5464
+ };
5465
+
5466
+ // src/ai-surface/tools/canvas.ts
5467
+ var canvasListTool = {
5468
+ definition: {
5469
+ name: "xnet_canvas_list",
5470
+ title: "List canvases",
5471
+ description: "List canvas nodes visible to the AI surface.",
5472
+ risk: "low",
5473
+ requiredScopes: ["canvas.read"],
5474
+ inputSchema: {
5475
+ type: "object",
5476
+ properties: {
5477
+ limit: { type: "number", description: "Maximum canvas count." },
5478
+ offset: { type: "number", description: "Canvas offset." }
5479
+ }
5480
+ }
5481
+ },
5482
+ execute: async (host, args) => await host.listCanvases({
5483
+ limit: readOptionalNumber(args, "limit"),
5484
+ offset: readOptionalNumber(args, "offset")
5485
+ })
5486
+ };
5487
+ var canvasReadViewportTool = {
5488
+ definition: {
5489
+ name: "xnet_canvas_read_viewport",
5490
+ title: "Read canvas viewport",
5491
+ description: "Read canvas objects and edges intersecting a viewport.",
5492
+ risk: "low",
5493
+ requiredScopes: ["canvas.read"],
5494
+ inputSchema: {
5495
+ type: "object",
5496
+ properties: {
5497
+ canvasId: { type: "string", description: "Canvas node id." },
5498
+ x: { type: "number", description: "Viewport x." },
5499
+ y: { type: "number", description: "Viewport y." },
5500
+ w: { type: "number", description: "Viewport width." },
5501
+ h: { type: "number", description: "Viewport height." },
5502
+ includeSourcePreviews: {
5503
+ type: "boolean",
5504
+ description: "Include previews for source-backed objects."
5505
+ },
5506
+ tileSize: { type: "number", description: "Optional tile size for tile scoping." },
5507
+ tileIds: {
5508
+ type: "array",
5509
+ description: "Optional tile ids such as 0/1/-2 to constrain the read.",
5510
+ items: { type: "string" }
5374
5511
  }
5375
5512
  },
5376
- {
5377
- name: "xnet_create_external_context_resource",
5378
- title: "Create untrusted external context resource",
5379
- description: "Wrap externally fetched content as an untrusted context-pack resource with an explicit instruction boundary.",
5380
- risk: "medium",
5381
- requiredScopes: ["network.fetch"],
5382
- inputSchema: {
5383
- type: "object",
5384
- properties: {
5385
- url: { type: "string", description: "External source URL." },
5386
- text: { type: "string", description: "Fetched external text content." },
5387
- mimeType: { type: "string", description: "Source MIME type. Defaults to text/plain." }
5388
- },
5389
- required: ["url", "text"]
5513
+ required: ["canvasId"]
5514
+ }
5515
+ },
5516
+ execute: async (host, args) => await host.readCanvasViewport({
5517
+ canvasId: readRequiredString(args, "canvasId"),
5518
+ x: readOptionalNumber(args, "x"),
5519
+ y: readOptionalNumber(args, "y"),
5520
+ w: readOptionalNumber(args, "w"),
5521
+ h: readOptionalNumber(args, "h"),
5522
+ tileSize: readOptionalNumber(args, "tileSize"),
5523
+ tileIds: readStringArray(args.tileIds),
5524
+ includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
5525
+ })
5526
+ };
5527
+ var canvasReadSelectionTool = {
5528
+ definition: {
5529
+ name: "xnet_canvas_read_selection",
5530
+ title: "Read canvas selection",
5531
+ description: "Read selected canvas objects, connected edges, and optional source previews.",
5532
+ risk: "low",
5533
+ requiredScopes: ["canvas.read"],
5534
+ inputSchema: {
5535
+ type: "object",
5536
+ properties: {
5537
+ canvasId: { type: "string", description: "Canvas node id." },
5538
+ objectIds: {
5539
+ type: "array",
5540
+ description: "Selected object ids.",
5541
+ items: { type: "string" }
5542
+ },
5543
+ includeSourcePreviews: {
5544
+ type: "boolean",
5545
+ description: "Include previews for source-backed objects."
5390
5546
  }
5391
5547
  },
5392
- {
5393
- name: "xnet_read_page_markdown",
5394
- title: "Read page Markdown",
5395
- description: "Read a page as Markdown with optional xNet frontmatter.",
5396
- risk: "low",
5397
- requiredScopes: ["page.read"],
5398
- inputSchema: {
5399
- type: "object",
5400
- properties: {
5401
- pageId: { type: "string", description: "Page node id." },
5402
- includeFrontmatter: {
5403
- type: "boolean",
5404
- description: "Include xNet identity frontmatter. Defaults to true."
5405
- }
5406
- },
5407
- required: ["pageId"]
5408
- }
5548
+ required: ["canvasId", "objectIds"]
5549
+ }
5550
+ },
5551
+ execute: async (host, args) => await host.readCanvasSelection({
5552
+ canvasId: readRequiredString(args, "canvasId"),
5553
+ objectIds: readRequiredStringArray(args.objectIds, "objectIds"),
5554
+ includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
5555
+ })
5556
+ };
5557
+ var canvasSearchTool = {
5558
+ definition: {
5559
+ name: "xnet_canvas_search",
5560
+ title: "Search canvas",
5561
+ description: "Search canvas object text, labels, ids, and source metadata.",
5562
+ risk: "low",
5563
+ requiredScopes: ["canvas.read"],
5564
+ inputSchema: {
5565
+ type: "object",
5566
+ properties: {
5567
+ canvasId: { type: "string", description: "Canvas node id." },
5568
+ query: { type: "string", description: "Search text." },
5569
+ limit: { type: "number", description: "Maximum result count." }
5409
5570
  },
5410
- {
5411
- name: "xnet_validate_page_markdown",
5412
- title: "Validate page Markdown",
5413
- description: "Validate xNet page frontmatter and supported xNet Markdown directives.",
5414
- risk: "low",
5415
- requiredScopes: ["page.read"],
5416
- inputSchema: {
5417
- type: "object",
5418
- properties: {
5419
- pageId: { type: "string", description: "Optional target page node id." },
5420
- baseRevision: { type: "string", description: "Optional expected base revision." },
5421
- markdown: { type: "string", description: "Markdown to validate." }
5422
- },
5423
- required: ["markdown"]
5424
- }
5571
+ required: ["canvasId", "query"]
5572
+ }
5573
+ },
5574
+ execute: async (host, args) => await host.searchCanvas({
5575
+ canvasId: readRequiredString(args, "canvasId"),
5576
+ query: readRequiredString(args, "query"),
5577
+ limit: readOptionalNumber(args, "limit")
5578
+ })
5579
+ };
5580
+ var canvasExportJsonCanvasTool = {
5581
+ definition: {
5582
+ name: "xnet_canvas_export_json_canvas",
5583
+ title: "Export canvas as JSON Canvas",
5584
+ description: "Export a canvas or viewport as JSON Canvas with xNet source metadata.",
5585
+ risk: "low",
5586
+ requiredScopes: ["canvas.read"],
5587
+ inputSchema: {
5588
+ type: "object",
5589
+ properties: {
5590
+ canvasId: { type: "string", description: "Canvas node id." },
5591
+ includeXNetMetadata: {
5592
+ type: "boolean",
5593
+ description: "Include xNet source metadata. Defaults to true."
5594
+ },
5595
+ x: { type: "number", description: "Optional viewport x." },
5596
+ y: { type: "number", description: "Optional viewport y." },
5597
+ w: { type: "number", description: "Optional viewport width." },
5598
+ h: { type: "number", description: "Optional viewport height." }
5425
5599
  },
5426
- {
5427
- name: "xnet_plan_page_patch",
5428
- title: "Plan page Markdown patch",
5429
- description: "Validate an edited Markdown page and return a mutation plan without applying it.",
5430
- risk: "medium",
5431
- requiredScopes: ["page.read", "page.propose"],
5432
- inputSchema: {
5433
- type: "object",
5434
- properties: {
5435
- pageId: { type: "string", description: "Page node id." },
5436
- baseRevision: { type: "string", description: "Revision the patch was based on." },
5437
- markdown: { type: "string", description: "Proposed full Markdown replacement." },
5438
- intent: { type: "string", description: "User or agent intent for the patch." },
5439
- actor: { type: "string", description: "Agent or user creating the plan." }
5440
- },
5441
- required: ["pageId", "markdown"]
5442
- }
5600
+ required: ["canvasId"]
5601
+ }
5602
+ },
5603
+ execute: async (host, args) => await host.exportCanvasJsonCanvas({
5604
+ canvasId: readRequiredString(args, "canvasId"),
5605
+ includeXNetMetadata: readOptionalBoolean(args, "includeXNetMetadata") ?? true,
5606
+ x: readOptionalNumber(args, "x"),
5607
+ y: readOptionalNumber(args, "y"),
5608
+ w: readOptionalNumber(args, "w"),
5609
+ h: readOptionalNumber(args, "h")
5610
+ })
5611
+ };
5612
+ var canvasPlanJsonCanvasImportTool = {
5613
+ definition: {
5614
+ name: "xnet_canvas_plan_json_canvas_import",
5615
+ title: "Plan JSON Canvas import",
5616
+ description: "Convert a JSON Canvas document into a plan-only canvas mutation.",
5617
+ risk: "medium",
5618
+ requiredScopes: ["canvas.read", "canvas.propose"],
5619
+ inputSchema: {
5620
+ type: "object",
5621
+ properties: {
5622
+ canvasId: { type: "string", description: "Canvas node id." },
5623
+ document: { type: "object", description: "JSON Canvas document." },
5624
+ baseRevision: { type: "string", description: "Revision the import was based on." },
5625
+ actor: { type: "string", description: "Agent or user creating the plan." },
5626
+ intent: { type: "string", description: "User or agent intent for the import." }
5443
5627
  },
5444
- {
5445
- name: "xnet_apply_page_markdown",
5446
- title: "Apply page Markdown plan",
5447
- description: "Apply a validated page Markdown mutation plan through the configured TipTap/Yjs document adapter, with a node-property fallback.",
5448
- risk: "high",
5449
- requiredScopes: ["page.read", "page.write"],
5450
- inputSchema: {
5451
- type: "object",
5452
- properties: {
5453
- plan: { type: "object", description: "Validated page Markdown mutation plan." },
5454
- confirmApply: {
5455
- type: "boolean",
5456
- description: "Must be true to apply the page Markdown plan."
5457
- },
5458
- allowStale: {
5459
- type: "boolean",
5460
- description: "Allow applying when the plan base revision differs from the live node."
5461
- }
5462
- },
5463
- required: ["plan", "confirmApply"]
5464
- }
5628
+ required: ["canvasId", "document"]
5629
+ }
5630
+ },
5631
+ execute: async (host, args) => await host.planCanvasJsonCanvasImport(args)
5632
+ };
5633
+ var planCanvasMutationTool = {
5634
+ definition: {
5635
+ name: "xnet_plan_canvas_mutation",
5636
+ title: "Plan canvas mutation",
5637
+ description: "Create a canvas mutation plan for later review without applying it.",
5638
+ risk: "medium",
5639
+ requiredScopes: ["canvas.read", "canvas.propose"],
5640
+ inputSchema: {
5641
+ type: "object",
5642
+ properties: {
5643
+ canvasId: { type: "string", description: "Canvas node id." },
5644
+ baseRevision: { type: "string", description: "Revision the mutation was based on." },
5645
+ operations: { type: "array", description: "Canvas operations to validate." },
5646
+ intent: { type: "string", description: "User or agent intent for the mutation." },
5647
+ actor: { type: "string", description: "Agent or user creating the plan." }
5465
5648
  },
5466
- {
5467
- name: "xnet_get_audit_log",
5468
- title: "Read AI audit log",
5469
- description: "Read recent AI mutation audit events with optional plan filtering.",
5470
- risk: "low",
5471
- requiredScopes: ["workspace.read"],
5472
- inputSchema: {
5473
- type: "object",
5474
- properties: {
5475
- planId: { type: "string", description: "Optional mutation plan id filter." },
5476
- limit: { type: "number", description: "Maximum audit events to return." }
5477
- }
5649
+ required: ["canvasId", "operations"]
5650
+ }
5651
+ },
5652
+ execute: async (host, args) => await host.planCanvasMutation(args)
5653
+ };
5654
+ var canvasToolEntries = [
5655
+ canvasListTool,
5656
+ canvasReadViewportTool,
5657
+ canvasReadSelectionTool,
5658
+ canvasSearchTool,
5659
+ canvasExportJsonCanvasTool,
5660
+ canvasPlanJsonCanvasImportTool,
5661
+ planCanvasMutationTool
5662
+ ];
5663
+
5664
+ // src/ai-surface/tools/database.ts
5665
+ var databaseDescribeTool = {
5666
+ definition: {
5667
+ name: "xnet_database_describe",
5668
+ title: "Describe database",
5669
+ description: "Describe database schema, columns, views, row schema, and row counts.",
5670
+ risk: "low",
5671
+ requiredScopes: ["database.read"],
5672
+ inputSchema: {
5673
+ type: "object",
5674
+ properties: {
5675
+ databaseId: { type: "string", description: "Database node id." },
5676
+ includeSample: {
5677
+ type: "boolean",
5678
+ description: "Include a small descriptor-backed row sample."
5478
5679
  }
5479
5680
  },
5480
- {
5481
- name: "xnet_rollback_page_markdown",
5482
- title: "Rollback page Markdown apply",
5483
- description: "Rollback a previously applied page Markdown plan by rollback handle.",
5484
- risk: "high",
5485
- requiredScopes: ["page.write"],
5486
- inputSchema: {
5681
+ required: ["databaseId"]
5682
+ }
5683
+ },
5684
+ execute: async (host, args) => await host.describeDatabase(readRequiredString(args, "databaseId"), {
5685
+ includeSample: readOptionalBoolean(args, "includeSample") ?? false
5686
+ })
5687
+ };
5688
+ var databaseQueryTool = {
5689
+ definition: {
5690
+ name: "xnet_database_query",
5691
+ title: "Query database rows",
5692
+ description: "Read a bounded page of database rows using NodeQueryDescriptor-compatible options.",
5693
+ risk: "low",
5694
+ requiredScopes: ["database.read", "database.query"],
5695
+ inputSchema: {
5696
+ type: "object",
5697
+ properties: {
5698
+ databaseId: { type: "string", description: "Database node id." },
5699
+ schemaId: { type: "string", description: "Optional row schema IRI." },
5700
+ descriptor: {
5487
5701
  type: "object",
5488
- properties: {
5489
- rollbackHandle: { type: "string", description: "Rollback handle from apply result." },
5490
- confirmRollback: {
5491
- type: "boolean",
5492
- description: "Must be true to perform the rollback."
5493
- }
5494
- },
5495
- required: ["rollbackHandle", "confirmRollback"]
5496
- }
5497
- },
5498
- {
5499
- name: "xnet_database_describe",
5500
- title: "Describe database",
5501
- description: "Describe database schema, columns, views, row schema, and row counts.",
5502
- risk: "low",
5503
- requiredScopes: ["database.read"],
5504
- inputSchema: {
5702
+ description: "Optional NodeQueryDescriptor-compatible query shape."
5703
+ },
5704
+ where: {
5505
5705
  type: "object",
5506
- properties: {
5507
- databaseId: { type: "string", description: "Database node id." },
5508
- includeSample: {
5509
- type: "boolean",
5510
- description: "Include a small descriptor-backed row sample."
5511
- }
5512
- },
5513
- required: ["databaseId"]
5514
- }
5515
- },
5516
- {
5517
- name: "xnet_database_query",
5518
- title: "Query database rows",
5519
- description: "Read a bounded page of database rows using NodeQueryDescriptor-compatible options.",
5520
- risk: "low",
5521
- requiredScopes: ["database.read", "database.query"],
5522
- inputSchema: {
5706
+ description: "Optional exact property filters for row nodes."
5707
+ },
5708
+ search: {
5523
5709
  type: "object",
5524
- properties: {
5525
- databaseId: { type: "string", description: "Database node id." },
5526
- schemaId: { type: "string", description: "Optional row schema IRI." },
5527
- descriptor: {
5528
- type: "object",
5529
- description: "Optional NodeQueryDescriptor-compatible query shape."
5530
- },
5531
- where: {
5532
- type: "object",
5533
- description: "Optional exact property filters for row nodes."
5534
- },
5535
- search: {
5536
- type: "object",
5537
- description: "Optional NodeQueryDescriptor search filter."
5538
- },
5539
- orderBy: {
5540
- type: "object",
5541
- description: "Optional NodeQueryDescriptor order map."
5542
- },
5543
- materializedView: {
5544
- type: "object",
5545
- description: "Optional materialized view query options."
5546
- },
5547
- count: { type: "string", description: "Page count mode: exact, estimate, or none." },
5548
- limit: { type: "number", description: "Maximum row count." },
5549
- offset: { type: "number", description: "Row offset." }
5550
- },
5551
- required: ["databaseId"]
5552
- }
5553
- },
5554
- {
5555
- name: "xnet_database_sample",
5556
- title: "Sample database rows",
5557
- description: "Return a small deterministic sample for schema and content inspection.",
5558
- risk: "low",
5559
- requiredScopes: ["database.read", "database.query"],
5560
- inputSchema: {
5710
+ description: "Optional NodeQueryDescriptor search filter."
5711
+ },
5712
+ orderBy: {
5561
5713
  type: "object",
5562
- properties: {
5563
- databaseId: { type: "string", description: "Database node id." },
5564
- schemaId: { type: "string", description: "Optional row schema IRI." },
5565
- sampleSize: { type: "number", description: "Sample row count." },
5566
- descriptor: {
5567
- type: "object",
5568
- description: "Optional NodeQueryDescriptor-compatible query shape."
5569
- }
5570
- },
5571
- required: ["databaseId"]
5572
- }
5573
- },
5574
- {
5575
- name: "xnet_database_explain_query",
5576
- title: "Explain database query",
5577
- description: "Explain descriptor, pagination, materialized view, and storage plan metadata.",
5578
- risk: "low",
5579
- requiredScopes: ["database.read", "database.query", "storage.diagnostics"],
5580
- inputSchema: {
5714
+ description: "Optional NodeQueryDescriptor order map."
5715
+ },
5716
+ materializedView: {
5581
5717
  type: "object",
5582
- properties: {
5583
- databaseId: { type: "string", description: "Database node id." },
5584
- schemaId: { type: "string", description: "Optional row schema IRI." },
5585
- descriptor: {
5586
- type: "object",
5587
- description: "Optional NodeQueryDescriptor-compatible query shape."
5588
- },
5589
- limit: { type: "number", description: "Maximum row count for the dry-run query." },
5590
- offset: { type: "number", description: "Row offset." }
5591
- },
5592
- required: ["databaseId"]
5593
- }
5718
+ description: "Optional materialized view query options."
5719
+ },
5720
+ count: { type: "string", description: "Page count mode: exact, estimate, or none." },
5721
+ limit: { type: "number", description: "Maximum row count." },
5722
+ offset: { type: "number", description: "Row offset." }
5594
5723
  },
5595
- {
5596
- name: "xnet_plan_database_mutation",
5597
- title: "Plan database mutation",
5598
- description: "Create a database mutation plan for later review without applying it.",
5599
- risk: "medium",
5600
- requiredScopes: ["database.read", "database.propose"],
5601
- inputSchema: {
5724
+ required: ["databaseId"]
5725
+ }
5726
+ },
5727
+ execute: async (host, args) => await host.queryDatabase({
5728
+ databaseId: readRequiredString(args, "databaseId"),
5729
+ schemaId: readOptionalString(args, "schemaId"),
5730
+ descriptor: readOptionalRecord(args, "descriptor"),
5731
+ where: readOptionalRecord(args, "where"),
5732
+ search: args.search,
5733
+ orderBy: readOptionalRecord(args, "orderBy"),
5734
+ materializedView: args.materializedView,
5735
+ count: readOptionalString(args, "count"),
5736
+ limit: readOptionalNumber(args, "limit"),
5737
+ offset: readOptionalNumber(args, "offset")
5738
+ })
5739
+ };
5740
+ var databaseSampleTool = {
5741
+ definition: {
5742
+ name: "xnet_database_sample",
5743
+ title: "Sample database rows",
5744
+ description: "Return a small deterministic sample for schema and content inspection.",
5745
+ risk: "low",
5746
+ requiredScopes: ["database.read", "database.query"],
5747
+ inputSchema: {
5748
+ type: "object",
5749
+ properties: {
5750
+ databaseId: { type: "string", description: "Database node id." },
5751
+ schemaId: { type: "string", description: "Optional row schema IRI." },
5752
+ sampleSize: { type: "number", description: "Sample row count." },
5753
+ descriptor: {
5602
5754
  type: "object",
5603
- properties: {
5604
- databaseId: { type: "string", description: "Database node id." },
5605
- baseRevision: { type: "string", description: "Revision the mutation was based on." },
5606
- operations: { type: "array", description: "Database operations to validate." },
5607
- intent: { type: "string", description: "User or agent intent for the mutation." },
5608
- actor: { type: "string", description: "Agent or user creating the plan." }
5609
- },
5610
- required: ["databaseId", "operations"]
5755
+ description: "Optional NodeQueryDescriptor-compatible query shape."
5611
5756
  }
5612
5757
  },
5613
- {
5614
- name: "xnet_apply_database_mutation",
5615
- title: "Apply database mutation plan",
5616
- description: "Apply a validated database row/schema mutation plan with transactional row rollback and audit logging.",
5617
- risk: "high",
5618
- requiredScopes: ["database.read", "database.write.rows", "database.write.schema"],
5619
- inputSchema: {
5758
+ required: ["databaseId"]
5759
+ }
5760
+ },
5761
+ execute: async (host, args) => await host.sampleDatabase({
5762
+ databaseId: readRequiredString(args, "databaseId"),
5763
+ schemaId: readOptionalString(args, "schemaId"),
5764
+ descriptor: readOptionalRecord(args, "descriptor"),
5765
+ sampleSize: readOptionalNumber(args, "sampleSize")
5766
+ })
5767
+ };
5768
+ var databaseExplainQueryTool = {
5769
+ definition: {
5770
+ name: "xnet_database_explain_query",
5771
+ title: "Explain database query",
5772
+ description: "Explain descriptor, pagination, materialized view, and storage plan metadata.",
5773
+ risk: "low",
5774
+ requiredScopes: ["database.read", "database.query", "storage.diagnostics"],
5775
+ inputSchema: {
5776
+ type: "object",
5777
+ properties: {
5778
+ databaseId: { type: "string", description: "Database node id." },
5779
+ schemaId: { type: "string", description: "Optional row schema IRI." },
5780
+ descriptor: {
5620
5781
  type: "object",
5621
- properties: {
5622
- plan: { type: "object", description: "Validated database mutation plan." },
5623
- confirmApply: {
5624
- type: "boolean",
5625
- description: "Must be true to apply the database mutation plan."
5626
- },
5627
- allowStale: {
5628
- type: "boolean",
5629
- description: "Allow applying when the plan base revision differs from the live database node."
5630
- }
5631
- },
5632
- required: ["plan", "confirmApply"]
5633
- }
5782
+ description: "Optional NodeQueryDescriptor-compatible query shape."
5783
+ },
5784
+ limit: { type: "number", description: "Maximum row count for the dry-run query." },
5785
+ offset: { type: "number", description: "Row offset." }
5634
5786
  },
5635
- {
5636
- name: "xnet_canvas_list",
5637
- title: "List canvases",
5638
- description: "List canvas nodes visible to the AI surface.",
5639
- risk: "low",
5640
- requiredScopes: ["canvas.read"],
5641
- inputSchema: {
5642
- type: "object",
5643
- properties: {
5644
- limit: { type: "number", description: "Maximum canvas count." },
5645
- offset: { type: "number", description: "Canvas offset." }
5646
- }
5647
- }
5787
+ required: ["databaseId"]
5788
+ }
5789
+ },
5790
+ execute: async (host, args) => await host.explainDatabaseQuery({
5791
+ databaseId: readRequiredString(args, "databaseId"),
5792
+ schemaId: readOptionalString(args, "schemaId"),
5793
+ descriptor: readOptionalRecord(args, "descriptor"),
5794
+ limit: readOptionalNumber(args, "limit"),
5795
+ offset: readOptionalNumber(args, "offset")
5796
+ })
5797
+ };
5798
+ var planDatabaseMutationTool = {
5799
+ definition: {
5800
+ name: "xnet_plan_database_mutation",
5801
+ title: "Plan database mutation",
5802
+ description: "Create a database mutation plan for later review without applying it.",
5803
+ risk: "medium",
5804
+ requiredScopes: ["database.read", "database.propose"],
5805
+ inputSchema: {
5806
+ type: "object",
5807
+ properties: {
5808
+ databaseId: { type: "string", description: "Database node id." },
5809
+ baseRevision: { type: "string", description: "Revision the mutation was based on." },
5810
+ operations: { type: "array", description: "Database operations to validate." },
5811
+ intent: { type: "string", description: "User or agent intent for the mutation." },
5812
+ actor: { type: "string", description: "Agent or user creating the plan." }
5648
5813
  },
5649
- {
5650
- name: "xnet_canvas_read_viewport",
5651
- title: "Read canvas viewport",
5652
- description: "Read canvas objects and edges intersecting a viewport.",
5653
- risk: "low",
5654
- requiredScopes: ["canvas.read"],
5655
- inputSchema: {
5656
- type: "object",
5657
- properties: {
5658
- canvasId: { type: "string", description: "Canvas node id." },
5659
- x: { type: "number", description: "Viewport x." },
5660
- y: { type: "number", description: "Viewport y." },
5661
- w: { type: "number", description: "Viewport width." },
5662
- h: { type: "number", description: "Viewport height." },
5663
- includeSourcePreviews: {
5664
- type: "boolean",
5665
- description: "Include previews for source-backed objects."
5666
- },
5667
- tileSize: { type: "number", description: "Optional tile size for tile scoping." },
5668
- tileIds: {
5669
- type: "array",
5670
- description: "Optional tile ids such as 0/1/-2 to constrain the read.",
5671
- items: { type: "string" }
5672
- }
5673
- },
5674
- required: ["canvasId"]
5814
+ required: ["databaseId", "operations"]
5815
+ }
5816
+ },
5817
+ execute: async (host, args) => await host.planDatabaseMutation(args)
5818
+ };
5819
+ var applyDatabaseMutationTool = {
5820
+ definition: {
5821
+ name: "xnet_apply_database_mutation",
5822
+ title: "Apply database mutation plan",
5823
+ description: "Apply a validated database row/schema mutation plan with transactional row rollback and audit logging.",
5824
+ risk: "high",
5825
+ requiredScopes: ["database.read", "database.write.rows", "database.write.schema"],
5826
+ inputSchema: {
5827
+ type: "object",
5828
+ properties: {
5829
+ plan: { type: "object", description: "Validated database mutation plan." },
5830
+ confirmApply: {
5831
+ type: "boolean",
5832
+ description: "Must be true to apply the database mutation plan."
5833
+ },
5834
+ allowStale: {
5835
+ type: "boolean",
5836
+ description: "Allow applying when the plan base revision differs from the live database node."
5675
5837
  }
5676
5838
  },
5677
- {
5678
- name: "xnet_canvas_read_selection",
5679
- title: "Read canvas selection",
5680
- description: "Read selected canvas objects, connected edges, and optional source previews.",
5681
- risk: "low",
5682
- requiredScopes: ["canvas.read"],
5683
- inputSchema: {
5684
- type: "object",
5685
- properties: {
5686
- canvasId: { type: "string", description: "Canvas node id." },
5687
- objectIds: {
5688
- type: "array",
5689
- description: "Selected object ids.",
5690
- items: { type: "string" }
5691
- },
5692
- includeSourcePreviews: {
5693
- type: "boolean",
5694
- description: "Include previews for source-backed objects."
5695
- }
5696
- },
5697
- required: ["canvasId", "objectIds"]
5839
+ required: ["plan", "confirmApply"]
5840
+ }
5841
+ },
5842
+ execute: async (host, args) => await host.applyDatabaseMutation(args)
5843
+ };
5844
+ var databaseToolEntries = [
5845
+ databaseDescribeTool,
5846
+ databaseQueryTool,
5847
+ databaseSampleTool,
5848
+ databaseExplainQueryTool,
5849
+ planDatabaseMutationTool,
5850
+ applyDatabaseMutationTool
5851
+ ];
5852
+
5853
+ // src/ai-surface/tools/page-mutation.ts
5854
+ var readPageMarkdownTool = {
5855
+ definition: {
5856
+ name: "xnet_read_page_markdown",
5857
+ title: "Read page Markdown",
5858
+ description: "Read a page as Markdown with optional xNet frontmatter.",
5859
+ risk: "low",
5860
+ requiredScopes: ["page.read"],
5861
+ inputSchema: {
5862
+ type: "object",
5863
+ properties: {
5864
+ pageId: { type: "string", description: "Page node id." },
5865
+ includeFrontmatter: {
5866
+ type: "boolean",
5867
+ description: "Include xNet identity frontmatter. Defaults to true."
5698
5868
  }
5699
5869
  },
5700
- {
5701
- name: "xnet_canvas_search",
5702
- title: "Search canvas",
5703
- description: "Search canvas object text, labels, ids, and source metadata.",
5704
- risk: "low",
5705
- requiredScopes: ["canvas.read"],
5706
- inputSchema: {
5707
- type: "object",
5708
- properties: {
5709
- canvasId: { type: "string", description: "Canvas node id." },
5710
- query: { type: "string", description: "Search text." },
5711
- limit: { type: "number", description: "Maximum result count." }
5712
- },
5713
- required: ["canvasId", "query"]
5714
- }
5870
+ required: ["pageId"]
5871
+ }
5872
+ },
5873
+ execute: async (host, args) => {
5874
+ const content = await host.readPageMarkdown(
5875
+ readRequiredString(args, "pageId"),
5876
+ readOptionalBoolean(args, "includeFrontmatter") ?? true
5877
+ );
5878
+ return { markdown: content.text, mimeType: content.mimeType, uri: content.uri };
5879
+ }
5880
+ };
5881
+ var validatePageMarkdownTool = {
5882
+ definition: {
5883
+ name: "xnet_validate_page_markdown",
5884
+ title: "Validate page Markdown",
5885
+ description: "Validate xNet page frontmatter and supported xNet Markdown directives.",
5886
+ risk: "low",
5887
+ requiredScopes: ["page.read"],
5888
+ inputSchema: {
5889
+ type: "object",
5890
+ properties: {
5891
+ pageId: { type: "string", description: "Optional target page node id." },
5892
+ baseRevision: { type: "string", description: "Optional expected base revision." },
5893
+ markdown: { type: "string", description: "Markdown to validate." }
5715
5894
  },
5716
- {
5717
- name: "xnet_canvas_export_json_canvas",
5718
- title: "Export canvas as JSON Canvas",
5719
- description: "Export a canvas or viewport as JSON Canvas with xNet source metadata.",
5720
- risk: "low",
5721
- requiredScopes: ["canvas.read"],
5722
- inputSchema: {
5723
- type: "object",
5724
- properties: {
5725
- canvasId: { type: "string", description: "Canvas node id." },
5726
- includeXNetMetadata: {
5727
- type: "boolean",
5728
- description: "Include xNet source metadata. Defaults to true."
5729
- },
5730
- x: { type: "number", description: "Optional viewport x." },
5731
- y: { type: "number", description: "Optional viewport y." },
5732
- w: { type: "number", description: "Optional viewport width." },
5733
- h: { type: "number", description: "Optional viewport height." }
5734
- },
5735
- required: ["canvasId"]
5736
- }
5895
+ required: ["markdown"]
5896
+ }
5897
+ },
5898
+ execute: async (host, args) => {
5899
+ const pageId = readOptionalString(args, "pageId");
5900
+ const node = pageId ? await host.getNodeOrThrow(pageId) : null;
5901
+ return validateXNetPageMarkdown(readRequiredString(args, "markdown"), {
5902
+ pageId,
5903
+ schemaId: node?.schemaId,
5904
+ baseRevision: readOptionalString(args, "baseRevision")
5905
+ });
5906
+ }
5907
+ };
5908
+ var planPagePatchTool = {
5909
+ definition: {
5910
+ name: "xnet_plan_page_patch",
5911
+ title: "Plan page Markdown patch",
5912
+ description: "Validate an edited Markdown page and return a mutation plan without applying it.",
5913
+ risk: "medium",
5914
+ requiredScopes: ["page.read", "page.propose"],
5915
+ inputSchema: {
5916
+ type: "object",
5917
+ properties: {
5918
+ pageId: { type: "string", description: "Page node id." },
5919
+ baseRevision: { type: "string", description: "Revision the patch was based on." },
5920
+ markdown: { type: "string", description: "Proposed full Markdown replacement." },
5921
+ intent: { type: "string", description: "User or agent intent for the patch." },
5922
+ actor: { type: "string", description: "Agent or user creating the plan." }
5737
5923
  },
5738
- {
5739
- name: "xnet_canvas_plan_json_canvas_import",
5740
- title: "Plan JSON Canvas import",
5741
- description: "Convert a JSON Canvas document into a plan-only canvas mutation.",
5742
- risk: "medium",
5743
- requiredScopes: ["canvas.read", "canvas.propose"],
5744
- inputSchema: {
5745
- type: "object",
5746
- properties: {
5747
- canvasId: { type: "string", description: "Canvas node id." },
5748
- document: { type: "object", description: "JSON Canvas document." },
5749
- baseRevision: { type: "string", description: "Revision the import was based on." },
5750
- actor: { type: "string", description: "Agent or user creating the plan." },
5751
- intent: { type: "string", description: "User or agent intent for the import." }
5752
- },
5753
- required: ["canvasId", "document"]
5924
+ required: ["pageId", "markdown"]
5925
+ }
5926
+ },
5927
+ execute: async (host, args) => await host.planPagePatch(args)
5928
+ };
5929
+ var applyPageMarkdownTool = {
5930
+ definition: {
5931
+ name: "xnet_apply_page_markdown",
5932
+ title: "Apply page Markdown plan",
5933
+ description: "Apply a validated page Markdown mutation plan through the configured TipTap/Yjs document adapter, with a node-property fallback.",
5934
+ risk: "high",
5935
+ requiredScopes: ["page.read", "page.write"],
5936
+ inputSchema: {
5937
+ type: "object",
5938
+ properties: {
5939
+ plan: { type: "object", description: "Validated page Markdown mutation plan." },
5940
+ confirmApply: {
5941
+ type: "boolean",
5942
+ description: "Must be true to apply the page Markdown plan."
5943
+ },
5944
+ allowStale: {
5945
+ type: "boolean",
5946
+ description: "Allow applying when the plan base revision differs from the live node."
5754
5947
  }
5755
5948
  },
5756
- {
5757
- name: "xnet_plan_canvas_mutation",
5758
- title: "Plan canvas mutation",
5759
- description: "Create a canvas mutation plan for later review without applying it.",
5760
- risk: "medium",
5761
- requiredScopes: ["canvas.read", "canvas.propose"],
5762
- inputSchema: {
5763
- type: "object",
5764
- properties: {
5765
- canvasId: { type: "string", description: "Canvas node id." },
5766
- baseRevision: { type: "string", description: "Revision the mutation was based on." },
5767
- operations: { type: "array", description: "Canvas operations to validate." },
5768
- intent: { type: "string", description: "User or agent intent for the mutation." },
5769
- actor: { type: "string", description: "Agent or user creating the plan." }
5770
- },
5771
- required: ["canvasId", "operations"]
5949
+ required: ["plan", "confirmApply"]
5950
+ }
5951
+ },
5952
+ execute: async (host, args) => await host.applyPageMarkdown(args)
5953
+ };
5954
+ var rollbackPageMarkdownTool = {
5955
+ definition: {
5956
+ name: "xnet_rollback_page_markdown",
5957
+ title: "Rollback page Markdown apply",
5958
+ description: "Rollback a previously applied page Markdown plan by rollback handle.",
5959
+ risk: "high",
5960
+ requiredScopes: ["page.write"],
5961
+ inputSchema: {
5962
+ type: "object",
5963
+ properties: {
5964
+ rollbackHandle: { type: "string", description: "Rollback handle from apply result." },
5965
+ confirmRollback: {
5966
+ type: "boolean",
5967
+ description: "Must be true to perform the rollback."
5772
5968
  }
5773
5969
  },
5774
- {
5775
- name: "xnet_validate_mutation_plan",
5776
- title: "Validate mutation plan",
5777
- description: "Validate a serialized mutation plan and return errors or warnings.",
5778
- risk: "medium",
5779
- requiredScopes: ["workspace.read"],
5780
- inputSchema: {
5781
- type: "object",
5782
- properties: {
5783
- plan: { type: "object", description: "Mutation plan object to validate." }
5784
- },
5785
- required: ["plan"]
5786
- }
5970
+ required: ["rollbackHandle", "confirmRollback"]
5971
+ }
5972
+ },
5973
+ execute: async (host, args) => await host.rollbackPageMarkdown(args)
5974
+ };
5975
+
5976
+ // src/ai-surface/tools/search.ts
5977
+ var searchTool4 = {
5978
+ definition: {
5979
+ name: "xnet_search",
5980
+ title: "Search xNet workspace",
5981
+ description: "Search node titles and searchable properties with pagination and limits.",
5982
+ risk: "low",
5983
+ requiredScopes: ["workspace.search"],
5984
+ inputSchema: {
5985
+ type: "object",
5986
+ properties: {
5987
+ query: { type: "string", description: "Search text." },
5988
+ schemaId: { type: "string", description: "Optional schema IRI filter." },
5989
+ limit: { type: "number", description: "Maximum result count." },
5990
+ offset: { type: "number", description: "Result offset for pagination." }
5991
+ },
5992
+ required: ["query"]
5993
+ }
5994
+ },
5995
+ execute: async (host, args) => await host.search({
5996
+ query: readRequiredString(args, "query"),
5997
+ schemaId: readOptionalString(args, "schemaId") ?? readOptionalString(args, "schema"),
5998
+ limit: readOptionalNumber(args, "limit"),
5999
+ offset: readOptionalNumber(args, "offset")
6000
+ })
6001
+ };
6002
+ var graphExpandTool = {
6003
+ definition: {
6004
+ name: "xnet_graph_expand",
6005
+ title: "Expand a node along its relations",
6006
+ description: "Walk typed relation edges out from a node to its connected neighbors (bounded by hops and a result limit). Use for just-in-time expansion: fetch a specific node\u2019s connections only when you need them, instead of pulling the whole graph into context.",
6007
+ risk: "low",
6008
+ requiredScopes: ["workspace.read"],
6009
+ inputSchema: {
6010
+ type: "object",
6011
+ properties: {
6012
+ nodeId: { type: "string", description: "The node to expand from." },
6013
+ hops: {
6014
+ type: "number",
6015
+ description: "How many relation hops to walk (1\u20132, default 1)."
6016
+ },
6017
+ limit: { type: "number", description: "Maximum neighbors to return." }
6018
+ },
6019
+ required: ["nodeId"]
6020
+ }
6021
+ },
6022
+ execute: async (host, args) => await host.expandGraph({
6023
+ nodeId: readRequiredString(args, "nodeId"),
6024
+ hops: readOptionalNumber(args, "hops"),
6025
+ limit: readOptionalNumber(args, "limit")
6026
+ })
6027
+ };
6028
+ var createContextPackTool = {
6029
+ definition: {
6030
+ name: "xnet_create_context_pack",
6031
+ title: "Create context pack",
6032
+ description: "Create a bounded context pack from seeds and optional search results.",
6033
+ risk: "low",
6034
+ requiredScopes: ["workspace.read", "workspace.search"],
6035
+ inputSchema: {
6036
+ type: "object",
6037
+ properties: {
6038
+ query: { type: "string", description: "Optional search query." },
6039
+ seeds: {
6040
+ type: "array",
6041
+ description: "Seed resources such as pages, databases, canvases, or nodes.",
6042
+ items: {
6043
+ type: "object",
6044
+ properties: {
6045
+ kind: { type: "string", description: "Seed kind." },
6046
+ id: { type: "string", description: "Seed id." }
6047
+ }
6048
+ }
6049
+ },
6050
+ limit: { type: "number", description: "Maximum resources to include." }
5787
6051
  }
6052
+ }
6053
+ },
6054
+ execute: async (host, args) => await host.createContextPack({
6055
+ query: readOptionalString(args, "query"),
6056
+ seeds: readContextSeeds(args.seeds),
6057
+ limit: readOptionalNumber(args, "limit")
6058
+ })
6059
+ };
6060
+ var createExternalContextResourceTool = {
6061
+ definition: {
6062
+ name: "xnet_create_external_context_resource",
6063
+ title: "Create untrusted external context resource",
6064
+ description: "Wrap externally fetched content as an untrusted context-pack resource with an explicit instruction boundary.",
6065
+ risk: "medium",
6066
+ requiredScopes: ["network.fetch"],
6067
+ inputSchema: {
6068
+ type: "object",
6069
+ properties: {
6070
+ url: { type: "string", description: "External source URL." },
6071
+ text: { type: "string", description: "Fetched external text content." },
6072
+ mimeType: { type: "string", description: "Source MIME type. Defaults to text/plain." }
6073
+ },
6074
+ required: ["url", "text"]
6075
+ }
6076
+ },
6077
+ execute: (host, args) => host.createExternalContextResource({
6078
+ url: readRequiredString(args, "url"),
6079
+ text: readRequiredString(args, "text"),
6080
+ mimeType: readOptionalString(args, "mimeType")
6081
+ })
6082
+ };
6083
+ var searchToolEntries = [
6084
+ searchTool4,
6085
+ graphExpandTool,
6086
+ createContextPackTool,
6087
+ createExternalContextResourceTool
6088
+ ];
6089
+
6090
+ // src/ai-surface/tools/index.ts
6091
+ var BUILT_IN_TOOL_ENTRIES = [
6092
+ ...searchToolEntries,
6093
+ readPageMarkdownTool,
6094
+ validatePageMarkdownTool,
6095
+ planPagePatchTool,
6096
+ applyPageMarkdownTool,
6097
+ getAuditLogTool,
6098
+ rollbackPageMarkdownTool,
6099
+ ...databaseToolEntries,
6100
+ ...canvasToolEntries,
6101
+ validateMutationPlanTool
6102
+ ];
6103
+ var BUILT_IN_TOOLS_BY_NAME = new Map(
6104
+ BUILT_IN_TOOL_ENTRIES.map((entry) => [entry.definition.name, entry])
6105
+ );
6106
+
6107
+ // src/ai-surface/service.ts
6108
+ var DEFAULT_LIMITS = {
6109
+ maxListLimit: 100,
6110
+ maxSearchScan: 500,
6111
+ maxSearchResults: 20,
6112
+ maxContextResources: 12,
6113
+ maxCharactersPerResource: 12e3,
6114
+ maxJsonCharacters: 24e3,
6115
+ maxCanvasObjects: 200,
6116
+ maxDatabaseRows: 100
6117
+ };
6118
+ var BUILT_IN_RESOURCE_ROUTES = createBuiltInResourceRouter();
6119
+ var AiSurfaceService = class {
6120
+ constructor(config) {
6121
+ this.config = config;
6122
+ this.limits = { ...DEFAULT_LIMITS, ...config.limits };
6123
+ this.clock = config.clock ?? (() => /* @__PURE__ */ new Date());
6124
+ for (const tool of config.extraTools ?? []) {
6125
+ if (!this.extraTools.has(tool.name)) this.extraTools.set(tool.name, tool);
6126
+ }
6127
+ }
6128
+ limits;
6129
+ clock;
6130
+ sequence = 0;
6131
+ auditEvents = [];
6132
+ rollbackSnapshots = /* @__PURE__ */ new Map();
6133
+ /** Contributed tools by name (exploration 0196), de-duped at construction. */
6134
+ extraTools = /* @__PURE__ */ new Map();
6135
+ /**
6136
+ * The narrow surface handed to built-in tool handlers and resource routes
6137
+ * (`tools/`, `resources/`): closures over the private methods, so the class
6138
+ * stays the facade and its public type is unchanged. The closures are lazy —
6139
+ * nothing here runs until a tool call or resource read arrives.
6140
+ */
6141
+ host = {
6142
+ search: (options) => this.search(options),
6143
+ expandGraph: (options) => this.expandGraph(options),
6144
+ createContextPack: (options) => this.createContextPack(options),
6145
+ createExternalContextResource: (options) => this.createExternalContextResource(options),
6146
+ getWorkspaceSummary: () => this.getWorkspaceSummary(),
6147
+ getRecentNodes: () => this.getRecentNodes(),
6148
+ listNodes: async () => {
6149
+ const nodes = await this.config.store.list({ limit: this.limits.maxListLimit });
6150
+ return { nodes, count: nodes.length, limit: this.limits.maxListLimit };
6151
+ },
6152
+ listSchemas: async () => ({ schemas: await this.getSchemaSummaries(true) }),
6153
+ getNodeOrThrow: (id) => this.getNodeOrThrow(id),
6154
+ getNodeProjection: (id) => this.getNodeProjection(id),
6155
+ readPageMarkdown: (pageId, includeFrontmatter, uri) => this.readPageMarkdown(pageId, includeFrontmatter, uri),
6156
+ readPageOutline: (pageId) => this.readPageOutline(pageId),
6157
+ planPagePatch: (args) => this.planPagePatch(args),
6158
+ applyPageMarkdown: (args) => this.applyPageMarkdown(args),
6159
+ rollbackPageMarkdown: (args) => this.rollbackPageMarkdown(args),
6160
+ getAuditLog: (options) => this.getAuditLog(options),
6161
+ describeDatabase: (databaseId, options) => this.describeDatabase(databaseId, options),
6162
+ readDatabaseViews: (databaseId) => this.readDatabaseViews(databaseId),
6163
+ queryDatabase: (options) => this.queryDatabase(options),
6164
+ sampleDatabase: (options) => this.sampleDatabase(options),
6165
+ explainDatabaseQuery: (options) => this.explainDatabaseQuery(options),
6166
+ planDatabaseMutation: (args) => this.planDatabaseMutation(args),
6167
+ applyDatabaseMutation: (args) => this.applyDatabaseMutation(args),
6168
+ listCanvases: (options) => this.listCanvases(options),
6169
+ readCanvasViewport: (options) => this.readCanvasViewport(options),
6170
+ readCanvasObjects: (canvasId) => this.readCanvasObjects(canvasId),
6171
+ readCanvasSelection: (options) => this.readCanvasSelection(options),
6172
+ searchCanvas: (options) => this.searchCanvas(options),
6173
+ exportCanvasJsonCanvas: (options) => this.exportCanvasJsonCanvas(options),
6174
+ readCanvasObject: (canvasId, objectId) => this.readCanvasObject(canvasId, objectId),
6175
+ planCanvasJsonCanvasImport: (args) => this.planCanvasJsonCanvasImport(args),
6176
+ planCanvasMutation: (args) => this.planCanvasMutation(args),
6177
+ jsonResource: (uri, value) => this.jsonResource(uri, value)
6178
+ };
6179
+ /** The contributed (non-built-in) tools, with built-in name collisions removed. */
6180
+ getExtraTools() {
6181
+ const builtIn = new Set(this.builtInTools().map((t) => t.name));
6182
+ return [...this.extraTools.values()].filter((t) => !builtIn.has(t.name));
6183
+ }
6184
+ getResources() {
6185
+ return [
6186
+ createResource(
6187
+ "xnet://workspace/summary",
6188
+ "Workspace Summary",
6189
+ "High-level schema counts, recent nodes, and AI surface capabilities.",
6190
+ "application/json",
6191
+ "low",
6192
+ ["workspace.read"]
6193
+ ),
6194
+ createResource(
6195
+ "xnet://workspace/recent",
6196
+ "Recent Workspace Nodes",
6197
+ "Recent nodes with ids, schemas, titles, and revisions.",
6198
+ "application/json",
6199
+ "low",
6200
+ ["workspace.read"]
6201
+ ),
6202
+ createResource(
6203
+ "xnet://nodes",
6204
+ "All Nodes",
6205
+ "Limited list of nodes in the local store.",
6206
+ "application/json",
6207
+ "low",
6208
+ ["workspace.read"]
6209
+ ),
6210
+ createResource(
6211
+ "xnet://schemas",
6212
+ "All Schemas",
6213
+ "List of available schemas and property metadata.",
6214
+ "application/json",
6215
+ "low",
6216
+ ["workspace.read"]
6217
+ ),
6218
+ createResource(
6219
+ "xnet://page/{pageId}.md",
6220
+ "Page Markdown",
6221
+ "Markdown projection for a page, including xNet frontmatter identity.",
6222
+ "text/markdown",
6223
+ "low",
6224
+ ["page.read"],
6225
+ true
6226
+ ),
6227
+ createResource(
6228
+ "xnet://page/{pageId}/outline",
6229
+ "Page Outline",
6230
+ "Page heading outline extracted from the Markdown projection.",
6231
+ "application/json",
6232
+ "low",
6233
+ ["page.read"],
6234
+ true
6235
+ ),
6236
+ createResource(
6237
+ "xnet://database/{databaseId}/schema",
6238
+ "Database Schema",
6239
+ "Database node metadata, declared properties, and known column descriptors.",
6240
+ "application/json",
6241
+ "low",
6242
+ ["database.read"],
6243
+ true
6244
+ ),
6245
+ createResource(
6246
+ "xnet://database/{databaseId}/views",
6247
+ "Database Views",
6248
+ "Known database view descriptors from the database node projection.",
6249
+ "application/json",
6250
+ "low",
6251
+ ["database.read"],
6252
+ true
6253
+ ),
6254
+ createResource(
6255
+ "xnet://database/{databaseId}/sample?limit=10",
6256
+ "Database Sample Rows",
6257
+ "Bounded sample of rows for a database using NodeQueryDescriptor semantics.",
6258
+ "application/json",
6259
+ "low",
6260
+ ["database.read", "database.query"],
6261
+ true
6262
+ ),
6263
+ createResource(
6264
+ "xnet://canvas/{canvasId}/viewport?x=0&y=0&w=1000&h=800",
6265
+ "Canvas Viewport",
6266
+ "Viewport-scoped canvas objects and edges.",
6267
+ "application/json",
6268
+ "low",
6269
+ ["canvas.read"],
6270
+ true
6271
+ ),
6272
+ createResource(
6273
+ "xnet://canvas/{canvasId}/objects",
6274
+ "Canvas Objects",
6275
+ "Bounded list of canvas objects and connectors.",
6276
+ "application/json",
6277
+ "low",
6278
+ ["canvas.read"],
6279
+ true
6280
+ ),
6281
+ createResource(
6282
+ "xnet://canvas/{canvasId}/selection?ids=object-1,object-2",
6283
+ "Canvas Selection",
6284
+ "Selection-scoped canvas objects, edges, and source previews.",
6285
+ "application/json",
6286
+ "low",
6287
+ ["canvas.read"],
6288
+ true
6289
+ ),
6290
+ createResource(
6291
+ "xnet://canvas/{canvasId}/json-canvas",
6292
+ "Canvas JSON Canvas",
6293
+ "JSON Canvas projection with xNet source metadata sidecars.",
6294
+ "application/json",
6295
+ "low",
6296
+ ["canvas.read"],
6297
+ true
6298
+ )
5788
6299
  ];
5789
6300
  }
6301
+ /** The full tool surface: built-in `xnet_*` tools plus contributed extras. */
6302
+ getTools() {
6303
+ const extras = this.getExtraTools().map(
6304
+ ({ invoke: _invoke, ...def }) => def
6305
+ );
6306
+ return [...this.builtInTools(), ...extras];
6307
+ }
6308
+ builtInTools() {
6309
+ return BUILT_IN_TOOL_ENTRIES.map((entry) => entry.definition);
6310
+ }
6311
+ /**
6312
+ * Dispatch a tool call: the built-in registry first (a built-in `xnet_*`
6313
+ * name always wins), then contributed tools (plugin/connector `agentTools`,
6314
+ * exploration 0196).
6315
+ */
5790
6316
  async callTool(name, args = {}) {
5791
- switch (name) {
5792
- case "xnet_search":
5793
- return await this.search({
5794
- query: readRequiredString(args, "query"),
5795
- schemaId: readOptionalString(args, "schemaId") ?? readOptionalString(args, "schema"),
5796
- limit: readOptionalNumber(args, "limit"),
5797
- offset: readOptionalNumber(args, "offset")
5798
- });
5799
- case "xnet_graph_expand":
5800
- return await this.expandGraph({
5801
- nodeId: readRequiredString(args, "nodeId"),
5802
- hops: readOptionalNumber(args, "hops"),
5803
- limit: readOptionalNumber(args, "limit")
5804
- });
5805
- case "xnet_create_context_pack":
5806
- return await this.createContextPack({
5807
- query: readOptionalString(args, "query"),
5808
- seeds: readContextSeeds(args.seeds),
5809
- limit: readOptionalNumber(args, "limit")
5810
- });
5811
- case "xnet_create_external_context_resource":
5812
- return this.createExternalContextResource({
5813
- url: readRequiredString(args, "url"),
5814
- text: readRequiredString(args, "text"),
5815
- mimeType: readOptionalString(args, "mimeType")
5816
- });
5817
- case "xnet_read_page_markdown": {
5818
- const content = await this.readPageMarkdown(
5819
- readRequiredString(args, "pageId"),
5820
- readOptionalBoolean(args, "includeFrontmatter") ?? true
5821
- );
5822
- return { markdown: content.text, mimeType: content.mimeType, uri: content.uri };
5823
- }
5824
- case "xnet_validate_page_markdown": {
5825
- const pageId = readOptionalString(args, "pageId");
5826
- const node = pageId ? await this.getNodeOrThrow(pageId) : null;
5827
- return validateXNetPageMarkdown(readRequiredString(args, "markdown"), {
5828
- pageId,
5829
- schemaId: node?.schemaId,
5830
- baseRevision: readOptionalString(args, "baseRevision")
5831
- });
5832
- }
5833
- case "xnet_plan_page_patch":
5834
- return await this.planPagePatch(args);
5835
- case "xnet_apply_page_markdown":
5836
- return await this.applyPageMarkdown(args);
5837
- case "xnet_get_audit_log":
5838
- return this.getAuditLog({
5839
- planId: readOptionalString(args, "planId"),
5840
- limit: readOptionalNumber(args, "limit")
5841
- });
5842
- case "xnet_rollback_page_markdown":
5843
- return await this.rollbackPageMarkdown(args);
5844
- case "xnet_database_describe":
5845
- return await this.describeDatabase(readRequiredString(args, "databaseId"), {
5846
- includeSample: readOptionalBoolean(args, "includeSample") ?? false
5847
- });
5848
- case "xnet_database_query":
5849
- return await this.queryDatabase({
5850
- databaseId: readRequiredString(args, "databaseId"),
5851
- schemaId: readOptionalString(args, "schemaId"),
5852
- descriptor: readOptionalRecord(args, "descriptor"),
5853
- where: readOptionalRecord(args, "where"),
5854
- search: args.search,
5855
- orderBy: readOptionalRecord(args, "orderBy"),
5856
- materializedView: args.materializedView,
5857
- count: readOptionalString(args, "count"),
5858
- limit: readOptionalNumber(args, "limit"),
5859
- offset: readOptionalNumber(args, "offset")
5860
- });
5861
- case "xnet_database_sample":
5862
- return await this.sampleDatabase({
5863
- databaseId: readRequiredString(args, "databaseId"),
5864
- schemaId: readOptionalString(args, "schemaId"),
5865
- descriptor: readOptionalRecord(args, "descriptor"),
5866
- sampleSize: readOptionalNumber(args, "sampleSize")
5867
- });
5868
- case "xnet_database_explain_query":
5869
- return await this.explainDatabaseQuery({
5870
- databaseId: readRequiredString(args, "databaseId"),
5871
- schemaId: readOptionalString(args, "schemaId"),
5872
- descriptor: readOptionalRecord(args, "descriptor"),
5873
- limit: readOptionalNumber(args, "limit"),
5874
- offset: readOptionalNumber(args, "offset")
5875
- });
5876
- case "xnet_plan_database_mutation":
5877
- return await this.planDatabaseMutation(args);
5878
- case "xnet_apply_database_mutation":
5879
- return await this.applyDatabaseMutation(args);
5880
- case "xnet_canvas_list":
5881
- return await this.listCanvases({
5882
- limit: readOptionalNumber(args, "limit"),
5883
- offset: readOptionalNumber(args, "offset")
5884
- });
5885
- case "xnet_canvas_read_viewport":
5886
- return await this.readCanvasViewport({
5887
- canvasId: readRequiredString(args, "canvasId"),
5888
- x: readOptionalNumber(args, "x"),
5889
- y: readOptionalNumber(args, "y"),
5890
- w: readOptionalNumber(args, "w"),
5891
- h: readOptionalNumber(args, "h"),
5892
- tileSize: readOptionalNumber(args, "tileSize"),
5893
- tileIds: readStringArray(args.tileIds),
5894
- includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
5895
- });
5896
- case "xnet_canvas_read_selection":
5897
- return await this.readCanvasSelection({
5898
- canvasId: readRequiredString(args, "canvasId"),
5899
- objectIds: readRequiredStringArray(args.objectIds, "objectIds"),
5900
- includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
5901
- });
5902
- case "xnet_canvas_search":
5903
- return await this.searchCanvas({
5904
- canvasId: readRequiredString(args, "canvasId"),
5905
- query: readRequiredString(args, "query"),
5906
- limit: readOptionalNumber(args, "limit")
5907
- });
5908
- case "xnet_canvas_export_json_canvas":
5909
- return await this.exportCanvasJsonCanvas({
5910
- canvasId: readRequiredString(args, "canvasId"),
5911
- includeXNetMetadata: readOptionalBoolean(args, "includeXNetMetadata") ?? true,
5912
- x: readOptionalNumber(args, "x"),
5913
- y: readOptionalNumber(args, "y"),
5914
- w: readOptionalNumber(args, "w"),
5915
- h: readOptionalNumber(args, "h")
5916
- });
5917
- case "xnet_canvas_plan_json_canvas_import":
5918
- return await this.planCanvasJsonCanvasImport(args);
5919
- case "xnet_plan_canvas_mutation":
5920
- return await this.planCanvasMutation(args);
5921
- case "xnet_validate_mutation_plan": {
5922
- const validation = validateAiMutationPlan(args.plan);
5923
- return { validation };
5924
- }
5925
- default: {
5926
- const extra = this.extraTools.get(name);
5927
- if (extra) return await extra.invoke(args);
5928
- throw new Error(`Unknown AI surface tool: ${name}`);
5929
- }
5930
- }
6317
+ const builtIn = BUILT_IN_TOOLS_BY_NAME.get(name);
6318
+ if (builtIn) return await builtIn.execute(this.host, args);
6319
+ const extra = this.extraTools.get(name);
6320
+ if (extra) return await extra.invoke(args);
6321
+ throw new Error(`Unknown AI surface tool: ${name}`);
5931
6322
  }
5932
6323
  async readResource(uri) {
5933
- const parsed = parseXNetUri(uri);
5934
- if (uri === "xnet://nodes") {
5935
- const nodes = await this.config.store.list({ limit: this.limits.maxListLimit });
5936
- return this.jsonResource(uri, { nodes, count: nodes.length, limit: this.limits.maxListLimit });
5937
- }
5938
- if (uri === "xnet://schemas") {
5939
- return this.jsonResource(uri, { schemas: await this.getSchemaSummaries(true) });
5940
- }
5941
- if (parsed.host === "workspace" && parsed.parts[0] === "summary") {
5942
- return this.jsonResource(uri, await this.getWorkspaceSummary());
5943
- }
5944
- if (parsed.host === "workspace" && parsed.parts[0] === "recent") {
5945
- return this.jsonResource(uri, await this.getRecentNodes());
5946
- }
5947
- if (parsed.host === "workspace" && parsed.parts[0] === "search") {
5948
- return this.jsonResource(
5949
- uri,
5950
- await this.search({
5951
- query: parsed.searchParams.get("q") ?? "",
5952
- schemaId: parsed.searchParams.get("schema") ?? void 0,
5953
- limit: readUrlNumber(parsed.searchParams, "limit"),
5954
- offset: readUrlNumber(parsed.searchParams, "offset")
5955
- })
5956
- );
5957
- }
5958
- if (parsed.host === "node" && parsed.parts[0]) {
5959
- return this.jsonResource(uri, await this.getNodeProjection(parsed.parts[0]));
5960
- }
5961
- if (parsed.host === "page" && parsed.parts[0]) {
5962
- const pageId = parsed.parts[0].endsWith(".md") ? parsed.parts[0].slice(0, -".md".length) : parsed.parts[0];
5963
- if (parsed.parts.length === 1 || parsed.parts[0].endsWith(".md")) {
5964
- return await this.readPageMarkdown(pageId, true, uri);
5965
- }
5966
- if (parsed.parts[1] === "outline") {
5967
- return this.jsonResource(uri, await this.readPageOutline(pageId));
5968
- }
5969
- if (parsed.parts[1] === "context-pack") {
5970
- return this.jsonResource(
5971
- uri,
5972
- await this.createContextPack({ seeds: [{ kind: "page", id: pageId }] })
5973
- );
5974
- }
5975
- }
5976
- if (parsed.host === "database" && parsed.parts[0]) {
5977
- const databaseId = parsed.parts[0];
5978
- if (parsed.parts[1] === "schema") {
5979
- return this.jsonResource(uri, await this.describeDatabase(databaseId));
5980
- }
5981
- if (parsed.parts[1] === "views") {
5982
- return this.jsonResource(uri, await this.readDatabaseViews(databaseId));
5983
- }
5984
- if (parsed.parts[1] === "sample") {
5985
- return this.jsonResource(
5986
- uri,
5987
- await this.sampleDatabase({
5988
- databaseId,
5989
- sampleSize: readUrlNumber(parsed.searchParams, "limit")
5990
- })
5991
- );
5992
- }
5993
- if (parsed.parts[1] === "query") {
5994
- return this.jsonResource(
5995
- uri,
5996
- await this.queryDatabase({
5997
- databaseId,
5998
- schemaId: parsed.searchParams.get("schema") ?? void 0,
5999
- search: parsed.searchParams.get("q") ?? void 0,
6000
- materializedView: parsed.searchParams.get("view") ? { viewId: parsed.searchParams.get("view") ?? "" } : void 0,
6001
- limit: readUrlNumber(parsed.searchParams, "limit"),
6002
- offset: readUrlNumber(parsed.searchParams, "offset")
6003
- })
6004
- );
6005
- }
6006
- }
6007
- if (parsed.host === "canvas" && parsed.parts[0]) {
6008
- const canvasId = parsed.parts[0];
6009
- if (parsed.parts[1] === "viewport") {
6010
- return this.jsonResource(
6011
- uri,
6012
- await this.readCanvasViewport({
6013
- canvasId,
6014
- x: readUrlNumber(parsed.searchParams, "x"),
6015
- y: readUrlNumber(parsed.searchParams, "y"),
6016
- w: readUrlNumber(parsed.searchParams, "w"),
6017
- h: readUrlNumber(parsed.searchParams, "h"),
6018
- tileSize: readUrlNumber(parsed.searchParams, "tileSize"),
6019
- tileIds: readCsvStringArray(parsed.searchParams.get("tileIds")),
6020
- includeSourcePreviews: parsed.searchParams.get("includeSourcePreviews") === "true"
6021
- })
6022
- );
6023
- }
6024
- if (parsed.parts[1] === "objects") {
6025
- return this.jsonResource(uri, await this.readCanvasObjects(canvasId));
6026
- }
6027
- if (parsed.parts[1] === "selection") {
6028
- return this.jsonResource(
6029
- uri,
6030
- await this.readCanvasSelection({
6031
- canvasId,
6032
- objectIds: readCsvStringArray(parsed.searchParams.get("ids")),
6033
- includeSourcePreviews: parsed.searchParams.get("includeSourcePreviews") !== "false"
6034
- })
6035
- );
6036
- }
6037
- if (parsed.parts[1] === "json-canvas") {
6038
- return this.jsonResource(
6039
- uri,
6040
- await this.exportCanvasJsonCanvas({
6041
- canvasId,
6042
- includeXNetMetadata: parsed.searchParams.get("includeXNetMetadata") !== "false"
6043
- })
6044
- );
6045
- }
6046
- if (parsed.parts[1] === "object" && parsed.parts[2]) {
6047
- return this.jsonResource(uri, await this.readCanvasObject(canvasId, parsed.parts[2]));
6048
- }
6049
- }
6050
- throw new Error(`Resource not found: ${uri}`);
6324
+ return await BUILT_IN_RESOURCE_ROUTES.resolve(this.host, uri);
6051
6325
  }
6052
6326
  toJsonText(value, format = "concise") {
6053
6327
  return this.stringifyJson(value, format);
@@ -6144,7 +6418,7 @@ var AiSurfaceService = class {
6144
6418
  const results = Array.isArray(search.results) ? search.results : [];
6145
6419
  const ids = [];
6146
6420
  for (const result of results) {
6147
- if (isRecord4(result) && typeof result.id === "string") ids.push(result.id);
6421
+ if (isRecord3(result) && typeof result.id === "string") ids.push(result.id);
6148
6422
  }
6149
6423
  return ids;
6150
6424
  }
@@ -6786,8 +7060,8 @@ var AiSurfaceService = class {
6786
7060
  h: options.h,
6787
7061
  includeSourcePreviews: false
6788
7062
  });
6789
- const objects = Array.isArray(viewport.objects) ? viewport.objects.filter(isRecord4) : [];
6790
- const edges = Array.isArray(viewport.edges) ? viewport.edges.filter(isRecord4) : [];
7063
+ const objects = Array.isArray(viewport.objects) ? viewport.objects.filter(isRecord3) : [];
7064
+ const edges = Array.isArray(viewport.edges) ? viewport.edges.filter(isRecord3) : [];
6791
7065
  return {
6792
7066
  canvasId: options.canvasId,
6793
7067
  revision: viewport.revision,
@@ -6829,7 +7103,7 @@ var AiSurfaceService = class {
6829
7103
  });
6830
7104
  const objects = Array.isArray(viewport.objects) ? viewport.objects : [];
6831
7105
  const object = objects.find(
6832
- (candidate) => isRecord4(candidate) && readRecordString(candidate, "id") === objectId
7106
+ (candidate) => isRecord3(candidate) && readRecordString(candidate, "id") === objectId
6833
7107
  );
6834
7108
  if (!object) throw new Error(`Canvas object not found: ${objectId}`);
6835
7109
  return {
@@ -7305,7 +7579,7 @@ function invalidPageApplyResult(plan, validation) {
7305
7579
  return {
7306
7580
  applied: false,
7307
7581
  pageId: "unknown",
7308
- planId: isRecord4(plan) && typeof plan.id === "string" ? plan.id : "unknown",
7582
+ planId: isRecord3(plan) && typeof plan.id === "string" ? plan.id : "unknown",
7309
7583
  mode: "node-property",
7310
7584
  baseRevision: "unknown",
7311
7585
  liveRevision: "unknown",
@@ -7328,7 +7602,7 @@ function rejectedPageApplyResult(input) {
7328
7602
  };
7329
7603
  }
7330
7604
  function isMutationPlan(value) {
7331
- return isRecord4(value) && typeof value.id === "string" && typeof value.actor === "string" && typeof value.intent === "string" && Array.isArray(value.changes) && isRecord4(value.validation);
7605
+ return isRecord3(value) && typeof value.id === "string" && typeof value.actor === "string" && typeof value.intent === "string" && Array.isArray(value.changes) && isRecord3(value.validation);
7332
7606
  }
7333
7607
  function createResource(uri, name, description, mimeType, risk, requiredScopes, dynamic = false) {
7334
7608
  return {
@@ -7341,22 +7615,6 @@ function createResource(uri, name, description, mimeType, risk, requiredScopes,
7341
7615
  dynamic
7342
7616
  };
7343
7617
  }
7344
- function parseXNetUri(uri) {
7345
- let parsed;
7346
- try {
7347
- parsed = new URL(uri);
7348
- } catch {
7349
- throw new Error(`Invalid xNet resource URI: ${uri}`);
7350
- }
7351
- if (parsed.protocol !== "xnet:") {
7352
- throw new Error(`Invalid xNet resource URI: ${uri}`);
7353
- }
7354
- return {
7355
- host: parsed.hostname,
7356
- parts: parsed.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part)),
7357
- searchParams: parsed.searchParams
7358
- };
7359
- }
7360
7618
  function renderPageMarkdown(node, includeFrontmatter, exportedAt) {
7361
7619
  const title = readStringProperty(node, "title") ?? "Untitled Page";
7362
7620
  const body = readMarkdownBody(node);
@@ -7383,7 +7641,7 @@ function readMarkdownBody(node) {
7383
7641
  const value = readStringProperty(node, "markdown") ?? readStringProperty(node, "content") ?? readStringProperty(node, "body") ?? readStringProperty(node, "text") ?? readStringProperty(node, "description");
7384
7642
  if (value) return value;
7385
7643
  const richContent = node.properties.content;
7386
- if (isRecord4(richContent)) {
7644
+ if (isRecord3(richContent)) {
7387
7645
  return `\`\`\`json
7388
7646
  ${JSON.stringify(richContent, null, 2)}
7389
7647
  \`\`\``;
@@ -7444,7 +7702,7 @@ function relationFieldNames(schema) {
7444
7702
  if (!schema) return [];
7445
7703
  const fields = [];
7446
7704
  for (const [name, definition] of Object.entries(schema.properties)) {
7447
- if (isRecord4(definition) && definition.type === "relation") fields.push(name);
7705
+ if (isRecord3(definition) && definition.type === "relation") fields.push(name);
7448
7706
  }
7449
7707
  return fields;
7450
7708
  }
@@ -7474,7 +7732,7 @@ function readArrayProperty(node, property) {
7474
7732
  }
7475
7733
  function readNestedArrayProperty(node, objectProperty, arrayProperty) {
7476
7734
  const value = node.properties[objectProperty];
7477
- return isRecord4(value) && Array.isArray(value[arrayProperty]) ? value[arrayProperty] : void 0;
7735
+ return isRecord3(value) && Array.isArray(value[arrayProperty]) ? value[arrayProperty] : void 0;
7478
7736
  }
7479
7737
  function belongsToDatabase(node, databaseId) {
7480
7738
  return readStringProperty(node, "databaseId") === databaseId || readStringProperty(node, "database") === databaseId || readStringProperty(node, "parentDatabaseId") === databaseId;
@@ -7533,7 +7791,7 @@ function normalizeQuerySearch(value) {
7533
7791
  const text3 = value.trim();
7534
7792
  return text3 ? { text: text3 } : void 0;
7535
7793
  }
7536
- if (!isRecord4(value) || typeof value.text !== "string" || !value.text.trim()) {
7794
+ if (!isRecord3(value) || typeof value.text !== "string" || !value.text.trim()) {
7537
7795
  return void 0;
7538
7796
  }
7539
7797
  const fields = Array.isArray(value.fields) ? value.fields.filter(
@@ -7549,7 +7807,7 @@ function normalizeQueryMaterializedView(value) {
7549
7807
  const viewId2 = value.trim();
7550
7808
  return viewId2 ? { viewId: viewId2 } : void 0;
7551
7809
  }
7552
- if (!isRecord4(value)) return void 0;
7810
+ if (!isRecord3(value)) return void 0;
7553
7811
  const viewId = readRecordString(value, "viewId");
7554
7812
  if (!viewId) return void 0;
7555
7813
  const maxAgeMs = readRecordNumber(value, "maxAgeMs");
@@ -7822,7 +8080,7 @@ function invalidDatabaseApplyResult(plan, validation) {
7822
8080
  return {
7823
8081
  applied: false,
7824
8082
  databaseId: "unknown",
7825
- planId: isRecord4(plan) && typeof plan.id === "string" ? plan.id : "unknown",
8083
+ planId: isRecord3(plan) && typeof plan.id === "string" ? plan.id : "unknown",
7826
8084
  baseRevision: "unknown",
7827
8085
  liveRevision: "unknown",
7828
8086
  appliedChangeIds: [],
@@ -7840,7 +8098,7 @@ function readTransactionOperations(operation) {
7840
8098
  );
7841
8099
  }
7842
8100
  function readTransactionOperation(value, path) {
7843
- if (!isRecord4(value)) throw new Error(`${path} must be an object`);
8101
+ if (!isRecord3(value)) throw new Error(`${path} must be an object`);
7844
8102
  if (value.type === "create") {
7845
8103
  const options = readRecord(value, "options");
7846
8104
  const schemaId = options ? readRecordString(options, "schemaId") : void 0;
@@ -7933,15 +8191,15 @@ function databaseEntityMatchesId(entity, id) {
7933
8191
  }
7934
8192
  function readRecordArrayProperty(record, key) {
7935
8193
  const value = record[key];
7936
- return Array.isArray(value) ? value.filter(isRecord4) : [];
8194
+ return Array.isArray(value) ? value.filter(isRecord3) : [];
7937
8195
  }
7938
8196
  function isCanvasNode(node) {
7939
8197
  return node.schemaId.includes("/Canvas") || node.schemaId.includes("Canvas@") || Array.isArray(node.properties.objects) || Array.isArray(node.properties.nodes);
7940
8198
  }
7941
8199
  function readCanvasScene(canvas) {
7942
8200
  return {
7943
- objects: (readArrayProperty(canvas, "objects") ?? readArrayProperty(canvas, "nodes") ?? []).filter(isRecord4).map(normalizeCanvasObject),
7944
- edges: (readArrayProperty(canvas, "edges") ?? readArrayProperty(canvas, "connectors") ?? []).filter(isRecord4).map(normalizeCanvasEdge)
8201
+ objects: (readArrayProperty(canvas, "objects") ?? readArrayProperty(canvas, "nodes") ?? []).filter(isRecord3).map(normalizeCanvasObject),
8202
+ edges: (readArrayProperty(canvas, "edges") ?? readArrayProperty(canvas, "connectors") ?? []).filter(isRecord3).map(normalizeCanvasEdge)
7945
8203
  };
7946
8204
  }
7947
8205
  function normalizeCanvasObject(object) {
@@ -8084,8 +8342,8 @@ function canvasObjectFile(object) {
8084
8342
  return readRecordString(object, "file") ?? readRecordString(object, "filePath") ?? readRecordString(properties ?? {}, "file") ?? readRecordString(properties ?? {}, "filePath");
8085
8343
  }
8086
8344
  function jsonCanvasDocumentToCanvasOperations(document) {
8087
- const nodes = Array.isArray(document.nodes) ? document.nodes.filter(isRecord4) : [];
8088
- const edges = Array.isArray(document.edges) ? document.edges.filter(isRecord4) : [];
8345
+ const nodes = Array.isArray(document.nodes) ? document.nodes.filter(isRecord3) : [];
8346
+ const edges = Array.isArray(document.edges) ? document.edges.filter(isRecord3) : [];
8089
8347
  const nodeIds = new Set(nodes.map((node) => readRecordString(node, "id")).filter(Boolean));
8090
8348
  const warnings = edges.flatMap((edge) => {
8091
8349
  const fromNode = readRecordString(edge, "fromNode");
@@ -8317,10 +8575,10 @@ function readOperations(value) {
8317
8575
  throw new Error("operations must contain at least one operation");
8318
8576
  }
8319
8577
  return value.map((operation, index) => {
8320
- if (!isRecord4(operation) || typeof operation.op !== "string") {
8578
+ if (!isRecord3(operation) || typeof operation.op !== "string") {
8321
8579
  throw new Error(`operations[${index}].op must be a string`);
8322
8580
  }
8323
- const args = isRecord4(operation.args) ? operation.args : {};
8581
+ const args = isRecord3(operation.args) ? operation.args : {};
8324
8582
  return createAiOperation(
8325
8583
  operation.op,
8326
8584
  args,
@@ -8328,15 +8586,6 @@ function readOperations(value) {
8328
8586
  );
8329
8587
  });
8330
8588
  }
8331
- function readContextSeeds(value) {
8332
- if (!Array.isArray(value)) return [];
8333
- return value.map((seed) => {
8334
- if (!isRecord4(seed)) return null;
8335
- const kind = typeof seed.kind === "string" ? seed.kind : null;
8336
- const id = typeof seed.id === "string" ? seed.id : null;
8337
- return kind && id ? { kind, id } : null;
8338
- }).filter((seed) => seed !== null);
8339
- }
8340
8589
  function uriForSeed(seed) {
8341
8590
  switch (seed.kind) {
8342
8591
  case "node":
@@ -8396,75 +8645,6 @@ function stringifyTruncatedJson(text3, maxCharacters) {
8396
8645
  function quoteYaml(value) {
8397
8646
  return JSON.stringify(value);
8398
8647
  }
8399
- function readRequiredString(record, key) {
8400
- const value = record[key];
8401
- if (typeof value !== "string" || !value.trim()) {
8402
- throw new Error(`${key} must be a non-empty string`);
8403
- }
8404
- return value;
8405
- }
8406
- function readRequiredRecord(record, key) {
8407
- const value = record[key];
8408
- if (!isRecord4(value)) {
8409
- throw new Error(`${key} must be an object`);
8410
- }
8411
- return value;
8412
- }
8413
- function readRequiredStringArray(value, key) {
8414
- const result = readStringArray(value);
8415
- if (result.length === 0) {
8416
- throw new Error(`${key} must contain at least one string`);
8417
- }
8418
- return result;
8419
- }
8420
- function readStringArray(value) {
8421
- if (!Array.isArray(value)) return [];
8422
- return value.filter((item) => typeof item === "string" && item.trim() !== "");
8423
- }
8424
- function readCsvStringArray(value) {
8425
- if (!value) return [];
8426
- return value.split(",").map((item) => item.trim()).filter(Boolean);
8427
- }
8428
- function readOptionalString(record, key) {
8429
- const value = record[key];
8430
- return typeof value === "string" && value.trim() ? value : void 0;
8431
- }
8432
- function readOptionalRecord(record, key) {
8433
- return readRecord(record, key);
8434
- }
8435
- function readOptionalNumber(record, key) {
8436
- const value = record[key];
8437
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8438
- }
8439
- function readOptionalBoolean(record, key) {
8440
- const value = record[key];
8441
- return typeof value === "boolean" ? value : void 0;
8442
- }
8443
- function readUrlNumber(params, key) {
8444
- const value = params.get(key);
8445
- if (value === null) return void 0;
8446
- const parsed = Number(value);
8447
- return Number.isFinite(parsed) ? parsed : void 0;
8448
- }
8449
- function readRecordString(record, key) {
8450
- const value = record[key];
8451
- return typeof value === "string" && value.trim() ? value : void 0;
8452
- }
8453
- function readRecord(record, key) {
8454
- const value = record[key];
8455
- return isRecord4(value) ? value : void 0;
8456
- }
8457
- function readRecordNumber(record, key) {
8458
- const value = record[key];
8459
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8460
- }
8461
- function readRecordBoolean(record, key) {
8462
- const value = record[key];
8463
- return typeof value === "boolean" ? value : void 0;
8464
- }
8465
- function isRecord4(value) {
8466
- return typeof value === "object" && value !== null && !Array.isArray(value);
8467
- }
8468
8648
 
8469
8649
  // src/sandbox/ast-validator.ts
8470
8650
  import * as acorn from "acorn";