@xnetjs/plugins 0.2.0 → 0.3.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.d.ts +12 -0
- package/dist/index.js +1146 -966
- package/dist/services/node.d.ts +12 -0
- package/dist/services/node.js +1146 -966
- package/package.json +5 -5
package/dist/services/node.js
CHANGED
|
@@ -180,6 +180,86 @@ function hasWriteScope(plan) {
|
|
|
180
180
|
);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// src/ai-surface/args.ts
|
|
184
|
+
function readRequiredString(record, key) {
|
|
185
|
+
const value = record[key];
|
|
186
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
187
|
+
throw new Error(`${key} must be a non-empty string`);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
function readRequiredRecord(record, key) {
|
|
192
|
+
const value = record[key];
|
|
193
|
+
if (!isRecord2(value)) {
|
|
194
|
+
throw new Error(`${key} must be an object`);
|
|
195
|
+
}
|
|
196
|
+
return value;
|
|
197
|
+
}
|
|
198
|
+
function readRequiredStringArray(value, key) {
|
|
199
|
+
const result = readStringArray(value);
|
|
200
|
+
if (result.length === 0) {
|
|
201
|
+
throw new Error(`${key} must contain at least one string`);
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
function readStringArray(value) {
|
|
206
|
+
if (!Array.isArray(value)) return [];
|
|
207
|
+
return value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
208
|
+
}
|
|
209
|
+
function readCsvStringArray(value) {
|
|
210
|
+
if (!value) return [];
|
|
211
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
212
|
+
}
|
|
213
|
+
function readOptionalString(record, key) {
|
|
214
|
+
const value = record[key];
|
|
215
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
216
|
+
}
|
|
217
|
+
function readOptionalRecord(record, key) {
|
|
218
|
+
return readRecord(record, key);
|
|
219
|
+
}
|
|
220
|
+
function readOptionalNumber(record, key) {
|
|
221
|
+
const value = record[key];
|
|
222
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
223
|
+
}
|
|
224
|
+
function readOptionalBoolean(record, key) {
|
|
225
|
+
const value = record[key];
|
|
226
|
+
return typeof value === "boolean" ? value : void 0;
|
|
227
|
+
}
|
|
228
|
+
function readUrlNumber(params, key) {
|
|
229
|
+
const value = params.get(key);
|
|
230
|
+
if (value === null) return void 0;
|
|
231
|
+
const parsed = Number(value);
|
|
232
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
233
|
+
}
|
|
234
|
+
function readRecordString(record, key) {
|
|
235
|
+
const value = record[key];
|
|
236
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
237
|
+
}
|
|
238
|
+
function readRecord(record, key) {
|
|
239
|
+
const value = record[key];
|
|
240
|
+
return isRecord2(value) ? value : void 0;
|
|
241
|
+
}
|
|
242
|
+
function readRecordNumber(record, key) {
|
|
243
|
+
const value = record[key];
|
|
244
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
245
|
+
}
|
|
246
|
+
function readRecordBoolean(record, key) {
|
|
247
|
+
const value = record[key];
|
|
248
|
+
return typeof value === "boolean" ? value : void 0;
|
|
249
|
+
}
|
|
250
|
+
function isRecord2(value) {
|
|
251
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
252
|
+
}
|
|
253
|
+
function readContextSeeds(value) {
|
|
254
|
+
if (!Array.isArray(value)) return [];
|
|
255
|
+
return value.map((seed) => {
|
|
256
|
+
if (!isRecord2(seed)) return null;
|
|
257
|
+
const kind = typeof seed.kind === "string" ? seed.kind : null;
|
|
258
|
+
const id = typeof seed.id === "string" ? seed.id : null;
|
|
259
|
+
return kind && id ? { kind, id } : null;
|
|
260
|
+
}).filter((seed) => seed !== null);
|
|
261
|
+
}
|
|
262
|
+
|
|
183
263
|
// src/ai-surface/page-markdown.ts
|
|
184
264
|
var XNET_MARKDOWN_DIRECTIVE_SPECS = [
|
|
185
265
|
{
|
|
@@ -415,7 +495,7 @@ function findJsonPayloadEnd(src, startIndex) {
|
|
|
415
495
|
function parseJsonObject(rawPayload, label, errors) {
|
|
416
496
|
try {
|
|
417
497
|
const parsed = JSON.parse(rawPayload);
|
|
418
|
-
if (
|
|
498
|
+
if (isRecord3(parsed)) return parsed;
|
|
419
499
|
errors.push(`${label} payload must be a JSON object`);
|
|
420
500
|
return null;
|
|
421
501
|
} catch {
|
|
@@ -449,7 +529,7 @@ function unquoteYamlScalar(value) {
|
|
|
449
529
|
return trimmed.slice(1, -1);
|
|
450
530
|
}
|
|
451
531
|
}
|
|
452
|
-
function
|
|
532
|
+
function isRecord3(value) {
|
|
453
533
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
454
534
|
}
|
|
455
535
|
function markdownDiffPrefix(kind) {
|
|
@@ -458,900 +538,1094 @@ function markdownDiffPrefix(kind) {
|
|
|
458
538
|
return " ";
|
|
459
539
|
}
|
|
460
540
|
|
|
461
|
-
// src/ai-surface/
|
|
462
|
-
var
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
541
|
+
// src/ai-surface/resources/router.ts
|
|
542
|
+
var XNET_URI_PREFIX = "xnet://";
|
|
543
|
+
var PARAM_SEGMENT_PATTERN = /^\{([a-zA-Z][a-zA-Z0-9]*)\}(.*)$/;
|
|
544
|
+
function createAiResourceRouter() {
|
|
545
|
+
const routes = [];
|
|
546
|
+
const router = {
|
|
547
|
+
register(template, handler) {
|
|
548
|
+
routes.push(compileRoute(template, handler));
|
|
549
|
+
return router;
|
|
550
|
+
},
|
|
551
|
+
async resolve(host, uri) {
|
|
552
|
+
const parsed = parseXNetUri(uri);
|
|
553
|
+
for (const route of routes) {
|
|
554
|
+
const params = matchRoute(route, parsed);
|
|
555
|
+
if (params) {
|
|
556
|
+
return await route.handler(host, { uri, params, searchParams: parsed.searchParams });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
479
560
|
}
|
|
561
|
+
};
|
|
562
|
+
return router;
|
|
563
|
+
}
|
|
564
|
+
function parseXNetUri(uri) {
|
|
565
|
+
let parsed;
|
|
566
|
+
try {
|
|
567
|
+
parsed = new URL(uri);
|
|
568
|
+
} catch {
|
|
569
|
+
throw new Error(`Invalid xNet resource URI: ${uri}`);
|
|
480
570
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
sequence = 0;
|
|
484
|
-
auditEvents = [];
|
|
485
|
-
rollbackSnapshots = /* @__PURE__ */ new Map();
|
|
486
|
-
/** Contributed tools by name (exploration 0196), de-duped at construction. */
|
|
487
|
-
extraTools = /* @__PURE__ */ new Map();
|
|
488
|
-
/** The contributed (non-built-in) tools, with built-in name collisions removed. */
|
|
489
|
-
getExtraTools() {
|
|
490
|
-
const builtIn = new Set(this.builtInTools().map((t) => t.name));
|
|
491
|
-
return [...this.extraTools.values()].filter((t) => !builtIn.has(t.name));
|
|
571
|
+
if (parsed.protocol !== "xnet:") {
|
|
572
|
+
throw new Error(`Invalid xNet resource URI: ${uri}`);
|
|
492
573
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
),
|
|
503
|
-
createResource(
|
|
504
|
-
"xnet://workspace/recent",
|
|
505
|
-
"Recent Workspace Nodes",
|
|
506
|
-
"Recent nodes with ids, schemas, titles, and revisions.",
|
|
507
|
-
"application/json",
|
|
508
|
-
"low",
|
|
509
|
-
["workspace.read"]
|
|
510
|
-
),
|
|
511
|
-
createResource(
|
|
512
|
-
"xnet://nodes",
|
|
513
|
-
"All Nodes",
|
|
514
|
-
"Limited list of nodes in the local store.",
|
|
515
|
-
"application/json",
|
|
516
|
-
"low",
|
|
517
|
-
["workspace.read"]
|
|
518
|
-
),
|
|
519
|
-
createResource(
|
|
520
|
-
"xnet://schemas",
|
|
521
|
-
"All Schemas",
|
|
522
|
-
"List of available schemas and property metadata.",
|
|
523
|
-
"application/json",
|
|
524
|
-
"low",
|
|
525
|
-
["workspace.read"]
|
|
526
|
-
),
|
|
527
|
-
createResource(
|
|
528
|
-
"xnet://page/{pageId}.md",
|
|
529
|
-
"Page Markdown",
|
|
530
|
-
"Markdown projection for a page, including xNet frontmatter identity.",
|
|
531
|
-
"text/markdown",
|
|
532
|
-
"low",
|
|
533
|
-
["page.read"],
|
|
534
|
-
true
|
|
535
|
-
),
|
|
536
|
-
createResource(
|
|
537
|
-
"xnet://page/{pageId}/outline",
|
|
538
|
-
"Page Outline",
|
|
539
|
-
"Page heading outline extracted from the Markdown projection.",
|
|
540
|
-
"application/json",
|
|
541
|
-
"low",
|
|
542
|
-
["page.read"],
|
|
543
|
-
true
|
|
544
|
-
),
|
|
545
|
-
createResource(
|
|
546
|
-
"xnet://database/{databaseId}/schema",
|
|
547
|
-
"Database Schema",
|
|
548
|
-
"Database node metadata, declared properties, and known column descriptors.",
|
|
549
|
-
"application/json",
|
|
550
|
-
"low",
|
|
551
|
-
["database.read"],
|
|
552
|
-
true
|
|
553
|
-
),
|
|
554
|
-
createResource(
|
|
555
|
-
"xnet://database/{databaseId}/views",
|
|
556
|
-
"Database Views",
|
|
557
|
-
"Known database view descriptors from the database node projection.",
|
|
558
|
-
"application/json",
|
|
559
|
-
"low",
|
|
560
|
-
["database.read"],
|
|
561
|
-
true
|
|
562
|
-
),
|
|
563
|
-
createResource(
|
|
564
|
-
"xnet://database/{databaseId}/sample?limit=10",
|
|
565
|
-
"Database Sample Rows",
|
|
566
|
-
"Bounded sample of rows for a database using NodeQueryDescriptor semantics.",
|
|
567
|
-
"application/json",
|
|
568
|
-
"low",
|
|
569
|
-
["database.read", "database.query"],
|
|
570
|
-
true
|
|
571
|
-
),
|
|
572
|
-
createResource(
|
|
573
|
-
"xnet://canvas/{canvasId}/viewport?x=0&y=0&w=1000&h=800",
|
|
574
|
-
"Canvas Viewport",
|
|
575
|
-
"Viewport-scoped canvas objects and edges.",
|
|
576
|
-
"application/json",
|
|
577
|
-
"low",
|
|
578
|
-
["canvas.read"],
|
|
579
|
-
true
|
|
580
|
-
),
|
|
581
|
-
createResource(
|
|
582
|
-
"xnet://canvas/{canvasId}/objects",
|
|
583
|
-
"Canvas Objects",
|
|
584
|
-
"Bounded list of canvas objects and connectors.",
|
|
585
|
-
"application/json",
|
|
586
|
-
"low",
|
|
587
|
-
["canvas.read"],
|
|
588
|
-
true
|
|
589
|
-
),
|
|
590
|
-
createResource(
|
|
591
|
-
"xnet://canvas/{canvasId}/selection?ids=object-1,object-2",
|
|
592
|
-
"Canvas Selection",
|
|
593
|
-
"Selection-scoped canvas objects, edges, and source previews.",
|
|
594
|
-
"application/json",
|
|
595
|
-
"low",
|
|
596
|
-
["canvas.read"],
|
|
597
|
-
true
|
|
598
|
-
),
|
|
599
|
-
createResource(
|
|
600
|
-
"xnet://canvas/{canvasId}/json-canvas",
|
|
601
|
-
"Canvas JSON Canvas",
|
|
602
|
-
"JSON Canvas projection with xNet source metadata sidecars.",
|
|
603
|
-
"application/json",
|
|
604
|
-
"low",
|
|
605
|
-
["canvas.read"],
|
|
606
|
-
true
|
|
607
|
-
)
|
|
608
|
-
];
|
|
574
|
+
return {
|
|
575
|
+
host: parsed.hostname,
|
|
576
|
+
parts: parsed.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part)),
|
|
577
|
+
searchParams: parsed.searchParams
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function compileRoute(template, handler) {
|
|
581
|
+
if (!template.startsWith(XNET_URI_PREFIX)) {
|
|
582
|
+
throw new Error(`Resource route template must start with ${XNET_URI_PREFIX}: ${template}`);
|
|
609
583
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
);
|
|
615
|
-
return [...this.builtInTools(), ...extras];
|
|
584
|
+
const [path] = template.slice(XNET_URI_PREFIX.length).split("?");
|
|
585
|
+
const [host, ...rawSegments] = path.split("/").filter(Boolean);
|
|
586
|
+
if (!host) {
|
|
587
|
+
throw new Error(`Resource route template must include a host: ${template}`);
|
|
616
588
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
589
|
+
return {
|
|
590
|
+
host,
|
|
591
|
+
segments: rawSegments.map((segment) => {
|
|
592
|
+
const param = PARAM_SEGMENT_PATTERN.exec(segment);
|
|
593
|
+
return param ? { kind: "param", name: param[1], suffix: param[2] } : { kind: "literal", value: segment };
|
|
594
|
+
}),
|
|
595
|
+
handler
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function matchRoute(route, parsed) {
|
|
599
|
+
if (parsed.host !== route.host) return null;
|
|
600
|
+
if (parsed.parts.length !== route.segments.length) return null;
|
|
601
|
+
const params = {};
|
|
602
|
+
for (const [index, segment] of route.segments.entries()) {
|
|
603
|
+
const part = parsed.parts[index];
|
|
604
|
+
if (segment.kind === "literal") {
|
|
605
|
+
if (part !== segment.value) return null;
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (segment.suffix && !part.endsWith(segment.suffix)) return null;
|
|
609
|
+
params[segment.name] = segment.suffix ? part.slice(0, -segment.suffix.length) : part;
|
|
610
|
+
}
|
|
611
|
+
return params;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// src/ai-surface/resources/routes.ts
|
|
615
|
+
function createBuiltInResourceRouter() {
|
|
616
|
+
return createAiResourceRouter().register(
|
|
617
|
+
"xnet://nodes",
|
|
618
|
+
async (host, { uri }) => host.jsonResource(uri, await host.listNodes())
|
|
619
|
+
).register(
|
|
620
|
+
"xnet://schemas",
|
|
621
|
+
async (host, { uri }) => host.jsonResource(uri, await host.listSchemas())
|
|
622
|
+
).register(
|
|
623
|
+
"xnet://workspace/summary",
|
|
624
|
+
async (host, { uri }) => host.jsonResource(uri, await host.getWorkspaceSummary())
|
|
625
|
+
).register(
|
|
626
|
+
"xnet://workspace/recent",
|
|
627
|
+
async (host, { uri }) => host.jsonResource(uri, await host.getRecentNodes())
|
|
628
|
+
).register(
|
|
629
|
+
"xnet://workspace/search",
|
|
630
|
+
async (host, { uri, searchParams }) => host.jsonResource(
|
|
631
|
+
uri,
|
|
632
|
+
await host.search({
|
|
633
|
+
query: searchParams.get("q") ?? "",
|
|
634
|
+
schemaId: searchParams.get("schema") ?? void 0,
|
|
635
|
+
limit: readUrlNumber(searchParams, "limit"),
|
|
636
|
+
offset: readUrlNumber(searchParams, "offset")
|
|
637
|
+
})
|
|
638
|
+
)
|
|
639
|
+
).register(
|
|
640
|
+
"xnet://node/{nodeId}",
|
|
641
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.getNodeProjection(params.nodeId))
|
|
642
|
+
).register(
|
|
643
|
+
"xnet://page/{pageId}.md",
|
|
644
|
+
async (host, { uri, params }) => await host.readPageMarkdown(params.pageId, true, uri)
|
|
645
|
+
).register(
|
|
646
|
+
"xnet://page/{pageId}",
|
|
647
|
+
async (host, { uri, params }) => await host.readPageMarkdown(params.pageId, true, uri)
|
|
648
|
+
).register(
|
|
649
|
+
"xnet://page/{pageId}/outline",
|
|
650
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.readPageOutline(params.pageId))
|
|
651
|
+
).register(
|
|
652
|
+
"xnet://page/{pageId}/context-pack",
|
|
653
|
+
async (host, { uri, params }) => host.jsonResource(
|
|
654
|
+
uri,
|
|
655
|
+
await host.createContextPack({ seeds: [{ kind: "page", id: params.pageId }] })
|
|
656
|
+
)
|
|
657
|
+
).register(
|
|
658
|
+
"xnet://database/{databaseId}/schema",
|
|
659
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.describeDatabase(params.databaseId))
|
|
660
|
+
).register(
|
|
661
|
+
"xnet://database/{databaseId}/views",
|
|
662
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.readDatabaseViews(params.databaseId))
|
|
663
|
+
).register(
|
|
664
|
+
"xnet://database/{databaseId}/sample",
|
|
665
|
+
async (host, { uri, params, searchParams }) => host.jsonResource(
|
|
666
|
+
uri,
|
|
667
|
+
await host.sampleDatabase({
|
|
668
|
+
databaseId: params.databaseId,
|
|
669
|
+
sampleSize: readUrlNumber(searchParams, "limit")
|
|
670
|
+
})
|
|
671
|
+
)
|
|
672
|
+
).register(
|
|
673
|
+
"xnet://database/{databaseId}/query",
|
|
674
|
+
async (host, { uri, params, searchParams }) => host.jsonResource(
|
|
675
|
+
uri,
|
|
676
|
+
await host.queryDatabase({
|
|
677
|
+
databaseId: params.databaseId,
|
|
678
|
+
schemaId: searchParams.get("schema") ?? void 0,
|
|
679
|
+
search: searchParams.get("q") ?? void 0,
|
|
680
|
+
materializedView: searchParams.get("view") ? { viewId: searchParams.get("view") ?? "" } : void 0,
|
|
681
|
+
limit: readUrlNumber(searchParams, "limit"),
|
|
682
|
+
offset: readUrlNumber(searchParams, "offset")
|
|
683
|
+
})
|
|
684
|
+
)
|
|
685
|
+
).register(
|
|
686
|
+
"xnet://canvas/{canvasId}/viewport",
|
|
687
|
+
async (host, { uri, params, searchParams }) => host.jsonResource(
|
|
688
|
+
uri,
|
|
689
|
+
await host.readCanvasViewport({
|
|
690
|
+
canvasId: params.canvasId,
|
|
691
|
+
x: readUrlNumber(searchParams, "x"),
|
|
692
|
+
y: readUrlNumber(searchParams, "y"),
|
|
693
|
+
w: readUrlNumber(searchParams, "w"),
|
|
694
|
+
h: readUrlNumber(searchParams, "h"),
|
|
695
|
+
tileSize: readUrlNumber(searchParams, "tileSize"),
|
|
696
|
+
tileIds: readCsvStringArray(searchParams.get("tileIds")),
|
|
697
|
+
includeSourcePreviews: searchParams.get("includeSourcePreviews") === "true"
|
|
698
|
+
})
|
|
699
|
+
)
|
|
700
|
+
).register(
|
|
701
|
+
"xnet://canvas/{canvasId}/objects",
|
|
702
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.readCanvasObjects(params.canvasId))
|
|
703
|
+
).register(
|
|
704
|
+
"xnet://canvas/{canvasId}/selection",
|
|
705
|
+
async (host, { uri, params, searchParams }) => host.jsonResource(
|
|
706
|
+
uri,
|
|
707
|
+
await host.readCanvasSelection({
|
|
708
|
+
canvasId: params.canvasId,
|
|
709
|
+
objectIds: readCsvStringArray(searchParams.get("ids")),
|
|
710
|
+
includeSourcePreviews: searchParams.get("includeSourcePreviews") !== "false"
|
|
711
|
+
})
|
|
712
|
+
)
|
|
713
|
+
).register(
|
|
714
|
+
"xnet://canvas/{canvasId}/json-canvas",
|
|
715
|
+
async (host, { uri, params, searchParams }) => host.jsonResource(
|
|
716
|
+
uri,
|
|
717
|
+
await host.exportCanvasJsonCanvas({
|
|
718
|
+
canvasId: params.canvasId,
|
|
719
|
+
includeXNetMetadata: searchParams.get("includeXNetMetadata") !== "false"
|
|
720
|
+
})
|
|
721
|
+
)
|
|
722
|
+
).register(
|
|
723
|
+
"xnet://canvas/{canvasId}/object/{objectId}",
|
|
724
|
+
async (host, { uri, params }) => host.jsonResource(uri, await host.readCanvasObject(params.canvasId, params.objectId))
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/ai-surface/tools/audit.ts
|
|
729
|
+
var getAuditLogTool = {
|
|
730
|
+
definition: {
|
|
731
|
+
name: "xnet_get_audit_log",
|
|
732
|
+
title: "Read AI audit log",
|
|
733
|
+
description: "Read recent AI mutation audit events with optional plan filtering.",
|
|
734
|
+
risk: "low",
|
|
735
|
+
requiredScopes: ["workspace.read"],
|
|
736
|
+
inputSchema: {
|
|
737
|
+
type: "object",
|
|
738
|
+
properties: {
|
|
739
|
+
planId: { type: "string", description: "Optional mutation plan id filter." },
|
|
740
|
+
limit: { type: "number", description: "Maximum audit events to return." }
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
execute: (host, args) => host.getAuditLog({
|
|
745
|
+
planId: readOptionalString(args, "planId"),
|
|
746
|
+
limit: readOptionalNumber(args, "limit")
|
|
747
|
+
})
|
|
748
|
+
};
|
|
749
|
+
var validateMutationPlanTool = {
|
|
750
|
+
definition: {
|
|
751
|
+
name: "xnet_validate_mutation_plan",
|
|
752
|
+
title: "Validate mutation plan",
|
|
753
|
+
description: "Validate a serialized mutation plan and return errors or warnings.",
|
|
754
|
+
risk: "medium",
|
|
755
|
+
requiredScopes: ["workspace.read"],
|
|
756
|
+
inputSchema: {
|
|
757
|
+
type: "object",
|
|
758
|
+
properties: {
|
|
759
|
+
plan: { type: "object", description: "Mutation plan object to validate." }
|
|
654
760
|
},
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
761
|
+
required: ["plan"]
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
execute: (_host, args) => {
|
|
765
|
+
const validation = validateAiMutationPlan(args.plan);
|
|
766
|
+
return { validation };
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// src/ai-surface/tools/canvas.ts
|
|
771
|
+
var canvasListTool = {
|
|
772
|
+
definition: {
|
|
773
|
+
name: "xnet_canvas_list",
|
|
774
|
+
title: "List canvases",
|
|
775
|
+
description: "List canvas nodes visible to the AI surface.",
|
|
776
|
+
risk: "low",
|
|
777
|
+
requiredScopes: ["canvas.read"],
|
|
778
|
+
inputSchema: {
|
|
779
|
+
type: "object",
|
|
780
|
+
properties: {
|
|
781
|
+
limit: { type: "number", description: "Maximum canvas count." },
|
|
782
|
+
offset: { type: "number", description: "Canvas offset." }
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
execute: async (host, args) => await host.listCanvases({
|
|
787
|
+
limit: readOptionalNumber(args, "limit"),
|
|
788
|
+
offset: readOptionalNumber(args, "offset")
|
|
789
|
+
})
|
|
790
|
+
};
|
|
791
|
+
var canvasReadViewportTool = {
|
|
792
|
+
definition: {
|
|
793
|
+
name: "xnet_canvas_read_viewport",
|
|
794
|
+
title: "Read canvas viewport",
|
|
795
|
+
description: "Read canvas objects and edges intersecting a viewport.",
|
|
796
|
+
risk: "low",
|
|
797
|
+
requiredScopes: ["canvas.read"],
|
|
798
|
+
inputSchema: {
|
|
799
|
+
type: "object",
|
|
800
|
+
properties: {
|
|
801
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
802
|
+
x: { type: "number", description: "Viewport x." },
|
|
803
|
+
y: { type: "number", description: "Viewport y." },
|
|
804
|
+
w: { type: "number", description: "Viewport width." },
|
|
805
|
+
h: { type: "number", description: "Viewport height." },
|
|
806
|
+
includeSourcePreviews: {
|
|
807
|
+
type: "boolean",
|
|
808
|
+
description: "Include previews for source-backed objects."
|
|
809
|
+
},
|
|
810
|
+
tileSize: { type: "number", description: "Optional tile size for tile scoping." },
|
|
811
|
+
tileIds: {
|
|
812
|
+
type: "array",
|
|
813
|
+
description: "Optional tile ids such as 0/1/-2 to constrain the read.",
|
|
814
|
+
items: { type: "string" }
|
|
678
815
|
}
|
|
679
816
|
},
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
817
|
+
required: ["canvasId"]
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
execute: async (host, args) => await host.readCanvasViewport({
|
|
821
|
+
canvasId: readRequiredString(args, "canvasId"),
|
|
822
|
+
x: readOptionalNumber(args, "x"),
|
|
823
|
+
y: readOptionalNumber(args, "y"),
|
|
824
|
+
w: readOptionalNumber(args, "w"),
|
|
825
|
+
h: readOptionalNumber(args, "h"),
|
|
826
|
+
tileSize: readOptionalNumber(args, "tileSize"),
|
|
827
|
+
tileIds: readStringArray(args.tileIds),
|
|
828
|
+
includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
|
|
829
|
+
})
|
|
830
|
+
};
|
|
831
|
+
var canvasReadSelectionTool = {
|
|
832
|
+
definition: {
|
|
833
|
+
name: "xnet_canvas_read_selection",
|
|
834
|
+
title: "Read canvas selection",
|
|
835
|
+
description: "Read selected canvas objects, connected edges, and optional source previews.",
|
|
836
|
+
risk: "low",
|
|
837
|
+
requiredScopes: ["canvas.read"],
|
|
838
|
+
inputSchema: {
|
|
839
|
+
type: "object",
|
|
840
|
+
properties: {
|
|
841
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
842
|
+
objectIds: {
|
|
843
|
+
type: "array",
|
|
844
|
+
description: "Selected object ids.",
|
|
845
|
+
items: { type: "string" }
|
|
846
|
+
},
|
|
847
|
+
includeSourcePreviews: {
|
|
848
|
+
type: "boolean",
|
|
849
|
+
description: "Include previews for source-backed objects."
|
|
694
850
|
}
|
|
695
851
|
},
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
852
|
+
required: ["canvasId", "objectIds"]
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
execute: async (host, args) => await host.readCanvasSelection({
|
|
856
|
+
canvasId: readRequiredString(args, "canvasId"),
|
|
857
|
+
objectIds: readRequiredStringArray(args.objectIds, "objectIds"),
|
|
858
|
+
includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
|
|
859
|
+
})
|
|
860
|
+
};
|
|
861
|
+
var canvasSearchTool = {
|
|
862
|
+
definition: {
|
|
863
|
+
name: "xnet_canvas_search",
|
|
864
|
+
title: "Search canvas",
|
|
865
|
+
description: "Search canvas object text, labels, ids, and source metadata.",
|
|
866
|
+
risk: "low",
|
|
867
|
+
requiredScopes: ["canvas.read"],
|
|
868
|
+
inputSchema: {
|
|
869
|
+
type: "object",
|
|
870
|
+
properties: {
|
|
871
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
872
|
+
query: { type: "string", description: "Search text." },
|
|
873
|
+
limit: { type: "number", description: "Maximum result count." }
|
|
713
874
|
},
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
875
|
+
required: ["canvasId", "query"]
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
execute: async (host, args) => await host.searchCanvas({
|
|
879
|
+
canvasId: readRequiredString(args, "canvasId"),
|
|
880
|
+
query: readRequiredString(args, "query"),
|
|
881
|
+
limit: readOptionalNumber(args, "limit")
|
|
882
|
+
})
|
|
883
|
+
};
|
|
884
|
+
var canvasExportJsonCanvasTool = {
|
|
885
|
+
definition: {
|
|
886
|
+
name: "xnet_canvas_export_json_canvas",
|
|
887
|
+
title: "Export canvas as JSON Canvas",
|
|
888
|
+
description: "Export a canvas or viewport as JSON Canvas with xNet source metadata.",
|
|
889
|
+
risk: "low",
|
|
890
|
+
requiredScopes: ["canvas.read"],
|
|
891
|
+
inputSchema: {
|
|
892
|
+
type: "object",
|
|
893
|
+
properties: {
|
|
894
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
895
|
+
includeXNetMetadata: {
|
|
896
|
+
type: "boolean",
|
|
897
|
+
description: "Include xNet source metadata. Defaults to true."
|
|
898
|
+
},
|
|
899
|
+
x: { type: "number", description: "Optional viewport x." },
|
|
900
|
+
y: { type: "number", description: "Optional viewport y." },
|
|
901
|
+
w: { type: "number", description: "Optional viewport width." },
|
|
902
|
+
h: { type: "number", description: "Optional viewport height." }
|
|
729
903
|
},
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
904
|
+
required: ["canvasId"]
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
execute: async (host, args) => await host.exportCanvasJsonCanvas({
|
|
908
|
+
canvasId: readRequiredString(args, "canvasId"),
|
|
909
|
+
includeXNetMetadata: readOptionalBoolean(args, "includeXNetMetadata") ?? true,
|
|
910
|
+
x: readOptionalNumber(args, "x"),
|
|
911
|
+
y: readOptionalNumber(args, "y"),
|
|
912
|
+
w: readOptionalNumber(args, "w"),
|
|
913
|
+
h: readOptionalNumber(args, "h")
|
|
914
|
+
})
|
|
915
|
+
};
|
|
916
|
+
var canvasPlanJsonCanvasImportTool = {
|
|
917
|
+
definition: {
|
|
918
|
+
name: "xnet_canvas_plan_json_canvas_import",
|
|
919
|
+
title: "Plan JSON Canvas import",
|
|
920
|
+
description: "Convert a JSON Canvas document into a plan-only canvas mutation.",
|
|
921
|
+
risk: "medium",
|
|
922
|
+
requiredScopes: ["canvas.read", "canvas.propose"],
|
|
923
|
+
inputSchema: {
|
|
924
|
+
type: "object",
|
|
925
|
+
properties: {
|
|
926
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
927
|
+
document: { type: "object", description: "JSON Canvas document." },
|
|
928
|
+
baseRevision: { type: "string", description: "Revision the import was based on." },
|
|
929
|
+
actor: { type: "string", description: "Agent or user creating the plan." },
|
|
930
|
+
intent: { type: "string", description: "User or agent intent for the import." }
|
|
747
931
|
},
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
}
|
|
932
|
+
required: ["canvasId", "document"]
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
execute: async (host, args) => await host.planCanvasJsonCanvasImport(args)
|
|
936
|
+
};
|
|
937
|
+
var planCanvasMutationTool = {
|
|
938
|
+
definition: {
|
|
939
|
+
name: "xnet_plan_canvas_mutation",
|
|
940
|
+
title: "Plan canvas mutation",
|
|
941
|
+
description: "Create a canvas mutation plan for later review without applying it.",
|
|
942
|
+
risk: "medium",
|
|
943
|
+
requiredScopes: ["canvas.read", "canvas.propose"],
|
|
944
|
+
inputSchema: {
|
|
945
|
+
type: "object",
|
|
946
|
+
properties: {
|
|
947
|
+
canvasId: { type: "string", description: "Canvas node id." },
|
|
948
|
+
baseRevision: { type: "string", description: "Revision the mutation was based on." },
|
|
949
|
+
operations: { type: "array", description: "Canvas operations to validate." },
|
|
950
|
+
intent: { type: "string", description: "User or agent intent for the mutation." },
|
|
951
|
+
actor: { type: "string", description: "Agent or user creating the plan." }
|
|
769
952
|
},
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
953
|
+
required: ["canvasId", "operations"]
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
execute: async (host, args) => await host.planCanvasMutation(args)
|
|
957
|
+
};
|
|
958
|
+
var canvasToolEntries = [
|
|
959
|
+
canvasListTool,
|
|
960
|
+
canvasReadViewportTool,
|
|
961
|
+
canvasReadSelectionTool,
|
|
962
|
+
canvasSearchTool,
|
|
963
|
+
canvasExportJsonCanvasTool,
|
|
964
|
+
canvasPlanJsonCanvasImportTool,
|
|
965
|
+
planCanvasMutationTool
|
|
966
|
+
];
|
|
967
|
+
|
|
968
|
+
// src/ai-surface/tools/database.ts
|
|
969
|
+
var databaseDescribeTool = {
|
|
970
|
+
definition: {
|
|
971
|
+
name: "xnet_database_describe",
|
|
972
|
+
title: "Describe database",
|
|
973
|
+
description: "Describe database schema, columns, views, row schema, and row counts.",
|
|
974
|
+
risk: "low",
|
|
975
|
+
requiredScopes: ["database.read"],
|
|
976
|
+
inputSchema: {
|
|
977
|
+
type: "object",
|
|
978
|
+
properties: {
|
|
979
|
+
databaseId: { type: "string", description: "Database node id." },
|
|
980
|
+
includeSample: {
|
|
981
|
+
type: "boolean",
|
|
982
|
+
description: "Include a small descriptor-backed row sample."
|
|
782
983
|
}
|
|
783
984
|
},
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
985
|
+
required: ["databaseId"]
|
|
986
|
+
}
|
|
987
|
+
},
|
|
988
|
+
execute: async (host, args) => await host.describeDatabase(readRequiredString(args, "databaseId"), {
|
|
989
|
+
includeSample: readOptionalBoolean(args, "includeSample") ?? false
|
|
990
|
+
})
|
|
991
|
+
};
|
|
992
|
+
var databaseQueryTool = {
|
|
993
|
+
definition: {
|
|
994
|
+
name: "xnet_database_query",
|
|
995
|
+
title: "Query database rows",
|
|
996
|
+
description: "Read a bounded page of database rows using NodeQueryDescriptor-compatible options.",
|
|
997
|
+
risk: "low",
|
|
998
|
+
requiredScopes: ["database.read", "database.query"],
|
|
999
|
+
inputSchema: {
|
|
1000
|
+
type: "object",
|
|
1001
|
+
properties: {
|
|
1002
|
+
databaseId: { type: "string", description: "Database node id." },
|
|
1003
|
+
schemaId: { type: "string", description: "Optional row schema IRI." },
|
|
1004
|
+
descriptor: {
|
|
791
1005
|
type: "object",
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
type: "boolean",
|
|
796
|
-
description: "Must be true to perform the rollback."
|
|
797
|
-
}
|
|
798
|
-
},
|
|
799
|
-
required: ["rollbackHandle", "confirmRollback"]
|
|
800
|
-
}
|
|
801
|
-
},
|
|
802
|
-
{
|
|
803
|
-
name: "xnet_database_describe",
|
|
804
|
-
title: "Describe database",
|
|
805
|
-
description: "Describe database schema, columns, views, row schema, and row counts.",
|
|
806
|
-
risk: "low",
|
|
807
|
-
requiredScopes: ["database.read"],
|
|
808
|
-
inputSchema: {
|
|
1006
|
+
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
1007
|
+
},
|
|
1008
|
+
where: {
|
|
809
1009
|
type: "object",
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
type: "boolean",
|
|
814
|
-
description: "Include a small descriptor-backed row sample."
|
|
815
|
-
}
|
|
816
|
-
},
|
|
817
|
-
required: ["databaseId"]
|
|
818
|
-
}
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
name: "xnet_database_query",
|
|
822
|
-
title: "Query database rows",
|
|
823
|
-
description: "Read a bounded page of database rows using NodeQueryDescriptor-compatible options.",
|
|
824
|
-
risk: "low",
|
|
825
|
-
requiredScopes: ["database.read", "database.query"],
|
|
826
|
-
inputSchema: {
|
|
1010
|
+
description: "Optional exact property filters for row nodes."
|
|
1011
|
+
},
|
|
1012
|
+
search: {
|
|
827
1013
|
type: "object",
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
descriptor: {
|
|
832
|
-
type: "object",
|
|
833
|
-
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
834
|
-
},
|
|
835
|
-
where: {
|
|
836
|
-
type: "object",
|
|
837
|
-
description: "Optional exact property filters for row nodes."
|
|
838
|
-
},
|
|
839
|
-
search: {
|
|
840
|
-
type: "object",
|
|
841
|
-
description: "Optional NodeQueryDescriptor search filter."
|
|
842
|
-
},
|
|
843
|
-
orderBy: {
|
|
844
|
-
type: "object",
|
|
845
|
-
description: "Optional NodeQueryDescriptor order map."
|
|
846
|
-
},
|
|
847
|
-
materializedView: {
|
|
848
|
-
type: "object",
|
|
849
|
-
description: "Optional materialized view query options."
|
|
850
|
-
},
|
|
851
|
-
count: { type: "string", description: "Page count mode: exact, estimate, or none." },
|
|
852
|
-
limit: { type: "number", description: "Maximum row count." },
|
|
853
|
-
offset: { type: "number", description: "Row offset." }
|
|
854
|
-
},
|
|
855
|
-
required: ["databaseId"]
|
|
856
|
-
}
|
|
857
|
-
},
|
|
858
|
-
{
|
|
859
|
-
name: "xnet_database_sample",
|
|
860
|
-
title: "Sample database rows",
|
|
861
|
-
description: "Return a small deterministic sample for schema and content inspection.",
|
|
862
|
-
risk: "low",
|
|
863
|
-
requiredScopes: ["database.read", "database.query"],
|
|
864
|
-
inputSchema: {
|
|
1014
|
+
description: "Optional NodeQueryDescriptor search filter."
|
|
1015
|
+
},
|
|
1016
|
+
orderBy: {
|
|
865
1017
|
type: "object",
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
sampleSize: { type: "number", description: "Sample row count." },
|
|
870
|
-
descriptor: {
|
|
871
|
-
type: "object",
|
|
872
|
-
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
873
|
-
}
|
|
874
|
-
},
|
|
875
|
-
required: ["databaseId"]
|
|
876
|
-
}
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
name: "xnet_database_explain_query",
|
|
880
|
-
title: "Explain database query",
|
|
881
|
-
description: "Explain descriptor, pagination, materialized view, and storage plan metadata.",
|
|
882
|
-
risk: "low",
|
|
883
|
-
requiredScopes: ["database.read", "database.query", "storage.diagnostics"],
|
|
884
|
-
inputSchema: {
|
|
1018
|
+
description: "Optional NodeQueryDescriptor order map."
|
|
1019
|
+
},
|
|
1020
|
+
materializedView: {
|
|
885
1021
|
type: "object",
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
892
|
-
},
|
|
893
|
-
limit: { type: "number", description: "Maximum row count for the dry-run query." },
|
|
894
|
-
offset: { type: "number", description: "Row offset." }
|
|
895
|
-
},
|
|
896
|
-
required: ["databaseId"]
|
|
897
|
-
}
|
|
1022
|
+
description: "Optional materialized view query options."
|
|
1023
|
+
},
|
|
1024
|
+
count: { type: "string", description: "Page count mode: exact, estimate, or none." },
|
|
1025
|
+
limit: { type: "number", description: "Maximum row count." },
|
|
1026
|
+
offset: { type: "number", description: "Row offset." }
|
|
898
1027
|
},
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1028
|
+
required: ["databaseId"]
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
execute: async (host, args) => await host.queryDatabase({
|
|
1032
|
+
databaseId: readRequiredString(args, "databaseId"),
|
|
1033
|
+
schemaId: readOptionalString(args, "schemaId"),
|
|
1034
|
+
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1035
|
+
where: readOptionalRecord(args, "where"),
|
|
1036
|
+
search: args.search,
|
|
1037
|
+
orderBy: readOptionalRecord(args, "orderBy"),
|
|
1038
|
+
materializedView: args.materializedView,
|
|
1039
|
+
count: readOptionalString(args, "count"),
|
|
1040
|
+
limit: readOptionalNumber(args, "limit"),
|
|
1041
|
+
offset: readOptionalNumber(args, "offset")
|
|
1042
|
+
})
|
|
1043
|
+
};
|
|
1044
|
+
var databaseSampleTool = {
|
|
1045
|
+
definition: {
|
|
1046
|
+
name: "xnet_database_sample",
|
|
1047
|
+
title: "Sample database rows",
|
|
1048
|
+
description: "Return a small deterministic sample for schema and content inspection.",
|
|
1049
|
+
risk: "low",
|
|
1050
|
+
requiredScopes: ["database.read", "database.query"],
|
|
1051
|
+
inputSchema: {
|
|
1052
|
+
type: "object",
|
|
1053
|
+
properties: {
|
|
1054
|
+
databaseId: { type: "string", description: "Database node id." },
|
|
1055
|
+
schemaId: { type: "string", description: "Optional row schema IRI." },
|
|
1056
|
+
sampleSize: { type: "number", description: "Sample row count." },
|
|
1057
|
+
descriptor: {
|
|
906
1058
|
type: "object",
|
|
907
|
-
|
|
908
|
-
databaseId: { type: "string", description: "Database node id." },
|
|
909
|
-
baseRevision: { type: "string", description: "Revision the mutation was based on." },
|
|
910
|
-
operations: { type: "array", description: "Database operations to validate." },
|
|
911
|
-
intent: { type: "string", description: "User or agent intent for the mutation." },
|
|
912
|
-
actor: { type: "string", description: "Agent or user creating the plan." }
|
|
913
|
-
},
|
|
914
|
-
required: ["databaseId", "operations"]
|
|
1059
|
+
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
915
1060
|
}
|
|
916
1061
|
},
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1062
|
+
required: ["databaseId"]
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
execute: async (host, args) => await host.sampleDatabase({
|
|
1066
|
+
databaseId: readRequiredString(args, "databaseId"),
|
|
1067
|
+
schemaId: readOptionalString(args, "schemaId"),
|
|
1068
|
+
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1069
|
+
sampleSize: readOptionalNumber(args, "sampleSize")
|
|
1070
|
+
})
|
|
1071
|
+
};
|
|
1072
|
+
var databaseExplainQueryTool = {
|
|
1073
|
+
definition: {
|
|
1074
|
+
name: "xnet_database_explain_query",
|
|
1075
|
+
title: "Explain database query",
|
|
1076
|
+
description: "Explain descriptor, pagination, materialized view, and storage plan metadata.",
|
|
1077
|
+
risk: "low",
|
|
1078
|
+
requiredScopes: ["database.read", "database.query", "storage.diagnostics"],
|
|
1079
|
+
inputSchema: {
|
|
1080
|
+
type: "object",
|
|
1081
|
+
properties: {
|
|
1082
|
+
databaseId: { type: "string", description: "Database node id." },
|
|
1083
|
+
schemaId: { type: "string", description: "Optional row schema IRI." },
|
|
1084
|
+
descriptor: {
|
|
924
1085
|
type: "object",
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
description: "Must be true to apply the database mutation plan."
|
|
930
|
-
},
|
|
931
|
-
allowStale: {
|
|
932
|
-
type: "boolean",
|
|
933
|
-
description: "Allow applying when the plan base revision differs from the live database node."
|
|
934
|
-
}
|
|
935
|
-
},
|
|
936
|
-
required: ["plan", "confirmApply"]
|
|
937
|
-
}
|
|
1086
|
+
description: "Optional NodeQueryDescriptor-compatible query shape."
|
|
1087
|
+
},
|
|
1088
|
+
limit: { type: "number", description: "Maximum row count for the dry-run query." },
|
|
1089
|
+
offset: { type: "number", description: "Row offset." }
|
|
938
1090
|
},
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1091
|
+
required: ["databaseId"]
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
execute: async (host, args) => await host.explainDatabaseQuery({
|
|
1095
|
+
databaseId: readRequiredString(args, "databaseId"),
|
|
1096
|
+
schemaId: readOptionalString(args, "schemaId"),
|
|
1097
|
+
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1098
|
+
limit: readOptionalNumber(args, "limit"),
|
|
1099
|
+
offset: readOptionalNumber(args, "offset")
|
|
1100
|
+
})
|
|
1101
|
+
};
|
|
1102
|
+
var planDatabaseMutationTool = {
|
|
1103
|
+
definition: {
|
|
1104
|
+
name: "xnet_plan_database_mutation",
|
|
1105
|
+
title: "Plan database mutation",
|
|
1106
|
+
description: "Create a database mutation plan for later review without applying it.",
|
|
1107
|
+
risk: "medium",
|
|
1108
|
+
requiredScopes: ["database.read", "database.propose"],
|
|
1109
|
+
inputSchema: {
|
|
1110
|
+
type: "object",
|
|
1111
|
+
properties: {
|
|
1112
|
+
databaseId: { type: "string", description: "Database node id." },
|
|
1113
|
+
baseRevision: { type: "string", description: "Revision the mutation was based on." },
|
|
1114
|
+
operations: { type: "array", description: "Database operations to validate." },
|
|
1115
|
+
intent: { type: "string", description: "User or agent intent for the mutation." },
|
|
1116
|
+
actor: { type: "string", description: "Agent or user creating the plan." }
|
|
952
1117
|
},
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
},
|
|
978
|
-
required: ["canvasId"]
|
|
1118
|
+
required: ["databaseId", "operations"]
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
execute: async (host, args) => await host.planDatabaseMutation(args)
|
|
1122
|
+
};
|
|
1123
|
+
var applyDatabaseMutationTool = {
|
|
1124
|
+
definition: {
|
|
1125
|
+
name: "xnet_apply_database_mutation",
|
|
1126
|
+
title: "Apply database mutation plan",
|
|
1127
|
+
description: "Apply a validated database row/schema mutation plan with transactional row rollback and audit logging.",
|
|
1128
|
+
risk: "high",
|
|
1129
|
+
requiredScopes: ["database.read", "database.write.rows", "database.write.schema"],
|
|
1130
|
+
inputSchema: {
|
|
1131
|
+
type: "object",
|
|
1132
|
+
properties: {
|
|
1133
|
+
plan: { type: "object", description: "Validated database mutation plan." },
|
|
1134
|
+
confirmApply: {
|
|
1135
|
+
type: "boolean",
|
|
1136
|
+
description: "Must be true to apply the database mutation plan."
|
|
1137
|
+
},
|
|
1138
|
+
allowStale: {
|
|
1139
|
+
type: "boolean",
|
|
1140
|
+
description: "Allow applying when the plan base revision differs from the live database node."
|
|
979
1141
|
}
|
|
980
1142
|
},
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1143
|
+
required: ["plan", "confirmApply"]
|
|
1144
|
+
}
|
|
1145
|
+
},
|
|
1146
|
+
execute: async (host, args) => await host.applyDatabaseMutation(args)
|
|
1147
|
+
};
|
|
1148
|
+
var databaseToolEntries = [
|
|
1149
|
+
databaseDescribeTool,
|
|
1150
|
+
databaseQueryTool,
|
|
1151
|
+
databaseSampleTool,
|
|
1152
|
+
databaseExplainQueryTool,
|
|
1153
|
+
planDatabaseMutationTool,
|
|
1154
|
+
applyDatabaseMutationTool
|
|
1155
|
+
];
|
|
1156
|
+
|
|
1157
|
+
// src/ai-surface/tools/page-mutation.ts
|
|
1158
|
+
var readPageMarkdownTool = {
|
|
1159
|
+
definition: {
|
|
1160
|
+
name: "xnet_read_page_markdown",
|
|
1161
|
+
title: "Read page Markdown",
|
|
1162
|
+
description: "Read a page as Markdown with optional xNet frontmatter.",
|
|
1163
|
+
risk: "low",
|
|
1164
|
+
requiredScopes: ["page.read"],
|
|
1165
|
+
inputSchema: {
|
|
1166
|
+
type: "object",
|
|
1167
|
+
properties: {
|
|
1168
|
+
pageId: { type: "string", description: "Page node id." },
|
|
1169
|
+
includeFrontmatter: {
|
|
1170
|
+
type: "boolean",
|
|
1171
|
+
description: "Include xNet identity frontmatter. Defaults to true."
|
|
1002
1172
|
}
|
|
1003
1173
|
},
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1174
|
+
required: ["pageId"]
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
execute: async (host, args) => {
|
|
1178
|
+
const content = await host.readPageMarkdown(
|
|
1179
|
+
readRequiredString(args, "pageId"),
|
|
1180
|
+
readOptionalBoolean(args, "includeFrontmatter") ?? true
|
|
1181
|
+
);
|
|
1182
|
+
return { markdown: content.text, mimeType: content.mimeType, uri: content.uri };
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
var validatePageMarkdownTool = {
|
|
1186
|
+
definition: {
|
|
1187
|
+
name: "xnet_validate_page_markdown",
|
|
1188
|
+
title: "Validate page Markdown",
|
|
1189
|
+
description: "Validate xNet page frontmatter and supported xNet Markdown directives.",
|
|
1190
|
+
risk: "low",
|
|
1191
|
+
requiredScopes: ["page.read"],
|
|
1192
|
+
inputSchema: {
|
|
1193
|
+
type: "object",
|
|
1194
|
+
properties: {
|
|
1195
|
+
pageId: { type: "string", description: "Optional target page node id." },
|
|
1196
|
+
baseRevision: { type: "string", description: "Optional expected base revision." },
|
|
1197
|
+
markdown: { type: "string", description: "Markdown to validate." }
|
|
1019
1198
|
},
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1199
|
+
required: ["markdown"]
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
execute: async (host, args) => {
|
|
1203
|
+
const pageId = readOptionalString(args, "pageId");
|
|
1204
|
+
const node = pageId ? await host.getNodeOrThrow(pageId) : null;
|
|
1205
|
+
return validateXNetPageMarkdown(readRequiredString(args, "markdown"), {
|
|
1206
|
+
pageId,
|
|
1207
|
+
schemaId: node?.schemaId,
|
|
1208
|
+
baseRevision: readOptionalString(args, "baseRevision")
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
var planPagePatchTool = {
|
|
1213
|
+
definition: {
|
|
1214
|
+
name: "xnet_plan_page_patch",
|
|
1215
|
+
title: "Plan page Markdown patch",
|
|
1216
|
+
description: "Validate an edited Markdown page and return a mutation plan without applying it.",
|
|
1217
|
+
risk: "medium",
|
|
1218
|
+
requiredScopes: ["page.read", "page.propose"],
|
|
1219
|
+
inputSchema: {
|
|
1220
|
+
type: "object",
|
|
1221
|
+
properties: {
|
|
1222
|
+
pageId: { type: "string", description: "Page node id." },
|
|
1223
|
+
baseRevision: { type: "string", description: "Revision the patch was based on." },
|
|
1224
|
+
markdown: { type: "string", description: "Proposed full Markdown replacement." },
|
|
1225
|
+
intent: { type: "string", description: "User or agent intent for the patch." },
|
|
1226
|
+
actor: { type: "string", description: "Agent or user creating the plan." }
|
|
1041
1227
|
},
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1228
|
+
required: ["pageId", "markdown"]
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
execute: async (host, args) => await host.planPagePatch(args)
|
|
1232
|
+
};
|
|
1233
|
+
var applyPageMarkdownTool = {
|
|
1234
|
+
definition: {
|
|
1235
|
+
name: "xnet_apply_page_markdown",
|
|
1236
|
+
title: "Apply page Markdown plan",
|
|
1237
|
+
description: "Apply a validated page Markdown mutation plan through the configured TipTap/Yjs document adapter, with a node-property fallback.",
|
|
1238
|
+
risk: "high",
|
|
1239
|
+
requiredScopes: ["page.read", "page.write"],
|
|
1240
|
+
inputSchema: {
|
|
1241
|
+
type: "object",
|
|
1242
|
+
properties: {
|
|
1243
|
+
plan: { type: "object", description: "Validated page Markdown mutation plan." },
|
|
1244
|
+
confirmApply: {
|
|
1245
|
+
type: "boolean",
|
|
1246
|
+
description: "Must be true to apply the page Markdown plan."
|
|
1247
|
+
},
|
|
1248
|
+
allowStale: {
|
|
1249
|
+
type: "boolean",
|
|
1250
|
+
description: "Allow applying when the plan base revision differs from the live node."
|
|
1058
1251
|
}
|
|
1059
1252
|
},
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1253
|
+
required: ["plan", "confirmApply"]
|
|
1254
|
+
}
|
|
1255
|
+
},
|
|
1256
|
+
execute: async (host, args) => await host.applyPageMarkdown(args)
|
|
1257
|
+
};
|
|
1258
|
+
var rollbackPageMarkdownTool = {
|
|
1259
|
+
definition: {
|
|
1260
|
+
name: "xnet_rollback_page_markdown",
|
|
1261
|
+
title: "Rollback page Markdown apply",
|
|
1262
|
+
description: "Rollback a previously applied page Markdown plan by rollback handle.",
|
|
1263
|
+
risk: "high",
|
|
1264
|
+
requiredScopes: ["page.write"],
|
|
1265
|
+
inputSchema: {
|
|
1266
|
+
type: "object",
|
|
1267
|
+
properties: {
|
|
1268
|
+
rollbackHandle: { type: "string", description: "Rollback handle from apply result." },
|
|
1269
|
+
confirmRollback: {
|
|
1270
|
+
type: "boolean",
|
|
1271
|
+
description: "Must be true to perform the rollback."
|
|
1076
1272
|
}
|
|
1077
1273
|
},
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1274
|
+
required: ["rollbackHandle", "confirmRollback"]
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
execute: async (host, args) => await host.rollbackPageMarkdown(args)
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
// src/ai-surface/tools/search.ts
|
|
1281
|
+
var searchTool = {
|
|
1282
|
+
definition: {
|
|
1283
|
+
name: "xnet_search",
|
|
1284
|
+
title: "Search xNet workspace",
|
|
1285
|
+
description: "Search node titles and searchable properties with pagination and limits.",
|
|
1286
|
+
risk: "low",
|
|
1287
|
+
requiredScopes: ["workspace.search"],
|
|
1288
|
+
inputSchema: {
|
|
1289
|
+
type: "object",
|
|
1290
|
+
properties: {
|
|
1291
|
+
query: { type: "string", description: "Search text." },
|
|
1292
|
+
schemaId: { type: "string", description: "Optional schema IRI filter." },
|
|
1293
|
+
limit: { type: "number", description: "Maximum result count." },
|
|
1294
|
+
offset: { type: "number", description: "Result offset for pagination." }
|
|
1295
|
+
},
|
|
1296
|
+
required: ["query"]
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
execute: async (host, args) => await host.search({
|
|
1300
|
+
query: readRequiredString(args, "query"),
|
|
1301
|
+
schemaId: readOptionalString(args, "schemaId") ?? readOptionalString(args, "schema"),
|
|
1302
|
+
limit: readOptionalNumber(args, "limit"),
|
|
1303
|
+
offset: readOptionalNumber(args, "offset")
|
|
1304
|
+
})
|
|
1305
|
+
};
|
|
1306
|
+
var graphExpandTool = {
|
|
1307
|
+
definition: {
|
|
1308
|
+
name: "xnet_graph_expand",
|
|
1309
|
+
title: "Expand a node along its relations",
|
|
1310
|
+
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.",
|
|
1311
|
+
risk: "low",
|
|
1312
|
+
requiredScopes: ["workspace.read"],
|
|
1313
|
+
inputSchema: {
|
|
1314
|
+
type: "object",
|
|
1315
|
+
properties: {
|
|
1316
|
+
nodeId: { type: "string", description: "The node to expand from." },
|
|
1317
|
+
hops: {
|
|
1318
|
+
type: "number",
|
|
1319
|
+
description: "How many relation hops to walk (1\u20132, default 1)."
|
|
1320
|
+
},
|
|
1321
|
+
limit: { type: "number", description: "Maximum neighbors to return." }
|
|
1322
|
+
},
|
|
1323
|
+
required: ["nodeId"]
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
execute: async (host, args) => await host.expandGraph({
|
|
1327
|
+
nodeId: readRequiredString(args, "nodeId"),
|
|
1328
|
+
hops: readOptionalNumber(args, "hops"),
|
|
1329
|
+
limit: readOptionalNumber(args, "limit")
|
|
1330
|
+
})
|
|
1331
|
+
};
|
|
1332
|
+
var createContextPackTool = {
|
|
1333
|
+
definition: {
|
|
1334
|
+
name: "xnet_create_context_pack",
|
|
1335
|
+
title: "Create context pack",
|
|
1336
|
+
description: "Create a bounded context pack from seeds and optional search results.",
|
|
1337
|
+
risk: "low",
|
|
1338
|
+
requiredScopes: ["workspace.read", "workspace.search"],
|
|
1339
|
+
inputSchema: {
|
|
1340
|
+
type: "object",
|
|
1341
|
+
properties: {
|
|
1342
|
+
query: { type: "string", description: "Optional search query." },
|
|
1343
|
+
seeds: {
|
|
1344
|
+
type: "array",
|
|
1345
|
+
description: "Seed resources such as pages, databases, canvases, or nodes.",
|
|
1346
|
+
items: {
|
|
1347
|
+
type: "object",
|
|
1348
|
+
properties: {
|
|
1349
|
+
kind: { type: "string", description: "Seed kind." },
|
|
1350
|
+
id: { type: "string", description: "Seed id." }
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
limit: { type: "number", description: "Maximum resources to include." }
|
|
1091
1355
|
}
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
execute: async (host, args) => await host.createContextPack({
|
|
1359
|
+
query: readOptionalString(args, "query"),
|
|
1360
|
+
seeds: readContextSeeds(args.seeds),
|
|
1361
|
+
limit: readOptionalNumber(args, "limit")
|
|
1362
|
+
})
|
|
1363
|
+
};
|
|
1364
|
+
var createExternalContextResourceTool = {
|
|
1365
|
+
definition: {
|
|
1366
|
+
name: "xnet_create_external_context_resource",
|
|
1367
|
+
title: "Create untrusted external context resource",
|
|
1368
|
+
description: "Wrap externally fetched content as an untrusted context-pack resource with an explicit instruction boundary.",
|
|
1369
|
+
risk: "medium",
|
|
1370
|
+
requiredScopes: ["network.fetch"],
|
|
1371
|
+
inputSchema: {
|
|
1372
|
+
type: "object",
|
|
1373
|
+
properties: {
|
|
1374
|
+
url: { type: "string", description: "External source URL." },
|
|
1375
|
+
text: { type: "string", description: "Fetched external text content." },
|
|
1376
|
+
mimeType: { type: "string", description: "Source MIME type. Defaults to text/plain." }
|
|
1377
|
+
},
|
|
1378
|
+
required: ["url", "text"]
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
execute: (host, args) => host.createExternalContextResource({
|
|
1382
|
+
url: readRequiredString(args, "url"),
|
|
1383
|
+
text: readRequiredString(args, "text"),
|
|
1384
|
+
mimeType: readOptionalString(args, "mimeType")
|
|
1385
|
+
})
|
|
1386
|
+
};
|
|
1387
|
+
var searchToolEntries = [
|
|
1388
|
+
searchTool,
|
|
1389
|
+
graphExpandTool,
|
|
1390
|
+
createContextPackTool,
|
|
1391
|
+
createExternalContextResourceTool
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
// src/ai-surface/tools/index.ts
|
|
1395
|
+
var BUILT_IN_TOOL_ENTRIES = [
|
|
1396
|
+
...searchToolEntries,
|
|
1397
|
+
readPageMarkdownTool,
|
|
1398
|
+
validatePageMarkdownTool,
|
|
1399
|
+
planPagePatchTool,
|
|
1400
|
+
applyPageMarkdownTool,
|
|
1401
|
+
getAuditLogTool,
|
|
1402
|
+
rollbackPageMarkdownTool,
|
|
1403
|
+
...databaseToolEntries,
|
|
1404
|
+
...canvasToolEntries,
|
|
1405
|
+
validateMutationPlanTool
|
|
1406
|
+
];
|
|
1407
|
+
var BUILT_IN_TOOLS_BY_NAME = new Map(
|
|
1408
|
+
BUILT_IN_TOOL_ENTRIES.map((entry) => [entry.definition.name, entry])
|
|
1409
|
+
);
|
|
1410
|
+
|
|
1411
|
+
// src/ai-surface/service.ts
|
|
1412
|
+
var DEFAULT_LIMITS = {
|
|
1413
|
+
maxListLimit: 100,
|
|
1414
|
+
maxSearchScan: 500,
|
|
1415
|
+
maxSearchResults: 20,
|
|
1416
|
+
maxContextResources: 12,
|
|
1417
|
+
maxCharactersPerResource: 12e3,
|
|
1418
|
+
maxJsonCharacters: 24e3,
|
|
1419
|
+
maxCanvasObjects: 200,
|
|
1420
|
+
maxDatabaseRows: 100
|
|
1421
|
+
};
|
|
1422
|
+
var BUILT_IN_RESOURCE_ROUTES = createBuiltInResourceRouter();
|
|
1423
|
+
var AiSurfaceService = class {
|
|
1424
|
+
constructor(config) {
|
|
1425
|
+
this.config = config;
|
|
1426
|
+
this.limits = { ...DEFAULT_LIMITS, ...config.limits };
|
|
1427
|
+
this.clock = config.clock ?? (() => /* @__PURE__ */ new Date());
|
|
1428
|
+
for (const tool of config.extraTools ?? []) {
|
|
1429
|
+
if (!this.extraTools.has(tool.name)) this.extraTools.set(tool.name, tool);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
limits;
|
|
1433
|
+
clock;
|
|
1434
|
+
sequence = 0;
|
|
1435
|
+
auditEvents = [];
|
|
1436
|
+
rollbackSnapshots = /* @__PURE__ */ new Map();
|
|
1437
|
+
/** Contributed tools by name (exploration 0196), de-duped at construction. */
|
|
1438
|
+
extraTools = /* @__PURE__ */ new Map();
|
|
1439
|
+
/**
|
|
1440
|
+
* The narrow surface handed to built-in tool handlers and resource routes
|
|
1441
|
+
* (`tools/`, `resources/`): closures over the private methods, so the class
|
|
1442
|
+
* stays the facade and its public type is unchanged. The closures are lazy —
|
|
1443
|
+
* nothing here runs until a tool call or resource read arrives.
|
|
1444
|
+
*/
|
|
1445
|
+
host = {
|
|
1446
|
+
search: (options) => this.search(options),
|
|
1447
|
+
expandGraph: (options) => this.expandGraph(options),
|
|
1448
|
+
createContextPack: (options) => this.createContextPack(options),
|
|
1449
|
+
createExternalContextResource: (options) => this.createExternalContextResource(options),
|
|
1450
|
+
getWorkspaceSummary: () => this.getWorkspaceSummary(),
|
|
1451
|
+
getRecentNodes: () => this.getRecentNodes(),
|
|
1452
|
+
listNodes: async () => {
|
|
1453
|
+
const nodes = await this.config.store.list({ limit: this.limits.maxListLimit });
|
|
1454
|
+
return { nodes, count: nodes.length, limit: this.limits.maxListLimit };
|
|
1455
|
+
},
|
|
1456
|
+
listSchemas: async () => ({ schemas: await this.getSchemaSummaries(true) }),
|
|
1457
|
+
getNodeOrThrow: (id) => this.getNodeOrThrow(id),
|
|
1458
|
+
getNodeProjection: (id) => this.getNodeProjection(id),
|
|
1459
|
+
readPageMarkdown: (pageId, includeFrontmatter, uri) => this.readPageMarkdown(pageId, includeFrontmatter, uri),
|
|
1460
|
+
readPageOutline: (pageId) => this.readPageOutline(pageId),
|
|
1461
|
+
planPagePatch: (args) => this.planPagePatch(args),
|
|
1462
|
+
applyPageMarkdown: (args) => this.applyPageMarkdown(args),
|
|
1463
|
+
rollbackPageMarkdown: (args) => this.rollbackPageMarkdown(args),
|
|
1464
|
+
getAuditLog: (options) => this.getAuditLog(options),
|
|
1465
|
+
describeDatabase: (databaseId, options) => this.describeDatabase(databaseId, options),
|
|
1466
|
+
readDatabaseViews: (databaseId) => this.readDatabaseViews(databaseId),
|
|
1467
|
+
queryDatabase: (options) => this.queryDatabase(options),
|
|
1468
|
+
sampleDatabase: (options) => this.sampleDatabase(options),
|
|
1469
|
+
explainDatabaseQuery: (options) => this.explainDatabaseQuery(options),
|
|
1470
|
+
planDatabaseMutation: (args) => this.planDatabaseMutation(args),
|
|
1471
|
+
applyDatabaseMutation: (args) => this.applyDatabaseMutation(args),
|
|
1472
|
+
listCanvases: (options) => this.listCanvases(options),
|
|
1473
|
+
readCanvasViewport: (options) => this.readCanvasViewport(options),
|
|
1474
|
+
readCanvasObjects: (canvasId) => this.readCanvasObjects(canvasId),
|
|
1475
|
+
readCanvasSelection: (options) => this.readCanvasSelection(options),
|
|
1476
|
+
searchCanvas: (options) => this.searchCanvas(options),
|
|
1477
|
+
exportCanvasJsonCanvas: (options) => this.exportCanvasJsonCanvas(options),
|
|
1478
|
+
readCanvasObject: (canvasId, objectId) => this.readCanvasObject(canvasId, objectId),
|
|
1479
|
+
planCanvasJsonCanvasImport: (args) => this.planCanvasJsonCanvasImport(args),
|
|
1480
|
+
planCanvasMutation: (args) => this.planCanvasMutation(args),
|
|
1481
|
+
jsonResource: (uri, value) => this.jsonResource(uri, value)
|
|
1482
|
+
};
|
|
1483
|
+
/** The contributed (non-built-in) tools, with built-in name collisions removed. */
|
|
1484
|
+
getExtraTools() {
|
|
1485
|
+
const builtIn = new Set(this.builtInTools().map((t) => t.name));
|
|
1486
|
+
return [...this.extraTools.values()].filter((t) => !builtIn.has(t.name));
|
|
1487
|
+
}
|
|
1488
|
+
getResources() {
|
|
1489
|
+
return [
|
|
1490
|
+
createResource(
|
|
1491
|
+
"xnet://workspace/summary",
|
|
1492
|
+
"Workspace Summary",
|
|
1493
|
+
"High-level schema counts, recent nodes, and AI surface capabilities.",
|
|
1494
|
+
"application/json",
|
|
1495
|
+
"low",
|
|
1496
|
+
["workspace.read"]
|
|
1497
|
+
),
|
|
1498
|
+
createResource(
|
|
1499
|
+
"xnet://workspace/recent",
|
|
1500
|
+
"Recent Workspace Nodes",
|
|
1501
|
+
"Recent nodes with ids, schemas, titles, and revisions.",
|
|
1502
|
+
"application/json",
|
|
1503
|
+
"low",
|
|
1504
|
+
["workspace.read"]
|
|
1505
|
+
),
|
|
1506
|
+
createResource(
|
|
1507
|
+
"xnet://nodes",
|
|
1508
|
+
"All Nodes",
|
|
1509
|
+
"Limited list of nodes in the local store.",
|
|
1510
|
+
"application/json",
|
|
1511
|
+
"low",
|
|
1512
|
+
["workspace.read"]
|
|
1513
|
+
),
|
|
1514
|
+
createResource(
|
|
1515
|
+
"xnet://schemas",
|
|
1516
|
+
"All Schemas",
|
|
1517
|
+
"List of available schemas and property metadata.",
|
|
1518
|
+
"application/json",
|
|
1519
|
+
"low",
|
|
1520
|
+
["workspace.read"]
|
|
1521
|
+
),
|
|
1522
|
+
createResource(
|
|
1523
|
+
"xnet://page/{pageId}.md",
|
|
1524
|
+
"Page Markdown",
|
|
1525
|
+
"Markdown projection for a page, including xNet frontmatter identity.",
|
|
1526
|
+
"text/markdown",
|
|
1527
|
+
"low",
|
|
1528
|
+
["page.read"],
|
|
1529
|
+
true
|
|
1530
|
+
),
|
|
1531
|
+
createResource(
|
|
1532
|
+
"xnet://page/{pageId}/outline",
|
|
1533
|
+
"Page Outline",
|
|
1534
|
+
"Page heading outline extracted from the Markdown projection.",
|
|
1535
|
+
"application/json",
|
|
1536
|
+
"low",
|
|
1537
|
+
["page.read"],
|
|
1538
|
+
true
|
|
1539
|
+
),
|
|
1540
|
+
createResource(
|
|
1541
|
+
"xnet://database/{databaseId}/schema",
|
|
1542
|
+
"Database Schema",
|
|
1543
|
+
"Database node metadata, declared properties, and known column descriptors.",
|
|
1544
|
+
"application/json",
|
|
1545
|
+
"low",
|
|
1546
|
+
["database.read"],
|
|
1547
|
+
true
|
|
1548
|
+
),
|
|
1549
|
+
createResource(
|
|
1550
|
+
"xnet://database/{databaseId}/views",
|
|
1551
|
+
"Database Views",
|
|
1552
|
+
"Known database view descriptors from the database node projection.",
|
|
1553
|
+
"application/json",
|
|
1554
|
+
"low",
|
|
1555
|
+
["database.read"],
|
|
1556
|
+
true
|
|
1557
|
+
),
|
|
1558
|
+
createResource(
|
|
1559
|
+
"xnet://database/{databaseId}/sample?limit=10",
|
|
1560
|
+
"Database Sample Rows",
|
|
1561
|
+
"Bounded sample of rows for a database using NodeQueryDescriptor semantics.",
|
|
1562
|
+
"application/json",
|
|
1563
|
+
"low",
|
|
1564
|
+
["database.read", "database.query"],
|
|
1565
|
+
true
|
|
1566
|
+
),
|
|
1567
|
+
createResource(
|
|
1568
|
+
"xnet://canvas/{canvasId}/viewport?x=0&y=0&w=1000&h=800",
|
|
1569
|
+
"Canvas Viewport",
|
|
1570
|
+
"Viewport-scoped canvas objects and edges.",
|
|
1571
|
+
"application/json",
|
|
1572
|
+
"low",
|
|
1573
|
+
["canvas.read"],
|
|
1574
|
+
true
|
|
1575
|
+
),
|
|
1576
|
+
createResource(
|
|
1577
|
+
"xnet://canvas/{canvasId}/objects",
|
|
1578
|
+
"Canvas Objects",
|
|
1579
|
+
"Bounded list of canvas objects and connectors.",
|
|
1580
|
+
"application/json",
|
|
1581
|
+
"low",
|
|
1582
|
+
["canvas.read"],
|
|
1583
|
+
true
|
|
1584
|
+
),
|
|
1585
|
+
createResource(
|
|
1586
|
+
"xnet://canvas/{canvasId}/selection?ids=object-1,object-2",
|
|
1587
|
+
"Canvas Selection",
|
|
1588
|
+
"Selection-scoped canvas objects, edges, and source previews.",
|
|
1589
|
+
"application/json",
|
|
1590
|
+
"low",
|
|
1591
|
+
["canvas.read"],
|
|
1592
|
+
true
|
|
1593
|
+
),
|
|
1594
|
+
createResource(
|
|
1595
|
+
"xnet://canvas/{canvasId}/json-canvas",
|
|
1596
|
+
"Canvas JSON Canvas",
|
|
1597
|
+
"JSON Canvas projection with xNet source metadata sidecars.",
|
|
1598
|
+
"application/json",
|
|
1599
|
+
"low",
|
|
1600
|
+
["canvas.read"],
|
|
1601
|
+
true
|
|
1602
|
+
)
|
|
1092
1603
|
];
|
|
1093
1604
|
}
|
|
1605
|
+
/** The full tool surface: built-in `xnet_*` tools plus contributed extras. */
|
|
1606
|
+
getTools() {
|
|
1607
|
+
const extras = this.getExtraTools().map(
|
|
1608
|
+
({ invoke: _invoke, ...def }) => def
|
|
1609
|
+
);
|
|
1610
|
+
return [...this.builtInTools(), ...extras];
|
|
1611
|
+
}
|
|
1612
|
+
builtInTools() {
|
|
1613
|
+
return BUILT_IN_TOOL_ENTRIES.map((entry) => entry.definition);
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Dispatch a tool call: the built-in registry first (a built-in `xnet_*`
|
|
1617
|
+
* name always wins), then contributed tools (plugin/connector `agentTools`,
|
|
1618
|
+
* exploration 0196).
|
|
1619
|
+
*/
|
|
1094
1620
|
async callTool(name, args = {}) {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
limit: readOptionalNumber(args, "limit"),
|
|
1101
|
-
offset: readOptionalNumber(args, "offset")
|
|
1102
|
-
});
|
|
1103
|
-
case "xnet_graph_expand":
|
|
1104
|
-
return await this.expandGraph({
|
|
1105
|
-
nodeId: readRequiredString(args, "nodeId"),
|
|
1106
|
-
hops: readOptionalNumber(args, "hops"),
|
|
1107
|
-
limit: readOptionalNumber(args, "limit")
|
|
1108
|
-
});
|
|
1109
|
-
case "xnet_create_context_pack":
|
|
1110
|
-
return await this.createContextPack({
|
|
1111
|
-
query: readOptionalString(args, "query"),
|
|
1112
|
-
seeds: readContextSeeds(args.seeds),
|
|
1113
|
-
limit: readOptionalNumber(args, "limit")
|
|
1114
|
-
});
|
|
1115
|
-
case "xnet_create_external_context_resource":
|
|
1116
|
-
return this.createExternalContextResource({
|
|
1117
|
-
url: readRequiredString(args, "url"),
|
|
1118
|
-
text: readRequiredString(args, "text"),
|
|
1119
|
-
mimeType: readOptionalString(args, "mimeType")
|
|
1120
|
-
});
|
|
1121
|
-
case "xnet_read_page_markdown": {
|
|
1122
|
-
const content = await this.readPageMarkdown(
|
|
1123
|
-
readRequiredString(args, "pageId"),
|
|
1124
|
-
readOptionalBoolean(args, "includeFrontmatter") ?? true
|
|
1125
|
-
);
|
|
1126
|
-
return { markdown: content.text, mimeType: content.mimeType, uri: content.uri };
|
|
1127
|
-
}
|
|
1128
|
-
case "xnet_validate_page_markdown": {
|
|
1129
|
-
const pageId = readOptionalString(args, "pageId");
|
|
1130
|
-
const node = pageId ? await this.getNodeOrThrow(pageId) : null;
|
|
1131
|
-
return validateXNetPageMarkdown(readRequiredString(args, "markdown"), {
|
|
1132
|
-
pageId,
|
|
1133
|
-
schemaId: node?.schemaId,
|
|
1134
|
-
baseRevision: readOptionalString(args, "baseRevision")
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1137
|
-
case "xnet_plan_page_patch":
|
|
1138
|
-
return await this.planPagePatch(args);
|
|
1139
|
-
case "xnet_apply_page_markdown":
|
|
1140
|
-
return await this.applyPageMarkdown(args);
|
|
1141
|
-
case "xnet_get_audit_log":
|
|
1142
|
-
return this.getAuditLog({
|
|
1143
|
-
planId: readOptionalString(args, "planId"),
|
|
1144
|
-
limit: readOptionalNumber(args, "limit")
|
|
1145
|
-
});
|
|
1146
|
-
case "xnet_rollback_page_markdown":
|
|
1147
|
-
return await this.rollbackPageMarkdown(args);
|
|
1148
|
-
case "xnet_database_describe":
|
|
1149
|
-
return await this.describeDatabase(readRequiredString(args, "databaseId"), {
|
|
1150
|
-
includeSample: readOptionalBoolean(args, "includeSample") ?? false
|
|
1151
|
-
});
|
|
1152
|
-
case "xnet_database_query":
|
|
1153
|
-
return await this.queryDatabase({
|
|
1154
|
-
databaseId: readRequiredString(args, "databaseId"),
|
|
1155
|
-
schemaId: readOptionalString(args, "schemaId"),
|
|
1156
|
-
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1157
|
-
where: readOptionalRecord(args, "where"),
|
|
1158
|
-
search: args.search,
|
|
1159
|
-
orderBy: readOptionalRecord(args, "orderBy"),
|
|
1160
|
-
materializedView: args.materializedView,
|
|
1161
|
-
count: readOptionalString(args, "count"),
|
|
1162
|
-
limit: readOptionalNumber(args, "limit"),
|
|
1163
|
-
offset: readOptionalNumber(args, "offset")
|
|
1164
|
-
});
|
|
1165
|
-
case "xnet_database_sample":
|
|
1166
|
-
return await this.sampleDatabase({
|
|
1167
|
-
databaseId: readRequiredString(args, "databaseId"),
|
|
1168
|
-
schemaId: readOptionalString(args, "schemaId"),
|
|
1169
|
-
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1170
|
-
sampleSize: readOptionalNumber(args, "sampleSize")
|
|
1171
|
-
});
|
|
1172
|
-
case "xnet_database_explain_query":
|
|
1173
|
-
return await this.explainDatabaseQuery({
|
|
1174
|
-
databaseId: readRequiredString(args, "databaseId"),
|
|
1175
|
-
schemaId: readOptionalString(args, "schemaId"),
|
|
1176
|
-
descriptor: readOptionalRecord(args, "descriptor"),
|
|
1177
|
-
limit: readOptionalNumber(args, "limit"),
|
|
1178
|
-
offset: readOptionalNumber(args, "offset")
|
|
1179
|
-
});
|
|
1180
|
-
case "xnet_plan_database_mutation":
|
|
1181
|
-
return await this.planDatabaseMutation(args);
|
|
1182
|
-
case "xnet_apply_database_mutation":
|
|
1183
|
-
return await this.applyDatabaseMutation(args);
|
|
1184
|
-
case "xnet_canvas_list":
|
|
1185
|
-
return await this.listCanvases({
|
|
1186
|
-
limit: readOptionalNumber(args, "limit"),
|
|
1187
|
-
offset: readOptionalNumber(args, "offset")
|
|
1188
|
-
});
|
|
1189
|
-
case "xnet_canvas_read_viewport":
|
|
1190
|
-
return await this.readCanvasViewport({
|
|
1191
|
-
canvasId: readRequiredString(args, "canvasId"),
|
|
1192
|
-
x: readOptionalNumber(args, "x"),
|
|
1193
|
-
y: readOptionalNumber(args, "y"),
|
|
1194
|
-
w: readOptionalNumber(args, "w"),
|
|
1195
|
-
h: readOptionalNumber(args, "h"),
|
|
1196
|
-
tileSize: readOptionalNumber(args, "tileSize"),
|
|
1197
|
-
tileIds: readStringArray(args.tileIds),
|
|
1198
|
-
includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
|
|
1199
|
-
});
|
|
1200
|
-
case "xnet_canvas_read_selection":
|
|
1201
|
-
return await this.readCanvasSelection({
|
|
1202
|
-
canvasId: readRequiredString(args, "canvasId"),
|
|
1203
|
-
objectIds: readRequiredStringArray(args.objectIds, "objectIds"),
|
|
1204
|
-
includeSourcePreviews: readOptionalBoolean(args, "includeSourcePreviews") ?? false
|
|
1205
|
-
});
|
|
1206
|
-
case "xnet_canvas_search":
|
|
1207
|
-
return await this.searchCanvas({
|
|
1208
|
-
canvasId: readRequiredString(args, "canvasId"),
|
|
1209
|
-
query: readRequiredString(args, "query"),
|
|
1210
|
-
limit: readOptionalNumber(args, "limit")
|
|
1211
|
-
});
|
|
1212
|
-
case "xnet_canvas_export_json_canvas":
|
|
1213
|
-
return await this.exportCanvasJsonCanvas({
|
|
1214
|
-
canvasId: readRequiredString(args, "canvasId"),
|
|
1215
|
-
includeXNetMetadata: readOptionalBoolean(args, "includeXNetMetadata") ?? true,
|
|
1216
|
-
x: readOptionalNumber(args, "x"),
|
|
1217
|
-
y: readOptionalNumber(args, "y"),
|
|
1218
|
-
w: readOptionalNumber(args, "w"),
|
|
1219
|
-
h: readOptionalNumber(args, "h")
|
|
1220
|
-
});
|
|
1221
|
-
case "xnet_canvas_plan_json_canvas_import":
|
|
1222
|
-
return await this.planCanvasJsonCanvasImport(args);
|
|
1223
|
-
case "xnet_plan_canvas_mutation":
|
|
1224
|
-
return await this.planCanvasMutation(args);
|
|
1225
|
-
case "xnet_validate_mutation_plan": {
|
|
1226
|
-
const validation = validateAiMutationPlan(args.plan);
|
|
1227
|
-
return { validation };
|
|
1228
|
-
}
|
|
1229
|
-
default: {
|
|
1230
|
-
const extra = this.extraTools.get(name);
|
|
1231
|
-
if (extra) return await extra.invoke(args);
|
|
1232
|
-
throw new Error(`Unknown AI surface tool: ${name}`);
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1621
|
+
const builtIn = BUILT_IN_TOOLS_BY_NAME.get(name);
|
|
1622
|
+
if (builtIn) return await builtIn.execute(this.host, args);
|
|
1623
|
+
const extra = this.extraTools.get(name);
|
|
1624
|
+
if (extra) return await extra.invoke(args);
|
|
1625
|
+
throw new Error(`Unknown AI surface tool: ${name}`);
|
|
1235
1626
|
}
|
|
1236
1627
|
async readResource(uri) {
|
|
1237
|
-
|
|
1238
|
-
if (uri === "xnet://nodes") {
|
|
1239
|
-
const nodes = await this.config.store.list({ limit: this.limits.maxListLimit });
|
|
1240
|
-
return this.jsonResource(uri, { nodes, count: nodes.length, limit: this.limits.maxListLimit });
|
|
1241
|
-
}
|
|
1242
|
-
if (uri === "xnet://schemas") {
|
|
1243
|
-
return this.jsonResource(uri, { schemas: await this.getSchemaSummaries(true) });
|
|
1244
|
-
}
|
|
1245
|
-
if (parsed.host === "workspace" && parsed.parts[0] === "summary") {
|
|
1246
|
-
return this.jsonResource(uri, await this.getWorkspaceSummary());
|
|
1247
|
-
}
|
|
1248
|
-
if (parsed.host === "workspace" && parsed.parts[0] === "recent") {
|
|
1249
|
-
return this.jsonResource(uri, await this.getRecentNodes());
|
|
1250
|
-
}
|
|
1251
|
-
if (parsed.host === "workspace" && parsed.parts[0] === "search") {
|
|
1252
|
-
return this.jsonResource(
|
|
1253
|
-
uri,
|
|
1254
|
-
await this.search({
|
|
1255
|
-
query: parsed.searchParams.get("q") ?? "",
|
|
1256
|
-
schemaId: parsed.searchParams.get("schema") ?? void 0,
|
|
1257
|
-
limit: readUrlNumber(parsed.searchParams, "limit"),
|
|
1258
|
-
offset: readUrlNumber(parsed.searchParams, "offset")
|
|
1259
|
-
})
|
|
1260
|
-
);
|
|
1261
|
-
}
|
|
1262
|
-
if (parsed.host === "node" && parsed.parts[0]) {
|
|
1263
|
-
return this.jsonResource(uri, await this.getNodeProjection(parsed.parts[0]));
|
|
1264
|
-
}
|
|
1265
|
-
if (parsed.host === "page" && parsed.parts[0]) {
|
|
1266
|
-
const pageId = parsed.parts[0].endsWith(".md") ? parsed.parts[0].slice(0, -".md".length) : parsed.parts[0];
|
|
1267
|
-
if (parsed.parts.length === 1 || parsed.parts[0].endsWith(".md")) {
|
|
1268
|
-
return await this.readPageMarkdown(pageId, true, uri);
|
|
1269
|
-
}
|
|
1270
|
-
if (parsed.parts[1] === "outline") {
|
|
1271
|
-
return this.jsonResource(uri, await this.readPageOutline(pageId));
|
|
1272
|
-
}
|
|
1273
|
-
if (parsed.parts[1] === "context-pack") {
|
|
1274
|
-
return this.jsonResource(
|
|
1275
|
-
uri,
|
|
1276
|
-
await this.createContextPack({ seeds: [{ kind: "page", id: pageId }] })
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
if (parsed.host === "database" && parsed.parts[0]) {
|
|
1281
|
-
const databaseId = parsed.parts[0];
|
|
1282
|
-
if (parsed.parts[1] === "schema") {
|
|
1283
|
-
return this.jsonResource(uri, await this.describeDatabase(databaseId));
|
|
1284
|
-
}
|
|
1285
|
-
if (parsed.parts[1] === "views") {
|
|
1286
|
-
return this.jsonResource(uri, await this.readDatabaseViews(databaseId));
|
|
1287
|
-
}
|
|
1288
|
-
if (parsed.parts[1] === "sample") {
|
|
1289
|
-
return this.jsonResource(
|
|
1290
|
-
uri,
|
|
1291
|
-
await this.sampleDatabase({
|
|
1292
|
-
databaseId,
|
|
1293
|
-
sampleSize: readUrlNumber(parsed.searchParams, "limit")
|
|
1294
|
-
})
|
|
1295
|
-
);
|
|
1296
|
-
}
|
|
1297
|
-
if (parsed.parts[1] === "query") {
|
|
1298
|
-
return this.jsonResource(
|
|
1299
|
-
uri,
|
|
1300
|
-
await this.queryDatabase({
|
|
1301
|
-
databaseId,
|
|
1302
|
-
schemaId: parsed.searchParams.get("schema") ?? void 0,
|
|
1303
|
-
search: parsed.searchParams.get("q") ?? void 0,
|
|
1304
|
-
materializedView: parsed.searchParams.get("view") ? { viewId: parsed.searchParams.get("view") ?? "" } : void 0,
|
|
1305
|
-
limit: readUrlNumber(parsed.searchParams, "limit"),
|
|
1306
|
-
offset: readUrlNumber(parsed.searchParams, "offset")
|
|
1307
|
-
})
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
if (parsed.host === "canvas" && parsed.parts[0]) {
|
|
1312
|
-
const canvasId = parsed.parts[0];
|
|
1313
|
-
if (parsed.parts[1] === "viewport") {
|
|
1314
|
-
return this.jsonResource(
|
|
1315
|
-
uri,
|
|
1316
|
-
await this.readCanvasViewport({
|
|
1317
|
-
canvasId,
|
|
1318
|
-
x: readUrlNumber(parsed.searchParams, "x"),
|
|
1319
|
-
y: readUrlNumber(parsed.searchParams, "y"),
|
|
1320
|
-
w: readUrlNumber(parsed.searchParams, "w"),
|
|
1321
|
-
h: readUrlNumber(parsed.searchParams, "h"),
|
|
1322
|
-
tileSize: readUrlNumber(parsed.searchParams, "tileSize"),
|
|
1323
|
-
tileIds: readCsvStringArray(parsed.searchParams.get("tileIds")),
|
|
1324
|
-
includeSourcePreviews: parsed.searchParams.get("includeSourcePreviews") === "true"
|
|
1325
|
-
})
|
|
1326
|
-
);
|
|
1327
|
-
}
|
|
1328
|
-
if (parsed.parts[1] === "objects") {
|
|
1329
|
-
return this.jsonResource(uri, await this.readCanvasObjects(canvasId));
|
|
1330
|
-
}
|
|
1331
|
-
if (parsed.parts[1] === "selection") {
|
|
1332
|
-
return this.jsonResource(
|
|
1333
|
-
uri,
|
|
1334
|
-
await this.readCanvasSelection({
|
|
1335
|
-
canvasId,
|
|
1336
|
-
objectIds: readCsvStringArray(parsed.searchParams.get("ids")),
|
|
1337
|
-
includeSourcePreviews: parsed.searchParams.get("includeSourcePreviews") !== "false"
|
|
1338
|
-
})
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
if (parsed.parts[1] === "json-canvas") {
|
|
1342
|
-
return this.jsonResource(
|
|
1343
|
-
uri,
|
|
1344
|
-
await this.exportCanvasJsonCanvas({
|
|
1345
|
-
canvasId,
|
|
1346
|
-
includeXNetMetadata: parsed.searchParams.get("includeXNetMetadata") !== "false"
|
|
1347
|
-
})
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
if (parsed.parts[1] === "object" && parsed.parts[2]) {
|
|
1351
|
-
return this.jsonResource(uri, await this.readCanvasObject(canvasId, parsed.parts[2]));
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
1628
|
+
return await BUILT_IN_RESOURCE_ROUTES.resolve(this.host, uri);
|
|
1355
1629
|
}
|
|
1356
1630
|
toJsonText(value, format = "concise") {
|
|
1357
1631
|
return this.stringifyJson(value, format);
|
|
@@ -1448,7 +1722,7 @@ var AiSurfaceService = class {
|
|
|
1448
1722
|
const results = Array.isArray(search.results) ? search.results : [];
|
|
1449
1723
|
const ids = [];
|
|
1450
1724
|
for (const result of results) {
|
|
1451
|
-
if (
|
|
1725
|
+
if (isRecord2(result) && typeof result.id === "string") ids.push(result.id);
|
|
1452
1726
|
}
|
|
1453
1727
|
return ids;
|
|
1454
1728
|
}
|
|
@@ -2090,8 +2364,8 @@ var AiSurfaceService = class {
|
|
|
2090
2364
|
h: options.h,
|
|
2091
2365
|
includeSourcePreviews: false
|
|
2092
2366
|
});
|
|
2093
|
-
const objects = Array.isArray(viewport.objects) ? viewport.objects.filter(
|
|
2094
|
-
const edges = Array.isArray(viewport.edges) ? viewport.edges.filter(
|
|
2367
|
+
const objects = Array.isArray(viewport.objects) ? viewport.objects.filter(isRecord2) : [];
|
|
2368
|
+
const edges = Array.isArray(viewport.edges) ? viewport.edges.filter(isRecord2) : [];
|
|
2095
2369
|
return {
|
|
2096
2370
|
canvasId: options.canvasId,
|
|
2097
2371
|
revision: viewport.revision,
|
|
@@ -2133,7 +2407,7 @@ var AiSurfaceService = class {
|
|
|
2133
2407
|
});
|
|
2134
2408
|
const objects = Array.isArray(viewport.objects) ? viewport.objects : [];
|
|
2135
2409
|
const object = objects.find(
|
|
2136
|
-
(candidate) =>
|
|
2410
|
+
(candidate) => isRecord2(candidate) && readRecordString(candidate, "id") === objectId
|
|
2137
2411
|
);
|
|
2138
2412
|
if (!object) throw new Error(`Canvas object not found: ${objectId}`);
|
|
2139
2413
|
return {
|
|
@@ -2609,7 +2883,7 @@ function invalidPageApplyResult(plan, validation) {
|
|
|
2609
2883
|
return {
|
|
2610
2884
|
applied: false,
|
|
2611
2885
|
pageId: "unknown",
|
|
2612
|
-
planId:
|
|
2886
|
+
planId: isRecord2(plan) && typeof plan.id === "string" ? plan.id : "unknown",
|
|
2613
2887
|
mode: "node-property",
|
|
2614
2888
|
baseRevision: "unknown",
|
|
2615
2889
|
liveRevision: "unknown",
|
|
@@ -2632,7 +2906,7 @@ function rejectedPageApplyResult(input) {
|
|
|
2632
2906
|
};
|
|
2633
2907
|
}
|
|
2634
2908
|
function isMutationPlan(value) {
|
|
2635
|
-
return
|
|
2909
|
+
return isRecord2(value) && typeof value.id === "string" && typeof value.actor === "string" && typeof value.intent === "string" && Array.isArray(value.changes) && isRecord2(value.validation);
|
|
2636
2910
|
}
|
|
2637
2911
|
function createResource(uri, name, description, mimeType, risk, requiredScopes, dynamic = false) {
|
|
2638
2912
|
return {
|
|
@@ -2645,22 +2919,6 @@ function createResource(uri, name, description, mimeType, risk, requiredScopes,
|
|
|
2645
2919
|
dynamic
|
|
2646
2920
|
};
|
|
2647
2921
|
}
|
|
2648
|
-
function parseXNetUri(uri) {
|
|
2649
|
-
let parsed;
|
|
2650
|
-
try {
|
|
2651
|
-
parsed = new URL(uri);
|
|
2652
|
-
} catch {
|
|
2653
|
-
throw new Error(`Invalid xNet resource URI: ${uri}`);
|
|
2654
|
-
}
|
|
2655
|
-
if (parsed.protocol !== "xnet:") {
|
|
2656
|
-
throw new Error(`Invalid xNet resource URI: ${uri}`);
|
|
2657
|
-
}
|
|
2658
|
-
return {
|
|
2659
|
-
host: parsed.hostname,
|
|
2660
|
-
parts: parsed.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part)),
|
|
2661
|
-
searchParams: parsed.searchParams
|
|
2662
|
-
};
|
|
2663
|
-
}
|
|
2664
2922
|
function renderPageMarkdown(node, includeFrontmatter, exportedAt) {
|
|
2665
2923
|
const title = readStringProperty(node, "title") ?? "Untitled Page";
|
|
2666
2924
|
const body = readMarkdownBody(node);
|
|
@@ -2687,7 +2945,7 @@ function readMarkdownBody(node) {
|
|
|
2687
2945
|
const value = readStringProperty(node, "markdown") ?? readStringProperty(node, "content") ?? readStringProperty(node, "body") ?? readStringProperty(node, "text") ?? readStringProperty(node, "description");
|
|
2688
2946
|
if (value) return value;
|
|
2689
2947
|
const richContent = node.properties.content;
|
|
2690
|
-
if (
|
|
2948
|
+
if (isRecord2(richContent)) {
|
|
2691
2949
|
return `\`\`\`json
|
|
2692
2950
|
${JSON.stringify(richContent, null, 2)}
|
|
2693
2951
|
\`\`\``;
|
|
@@ -2748,7 +3006,7 @@ function relationFieldNames(schema) {
|
|
|
2748
3006
|
if (!schema) return [];
|
|
2749
3007
|
const fields = [];
|
|
2750
3008
|
for (const [name, definition] of Object.entries(schema.properties)) {
|
|
2751
|
-
if (
|
|
3009
|
+
if (isRecord2(definition) && definition.type === "relation") fields.push(name);
|
|
2752
3010
|
}
|
|
2753
3011
|
return fields;
|
|
2754
3012
|
}
|
|
@@ -2778,7 +3036,7 @@ function readArrayProperty(node, property) {
|
|
|
2778
3036
|
}
|
|
2779
3037
|
function readNestedArrayProperty(node, objectProperty, arrayProperty) {
|
|
2780
3038
|
const value = node.properties[objectProperty];
|
|
2781
|
-
return
|
|
3039
|
+
return isRecord2(value) && Array.isArray(value[arrayProperty]) ? value[arrayProperty] : void 0;
|
|
2782
3040
|
}
|
|
2783
3041
|
function belongsToDatabase(node, databaseId) {
|
|
2784
3042
|
return readStringProperty(node, "databaseId") === databaseId || readStringProperty(node, "database") === databaseId || readStringProperty(node, "parentDatabaseId") === databaseId;
|
|
@@ -2837,7 +3095,7 @@ function normalizeQuerySearch(value) {
|
|
|
2837
3095
|
const text = value.trim();
|
|
2838
3096
|
return text ? { text } : void 0;
|
|
2839
3097
|
}
|
|
2840
|
-
if (!
|
|
3098
|
+
if (!isRecord2(value) || typeof value.text !== "string" || !value.text.trim()) {
|
|
2841
3099
|
return void 0;
|
|
2842
3100
|
}
|
|
2843
3101
|
const fields = Array.isArray(value.fields) ? value.fields.filter(
|
|
@@ -2853,7 +3111,7 @@ function normalizeQueryMaterializedView(value) {
|
|
|
2853
3111
|
const viewId2 = value.trim();
|
|
2854
3112
|
return viewId2 ? { viewId: viewId2 } : void 0;
|
|
2855
3113
|
}
|
|
2856
|
-
if (!
|
|
3114
|
+
if (!isRecord2(value)) return void 0;
|
|
2857
3115
|
const viewId = readRecordString(value, "viewId");
|
|
2858
3116
|
if (!viewId) return void 0;
|
|
2859
3117
|
const maxAgeMs = readRecordNumber(value, "maxAgeMs");
|
|
@@ -3126,7 +3384,7 @@ function invalidDatabaseApplyResult(plan, validation) {
|
|
|
3126
3384
|
return {
|
|
3127
3385
|
applied: false,
|
|
3128
3386
|
databaseId: "unknown",
|
|
3129
|
-
planId:
|
|
3387
|
+
planId: isRecord2(plan) && typeof plan.id === "string" ? plan.id : "unknown",
|
|
3130
3388
|
baseRevision: "unknown",
|
|
3131
3389
|
liveRevision: "unknown",
|
|
3132
3390
|
appliedChangeIds: [],
|
|
@@ -3144,7 +3402,7 @@ function readTransactionOperations(operation) {
|
|
|
3144
3402
|
);
|
|
3145
3403
|
}
|
|
3146
3404
|
function readTransactionOperation(value, path) {
|
|
3147
|
-
if (!
|
|
3405
|
+
if (!isRecord2(value)) throw new Error(`${path} must be an object`);
|
|
3148
3406
|
if (value.type === "create") {
|
|
3149
3407
|
const options = readRecord(value, "options");
|
|
3150
3408
|
const schemaId = options ? readRecordString(options, "schemaId") : void 0;
|
|
@@ -3237,15 +3495,15 @@ function databaseEntityMatchesId(entity, id) {
|
|
|
3237
3495
|
}
|
|
3238
3496
|
function readRecordArrayProperty(record, key) {
|
|
3239
3497
|
const value = record[key];
|
|
3240
|
-
return Array.isArray(value) ? value.filter(
|
|
3498
|
+
return Array.isArray(value) ? value.filter(isRecord2) : [];
|
|
3241
3499
|
}
|
|
3242
3500
|
function isCanvasNode(node) {
|
|
3243
3501
|
return node.schemaId.includes("/Canvas") || node.schemaId.includes("Canvas@") || Array.isArray(node.properties.objects) || Array.isArray(node.properties.nodes);
|
|
3244
3502
|
}
|
|
3245
3503
|
function readCanvasScene(canvas) {
|
|
3246
3504
|
return {
|
|
3247
|
-
objects: (readArrayProperty(canvas, "objects") ?? readArrayProperty(canvas, "nodes") ?? []).filter(
|
|
3248
|
-
edges: (readArrayProperty(canvas, "edges") ?? readArrayProperty(canvas, "connectors") ?? []).filter(
|
|
3505
|
+
objects: (readArrayProperty(canvas, "objects") ?? readArrayProperty(canvas, "nodes") ?? []).filter(isRecord2).map(normalizeCanvasObject),
|
|
3506
|
+
edges: (readArrayProperty(canvas, "edges") ?? readArrayProperty(canvas, "connectors") ?? []).filter(isRecord2).map(normalizeCanvasEdge)
|
|
3249
3507
|
};
|
|
3250
3508
|
}
|
|
3251
3509
|
function normalizeCanvasObject(object) {
|
|
@@ -3388,8 +3646,8 @@ function canvasObjectFile(object) {
|
|
|
3388
3646
|
return readRecordString(object, "file") ?? readRecordString(object, "filePath") ?? readRecordString(properties ?? {}, "file") ?? readRecordString(properties ?? {}, "filePath");
|
|
3389
3647
|
}
|
|
3390
3648
|
function jsonCanvasDocumentToCanvasOperations(document) {
|
|
3391
|
-
const nodes = Array.isArray(document.nodes) ? document.nodes.filter(
|
|
3392
|
-
const edges = Array.isArray(document.edges) ? document.edges.filter(
|
|
3649
|
+
const nodes = Array.isArray(document.nodes) ? document.nodes.filter(isRecord2) : [];
|
|
3650
|
+
const edges = Array.isArray(document.edges) ? document.edges.filter(isRecord2) : [];
|
|
3393
3651
|
const nodeIds = new Set(nodes.map((node) => readRecordString(node, "id")).filter(Boolean));
|
|
3394
3652
|
const warnings = edges.flatMap((edge) => {
|
|
3395
3653
|
const fromNode = readRecordString(edge, "fromNode");
|
|
@@ -3621,10 +3879,10 @@ function readOperations(value) {
|
|
|
3621
3879
|
throw new Error("operations must contain at least one operation");
|
|
3622
3880
|
}
|
|
3623
3881
|
return value.map((operation, index) => {
|
|
3624
|
-
if (!
|
|
3882
|
+
if (!isRecord2(operation) || typeof operation.op !== "string") {
|
|
3625
3883
|
throw new Error(`operations[${index}].op must be a string`);
|
|
3626
3884
|
}
|
|
3627
|
-
const args =
|
|
3885
|
+
const args = isRecord2(operation.args) ? operation.args : {};
|
|
3628
3886
|
return createAiOperation(
|
|
3629
3887
|
operation.op,
|
|
3630
3888
|
args,
|
|
@@ -3632,15 +3890,6 @@ function readOperations(value) {
|
|
|
3632
3890
|
);
|
|
3633
3891
|
});
|
|
3634
3892
|
}
|
|
3635
|
-
function readContextSeeds(value) {
|
|
3636
|
-
if (!Array.isArray(value)) return [];
|
|
3637
|
-
return value.map((seed) => {
|
|
3638
|
-
if (!isRecord3(seed)) return null;
|
|
3639
|
-
const kind = typeof seed.kind === "string" ? seed.kind : null;
|
|
3640
|
-
const id = typeof seed.id === "string" ? seed.id : null;
|
|
3641
|
-
return kind && id ? { kind, id } : null;
|
|
3642
|
-
}).filter((seed) => seed !== null);
|
|
3643
|
-
}
|
|
3644
3893
|
function uriForSeed(seed) {
|
|
3645
3894
|
switch (seed.kind) {
|
|
3646
3895
|
case "node":
|
|
@@ -3700,75 +3949,6 @@ function stringifyTruncatedJson(text, maxCharacters) {
|
|
|
3700
3949
|
function quoteYaml(value) {
|
|
3701
3950
|
return JSON.stringify(value);
|
|
3702
3951
|
}
|
|
3703
|
-
function readRequiredString(record, key) {
|
|
3704
|
-
const value = record[key];
|
|
3705
|
-
if (typeof value !== "string" || !value.trim()) {
|
|
3706
|
-
throw new Error(`${key} must be a non-empty string`);
|
|
3707
|
-
}
|
|
3708
|
-
return value;
|
|
3709
|
-
}
|
|
3710
|
-
function readRequiredRecord(record, key) {
|
|
3711
|
-
const value = record[key];
|
|
3712
|
-
if (!isRecord3(value)) {
|
|
3713
|
-
throw new Error(`${key} must be an object`);
|
|
3714
|
-
}
|
|
3715
|
-
return value;
|
|
3716
|
-
}
|
|
3717
|
-
function readRequiredStringArray(value, key) {
|
|
3718
|
-
const result = readStringArray(value);
|
|
3719
|
-
if (result.length === 0) {
|
|
3720
|
-
throw new Error(`${key} must contain at least one string`);
|
|
3721
|
-
}
|
|
3722
|
-
return result;
|
|
3723
|
-
}
|
|
3724
|
-
function readStringArray(value) {
|
|
3725
|
-
if (!Array.isArray(value)) return [];
|
|
3726
|
-
return value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
3727
|
-
}
|
|
3728
|
-
function readCsvStringArray(value) {
|
|
3729
|
-
if (!value) return [];
|
|
3730
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
3731
|
-
}
|
|
3732
|
-
function readOptionalString(record, key) {
|
|
3733
|
-
const value = record[key];
|
|
3734
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
3735
|
-
}
|
|
3736
|
-
function readOptionalRecord(record, key) {
|
|
3737
|
-
return readRecord(record, key);
|
|
3738
|
-
}
|
|
3739
|
-
function readOptionalNumber(record, key) {
|
|
3740
|
-
const value = record[key];
|
|
3741
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3742
|
-
}
|
|
3743
|
-
function readOptionalBoolean(record, key) {
|
|
3744
|
-
const value = record[key];
|
|
3745
|
-
return typeof value === "boolean" ? value : void 0;
|
|
3746
|
-
}
|
|
3747
|
-
function readUrlNumber(params, key) {
|
|
3748
|
-
const value = params.get(key);
|
|
3749
|
-
if (value === null) return void 0;
|
|
3750
|
-
const parsed = Number(value);
|
|
3751
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
3752
|
-
}
|
|
3753
|
-
function readRecordString(record, key) {
|
|
3754
|
-
const value = record[key];
|
|
3755
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
3756
|
-
}
|
|
3757
|
-
function readRecord(record, key) {
|
|
3758
|
-
const value = record[key];
|
|
3759
|
-
return isRecord3(value) ? value : void 0;
|
|
3760
|
-
}
|
|
3761
|
-
function readRecordNumber(record, key) {
|
|
3762
|
-
const value = record[key];
|
|
3763
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3764
|
-
}
|
|
3765
|
-
function readRecordBoolean(record, key) {
|
|
3766
|
-
const value = record[key];
|
|
3767
|
-
return typeof value === "boolean" ? value : void 0;
|
|
3768
|
-
}
|
|
3769
|
-
function isRecord3(value) {
|
|
3770
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3771
|
-
}
|
|
3772
3952
|
|
|
3773
3953
|
// src/ai-surface/skill.ts
|
|
3774
3954
|
var XNET_AGENT_SKILL_MD = `---
|