@xnetjs/plugins 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/index.d.ts +3759 -173
- package/dist/index.js +9923 -651
- package/dist/services/node.d.ts +1207 -1
- package/dist/services/node.js +7067 -584
- package/package.json +8 -4
package/dist/services/node.d.ts
CHANGED
|
@@ -1,3 +1,515 @@
|
|
|
1
|
+
import { NodeQueryDescriptor, NodeQueryResult } from '@xnetjs/data';
|
|
2
|
+
import { PublicWriteBudgetPolicy, AbuseSurface, AISignalProvenanceInput } from '@xnetjs/abuse';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AI surface contract types for xNet resources, tools, mutation plans, and audit events.
|
|
7
|
+
*/
|
|
8
|
+
type AiRiskLevel = 'low' | 'medium' | 'high' | 'critical';
|
|
9
|
+
type AiScope = 'workspace.read' | 'workspace.search' | 'page.read' | 'page.propose' | 'page.write' | 'database.read' | 'database.query' | 'database.propose' | 'database.write.rows' | 'database.write.schema' | 'canvas.read' | 'canvas.propose' | 'canvas.write' | 'storage.diagnostics' | 'storage.recovery' | 'network.fetch' | 'agent.workspace.export' | 'agent.workspace.import';
|
|
10
|
+
declare const AI_RISK_LEVELS: readonly AiRiskLevel[];
|
|
11
|
+
declare const AI_SCOPES: readonly AiScope[];
|
|
12
|
+
type AiTargetKind = 'workspace' | 'node' | 'page' | 'database' | 'databaseRows' | 'canvas' | 'storage';
|
|
13
|
+
declare const AI_TARGET_KINDS: readonly AiTargetKind[];
|
|
14
|
+
type AiJsonSchemaType = 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
15
|
+
type AiJsonSchema = {
|
|
16
|
+
type: AiJsonSchemaType;
|
|
17
|
+
description?: string;
|
|
18
|
+
enum?: readonly string[];
|
|
19
|
+
properties?: Record<string, AiJsonSchema>;
|
|
20
|
+
required?: readonly string[];
|
|
21
|
+
items?: AiJsonSchema;
|
|
22
|
+
additionalProperties?: boolean | AiJsonSchema;
|
|
23
|
+
};
|
|
24
|
+
type AiResource = {
|
|
25
|
+
uri: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
mimeType: string;
|
|
29
|
+
risk: AiRiskLevel;
|
|
30
|
+
requiredScopes: AiScope[];
|
|
31
|
+
dynamic?: boolean;
|
|
32
|
+
};
|
|
33
|
+
type AiToolDefinition = {
|
|
34
|
+
name: string;
|
|
35
|
+
title: string;
|
|
36
|
+
description: string;
|
|
37
|
+
risk: AiRiskLevel;
|
|
38
|
+
requiredScopes: AiScope[];
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object';
|
|
41
|
+
properties: Record<string, AiJsonSchema>;
|
|
42
|
+
required?: readonly string[];
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type AiToolCallResult = {
|
|
46
|
+
content: Array<{
|
|
47
|
+
type: 'text';
|
|
48
|
+
text: string;
|
|
49
|
+
}>;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* A tool the AI surface can list and call but whose implementation lives outside
|
|
53
|
+
* the surface (a plugin/connector contribution, exploration 0196). `invoke`
|
|
54
|
+
* returns raw data; the surface serializes it for the model, keeping contributed
|
|
55
|
+
* tools symmetric with the built-in `xnet_*` tools.
|
|
56
|
+
*/
|
|
57
|
+
type AiExtraTool = AiToolDefinition & {
|
|
58
|
+
invoke: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
59
|
+
};
|
|
60
|
+
type AiValidationResult = {
|
|
61
|
+
valid: boolean;
|
|
62
|
+
warnings: string[];
|
|
63
|
+
errors: string[];
|
|
64
|
+
};
|
|
65
|
+
type AiOperation<TArgs extends Record<string, unknown> = Record<string, unknown>> = {
|
|
66
|
+
op: string;
|
|
67
|
+
args: TArgs;
|
|
68
|
+
rationale?: string;
|
|
69
|
+
};
|
|
70
|
+
type AiChangeSet = {
|
|
71
|
+
targetKind: AiTargetKind;
|
|
72
|
+
targetId: string;
|
|
73
|
+
baseRevision: string;
|
|
74
|
+
operations: AiOperation[];
|
|
75
|
+
};
|
|
76
|
+
type AiMutationPlanStatus = 'proposed' | 'validated' | 'applied' | 'rejected';
|
|
77
|
+
type AiMutationPlan = {
|
|
78
|
+
id: string;
|
|
79
|
+
workspaceId?: string;
|
|
80
|
+
actor: string;
|
|
81
|
+
intent: string;
|
|
82
|
+
risk: AiRiskLevel;
|
|
83
|
+
requiredScopes: AiScope[];
|
|
84
|
+
changes: AiChangeSet[];
|
|
85
|
+
validation: AiValidationResult;
|
|
86
|
+
createdAt: string;
|
|
87
|
+
status: AiMutationPlanStatus;
|
|
88
|
+
};
|
|
89
|
+
type AiAuditEvent = {
|
|
90
|
+
id: string;
|
|
91
|
+
planId: string;
|
|
92
|
+
actor: string;
|
|
93
|
+
risk: AiRiskLevel;
|
|
94
|
+
requiredScopes: AiScope[];
|
|
95
|
+
validation: AiValidationResult;
|
|
96
|
+
appliedChangeIds: string[];
|
|
97
|
+
rollbackHandle?: string;
|
|
98
|
+
createdAt: string;
|
|
99
|
+
};
|
|
100
|
+
type AiContextSeed = {
|
|
101
|
+
kind: AiTargetKind;
|
|
102
|
+
id: string;
|
|
103
|
+
};
|
|
104
|
+
type AiContextPackResource = {
|
|
105
|
+
uri: string;
|
|
106
|
+
mimeType: string;
|
|
107
|
+
text: string;
|
|
108
|
+
trust: {
|
|
109
|
+
level: 'workspace' | 'external-untrusted';
|
|
110
|
+
instructionBoundary: string;
|
|
111
|
+
};
|
|
112
|
+
citation: {
|
|
113
|
+
kind: AiTargetKind;
|
|
114
|
+
id: string;
|
|
115
|
+
revision?: string;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
type AiContextPack = {
|
|
119
|
+
id: string;
|
|
120
|
+
query?: string;
|
|
121
|
+
seeds: AiContextSeed[];
|
|
122
|
+
resources: AiContextPackResource[];
|
|
123
|
+
createdAt: string;
|
|
124
|
+
limits: {
|
|
125
|
+
maxResources: number;
|
|
126
|
+
maxCharactersPerResource: number;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
declare function isAiRiskLevel(value: unknown): value is AiRiskLevel;
|
|
130
|
+
declare function isAiScope(value: unknown): value is AiScope;
|
|
131
|
+
declare function isAiTargetKind(value: unknown): value is AiTargetKind;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @xnetjs/plugins — agent tools contribution point (exploration 0196).
|
|
135
|
+
*
|
|
136
|
+
* 0194 Phase 2 (`contributionsAsAiTools`) let the AI call a plugin *command*.
|
|
137
|
+
* This adds first-class, model-facing tools that are NOT tied to a UI command —
|
|
138
|
+
* the shape a Connector uses to expose, say, `slack_search_mentions` that reads
|
|
139
|
+
* the governed node store. Register once via `contributes.agentTools`; the host
|
|
140
|
+
* folds them into the AI surface's `extraTools`, so they appear in the in-app
|
|
141
|
+
* AI, the MCP server, and the files-first skill alike.
|
|
142
|
+
*
|
|
143
|
+
* A tool's `invoke` returns raw data; the surface serializes it for the model,
|
|
144
|
+
* keeping contributed tools symmetric with the built-in `xnet_*` tools.
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
/** Input schema shape, shared with `AiToolDefinition`. */
|
|
148
|
+
type AgentToolInputSchema = AiToolDefinition['inputSchema'];
|
|
149
|
+
/**
|
|
150
|
+
* A plugin-contributed agent tool. Registered under `id`; exposed to the model
|
|
151
|
+
* under `name` (snake_case, plugin-namespaced by convention, e.g.
|
|
152
|
+
* `slack_search_mentions`).
|
|
153
|
+
*/
|
|
154
|
+
interface AgentToolContribution {
|
|
155
|
+
/** Registry key, plugin-scoped (e.g. `dev.xnet.connector.slack.search`). */
|
|
156
|
+
id: string;
|
|
157
|
+
/** Model-facing tool name (snake_case), e.g. `slack_search_mentions`. */
|
|
158
|
+
name: string;
|
|
159
|
+
/** Human title for menus (defaults to `name`). */
|
|
160
|
+
title?: string;
|
|
161
|
+
/** What the tool does — shown to the model. */
|
|
162
|
+
description: string;
|
|
163
|
+
/** Risk surfaced to the agent + consent layer (default `medium`). */
|
|
164
|
+
risk?: AiRiskLevel;
|
|
165
|
+
/** AI scopes this tool requires (default none). */
|
|
166
|
+
requiredScopes?: AiScope[];
|
|
167
|
+
/** JSON schema for the tool args (default: empty object schema). */
|
|
168
|
+
inputSchema?: AgentToolInputSchema;
|
|
169
|
+
/** Execute the tool. Returns raw data; the surface serializes it for the model. */
|
|
170
|
+
invoke: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Validation helpers for AI surface mutation plans.
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
declare function createAiValidationResult(errors?: string[], warnings?: string[]): AiValidationResult;
|
|
178
|
+
/**
|
|
179
|
+
* Validate a serialized mutation plan before it can be previewed or applied.
|
|
180
|
+
*/
|
|
181
|
+
declare function validateAiMutationPlan(plan: unknown): AiValidationResult;
|
|
182
|
+
/**
|
|
183
|
+
* Return a copy of a plan with the latest validation result attached.
|
|
184
|
+
*/
|
|
185
|
+
declare function attachAiPlanValidation(plan: AiMutationPlan): AiMutationPlan;
|
|
186
|
+
declare function serializeAiMutationPlan(plan: AiMutationPlan): string;
|
|
187
|
+
declare function parseAiMutationPlan(serialized: string): {
|
|
188
|
+
plan: AiMutationPlan | null;
|
|
189
|
+
validation: AiValidationResult;
|
|
190
|
+
};
|
|
191
|
+
declare function createAiOperation<TArgs extends Record<string, unknown>>(op: string, args: TArgs, rationale?: string): AiOperation<TArgs>;
|
|
192
|
+
declare function createAiChangeSet(input: AiChangeSet): AiChangeSet;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* AI surface service for focused resources, context packs, and plan-only tools.
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
type AiResourceContent = {
|
|
199
|
+
uri: string;
|
|
200
|
+
mimeType: string;
|
|
201
|
+
text: string;
|
|
202
|
+
};
|
|
203
|
+
type AiSearchResult = {
|
|
204
|
+
id: string;
|
|
205
|
+
schemaId: string;
|
|
206
|
+
title: string;
|
|
207
|
+
snippet: string;
|
|
208
|
+
score: number;
|
|
209
|
+
revision: string;
|
|
210
|
+
updatedAt: number;
|
|
211
|
+
};
|
|
212
|
+
type AiSearchOptions = {
|
|
213
|
+
query: string;
|
|
214
|
+
schemaId?: string;
|
|
215
|
+
limit?: number;
|
|
216
|
+
offset?: number;
|
|
217
|
+
};
|
|
218
|
+
type AiSurfaceLimits = {
|
|
219
|
+
maxListLimit: number;
|
|
220
|
+
maxSearchScan: number;
|
|
221
|
+
maxSearchResults: number;
|
|
222
|
+
maxContextResources: number;
|
|
223
|
+
maxCharactersPerResource: number;
|
|
224
|
+
maxJsonCharacters: number;
|
|
225
|
+
maxCanvasObjects: number;
|
|
226
|
+
maxDatabaseRows: number;
|
|
227
|
+
};
|
|
228
|
+
type AiPageMarkdownApplyAdapterInput = {
|
|
229
|
+
pageId: string;
|
|
230
|
+
markdown: string;
|
|
231
|
+
bodyMarkdown: string;
|
|
232
|
+
baseRevision: string;
|
|
233
|
+
plan: AiMutationPlan;
|
|
234
|
+
operation: AiOperation;
|
|
235
|
+
};
|
|
236
|
+
type AiPageMarkdownApplyAdapterResult = {
|
|
237
|
+
mode: 'tiptap-yjs' | 'yjs' | 'custom';
|
|
238
|
+
yjsField?: string;
|
|
239
|
+
documentUpdate?: unknown;
|
|
240
|
+
warnings?: string[];
|
|
241
|
+
};
|
|
242
|
+
type AiPageMarkdownApplyAdapter = {
|
|
243
|
+
applyMarkdown(input: AiPageMarkdownApplyAdapterInput): Promise<AiPageMarkdownApplyAdapterResult>;
|
|
244
|
+
};
|
|
245
|
+
type AiPageMarkdownApplyResult = {
|
|
246
|
+
applied: boolean;
|
|
247
|
+
pageId: string;
|
|
248
|
+
planId: string;
|
|
249
|
+
mode: 'tiptap-yjs' | 'yjs' | 'custom' | 'node-property';
|
|
250
|
+
baseRevision: string;
|
|
251
|
+
liveRevision: string;
|
|
252
|
+
markdownHash: string;
|
|
253
|
+
bodyMarkdownHash: string;
|
|
254
|
+
validation: {
|
|
255
|
+
valid: boolean;
|
|
256
|
+
errors: string[];
|
|
257
|
+
warnings: string[];
|
|
258
|
+
};
|
|
259
|
+
auditEventId?: string;
|
|
260
|
+
rollbackHandle?: string;
|
|
261
|
+
yjsField?: string;
|
|
262
|
+
documentUpdate?: unknown;
|
|
263
|
+
};
|
|
264
|
+
type AiPageMarkdownRollbackResult = {
|
|
265
|
+
rolledBack: boolean;
|
|
266
|
+
pageId: string;
|
|
267
|
+
planId: string;
|
|
268
|
+
rollbackHandle: string;
|
|
269
|
+
auditEventId?: string;
|
|
270
|
+
validation: {
|
|
271
|
+
valid: boolean;
|
|
272
|
+
errors: string[];
|
|
273
|
+
warnings: string[];
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
/** A node surfaced by an external retriever, with optional provenance. */
|
|
277
|
+
type AiRetrievedNode = {
|
|
278
|
+
nodeId: string;
|
|
279
|
+
/** Human-readable graph path back to an entry node (for citation/provenance). */
|
|
280
|
+
pathLabel?: string;
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Optional graph-aware retriever (exploration 0211). When provided, the `query`
|
|
284
|
+
* path of `createContextPack` uses it instead of the built-in linear keyword
|
|
285
|
+
* scan — i.e. hybrid vector + keyword entry search plus bounded graph expansion,
|
|
286
|
+
* budgeted. The app injects `@xnetjs/brain`'s `retrieve` here (mapping its items
|
|
287
|
+
* to `{ nodeId, pathLabel }`); absent it, context packs fall back to `search()`
|
|
288
|
+
* with no behavior change.
|
|
289
|
+
*/
|
|
290
|
+
type AiContextRetriever = (query: string, options: {
|
|
291
|
+
limit: number;
|
|
292
|
+
}) => Promise<AiRetrievedNode[]>;
|
|
293
|
+
type AiSurfaceServiceConfig = {
|
|
294
|
+
store: NodeStoreAPI;
|
|
295
|
+
schemas: SchemaRegistryAPI;
|
|
296
|
+
limits?: Partial<AiSurfaceLimits>;
|
|
297
|
+
clock?: () => Date;
|
|
298
|
+
pageMarkdownAdapter?: AiPageMarkdownApplyAdapter;
|
|
299
|
+
/**
|
|
300
|
+
* Tools contributed from outside the surface — plugin/connector `agentTools`
|
|
301
|
+
* (exploration 0196). They are appended to `getTools()` and dispatched by
|
|
302
|
+
* `callTool()`, so every consumer (in-app AI, the MCP server, the files-first
|
|
303
|
+
* skill) sees them by registering once. A built-in `xnet_*` name always wins
|
|
304
|
+
* over a colliding extra tool.
|
|
305
|
+
*/
|
|
306
|
+
extraTools?: AiExtraTool[];
|
|
307
|
+
/**
|
|
308
|
+
* Optional graph-aware context retriever (exploration 0211). Drives the
|
|
309
|
+
* `query` path of `createContextPack` when present; falls back to the built-in
|
|
310
|
+
* keyword `search()` otherwise.
|
|
311
|
+
*/
|
|
312
|
+
retrieveContext?: AiContextRetriever;
|
|
313
|
+
};
|
|
314
|
+
declare class AiSurfaceService {
|
|
315
|
+
private readonly config;
|
|
316
|
+
private readonly limits;
|
|
317
|
+
private readonly clock;
|
|
318
|
+
private sequence;
|
|
319
|
+
private auditEvents;
|
|
320
|
+
private readonly rollbackSnapshots;
|
|
321
|
+
/** Contributed tools by name (exploration 0196), de-duped at construction. */
|
|
322
|
+
private readonly extraTools;
|
|
323
|
+
constructor(config: AiSurfaceServiceConfig);
|
|
324
|
+
/** The contributed (non-built-in) tools, with built-in name collisions removed. */
|
|
325
|
+
private getExtraTools;
|
|
326
|
+
getResources(): AiResource[];
|
|
327
|
+
/** The full tool surface: built-in `xnet_*` tools plus contributed extras. */
|
|
328
|
+
getTools(): AiToolDefinition[];
|
|
329
|
+
private builtInTools;
|
|
330
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
331
|
+
readResource(uri: string): Promise<AiResourceContent>;
|
|
332
|
+
toJsonText(value: unknown, format?: 'concise' | 'detailed'): string;
|
|
333
|
+
private getWorkspaceSummary;
|
|
334
|
+
private getRecentNodes;
|
|
335
|
+
search(options: AiSearchOptions): Promise<Record<string, unknown>>;
|
|
336
|
+
/**
|
|
337
|
+
* Candidate node ids for a context-pack query: the injected graph-aware
|
|
338
|
+
* retriever (exploration 0211) when configured, else the built-in keyword
|
|
339
|
+
* `search()`. Order is preserved (best-first).
|
|
340
|
+
*/
|
|
341
|
+
private candidateNodeIdsForQuery;
|
|
342
|
+
createContextPack(options: {
|
|
343
|
+
query?: string;
|
|
344
|
+
seeds?: AiContextSeed[];
|
|
345
|
+
limit?: number;
|
|
346
|
+
}): Promise<AiContextPack>;
|
|
347
|
+
/**
|
|
348
|
+
* Walk typed relation edges out from a node to its connected neighbors, bounded
|
|
349
|
+
* by `hops` (1–2) and a result `limit`. This is the just-in-time companion to
|
|
350
|
+
* `createContextPack` (exploration 0211): retrieval hands the agent a budgeted
|
|
351
|
+
* slice plus expandable node ids, and this lets it pull a specific node's
|
|
352
|
+
* connections on demand instead of over-fetching the whole graph up front.
|
|
353
|
+
*/
|
|
354
|
+
private expandGraph;
|
|
355
|
+
private graphExpansion;
|
|
356
|
+
createExternalContextResource(options: {
|
|
357
|
+
url: string;
|
|
358
|
+
text: string;
|
|
359
|
+
mimeType?: string;
|
|
360
|
+
}): AiContextPackResource;
|
|
361
|
+
private readPageMarkdown;
|
|
362
|
+
private readPageOutline;
|
|
363
|
+
private planPagePatch;
|
|
364
|
+
private applyPageMarkdown;
|
|
365
|
+
private finalizeAppliedPageMarkdown;
|
|
366
|
+
private getAuditLog;
|
|
367
|
+
private rollbackPageMarkdown;
|
|
368
|
+
private recordAuditEvent;
|
|
369
|
+
private describeDatabase;
|
|
370
|
+
private readDatabaseViews;
|
|
371
|
+
private queryDatabase;
|
|
372
|
+
private sampleDatabase;
|
|
373
|
+
private explainDatabaseQuery;
|
|
374
|
+
private executeDatabaseQueryDescriptor;
|
|
375
|
+
private listCanvases;
|
|
376
|
+
private readCanvasObjects;
|
|
377
|
+
private readCanvasViewport;
|
|
378
|
+
private readCanvasSelection;
|
|
379
|
+
private searchCanvas;
|
|
380
|
+
private exportCanvasJsonCanvas;
|
|
381
|
+
private planCanvasJsonCanvasImport;
|
|
382
|
+
private readCanvasObject;
|
|
383
|
+
private hydrateSourcePreviews;
|
|
384
|
+
private planCanvasMutation;
|
|
385
|
+
private planDatabaseMutation;
|
|
386
|
+
private applyDatabaseMutation;
|
|
387
|
+
private applyDatabaseRowOperations;
|
|
388
|
+
private applyDatabaseRowTransactionOperation;
|
|
389
|
+
private rollbackAppliedDatabaseRowChanges;
|
|
390
|
+
private applyDatabaseSchemaOperations;
|
|
391
|
+
private planSurfaceMutation;
|
|
392
|
+
private validatedPlan;
|
|
393
|
+
private getNodeOrThrow;
|
|
394
|
+
private getNodeProjection;
|
|
395
|
+
private getSchemaSummaries;
|
|
396
|
+
private readSchemaSummary;
|
|
397
|
+
private contextResourceForSeed;
|
|
398
|
+
private jsonResource;
|
|
399
|
+
private stringifyJson;
|
|
400
|
+
private nextId;
|
|
401
|
+
private nowIso;
|
|
402
|
+
}
|
|
403
|
+
declare function createAiSurfaceService(config: AiSurfaceServiceConfig): AiSurfaceService;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* The xNet agent skill: a single cross-harness SKILL.md (Claude Code, Codex,
|
|
407
|
+
* Gemini CLI, Cursor) describing the vault checkout and the xnet CLI.
|
|
408
|
+
*
|
|
409
|
+
* This is the primary agent interface contract. Keep it stable between
|
|
410
|
+
* releases (prompt caching) and under the token budget guarded by
|
|
411
|
+
* agent-token-budget tests (~1k tokens).
|
|
412
|
+
*/
|
|
413
|
+
declare const XNET_AGENT_SKILL_MD = "---\nname: xnet\ndescription: Read, search, and edit the user's xNet workspace (pages, databases, canvases) via vault files and the xnet CLI.\n---\n\n# Working with xNet\n\nThis folder is a checkout of the user's xNet workspace. The xNet database\nstays the source of truth; these files are a working tree. Identity lives in\nfrontmatter and the manifest, never in filenames.\n\n## Layout\n\n- `Pages/<slug>.md` \u2014 pages as Markdown with YAML frontmatter\n (`xnet.id`, `xnet.revision`). Never edit frontmatter.\n- `Databases/<slug>.schema.json` + `<slug>.rows.jsonl` \u2014 one JSON object\n per line; edit, append, or remove lines to change rows.\n `<slug>.tsv` sidecars are read-only fast reads.\n- `Canvases/<slug>.canvas` \u2014 JSON Canvas projections.\n- Wikilinks `[[Title]]`, `:::xnet-database` blocks, and\n `{{xnet-ref ...}}` directives are live references \u2014 preserve them.\n\n## Workflow\n\n- Find things: Grep this checkout first; `xnet search \"<text>\"` for ranked\n results (TSV: id, slug, title, snippet).\n- Need more data? `xnet checkout --query \"<text>\"` (or `--schema <iri>`,\n `--kind page|database|canvas`, `--node <id>`) materializes a scoped\n slice into this folder. Never export everything.\n- Query tables: `xnet query <database> --where field=value --format tsv`\n (default TSV; `--format jsonl|json` when you need structure).\n- File edits become validated mutation plans. With `xnet daemon` running\n they are picked up automatically; otherwise run `xnet commit`.\n- `xnet status` lists pending plans and conflicts. Conflicts are quarantined\n in `.xnet/conflicts/` with a Markdown note explaining how to resolve;\n fix the file and save again. Stale edits never silently overwrite.\n- Bulk or aggregate work: `xnet run <script.js>` executes a sandboxed\n script with an `api` object (`api.nodes(schema)`, `api.search(text)`,\n `api.proposeUpdate(id, props)`, `api.proposeCreate(schema, props)`).\n Writes are proposals that flow through the same plan pipeline. Return a\n digest, not raw rows.\n";
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Token-efficient output formats for agent-facing surfaces.
|
|
417
|
+
*
|
|
418
|
+
* TSV is ~2x cheaper than JSON for tabular reads; these helpers are shared by
|
|
419
|
+
* the workspace exporter (.tsv sidecars), the xnet CLI, and MCP responses.
|
|
420
|
+
*/
|
|
421
|
+
/** Render rows as TSV for cheap agent reads. Tabs/newlines collapse to spaces. */
|
|
422
|
+
declare function toTsv(nodeRows: Record<string, unknown>[]): string;
|
|
423
|
+
/** Database rows are node-shaped; lift `properties` to top-level TSV columns. */
|
|
424
|
+
declare function flattenRowForTsv(row: Record<string, unknown>): Record<string, unknown>;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Page Markdown validation for AI-edited xNet page projections.
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
type XNetPageMarkdownFrontmatter = {
|
|
431
|
+
id?: string;
|
|
432
|
+
schemaId?: string;
|
|
433
|
+
revision?: string;
|
|
434
|
+
exportedAt?: string;
|
|
435
|
+
};
|
|
436
|
+
type XNetMarkdownDirective = {
|
|
437
|
+
kind: 'block' | 'inline' | 'wikilink';
|
|
438
|
+
name: string;
|
|
439
|
+
index: number;
|
|
440
|
+
payload?: Record<string, unknown>;
|
|
441
|
+
target?: string;
|
|
442
|
+
};
|
|
443
|
+
type XNetMarkdownDirectiveSpec = {
|
|
444
|
+
kind: 'block' | 'inline' | 'wikilink';
|
|
445
|
+
name: string;
|
|
446
|
+
editorExtension: string;
|
|
447
|
+
description: string;
|
|
448
|
+
};
|
|
449
|
+
type XNetPageMarkdownValidationOptions = {
|
|
450
|
+
pageId?: string;
|
|
451
|
+
schemaId?: string;
|
|
452
|
+
baseRevision?: string;
|
|
453
|
+
};
|
|
454
|
+
type XNetPageMarkdownValidation = {
|
|
455
|
+
frontmatter: XNetPageMarkdownFrontmatter | null;
|
|
456
|
+
directives: XNetMarkdownDirective[];
|
|
457
|
+
validation: AiValidationResult;
|
|
458
|
+
};
|
|
459
|
+
type XNetMarkdownDiffLineKind = 'context' | 'removed' | 'added';
|
|
460
|
+
type XNetMarkdownDiffLine = {
|
|
461
|
+
kind: XNetMarkdownDiffLineKind;
|
|
462
|
+
text: string;
|
|
463
|
+
beforeLine?: number;
|
|
464
|
+
afterLine?: number;
|
|
465
|
+
};
|
|
466
|
+
type XNetMarkdownReviewDiff = {
|
|
467
|
+
kind: 'markdown-diff';
|
|
468
|
+
format: 'line';
|
|
469
|
+
beforeLineCount: number;
|
|
470
|
+
afterLineCount: number;
|
|
471
|
+
lineCount: number;
|
|
472
|
+
unifiedDiff: string;
|
|
473
|
+
lines: XNetMarkdownDiffLine[];
|
|
474
|
+
};
|
|
475
|
+
declare const XNET_MARKDOWN_DIRECTIVE_SPECS: readonly [{
|
|
476
|
+
readonly kind: "block";
|
|
477
|
+
readonly name: "xnet-database";
|
|
478
|
+
readonly editorExtension: "DatabaseEmbedExtension";
|
|
479
|
+
readonly description: "Embeds a database view block with JSON view configuration.";
|
|
480
|
+
}, {
|
|
481
|
+
readonly kind: "block";
|
|
482
|
+
readonly name: "xnet-page";
|
|
483
|
+
readonly editorExtension: "PageEmbedExtension";
|
|
484
|
+
readonly description: "Embeds a page preview block with page identity and preview metadata.";
|
|
485
|
+
}, {
|
|
486
|
+
readonly kind: "block";
|
|
487
|
+
readonly name: "xnet-embed";
|
|
488
|
+
readonly editorExtension: "EmbedExtension";
|
|
489
|
+
readonly description: "Embeds rich external media with provider metadata.";
|
|
490
|
+
}, {
|
|
491
|
+
readonly kind: "inline";
|
|
492
|
+
readonly name: "xnet-ref";
|
|
493
|
+
readonly editorExtension: "SmartReferenceExtension";
|
|
494
|
+
readonly description: "Embeds an inline smart reference to an external or internal resource.";
|
|
495
|
+
}, {
|
|
496
|
+
readonly kind: "inline";
|
|
497
|
+
readonly name: "xnet-db-ref";
|
|
498
|
+
readonly editorExtension: "DatabaseReferenceExtension";
|
|
499
|
+
readonly description: "Embeds an inline database reference.";
|
|
500
|
+
}, {
|
|
501
|
+
readonly kind: "wikilink";
|
|
502
|
+
readonly name: "wikilink";
|
|
503
|
+
readonly editorExtension: "Wikilink";
|
|
504
|
+
readonly description: "Parses [[Page Title]] wikilinks into xNet page links.";
|
|
505
|
+
}];
|
|
506
|
+
declare function validateXNetPageMarkdown(markdown: string, options?: XNetPageMarkdownValidationOptions): XNetPageMarkdownValidation;
|
|
507
|
+
declare function getXNetMarkdownDirectiveSpecs(): readonly XNetMarkdownDirectiveSpec[];
|
|
508
|
+
declare function parseXNetPageFrontmatter(markdown: string): XNetPageMarkdownFrontmatter | null;
|
|
509
|
+
declare function stripXNetPageFrontmatter(markdown: string): string;
|
|
510
|
+
declare function renderMarkdownLineDiff(before: string, after: string): string;
|
|
511
|
+
declare function renderMarkdownReviewDiff(before: string, after: string): XNetMarkdownReviewDiff;
|
|
512
|
+
|
|
1
513
|
/**
|
|
2
514
|
* Local HTTP API Server
|
|
3
515
|
*
|
|
@@ -6,6 +518,16 @@
|
|
|
6
518
|
*
|
|
7
519
|
* This is designed to run in the Electron main process.
|
|
8
520
|
*/
|
|
521
|
+
|
|
522
|
+
type LocalAPITokenScope = 'health.read' | 'nodes.read' | 'nodes.write' | 'schemas.read' | 'events.read' | 'ai.read' | 'ai.write' | 'admin';
|
|
523
|
+
type LocalAPITokenConfig = {
|
|
524
|
+
token: string;
|
|
525
|
+
label?: string;
|
|
526
|
+
scopes: LocalAPITokenScope[];
|
|
527
|
+
aiScopes?: AiScope[];
|
|
528
|
+
createdAt?: string;
|
|
529
|
+
};
|
|
530
|
+
type LocalAPITokenSummary = Omit<LocalAPITokenConfig, 'token'>;
|
|
9
531
|
/**
|
|
10
532
|
* Node store interface (minimal subset needed by API)
|
|
11
533
|
*/
|
|
@@ -16,6 +538,7 @@ interface NodeStoreAPI {
|
|
|
16
538
|
limit?: number;
|
|
17
539
|
offset?: number;
|
|
18
540
|
}): Promise<NodeData[]>;
|
|
541
|
+
query?(descriptor: NodeQueryDescriptor): Promise<NodeQueryResult>;
|
|
19
542
|
create(options: {
|
|
20
543
|
schemaId: string;
|
|
21
544
|
properties: Record<string, unknown>;
|
|
@@ -72,6 +595,8 @@ interface LocalAPIConfig {
|
|
|
72
595
|
host?: string;
|
|
73
596
|
/** API token for authentication (optional) */
|
|
74
597
|
token?: string;
|
|
598
|
+
/** Scoped API tokens for local-only integrations. */
|
|
599
|
+
tokens?: LocalAPITokenConfig[];
|
|
75
600
|
/**
|
|
76
601
|
* Allowed CORS origins (default: none).
|
|
77
602
|
* Set to specific origins like ['http://localhost:3000'] for local dev,
|
|
@@ -83,6 +608,10 @@ interface LocalAPIConfig {
|
|
|
83
608
|
store: NodeStoreAPI;
|
|
84
609
|
/** SchemaRegistry instance */
|
|
85
610
|
schemas: SchemaRegistryAPI;
|
|
611
|
+
/** Shared AI surface service. A default service is created when omitted. */
|
|
612
|
+
aiSurface?: AiSurfaceService;
|
|
613
|
+
/** Optional output and pagination limits for the default AI surface. */
|
|
614
|
+
aiLimits?: Partial<AiSurfaceLimits>;
|
|
86
615
|
}
|
|
87
616
|
/**
|
|
88
617
|
* Local HTTP API server for xNet integrations.
|
|
@@ -109,6 +638,18 @@ declare class LocalAPIServer {
|
|
|
109
638
|
* Check if server is running
|
|
110
639
|
*/
|
|
111
640
|
get isRunning(): boolean;
|
|
641
|
+
/**
|
|
642
|
+
* Return redacted token metadata for UI permission management.
|
|
643
|
+
*/
|
|
644
|
+
getTokenSummaries(): LocalAPITokenSummary[];
|
|
645
|
+
/**
|
|
646
|
+
* Replace scoped local API tokens.
|
|
647
|
+
*/
|
|
648
|
+
setTokens(tokens: LocalAPITokenConfig[]): LocalAPITokenSummary[];
|
|
649
|
+
/**
|
|
650
|
+
* Rotate to a new single scoped token.
|
|
651
|
+
*/
|
|
652
|
+
rotateToken(token: string, options?: Partial<Omit<LocalAPITokenConfig, 'token'>>): LocalAPITokenSummary;
|
|
112
653
|
private handleRequest;
|
|
113
654
|
private handleHealth;
|
|
114
655
|
private handleListNodes;
|
|
@@ -120,6 +661,13 @@ declare class LocalAPIServer {
|
|
|
120
661
|
private handleGetEvents;
|
|
121
662
|
private handleListSchemas;
|
|
122
663
|
private handleGetSchema;
|
|
664
|
+
private handleListAIResources;
|
|
665
|
+
private handleReadAIResource;
|
|
666
|
+
private handleCallAITool;
|
|
667
|
+
private handleAISearch;
|
|
668
|
+
private handleAIContextPack;
|
|
669
|
+
private handleAIPlanValidation;
|
|
670
|
+
private authenticateRequest;
|
|
123
671
|
private parseBody;
|
|
124
672
|
private sendJSON;
|
|
125
673
|
private sendError;
|
|
@@ -144,6 +692,92 @@ declare class LocalAPIServer {
|
|
|
144
692
|
*/
|
|
145
693
|
declare function createLocalAPI(config: LocalAPIConfig): LocalAPIServer;
|
|
146
694
|
|
|
695
|
+
/**
|
|
696
|
+
* Write guardrail for the MCP server (exploration 0175, "boundary hardening").
|
|
697
|
+
*
|
|
698
|
+
* The 0175 pitch is that xNet's guardrail makes an autonomous agent safe to
|
|
699
|
+
* point at a workspace. But the generic `xnet_create/update/delete` tools used
|
|
700
|
+
* to mutate the store directly, bypassing the mutation-plan guardrail that the
|
|
701
|
+
* page/database tools enforce. This closes that gap for *every* MCP client
|
|
702
|
+
* (OpenClaw, Claude Code, …) at the boundary, independent of the client's own
|
|
703
|
+
* (often weak) safety model:
|
|
704
|
+
*
|
|
705
|
+
* - **Risk classification.** Deletes are `high`; creating an outward-facing node
|
|
706
|
+
* (e.g. a chat message — "sending") is `high`; ordinary creates/updates are
|
|
707
|
+
* `low`.
|
|
708
|
+
* - **Confirmation gate.** `high`/`critical` and outward-facing writes return
|
|
709
|
+
* `needs-confirmation` instead of mutating, until the caller re-issues with
|
|
710
|
+
* `confirm: true` (after the human approves — see the ClawHub skill).
|
|
711
|
+
* - **Cost budget.** Writes are charged against a per-surface budget
|
|
712
|
+
* (`@xnetjs/abuse`); a runaway agent is throttled, not unbounded.
|
|
713
|
+
* - **Provenance + audit.** Applied writes are recorded with their risk and an
|
|
714
|
+
* optional AI-provenance evidence ref, queryable for review.
|
|
715
|
+
*/
|
|
716
|
+
|
|
717
|
+
type McpWriteKind = 'create' | 'update' | 'delete';
|
|
718
|
+
interface McpWriteRequest {
|
|
719
|
+
kind: McpWriteKind;
|
|
720
|
+
/** Schema IRI (for create). Used to detect outward-facing writes. */
|
|
721
|
+
schemaId?: string;
|
|
722
|
+
/** Target node id (for update/delete). */
|
|
723
|
+
nodeId?: string;
|
|
724
|
+
/** Caller confirmation; required to apply high-risk / outward-facing writes. */
|
|
725
|
+
confirm?: boolean;
|
|
726
|
+
/** Optional provenance from the calling agent (model that authored the write). */
|
|
727
|
+
provenance?: AISignalProvenanceInput;
|
|
728
|
+
}
|
|
729
|
+
type McpWriteVerdict = {
|
|
730
|
+
decision: 'allow';
|
|
731
|
+
risk: AiRiskLevel;
|
|
732
|
+
outwardFacing: boolean;
|
|
733
|
+
provenanceRef: string | null;
|
|
734
|
+
} | {
|
|
735
|
+
decision: 'needs-confirmation';
|
|
736
|
+
risk: AiRiskLevel;
|
|
737
|
+
outwardFacing: boolean;
|
|
738
|
+
reason: string;
|
|
739
|
+
} | {
|
|
740
|
+
decision: 'blocked';
|
|
741
|
+
risk: AiRiskLevel;
|
|
742
|
+
reason: string;
|
|
743
|
+
};
|
|
744
|
+
interface McpWriteAuditEvent {
|
|
745
|
+
kind: McpWriteKind;
|
|
746
|
+
risk: AiRiskLevel;
|
|
747
|
+
outwardFacing: boolean;
|
|
748
|
+
schemaId?: string;
|
|
749
|
+
nodeId?: string;
|
|
750
|
+
provenanceRef?: string;
|
|
751
|
+
at: number;
|
|
752
|
+
}
|
|
753
|
+
interface McpWriteGuardrailOptions {
|
|
754
|
+
/** Schema IRIs whose creation is outward-facing (raises risk, requires confirm). */
|
|
755
|
+
outwardFacingSchemas?: readonly string[];
|
|
756
|
+
/** Cost budget policy. Defaults to 120 writes / 60s on the surface. */
|
|
757
|
+
budgetPolicy?: PublicWriteBudgetPolicy;
|
|
758
|
+
/** Abuse surface for budget keys. Defaults to `localApi`. */
|
|
759
|
+
surface?: AbuseSurface;
|
|
760
|
+
/** Injectable clock (ms). Defaults to `Date.now`. */
|
|
761
|
+
clock?: () => number;
|
|
762
|
+
}
|
|
763
|
+
declare class McpWriteGuardrail {
|
|
764
|
+
private readonly outward;
|
|
765
|
+
private readonly policy;
|
|
766
|
+
private readonly surface;
|
|
767
|
+
private readonly now;
|
|
768
|
+
private usage;
|
|
769
|
+
private auditLog;
|
|
770
|
+
constructor(options?: McpWriteGuardrailOptions);
|
|
771
|
+
/** Classify and gate a write. Does not mutate anything; charges budget only on `allow`. */
|
|
772
|
+
evaluate(req: McpWriteRequest): McpWriteVerdict;
|
|
773
|
+
/** Record an applied write for audit/rollback review. Returns the event. */
|
|
774
|
+
recordApplied(req: McpWriteRequest, verdict: Extract<McpWriteVerdict, {
|
|
775
|
+
decision: 'allow';
|
|
776
|
+
}>, nodeId?: string): McpWriteAuditEvent;
|
|
777
|
+
getAuditLog(limit?: number): McpWriteAuditEvent[];
|
|
778
|
+
private classify;
|
|
779
|
+
}
|
|
780
|
+
|
|
147
781
|
/**
|
|
148
782
|
* MCP Server for xNet
|
|
149
783
|
*
|
|
@@ -158,7 +792,15 @@ declare function createLocalAPI(config: LocalAPIConfig): LocalAPIServer;
|
|
|
158
792
|
*/
|
|
159
793
|
interface MCPTool {
|
|
160
794
|
name: string;
|
|
795
|
+
title?: string;
|
|
161
796
|
description: string;
|
|
797
|
+
risk?: string;
|
|
798
|
+
requiredScopes?: string[];
|
|
799
|
+
/**
|
|
800
|
+
* Tool Search Tool hint (advanced-tool-use): deferred tools are discovered
|
|
801
|
+
* on demand instead of paying their definition tokens on every turn.
|
|
802
|
+
*/
|
|
803
|
+
defer_loading?: boolean;
|
|
162
804
|
inputSchema: {
|
|
163
805
|
type: 'object';
|
|
164
806
|
properties: Record<string, MCPPropertySchema>;
|
|
@@ -171,7 +813,11 @@ interface MCPTool {
|
|
|
171
813
|
interface MCPPropertySchema {
|
|
172
814
|
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
173
815
|
description?: string;
|
|
816
|
+
enum?: readonly string[];
|
|
817
|
+
properties?: Record<string, MCPPropertySchema>;
|
|
818
|
+
required?: string[];
|
|
174
819
|
items?: MCPPropertySchema;
|
|
820
|
+
additionalProperties?: boolean | MCPPropertySchema;
|
|
175
821
|
}
|
|
176
822
|
/**
|
|
177
823
|
* MCP Resource definition
|
|
@@ -181,6 +827,9 @@ interface MCPResource {
|
|
|
181
827
|
name: string;
|
|
182
828
|
description?: string;
|
|
183
829
|
mimeType?: string;
|
|
830
|
+
risk?: string;
|
|
831
|
+
requiredScopes?: string[];
|
|
832
|
+
dynamic?: boolean;
|
|
184
833
|
}
|
|
185
834
|
/**
|
|
186
835
|
* MCP Request from client
|
|
@@ -210,6 +859,23 @@ interface MCPResponse {
|
|
|
210
859
|
interface MCPServerConfig {
|
|
211
860
|
store: NodeStoreAPI;
|
|
212
861
|
schemas: SchemaRegistryAPI;
|
|
862
|
+
/** Shared AI surface service. A default service is created when omitted. */
|
|
863
|
+
aiSurface?: AiSurfaceService;
|
|
864
|
+
/** Optional output and pagination limits for the default AI surface. */
|
|
865
|
+
aiLimits?: Partial<AiSurfaceLimits>;
|
|
866
|
+
/**
|
|
867
|
+
* Plugin/connector agent tools to expose (exploration 0196). Folded into the
|
|
868
|
+
* default AI surface's `extraTools`, so they appear in `tools/list` (deferred,
|
|
869
|
+
* since not in {@link MCP_CORE_TOOL_NAMES}) and dispatch through `tools/call`.
|
|
870
|
+
* Ignored when a pre-built `aiSurface` is supplied — wire `extraTools` there.
|
|
871
|
+
*/
|
|
872
|
+
agentTools?: AgentToolContribution[];
|
|
873
|
+
/**
|
|
874
|
+
* Write guardrail for the generic + first-class write tools. A default
|
|
875
|
+
* guardrail (delete/outward writes need confirmation, cost budget, audit) is
|
|
876
|
+
* created when omitted. Pass a configured instance to tune it.
|
|
877
|
+
*/
|
|
878
|
+
guardrail?: McpWriteGuardrail;
|
|
213
879
|
/** Server name (default: 'xnet') */
|
|
214
880
|
name?: string;
|
|
215
881
|
/** Server version (default: '1.0.0') */
|
|
@@ -240,6 +906,7 @@ interface MCPServerConfig {
|
|
|
240
906
|
declare class MCPServer {
|
|
241
907
|
private config;
|
|
242
908
|
private tools;
|
|
909
|
+
private aiToolNames;
|
|
243
910
|
private running;
|
|
244
911
|
constructor(config: MCPServerConfig);
|
|
245
912
|
/**
|
|
@@ -273,12 +940,22 @@ declare class MCPServer {
|
|
|
273
940
|
* Reads JSON-RPC requests from stdin and writes responses to stdout.
|
|
274
941
|
*/
|
|
275
942
|
startStdio(): Promise<void>;
|
|
943
|
+
/** Parse one stdin line as a JSON-RPC request and write the response. */
|
|
944
|
+
private handleStdioLine;
|
|
276
945
|
/**
|
|
277
946
|
* Stop the stdio server
|
|
278
947
|
*/
|
|
279
948
|
stop(): void;
|
|
280
949
|
private registerTools;
|
|
281
950
|
private handleToolCall;
|
|
951
|
+
/**
|
|
952
|
+
* Route a write through the guardrail. Returns a gate result
|
|
953
|
+
* (`requiresConfirmation` / `blocked`) without mutating, or applies + records
|
|
954
|
+
* an audit entry and returns the applied value.
|
|
955
|
+
*/
|
|
956
|
+
private guardedWrite;
|
|
957
|
+
/** Shared create path for the first-class write tools. */
|
|
958
|
+
private createNode;
|
|
282
959
|
private handleResourceRead;
|
|
283
960
|
}
|
|
284
961
|
/**
|
|
@@ -300,6 +977,535 @@ declare class MCPServer {
|
|
|
300
977
|
*/
|
|
301
978
|
declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
302
979
|
|
|
980
|
+
/**
|
|
981
|
+
* HTTP transport for the xNet MCP server (exploration 0175).
|
|
982
|
+
*
|
|
983
|
+
* The {@link MCPServer} ships a stdio transport for process-spawned clients
|
|
984
|
+
* (Claude Code, Codex, OpenClaw `mcp.servers` with `transport: "stdio"`). A
|
|
985
|
+
* browser or an HTTP-only MCP client (OpenClaw `transport: "streamable-http"`)
|
|
986
|
+
* cannot speak stdio, so this exposes the same `handleRequest` JSON-RPC surface
|
|
987
|
+
* over HTTP.
|
|
988
|
+
*
|
|
989
|
+
* Because the same surface can mutate a user's workspace, the transport is a
|
|
990
|
+
* trust boundary and is hardened accordingly (exploration 0175, "boundary
|
|
991
|
+
* hardening"):
|
|
992
|
+
*
|
|
993
|
+
* - **Loopback only.** Binds `127.0.0.1`/`::1`; refuses to start on a
|
|
994
|
+
* non-loopback host so the substrate is never exposed to the network the way
|
|
995
|
+
* OpenClaw's own `0.0.0.0:18789` default famously is.
|
|
996
|
+
* - **Pairing token.** Every JSON-RPC request must carry the shared
|
|
997
|
+
* `x-xnet-pairing` secret; compared in constant time.
|
|
998
|
+
* - **Origin allowlist.** Browser requests (those carrying an `Origin`) must
|
|
999
|
+
* match the allowlist; never reflects `*`. Non-browser clients (no `Origin`)
|
|
1000
|
+
* are allowed through to the token check.
|
|
1001
|
+
* - **Private Network Access.** Emits `Access-Control-Allow-Private-Network`
|
|
1002
|
+
* and answers `OPTIONS` preflights so Chrome's Local Network Access flow can
|
|
1003
|
+
* reach a loopback substrate from an https origin.
|
|
1004
|
+
*
|
|
1005
|
+
* The guardrail itself (mutation plans, approval, audit) lives in the
|
|
1006
|
+
* `AiSurfaceService` behind `MCPServer`, so it is preserved across transports —
|
|
1007
|
+
* this layer only governs *who* may talk to the server.
|
|
1008
|
+
*/
|
|
1009
|
+
|
|
1010
|
+
interface McpHttpServerConfig {
|
|
1011
|
+
/** The MCP server whose `handleRequest` is exposed over HTTP. */
|
|
1012
|
+
server: MCPServer;
|
|
1013
|
+
/**
|
|
1014
|
+
* Shared secret required in the `x-xnet-pairing` header. A cryptographically
|
|
1015
|
+
* random token is generated when omitted; read it back from
|
|
1016
|
+
* {@link McpHttpServerHandle.pairingToken} to hand to the client.
|
|
1017
|
+
*/
|
|
1018
|
+
pairingToken?: string;
|
|
1019
|
+
/**
|
|
1020
|
+
* Browser origins permitted to call the server. Requests that send an
|
|
1021
|
+
* `Origin` header not in this list are rejected with 403. Requests without an
|
|
1022
|
+
* `Origin` (CLI/native MCP clients) bypass this check and are gated by the
|
|
1023
|
+
* pairing token alone. Defaults to none (browser-less).
|
|
1024
|
+
*/
|
|
1025
|
+
allowedOrigins?: readonly string[];
|
|
1026
|
+
/** Loopback host to bind. Defaults to `127.0.0.1`. Non-loopback hosts throw. */
|
|
1027
|
+
host?: string;
|
|
1028
|
+
/** Port to bind. Defaults to `31416`; pass `0` for an ephemeral port. */
|
|
1029
|
+
port?: number;
|
|
1030
|
+
/** JSON-RPC endpoint path. Defaults to `/mcp`. */
|
|
1031
|
+
path?: string;
|
|
1032
|
+
}
|
|
1033
|
+
interface McpHttpServerHandle {
|
|
1034
|
+
/** Resolved base URL, e.g. `http://127.0.0.1:31416`. Valid after `start()`. */
|
|
1035
|
+
readonly url: string;
|
|
1036
|
+
/** Bound port. Valid after `start()` (reflects the OS-assigned port if 0). */
|
|
1037
|
+
readonly port: number;
|
|
1038
|
+
/** The pairing token clients must present. */
|
|
1039
|
+
readonly pairingToken: string;
|
|
1040
|
+
/** JSON-RPC endpoint path. */
|
|
1041
|
+
readonly path: string;
|
|
1042
|
+
start(): Promise<void>;
|
|
1043
|
+
stop(): Promise<void>;
|
|
1044
|
+
/** The raw Node request handler — exposed for tests and embedding. */
|
|
1045
|
+
readonly handler: (req: IncomingMessage, res: ServerResponse) => void;
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Create (but do not start) an HTTP transport for an {@link MCPServer}.
|
|
1049
|
+
*
|
|
1050
|
+
* @throws if `host` is not a loopback address.
|
|
1051
|
+
*/
|
|
1052
|
+
declare function createMcpHttpServer(config: McpHttpServerConfig): McpHttpServerHandle;
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Node-only AI workspace folder exporter.
|
|
1056
|
+
*/
|
|
1057
|
+
|
|
1058
|
+
type AiWorkspaceExportKind = 'page' | 'database' | 'canvas' | 'node';
|
|
1059
|
+
type AiWorkspaceExportScope = {
|
|
1060
|
+
nodeIds?: string[];
|
|
1061
|
+
schemaIds?: string[];
|
|
1062
|
+
/** Search query; matching nodes are materialized into the checkout. */
|
|
1063
|
+
query?: string;
|
|
1064
|
+
/** Folder-style scope: export every node of these kinds (bounded by limit). */
|
|
1065
|
+
kinds?: AiWorkspaceExportKind[];
|
|
1066
|
+
limit?: number;
|
|
1067
|
+
};
|
|
1068
|
+
type AiWorkspaceExportOptions = {
|
|
1069
|
+
rootDir: string;
|
|
1070
|
+
workspaceName?: string;
|
|
1071
|
+
scope?: AiWorkspaceExportScope;
|
|
1072
|
+
};
|
|
1073
|
+
type AiWorkspaceCheckoutOptions = {
|
|
1074
|
+
rootDir: string;
|
|
1075
|
+
workspaceName?: string;
|
|
1076
|
+
/** Required scope: a checkout always materializes an explicit slice. */
|
|
1077
|
+
scope: AiWorkspaceExportScope;
|
|
1078
|
+
};
|
|
1079
|
+
type AiWorkspaceManifestEntry = {
|
|
1080
|
+
path: string;
|
|
1081
|
+
kind: AiWorkspaceExportKind;
|
|
1082
|
+
id: string;
|
|
1083
|
+
schemaId: string;
|
|
1084
|
+
revision: string;
|
|
1085
|
+
sha256: string;
|
|
1086
|
+
exportedAt: string;
|
|
1087
|
+
};
|
|
1088
|
+
type AiWorkspaceExportResult = {
|
|
1089
|
+
rootDir: string;
|
|
1090
|
+
files: string[];
|
|
1091
|
+
manifestEntries: AiWorkspaceManifestEntry[];
|
|
1092
|
+
exportedAt: string;
|
|
1093
|
+
incremental: boolean;
|
|
1094
|
+
skippedNodeIds: string[];
|
|
1095
|
+
};
|
|
1096
|
+
type AiWorkspaceExporterConfig = {
|
|
1097
|
+
store: NodeStoreAPI;
|
|
1098
|
+
schemas: SchemaRegistryAPI;
|
|
1099
|
+
aiSurface?: AiSurfaceService;
|
|
1100
|
+
clock?: () => Date;
|
|
1101
|
+
/**
|
|
1102
|
+
* Generate a read-optimized `.tsv` sidecar for databases with at least this
|
|
1103
|
+
* many rows. JSONL stays the write format; sidecars are read-only.
|
|
1104
|
+
*/
|
|
1105
|
+
tsvSidecarMinRows?: number;
|
|
1106
|
+
};
|
|
1107
|
+
type AiWorkspaceChangedFileStatus = 'modified' | 'missing';
|
|
1108
|
+
type AiWorkspaceChangedFile = {
|
|
1109
|
+
path: string;
|
|
1110
|
+
kind: AiWorkspaceExportKind;
|
|
1111
|
+
id: string;
|
|
1112
|
+
status: AiWorkspaceChangedFileStatus;
|
|
1113
|
+
previousSha256: string;
|
|
1114
|
+
currentSha256?: string;
|
|
1115
|
+
};
|
|
1116
|
+
type AiWorkspacePendingPlan = {
|
|
1117
|
+
path: string;
|
|
1118
|
+
planPath: string;
|
|
1119
|
+
plan: AiMutationPlan;
|
|
1120
|
+
currentSha256: string;
|
|
1121
|
+
};
|
|
1122
|
+
type AiWorkspaceConflictKind = 'missing-file' | 'stale-export' | 'invalid-json' | 'invalid-jsonl' | 'invalid-plan' | 'validation-warning' | 'unsupported-change' | 'tool-error';
|
|
1123
|
+
type AiWorkspaceConflict = {
|
|
1124
|
+
kind: AiWorkspaceConflictKind;
|
|
1125
|
+
path: string;
|
|
1126
|
+
id?: string;
|
|
1127
|
+
message: string;
|
|
1128
|
+
conflictPath?: string;
|
|
1129
|
+
/** Human/agent-readable Markdown note with resolution instructions. */
|
|
1130
|
+
notePath?: string;
|
|
1131
|
+
detectedAt: string;
|
|
1132
|
+
};
|
|
1133
|
+
type AiWorkspaceReviewStatus = 'needs-review';
|
|
1134
|
+
type AiWorkspaceReviewAction = 'approve' | 'reject' | 'request-revision';
|
|
1135
|
+
type AiWorkspaceReviewEntryKind = 'pending-plan' | 'conflict';
|
|
1136
|
+
type AiWorkspaceReviewEntry = {
|
|
1137
|
+
kind: AiWorkspaceReviewEntryKind;
|
|
1138
|
+
status: AiWorkspaceReviewStatus;
|
|
1139
|
+
path: string;
|
|
1140
|
+
id?: string;
|
|
1141
|
+
title: string;
|
|
1142
|
+
message: string;
|
|
1143
|
+
artifactPath?: string;
|
|
1144
|
+
planId?: string;
|
|
1145
|
+
planPath?: string;
|
|
1146
|
+
conflictKind?: AiWorkspaceConflictKind;
|
|
1147
|
+
conflictPath?: string;
|
|
1148
|
+
risk?: AiRiskLevel;
|
|
1149
|
+
requiredScopes?: AiScope[];
|
|
1150
|
+
suggestedActions: AiWorkspaceReviewAction[];
|
|
1151
|
+
createdAt: string;
|
|
1152
|
+
};
|
|
1153
|
+
type AiWorkspaceReviewIndex = {
|
|
1154
|
+
rootDir: string;
|
|
1155
|
+
generatedAt: string;
|
|
1156
|
+
entries: AiWorkspaceReviewEntry[];
|
|
1157
|
+
};
|
|
1158
|
+
type AiWorkspaceWatcherScanOptions = {
|
|
1159
|
+
rootDir: string;
|
|
1160
|
+
actor?: string;
|
|
1161
|
+
writePendingPlans?: boolean;
|
|
1162
|
+
writeConflicts?: boolean;
|
|
1163
|
+
writeReviewIndex?: boolean;
|
|
1164
|
+
};
|
|
1165
|
+
type AiWorkspaceWatcherScanResult = {
|
|
1166
|
+
rootDir: string;
|
|
1167
|
+
scannedAt: string;
|
|
1168
|
+
changedFiles: AiWorkspaceChangedFile[];
|
|
1169
|
+
pendingPlans: AiWorkspacePendingPlan[];
|
|
1170
|
+
conflicts: AiWorkspaceConflict[];
|
|
1171
|
+
review: AiWorkspaceReviewIndex;
|
|
1172
|
+
};
|
|
1173
|
+
type AiWorkspaceWatchOptions = AiWorkspaceWatcherScanOptions & {
|
|
1174
|
+
/** Force interval polling instead of fs.watch (reliable cross-platform). */
|
|
1175
|
+
usePolling?: boolean;
|
|
1176
|
+
/** Poll interval when polling is active. Defaults to 2000ms. */
|
|
1177
|
+
pollIntervalMs?: number;
|
|
1178
|
+
};
|
|
1179
|
+
type AiWorkspaceWatchHandle = {
|
|
1180
|
+
close(): void;
|
|
1181
|
+
/** True when the handle fell back to (or was configured for) polling. */
|
|
1182
|
+
isPolling(): boolean;
|
|
1183
|
+
};
|
|
1184
|
+
declare class AiWorkspaceExporter {
|
|
1185
|
+
private readonly config;
|
|
1186
|
+
private readonly aiSurface;
|
|
1187
|
+
private readonly clock;
|
|
1188
|
+
constructor(config: AiWorkspaceExporterConfig);
|
|
1189
|
+
exportWorkspace(options: AiWorkspaceExportOptions): Promise<AiWorkspaceExportResult>;
|
|
1190
|
+
exportWorkspaceIncremental(options: AiWorkspaceExportOptions): Promise<AiWorkspaceExportResult>;
|
|
1191
|
+
/**
|
|
1192
|
+
* Lazily materialize an explicit slice of the workspace into the checkout.
|
|
1193
|
+
*
|
|
1194
|
+
* Unlike exportWorkspace there is no default "export everything up to the
|
|
1195
|
+
* limit" behavior: a checkout always names what it wants (query, schema,
|
|
1196
|
+
* node ids, or kind folders). Repeated checkouts are incremental and merge
|
|
1197
|
+
* with whatever is already checked out.
|
|
1198
|
+
*/
|
|
1199
|
+
checkout(options: AiWorkspaceCheckoutOptions): Promise<AiWorkspaceExportResult>;
|
|
1200
|
+
private exportWorkspaceProjection;
|
|
1201
|
+
private resolveNodes;
|
|
1202
|
+
private resolveNodeIdScope;
|
|
1203
|
+
private resolveSchemaScope;
|
|
1204
|
+
private resolveQueryScope;
|
|
1205
|
+
private resolveKindScope;
|
|
1206
|
+
private ensureBaseFolders;
|
|
1207
|
+
private writeSupportFiles;
|
|
1208
|
+
private exportPage;
|
|
1209
|
+
private exportDatabase;
|
|
1210
|
+
private exportCanvas;
|
|
1211
|
+
private exportNode;
|
|
1212
|
+
private writeManifest;
|
|
1213
|
+
private writeText;
|
|
1214
|
+
}
|
|
1215
|
+
declare class AiWorkspaceWatcher {
|
|
1216
|
+
private readonly config;
|
|
1217
|
+
private readonly aiSurface;
|
|
1218
|
+
private readonly clock;
|
|
1219
|
+
constructor(config: AiWorkspaceExporterConfig);
|
|
1220
|
+
scanChangedFiles(options: AiWorkspaceWatcherScanOptions): Promise<AiWorkspaceWatcherScanResult>;
|
|
1221
|
+
watchWorkspace(options: AiWorkspaceWatchOptions, onScan: (result: AiWorkspaceWatcherScanResult) => void | Promise<void>): AiWorkspaceWatchHandle;
|
|
1222
|
+
private planChangedFile;
|
|
1223
|
+
private createStaleExportConflict;
|
|
1224
|
+
private planDatabaseProjection;
|
|
1225
|
+
private planCanvasProjection;
|
|
1226
|
+
private createGenericProjectionPlan;
|
|
1227
|
+
private expectPlan;
|
|
1228
|
+
private writePendingPlan;
|
|
1229
|
+
private createConflict;
|
|
1230
|
+
private writeReviewIndex;
|
|
1231
|
+
}
|
|
1232
|
+
declare function createAiWorkspaceExporter(config: AiWorkspaceExporterConfig): AiWorkspaceExporter;
|
|
1233
|
+
declare function createAiWorkspaceWatcher(config: AiWorkspaceExporterConfig): AiWorkspaceWatcher;
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* ScriptContext - Safe API surface for user scripts
|
|
1237
|
+
*
|
|
1238
|
+
* Provides a frozen, read-only context with utility functions.
|
|
1239
|
+
* Scripts cannot modify nodes directly - they return values/mutations
|
|
1240
|
+
* that the runtime applies.
|
|
1241
|
+
*/
|
|
1242
|
+
/**
|
|
1243
|
+
* A flat node representation (simplified for script access)
|
|
1244
|
+
*/
|
|
1245
|
+
interface FlatNode {
|
|
1246
|
+
id: string;
|
|
1247
|
+
schemaIRI: string;
|
|
1248
|
+
[key: string]: unknown;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Format helper functions (no side effects)
|
|
1252
|
+
*/
|
|
1253
|
+
interface FormatHelpers {
|
|
1254
|
+
/** Format a timestamp as a date string */
|
|
1255
|
+
date: (timestamp: number, options?: Intl.DateTimeFormatOptions) => string;
|
|
1256
|
+
/** Format a number with options */
|
|
1257
|
+
number: (value: number, options?: Intl.NumberFormatOptions) => string;
|
|
1258
|
+
/** Format a number as currency */
|
|
1259
|
+
currency: (value: number, currency?: string, locale?: string) => string;
|
|
1260
|
+
/** Format a timestamp as relative time (e.g., "5m ago") */
|
|
1261
|
+
relative: (timestamp: number) => string;
|
|
1262
|
+
/** Format bytes as human-readable size */
|
|
1263
|
+
bytes: (bytes: number) => string;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Math helper functions (pure functions)
|
|
1267
|
+
*/
|
|
1268
|
+
interface MathHelpers {
|
|
1269
|
+
/** Sum an array of numbers */
|
|
1270
|
+
sum: (values: number[]) => number;
|
|
1271
|
+
/** Average of an array of numbers */
|
|
1272
|
+
avg: (values: number[]) => number;
|
|
1273
|
+
/** Minimum value */
|
|
1274
|
+
min: (values: number[]) => number;
|
|
1275
|
+
/** Maximum value */
|
|
1276
|
+
max: (values: number[]) => number;
|
|
1277
|
+
/** Round to decimal places */
|
|
1278
|
+
round: (value: number, decimals?: number) => number;
|
|
1279
|
+
/** Clamp value between min and max */
|
|
1280
|
+
clamp: (value: number, min: number, max: number) => number;
|
|
1281
|
+
/** Absolute value */
|
|
1282
|
+
abs: (value: number) => number;
|
|
1283
|
+
/** Floor */
|
|
1284
|
+
floor: (value: number) => number;
|
|
1285
|
+
/** Ceiling */
|
|
1286
|
+
ceil: (value: number) => number;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Text helper functions (pure functions)
|
|
1290
|
+
*/
|
|
1291
|
+
interface TextHelpers {
|
|
1292
|
+
/** Convert to URL-safe slug */
|
|
1293
|
+
slugify: (text: string) => string;
|
|
1294
|
+
/** Truncate with ellipsis */
|
|
1295
|
+
truncate: (text: string, maxLength: number) => string;
|
|
1296
|
+
/** Capitalize first letter */
|
|
1297
|
+
capitalize: (text: string) => string;
|
|
1298
|
+
/** Title case */
|
|
1299
|
+
titleCase: (text: string) => string;
|
|
1300
|
+
/** Check if text contains search (case-insensitive) */
|
|
1301
|
+
contains: (text: string, search: string) => boolean;
|
|
1302
|
+
/** Simple template substitution: {key} replaced by vars[key] */
|
|
1303
|
+
template: (template: string, vars: Record<string, unknown>) => string;
|
|
1304
|
+
/** Trim whitespace */
|
|
1305
|
+
trim: (text: string) => string;
|
|
1306
|
+
/** Convert to lowercase */
|
|
1307
|
+
lower: (text: string) => string;
|
|
1308
|
+
/** Convert to uppercase */
|
|
1309
|
+
upper: (text: string) => string;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Array helper functions (pure functions)
|
|
1313
|
+
*/
|
|
1314
|
+
interface ArrayHelpers {
|
|
1315
|
+
/** Get first element */
|
|
1316
|
+
first: <T>(items: T[]) => T | undefined;
|
|
1317
|
+
/** Get last element */
|
|
1318
|
+
last: <T>(items: T[]) => T | undefined;
|
|
1319
|
+
/** Sort by property */
|
|
1320
|
+
sortBy: <T>(items: T[], key: keyof T, desc?: boolean) => T[];
|
|
1321
|
+
/** Group by property */
|
|
1322
|
+
groupBy: <T>(items: T[], key: keyof T) => Record<string, T[]>;
|
|
1323
|
+
/** Unique values */
|
|
1324
|
+
unique: <T>(items: T[]) => T[];
|
|
1325
|
+
/** Count items */
|
|
1326
|
+
count: <T>(items: T[]) => number;
|
|
1327
|
+
/** Filter truthy */
|
|
1328
|
+
compact: <T>(items: (T | null | undefined)[]) => T[];
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* The context object passed to scripts.
|
|
1332
|
+
* All properties and nested objects are frozen (immutable).
|
|
1333
|
+
*/
|
|
1334
|
+
interface ScriptContext {
|
|
1335
|
+
/** The current node being processed (read-only, frozen) */
|
|
1336
|
+
node: Readonly<FlatNode>;
|
|
1337
|
+
/**
|
|
1338
|
+
* Query sibling nodes by schema.
|
|
1339
|
+
* Returns a frozen array of frozen nodes.
|
|
1340
|
+
*/
|
|
1341
|
+
nodes: (schemaIRI?: string) => ReadonlyArray<Readonly<FlatNode>>;
|
|
1342
|
+
/** Current timestamp in milliseconds */
|
|
1343
|
+
now: () => number;
|
|
1344
|
+
/** Format helpers */
|
|
1345
|
+
format: Readonly<FormatHelpers>;
|
|
1346
|
+
/** Math helpers */
|
|
1347
|
+
math: Readonly<MathHelpers>;
|
|
1348
|
+
/** Text helpers */
|
|
1349
|
+
text: Readonly<TextHelpers>;
|
|
1350
|
+
/** Array helpers */
|
|
1351
|
+
array: Readonly<ArrayHelpers>;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* @xnet/agent-api - Code-execution surface for agent scripts (`xnet run`).
|
|
1356
|
+
*
|
|
1357
|
+
* Extends the read-only ScriptContext with an `api` object that can query the
|
|
1358
|
+
* loaded workspace slice and *propose* writes. Proposals never mutate the
|
|
1359
|
+
* store directly; they are lifted into mutation plans that flow through the
|
|
1360
|
+
* same plan/validate/apply pipeline as file edits and MCP tools.
|
|
1361
|
+
*/
|
|
1362
|
+
|
|
1363
|
+
type AgentSearchResult = {
|
|
1364
|
+
id: string;
|
|
1365
|
+
schemaIRI: string;
|
|
1366
|
+
title: string;
|
|
1367
|
+
snippet: string;
|
|
1368
|
+
};
|
|
1369
|
+
type AgentWriteProposal = {
|
|
1370
|
+
kind: 'update';
|
|
1371
|
+
nodeId: string;
|
|
1372
|
+
properties: Record<string, unknown>;
|
|
1373
|
+
baseRevision: string;
|
|
1374
|
+
rationale?: string;
|
|
1375
|
+
} | {
|
|
1376
|
+
kind: 'create';
|
|
1377
|
+
schemaId: string;
|
|
1378
|
+
properties: Record<string, unknown>;
|
|
1379
|
+
rationale?: string;
|
|
1380
|
+
};
|
|
1381
|
+
interface AgentApi {
|
|
1382
|
+
/** Query the loaded workspace slice by schema IRI. */
|
|
1383
|
+
nodes(schemaIRI?: string): ReadonlyArray<Readonly<FlatNode>>;
|
|
1384
|
+
/** Search titles and string properties of the loaded slice. */
|
|
1385
|
+
search(text: string): ReadonlyArray<Readonly<AgentSearchResult>>;
|
|
1386
|
+
/** Propose a property update; becomes a mutation plan, never a direct write. */
|
|
1387
|
+
proposeUpdate(nodeId: string, properties: Record<string, unknown>, rationale?: string): void;
|
|
1388
|
+
/** Propose a new node; becomes a mutation plan, never a direct write. */
|
|
1389
|
+
proposeCreate(schemaId: string, properties: Record<string, unknown>, rationale?: string): void;
|
|
1390
|
+
}
|
|
1391
|
+
type AgentScriptContext = ScriptContext & {
|
|
1392
|
+
api: Readonly<AgentApi>;
|
|
1393
|
+
};
|
|
1394
|
+
type CreateAgentScriptContextInput = {
|
|
1395
|
+
/** Workspace slice available to the script (already bounded by the caller). */
|
|
1396
|
+
nodes: FlatNode[];
|
|
1397
|
+
/** Optional current node; defaults to a synthetic agent-script node. */
|
|
1398
|
+
node?: FlatNode;
|
|
1399
|
+
/** Cap on proposals per run; guards against runaway scripts. */
|
|
1400
|
+
maxProposals?: number;
|
|
1401
|
+
};
|
|
1402
|
+
type AgentScriptSession = {
|
|
1403
|
+
context: AgentScriptContext;
|
|
1404
|
+
getProposals(): AgentWriteProposal[];
|
|
1405
|
+
/** Lift accumulated proposals into a validated mutation plan (or none). */
|
|
1406
|
+
toMutationPlan(input: {
|
|
1407
|
+
actor: string;
|
|
1408
|
+
intent?: string;
|
|
1409
|
+
clock?: () => Date;
|
|
1410
|
+
}): AiMutationPlan | null;
|
|
1411
|
+
};
|
|
1412
|
+
declare function createAgentScriptContext(input: CreateAgentScriptContextInput): AgentScriptSession;
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* ScriptSandbox - Isolated execution environment for user scripts
|
|
1416
|
+
*
|
|
1417
|
+
* Executes scripts with:
|
|
1418
|
+
* - AST validation before execution
|
|
1419
|
+
* - Timeout protection
|
|
1420
|
+
* - Global shadowing to prevent escape
|
|
1421
|
+
* - Output sanitization
|
|
1422
|
+
*/
|
|
1423
|
+
|
|
1424
|
+
/**
|
|
1425
|
+
* Duck-typed telemetry interface to avoid circular dependencies.
|
|
1426
|
+
*/
|
|
1427
|
+
interface TelemetryReporter {
|
|
1428
|
+
reportPerformance(metricName: string, durationMs: number): void;
|
|
1429
|
+
reportUsage(metricName: string, count: number): void;
|
|
1430
|
+
reportCrash(error: Error, context?: Record<string, unknown>): void;
|
|
1431
|
+
}
|
|
1432
|
+
interface SandboxOptions {
|
|
1433
|
+
/** Maximum execution time in milliseconds (default: 1000) */
|
|
1434
|
+
timeoutMs?: number;
|
|
1435
|
+
/** Whether to run AST validation (default: true) */
|
|
1436
|
+
validateAST?: boolean;
|
|
1437
|
+
/** Optional telemetry reporter */
|
|
1438
|
+
telemetry?: TelemetryReporter;
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Sandbox for executing user scripts safely.
|
|
1442
|
+
*
|
|
1443
|
+
* Scripts are validated via AST analysis and executed in an environment
|
|
1444
|
+
* where dangerous globals are shadowed to undefined.
|
|
1445
|
+
*
|
|
1446
|
+
* @example
|
|
1447
|
+
* ```typescript
|
|
1448
|
+
* const sandbox = new ScriptSandbox({ timeoutMs: 500 })
|
|
1449
|
+
*
|
|
1450
|
+
* const context = createScriptContext(node, queryFn)
|
|
1451
|
+
* const result = await sandbox.execute('(node) => node.amount * 1.08', context)
|
|
1452
|
+
* ```
|
|
1453
|
+
*/
|
|
1454
|
+
declare class ScriptSandbox {
|
|
1455
|
+
private options;
|
|
1456
|
+
constructor(options?: SandboxOptions);
|
|
1457
|
+
/**
|
|
1458
|
+
* Execute a script in the sandbox.
|
|
1459
|
+
*
|
|
1460
|
+
* @param code - JavaScript code (expression or arrow function)
|
|
1461
|
+
* @param context - The frozen ScriptContext
|
|
1462
|
+
* @returns The script's return value (sanitized)
|
|
1463
|
+
* @throws ScriptValidationError if AST validation fails
|
|
1464
|
+
* @throws ScriptTimeoutError if execution exceeds timeout
|
|
1465
|
+
* @throws ScriptError for other execution errors
|
|
1466
|
+
*/
|
|
1467
|
+
execute(code: string, context: ScriptContext): Promise<unknown>;
|
|
1468
|
+
/**
|
|
1469
|
+
* Execute a script synchronously (for computed columns).
|
|
1470
|
+
* Use with caution - no timeout protection in sync mode.
|
|
1471
|
+
*/
|
|
1472
|
+
executeSync(code: string, context: ScriptContext): unknown;
|
|
1473
|
+
/**
|
|
1474
|
+
* Create an isolated function from user code.
|
|
1475
|
+
*
|
|
1476
|
+
* The function receives context values as arguments and has dangerous
|
|
1477
|
+
* globals shadowed to undefined in its scope.
|
|
1478
|
+
*/
|
|
1479
|
+
private createIsolatedFunction;
|
|
1480
|
+
/**
|
|
1481
|
+
* Execute a function with a timeout.
|
|
1482
|
+
*/
|
|
1483
|
+
private executeWithTimeout;
|
|
1484
|
+
/**
|
|
1485
|
+
* Sanitize script output to ensure only safe values are returned.
|
|
1486
|
+
*
|
|
1487
|
+
* Allowed types: null, undefined, string, number, boolean, plain objects, arrays
|
|
1488
|
+
* Stripped: functions, symbols, circular references, dunder properties
|
|
1489
|
+
*/
|
|
1490
|
+
private sanitizeOutput;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* In-memory NodeStore/SchemaRegistry backend.
|
|
1495
|
+
*
|
|
1496
|
+
* Shared by tests, the agent-surface benchmark, and CLI fixtures so each
|
|
1497
|
+
* consumer does not re-implement the same mock store.
|
|
1498
|
+
*/
|
|
1499
|
+
|
|
1500
|
+
type MemoryNodeStore = NodeStoreAPI & {
|
|
1501
|
+
/** Test helper: update properties and bump updatedAt without going through update(). */
|
|
1502
|
+
setNode(id: string, properties: Record<string, unknown>): void;
|
|
1503
|
+
};
|
|
1504
|
+
declare function createMemoryNodeStore(initialNodes: NodeData[]): MemoryNodeStore;
|
|
1505
|
+
declare function createMemorySchemaRegistry(schemas: SchemaData[]): SchemaRegistryAPI;
|
|
1506
|
+
/** The Page/Database/Canvas schema set most agent-surface fixtures use. */
|
|
1507
|
+
declare function createWorkspaceFixtureSchemas(): SchemaRegistryAPI;
|
|
1508
|
+
|
|
303
1509
|
/**
|
|
304
1510
|
* Service Plugin Types
|
|
305
1511
|
*
|
|
@@ -580,4 +1786,4 @@ declare const SERVICE_IPC_CHANNELS: {
|
|
|
580
1786
|
readonly OUTPUT: "xnet:service:output";
|
|
581
1787
|
};
|
|
582
1788
|
|
|
583
|
-
export { type IProcessManager, type LocalAPIConfig, LocalAPIServer, type MCPPropertySchema, type MCPRequest, type MCPResource, type MCPResponse, MCPServer, type MCPServerConfig, type MCPTool, type NodeChangeEventData, type NodeData, type NodeStoreAPI, ProcessManager, type ProcessManagerEvents, SERVICE_IPC_CHANNELS, type SchemaData, type SchemaRegistryAPI, type ServiceClient, type ServiceCommunication, type ServiceDefinition, type ServiceHealthCheck, type ServiceLifecycle, type ServiceOutputEvent, type ServiceProcessConfig, type ServiceProvides, type ServiceState, type ServiceStatus, type ServiceStatusEvent, createLocalAPI, createMCPServer };
|
|
1789
|
+
export { AI_RISK_LEVELS, AI_SCOPES, AI_TARGET_KINDS, type AgentApi, type AgentScriptContext, type AgentScriptSession, type AgentSearchResult, type AgentWriteProposal, type AiAuditEvent, type AiChangeSet, type AiContextPack, type AiContextPackResource, type AiContextSeed, type AiJsonSchema, type AiJsonSchemaType, type AiMutationPlan, type AiMutationPlanStatus, type AiOperation, type AiPageMarkdownApplyAdapter, type AiPageMarkdownApplyAdapterInput, type AiPageMarkdownApplyAdapterResult, type AiPageMarkdownApplyResult, type AiPageMarkdownRollbackResult, type AiResource, type AiResourceContent, type AiRiskLevel, type AiScope, type AiSearchOptions, type AiSearchResult, type AiSurfaceLimits, AiSurfaceService, type AiSurfaceServiceConfig, type AiTargetKind, type AiToolCallResult, type AiToolDefinition, type AiValidationResult, type AiWorkspaceChangedFile, type AiWorkspaceChangedFileStatus, type AiWorkspaceCheckoutOptions, type AiWorkspaceConflict, type AiWorkspaceConflictKind, type AiWorkspaceExportKind, type AiWorkspaceExportOptions, type AiWorkspaceExportResult, type AiWorkspaceExportScope, AiWorkspaceExporter, type AiWorkspaceExporterConfig, type AiWorkspaceManifestEntry, type AiWorkspacePendingPlan, type AiWorkspaceReviewAction, type AiWorkspaceReviewEntry, type AiWorkspaceReviewEntryKind, type AiWorkspaceReviewIndex, type AiWorkspaceReviewStatus, type AiWorkspaceWatchHandle, type AiWorkspaceWatchOptions, AiWorkspaceWatcher, type AiWorkspaceWatcherScanOptions, type AiWorkspaceWatcherScanResult, type CreateAgentScriptContextInput, type FlatNode, type IProcessManager, type LocalAPIConfig, LocalAPIServer, type LocalAPITokenConfig, type LocalAPITokenScope, type LocalAPITokenSummary, type MCPPropertySchema, type MCPRequest, type MCPResource, type MCPResponse, MCPServer, type MCPServerConfig, type MCPTool, type McpHttpServerConfig, type McpHttpServerHandle, type MemoryNodeStore, type NodeChangeEventData, type NodeData, type NodeStoreAPI, ProcessManager, type ProcessManagerEvents, SERVICE_IPC_CHANNELS, type SchemaData, type SchemaRegistryAPI, ScriptSandbox, type ServiceClient, type ServiceCommunication, type ServiceDefinition, type ServiceHealthCheck, type ServiceLifecycle, type ServiceOutputEvent, type ServiceProcessConfig, type ServiceProvides, type ServiceState, type ServiceStatus, type ServiceStatusEvent, XNET_AGENT_SKILL_MD, XNET_MARKDOWN_DIRECTIVE_SPECS, type XNetMarkdownDiffLine, type XNetMarkdownDiffLineKind, type XNetMarkdownDirective, type XNetMarkdownDirectiveSpec, type XNetMarkdownReviewDiff, type XNetPageMarkdownFrontmatter, type XNetPageMarkdownValidation, type XNetPageMarkdownValidationOptions, attachAiPlanValidation, createAgentScriptContext, createAiChangeSet, createAiOperation, createAiSurfaceService, createAiValidationResult, createAiWorkspaceExporter, createAiWorkspaceWatcher, createLocalAPI, createMCPServer, createMcpHttpServer, createMemoryNodeStore, createMemorySchemaRegistry, createWorkspaceFixtureSchemas, flattenRowForTsv, getXNetMarkdownDirectiveSpecs, isAiRiskLevel, isAiScope, isAiTargetKind, parseAiMutationPlan, parseXNetPageFrontmatter, renderMarkdownLineDiff, renderMarkdownReviewDiff, serializeAiMutationPlan, stripXNetPageFrontmatter, toTsv, validateAiMutationPlan, validateXNetPageMarkdown };
|