@xnetjs/plugins 0.0.2 → 0.1.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 CHANGED
@@ -1,7 +1,10 @@
1
1
  import * as _xnetjs_data from '@xnetjs/data';
2
- import { SchemaIRI, NodeStore, NodeState } from '@xnetjs/data';
2
+ import { SchemaIRI, NodeStore, NodeState, NodeQueryDescriptor, NodeQueryResult } from '@xnetjs/data';
3
3
  import { Extension } from '@tiptap/core';
4
4
  import { ComponentType } from 'react';
5
+ import { PublicWriteBudgetPolicy, AbuseSurface, AISignalProvenanceInput } from '@xnetjs/abuse';
6
+ import { InstallProvenance, TrustTier } from '@xnetjs/trust';
7
+ export { InstallProvenance, TrustTier as PluginTrustTier, SandboxKind, deriveTrustTier, requiresCapabilityReprompt, sandboxForTier } from '@xnetjs/trust';
5
8
 
6
9
  /**
7
10
  * Core types for the plugin system
@@ -57,10 +60,256 @@ interface ExtensionStorage {
57
60
  }
58
61
  declare function createExtensionStorage(): ExtensionStorage;
59
62
 
63
+ /**
64
+ * AI surface contract types for xNet resources, tools, mutation plans, and audit events.
65
+ */
66
+ type AiRiskLevel = 'low' | 'medium' | 'high' | 'critical';
67
+ 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';
68
+ declare const AI_RISK_LEVELS: readonly AiRiskLevel[];
69
+ declare const AI_SCOPES: readonly AiScope[];
70
+ type AiTargetKind = 'workspace' | 'node' | 'page' | 'database' | 'databaseRows' | 'canvas' | 'storage';
71
+ declare const AI_TARGET_KINDS: readonly AiTargetKind[];
72
+ type AiJsonSchemaType = 'string' | 'number' | 'boolean' | 'object' | 'array';
73
+ type AiJsonSchema = {
74
+ type: AiJsonSchemaType;
75
+ description?: string;
76
+ enum?: readonly string[];
77
+ properties?: Record<string, AiJsonSchema>;
78
+ required?: readonly string[];
79
+ items?: AiJsonSchema;
80
+ additionalProperties?: boolean | AiJsonSchema;
81
+ };
82
+ type AiResource = {
83
+ uri: string;
84
+ name: string;
85
+ description?: string;
86
+ mimeType: string;
87
+ risk: AiRiskLevel;
88
+ requiredScopes: AiScope[];
89
+ dynamic?: boolean;
90
+ };
91
+ type AiToolDefinition = {
92
+ name: string;
93
+ title: string;
94
+ description: string;
95
+ risk: AiRiskLevel;
96
+ requiredScopes: AiScope[];
97
+ inputSchema: {
98
+ type: 'object';
99
+ properties: Record<string, AiJsonSchema>;
100
+ required?: readonly string[];
101
+ };
102
+ };
103
+ type AiToolCallResult = {
104
+ content: Array<{
105
+ type: 'text';
106
+ text: string;
107
+ }>;
108
+ };
109
+ /**
110
+ * A tool the AI surface can list and call but whose implementation lives outside
111
+ * the surface (a plugin/connector contribution, exploration 0196). `invoke`
112
+ * returns raw data; the surface serializes it for the model, keeping contributed
113
+ * tools symmetric with the built-in `xnet_*` tools.
114
+ */
115
+ type AiExtraTool = AiToolDefinition & {
116
+ invoke: (args: Record<string, unknown>) => unknown | Promise<unknown>;
117
+ };
118
+ type AiValidationResult = {
119
+ valid: boolean;
120
+ warnings: string[];
121
+ errors: string[];
122
+ };
123
+ type AiOperation<TArgs extends Record<string, unknown> = Record<string, unknown>> = {
124
+ op: string;
125
+ args: TArgs;
126
+ rationale?: string;
127
+ };
128
+ type AiChangeSet = {
129
+ targetKind: AiTargetKind;
130
+ targetId: string;
131
+ baseRevision: string;
132
+ operations: AiOperation[];
133
+ };
134
+ type AiMutationPlanStatus = 'proposed' | 'validated' | 'applied' | 'rejected';
135
+ type AiMutationPlan = {
136
+ id: string;
137
+ workspaceId?: string;
138
+ actor: string;
139
+ intent: string;
140
+ risk: AiRiskLevel;
141
+ requiredScopes: AiScope[];
142
+ changes: AiChangeSet[];
143
+ validation: AiValidationResult;
144
+ createdAt: string;
145
+ status: AiMutationPlanStatus;
146
+ };
147
+ type AiAuditEvent = {
148
+ id: string;
149
+ planId: string;
150
+ actor: string;
151
+ risk: AiRiskLevel;
152
+ requiredScopes: AiScope[];
153
+ validation: AiValidationResult;
154
+ appliedChangeIds: string[];
155
+ rollbackHandle?: string;
156
+ createdAt: string;
157
+ };
158
+ type AiContextSeed = {
159
+ kind: AiTargetKind;
160
+ id: string;
161
+ };
162
+ type AiContextPackResource = {
163
+ uri: string;
164
+ mimeType: string;
165
+ text: string;
166
+ trust: {
167
+ level: 'workspace' | 'external-untrusted';
168
+ instructionBoundary: string;
169
+ };
170
+ citation: {
171
+ kind: AiTargetKind;
172
+ id: string;
173
+ revision?: string;
174
+ };
175
+ };
176
+ type AiContextPack = {
177
+ id: string;
178
+ query?: string;
179
+ seeds: AiContextSeed[];
180
+ resources: AiContextPackResource[];
181
+ createdAt: string;
182
+ limits: {
183
+ maxResources: number;
184
+ maxCharactersPerResource: number;
185
+ };
186
+ };
187
+ declare function isAiRiskLevel(value: unknown): value is AiRiskLevel;
188
+ declare function isAiScope(value: unknown): value is AiScope;
189
+ declare function isAiTargetKind(value: unknown): value is AiTargetKind;
190
+
191
+ /**
192
+ * @xnetjs/plugins — agent tools contribution point (exploration 0196).
193
+ *
194
+ * 0194 Phase 2 (`contributionsAsAiTools`) let the AI call a plugin *command*.
195
+ * This adds first-class, model-facing tools that are NOT tied to a UI command —
196
+ * the shape a Connector uses to expose, say, `slack_search_mentions` that reads
197
+ * the governed node store. Register once via `contributes.agentTools`; the host
198
+ * folds them into the AI surface's `extraTools`, so they appear in the in-app
199
+ * AI, the MCP server, and the files-first skill alike.
200
+ *
201
+ * A tool's `invoke` returns raw data; the surface serializes it for the model,
202
+ * keeping contributed tools symmetric with the built-in `xnet_*` tools.
203
+ */
204
+
205
+ /** Input schema shape, shared with `AiToolDefinition`. */
206
+ type AgentToolInputSchema = AiToolDefinition['inputSchema'];
207
+ /**
208
+ * A plugin-contributed agent tool. Registered under `id`; exposed to the model
209
+ * under `name` (snake_case, plugin-namespaced by convention, e.g.
210
+ * `slack_search_mentions`).
211
+ */
212
+ interface AgentToolContribution {
213
+ /** Registry key, plugin-scoped (e.g. `dev.xnet.connector.slack.search`). */
214
+ id: string;
215
+ /** Model-facing tool name (snake_case), e.g. `slack_search_mentions`. */
216
+ name: string;
217
+ /** Human title for menus (defaults to `name`). */
218
+ title?: string;
219
+ /** What the tool does — shown to the model. */
220
+ description: string;
221
+ /** Risk surfaced to the agent + consent layer (default `medium`). */
222
+ risk?: AiRiskLevel;
223
+ /** AI scopes this tool requires (default none). */
224
+ requiredScopes?: AiScope[];
225
+ /** JSON schema for the tool args (default: empty object schema). */
226
+ inputSchema?: AgentToolInputSchema;
227
+ /** Execute the tool. Returns raw data; the surface serializes it for the model. */
228
+ invoke: (args: Record<string, unknown>) => unknown | Promise<unknown>;
229
+ }
230
+ /** Convert one contribution into an AI-surface tool. */
231
+ declare function agentToolToExtraTool(tool: AgentToolContribution): AiExtraTool;
232
+ /**
233
+ * Convert contributed agent tools into AI-surface tools, de-duped by tool name
234
+ * (first wins — registration order). Pass the result as the AI surface's
235
+ * `extraTools` so they surface in the in-app AI and the MCP server.
236
+ */
237
+ declare function agentToolsAsExtraTools(tools: readonly AgentToolContribution[]): AiExtraTool[];
238
+
239
+ /**
240
+ * @xnetjs/plugins — extensible mention/typeahead providers (exploration 0194
241
+ * Phase 4).
242
+ *
243
+ * The editor's `[[` / `#` / `@` typeaheads are host-callback-driven and not
244
+ * extensible by plugins. This contribution point lets a plugin add a new entity
245
+ * type to a trigger — a GitHub plugin makes `@` resolve issues, a CRM plugin
246
+ * makes it resolve contacts — and the host merges them with its own suggestions.
247
+ *
248
+ * `resolveMentionProviders` is the consumer logic the editor runs: it fans the
249
+ * query out to every provider for a trigger in parallel, merges by priority,
250
+ * dedups by suggestion id, and **timeout-guards** each provider so one slow or
251
+ * throwing provider can never block (or break) the menu.
252
+ */
253
+ /** A single typeahead suggestion offered by a provider. */
254
+ interface MentionSuggestion {
255
+ /** Stable id, used for dedup across providers. */
256
+ id: string;
257
+ /** Text shown in the menu. */
258
+ label: string;
259
+ /** Secondary text (e.g. a handle, path, or type). */
260
+ detail?: string;
261
+ /** The reference to insert when chosen (defaults to `id`). */
262
+ value?: string;
263
+ /** Lucide icon name. */
264
+ icon?: string;
265
+ }
266
+ /** A plugin-contributed provider for one typeahead trigger. */
267
+ interface MentionProviderContribution {
268
+ /** Unique provider id. */
269
+ id: string;
270
+ /** Trigger token: `'[['` | `'#'` | `'@'`, or a plugin's own string. */
271
+ trigger: string;
272
+ /** Lower runs first when merging (default 100). */
273
+ priority?: number;
274
+ /** Resolve suggestions for a query under this trigger. */
275
+ getSuggestions: (query: string) => MentionSuggestion[] | Promise<MentionSuggestion[]>;
276
+ }
277
+ interface ResolveMentionOptions {
278
+ /** Per-provider timeout; a provider that exceeds it contributes nothing. */
279
+ timeoutMs?: number;
280
+ /** Cap the merged result length. */
281
+ limit?: number;
282
+ }
283
+ /**
284
+ * Merge the suggestions from every provider registered for `trigger`, in
285
+ * priority order, deduped by suggestion id, each provider timeout-guarded.
286
+ */
287
+ declare function resolveMentionProviders(providers: readonly MentionProviderContribution[], trigger: string, query: string, options?: ResolveMentionOptions): Promise<MentionSuggestion[]>;
288
+
60
289
  /**
61
290
  * Contribution types and registry for plugin-provided extensions
62
291
  */
63
292
 
293
+ /**
294
+ * How a command opts in to being callable by the AI agent as a tool
295
+ * (exploration 0194 Phase 2). Absent / `aiExposed:false` → the AI never sees it.
296
+ */
297
+ interface AiCommandExposure {
298
+ /** Expose this command to the AI agent as a callable tool. Opt-in. */
299
+ aiExposed?: boolean;
300
+ /** JSON schema for the tool's args (defaults to an empty object schema). */
301
+ aiInputSchema?: {
302
+ type: 'object';
303
+ properties: Record<string, AiJsonSchema>;
304
+ required?: readonly string[];
305
+ };
306
+ /** Risk level surfaced to the agent + consent (default `medium`). */
307
+ aiRisk?: AiRiskLevel;
308
+ /** AI scopes this command requires (checked against the plugin's grant; default none). */
309
+ aiScopes?: AiScope[];
310
+ /** Arg-taking AI invocation; falls back to `execute()` when absent. */
311
+ aiInvoke?: (args: Record<string, unknown>) => unknown | Promise<unknown>;
312
+ }
64
313
  interface ViewContribution {
65
314
  /** Unique view type identifier */
66
315
  type: string;
@@ -77,7 +326,7 @@ interface ViewProps {
77
326
  nodeId: string;
78
327
  schemaId: string;
79
328
  }
80
- interface CommandContribution {
329
+ interface CommandContribution extends AiCommandExposure {
81
330
  /** Unique command ID */
82
331
  id: string;
83
332
  /** Display name */
@@ -143,6 +392,24 @@ interface EditorContribution {
143
392
  /** Priority for extension ordering (lower = earlier, default: 100) */
144
393
  priority?: number;
145
394
  }
395
+ /**
396
+ * Status bar item contributed by a plugin (0166 workbench).
397
+ * Left side = workspace scope; right side = view scope.
398
+ */
399
+ interface StatusBarContribution {
400
+ /** Unique item ID */
401
+ id: string;
402
+ /** Static text, or a getter polled on render */
403
+ text: string | (() => string);
404
+ /** Which side of the status bar (default 'left') */
405
+ side?: 'left' | 'right';
406
+ /** Tooltip */
407
+ tooltip?: string;
408
+ /** Command id (CommandRegistry) to run on click */
409
+ command?: string;
410
+ /** Priority within the side (lower = earlier) */
411
+ priority?: number;
412
+ }
146
413
  interface SidebarContribution {
147
414
  /** Unique item ID */
148
415
  id: string;
@@ -163,6 +430,73 @@ interface SidebarContribution {
163
430
  /** Optional panel component to render */
164
431
  panel?: ComponentType;
165
432
  }
433
+ /**
434
+ * Dashboard widget contribution (0162).
435
+ *
436
+ * Mirrors WidgetDefinition from @xnetjs/dashboard structurally (kept
437
+ * dependency-free here, like ViewContribution). The host assigns the trust
438
+ * tier from the plugin's install source — plugins never self-declare it —
439
+ * and registers the widget into the dashboard WidgetRegistry.
440
+ */
441
+ interface WidgetContribution {
442
+ /** Widget registry key, e.g. 'com.example.burndown' (plugin-scoped) */
443
+ type: string;
444
+ /** Display name shown in the widget picker */
445
+ name: string;
446
+ /** Lucide icon name or component */
447
+ icon?: string | ComponentType;
448
+ /** Short description for the picker */
449
+ description?: string;
450
+ /** Config fields driving the auto-generated editor */
451
+ configFields?: WidgetContributionConfigField[];
452
+ /** Default + minimum tile size in 12-column grid units */
453
+ defaultSize: {
454
+ w: number;
455
+ h: number;
456
+ minW?: number;
457
+ minH?: number;
458
+ };
459
+ /** Sensible defaults so a freshly added widget renders immediately */
460
+ getStubConfig: (ctx: {
461
+ schemas: string[];
462
+ }) => {
463
+ config: Record<string, unknown>;
464
+ query?: unknown;
465
+ };
466
+ /** The renderer (runs in the tier-appropriate host) */
467
+ component: ComponentType<WidgetContributionProps>;
468
+ }
469
+ interface WidgetContributionConfigField {
470
+ key: string;
471
+ label: string;
472
+ type: 'property-select' | 'select' | 'number' | 'checkbox' | 'text' | 'color';
473
+ options?: Array<{
474
+ label: string;
475
+ value: string;
476
+ }>;
477
+ required?: boolean;
478
+ description?: string;
479
+ defaultValue?: unknown;
480
+ }
481
+ interface WidgetContributionProps {
482
+ config: Record<string, unknown>;
483
+ data: {
484
+ rows: Array<Record<string, unknown> & {
485
+ id: string;
486
+ }>;
487
+ aggregates: unknown;
488
+ queries: Record<string, Array<Record<string, unknown> & {
489
+ id: string;
490
+ }>>;
491
+ loading: boolean;
492
+ error: Error | null;
493
+ };
494
+ width: number;
495
+ height: number;
496
+ variables: Readonly<Record<string, unknown>>;
497
+ onConfigChange?: (next: Record<string, unknown>) => void;
498
+ onOpenNode?: (nodeId: string, schemaId: string) => void;
499
+ }
166
500
  interface PropertyHandlerContribution {
167
501
  /** Property type this handler manages */
168
502
  type: string;
@@ -228,6 +562,128 @@ interface SchemaContribution {
228
562
  /** The schema definition */
229
563
  schema: unknown;
230
564
  }
565
+ type CanvasPreviewTier = 'summary' | 'thumbnail' | 'shell' | 'live';
566
+ type CanvasIngestInputKind = 'url' | 'file' | 'data-transfer' | 'text' | 'node' | 'custom';
567
+ type CanvasToolGroup = 'select' | 'create' | 'connect' | 'annotate' | 'layout' | 'custom';
568
+ type CanvasLayoutScope = 'selection' | 'frame' | 'canvas' | 'query-results' | 'mind-map' | 'custom';
569
+ type CanvasInspectorPlacement = 'popover' | 'side-panel' | 'bottom-panel';
570
+ type CanvasTemplateCategory = 'planning' | 'research' | 'operations' | 'diagramming' | 'erp' | 'custom';
571
+ type CanvasContributionPermission = 'canvas.read' | 'canvas.write' | 'canvas.ingest' | 'canvas.render' | 'canvas.layout' | 'network' | 'storage' | 'clipboard';
572
+ interface CanvasContributionBase {
573
+ /** Unique contribution ID, preferably plugin-scoped */
574
+ id: string;
575
+ /** Contribution discriminator for validation and registry routing */
576
+ type: string;
577
+ /** Display name shown in canvas menus, toolbars, or inspectors */
578
+ name?: string;
579
+ /** Short help text for command palettes and plugin management */
580
+ description?: string;
581
+ /** Lucide icon name or plugin-owned icon token */
582
+ icon?: string;
583
+ /** Lower values win when multiple plugin contributions match */
584
+ priority?: number;
585
+ /** Fine-grained capabilities requested by this canvas contribution */
586
+ permissions?: CanvasContributionPermission[];
587
+ }
588
+ interface CanvasCardContribution extends CanvasContributionBase {
589
+ type: 'canvas.card';
590
+ /** Source schema this card knows how to render */
591
+ schemaId?: string;
592
+ /** External-reference provider ID, such as youtube, spotify, github, or crm */
593
+ provider?: string;
594
+ /** Canvas object kinds supported by this card */
595
+ canvasKinds?: string[];
596
+ /** LOD tiers this card can satisfy */
597
+ previewTiers?: CanvasPreviewTier[];
598
+ /** Sandboxed renderer module/function ID */
599
+ rendererEntrypoint: string;
600
+ /** Optional deterministic thumbnail/summary entrypoint */
601
+ previewEntrypoint?: string;
602
+ /** Human-readable fallback label when renderer is unavailable */
603
+ fallbackLabel?: string;
604
+ }
605
+ interface CanvasIngestorContribution extends CanvasContributionBase {
606
+ type: 'canvas.ingestor';
607
+ input: CanvasIngestInputKind;
608
+ /** MIME types accepted by file/data-transfer ingestors */
609
+ mimeTypes?: string[];
610
+ /** File extensions accepted by file ingestors */
611
+ fileExtensions?: string[];
612
+ /** URL patterns or provider tokens matched by URL ingestors */
613
+ urlPatterns?: string[];
614
+ /** Sandboxed matcher entrypoint */
615
+ matchEntrypoint: string;
616
+ /** Sandboxed ingestion entrypoint */
617
+ ingestEntrypoint: string;
618
+ }
619
+ interface CanvasToolContribution extends CanvasContributionBase {
620
+ type: 'canvas.tool';
621
+ group?: CanvasToolGroup;
622
+ keybinding?: string;
623
+ cursor?: string;
624
+ /** Sandboxed activation entrypoint that returns a tool controller */
625
+ activationEntrypoint: string;
626
+ }
627
+ interface CanvasLayoutContribution extends CanvasContributionBase {
628
+ type: 'canvas.layout';
629
+ scope: CanvasLayoutScope;
630
+ supportedKinds?: string[];
631
+ supportedSchemas?: string[];
632
+ /** Sandboxed layout function entrypoint */
633
+ applyEntrypoint: string;
634
+ }
635
+ interface CanvasEdgeContribution extends CanvasContributionBase {
636
+ type: 'canvas.edge';
637
+ label: string;
638
+ directed: boolean;
639
+ allowedSourceSchemas?: string[];
640
+ allowedTargetSchemas?: string[];
641
+ style?: 'solid' | 'dashed' | 'dotted';
642
+ }
643
+ interface CanvasInspectorContribution extends CanvasContributionBase {
644
+ type: 'canvas.inspector';
645
+ placement: CanvasInspectorPlacement;
646
+ supportedKinds?: string[];
647
+ supportedSchemas?: string[];
648
+ supportedProviders?: string[];
649
+ /** Sandboxed panel renderer entrypoint */
650
+ panelEntrypoint: string;
651
+ }
652
+ interface CanvasTemplateContribution extends CanvasContributionBase {
653
+ type: 'canvas.template';
654
+ category: CanvasTemplateCategory;
655
+ tags?: string[];
656
+ /** Sandboxed template instantiation entrypoint */
657
+ instantiateEntrypoint: string;
658
+ /** Optional preview renderer entrypoint */
659
+ previewEntrypoint?: string;
660
+ }
661
+ type CanvasContribution = CanvasCardContribution | CanvasIngestorContribution | CanvasToolContribution | CanvasLayoutContribution | CanvasEdgeContribution | CanvasInspectorContribution | CanvasTemplateContribution;
662
+ /**
663
+ * Importer contribution (exploration 0189).
664
+ *
665
+ * A data-export / source importer — e.g. an Instagram or YouTube archive
666
+ * importer. Mirrors `@xnetjs/social`'s `SocialImportAdapter` *structurally* so
667
+ * the first-party social adapters can register through the plugin system
668
+ * without `@xnetjs/plugins` depending on `@xnetjs/social` (dependency direction
669
+ * stays social → plugins). The `adapter` is opaque to the registry; the consumer
670
+ * (the import flow) casts it to its known importer shape. This is the same
671
+ * "defined now, consumed later" pattern as the canvas contributions.
672
+ */
673
+ interface ImporterContribution {
674
+ /** Unique importer ID, preferably plugin-scoped (e.g. 'fyi.xnet.import.instagram'). */
675
+ id: string;
676
+ /** Source platform/system this importer handles (e.g. 'instagram', 'youtube'). */
677
+ platform: string;
678
+ /** Importer version. */
679
+ version: string;
680
+ /** Human-readable label for the import picker. */
681
+ name?: string;
682
+ /** Lucide icon name. */
683
+ icon?: string;
684
+ /** The importer adapter implementation (structurally a `SocialImportAdapter`). */
685
+ adapter: unknown;
686
+ }
231
687
  /**
232
688
  * A registry for typed contributions with change notifications
233
689
  */
@@ -252,19 +708,151 @@ declare class TypedRegistry<T extends {
252
708
  */
253
709
  declare class ContributionRegistry {
254
710
  readonly views: TypedRegistry<ViewContribution>;
711
+ readonly widgets: TypedRegistry<WidgetContribution>;
255
712
  readonly commands: TypedRegistry<CommandContribution>;
256
713
  readonly slashCommands: TypedRegistry<SlashCommandContribution>;
257
714
  readonly sidebar: TypedRegistry<SidebarContribution>;
715
+ readonly statusBar: TypedRegistry<StatusBarContribution>;
258
716
  readonly editor: TypedRegistry<EditorContribution>;
259
717
  readonly propertyHandlers: TypedRegistry<PropertyHandlerContribution>;
260
718
  readonly blocks: TypedRegistry<BlockContribution>;
261
719
  readonly settings: TypedRegistry<SettingContribution>;
720
+ readonly canvasCards: TypedRegistry<CanvasCardContribution>;
721
+ readonly canvasIngestors: TypedRegistry<CanvasIngestorContribution>;
722
+ readonly canvasTools: TypedRegistry<CanvasToolContribution>;
723
+ readonly canvasLayouts: TypedRegistry<CanvasLayoutContribution>;
724
+ readonly canvasEdges: TypedRegistry<CanvasEdgeContribution>;
725
+ readonly canvasInspectors: TypedRegistry<CanvasInspectorContribution>;
726
+ readonly canvasTemplates: TypedRegistry<CanvasTemplateContribution>;
727
+ readonly importers: TypedRegistry<ImporterContribution>;
728
+ readonly mentionProviders: TypedRegistry<MentionProviderContribution>;
729
+ readonly agentTools: TypedRegistry<AgentToolContribution>;
262
730
  /**
263
731
  * Clear all registries (for cleanup/testing)
264
732
  */
265
733
  clear(): void;
266
734
  }
267
735
 
736
+ /**
737
+ * Canvas plugin permission policy helpers.
738
+ */
739
+
740
+ type CanvasPluginPermissionDecisionStatus = 'allowed' | 'prompt-required' | 'blocked';
741
+ type CanvasPluginPromptMode = 'allow-once' | 'allow-workspace' | 'deny';
742
+ type CanvasPluginWorkspacePolicy = {
743
+ allowUnknownPlugins?: boolean;
744
+ trustedPluginIds?: string[];
745
+ blockedPluginIds?: string[];
746
+ allowedPermissions?: CanvasContributionPermission[] | '*';
747
+ blockedPermissions?: CanvasContributionPermission[];
748
+ promptPermissions?: CanvasContributionPermission[];
749
+ allowedNetworkDomains?: string[];
750
+ blockedNetworkDomains?: string[];
751
+ };
752
+ type CanvasPluginPermissionGateInput = {
753
+ pluginId: string;
754
+ contributionId: string;
755
+ contributionName?: string;
756
+ requestedPermissions?: CanvasContributionPermission[];
757
+ requestedNetworkDomains?: string[];
758
+ policy?: CanvasPluginWorkspacePolicy | null;
759
+ };
760
+ type CanvasPluginPermissionPromptOption = {
761
+ mode: CanvasPluginPromptMode;
762
+ label: string;
763
+ description: string;
764
+ persistsDecision: boolean;
765
+ };
766
+ type CanvasPluginPermissionPrompt = {
767
+ pluginId: string;
768
+ contributionId: string;
769
+ title: string;
770
+ message: string;
771
+ requestedPermissions: CanvasContributionPermission[];
772
+ requestedNetworkDomains: string[];
773
+ options: CanvasPluginPermissionPromptOption[];
774
+ };
775
+ type CanvasPluginPermissionGateDecision = {
776
+ status: CanvasPluginPermissionDecisionStatus;
777
+ allowed: boolean;
778
+ prompt: CanvasPluginPermissionPrompt | null;
779
+ grantedPermissions: CanvasContributionPermission[];
780
+ pendingPermissions: CanvasContributionPermission[];
781
+ blockedPermissions: CanvasContributionPermission[];
782
+ blockedNetworkDomains: string[];
783
+ pendingNetworkDomains: string[];
784
+ issues: string[];
785
+ };
786
+ declare function evaluateCanvasPluginPermissionGate(input: CanvasPluginPermissionGateInput): CanvasPluginPermissionGateDecision;
787
+ declare function createCanvasPluginPermissionPrompt(input: {
788
+ pluginId: string;
789
+ contributionId: string;
790
+ contributionName?: string;
791
+ requestedPermissions: CanvasContributionPermission[];
792
+ requestedNetworkDomains: string[];
793
+ }): CanvasPluginPermissionPrompt;
794
+ declare function normalizeCanvasPluginWorkspacePolicy(policy?: CanvasPluginWorkspacePolicy | null): Required<CanvasPluginWorkspacePolicy>;
795
+
796
+ /**
797
+ * @xnetjs/plugins — FeatureModule (exploration 0189).
798
+ *
799
+ * A `FeatureModule` is the uniform shape every first-party integration and
800
+ * community plugin can take: a superset of `XNetExtension` (the client
801
+ * contributions) that also **declares its capability surface** and, by
802
+ * convention via a shared `id`, links to a hub-side feature mounted through
803
+ * `@xnetjs/hub`'s feature registry.
804
+ *
805
+ * The client stays hub-free on purpose: `hub` here is a *declarative pointer*
806
+ * (an id), not server code. The actual hub routes/secrets live in the hub
807
+ * package's `HubFeature`, and the hub's capability/secret broker is what
808
+ * enforces `capabilities.secrets`. This keeps the dependency direction clean
809
+ * (no `@xnetjs/plugins` → `@xnetjs/hub` edge) while letting one manifest
810
+ * document the full two-sided surface — billing, GitHub, importers, and
811
+ * eventually core surfaces all expressed the same way.
812
+ */
813
+
814
+ /**
815
+ * The capability surface a module declares. The install UI renders this as a
816
+ * consent dialog; the hub broker injects only the declared `secrets`; the
817
+ * client sandbox exposes only the declared `endowments`.
818
+ */
819
+ interface ModuleCapabilities {
820
+ /**
821
+ * Env secret keys the module's hub half is permitted to read. Supports exact
822
+ * keys (`STRIPE_SECRET_KEY`) and `PREFIX_*` globs (`BTCPAY_*`). First-party
823
+ * tier only — community modules never receive raw secrets.
824
+ */
825
+ secrets?: string[];
826
+ /** Schema IRIs the module may write. */
827
+ schemaWrite?: string[];
828
+ /** Schema IRIs the module may read. */
829
+ schemaRead?: string[];
830
+ /** Network domains the module may reach (allowlist), e.g. for unfurl/proxy. */
831
+ network?: string[];
832
+ /** Host APIs the client sandbox should expose to the module's code. */
833
+ endowments?: string[];
834
+ }
835
+ /**
836
+ * A two-sided feature module. Inherits all client `contributes` (including the
837
+ * new `importers` point) from `XNetExtension`, and adds the capability
838
+ * declaration + the hub linkage.
839
+ */
840
+ interface FeatureModule extends XNetExtension {
841
+ /** Declared capability surface (client + hub). The hub broker enforces `secrets`. */
842
+ capabilities?: ModuleCapabilities;
843
+ /**
844
+ * Declarative pointer to a hub-side feature. By convention the hub feature is
845
+ * registered under the same `id` and mounted at `/x/<featureId>` (or, for the
846
+ * bundled first-party features, at their legacy path). Absence means the
847
+ * module is client-only.
848
+ */
849
+ hub?: {
850
+ featureId: string;
851
+ };
852
+ }
853
+ /** Define a feature module with type checking (mirrors `defineExtension`). */
854
+ declare function defineFeatureModule(module: FeatureModule): FeatureModule;
855
+
268
856
  /**
269
857
  * NodeStore middleware chain for pre/post change hooks
270
858
  */
@@ -369,6 +957,8 @@ interface ExtensionContext {
369
957
  registerSchema(schema: unknown): Disposable$1;
370
958
  /** Register a custom view type */
371
959
  registerView(view: ViewContribution): Disposable$1;
960
+ /** Register a dashboard widget (trust tier assigned by the host) */
961
+ registerWidget(widget: WidgetContribution): Disposable$1;
372
962
  /** Register a property type handler */
373
963
  registerPropertyHandler(type: string, handler: PropertyHandlerContribution['handler']): Disposable$1;
374
964
  /** Register a command */
@@ -381,6 +971,26 @@ interface ExtensionContext {
381
971
  registerSlashCommand(cmd: SlashCommandContribution): Disposable$1;
382
972
  /** Register a custom block type */
383
973
  registerBlockType(block: BlockContribution): Disposable$1;
974
+ /** Register a canvas card renderer descriptor */
975
+ registerCanvasCard(card: CanvasCardContribution): Disposable$1;
976
+ /** Register a canvas ingestor descriptor */
977
+ registerCanvasIngestor(ingestor: CanvasIngestorContribution): Disposable$1;
978
+ /** Register a canvas tool descriptor */
979
+ registerCanvasTool(tool: CanvasToolContribution): Disposable$1;
980
+ /** Register a canvas layout descriptor */
981
+ registerCanvasLayout(layout: CanvasLayoutContribution): Disposable$1;
982
+ /** Register a canvas edge relationship descriptor */
983
+ registerCanvasEdge(edge: CanvasEdgeContribution): Disposable$1;
984
+ /** Register a canvas inspector descriptor */
985
+ registerCanvasInspector(inspector: CanvasInspectorContribution): Disposable$1;
986
+ /** Register a canvas template descriptor */
987
+ registerCanvasTemplate(template: CanvasTemplateContribution): Disposable$1;
988
+ /** Register a data-export / source importer (exploration 0189) */
989
+ registerImporter(importer: ImporterContribution): Disposable$1;
990
+ /** Register a mention/typeahead provider (exploration 0194) */
991
+ registerMentionProvider(provider: MentionProviderContribution): Disposable$1;
992
+ /** Register a model-facing agent tool (exploration 0196) */
993
+ registerAgentTool(tool: AgentToolContribution): Disposable$1;
384
994
  /** Add middleware to NodeStore */
385
995
  addMiddleware(middleware: NodeStoreMiddleware): Disposable$1;
386
996
  /** Plugin-private storage */
@@ -398,6 +1008,13 @@ interface CreateContextOptions {
398
1008
  middlewareChain?: {
399
1009
  add(middleware: NodeStoreMiddleware): Disposable$1;
400
1010
  };
1011
+ /**
1012
+ * Declared capability grant (exploration 0192). When present, the plugin's
1013
+ * `store` handle is wrapped so writes outside `schemaWrite` throw — turning the
1014
+ * declaration into an enforced gate at the one choke point a plugin can't
1015
+ * route around. Absent/empty → the store is handed through unguarded.
1016
+ */
1017
+ capabilities?: ModuleCapabilities;
401
1018
  }
402
1019
  /**
403
1020
  * Create an ExtensionContext for a plugin
@@ -408,6 +1025,29 @@ declare function createExtensionContext(options: CreateContextOptions): Extensio
408
1025
  * Plugin manifest types and validation
409
1026
  */
410
1027
 
1028
+ /**
1029
+ * How a plugin is monetized (exploration 0196). `free` is the default when
1030
+ * `pricing` is absent. Paid plugins (`one-time`/`subscription`) are gated at
1031
+ * install by a license check (see `PluginRegistry.install`'s `checkLicense`).
1032
+ */
1033
+ interface PluginPricing {
1034
+ /** `free` — no license required. `one-time`/`subscription` — license-gated. */
1035
+ mode: 'free' | 'one-time' | 'subscription';
1036
+ /** Price in integer minor units (e.g. cents). Omitted/0 for free. */
1037
+ amountMinor?: number;
1038
+ /** ISO-4217 currency code (e.g. `USD`). Required when `amountMinor` > 0. */
1039
+ currency?: string;
1040
+ /**
1041
+ * Who runs checkout: `managed` = the xNet marketplace via Stripe Connect (the
1042
+ * platform takes its fee); `byo` = the author hosts their own checkout and
1043
+ * mints their own license (xNet takes 0%). Default `managed`.
1044
+ */
1045
+ billing?: 'managed' | 'byo';
1046
+ /** Free-trial length in days (subscriptions). */
1047
+ trialDays?: number;
1048
+ }
1049
+ /** True when a pricing descriptor denotes a paid plugin that needs a license. */
1050
+ declare function isPaidPricing(pricing: PluginPricing | undefined): boolean;
411
1051
  /**
412
1052
  * Plugin manifest - defines what a plugin provides and how it integrates
413
1053
  */
@@ -422,12 +1062,31 @@ interface XNetExtension {
422
1062
  description?: string;
423
1063
  /** Author name or organization */
424
1064
  author?: string;
425
- /** Minimum compatible xNet version */
1065
+ /** Minimum compatible xNet version (semver range, e.g. ">=0.6.0") */
426
1066
  xnetVersion?: string;
427
1067
  /** Platforms this plugin supports (default: all) */
428
1068
  platforms?: Platform[];
429
1069
  /** Permission declarations */
430
1070
  permissions?: PluginPermissions;
1071
+ /**
1072
+ * Other plugins this one requires, as `{ '<pluginId>': '<versionRange>' }`
1073
+ * (exploration 0192). Resolved at install time — see `ecosystem/dependencies`.
1074
+ */
1075
+ dependencies?: Record<string, string>;
1076
+ /**
1077
+ * SPDX license id (exploration 0196). Paid plugins must declare a license the
1078
+ * marketplace pre-approves — `FSL-1.1-MIT` / `FSL-1.1-Apache-2.0` (source-
1079
+ * available, auto-opens after 2 years) or an OSI id (`MIT`, `Apache-2.0`, …).
1080
+ * Defaults to `MIT` when absent.
1081
+ */
1082
+ license?: string;
1083
+ /** How this plugin is monetized (exploration 0196). Absent = free. */
1084
+ pricing?: PluginPricing;
1085
+ /**
1086
+ * The publisher's DID. Supersedes the bare `author` string for paid plugins —
1087
+ * licenses, payouts, and provenance attach to this identity (exploration 0196).
1088
+ */
1089
+ publisherDid?: string;
431
1090
  /** Static contributions declared in manifest */
432
1091
  contributes?: PluginContributions;
433
1092
  /** Called when plugin is activated */
@@ -441,6 +1100,8 @@ interface XNetExtension {
441
1100
  interface PluginContributions {
442
1101
  schemas?: SchemaContribution[];
443
1102
  views?: ViewContribution[];
1103
+ /** Dashboard widgets (0162); trust tier is host-assigned at registration */
1104
+ widgets?: WidgetContribution[];
444
1105
  editorExtensions?: EditorContribution[];
445
1106
  propertyHandlers?: PropertyHandlerContribution[];
446
1107
  blocks?: BlockContribution[];
@@ -448,6 +1109,19 @@ interface PluginContributions {
448
1109
  settings?: SettingContribution[];
449
1110
  sidebarItems?: SidebarContribution[];
450
1111
  slashCommands?: SlashCommandContribution[];
1112
+ canvasCards?: CanvasCardContribution[];
1113
+ canvasIngestors?: CanvasIngestorContribution[];
1114
+ canvasTools?: CanvasToolContribution[];
1115
+ canvasLayouts?: CanvasLayoutContribution[];
1116
+ canvasEdges?: CanvasEdgeContribution[];
1117
+ canvasInspectors?: CanvasInspectorContribution[];
1118
+ canvasTemplates?: CanvasTemplateContribution[];
1119
+ /** Data-export / source importers (exploration 0189). */
1120
+ importers?: ImporterContribution[];
1121
+ /** Mention/typeahead providers — extend `[[`/`#`/`@` (exploration 0194). */
1122
+ mentionProviders?: MentionProviderContribution[];
1123
+ /** Model-facing agent tools — what a Connector exposes (exploration 0196). */
1124
+ agentTools?: AgentToolContribution[];
451
1125
  }
452
1126
  declare class PluginValidationError extends Error {
453
1127
  readonly issues: string[];
@@ -463,6 +1137,148 @@ declare function validateManifest(manifest: unknown): XNetExtension;
463
1137
  */
464
1138
  declare function defineExtension(manifest: XNetExtension): XNetExtension;
465
1139
 
1140
+ /**
1141
+ * @xnetjs/plugins — importer resolution (exploration 0189).
1142
+ *
1143
+ * Consumer-side helpers for the `importers` contribution point: pull the adapter
1144
+ * objects out of plugin contributions, and merge them with a set of built-in
1145
+ * importers (deduped by id — a plugin-contributed importer overrides a built-in
1146
+ * with the same id). Kept generic and adapter-shape-agnostic so the social
1147
+ * import flow (or any importer host) can call it without `@xnetjs/plugins`
1148
+ * depending on `@xnetjs/social`.
1149
+ */
1150
+
1151
+ /** The adapter objects carried by importer contributions (opaque to the registry). */
1152
+ declare function importerAdapters(contributions: readonly ImporterContribution[]): unknown[];
1153
+ /**
1154
+ * Merge built-in importer adapters with contributed ones, deduped by `id`. A
1155
+ * contributed importer with the same id as a built-in overrides it (so a plugin
1156
+ * can replace a first-party importer). The consumer casts the result to its own
1157
+ * importer-adapter type.
1158
+ */
1159
+ declare function resolveImporters<A extends {
1160
+ id: string;
1161
+ }>(builtIns: readonly A[], contributed: readonly A[]): A[];
1162
+
1163
+ /**
1164
+ * Fake canvas plugin fixtures for CRM, ERP, and media-provider cards.
1165
+ */
1166
+
1167
+ type CanvasPluginFixtureKind = 'crm' | 'erp' | 'media-provider';
1168
+ type CanvasPluginFixtureCardSample = {
1169
+ id: string;
1170
+ pluginId: string;
1171
+ contributionId: string;
1172
+ title: string;
1173
+ subtitle: string;
1174
+ schemaId?: `xnet://${string}/${string}`;
1175
+ provider?: string;
1176
+ canvasKind: string;
1177
+ previewTier: 'summary' | 'thumbnail' | 'shell' | 'live';
1178
+ properties: Record<string, unknown>;
1179
+ };
1180
+ type CanvasPluginFixture = {
1181
+ kind: CanvasPluginFixtureKind;
1182
+ manifest: XNetExtension;
1183
+ sampleCards: CanvasPluginFixtureCardSample[];
1184
+ };
1185
+ declare const CRM_CANVAS_PLUGIN_FIXTURE: CanvasPluginFixture;
1186
+ declare const ERP_CANVAS_PLUGIN_FIXTURE: CanvasPluginFixture;
1187
+ declare const MEDIA_PROVIDER_CANVAS_PLUGIN_FIXTURE: CanvasPluginFixture;
1188
+ declare const CANVAS_PLUGIN_FIXTURES: CanvasPluginFixture[];
1189
+ declare function getCanvasPluginFixture(kind: CanvasPluginFixtureKind): CanvasPluginFixture | undefined;
1190
+ declare function createCanvasPluginFixtureManifests(): XNetExtension[];
1191
+ declare function createCanvasPluginFixtureCards(): CanvasPluginFixtureCardSample[];
1192
+
1193
+ /**
1194
+ * Sample ERP canvas prototype built from the ERP canvas plugin fixture.
1195
+ */
1196
+
1197
+ type CanvasErpPrototypeEntityKind = 'purchase-order' | 'inventory-item';
1198
+ type CanvasErpPrototypeRisk = 'low' | 'medium' | 'high';
1199
+ type CanvasErpPrototypeStatus = 'awaiting-shipment' | 'in-transit' | 'qa-hold' | 'stockout-risk' | 'healthy' | 'receiving';
1200
+ type CanvasErpPrototypeLayoutKind = 'supply-chain' | 'procurement-kanban' | 'inventory-risk-grid' | 'receiving-timeline';
1201
+ type CanvasErpPrototypeAuditOperation = 'created' | 'moved' | 'resized' | 'field-updated' | 'bulk-updated' | 'synced';
1202
+ type CanvasErpPrototypeAuditSource = 'user' | 'plugin' | 'external-system';
1203
+ type CanvasErpPrototypeRect = {
1204
+ readonly x: number;
1205
+ readonly y: number;
1206
+ readonly width: number;
1207
+ readonly height: number;
1208
+ };
1209
+ type CanvasErpPrototypeCard = Omit<CanvasPluginFixtureCardSample, 'properties'> & {
1210
+ readonly sourceNodeId: `xnet://fixtures.erp/${string}`;
1211
+ readonly entityKind: CanvasErpPrototypeEntityKind;
1212
+ readonly status: CanvasErpPrototypeStatus;
1213
+ readonly risk: CanvasErpPrototypeRisk;
1214
+ readonly position: CanvasErpPrototypeRect;
1215
+ readonly tags: readonly string[];
1216
+ readonly properties: Record<string, unknown>;
1217
+ };
1218
+ type CanvasErpPrototypeEdge = {
1219
+ readonly id: string;
1220
+ readonly contributionId: 'erp.supplies' | 'erp.delayed-by';
1221
+ readonly sourceCardId: string;
1222
+ readonly targetCardId: string;
1223
+ readonly label: string;
1224
+ readonly directed: true;
1225
+ readonly style: 'solid' | 'dotted';
1226
+ readonly properties: Record<string, unknown>;
1227
+ };
1228
+ type CanvasErpPrototypeQueryPredicate = {
1229
+ readonly field: string;
1230
+ readonly operator: 'equals' | 'not-equals' | 'less-than' | 'greater-than' | 'contains';
1231
+ readonly value: unknown;
1232
+ };
1233
+ type CanvasErpPrototypeQueryFrame = {
1234
+ readonly id: string;
1235
+ readonly title: string;
1236
+ readonly layoutKind: CanvasErpPrototypeLayoutKind;
1237
+ readonly bounds: CanvasErpPrototypeRect;
1238
+ readonly schemaIds: readonly `xnet://${string}/${string}`[];
1239
+ readonly predicates: readonly CanvasErpPrototypeQueryPredicate[];
1240
+ readonly cardIds: readonly string[];
1241
+ };
1242
+ type CanvasErpPrototypeCommand = {
1243
+ readonly id: string;
1244
+ readonly label: string;
1245
+ readonly scope: 'single-card' | 'multi-card' | 'frame';
1246
+ readonly requiredPermissions: readonly ('canvas.read' | 'canvas.write' | 'canvas.layout')[];
1247
+ readonly supportedEntityKinds: readonly CanvasErpPrototypeEntityKind[];
1248
+ };
1249
+ type CanvasErpPrototypeAuditEntry = {
1250
+ readonly id: string;
1251
+ readonly cardId: string;
1252
+ readonly operation: CanvasErpPrototypeAuditOperation;
1253
+ readonly source: CanvasErpPrototypeAuditSource;
1254
+ readonly actor: string;
1255
+ readonly at: string;
1256
+ readonly summary: string;
1257
+ readonly batchId?: string;
1258
+ };
1259
+ type CanvasErpPrototypeRiskSummary = {
1260
+ readonly total: number;
1261
+ readonly low: number;
1262
+ readonly medium: number;
1263
+ readonly high: number;
1264
+ };
1265
+ type CanvasErpPrototypeScenario = {
1266
+ readonly id: string;
1267
+ readonly title: string;
1268
+ readonly pluginManifest: XNetExtension;
1269
+ readonly templateId: 'erp.procurement-war-room';
1270
+ readonly cards: readonly CanvasErpPrototypeCard[];
1271
+ readonly edges: readonly CanvasErpPrototypeEdge[];
1272
+ readonly queryFrames: readonly CanvasErpPrototypeQueryFrame[];
1273
+ readonly commands: readonly CanvasErpPrototypeCommand[];
1274
+ readonly auditEntries: readonly CanvasErpPrototypeAuditEntry[];
1275
+ readonly riskSummary: CanvasErpPrototypeRiskSummary;
1276
+ };
1277
+ declare function createCanvasErpPrototypeRiskSummary(cards: readonly CanvasErpPrototypeCard[]): CanvasErpPrototypeRiskSummary;
1278
+ declare function createCanvasErpPrototypeScenario(): CanvasErpPrototypeScenario;
1279
+ declare function getCanvasErpPrototypeCardsForFrame(scenario: CanvasErpPrototypeScenario, frameId: string): CanvasErpPrototypeCard[];
1280
+ declare function getCanvasErpPrototypeAuditEntriesForCard(scenario: CanvasErpPrototypeScenario, cardId: string): CanvasErpPrototypeAuditEntry[];
1281
+
466
1282
  /**
467
1283
  * Keyboard Shortcut Manager
468
1284
  *
@@ -551,14 +1367,1525 @@ declare class ShortcutManager {
551
1367
  clear(): void;
552
1368
  }
553
1369
  /**
554
- * Get or create the global ShortcutManager instance.
1370
+ * Get or create the global ShortcutManager instance.
1371
+ */
1372
+ declare function getShortcutManager(): ShortcutManager;
1373
+ /**
1374
+ * Install the global shortcut manager on the window.
1375
+ * Call this once at app startup.
1376
+ */
1377
+ declare function installShortcutHandler(): () => void;
1378
+
1379
+ /**
1380
+ * Workspace Command Registry
1381
+ *
1382
+ * One registry for every keyboard-driven verb in the workspace
1383
+ * (exploration 0161, phase 3). Generalizes ShortcutManager with:
1384
+ *
1385
+ * - **Scopes**: commands belong to a scope ('global', 'surface:grid',
1386
+ * 'task-focused', …). Surfaces activate their scope on focus/mount and
1387
+ * dispose on blur/unmount; only commands in active scopes fire. The most
1388
+ * recently activated scope wins key conflicts (global loses to all).
1389
+ * - **Single-key verbs**: Linear-style bindings ('s', 'a', 'p') that are
1390
+ * automatically suppressed while any input/textarea/contenteditable has
1391
+ * focus — modifier combos can opt into editors via `allowInInput`.
1392
+ * - **Chords**: 'g t'-style two-step sequences with a pending-step timeout.
1393
+ *
1394
+ * Surfaces register commands instead of owning their own key handling, so
1395
+ * the palette can list every available action with its binding.
1396
+ */
1397
+
1398
+ type CommandScope = 'global' | (string & NonNullable<unknown>);
1399
+ interface CommandContext {
1400
+ /** The scope the command was resolved in */
1401
+ scope: CommandScope;
1402
+ /** The triggering keyboard event (absent when run from the palette) */
1403
+ event?: KeyboardEvent;
1404
+ }
1405
+ interface WorkspaceCommand {
1406
+ /** Unique id, e.g. 'task.setStatus' */
1407
+ id: string;
1408
+ /** Palette title, e.g. 'Change status…' */
1409
+ title: string;
1410
+ /** Scope this command belongs to (default 'global') */
1411
+ scope?: CommandScope;
1412
+ /**
1413
+ * Key binding: a single key ('s', '?'), a modifier combo ('Mod-K'),
1414
+ * or a space-separated chord ('g t'). Omit for palette-only commands.
1415
+ */
1416
+ key?: string;
1417
+ /** Allow firing while an input/editor has focus (modifier combos only) */
1418
+ allowInInput?: boolean;
1419
+ /** Availability guard checked at dispatch and palette-listing time */
1420
+ when?: () => boolean;
1421
+ run: (context: CommandContext) => void | Promise<void>;
1422
+ }
1423
+ declare class CommandRegistry {
1424
+ private commands;
1425
+ /** Active scopes in activation order ('global' is always index 0) */
1426
+ private scopeStack;
1427
+ private pendingSteps;
1428
+ private pendingTimer;
1429
+ private enabled;
1430
+ register(command: WorkspaceCommand): Disposable$1;
1431
+ /**
1432
+ * Activate a scope (e.g. when a surface mounts or a task row gains
1433
+ * focus). Re-activating moves it to the top of the priority stack.
1434
+ */
1435
+ activateScope(scope: CommandScope): Disposable$1;
1436
+ isScopeActive(scope: CommandScope): boolean;
1437
+ getActiveScopes(): CommandScope[];
1438
+ /** Commands available right now (active scope + passing when()) */
1439
+ getAvailableCommands(): WorkspaceCommand[];
1440
+ getAllCommands(): WorkspaceCommand[];
1441
+ /**
1442
+ * Commands declared in any of the given scopes (passing `when()`),
1443
+ * regardless of whether those scopes are currently active. Lets a
1444
+ * context-aware command menu surface, say, the focused-task verbs even
1445
+ * when its own input has stolen scope focus.
1446
+ */
1447
+ commandsForScopes(scopes: readonly CommandScope[]): WorkspaceCommand[];
1448
+ getCommand(id: string): WorkspaceCommand | undefined;
1449
+ runCommand(id: string): Promise<boolean>;
1450
+ setEnabled(enabled: boolean): void;
1451
+ /**
1452
+ * Handle a keydown. Returns true when a command fired (or a chord step
1453
+ * was consumed); callers should attach exactly one handler per window.
1454
+ */
1455
+ handleKeyDown(event: KeyboardEvent): boolean;
1456
+ /** Format a binding for display ('Mod-K' → '⌘K' on Mac). */
1457
+ formatForDisplay(binding: string): string;
1458
+ clear(): void;
1459
+ private clearPending;
1460
+ }
1461
+ declare function getCommandRegistry(): CommandRegistry;
1462
+ /**
1463
+ * Install the global command handler on the window.
1464
+ * Call once at app startup.
1465
+ */
1466
+ declare function installCommandHandler(): () => void;
1467
+
1468
+ /**
1469
+ * Editor schema-skew guard for plugin contributions (exploration 0205).
1470
+ *
1471
+ * A plugin editor contribution whose TipTap extension is a Node or Mark changes
1472
+ * the PERSISTED document schema. Under Yjs collaboration, if some collaborators
1473
+ * have the plugin and others don't, ProseMirror silently drops the unknown
1474
+ * content and the corruption syncs without any error. Behavior-only extensions
1475
+ * (commands, keymaps, decorations, slash items) are skew-safe.
1476
+ *
1477
+ * This module classifies a plugin's editor contributions so the registry can
1478
+ * warn loudly in development. It mirrors `@xnetjs/editor`'s extension-tiers
1479
+ * classifier but lives here so the plugins package needs no editor dependency.
1480
+ */
1481
+
1482
+ interface EditorSchemaRisk {
1483
+ /** The contribution id. */
1484
+ id: string;
1485
+ /** 'node' | 'mark' (the skew-sensitive kinds). */
1486
+ kind: string;
1487
+ /** The TipTap extension name. */
1488
+ name: string;
1489
+ }
1490
+ /** True if the extension defines persisted document schema (a Node or Mark). */
1491
+ declare function isSchemaDefiningExtension(ext: {
1492
+ type?: string;
1493
+ }): boolean;
1494
+ /**
1495
+ * Of a plugin's editor contributions, return those that add persisted schema
1496
+ * and therefore risk silent Yjs content loss across version skew. Empty array
1497
+ * means the contributions are skew-safe.
1498
+ */
1499
+ declare function findEditorSchemaRisks(contributions: readonly EditorContribution[]): EditorSchemaRisk[];
1500
+ /**
1501
+ * Warn (in development) when a plugin's editor contributions add schema. Returns
1502
+ * the detected risks so callers can also gate or surface them.
1503
+ */
1504
+ declare function warnOnEditorSchemaRisks(pluginId: string, contributions: readonly EditorContribution[]): EditorSchemaRisk[];
1505
+
1506
+ /**
1507
+ * @xnetjs/plugins — the Connector primitive (exploration 0196).
1508
+ *
1509
+ * A Connector is xNet's answer to the agent-native CLI (Printing Press / OpenClaw):
1510
+ * instead of giving the agent a credentialed shell, it syncs an external service
1511
+ * into governed XNet nodes and exposes agent-callable tools over them. It is a
1512
+ * `FeatureModule` subtype that bundles three things:
1513
+ *
1514
+ * 1. a `capabilities` manifest — `secrets` (held by the hub broker, never the
1515
+ * agent), `schemaWrite` (what it may materialize), `network` (where it may
1516
+ * reach);
1517
+ * 2. a server-side `sync` adapter that pulls the external API into nodes; and
1518
+ * 3. `agentTools` the model can call over the synced, policy-evaluated store.
1519
+ *
1520
+ * `defineConnector` produces the `FeatureModule` (so it installs, consents, and
1521
+ * ships through the marketplace like any plugin) plus the sync spec and tools.
1522
+ * The hub half is wired by convention under `<id>.sync` (see the hub
1523
+ * `connectorSyncFeature`). The guards are enforced by composition — `guardStore`,
1524
+ * `guardedFetch`, `scopedEnv`, the policy evaluator — not by new code here.
1525
+ */
1526
+
1527
+ /** How often a connector re-syncs. `manual` = only on explicit/agent trigger. */
1528
+ type ConnectorCadence = 'manual' | 'hourly' | 'daily' | {
1529
+ everyMs: number;
1530
+ };
1531
+ /** The minimal node store surface a connector's `pull` writes through. */
1532
+ interface ConnectorStore {
1533
+ create(options: {
1534
+ schemaId: string;
1535
+ properties: Record<string, unknown>;
1536
+ }): Promise<{
1537
+ id: string;
1538
+ schemaId: string;
1539
+ }>;
1540
+ get(id: string): Promise<{
1541
+ schemaId: string;
1542
+ } | null>;
1543
+ update(id: string, options: {
1544
+ properties: Record<string, unknown>;
1545
+ }): Promise<unknown>;
1546
+ }
1547
+ /** A `fetch`-like the connector reaches the network through (host-allowlisted). */
1548
+ type ConnectorFetch = (input: string | {
1549
+ url: string;
1550
+ }, init?: unknown) => Promise<unknown>;
1551
+ /** Context handed to `pull`: scoped secrets, guarded fetch + store, target space. */
1552
+ interface ConnectorSyncContext {
1553
+ /** Broker-scoped env — only the keys declared in `capabilities.secrets`. */
1554
+ env: Record<string, string | undefined>;
1555
+ /** Guarded fetch — egress limited to `capabilities.network`. */
1556
+ fetch: ConnectorFetch;
1557
+ /** Guarded store — writes limited to `capabilities.schemaWrite`, space-stamped. */
1558
+ store: ConnectorStore;
1559
+ /** The target Space id every synced node is scoped to (the cascade boundary). */
1560
+ space: string;
1561
+ }
1562
+ interface ConnectorSyncResult {
1563
+ /** Number of nodes written this run. */
1564
+ written: number;
1565
+ [key: string]: unknown;
1566
+ }
1567
+ interface ConnectorSyncSpec {
1568
+ /** Schema IRIs this connector materializes (must be a subset of `schemaWrite`). */
1569
+ schemas: string[];
1570
+ /**
1571
+ * The relation property each synced node carries the target Space in (default
1572
+ * `space`). The runner stamps it so an author cannot forget — that stamp is
1573
+ * what makes the space cascade (and thus cross-contamination protection) hold.
1574
+ */
1575
+ spaceProperty?: string;
1576
+ /** Re-sync cadence (default `manual`). */
1577
+ cadence?: ConnectorCadence;
1578
+ /** Pull external data into nodes. Runs hub-side with scoped secrets. */
1579
+ pull(ctx: ConnectorSyncContext): Promise<ConnectorSyncResult>;
1580
+ }
1581
+ interface ConnectorDefinition {
1582
+ /** Reverse-domain id, e.g. `dev.xnet.connector.slack`. */
1583
+ id: string;
1584
+ /** Human-readable name. */
1585
+ name: string;
1586
+ /** Semantic version (default `0.1.0`). */
1587
+ version?: string;
1588
+ author?: string;
1589
+ description?: string;
1590
+ /** The capability surface — enforced, not advisory. `network` is required. */
1591
+ capabilities: ModuleCapabilities & {
1592
+ schemaWrite: string[];
1593
+ network: string[];
1594
+ };
1595
+ /** Server-side sync adapter. */
1596
+ sync: ConnectorSyncSpec;
1597
+ /** Model-facing tools over the synced store. */
1598
+ agentTools?: AgentToolContribution[];
1599
+ }
1600
+ /** The product of {@link defineConnector}: a module plus its runnable parts. */
1601
+ interface DefinedConnector {
1602
+ /** The installable, consent-gated, marketplace-shippable feature module. */
1603
+ module: FeatureModule;
1604
+ /** The sync adapter (run by the hub `connectorSyncFeature`). */
1605
+ sync: ConnectorSyncSpec;
1606
+ /** The contributed agent tools. */
1607
+ agentTools: AgentToolContribution[];
1608
+ /** The original definition. */
1609
+ definition: ConnectorDefinition;
1610
+ }
1611
+ declare class ConnectorDefinitionError extends Error {
1612
+ constructor(message: string);
1613
+ }
1614
+ /**
1615
+ * Define a Connector. Validates the capability/sync coherence (every synced
1616
+ * schema is writable; a network host is declared) and produces a `FeatureModule`
1617
+ * whose `hub.featureId` points at the sync feature mounted under `<id>.sync`.
1618
+ *
1619
+ * @throws {ConnectorDefinitionError} when the definition is incoherent.
1620
+ */
1621
+ declare function defineConnector(def: ConnectorDefinition): DefinedConnector;
1622
+
1623
+ /**
1624
+ * @xnetjs/plugins — plugin contributions as AI tools (exploration 0194 Phase 2).
1625
+ *
1626
+ * The AI agent can read/write the workspace (the `AiSurfaceService` tools), but
1627
+ * until now it could not invoke a *plugin command*. This exposes opted-in
1628
+ * commands as `AiToolDefinition`s with an `invoke`, so the agent's tool surface
1629
+ * grows with installed plugins — composable, the way MCP made the 2026 ecosystem.
1630
+ *
1631
+ * Opt-in is explicit (`aiExposed: true`) and capability-scoped (`aiScopes`): a
1632
+ * command the AI shouldn't touch simply never opts in, and a high-risk one is
1633
+ * surfaced as such so the agent/consent layer can gate it.
1634
+ */
1635
+
1636
+ /** An `AiToolDefinition` that can actually be called. */
1637
+ type AiCallableTool = AiToolDefinition & {
1638
+ invoke: (args: Record<string, unknown>) => Promise<AiToolCallResult>;
1639
+ };
1640
+ /**
1641
+ * Wrap the opted-in plugin commands as capability-scoped, callable AI tools.
1642
+ * Only commands with `aiExposed: true` are included — exposure is never implicit.
1643
+ */
1644
+ declare function contributionsAsAiTools(commands: readonly CommandContribution[]): AiCallableTool[];
1645
+
1646
+ /**
1647
+ * Validation helpers for AI surface mutation plans.
1648
+ */
1649
+
1650
+ declare function createAiValidationResult(errors?: string[], warnings?: string[]): AiValidationResult;
1651
+ /**
1652
+ * Validate a serialized mutation plan before it can be previewed or applied.
1653
+ */
1654
+ declare function validateAiMutationPlan(plan: unknown): AiValidationResult;
1655
+ /**
1656
+ * Return a copy of a plan with the latest validation result attached.
1657
+ */
1658
+ declare function attachAiPlanValidation(plan: AiMutationPlan): AiMutationPlan;
1659
+ declare function serializeAiMutationPlan(plan: AiMutationPlan): string;
1660
+ declare function parseAiMutationPlan(serialized: string): {
1661
+ plan: AiMutationPlan | null;
1662
+ validation: AiValidationResult;
1663
+ };
1664
+ declare function createAiOperation<TArgs extends Record<string, unknown>>(op: string, args: TArgs, rationale?: string): AiOperation<TArgs>;
1665
+ declare function createAiChangeSet(input: AiChangeSet): AiChangeSet;
1666
+
1667
+ /**
1668
+ * Local HTTP API Server
1669
+ *
1670
+ * Provides a REST API for external integrations (N8N, MCP, etc.) to interact
1671
+ * with xNet data. Runs on localhost only (port 31415 by default).
1672
+ *
1673
+ * This is designed to run in the Electron main process.
1674
+ */
1675
+
1676
+ type LocalAPITokenScope = 'health.read' | 'nodes.read' | 'nodes.write' | 'schemas.read' | 'events.read' | 'ai.read' | 'ai.write' | 'admin';
1677
+ type LocalAPITokenConfig = {
1678
+ token: string;
1679
+ label?: string;
1680
+ scopes: LocalAPITokenScope[];
1681
+ aiScopes?: AiScope[];
1682
+ createdAt?: string;
1683
+ };
1684
+ type LocalAPITokenSummary = Omit<LocalAPITokenConfig, 'token'>;
1685
+ /**
1686
+ * Node store interface (minimal subset needed by API)
1687
+ */
1688
+ interface NodeStoreAPI {
1689
+ get(id: string): Promise<NodeData | null>;
1690
+ list(options?: {
1691
+ schemaId?: string;
1692
+ limit?: number;
1693
+ offset?: number;
1694
+ }): Promise<NodeData[]>;
1695
+ query?(descriptor: NodeQueryDescriptor): Promise<NodeQueryResult>;
1696
+ create(options: {
1697
+ schemaId: string;
1698
+ properties: Record<string, unknown>;
1699
+ }): Promise<NodeData>;
1700
+ update(id: string, options: {
1701
+ properties: Record<string, unknown>;
1702
+ }): Promise<NodeData>;
1703
+ delete(id: string): Promise<void>;
1704
+ subscribe(listener: (event: NodeChangeEventData) => void): () => void;
1705
+ }
1706
+ /**
1707
+ * Schema registry interface (minimal subset needed by API)
1708
+ */
1709
+ interface SchemaRegistryAPI {
1710
+ getAllIRIs(): string[];
1711
+ get(iri: string): Promise<SchemaData | null>;
1712
+ }
1713
+ /**
1714
+ * Node data returned by API
1715
+ */
1716
+ interface NodeData {
1717
+ id: string;
1718
+ schemaId: string;
1719
+ properties: Record<string, unknown>;
1720
+ deleted: boolean;
1721
+ createdAt: number;
1722
+ updatedAt: number;
1723
+ }
1724
+ /**
1725
+ * Schema data returned by API
1726
+ */
1727
+ interface SchemaData {
1728
+ iri: string;
1729
+ name: string;
1730
+ properties: Record<string, unknown>;
1731
+ }
1732
+ /**
1733
+ * Node change event data
1734
+ */
1735
+ interface NodeChangeEventData {
1736
+ change: {
1737
+ type: string;
1738
+ };
1739
+ node: NodeData | null;
1740
+ isRemote: boolean;
1741
+ }
1742
+ /**
1743
+ * API configuration
1744
+ */
1745
+ interface LocalAPIConfig {
1746
+ /** Port to listen on (default: 31415) */
1747
+ port?: number;
1748
+ /** Host to bind to (default: '127.0.0.1') */
1749
+ host?: string;
1750
+ /** API token for authentication (optional) */
1751
+ token?: string;
1752
+ /** Scoped API tokens for local-only integrations. */
1753
+ tokens?: LocalAPITokenConfig[];
1754
+ /**
1755
+ * Allowed CORS origins (default: none).
1756
+ * Set to specific origins like ['http://localhost:3000'] for local dev,
1757
+ * or ['*'] to allow all origins (not recommended for production).
1758
+ * Empty array or undefined disables CORS entirely.
1759
+ */
1760
+ allowedOrigins?: string[];
1761
+ /** NodeStore instance */
1762
+ store: NodeStoreAPI;
1763
+ /** SchemaRegistry instance */
1764
+ schemas: SchemaRegistryAPI;
1765
+ /** Shared AI surface service. A default service is created when omitted. */
1766
+ aiSurface?: AiSurfaceService;
1767
+ /** Optional output and pagination limits for the default AI surface. */
1768
+ aiLimits?: Partial<AiSurfaceLimits>;
1769
+ }
1770
+
1771
+ /**
1772
+ * AI surface service for focused resources, context packs, and plan-only tools.
1773
+ */
1774
+
1775
+ type AiResourceContent = {
1776
+ uri: string;
1777
+ mimeType: string;
1778
+ text: string;
1779
+ };
1780
+ type AiSearchResult = {
1781
+ id: string;
1782
+ schemaId: string;
1783
+ title: string;
1784
+ snippet: string;
1785
+ score: number;
1786
+ revision: string;
1787
+ updatedAt: number;
1788
+ };
1789
+ type AiSearchOptions = {
1790
+ query: string;
1791
+ schemaId?: string;
1792
+ limit?: number;
1793
+ offset?: number;
1794
+ };
1795
+ type AiSurfaceLimits = {
1796
+ maxListLimit: number;
1797
+ maxSearchScan: number;
1798
+ maxSearchResults: number;
1799
+ maxContextResources: number;
1800
+ maxCharactersPerResource: number;
1801
+ maxJsonCharacters: number;
1802
+ maxCanvasObjects: number;
1803
+ maxDatabaseRows: number;
1804
+ };
1805
+ type AiPageMarkdownApplyAdapterInput = {
1806
+ pageId: string;
1807
+ markdown: string;
1808
+ bodyMarkdown: string;
1809
+ baseRevision: string;
1810
+ plan: AiMutationPlan;
1811
+ operation: AiOperation;
1812
+ };
1813
+ type AiPageMarkdownApplyAdapterResult = {
1814
+ mode: 'tiptap-yjs' | 'yjs' | 'custom';
1815
+ yjsField?: string;
1816
+ documentUpdate?: unknown;
1817
+ warnings?: string[];
1818
+ };
1819
+ type AiPageMarkdownApplyAdapter = {
1820
+ applyMarkdown(input: AiPageMarkdownApplyAdapterInput): Promise<AiPageMarkdownApplyAdapterResult>;
1821
+ };
1822
+ type AiPageMarkdownApplyResult = {
1823
+ applied: boolean;
1824
+ pageId: string;
1825
+ planId: string;
1826
+ mode: 'tiptap-yjs' | 'yjs' | 'custom' | 'node-property';
1827
+ baseRevision: string;
1828
+ liveRevision: string;
1829
+ markdownHash: string;
1830
+ bodyMarkdownHash: string;
1831
+ validation: {
1832
+ valid: boolean;
1833
+ errors: string[];
1834
+ warnings: string[];
1835
+ };
1836
+ auditEventId?: string;
1837
+ rollbackHandle?: string;
1838
+ yjsField?: string;
1839
+ documentUpdate?: unknown;
1840
+ };
1841
+ type AiPageMarkdownRollbackResult = {
1842
+ rolledBack: boolean;
1843
+ pageId: string;
1844
+ planId: string;
1845
+ rollbackHandle: string;
1846
+ auditEventId?: string;
1847
+ validation: {
1848
+ valid: boolean;
1849
+ errors: string[];
1850
+ warnings: string[];
1851
+ };
1852
+ };
1853
+ type AiDatabaseMutationApplyResult = {
1854
+ applied: boolean;
1855
+ databaseId: string;
1856
+ planId: string;
1857
+ baseRevision: string;
1858
+ liveRevision: string;
1859
+ appliedChangeIds: string[];
1860
+ rolledBackChangeIds: string[];
1861
+ validation: {
1862
+ valid: boolean;
1863
+ errors: string[];
1864
+ warnings: string[];
1865
+ };
1866
+ auditEventId?: string;
1867
+ };
1868
+ /** A node surfaced by an external retriever, with optional provenance. */
1869
+ type AiRetrievedNode = {
1870
+ nodeId: string;
1871
+ /** Human-readable graph path back to an entry node (for citation/provenance). */
1872
+ pathLabel?: string;
1873
+ };
1874
+ /**
1875
+ * Optional graph-aware retriever (exploration 0211). When provided, the `query`
1876
+ * path of `createContextPack` uses it instead of the built-in linear keyword
1877
+ * scan — i.e. hybrid vector + keyword entry search plus bounded graph expansion,
1878
+ * budgeted. The app injects `@xnetjs/brain`'s `retrieve` here (mapping its items
1879
+ * to `{ nodeId, pathLabel }`); absent it, context packs fall back to `search()`
1880
+ * with no behavior change.
1881
+ */
1882
+ type AiContextRetriever = (query: string, options: {
1883
+ limit: number;
1884
+ }) => Promise<AiRetrievedNode[]>;
1885
+ type AiSurfaceServiceConfig = {
1886
+ store: NodeStoreAPI;
1887
+ schemas: SchemaRegistryAPI;
1888
+ limits?: Partial<AiSurfaceLimits>;
1889
+ clock?: () => Date;
1890
+ pageMarkdownAdapter?: AiPageMarkdownApplyAdapter;
1891
+ /**
1892
+ * Tools contributed from outside the surface — plugin/connector `agentTools`
1893
+ * (exploration 0196). They are appended to `getTools()` and dispatched by
1894
+ * `callTool()`, so every consumer (in-app AI, the MCP server, the files-first
1895
+ * skill) sees them by registering once. A built-in `xnet_*` name always wins
1896
+ * over a colliding extra tool.
1897
+ */
1898
+ extraTools?: AiExtraTool[];
1899
+ /**
1900
+ * Optional graph-aware context retriever (exploration 0211). Drives the
1901
+ * `query` path of `createContextPack` when present; falls back to the built-in
1902
+ * keyword `search()` otherwise.
1903
+ */
1904
+ retrieveContext?: AiContextRetriever;
1905
+ };
1906
+ declare class AiSurfaceService {
1907
+ private readonly config;
1908
+ private readonly limits;
1909
+ private readonly clock;
1910
+ private sequence;
1911
+ private auditEvents;
1912
+ private readonly rollbackSnapshots;
1913
+ /** Contributed tools by name (exploration 0196), de-duped at construction. */
1914
+ private readonly extraTools;
1915
+ constructor(config: AiSurfaceServiceConfig);
1916
+ /** The contributed (non-built-in) tools, with built-in name collisions removed. */
1917
+ private getExtraTools;
1918
+ getResources(): AiResource[];
1919
+ /** The full tool surface: built-in `xnet_*` tools plus contributed extras. */
1920
+ getTools(): AiToolDefinition[];
1921
+ private builtInTools;
1922
+ callTool(name: string, args?: Record<string, unknown>): Promise<unknown>;
1923
+ readResource(uri: string): Promise<AiResourceContent>;
1924
+ toJsonText(value: unknown, format?: 'concise' | 'detailed'): string;
1925
+ private getWorkspaceSummary;
1926
+ private getRecentNodes;
1927
+ search(options: AiSearchOptions): Promise<Record<string, unknown>>;
1928
+ /**
1929
+ * Candidate node ids for a context-pack query: the injected graph-aware
1930
+ * retriever (exploration 0211) when configured, else the built-in keyword
1931
+ * `search()`. Order is preserved (best-first).
1932
+ */
1933
+ private candidateNodeIdsForQuery;
1934
+ createContextPack(options: {
1935
+ query?: string;
1936
+ seeds?: AiContextSeed[];
1937
+ limit?: number;
1938
+ }): Promise<AiContextPack>;
1939
+ /**
1940
+ * Walk typed relation edges out from a node to its connected neighbors, bounded
1941
+ * by `hops` (1–2) and a result `limit`. This is the just-in-time companion to
1942
+ * `createContextPack` (exploration 0211): retrieval hands the agent a budgeted
1943
+ * slice plus expandable node ids, and this lets it pull a specific node's
1944
+ * connections on demand instead of over-fetching the whole graph up front.
1945
+ */
1946
+ private expandGraph;
1947
+ private graphExpansion;
1948
+ createExternalContextResource(options: {
1949
+ url: string;
1950
+ text: string;
1951
+ mimeType?: string;
1952
+ }): AiContextPackResource;
1953
+ private readPageMarkdown;
1954
+ private readPageOutline;
1955
+ private planPagePatch;
1956
+ private applyPageMarkdown;
1957
+ private finalizeAppliedPageMarkdown;
1958
+ private getAuditLog;
1959
+ private rollbackPageMarkdown;
1960
+ private recordAuditEvent;
1961
+ private describeDatabase;
1962
+ private readDatabaseViews;
1963
+ private queryDatabase;
1964
+ private sampleDatabase;
1965
+ private explainDatabaseQuery;
1966
+ private executeDatabaseQueryDescriptor;
1967
+ private listCanvases;
1968
+ private readCanvasObjects;
1969
+ private readCanvasViewport;
1970
+ private readCanvasSelection;
1971
+ private searchCanvas;
1972
+ private exportCanvasJsonCanvas;
1973
+ private planCanvasJsonCanvasImport;
1974
+ private readCanvasObject;
1975
+ private hydrateSourcePreviews;
1976
+ private planCanvasMutation;
1977
+ private planDatabaseMutation;
1978
+ private applyDatabaseMutation;
1979
+ private applyDatabaseRowOperations;
1980
+ private applyDatabaseRowTransactionOperation;
1981
+ private rollbackAppliedDatabaseRowChanges;
1982
+ private applyDatabaseSchemaOperations;
1983
+ private planSurfaceMutation;
1984
+ private validatedPlan;
1985
+ private getNodeOrThrow;
1986
+ private getNodeProjection;
1987
+ private getSchemaSummaries;
1988
+ private readSchemaSummary;
1989
+ private contextResourceForSeed;
1990
+ private jsonResource;
1991
+ private stringifyJson;
1992
+ private nextId;
1993
+ private nowIso;
1994
+ }
1995
+ declare function createAiSurfaceService(config: AiSurfaceServiceConfig): AiSurfaceService;
1996
+
1997
+ /**
1998
+ * Page Markdown validation for AI-edited xNet page projections.
1999
+ */
2000
+
2001
+ type XNetPageMarkdownFrontmatter = {
2002
+ id?: string;
2003
+ schemaId?: string;
2004
+ revision?: string;
2005
+ exportedAt?: string;
2006
+ };
2007
+ type XNetMarkdownDirective = {
2008
+ kind: 'block' | 'inline' | 'wikilink';
2009
+ name: string;
2010
+ index: number;
2011
+ payload?: Record<string, unknown>;
2012
+ target?: string;
2013
+ };
2014
+ type XNetMarkdownDirectiveSpec = {
2015
+ kind: 'block' | 'inline' | 'wikilink';
2016
+ name: string;
2017
+ editorExtension: string;
2018
+ description: string;
2019
+ };
2020
+ type XNetPageMarkdownValidationOptions = {
2021
+ pageId?: string;
2022
+ schemaId?: string;
2023
+ baseRevision?: string;
2024
+ };
2025
+ type XNetPageMarkdownValidation = {
2026
+ frontmatter: XNetPageMarkdownFrontmatter | null;
2027
+ directives: XNetMarkdownDirective[];
2028
+ validation: AiValidationResult;
2029
+ };
2030
+ type XNetMarkdownDiffLineKind = 'context' | 'removed' | 'added';
2031
+ type XNetMarkdownDiffLine = {
2032
+ kind: XNetMarkdownDiffLineKind;
2033
+ text: string;
2034
+ beforeLine?: number;
2035
+ afterLine?: number;
2036
+ };
2037
+ type XNetMarkdownReviewDiff = {
2038
+ kind: 'markdown-diff';
2039
+ format: 'line';
2040
+ beforeLineCount: number;
2041
+ afterLineCount: number;
2042
+ lineCount: number;
2043
+ unifiedDiff: string;
2044
+ lines: XNetMarkdownDiffLine[];
2045
+ };
2046
+ declare const XNET_MARKDOWN_DIRECTIVE_SPECS: readonly [{
2047
+ readonly kind: "block";
2048
+ readonly name: "xnet-database";
2049
+ readonly editorExtension: "DatabaseEmbedExtension";
2050
+ readonly description: "Embeds a database view block with JSON view configuration.";
2051
+ }, {
2052
+ readonly kind: "block";
2053
+ readonly name: "xnet-page";
2054
+ readonly editorExtension: "PageEmbedExtension";
2055
+ readonly description: "Embeds a page preview block with page identity and preview metadata.";
2056
+ }, {
2057
+ readonly kind: "block";
2058
+ readonly name: "xnet-embed";
2059
+ readonly editorExtension: "EmbedExtension";
2060
+ readonly description: "Embeds rich external media with provider metadata.";
2061
+ }, {
2062
+ readonly kind: "inline";
2063
+ readonly name: "xnet-ref";
2064
+ readonly editorExtension: "SmartReferenceExtension";
2065
+ readonly description: "Embeds an inline smart reference to an external or internal resource.";
2066
+ }, {
2067
+ readonly kind: "inline";
2068
+ readonly name: "xnet-db-ref";
2069
+ readonly editorExtension: "DatabaseReferenceExtension";
2070
+ readonly description: "Embeds an inline database reference.";
2071
+ }, {
2072
+ readonly kind: "wikilink";
2073
+ readonly name: "wikilink";
2074
+ readonly editorExtension: "Wikilink";
2075
+ readonly description: "Parses [[Page Title]] wikilinks into xNet page links.";
2076
+ }];
2077
+ declare function validateXNetPageMarkdown(markdown: string, options?: XNetPageMarkdownValidationOptions): XNetPageMarkdownValidation;
2078
+ declare function getXNetMarkdownDirectiveSpecs(): readonly XNetMarkdownDirectiveSpec[];
2079
+ declare function parseXNetPageFrontmatter(markdown: string): XNetPageMarkdownFrontmatter | null;
2080
+ declare function stripXNetPageFrontmatter(markdown: string): string;
2081
+ declare function renderMarkdownLineDiff(before: string, after: string): string;
2082
+ declare function renderMarkdownReviewDiff(before: string, after: string): XNetMarkdownReviewDiff;
2083
+
2084
+ /**
2085
+ * Write guardrail for the MCP server (exploration 0175, "boundary hardening").
2086
+ *
2087
+ * The 0175 pitch is that xNet's guardrail makes an autonomous agent safe to
2088
+ * point at a workspace. But the generic `xnet_create/update/delete` tools used
2089
+ * to mutate the store directly, bypassing the mutation-plan guardrail that the
2090
+ * page/database tools enforce. This closes that gap for *every* MCP client
2091
+ * (OpenClaw, Claude Code, …) at the boundary, independent of the client's own
2092
+ * (often weak) safety model:
2093
+ *
2094
+ * - **Risk classification.** Deletes are `high`; creating an outward-facing node
2095
+ * (e.g. a chat message — "sending") is `high`; ordinary creates/updates are
2096
+ * `low`.
2097
+ * - **Confirmation gate.** `high`/`critical` and outward-facing writes return
2098
+ * `needs-confirmation` instead of mutating, until the caller re-issues with
2099
+ * `confirm: true` (after the human approves — see the ClawHub skill).
2100
+ * - **Cost budget.** Writes are charged against a per-surface budget
2101
+ * (`@xnetjs/abuse`); a runaway agent is throttled, not unbounded.
2102
+ * - **Provenance + audit.** Applied writes are recorded with their risk and an
2103
+ * optional AI-provenance evidence ref, queryable for review.
2104
+ */
2105
+
2106
+ type McpWriteKind = 'create' | 'update' | 'delete';
2107
+ interface McpWriteRequest {
2108
+ kind: McpWriteKind;
2109
+ /** Schema IRI (for create). Used to detect outward-facing writes. */
2110
+ schemaId?: string;
2111
+ /** Target node id (for update/delete). */
2112
+ nodeId?: string;
2113
+ /** Caller confirmation; required to apply high-risk / outward-facing writes. */
2114
+ confirm?: boolean;
2115
+ /** Optional provenance from the calling agent (model that authored the write). */
2116
+ provenance?: AISignalProvenanceInput;
2117
+ }
2118
+ type McpWriteVerdict = {
2119
+ decision: 'allow';
2120
+ risk: AiRiskLevel;
2121
+ outwardFacing: boolean;
2122
+ provenanceRef: string | null;
2123
+ } | {
2124
+ decision: 'needs-confirmation';
2125
+ risk: AiRiskLevel;
2126
+ outwardFacing: boolean;
2127
+ reason: string;
2128
+ } | {
2129
+ decision: 'blocked';
2130
+ risk: AiRiskLevel;
2131
+ reason: string;
2132
+ };
2133
+ interface McpWriteAuditEvent {
2134
+ kind: McpWriteKind;
2135
+ risk: AiRiskLevel;
2136
+ outwardFacing: boolean;
2137
+ schemaId?: string;
2138
+ nodeId?: string;
2139
+ provenanceRef?: string;
2140
+ at: number;
2141
+ }
2142
+ interface McpWriteGuardrailOptions {
2143
+ /** Schema IRIs whose creation is outward-facing (raises risk, requires confirm). */
2144
+ outwardFacingSchemas?: readonly string[];
2145
+ /** Cost budget policy. Defaults to 120 writes / 60s on the surface. */
2146
+ budgetPolicy?: PublicWriteBudgetPolicy;
2147
+ /** Abuse surface for budget keys. Defaults to `localApi`. */
2148
+ surface?: AbuseSurface;
2149
+ /** Injectable clock (ms). Defaults to `Date.now`. */
2150
+ clock?: () => number;
2151
+ }
2152
+ declare class McpWriteGuardrail {
2153
+ private readonly outward;
2154
+ private readonly policy;
2155
+ private readonly surface;
2156
+ private readonly now;
2157
+ private usage;
2158
+ private auditLog;
2159
+ constructor(options?: McpWriteGuardrailOptions);
2160
+ /** Classify and gate a write. Does not mutate anything; charges budget only on `allow`. */
2161
+ evaluate(req: McpWriteRequest): McpWriteVerdict;
2162
+ /** Record an applied write for audit/rollback review. Returns the event. */
2163
+ recordApplied(req: McpWriteRequest, verdict: Extract<McpWriteVerdict, {
2164
+ decision: 'allow';
2165
+ }>, nodeId?: string): McpWriteAuditEvent;
2166
+ getAuditLog(limit?: number): McpWriteAuditEvent[];
2167
+ private classify;
2168
+ }
2169
+
2170
+ /**
2171
+ * @xnetjs/plugins — connector sync runner (exploration 0196).
2172
+ *
2173
+ * Runs a connector's `pull` with the guardrails composed, so the *author* writes
2174
+ * plain `store.create(...)` / `fetch(...)` and the *framework* guarantees:
2175
+ *
2176
+ * - **egress containment** — `fetch` is `guardedFetch`, limited to the declared
2177
+ * `capabilities.network`;
2178
+ * - **schema-write containment** — `store` is `guardStore`, limited to the
2179
+ * declared `capabilities.schemaWrite`;
2180
+ * - **space scoping** — every created node is stamped with the target Space, so
2181
+ * the authorization cascade keeps one space's synced data invisible to agents
2182
+ * working in another (no cross-contamination);
2183
+ * - **budget** — writes are charged against the `connector` surface, separate
2184
+ * from the interactive agent's `localApi` budget, so a bulk backfill is
2185
+ * throttled rather than unbounded.
2186
+ *
2187
+ * Secret scoping is the hub's job: `mountFeatures` hands `pull` a `scopedEnv`, so
2188
+ * this runner never sees (or needs) the full process env — keeping the dependency
2189
+ * direction clean (no `@xnetjs/plugins` → `@xnetjs/hub` edge).
2190
+ */
2191
+
2192
+ declare class ConnectorSyncError extends Error {
2193
+ constructor(message: string);
2194
+ }
2195
+ /** A node store with the methods the runner guards (create/get/update). */
2196
+ type GuardableConnectorStore = ConnectorStore;
2197
+ interface RunConnectorSyncPorts {
2198
+ /** Broker-scoped env (the hub scopes it; tests pass a minimal object). */
2199
+ env: Record<string, string | undefined>;
2200
+ /** Underlying fetch — wrapped in `guardedFetch` before reaching `pull`. */
2201
+ fetch: ConnectorFetch;
2202
+ /** Underlying store — wrapped in `guardStore` + space-stamp before `pull`. */
2203
+ store: GuardableConnectorStore;
2204
+ /** Target Space id. Required unless `allowUnscoped` is set. */
2205
+ space: string | null;
2206
+ /** Write guardrail; defaults to a fresh `connector`-surface guardrail. */
2207
+ guardrail?: McpWriteGuardrail;
2208
+ /** Opt out of the space requirement (personal / intentionally unscoped sync). */
2209
+ allowUnscoped?: boolean;
2210
+ }
2211
+ /**
2212
+ * Run a connector's sync with all guards composed. The connector author's `pull`
2213
+ * sees a guarded fetch + store and a target space; it cannot reach undeclared
2214
+ * hosts, write undeclared schemas, leak across spaces, or exceed its budget.
2215
+ *
2216
+ * @throws {ConnectorSyncError} when a target space is required but missing.
2217
+ */
2218
+ declare function runConnectorSync(def: ConnectorDefinition, ports: RunConnectorSyncPorts): Promise<ConnectorSyncResult>;
2219
+
2220
+ /**
2221
+ * @xnetjs/plugins — marketplace index + search (exploration 0192).
2222
+ *
2223
+ * The data layer behind the 0047 GitHub-backed registry: the shape of a
2224
+ * `registry.json` entry, pure browse/search/sort over it, rating aggregation,
2225
+ * and a `MarketplaceClient` whose network access is injected (a `fetchJson`
2226
+ * port) so it is unit-testable without a server. The in-app Marketplace view
2227
+ * renders on top of this; the view itself is app-side.
2228
+ */
2229
+
2230
+ /** A single entry in the marketplace index (`registry.json`). */
2231
+ interface MarketplaceEntry {
2232
+ id: string;
2233
+ name: string;
2234
+ description: string;
2235
+ version: string;
2236
+ author: string;
2237
+ /** Search keywords / tags. */
2238
+ keywords?: string[];
2239
+ /** Coarse category for filtering (`productivity`, `finance`, `social`, …). */
2240
+ category?: string;
2241
+ /** Capabilities the plugin requests (drives the consent preview). */
2242
+ capabilities?: ModuleCapabilities;
2243
+ /** URL the full manifest is fetched from at install. */
2244
+ manifestUrl: string;
2245
+ /** Lifetime install count (trust signal). */
2246
+ installs?: number;
2247
+ /** GitHub stars / community signal. */
2248
+ stars?: number;
2249
+ /** SPDX license id (exploration 0196) — shown as a badge; gated by CI policy. */
2250
+ license?: string;
2251
+ /** Monetization (exploration 0196). Absent = free. */
2252
+ pricing?: PluginPricing;
2253
+ /** Publisher identity for paid listings (exploration 0196). */
2254
+ publisherDid?: string;
2255
+ /** Provenance reference for verification (see `./provenance`). */
2256
+ provenance?: {
2257
+ sigstoreBundleUrl?: string;
2258
+ sourceRepo?: string;
2259
+ sourceCommit?: string;
2260
+ };
2261
+ }
2262
+ /** How a marketplace listing is sorted. */
2263
+ type MarketplaceSort = 'relevance' | 'installs' | 'stars' | 'name';
2264
+ /** Filter an index by free-text query (name/description/keywords/author/category). */
2265
+ declare function searchMarketplace(index: readonly MarketplaceEntry[], query: string): MarketplaceEntry[];
2266
+ /** Sort a marketplace listing. `relevance` requires the originating `query`. */
2267
+ declare function sortMarketplace(entries: readonly MarketplaceEntry[], sort: MarketplaceSort, query?: string): MarketplaceEntry[];
2268
+ /** Filter by category (case-insensitive); empty/undefined category returns all. */
2269
+ declare function filterByCategory(entries: readonly MarketplaceEntry[], category: string | undefined): MarketplaceEntry[];
2270
+ /** A weighted signal about what the user works with (from workspace usage). */
2271
+ interface UsageSignal {
2272
+ /** A category the user engages with (e.g. `finance`). */
2273
+ category?: string;
2274
+ /** A keyword the user engages with (e.g. `invoice`). */
2275
+ keyword?: string;
2276
+ /** Relative strength (default 1). */
2277
+ weight?: number;
2278
+ }
2279
+ interface RecommendOptions {
2280
+ /** Ids already installed — excluded from recommendations. */
2281
+ installedIds?: readonly string[];
2282
+ /** Cap the number of recommendations (default 5). */
2283
+ limit?: number;
2284
+ }
2285
+ /**
2286
+ * Recommend extensions from the marketplace index for a user, given weighted
2287
+ * usage signals (category/keyword affinity). Excludes already-installed ids and
2288
+ * ranks by match score, with install-count as a tiebreaker. Pure — the "AI
2289
+ * brain" decides the signals; this turns them into a ranked shortlist.
2290
+ */
2291
+ declare function recommendExtensions(index: readonly MarketplaceEntry[], signals: readonly UsageSignal[], options?: RecommendOptions): MarketplaceEntry[];
2292
+ /** A single rating (one node per rating; aggregated for display). */
2293
+ interface PluginRating {
2294
+ pluginId: string;
2295
+ /** 1–5 stars. */
2296
+ stars: number;
2297
+ authorDID: string;
2298
+ review?: string;
2299
+ }
2300
+ interface RatingSummary {
2301
+ count: number;
2302
+ average: number;
2303
+ /** Histogram indexed 1..5. */
2304
+ histogram: Record<1 | 2 | 3 | 4 | 5, number>;
2305
+ }
2306
+ /** Aggregate ratings into a summary (rounds stars to 1..5, ignores invalid). */
2307
+ declare function aggregateRatings(ratings: readonly PluginRating[]): RatingSummary;
2308
+ /** Injected network port so the client is testable without a real fetch. */
2309
+ type FetchJson = <T>(url: string) => Promise<T>;
2310
+ interface MarketplaceClientOptions {
2311
+ /** URL of the registry index (`registry.json`). */
2312
+ indexUrl: string;
2313
+ /** How to fetch JSON (defaults to `globalThis.fetch`). */
2314
+ fetchJson?: FetchJson;
2315
+ }
2316
+ /**
2317
+ * A thin client over the registry index: fetch once (cached), then browse/search
2318
+ * in memory. The marketplace stays offline-friendly — the index is small JSON.
2319
+ */
2320
+ declare class MarketplaceClient {
2321
+ private readonly options;
2322
+ private cache;
2323
+ private readonly fetchJson;
2324
+ constructor(options: MarketplaceClientOptions);
2325
+ /** Fetch (and cache) the registry index. */
2326
+ load(force?: boolean): Promise<MarketplaceEntry[]>;
2327
+ /** Search + sort the index in one call. */
2328
+ search(query: string, opts?: {
2329
+ sort?: MarketplaceSort;
2330
+ category?: string;
2331
+ }): Promise<MarketplaceEntry[]>;
2332
+ }
2333
+ /** The provenance an install should pass to the registry (marketplace tier). */
2334
+ declare const MARKETPLACE_PROVENANCE: InstallProvenance;
2335
+
2336
+ /**
2337
+ * @xnetjs/plugins — connector artifacts + interop (exploration 0196).
2338
+ *
2339
+ * A Connector is governed *inside* xNet, but it should also be a good citizen of
2340
+ * the wider CLI/MCP ecosystem. From one definition this emits:
2341
+ *
2342
+ * - a `connectors`-category marketplace entry (so it ships like any plugin);
2343
+ * - per-tool descriptors for advertisement (what the user's own harness, e.g.
2344
+ * `xnet mcp serve`, exposes to Claude Code / Codex / OpenClaw);
2345
+ * - a per-connector `SKILL.md` fragment for external/files-first harnesses; and
2346
+ * - an `ImporterContribution`-shaped adapter, so a connector's sync doubles as
2347
+ * a one-shot importer (generalizing the dormant 0189 importers point).
2348
+ *
2349
+ * All pure — no I/O — so the marketplace publisher, the MCP server, and the CLI
2350
+ * each consume what they need.
2351
+ */
2352
+
2353
+ /** The marketplace category every Connector lists under. */
2354
+ declare const CONNECTOR_CATEGORY = "connectors";
2355
+ /** A model-facing descriptor of one connector tool (for tool advertisement). */
2356
+ interface ConnectorToolDescriptor {
2357
+ name: string;
2358
+ description: string;
2359
+ risk: string;
2360
+ }
2361
+ interface ConnectorArtifacts {
2362
+ /** Tool descriptors the user's harness advertises (MCP `tools/list`-style). */
2363
+ agentTools: ConnectorToolDescriptor[];
2364
+ /** A per-connector SKILL.md fragment for external / files-first harnesses. */
2365
+ skillMarkdown: string;
2366
+ /** A `connectors`-category marketplace entry. */
2367
+ marketplaceEntry: MarketplaceEntry;
2368
+ }
2369
+ /** Build a `connectors`-category marketplace entry from a connector. */
2370
+ declare function connectorMarketplaceEntry(connector: DefinedConnector, options?: {
2371
+ manifestUrl?: string;
2372
+ }): MarketplaceEntry;
2373
+ /**
2374
+ * Expose a connector's sync as an `ImporterContribution` — the connector's
2375
+ * `pull` doubles as a one-shot importer for the source platform. The adapter is
2376
+ * opaque to the importers registry (the import flow casts it), matching the 0189
2377
+ * "defined now, consumed later" shape.
2378
+ */
2379
+ declare function connectorAsImporter(connector: DefinedConnector): ImporterContribution;
2380
+ /**
2381
+ * Emit the portable artifacts for a connector: tool descriptors, a SKILL.md
2382
+ * fragment, and a marketplace entry. One definition → every surface.
2383
+ */
2384
+ declare function emitConnectorArtifacts(connector: DefinedConnector, options?: {
2385
+ manifestUrl?: string;
2386
+ }): ConnectorArtifacts;
2387
+
2388
+ /**
2389
+ * @xnetjs/plugins — capability consent (exploration 0192).
2390
+ *
2391
+ * Turns a `ModuleCapabilities` declaration into human-readable consent lines for
2392
+ * the install dialog, and decides whether a given install even needs a consent
2393
+ * prompt (wiring `requiresCapabilityReprompt` into the install flow). The React
2394
+ * dialog is app-side; this is the headless logic it renders and obeys.
2395
+ *
2396
+ * Design note: a `*` (all-schemas) or raw-secret grant is rendered as `danger`
2397
+ * so the UI can shout — the npm-permissions failure mode is consent fatigue, so
2398
+ * broad grants must look different from narrow ones.
2399
+ */
2400
+
2401
+ /** A single human-readable line in the consent dialog. */
2402
+ interface ConsentLine {
2403
+ /** Coarse icon hint for the UI (`edit`, `eye`, `globe`, `key`, `plug`). */
2404
+ icon: 'edit' | 'eye' | 'globe' | 'key' | 'plug';
2405
+ /** Human sentence, e.g. "Modify your Task data". */
2406
+ text: string;
2407
+ /** Whether this grant is broad/sensitive enough to warrant a warning. */
2408
+ danger: boolean;
2409
+ }
2410
+ /** Short, human label for a schema IRI: `xnet://xnet.fyi/Task@1.0.0` → `Task`. */
2411
+ declare function shortSchemaName(iri: string): string;
2412
+ /** Describe a capability grant as consent lines (one per declared capability). */
2413
+ declare function describeCapabilities(caps: ModuleCapabilities | undefined): ConsentLine[];
2414
+ /** The decision the install path makes about consent for a given install. */
2415
+ interface ConsentDecision {
2416
+ /** Provenance-derived trust tier this plugin will run at. */
2417
+ tier: TrustTier;
2418
+ /** Whether the user must be prompted before activation. */
2419
+ needsPrompt: boolean;
2420
+ /** The lines to show, if prompted. */
2421
+ lines: ConsentLine[];
2422
+ /** True if any requested grant is broad/sensitive. */
2423
+ hasDanger: boolean;
2424
+ }
2425
+ /**
2426
+ * Decide whether an install needs consent and what to show. `needsPrompt` is
2427
+ * true only when provenance requires a re-prompt AND the plugin actually
2428
+ * requests capabilities — a capability-free marketplace plugin still installs
2429
+ * into the marketplace sandbox tier but has nothing to consent to.
2430
+ */
2431
+ declare function evaluateInstallConsent(provenance: InstallProvenance, caps: ModuleCapabilities | undefined): ConsentDecision;
2432
+
2433
+ /**
2434
+ * @xnetjs/plugins — connector install gate (exploration 0196).
2435
+ *
2436
+ * Connectors flow through the same provenance→trust + consent machinery as any
2437
+ * plugin, with one extra rule: **a connector that requests secrets can never be
2438
+ * auto-trusted on `ai-generated` provenance.** "The model authored it" must not
2439
+ * be sufficient to hand it a credential — a human promotes it (re-authoring or
2440
+ * publishing it, which changes the provenance) before it can hold a token.
2441
+ */
2442
+
2443
+ interface ConnectorInstallGate {
2444
+ /** The normal capability-consent decision (lines, danger, reprompt). */
2445
+ consent: ConsentDecision;
2446
+ /** Whether the connector may install after the normal consent flow. */
2447
+ installable: boolean;
2448
+ /** Why it is not installable (requires manual promotion), when blocked. */
2449
+ blockedReason?: string;
2450
+ }
2451
+ /**
2452
+ * Evaluate whether a connector may install given its provenance + capabilities.
2453
+ * Returns the consent decision plus a hard gate that blocks secret-holding
2454
+ * AI-generated connectors from auto-install.
2455
+ */
2456
+ declare function evaluateConnectorInstall(provenance: InstallProvenance, capabilities: ModuleCapabilities | undefined): ConnectorInstallGate;
2457
+
2458
+ /**
2459
+ * @xnetjs/plugins — wrap an external CLI as a Connector (exploration 0196).
2460
+ *
2461
+ * The interop play: an existing agent-native CLI (e.g. a Printing Press tool)
2462
+ * becomes a governed Connector by running it and mapping its output into nodes.
2463
+ * The wrapper governs the **output mapping** — the synced schema, the target
2464
+ * Space, the write budget — so the agent reads policy-evaluated nodes instead of
2465
+ * the raw CLI.
2466
+ *
2467
+ * Honest caveat: the wrapped CLI is a subprocess that reaches the network itself,
2468
+ * so its *egress* is NOT enforced by `guardedFetch`. `network` here is for the
2469
+ * consent/marketplace display; a wrapped CLI should be a trusted, clearly-labeled
2470
+ * tier. The `runCli` port is injected (no `@xnetjs/devkit` edge here), so the
2471
+ * caller decides how the CLI actually runs (and can sandbox it).
2472
+ */
2473
+
2474
+ interface WrapCliConnectorOptions {
2475
+ /** Reverse-domain id, e.g. `dev.acme.connector.wikipedia-cli`. */
2476
+ id: string;
2477
+ name: string;
2478
+ version?: string;
2479
+ author?: string;
2480
+ description?: string;
2481
+ /** The schema each parsed record is materialized as. */
2482
+ schema: string;
2483
+ /** Hosts the CLI reaches — for display only (subprocess egress is not enforced). */
2484
+ network: string[];
2485
+ /** Secrets the CLI needs (held by the hub broker). */
2486
+ secrets?: string[];
2487
+ /** The Space relation property to stamp (default `space`). */
2488
+ spaceProperty?: string;
2489
+ /** Run the external CLI and return its stdout. Injected (e.g. a CommandRunner). */
2490
+ runCli: () => Promise<string>;
2491
+ /** Parse the CLI stdout into records; each becomes one node's properties. */
2492
+ parse: (stdout: string) => Array<Record<string, unknown>>;
2493
+ /** Optional agent tools over the synced nodes. */
2494
+ agentTools?: AgentToolContribution[];
2495
+ }
2496
+ /**
2497
+ * Produce a governed Connector that runs an external CLI and maps its output
2498
+ * into nodes. The CLI runs once per sync; each parsed record is written through
2499
+ * the guarded, space-stamped, budget-charged store.
2500
+ */
2501
+ declare function wrapCliConnector(options: WrapCliConnectorOptions): DefinedConnector;
2502
+
2503
+ /**
2504
+ * @xnetjs/plugins — the Slack migration connector (exploration 0198).
2505
+ *
2506
+ * "Switch from Slack to XNet and bring my data with me." This is the Half-1
2507
+ * answer from exploration 0198: a {@link defineConnector} that pulls a Slack
2508
+ * workspace's channels and message history into XNet's native `Channel` and
2509
+ * `ChatMessage` nodes, through the guarded connector store (egress-contained to
2510
+ * `slack.com`, space-stamped, budget-charged). The Slack token lives in the hub
2511
+ * broker and never reaches the agent — it only ever sees the synced nodes.
2512
+ *
2513
+ * Message bodies are translated to GitHub-flavored markdown via
2514
+ * `@xnetjs/slack-compat` (Block Kit first, then `mrkdwn`), so they render
2515
+ * natively in XNet chat. Pagination, DMs, files and reactions are deferred (see
2516
+ * the exploration's checklist).
2517
+ */
2518
+
2519
+ /** Default reverse-domain id; matches the worked example in the connector docs. */
2520
+ declare const SLACK_CONNECTOR_ID = "dev.xnet.connector.slack";
2521
+ declare const CHANNEL_SCHEMA = "xnet://xnet.fyi/Channel@1.0.0";
2522
+ declare const CHAT_MESSAGE_SCHEMA = "xnet://xnet.fyi/ChatMessage@1.0.0";
2523
+ interface SlackConnectorOptions {
2524
+ /** Override the connector id (default {@link SLACK_CONNECTOR_ID}). */
2525
+ id?: string;
2526
+ /**
2527
+ * Backing for the `slack_search_messages` agent tool. When provided, the
2528
+ * connector contributes a model-facing search over the imported messages;
2529
+ * when omitted, no agent tool is contributed (pull-only migration).
2530
+ */
2531
+ search?: (args: {
2532
+ query: string;
2533
+ }) => unknown | Promise<unknown>;
2534
+ }
2535
+ /**
2536
+ * Build the Slack migration connector. The `pull` imports channels +
2537
+ * history into `Channel`/`ChatMessage` nodes via the guarded store.
2538
+ */
2539
+ declare function buildSlackConnector(options?: SlackConnectorOptions): DefinedConnector;
2540
+
2541
+ /**
2542
+ * @xnetjs/plugins — the RSS / Atom connector (exploration 0213).
2543
+ *
2544
+ * The zero-auth, hobbyist-loved integration: poll a feed URL and materialize
2545
+ * each entry as a governed `FeedItem` node through the guarded connector store.
2546
+ * The parser is dependency-free (a small, defensive RSS+Atom extractor) so the
2547
+ * package keeps its no-runtime-deps posture. `guid` carries the feed entry's
2548
+ * stable id so a host can de-duplicate across polls.
2549
+ */
2550
+
2551
+ declare const RSS_CONNECTOR_ID = "dev.xnet.connector.rss";
2552
+ declare const FEED_ITEM_SCHEMA = "xnet://xnet.fyi/FeedItem@1.0.0";
2553
+ /** One normalized feed entry (RSS `<item>` or Atom `<entry>`). */
2554
+ interface FeedEntry {
2555
+ title: string;
2556
+ link?: string;
2557
+ guid?: string;
2558
+ summary?: string;
2559
+ publishedAt?: number;
2560
+ }
2561
+ /**
2562
+ * Parse an RSS or Atom document into normalized entries. Defensive by design:
2563
+ * malformed or unknown markup yields fewer entries rather than throwing, and the
2564
+ * scan is linear so a hostile feed cannot pin the event loop.
2565
+ */
2566
+ declare function parseFeed(xml: string): FeedEntry[];
2567
+ interface RssConnectorOptions {
2568
+ /** The RSS/Atom feed URL to poll. Its host becomes the sole `network` grant. */
2569
+ feedUrl: string;
2570
+ /** Override the connector id (default {@link RSS_CONNECTOR_ID}). */
2571
+ id?: string;
2572
+ /** The `Feed` node id every item links back to (optional). */
2573
+ feedNodeId?: string;
2574
+ /** Backing for an optional `rss_search_items` agent tool. */
2575
+ search?: (args: {
2576
+ query: string;
2577
+ }) => unknown | Promise<unknown>;
2578
+ }
2579
+ /**
2580
+ * Build the RSS/Atom connector. The `pull` polls one feed URL and materializes
2581
+ * its entries into `FeedItem` nodes via the guarded store.
2582
+ */
2583
+ declare function buildRssConnector(options: RssConnectorOptions): DefinedConnector;
2584
+
2585
+ /**
2586
+ * @xnetjs/plugins — API pull connectors: GitHub, Notion, Airtable
2587
+ * (exploration 0213).
2588
+ *
2589
+ * The API-key tier of the integration catalog. Each polls a service's REST API
2590
+ * and materializes objects into the generic, governed `ExternalItem` node
2591
+ * (source / kind / externalId / title / url / status / raw), through the guarded
2592
+ * connector store — the credential stays in the hub broker; the agent only ever
2593
+ * sees the synced nodes. Schema IRIs are inlined (matching `slack-migration`) so
2594
+ * the package keeps no `@xnetjs/data` dependency.
2595
+ *
2596
+ * Robustness (0213 review): each pull (1) requires its declared secret to be
2597
+ * present (a misconfiguration is a loud error, not a silent `Bearer ` request),
2598
+ * (2) throws on a non-2xx response instead of returning `{ written: 0 }`, and
2599
+ * (3) follows the provider's pagination so it doesn't silently cap at one page.
2600
+ */
2601
+
2602
+ declare const EXTERNAL_ITEM_SCHEMA = "xnet://xnet.fyi/ExternalItem@1.0.0";
2603
+ interface ApiConnectorBaseOptions {
2604
+ id?: string;
2605
+ search?: (args: {
2606
+ query: string;
2607
+ }) => unknown | Promise<unknown>;
2608
+ }
2609
+ declare const GITHUB_CONNECTOR_ID = "dev.xnet.connector.github";
2610
+ interface GithubConnectorOptions extends ApiConnectorBaseOptions {
2611
+ /** Repository owner (user or org). */
2612
+ owner: string;
2613
+ /** Repository name. */
2614
+ repo: string;
2615
+ }
2616
+ /**
2617
+ * Build the GitHub connector. Imports a repo's issues and pull requests
2618
+ * (`state=all`, all pages) into `ExternalItem` nodes via the GitHub REST API
2619
+ * (`GITHUB_TOKEN`).
2620
+ */
2621
+ declare function buildGithubConnector(options: GithubConnectorOptions): DefinedConnector;
2622
+ declare const NOTION_CONNECTOR_ID = "dev.xnet.connector.notion";
2623
+ type NotionConnectorOptions = ApiConnectorBaseOptions;
2624
+ /**
2625
+ * Build the Notion connector. Imports pages the integration token can see (via
2626
+ * `/v1/search`, following `has_more`) into `ExternalItem` nodes (`NOTION_TOKEN`).
2627
+ */
2628
+ declare function buildNotionConnector(options?: NotionConnectorOptions): DefinedConnector;
2629
+ declare const AIRTABLE_CONNECTOR_ID = "dev.xnet.connector.airtable";
2630
+ interface AirtableConnectorOptions extends ApiConnectorBaseOptions {
2631
+ /** The Airtable base id (`app...`). */
2632
+ baseId: string;
2633
+ /** The table id or name. */
2634
+ tableId: string;
2635
+ }
2636
+ /**
2637
+ * Build the Airtable connector. Imports a table's records (following the
2638
+ * `offset` cursor) into `ExternalItem` nodes, preserving each record's fields in
2639
+ * `raw` (`AIRTABLE_TOKEN`).
2640
+ */
2641
+ declare function buildAirtableConnector(options: AirtableConnectorOptions): DefinedConnector;
2642
+ declare const LINEAR_CONNECTOR_ID = "dev.xnet.connector.linear";
2643
+ type LinearConnectorOptions = ApiConnectorBaseOptions;
2644
+ /**
2645
+ * Build the Linear connector. Imports issues via Linear's GraphQL API (following
2646
+ * the `pageInfo` cursor) into `ExternalItem` nodes (`LINEAR_API_KEY`). Personal
2647
+ * API keys go in the `Authorization` header verbatim (no `Bearer` prefix).
2648
+ */
2649
+ declare function buildLinearConnector(options?: LinearConnectorOptions): DefinedConnector;
2650
+
2651
+ /**
2652
+ * @xnetjs/plugins — network endowment (exploration 0192).
2653
+ *
2654
+ * The capability guard ({@link ../ecosystem/capability-guard}) closes the
2655
+ * `schemaWrite` hole at the store boundary. This closes the `network` hole at
2656
+ * the fetch boundary: a plugin that wants to reach the network gets a
2657
+ * `guardedFetch` whose every request is checked against its declared `network`
2658
+ * allowlist (`isNetworkAllowed`), so a plugin can only talk to the hosts it
2659
+ * declared — and a plugin that declared none gets no egress at all.
2660
+ *
2661
+ * Like `guardStore`, this is the one handle a plugin should receive instead of
2662
+ * the ambient `fetch`; the host injects it as the `network` endowment.
2663
+ */
2664
+
2665
+ /** The subset of the `fetch` signature we wrap (kept structural, no DOM types). */
2666
+ type FetchLike = (input: string | {
2667
+ url: string;
2668
+ }, init?: unknown) => Promise<unknown>;
2669
+ /**
2670
+ * Wrap a `fetch` implementation so every request host is checked against the
2671
+ * plugin's declared `network` capability. Throws `CapabilityError` before the
2672
+ * request leaves if the host isn't allowed. A plugin with no `network` grant
2673
+ * can reach nothing (closed by default) — the wrapper still returns, but every
2674
+ * call throws until a host is declared.
2675
+ *
2676
+ * @param caps the plugin's declared capability grant
2677
+ * @param pluginId for error attribution
2678
+ * @param fetchImpl the underlying fetch (defaults to `globalThis.fetch`)
2679
+ */
2680
+ declare function guardedFetch(caps: ModuleCapabilities | undefined, pluginId: string, fetchImpl?: FetchLike): FetchLike;
2681
+
2682
+ /**
2683
+ * @xnetjs/plugins — the outbound Action primitive (exploration 0213).
2684
+ *
2685
+ * Connectors *pull* an external service into nodes; an Action is the reverse —
2686
+ * "when something happens in xNet, reach out." It is the other half of the
2687
+ * Zapier/IFTTT story (post to Discord on task close, send an email, hit a
2688
+ * webhook) and the symmetric twin of {@link ../connectors/define-connector}:
2689
+ *
2690
+ * 1. a `capabilities` manifest — `network` (where it may POST, closed by
2691
+ * default) and `secrets` (held by the hub broker, e.g. a Discord webhook
2692
+ * URL or a bot token);
2693
+ * 2. a `trigger` declaring what fires it (a node change on given schemas, a
2694
+ * schedule, or manual); and
2695
+ * 3. a `dispatch(event, ctx)` that does the outbound call through a guarded,
2696
+ * SSRF-checked `fetch`.
2697
+ *
2698
+ * Like a Connector it produces a `FeatureModule` (so it installs, consents, and
2699
+ * ships through the marketplace), with the hub half wired by convention under
2700
+ * `<id>.trigger`. The guards are enforced by composition in the action runner
2701
+ * (see {@link ./runner}); this module is just the shape + validation.
2702
+ */
2703
+
2704
+ /** What fires an action. */
2705
+ type ActionTrigger = {
2706
+ kind: 'schema-change';
2707
+ schemas: string[];
2708
+ } | {
2709
+ kind: 'schedule';
2710
+ cadence: ConnectorCadence;
2711
+ } | {
2712
+ kind: 'manual';
2713
+ };
2714
+ /** The change that triggered a dispatch (schema-change triggers carry a node). */
2715
+ interface ActionEvent {
2716
+ trigger: 'schema-change' | 'schedule' | 'manual';
2717
+ /** Change kind for schema-change events. */
2718
+ change?: 'create' | 'update' | 'delete';
2719
+ /** The affected node for schema-change events. */
2720
+ node?: {
2721
+ id: string;
2722
+ schemaId: string;
2723
+ properties?: Record<string, unknown>;
2724
+ };
2725
+ }
2726
+ /** Context handed to `dispatch`: scoped secrets + a guarded, SSRF-checked fetch. */
2727
+ interface ActionContext {
2728
+ /** Broker-scoped env — only the keys declared in `capabilities.secrets`. */
2729
+ env: Record<string, string | undefined>;
2730
+ /** Guarded fetch — egress limited to `capabilities.network`, SSRF-checked. */
2731
+ fetch: FetchLike;
2732
+ }
2733
+ interface ActionDefinition {
2734
+ /** Reverse-domain id, e.g. `dev.xnet.action.discord`. */
2735
+ id: string;
2736
+ name: string;
2737
+ version?: string;
2738
+ author?: string;
2739
+ description?: string;
2740
+ /** Capability surface — `network` is required (closed by default). */
2741
+ capabilities: ModuleCapabilities & {
2742
+ network: string[];
2743
+ };
2744
+ /** What fires this action. */
2745
+ trigger: ActionTrigger;
2746
+ /** Perform the outbound call. Runs hub-side with scoped secrets. */
2747
+ dispatch(event: ActionEvent, ctx: ActionContext): Promise<void>;
2748
+ /** Optional model-facing tools (e.g. a manual "send now"). */
2749
+ agentTools?: AgentToolContribution[];
2750
+ }
2751
+ /** The product of {@link defineAction}. */
2752
+ interface DefinedAction {
2753
+ module: FeatureModule;
2754
+ trigger: ActionTrigger;
2755
+ dispatch: ActionDefinition['dispatch'];
2756
+ agentTools: AgentToolContribution[];
2757
+ definition: ActionDefinition;
2758
+ }
2759
+ declare class ActionDefinitionError extends Error {
2760
+ constructor(message: string);
2761
+ }
2762
+ /**
2763
+ * Whether `action`'s trigger should fire for `event`. The "when X happens"
2764
+ * decision, kept pure so a host can fan an event out to matching actions.
555
2765
  */
556
- declare function getShortcutManager(): ShortcutManager;
2766
+ declare function shouldDispatch(trigger: ActionTrigger, event: ActionEvent): boolean;
557
2767
  /**
558
- * Install the global shortcut manager on the window.
559
- * Call this once at app startup.
2768
+ * Define an outbound Action. Validates the capability/trigger coherence (a
2769
+ * network host is declared; a schema-change trigger lists schemas) and produces
2770
+ * a `FeatureModule` whose `hub.featureId` points at `<id>.trigger`.
2771
+ *
2772
+ * @throws {ActionDefinitionError} when the definition is incoherent.
560
2773
  */
561
- declare function installShortcutHandler(): () => void;
2774
+ declare function defineAction(def: ActionDefinition): DefinedAction;
2775
+
2776
+ /**
2777
+ * @xnetjs/plugins — outbound action runner (exploration 0213).
2778
+ *
2779
+ * Runs an action's `dispatch` with the guards composed, so the *author* writes a
2780
+ * plain `fetch(...)` and the *framework* guarantees:
2781
+ *
2782
+ * - **egress containment** — `fetch` is `guardedFetch`, limited to the declared
2783
+ * `capabilities.network`;
2784
+ * - **SSRF protection** — every request URL is `assertPublicUrl`-checked, so a
2785
+ * user-configured target cannot reach localhost, RFC-1918, or cloud metadata
2786
+ * even if the host was allowlisted via the configured URL.
2787
+ *
2788
+ * Secret scoping is the hub's job (a `scopedEnv` is passed in), keeping the
2789
+ * dependency direction clean (no `@xnetjs/plugins` → `@xnetjs/hub` edge).
2790
+ */
2791
+
2792
+ declare class ActionDispatchError extends Error {
2793
+ constructor(message: string);
2794
+ }
2795
+ interface RunActionPorts {
2796
+ /** Broker-scoped env (the hub scopes it; tests pass a minimal object). */
2797
+ env: Record<string, string | undefined>;
2798
+ /** Underlying fetch — wrapped (network allowlist + SSRF) before `dispatch`. */
2799
+ fetch: FetchLike;
2800
+ }
2801
+ /**
2802
+ * Wrap a fetch so every outbound action request is (1) within the declared
2803
+ * `network` allowlist and (2) not pointed at a non-public host (SSRF).
2804
+ */
2805
+ declare function guardedActionFetch(def: Pick<ActionDefinition, 'id' | 'capabilities'>, fetchImpl: FetchLike): FetchLike;
2806
+ /**
2807
+ * Run one action's `dispatch` with the guards composed. The author never sees
2808
+ * the raw fetch or the full env.
2809
+ */
2810
+ declare function runAction(action: DefinedAction, event: ActionEvent, ports: RunActionPorts): Promise<void>;
2811
+
2812
+ /**
2813
+ * @xnetjs/plugins — SSRF guard for outbound actions (exploration 0213).
2814
+ *
2815
+ * Outbound actions POST to URLs that may be user-configured (a generic
2816
+ * webhook-out target). Even when the host is "allowlisted" (the action derived
2817
+ * its `network` grant from the configured URL), a user could point it at an
2818
+ * internal target — `http://169.254.169.254/` (cloud metadata), `localhost`, a
2819
+ * private RFC-1918 range — to exfiltrate credentials or reach internal
2820
+ * services. {@link assertPublicUrl} rejects those before the request leaves.
2821
+ *
2822
+ * The literal-host check itself lives in `@xnetjs/core`; this module keeps the
2823
+ * action-specific {@link ActionSsrfError} contract that callers/tests depend on.
2824
+ */
2825
+ declare class ActionSsrfError extends Error {
2826
+ readonly url: string;
2827
+ constructor(message: string, url: string);
2828
+ }
2829
+ /**
2830
+ * Throw {@link ActionSsrfError} unless `rawUrl` is a plausibly-public HTTP(S)
2831
+ * endpoint: rejects non-http(s) schemes, localhost, `.local`/`.internal`
2832
+ * suffixes, the cloud metadata host, and private/loopback/link-local IP
2833
+ * literals (v4 and v6).
2834
+ */
2835
+ declare function assertPublicUrl(rawUrl: string): void;
2836
+
2837
+ /**
2838
+ * @xnetjs/plugins — first-party outbound actions (exploration 0213).
2839
+ *
2840
+ * The Top-5/Top-10 notification sinks built on {@link defineAction}: Discord and
2841
+ * Slack incoming webhooks, Telegram bots, transactional email (Resend), and a
2842
+ * generic webhook-out that bridges Zapier/Make/n8n. Each reads its credential
2843
+ * from the broker-scoped env, renders the triggering event to a message, and
2844
+ * POSTs through the guarded, SSRF-checked fetch. Hosts are declared at build
2845
+ * time so egress stays closed-by-default; the generic webhook-out locks its
2846
+ * `network` grant to the configured URL's host.
2847
+ */
2848
+
2849
+ /** A compact, human-readable summary of the triggering event. */
2850
+ declare function renderEvent(event: ActionEvent): string;
2851
+ interface BaseActionOptions {
2852
+ /** Override the connector-style id. */
2853
+ id?: string;
2854
+ /** What fires the action (default: manual). */
2855
+ trigger?: ActionTrigger;
2856
+ /** Render the event to a message (default {@link renderEvent}). */
2857
+ render?: (event: ActionEvent) => string;
2858
+ }
2859
+ /** Discord: POST `{ content }` to an incoming-webhook URL (`DISCORD_WEBHOOK_URL`). */
2860
+ declare function buildDiscordAction(options?: BaseActionOptions): DefinedAction;
2861
+ /** Slack: POST `{ text }` to an incoming-webhook URL (`SLACK_WEBHOOK_URL`). */
2862
+ declare function buildSlackWebhookAction(options?: BaseActionOptions): DefinedAction;
2863
+ /** Telegram: send a message via the Bot API (`TELEGRAM_BOT_TOKEN`/`TELEGRAM_CHAT_ID`). */
2864
+ declare function buildTelegramAction(options?: BaseActionOptions): DefinedAction;
2865
+ interface EmailActionOptions extends BaseActionOptions {
2866
+ /** Verified sender, e.g. `xNet <alerts@example.com>`. */
2867
+ from: string;
2868
+ /** Recipient address(es). */
2869
+ to: string | string[];
2870
+ /** Subject line (default: a short event summary). */
2871
+ subject?: string;
2872
+ }
2873
+ /** Email (Resend): send a transactional email (`RESEND_API_KEY`). */
2874
+ declare function buildEmailAction(options: EmailActionOptions): DefinedAction;
2875
+ interface WebhookOutOptions extends BaseActionOptions {
2876
+ /** The destination URL. Its host becomes the action's sole `network` grant. */
2877
+ url: string;
2878
+ /** Transform the event into the POST body (default: the event itself). */
2879
+ transform?: (event: ActionEvent) => unknown;
2880
+ /** Extra request headers. */
2881
+ headers?: Record<string, string>;
2882
+ }
2883
+ /**
2884
+ * Generic webhook-out: POST each triggering event as JSON to a configured URL —
2885
+ * the escape hatch that bridges Zapier/Make/n8n/IFTTT. The `network` grant is
2886
+ * locked to the URL's host (and the SSRF guard still blocks internal targets).
2887
+ */
2888
+ declare function buildWebhookOutAction(options: WebhookOutOptions): DefinedAction;
562
2889
 
563
2890
  /**
564
2891
  * PluginRegistry - Central coordinator for plugin lifecycle
@@ -570,10 +2897,49 @@ interface RegisteredPlugin {
570
2897
  status: PluginStatus;
571
2898
  context?: ExtensionContext;
572
2899
  error?: Error;
2900
+ /** Where this plugin was installed from — derives its trust tier (0192). */
2901
+ provenance?: InstallProvenance;
2902
+ /** Provenance-derived execution trust tier (0192). */
2903
+ trustTier?: TrustTier;
2904
+ }
2905
+ /**
2906
+ * Result of a paid-plugin license check (exploration 0196). The host wires this
2907
+ * to `@xnetjs/licenses`' `checkLicenseFor`; the plugin package stays free of a
2908
+ * hard dependency on the license verifier.
2909
+ */
2910
+ interface LicenseCheckResult {
2911
+ /** `true` if the buyer holds a valid license for this plugin. */
2912
+ ok: boolean;
2913
+ /** Why it failed (`no-license`, `expired`, `bad-signature`, …) — surfaced to UI. */
2914
+ reason?: string;
2915
+ }
2916
+ /** Options for {@link PluginRegistry.install} (all optional, back-compatible). */
2917
+ interface InstallOptions {
2918
+ /** Where the plugin came from. Drives trust tier + consent. Default `imported`. */
2919
+ provenance?: InstallProvenance;
2920
+ /** Host app version, for the `xnetVersion` compatibility gate. Skipped if absent. */
2921
+ hostVersion?: string;
2922
+ /**
2923
+ * Consent callback. Called only when provenance requires a re-prompt and the
2924
+ * plugin actually requests capabilities. Return `false` to abort the install.
2925
+ */
2926
+ onConsent?: (decision: ConsentDecision) => boolean | Promise<boolean>;
2927
+ /**
2928
+ * Paid-license callback (exploration 0196). Called only when the manifest's
2929
+ * `pricing` is non-free. Return `{ ok: false }` to block the install with a
2930
+ * {@link LicenseRequiredError}. Absent ⇒ paid plugins are blocked (fail-closed).
2931
+ */
2932
+ checkLicense?: (manifest: XNetExtension) => LicenseCheckResult | Promise<LicenseCheckResult>;
573
2933
  }
574
2934
  declare class PluginError extends Error {
575
2935
  constructor(message: string);
576
2936
  }
2937
+ /** Thrown when a paid plugin is installed without a valid license (0196). */
2938
+ declare class LicenseRequiredError extends PluginError {
2939
+ readonly pluginId: string;
2940
+ readonly reason: string;
2941
+ constructor(pluginId: string, reason: string);
2942
+ }
577
2943
  /**
578
2944
  * Manages plugin lifecycle: install, activate, deactivate, uninstall
579
2945
  */
@@ -586,9 +2952,15 @@ declare class PluginRegistry {
586
2952
  private listeners;
587
2953
  constructor(store: NodeStore, platform: Platform);
588
2954
  /**
589
- * Install and activate a plugin
2955
+ * Install and activate a plugin.
2956
+ *
2957
+ * Beyond manifest/platform validation, install runs the 0192 trust gates:
2958
+ * host-version compatibility, dependency resolution, and (for non-local
2959
+ * provenance) capability consent. Provenance derives the plugin's trust tier.
590
2960
  */
591
- install(manifest: XNetExtension): Promise<void>;
2961
+ install(manifest: XNetExtension, options?: InstallOptions): Promise<void>;
2962
+ /** Dependencies of `manifest` not satisfied by currently installed plugins. */
2963
+ private missingDependencies;
592
2964
  /**
593
2965
  * Activate an installed plugin
594
2966
  */
@@ -649,6 +3021,530 @@ declare class PluginRegistry {
649
3021
  loadFromStore(): Promise<void>;
650
3022
  }
651
3023
 
3024
+ /**
3025
+ * @xnetjs/plugins — capability enforcement (exploration 0192).
3026
+ *
3027
+ * 0189 added `ModuleCapabilities` (`schemaWrite`/`schemaRead`/`network`/…) as a
3028
+ * *declaration*. This module makes the declaration load-bearing: it turns the
3029
+ * declared grant into runtime gates.
3030
+ *
3031
+ * The enforcement point is the `NodeStore` handle a plugin receives in its
3032
+ * `ExtensionContext`. Plugins call `ctx.store.create/update/delete` directly, so
3033
+ * wrapping that handle is the one choke point a plugin cannot route around. We
3034
+ * wrap with a `Proxy` (rather than re-implementing the store) so the guard keeps
3035
+ * working as `NodeStore` grows methods — only `create`/`update`/`delete`/`get`/
3036
+ * `list` are intercepted; everything else passes straight through.
3037
+ *
3038
+ * A grant with neither `schemaWrite` nor `schemaRead` is unconstrained (the guard
3039
+ * returns the store untouched) — first-party/host code is meant to be unguarded.
3040
+ */
3041
+
3042
+ /** Thrown when a plugin tries to act outside its declared capability grant. */
3043
+ declare class CapabilityError extends Error {
3044
+ readonly pluginId: string;
3045
+ readonly capability: 'schemaWrite' | 'schemaRead' | 'network';
3046
+ readonly target: string;
3047
+ constructor(message: string, pluginId: string, capability: 'schemaWrite' | 'schemaRead' | 'network', target: string);
3048
+ }
3049
+ /**
3050
+ * Match a schema IRI against a capability pattern. Supports:
3051
+ * - exact: `xnet://xnet.fyi/Task@1.0.0`
3052
+ * - all: `*`
3053
+ * - version wildcard: `xnet://xnet.fyi/Task@*` (any version of Task)
3054
+ * - prefix wildcard: `xnet://xnet.fyi/*` (any schema under an authority)
3055
+ */
3056
+ declare function matchSchemaIri(pattern: string, iri: string): boolean;
3057
+ /** Whether the grant permits writing the given schema IRI. */
3058
+ declare function isSchemaWriteAllowed(caps: ModuleCapabilities | undefined, iri: string): boolean;
3059
+ /**
3060
+ * Whether the grant permits reading the given schema IRI. A grant that declares
3061
+ * no `schemaRead` does not restrict reads (reads are lower risk than writes;
3062
+ * restricting them is opt-in by declaring `schemaRead`).
3063
+ */
3064
+ declare function isSchemaReadAllowed(caps: ModuleCapabilities | undefined, iri: string): boolean;
3065
+ /**
3066
+ * Whether the grant permits a network request to the given URL/host. A
3067
+ * `network` entry may be an exact host (`api.stripe.com`) or a leading-dot
3068
+ * suffix (`.stripe.com`, matching any subdomain). No declared `network` → no
3069
+ * egress permitted (closed by default).
3070
+ */
3071
+ declare function isNetworkAllowed(caps: ModuleCapabilities | undefined, urlOrHost: string): boolean;
3072
+ /** Assert a schema write is permitted, or throw {@link CapabilityError}. */
3073
+ declare function assertSchemaWrite(caps: ModuleCapabilities | undefined, iri: string, pluginId: string): void;
3074
+ /** Assert a network request is permitted, or throw {@link CapabilityError}. */
3075
+ declare function assertNetwork(caps: ModuleCapabilities | undefined, urlOrHost: string, pluginId: string): void;
3076
+ /**
3077
+ * Wrap a NodeStore so writes are checked against the plugin's grant. Returns the
3078
+ * store unchanged when the grant constrains nothing. Generic over the concrete
3079
+ * store type so callers keep their `NodeStore` typing.
3080
+ */
3081
+ declare function guardStore<T extends object>(store: T, caps: ModuleCapabilities | undefined, pluginId: string): T;
3082
+
3083
+ /**
3084
+ * @xnetjs/plugins — version compatibility (exploration 0192).
3085
+ *
3086
+ * A tiny, dependency-free semver subset — enough to gate plugin installs on the
3087
+ * host's `xnetVersion` and to detect available updates. We deliberately avoid
3088
+ * pulling in `semver` (a node-centric dep) for what is a handful of comparisons;
3089
+ * the manifest already only accepts `\d+\.\d+\.\d+`-shaped versions.
3090
+ *
3091
+ * Supported range syntax: `*` / `x` (any), exact `1.2.3`, `>=1.2.3`, `>1.2.3`,
3092
+ * `<=1.2.3`, `<1.2.3`, caret `^1.2.3`, tilde `~1.2.3`. Pre-release/build
3093
+ * metadata is ignored (compared on major.minor.patch only).
3094
+ */
3095
+ interface SemVer {
3096
+ major: number;
3097
+ minor: number;
3098
+ patch: number;
3099
+ }
3100
+ /** Parse `1.2.3` (ignoring any `-pre`/`+build` suffix). Returns null if invalid. */
3101
+ declare function parseVersion(input: string): SemVer | null;
3102
+ /** Compare two versions: negative if a<b, 0 if equal, positive if a>b. */
3103
+ declare function compareVersions(a: SemVer, b: SemVer): number;
3104
+ /**
3105
+ * Whether `version` satisfies `range`. Unknown/unparseable ranges are treated as
3106
+ * "any" (`true`) so a missing `xnetVersion` never blocks an install; an explicit
3107
+ * but malformed bound also fails open by design (we cannot prove incompatibility).
3108
+ */
3109
+ declare function satisfiesRange(version: string, range: string): boolean;
3110
+ /**
3111
+ * Whether a plugin declaring `requiredHostRange` (its `xnetVersion`) is
3112
+ * compatible with `hostVersion`. A plugin with no declared requirement is
3113
+ * always compatible.
3114
+ */
3115
+ declare function isHostCompatible(requiredHostRange: string | undefined, hostVersion: string): boolean;
3116
+ /**
3117
+ * Whether `available` is a newer version than `installed` (a real update is
3118
+ * offerable). Returns false when either side is unparseable.
3119
+ */
3120
+ declare function hasUpdate(installed: string, available: string): boolean;
3121
+
3122
+ /**
3123
+ * @xnetjs/plugins — inter-plugin dependencies (exploration 0192).
3124
+ *
3125
+ * A plugin may declare `dependencies: { '<pluginId>': '<versionRange>' }` in its
3126
+ * manifest. These helpers resolve a safe install order (topological sort),
3127
+ * detect cycles, and report missing/incompatible dependencies — the pure logic
3128
+ * an installer runs before activating a set of plugins.
3129
+ *
3130
+ * Kept structural (operates on a minimal `{ id, version, dependencies }` shape)
3131
+ * so it works for `XNetExtension` and `FeatureModule` alike.
3132
+ */
3133
+ /** The minimal manifest shape the dependency resolver needs. */
3134
+ interface DependencyNode {
3135
+ id: string;
3136
+ version: string;
3137
+ dependencies?: Record<string, string>;
3138
+ }
3139
+ /** A dependency that is absent or version-incompatible among installed plugins. */
3140
+ interface MissingDependency {
3141
+ /** The plugin that declares the requirement. */
3142
+ dependent: string;
3143
+ /** The required plugin id. */
3144
+ required: string;
3145
+ /** The required version range. */
3146
+ range: string;
3147
+ /** Why it is unsatisfied. */
3148
+ reason: 'not-installed' | 'version-mismatch';
3149
+ /** The installed version, when present but mismatched. */
3150
+ installedVersion?: string;
3151
+ }
3152
+ /**
3153
+ * Report dependencies of `target` that are not satisfied by `installed`.
3154
+ * Satisfied = a plugin with the required id is installed AND its version
3155
+ * satisfies the declared range.
3156
+ */
3157
+ declare function findMissingDependencies(target: DependencyNode, installed: readonly DependencyNode[]): MissingDependency[];
3158
+ /** Thrown when a dependency graph contains a cycle. */
3159
+ declare class DependencyCycleError extends Error {
3160
+ readonly cycle: string[];
3161
+ constructor(cycle: string[]);
3162
+ }
3163
+ /**
3164
+ * Return the ids in a safe install order (dependencies before dependents).
3165
+ * Only edges *within the provided set* are ordered; dependencies on plugins not
3166
+ * in the set are ignored here (use {@link findMissingDependencies} for those).
3167
+ *
3168
+ * @throws {DependencyCycleError} if the graph cannot be linearised.
3169
+ */
3170
+ declare function resolveInstallOrder(nodes: readonly DependencyNode[]): string[];
3171
+
3172
+ /**
3173
+ * @xnetjs/plugins — supply-chain provenance verification (exploration 0192).
3174
+ *
3175
+ * The 2025–26 marketplace-malware wave (GlassWorm, OctoRAT, typosquats) taught
3176
+ * the field one thing: "Verified Publisher" badges verify domain ownership, not
3177
+ * code safety. The answer the JS ecosystem converged on is Sigstore-style
3178
+ * **provenance** — a keyless attestation that a package was built from a known
3179
+ * source by a known builder, logged in a transparency ledger (Rekor).
3180
+ *
3181
+ * This module defines the verification *contract* and a **fail-closed** default.
3182
+ * The actual Sigstore bundle verification (cosign/rekor) is a pluggable
3183
+ * `ProvenanceVerifier` — wired in where the crypto deps are acceptable
3184
+ * (publishing CI / desktop). The substrate is safe by default: with no verifier,
3185
+ * everything is reported `unverified`, and the install UI decides what to do
3186
+ * with that (warn / block) — it never silently treats unsigned code as trusted.
3187
+ */
3188
+ /** A provenance attestation attached to a marketplace package. */
3189
+ interface Provenance {
3190
+ /** The Sigstore bundle (or a URL to it). */
3191
+ sigstoreBundle?: string;
3192
+ /** Rekor transparency-log index, if logged. */
3193
+ rekorLogIndex?: number;
3194
+ /** DID/identity of the builder (CI workflow identity). */
3195
+ builderDID?: string;
3196
+ /** Source repository the artifact was built from. */
3197
+ sourceRepo?: string;
3198
+ /** Source commit SHA. */
3199
+ sourceCommit?: string;
3200
+ /** SHA-256 digest of the manifest/artifact being attested. */
3201
+ artifactDigest?: string;
3202
+ }
3203
+ /** The outcome of verifying provenance for an artifact. */
3204
+ interface ProvenanceResult {
3205
+ /** True only when a verifier cryptographically confirmed the attestation. */
3206
+ verified: boolean;
3207
+ /** Why verification failed or what could not be confirmed. */
3208
+ reason?: string;
3209
+ /** Source repo, when confirmed. */
3210
+ sourceRepo?: string;
3211
+ /** Builder identity, when confirmed. */
3212
+ builderDID?: string;
3213
+ }
3214
+ /** The input to a verification: the attestation plus the artifact it covers. */
3215
+ interface VerifyProvenanceInput {
3216
+ provenance?: Provenance;
3217
+ /** SHA-256 digest computed locally over the fetched manifest/artifact. */
3218
+ artifactDigest: string;
3219
+ }
3220
+ /** A pluggable verifier (cosign/rekor under the hood). */
3221
+ interface ProvenanceVerifier {
3222
+ verify(input: VerifyProvenanceInput): Promise<ProvenanceResult>;
3223
+ }
3224
+ /**
3225
+ * The default verifier: **fails closed**. Absent a real cryptographic verifier,
3226
+ * nothing is "verified" — unsigned/unattested packages are reported as such so
3227
+ * the UI surfaces an explicit "unverified build" state rather than a false green.
3228
+ */
3229
+ declare const failClosedVerifier: ProvenanceVerifier;
3230
+ /**
3231
+ * Verify provenance, defaulting to the fail-closed verifier. A convenience
3232
+ * wrapper so callers always get a `ProvenanceResult` (never an exception) and
3233
+ * unverified is the safe default.
3234
+ */
3235
+ declare function verifyProvenance(input: VerifyProvenanceInput, verifier?: ProvenanceVerifier): Promise<ProvenanceResult>;
3236
+ /** A one-line human summary of a provenance result, for the consent dialog. */
3237
+ declare function summarizeProvenance(result: ProvenanceResult): string;
3238
+
3239
+ /**
3240
+ * @xnetjs/plugins — plugin authoring test kit (exploration 0192).
3241
+ *
3242
+ * A zero-setup harness so plugin authors can unit-test a manifest without a real
3243
+ * NodeStore or the app: spin up an in-memory store, install the plugin into a
3244
+ * real `PluginRegistry`, and assert on the contributions it registered. This is
3245
+ * the "@xnetjs/plugin-testing" surface, kept in-package to avoid a new workspace
3246
+ * dependency.
3247
+ */
3248
+
3249
+ interface MemNode {
3250
+ id: string;
3251
+ schemaId: string;
3252
+ properties: Record<string, unknown>;
3253
+ deleted?: boolean;
3254
+ }
3255
+ type Listener = (event: unknown) => void;
3256
+ /** A minimal in-memory NodeStore good enough for plugin lifecycle tests. */
3257
+ interface TestNodeStore {
3258
+ create(options: {
3259
+ schemaId: string;
3260
+ properties?: Record<string, unknown>;
3261
+ }): Promise<MemNode>;
3262
+ get(id: string): Promise<MemNode | null>;
3263
+ update(id: string, options: {
3264
+ properties?: Record<string, unknown>;
3265
+ }): Promise<MemNode>;
3266
+ delete(id: string): Promise<void>;
3267
+ list(options?: {
3268
+ schemaId?: string;
3269
+ }): Promise<MemNode[]>;
3270
+ subscribe(listener: Listener): () => void;
3271
+ /** Test helper: how many live nodes exist (optionally for one schema). */
3272
+ count(schemaId?: string): number;
3273
+ }
3274
+ declare function createTestNodeStore(initial?: MemNode[]): TestNodeStore;
3275
+ interface TestPluginHarness {
3276
+ registry: PluginRegistry;
3277
+ store: TestNodeStore;
3278
+ /** Install a plugin and return its registered record. */
3279
+ install(manifest: XNetExtension): Promise<void>;
3280
+ }
3281
+ interface TestHarnessOptions {
3282
+ platform?: Platform;
3283
+ initialNodes?: MemNode[];
3284
+ }
3285
+ /**
3286
+ * Build a ready-to-use plugin test harness: an in-memory store wired into a real
3287
+ * `PluginRegistry`. Use it to install a plugin and assert on
3288
+ * `registry.getContributions()` or `registry.get(id)?.status`.
3289
+ *
3290
+ * @example
3291
+ * const h = createTestPluginHarness()
3292
+ * await h.install(MyPlugin)
3293
+ * expect(h.registry.get('com.me.plugin')?.status).toBe('active')
3294
+ * expect(h.registry.getContributions().slashCommands.getAll()).toHaveLength(1)
3295
+ */
3296
+ declare function createTestPluginHarness(options?: TestHarnessOptions): TestPluginHarness;
3297
+
3298
+ /**
3299
+ * @xnetjs/plugins — plugin project scaffolder (exploration 0192).
3300
+ *
3301
+ * The pure core behind `create-xnet-plugin`: given a small spec, produce the
3302
+ * full set of project files (manifest, tests, package.json, README) as a
3303
+ * path→content map. Keeping it pure means it's unit-testable without touching
3304
+ * disk; a thin CLI writes the map out.
3305
+ *
3306
+ * Templates mirror the authoring tracks in the explorations: `client`
3307
+ * (contributions only), `two-sided` (client + a hub feature + declared
3308
+ * capabilities), `ai-script` (a script-backed slash command), and `connector`
3309
+ * (0196 — sync an external service into governed nodes + expose agent tools).
3310
+ */
3311
+
3312
+ type ScaffoldTemplate = 'client' | 'two-sided' | 'ai-script' | 'connector';
3313
+ interface ScaffoldSpec {
3314
+ /** Reverse-domain plugin id, e.g. `com.acme.kanban`. */
3315
+ id: string;
3316
+ /** Human-readable name. */
3317
+ name: string;
3318
+ /** Which starter template to generate. */
3319
+ template: ScaffoldTemplate;
3320
+ author?: string;
3321
+ description?: string;
3322
+ /** Declared capability grant (two-sided templates surface this in the manifest). */
3323
+ capabilities?: ModuleCapabilities;
3324
+ /** SPDX license id (exploration 0196). Defaults to FSL-1.1-MIT. */
3325
+ license?: string;
3326
+ /** Monetization (exploration 0196). When paid, the manifest declares `pricing`. */
3327
+ pricing?: PluginPricing;
3328
+ /** Publisher DID for paid plugins (exploration 0196). */
3329
+ publisherDid?: string;
3330
+ /** Copyright year for the generated LICENSE (defaults supplied by the caller). */
3331
+ year?: number;
3332
+ }
3333
+ interface ScaffoldResult {
3334
+ /** Project files keyed by relative path. */
3335
+ files: Record<string, string>;
3336
+ }
3337
+ declare class ScaffoldError extends Error {
3338
+ constructor(message: string);
3339
+ }
3340
+ /** `com.acme.kanban-board` → `KanbanBoard` (a safe JS identifier). */
3341
+ declare function pascalCase(id: string): string;
3342
+ /** `com.acme.kanban` → `acme-kanban` (an npm-safe package name). */
3343
+ declare function packageName(id: string): string;
3344
+ /**
3345
+ * Scaffold a plugin project as a path→content map. Pure: write the result to
3346
+ * disk with a thin CLI, or assert on it in tests.
3347
+ *
3348
+ * @throws {ScaffoldError} if the spec is invalid.
3349
+ */
3350
+ declare function scaffoldPlugin(spec: ScaffoldSpec): ScaffoldResult;
3351
+
3352
+ /**
3353
+ * @xnetjs/plugins — paid-plugin license policy (exploration 0196).
3354
+ *
3355
+ * The marketplace pre-approves a small, fixed set of licenses so a paid plugin
3356
+ * needs no per-listing legal review. The default is **FSL-1.1-MIT** — source-
3357
+ * available, forbids only a competing marketplace, and auto-converts to MIT two
3358
+ * years after each version ships (mirrors `@xnetjs/cloud`'s FSL). Plain OSI
3359
+ * licenses are allowed too. This module is the single source of truth for the
3360
+ * allowed set and for generating the `LICENSE` file the scaffolder emits;
3361
+ * `scripts/check-plugin-licenses.sh` enforces the same set in CI.
3362
+ */
3363
+ /** SPDX ids a paid plugin may declare. */
3364
+ declare const ALLOWED_PLUGIN_LICENSES: readonly ["FSL-1.1-MIT", "FSL-1.1-Apache-2.0", "MIT", "Apache-2.0", "AGPL-3.0-only"];
3365
+ type AllowedPluginLicense = (typeof ALLOWED_PLUGIN_LICENSES)[number];
3366
+ /** The default license suggested by the scaffolder for a new plugin. */
3367
+ declare const DEFAULT_PLUGIN_LICENSE: AllowedPluginLicense;
3368
+ /** True if `spdx` is one of the marketplace-approved licenses. */
3369
+ declare function isAllowedPluginLicense(spdx: string): spdx is AllowedPluginLicense;
3370
+ /**
3371
+ * Generate the `LICENSE` file body for a plugin, or `null` for an unrecognized
3372
+ * license (the scaffolder then omits the file and the author supplies their own).
3373
+ */
3374
+ declare function pluginLicenseText(spdx: string, year: number, holder: string): string | null;
3375
+
3376
+ /**
3377
+ * @xnetjs/plugins — AI-authored plugin transform (exploration 0192).
3378
+ *
3379
+ * The hard part of AI authoring already ships (`@xnetjs/plugins/ai`:
3380
+ * NL → AST-validated script + provider routing). The missing headless step is
3381
+ * turning a generated script into an *installable* plugin: this wraps a
3382
+ * validated `GeneratedScript` into a `FeatureModule` whose command runs the
3383
+ * script, stamped with `ai-generated` provenance so the install path sandboxes
3384
+ * it at the `user` tier and still routes its capability requests through consent.
3385
+ *
3386
+ * It deliberately **refuses unvalidated code** — "the AI made it" never bypasses
3387
+ * the safety gate.
3388
+ */
3389
+
3390
+ /** The subset of `@xnetjs/plugins/ai`'s `AIScriptResponse` this transform needs. */
3391
+ interface GeneratedScript {
3392
+ code: string;
3393
+ suggestedName: string;
3394
+ validated: boolean;
3395
+ explanation?: string;
3396
+ }
3397
+ /** Runs the generated script body (injected so this stays decoupled from the sandbox). */
3398
+ type ScriptExecutor = (code: string) => void | Promise<void>;
3399
+ interface ScriptToManifestInput {
3400
+ /** Reverse-domain id for the new plugin. */
3401
+ id: string;
3402
+ /** The validated generated script. */
3403
+ script: GeneratedScript;
3404
+ author?: string;
3405
+ /** Capabilities the script needs (default: none — Layer-1 scripts are sandboxed). */
3406
+ capabilities?: ModuleCapabilities;
3407
+ /** How to run the script when the command fires (default: throws until wired). */
3408
+ run?: ScriptExecutor;
3409
+ }
3410
+ interface AiAuthoredPlugin {
3411
+ manifest: FeatureModule;
3412
+ /** Always `ai-generated` — the install path derives the `user` trust tier from it. */
3413
+ provenance: InstallProvenance;
3414
+ /** The raw script body, for persistence/audit. */
3415
+ code: string;
3416
+ }
3417
+ declare class AiAuthoringError extends Error {
3418
+ constructor(message: string);
3419
+ }
3420
+ /**
3421
+ * Wrap a validated generated script into an installable, `ai-generated` plugin.
3422
+ *
3423
+ * @throws {AiAuthoringError} if the id is malformed or the script is unvalidated.
3424
+ */
3425
+ declare function scriptToPluginManifest(input: ScriptToManifestInput): AiAuthoredPlugin;
3426
+
3427
+ /**
3428
+ * @xnetjs/plugins — run plugin code on the labs runtime ladder (0194 Phase 1).
3429
+ *
3430
+ * The unification the exploration calls for: instead of plugins maintaining their
3431
+ * own sandbox, user/marketplace-tier plugin code runs on the *same* runtime
3432
+ * ladder `@xnetjs/labs` uses (SES/QuickJS for `sandbox`, an iframe for `app`).
3433
+ * One sandbox, one security audit — and plugins gain the ladder's Python/server
3434
+ * tiers for free.
3435
+ *
3436
+ * The ladder is taken as a **structural port** (`PluginRuntimeLadder`), not an
3437
+ * `@xnetjs/labs` import: labs already depends on `@xnetjs/plugins`, so a direct
3438
+ * edge here would cycle. The host (web/electron) passes its concrete labs ladder.
3439
+ *
3440
+ * First-party code is trusted and runs in the host realm, NOT through the ladder
3441
+ * — `runPluginCode` rejects a first-party tier so a caller can't accidentally
3442
+ * sandbox (and slow) trusted code.
3443
+ */
3444
+
3445
+ /** The labs runtime tiers a plugin can target. */
3446
+ type LadderRuntimeTier = 'sandbox' | 'app' | 'server';
3447
+ /** A single run on the ladder. */
3448
+ interface PluginRunInput {
3449
+ language: 'javascript' | 'typescript';
3450
+ tier: LadderRuntimeTier;
3451
+ code: string;
3452
+ /** Host bridge the sandbox may call (capability-gated by the host). */
3453
+ host?: unknown;
3454
+ }
3455
+ interface PluginRunResult {
3456
+ ok: boolean;
3457
+ value?: unknown;
3458
+ logs?: string[];
3459
+ error?: string;
3460
+ }
3461
+ /** The minimal slice of the labs `RuntimeLadder` this adapter needs. */
3462
+ interface PluginRuntimeLadder {
3463
+ run(input: PluginRunInput): Promise<PluginRunResult>;
3464
+ }
3465
+ declare class PluginRuntimeError extends Error {
3466
+ constructor(message: string);
3467
+ }
3468
+ /**
3469
+ * Map a plugin's trust tier to the ladder rung its code should run on. `user`
3470
+ * code runs in the deterministic `sandbox` (SES/QuickJS); `marketplace` code in
3471
+ * the `app` (iframe) rung. `first-party` has no ladder rung — it runs in the
3472
+ * host realm — so this throws for it.
3473
+ */
3474
+ declare function ladderTierForTrust(tier: TrustTier): LadderRuntimeTier;
3475
+ interface RunPluginCodeInput {
3476
+ code: string;
3477
+ trustTier: TrustTier;
3478
+ language?: 'javascript' | 'typescript';
3479
+ host?: unknown;
3480
+ }
3481
+ /**
3482
+ * Run user/marketplace-tier plugin code on the labs ladder, choosing the rung by
3483
+ * trust tier. Throws `PluginRuntimeError` for first-party (which belongs in the
3484
+ * host realm). Returns the ladder's result unchanged.
3485
+ */
3486
+ declare function runPluginCode(ladder: PluginRuntimeLadder, input: RunPluginCodeInput): Promise<PluginRunResult>;
3487
+
3488
+ /**
3489
+ * @xnetjs/plugins — the AI→Lab→Plugin assembly line (exploration 0194 Phase 2).
3490
+ *
3491
+ * Closes the authoring loop: the AI *generates* a script, the script *runs* in a
3492
+ * Lab so the AI (and the user) see real output, and only after a human approves
3493
+ * is it *published* as a plugin. Each hop is an injected port, so this is pure
3494
+ * orchestration — testable without the AI provider, the labs ladder, or the
3495
+ * registry, and free of the `plugins → labs` dependency edge.
3496
+ *
3497
+ * It never auto-publishes: publishing is always gated on `consent`. The script
3498
+ * must be validated before it becomes a plugin (`scriptToPluginManifest` refuses
3499
+ * unvalidated code), so "the AI made it" never bypasses the gate.
3500
+ */
3501
+
3502
+ /** The result of running a generated script in a Lab. */
3503
+ interface LabRunOutcome {
3504
+ ok: boolean;
3505
+ output?: string;
3506
+ error?: string;
3507
+ }
3508
+ /** The injected capabilities the pipeline orchestrates. */
3509
+ interface AiPluginPipelinePorts {
3510
+ /** Generate a (validated) script from natural-language intent. */
3511
+ generate: (intent: string) => Promise<GeneratedScript>;
3512
+ /** Run the script in a Lab and report the real result. */
3513
+ runLab: (code: string) => Promise<LabRunOutcome>;
3514
+ /** Ask the human to approve publishing, given the plugin + its run output. */
3515
+ consent: (plugin: AiAuthoredPlugin, run: LabRunOutcome) => Promise<boolean>;
3516
+ /** Publish the approved plugin (e.g. `publishLabAsExtension` / `registry.install`). */
3517
+ publish: (plugin: AiAuthoredPlugin) => Promise<void>;
3518
+ }
3519
+ interface AiPluginPipelineInput {
3520
+ /** Natural-language description of the plugin to build. */
3521
+ intent: string;
3522
+ /** Reverse-domain id for the new plugin. */
3523
+ id: string;
3524
+ author?: string;
3525
+ }
3526
+ type AiPluginPipelineResult = {
3527
+ status: 'generation-invalid';
3528
+ reason: string;
3529
+ } | {
3530
+ status: 'run-failed';
3531
+ plugin: AiAuthoredPlugin;
3532
+ run: LabRunOutcome;
3533
+ } | {
3534
+ status: 'declined';
3535
+ plugin: AiAuthoredPlugin;
3536
+ run: LabRunOutcome;
3537
+ } | {
3538
+ status: 'published';
3539
+ plugin: AiAuthoredPlugin;
3540
+ run: LabRunOutcome;
3541
+ };
3542
+ /**
3543
+ * Run the generate → lab-test → consent → publish pipeline. Returns a tagged
3544
+ * result at whichever stage it stops; only `published` means the plugin landed.
3545
+ */
3546
+ declare function runAiPluginPipeline(input: AiPluginPipelineInput, ports: AiPluginPipelinePorts): Promise<AiPluginPipelineResult>;
3547
+
652
3548
  /**
653
3549
  * Plugin schema - stores plugin metadata as Nodes for P2P sync
654
3550
  */
@@ -1187,6 +4083,65 @@ declare class ScriptRunner {
1187
4083
  private handleScriptChange;
1188
4084
  }
1189
4085
 
4086
+ /**
4087
+ * Canvas Plugin Sandbox
4088
+ *
4089
+ * Policy helpers for plugin-owned canvas renderers and preview generators.
4090
+ */
4091
+
4092
+ type CanvasPluginSandboxKind = 'renderer' | 'preview';
4093
+ type CanvasPluginSandboxDomAccess = 'none' | 'isolated-iframe';
4094
+ type CanvasPluginSandboxNetworkAccess = 'none' | 'workspace-approved';
4095
+ type CanvasPluginSandboxMutationAccess = 'none';
4096
+ type CanvasPluginSandboxOutputKind = 'view-model' | 'html-fragment' | 'summary' | 'thumbnail' | 'template-draft';
4097
+ type CanvasPluginSandboxPolicy = {
4098
+ kind: CanvasPluginSandboxKind;
4099
+ timeoutMs: number;
4100
+ maxOutputBytes: number;
4101
+ domAccess: CanvasPluginSandboxDomAccess;
4102
+ networkAccess: CanvasPluginSandboxNetworkAccess;
4103
+ mutationAccess: CanvasPluginSandboxMutationAccess;
4104
+ allowedOutputKinds: CanvasPluginSandboxOutputKind[];
4105
+ blockedGlobals: string[];
4106
+ };
4107
+ type CanvasPluginSandboxRequest = {
4108
+ pluginId: string;
4109
+ contributionId: string;
4110
+ kind: CanvasPluginSandboxKind;
4111
+ entrypoint: string;
4112
+ permissions?: CanvasContributionPermission[];
4113
+ requestedNetworkDomains?: string[];
4114
+ };
4115
+ type CanvasPluginSandboxDecision = {
4116
+ allowed: boolean;
4117
+ policy: CanvasPluginSandboxPolicy;
4118
+ issues: string[];
4119
+ };
4120
+ type CanvasPluginSandboxOutput = {
4121
+ kind: CanvasPluginSandboxOutputKind;
4122
+ payload?: unknown;
4123
+ html?: string;
4124
+ bytes?: number;
4125
+ };
4126
+ type CanvasPluginSandboxOutputValidation = {
4127
+ valid: boolean;
4128
+ issues: string[];
4129
+ };
4130
+ type CanvasRendererSandboxContribution = CanvasCardContribution | CanvasInspectorContribution | CanvasTemplateContribution;
4131
+ declare function createCanvasPluginSandboxPolicy(kind: CanvasPluginSandboxKind, permissions?: CanvasContributionPermission[]): CanvasPluginSandboxPolicy;
4132
+ declare function evaluateCanvasPluginSandboxRequest(request: CanvasPluginSandboxRequest): CanvasPluginSandboxDecision;
4133
+ declare function createCanvasRendererSandboxRequest(input: {
4134
+ pluginId: string;
4135
+ contribution: CanvasRendererSandboxContribution;
4136
+ entrypoint?: string;
4137
+ }): CanvasPluginSandboxRequest;
4138
+ declare function createCanvasPreviewSandboxRequest(input: {
4139
+ pluginId: string;
4140
+ contribution: CanvasCardContribution | CanvasTemplateContribution;
4141
+ entrypoint?: string;
4142
+ }): CanvasPluginSandboxRequest;
4143
+ declare function validateCanvasPluginSandboxOutput(output: CanvasPluginSandboxOutput, policy: CanvasPluginSandboxPolicy): CanvasPluginSandboxOutputValidation;
4144
+
1190
4145
  /**
1191
4146
  * AI Prompt Builder for Script Generation
1192
4147
  *
@@ -1264,10 +4219,100 @@ declare function buildRetryPrompt(originalPrompt: string, errors: string[]): str
1264
4219
  * AI Provider Abstraction
1265
4220
  *
1266
4221
  * Defines a common interface for AI providers and includes
1267
- * implementations for Anthropic, OpenAI, and local (Ollama).
4222
+ * implementations for Anthropic, OpenAI-compatible endpoints, OpenAI,
4223
+ * and local (Ollama).
1268
4224
  */
4225
+ type AIMessageRole = 'system' | 'user' | 'assistant' | 'tool';
4226
+ type AIMessage = {
4227
+ role: AIMessageRole;
4228
+ content: string;
4229
+ name?: string;
4230
+ toolCallId?: string;
4231
+ };
4232
+ type AIToolSpec = {
4233
+ name: string;
4234
+ description?: string;
4235
+ inputSchema?: Record<string, unknown>;
4236
+ };
4237
+ type AIPrivacyLevel = 'local' | 'cloud' | 'proxy';
4238
+ type AIModelQuality = 'local' | 'balanced' | 'strong';
4239
+ type AICostModel = {
4240
+ inputPerMillion: number;
4241
+ outputPerMillion: number;
4242
+ currency: 'USD';
4243
+ };
4244
+ type AIModelCapabilities = {
4245
+ tools: boolean;
4246
+ structuredOutputs: boolean;
4247
+ streaming: boolean;
4248
+ contextWindow: number;
4249
+ local: boolean;
4250
+ privacy: AIPrivacyLevel;
4251
+ quality: AIModelQuality;
4252
+ cost?: AICostModel;
4253
+ };
4254
+ type AIRiskLevel = 'low' | 'medium' | 'high' | 'critical';
4255
+ type AIComplexityLevel = 'low' | 'medium' | 'high';
4256
+ type AIGenerateRequest = {
4257
+ prompt?: string;
4258
+ messages?: AIMessage[];
4259
+ tools?: AIToolSpec[];
4260
+ responseSchema?: Record<string, unknown>;
4261
+ stream?: boolean;
4262
+ risk?: AIRiskLevel;
4263
+ complexity?: AIComplexityLevel;
4264
+ maxTokens?: number;
4265
+ temperature?: number;
4266
+ preferredProvider?: string;
4267
+ metadata?: Record<string, unknown>;
4268
+ };
4269
+ type AIToolCall = {
4270
+ id: string;
4271
+ name: string;
4272
+ arguments: Record<string, unknown>;
4273
+ };
4274
+ type AIUsage = {
4275
+ inputTokens?: number;
4276
+ outputTokens?: number;
4277
+ totalTokens?: number;
4278
+ estimatedCostUsd?: number;
4279
+ };
4280
+ type AIGenerateResponse = {
4281
+ text: string;
4282
+ provider: string;
4283
+ model: string;
4284
+ toolCalls?: AIToolCall[];
4285
+ usage?: AIUsage;
4286
+ };
4287
+ type AIStreamChunk = {
4288
+ type: 'text';
4289
+ text: string;
4290
+ provider: string;
4291
+ model: string;
4292
+ } | {
4293
+ type: 'tool_call';
4294
+ toolCall: AIToolCall;
4295
+ provider: string;
4296
+ model: string;
4297
+ } | {
4298
+ type: 'usage';
4299
+ usage: AIUsage;
4300
+ provider: string;
4301
+ model: string;
4302
+ } | {
4303
+ type: 'done';
4304
+ provider: string;
4305
+ model: string;
4306
+ };
4307
+ type AIProviderUsage = {
4308
+ requests: number;
4309
+ inputTokens: number;
4310
+ outputTokens: number;
4311
+ totalTokens: number;
4312
+ estimatedCostUsd: number;
4313
+ };
1269
4314
  /**
1270
- * Common interface for AI text generation providers
4315
+ * Common interface for AI text generation providers.
1271
4316
  */
1272
4317
  interface AIProvider {
1273
4318
  /** Provider name for display/logging */
@@ -1280,9 +4325,21 @@ interface AIProvider {
1280
4325
  * @throws Error if generation fails
1281
4326
  */
1282
4327
  generate(prompt: string): Promise<string>;
4328
+ /**
4329
+ * Generate a response with optional tool and structured-output metadata.
4330
+ */
4331
+ generateWithTools?(request: AIGenerateRequest): Promise<AIGenerateResponse>;
4332
+ /**
4333
+ * Stream model output and tool events.
4334
+ */
4335
+ stream?(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
4336
+ /**
4337
+ * Describe model/provider capabilities for routing.
4338
+ */
4339
+ getCapabilities?(): AIModelCapabilities;
1283
4340
  }
1284
4341
  /**
1285
- * Options for configuring AI providers
4342
+ * Options for configuring AI providers.
1286
4343
  */
1287
4344
  interface AIProviderOptions {
1288
4345
  /** API key (for cloud providers) */
@@ -1295,20 +4352,40 @@ interface AIProviderOptions {
1295
4352
  maxTokens?: number;
1296
4353
  /** Temperature (0-1, higher = more creative) */
1297
4354
  temperature?: number;
4355
+ /** Display name for OpenAI-compatible providers */
4356
+ providerName?: string;
4357
+ /** Additional HTTP headers for compatible proxies */
4358
+ defaultHeaders?: Record<string, string>;
4359
+ /** Whether an API key is required */
4360
+ apiKeyRequired?: boolean;
4361
+ /** Capability overrides for routing */
4362
+ capabilities?: Partial<AIModelCapabilities>;
4363
+ /**
4364
+ * Send the Anthropic browser-CORS header
4365
+ * (`anthropic-dangerous-direct-browser-access`) so a bring-your-own-key call
4366
+ * succeeds from browser JavaScript. Only `AnthropicProvider` reads this.
4367
+ * Defaults to `true` when a DOM is present (`typeof window !== 'undefined'`).
4368
+ */
4369
+ allowBrowser?: boolean;
1298
4370
  }
4371
+ type OpenAICompatibleProviderOptions = AIProviderOptions;
1299
4372
  /**
1300
- * AI provider type identifier
4373
+ * AI provider type identifier.
1301
4374
  */
1302
- type AIProviderType = 'anthropic' | 'openai' | 'ollama' | 'custom';
4375
+ type AIProviderType = 'anthropic' | 'openai' | 'ollama' | 'openai-compatible' | 'openrouter' | 'ollama-openai' | 'lmstudio' | 'vllm' | 'litellm' | 'managed' | 'custom';
1303
4376
  /**
1304
- * Configuration for selecting an AI provider
4377
+ * Configuration for selecting an AI provider.
1305
4378
  */
1306
4379
  interface AIProviderConfig {
1307
4380
  type: AIProviderType;
1308
4381
  options: AIProviderOptions;
1309
4382
  }
4383
+ type AIProviderRouterOptions = {
4384
+ preferLocalRiskLevels?: AIRiskLevel[];
4385
+ strongModelRiskLevels?: AIRiskLevel[];
4386
+ };
1310
4387
  /**
1311
- * Error thrown when AI generation fails
4388
+ * Error thrown when AI generation fails.
1312
4389
  */
1313
4390
  declare class AIGenerationError extends Error {
1314
4391
  readonly provider: string;
@@ -1331,8 +4408,34 @@ declare class AnthropicProvider implements AIProvider {
1331
4408
  private model;
1332
4409
  private maxTokens;
1333
4410
  private temperature;
4411
+ private allowBrowser;
1334
4412
  constructor(options: AIProviderOptions);
4413
+ getCapabilities(): AIModelCapabilities;
4414
+ generate(prompt: string): Promise<string>;
4415
+ }
4416
+ /**
4417
+ * Tool-capable adapter for OpenAI-compatible chat completion endpoints.
4418
+ *
4419
+ * Works with OpenAI, OpenRouter, Ollama's `/v1` compatibility API,
4420
+ * LM Studio, vLLM, and LiteLLM.
4421
+ */
4422
+ declare class OpenAICompatibleProvider implements AIProvider {
4423
+ readonly name: string;
4424
+ private apiKey?;
4425
+ private baseUrl;
4426
+ private model;
4427
+ private maxTokens;
4428
+ private temperature;
4429
+ private defaultHeaders;
4430
+ private capabilities;
4431
+ constructor(options?: OpenAICompatibleProviderOptions);
4432
+ getCapabilities(): AIModelCapabilities;
1335
4433
  generate(prompt: string): Promise<string>;
4434
+ generateWithTools(request: AIGenerateRequest): Promise<AIGenerateResponse>;
4435
+ stream(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
4436
+ private createHeaders;
4437
+ private createPayload;
4438
+ private parseToolCalls;
1336
4439
  }
1337
4440
  /**
1338
4441
  * AI provider for OpenAI's GPT models.
@@ -1343,15 +4446,8 @@ declare class AnthropicProvider implements AIProvider {
1343
4446
  * const response = await provider.generate('Hello!')
1344
4447
  * ```
1345
4448
  */
1346
- declare class OpenAIProvider implements AIProvider {
1347
- readonly name = "OpenAI";
1348
- private apiKey;
1349
- private baseUrl;
1350
- private model;
1351
- private maxTokens;
1352
- private temperature;
4449
+ declare class OpenAIProvider extends OpenAICompatibleProvider {
1353
4450
  constructor(options: AIProviderOptions);
1354
- generate(prompt: string): Promise<string>;
1355
4451
  }
1356
4452
  /**
1357
4453
  * AI provider for local Ollama instance.
@@ -1368,8 +4464,85 @@ declare class OllamaProvider implements AIProvider {
1368
4464
  private model;
1369
4465
  private temperature;
1370
4466
  constructor(options?: AIProviderOptions);
4467
+ getCapabilities(): AIModelCapabilities;
4468
+ generate(prompt: string): Promise<string>;
4469
+ }
4470
+ declare class AIProviderRouter implements AIProvider {
4471
+ readonly name = "AIProviderRouter";
4472
+ private providers;
4473
+ private options;
4474
+ private usageByProvider;
4475
+ constructor(providers: AIProvider[], options?: AIProviderRouterOptions);
4476
+ getCapabilities(): AIModelCapabilities;
4477
+ getUsage(): Record<string, AIProviderUsage>;
4478
+ selectProvider(request: AIGenerateRequest): AIProvider;
4479
+ generate(prompt: string): Promise<string>;
4480
+ generateWithTools(request: AIGenerateRequest): Promise<AIGenerateResponse>;
4481
+ stream(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
4482
+ private canHandle;
4483
+ private shouldPreferLocal;
4484
+ private shouldPreferStrong;
4485
+ private getProviderCapabilities;
4486
+ private recordUsage;
4487
+ }
4488
+ /** The live budget snapshot a managed call reports back (drives the panel gauge). */
4489
+ interface ManagedBudgetSnapshot {
4490
+ /** Marked-up spend accrued this billing period, in USD. */
4491
+ spendThisPeriodUsd: number;
4492
+ /** Free included allotment for the period, in USD. */
4493
+ includedUsd: number;
4494
+ /** Hard monthly cap, in USD — requests stop here. */
4495
+ budgetUsd: number;
4496
+ /** Coarse state driving the gauge colour. */
4497
+ budgetState: 'included' | 'overage' | 'near-cap' | 'over-cap';
4498
+ }
4499
+ /** Thrown when managed AI returns `402` — the surprise-bill hard stop. */
4500
+ declare class AiBudgetError extends Error {
4501
+ readonly spentUsd: number;
4502
+ readonly budgetUsd: number;
4503
+ constructor(spentUsd: number, budgetUsd: number);
4504
+ }
4505
+ type ManagedProviderOptions = AIProviderOptions & {
4506
+ /** Injected fetch for tests; defaults to the global `fetch`. */
4507
+ fetchImpl?: typeof fetch;
4508
+ /** Receives the live budget after each managed call (drives the panel gauge). */
4509
+ onBudget?: (snapshot: ManagedBudgetSnapshot) => void;
4510
+ };
4511
+ /**
4512
+ * XNet Cloud **managed** AI provider (exploration 0208).
4513
+ *
4514
+ * Posts to the hub's `/ai/chat`, which forwards to the metered control-plane
4515
+ * gateway (OpenRouter behind a per-tenant budgeted key). Unlike every other cloud
4516
+ * provider it carries **no API key** — the hub injects the per-tenant credential
4517
+ * server-side — so it is the only cloud tier safe to use without the user pasting
4518
+ * a secret. It surfaces the live budget (so the panel can render "used / included
4519
+ * / cap") and turns a `402` into a typed {@link AiBudgetError} the UI can act on.
4520
+ *
4521
+ * Model switching is per-instance: the configured `model` is sent on every call,
4522
+ * so changing the picker rebuilds the provider (same pattern as the BYO tiers).
4523
+ * No `stream` method, so the runtime uses the request/response path.
4524
+ */
4525
+ declare class ManagedProvider implements AIProvider {
4526
+ readonly name = "XNet Cloud";
4527
+ private readonly baseUrl;
4528
+ private readonly model?;
4529
+ private readonly fetchImpl;
4530
+ private readonly onBudget?;
4531
+ constructor(options?: ManagedProviderOptions);
4532
+ getCapabilities(): AIModelCapabilities;
1371
4533
  generate(prompt: string): Promise<string>;
4534
+ generateWithTools(request: AIGenerateRequest): Promise<AIGenerateResponse>;
4535
+ /**
4536
+ * Stream the managed completion over SSE (`/ai/chat/stream`). Yields a `text`
4537
+ * chunk per delta, then `usage` + `done`; emits the live budget from the terminal
4538
+ * `done` event. A `402` (pre-stream) or an `ai_budget_exceeded` error event
4539
+ * becomes an {@link AiBudgetError} (exploration 0244).
4540
+ */
4541
+ stream(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
4542
+ private emitBudget;
1372
4543
  }
4544
+ /** Build a {@link ManagedProvider} (parallels `createPromptApiProvider`). */
4545
+ declare const createManagedProvider: (options?: ManagedProviderOptions) => ManagedProvider;
1373
4546
  /**
1374
4547
  * Create an AI provider from configuration.
1375
4548
  *
@@ -1385,6 +4558,7 @@ declare class OllamaProvider implements AIProvider {
1385
4558
  * ```
1386
4559
  */
1387
4560
  declare function createAIProvider(config: AIProviderConfig): AIProvider;
4561
+ declare function createAIProviderRouter(providers: AIProvider[], options?: AIProviderRouterOptions): AIProviderRouter;
1388
4562
  /**
1389
4563
  * Check if Ollama is available locally.
1390
4564
  *
@@ -1498,6 +4672,556 @@ declare class ScriptGenerator {
1498
4672
  */
1499
4673
  declare function generateScript(provider: AIProvider, request: AIScriptRequest): Promise<AIScriptResponse>;
1500
4674
 
4675
+ /**
4676
+ * In-app AI agent runtime for persistent threads, approvals, events, and telemetry.
4677
+ */
4678
+
4679
+ type AiAgentOrchestratorMode = 'custom' | 'codex-app-server' | 'hybrid';
4680
+ /**
4681
+ * How the assistant relates to the user's work (xNet Humane Internet Charter
4682
+ * §Agency). `scaffold` — the default — keeps the human as the author: the model
4683
+ * proposes and cites, the user writes and owns. It is a guard against the
4684
+ * cognitive-debt / deskilling effect of LLM-authored work (MIT Media Lab,
4685
+ * arXiv:2506.08872). `draft`, where the model produces finished prose, must be
4686
+ * opted into explicitly. Either way the resulting turn is tagged `ai-generated`.
4687
+ */
4688
+ type AiAssistMode = 'scaffold' | 'draft';
4689
+ type AiAgentThreadStatus = 'idle' | 'running' | 'waiting-approval' | 'cancelled' | 'failed';
4690
+ type AiAgentTurnRole = 'system' | 'user' | 'assistant' | 'tool';
4691
+ type AiAgentTurnStatus = 'pending' | 'streaming' | 'completed' | 'cancelled' | 'failed';
4692
+ type AiAgentApprovalStatus = 'pending' | 'approved' | 'rejected' | 'revision-requested';
4693
+ type AiAgentBackgroundJobStatus = 'queued' | 'running' | 'completed' | 'failed' | 'cancelled';
4694
+ type AiAgentEventType = 'thread.created' | 'turn.created' | 'model.delta' | 'model.completed' | 'tool.call' | 'usage' | 'approval.requested' | 'approval.resolved' | 'run.cancelled' | 'run.completed' | 'run.failed' | 'run.steered' | 'background.started' | 'background.completed' | 'background.failed' | 'background.cancelled';
4695
+ type AiAgentThread = {
4696
+ id: string;
4697
+ title: string;
4698
+ mode: AiAgentOrchestratorMode;
4699
+ status: AiAgentThreadStatus;
4700
+ createdAt: string;
4701
+ updatedAt: string;
4702
+ metadata?: Record<string, unknown>;
4703
+ };
4704
+ type AiAgentTurn = {
4705
+ id: string;
4706
+ threadId: string;
4707
+ role: AiAgentTurnRole;
4708
+ status: AiAgentTurnStatus;
4709
+ content: string;
4710
+ createdAt: string;
4711
+ updatedAt: string;
4712
+ provider?: string;
4713
+ model?: string;
4714
+ usage?: AIUsage;
4715
+ toolCalls?: AIToolCall[];
4716
+ error?: string;
4717
+ metadata?: Record<string, unknown>;
4718
+ };
4719
+ type AiAgentApproval = {
4720
+ id: string;
4721
+ threadId: string;
4722
+ turnId?: string;
4723
+ planId: string;
4724
+ risk: AiRiskLevel;
4725
+ requiredScopes: AiScope[];
4726
+ status: AiAgentApprovalStatus;
4727
+ createdAt: string;
4728
+ resolvedAt?: string;
4729
+ note?: string;
4730
+ plan: AiMutationPlan;
4731
+ };
4732
+ type AiAgentBackgroundJob = {
4733
+ id: string;
4734
+ kind: 'export' | 'analysis' | 'custom';
4735
+ title: string;
4736
+ status: AiAgentBackgroundJobStatus;
4737
+ createdAt: string;
4738
+ updatedAt: string;
4739
+ completedAt?: string;
4740
+ result?: unknown;
4741
+ error?: string;
4742
+ metadata?: Record<string, unknown>;
4743
+ };
4744
+ type AiAgentEvent = {
4745
+ id: string;
4746
+ threadId?: string;
4747
+ turnId?: string;
4748
+ type: AiAgentEventType;
4749
+ createdAt: string;
4750
+ payload: Record<string, unknown>;
4751
+ };
4752
+ type AiAgentTelemetrySnapshot = {
4753
+ runsStarted: number;
4754
+ runsCompleted: number;
4755
+ runsCancelled: number;
4756
+ runsFailed: number;
4757
+ totalLatencyMs: number;
4758
+ lastLatencyMs?: number;
4759
+ acceptedChanges: number;
4760
+ rejectedChanges: number;
4761
+ revisionRequests: number;
4762
+ toolFailures: number;
4763
+ backgroundJobsStarted: number;
4764
+ backgroundJobsCompleted: number;
4765
+ backgroundJobsFailed: number;
4766
+ };
4767
+ type AiAgentRuntimeSnapshot = {
4768
+ threads: AiAgentThread[];
4769
+ turns: AiAgentTurn[];
4770
+ approvals: AiAgentApproval[];
4771
+ backgroundJobs: AiAgentBackgroundJob[];
4772
+ events: AiAgentEvent[];
4773
+ telemetry: AiAgentTelemetrySnapshot;
4774
+ };
4775
+ type AiAgentRuntimeStorage = {
4776
+ load(): Promise<AiAgentRuntimeSnapshot | null>;
4777
+ save(snapshot: AiAgentRuntimeSnapshot): Promise<void>;
4778
+ };
4779
+ /**
4780
+ * Builds extra messages (workspace context, retrieved resources, …) to inject
4781
+ * before the conversation history on each run. Returned messages are placed
4782
+ * after {@link AiAgentRuntimeConfig.systemPrompt} and before the thread
4783
+ * history, so the model sees grounding context ahead of the dialogue. Resolve
4784
+ * to `[]` (or throw — failures are swallowed) to skip context for a turn.
4785
+ */
4786
+ type AiAgentContextProvider = (input: {
4787
+ threadId: string;
4788
+ content: string;
4789
+ }) => AIMessage[] | Promise<AIMessage[]>;
4790
+ type AiAgentRuntimeConfig = {
4791
+ provider: AIProvider;
4792
+ storage?: AiAgentRuntimeStorage;
4793
+ clock?: () => Date;
4794
+ mode?: AiAgentOrchestratorMode;
4795
+ maxEvents?: number;
4796
+ /** Optional system prompt prepended to every run. */
4797
+ systemPrompt?: string;
4798
+ /** Optional hook injecting grounding context messages before the history. */
4799
+ contextProvider?: AiAgentContextProvider;
4800
+ /**
4801
+ * Assist mode (default `scaffold`). Tags every assistant turn's provenance and
4802
+ * is exposed via {@link AiAgentRuntime.getAssistMode}; apps compose the
4803
+ * scaffold guard into their system prompt with {@link composeAssistSystemPrompt}.
4804
+ * `draft` is never the default — it must be set explicitly.
4805
+ */
4806
+ assistMode?: AiAssistMode;
4807
+ };
4808
+ type AiAgentThreadCreateInput = {
4809
+ title: string;
4810
+ mode?: AiAgentOrchestratorMode;
4811
+ metadata?: Record<string, unknown>;
4812
+ };
4813
+ type AiAgentRunTurnInput = {
4814
+ threadId: string;
4815
+ content: string;
4816
+ request?: Omit<AIGenerateRequest, 'prompt' | 'messages'>;
4817
+ metadata?: Record<string, unknown>;
4818
+ };
4819
+ type AiAgentSelectionKind = 'page-text' | 'database-rows' | 'canvas-objects' | 'nodes';
4820
+ type AiAgentSelectionContext = {
4821
+ kind: AiAgentSelectionKind;
4822
+ label?: string;
4823
+ pageId?: string;
4824
+ databaseId?: string;
4825
+ canvasId?: string;
4826
+ nodeIds?: string[];
4827
+ rowIds?: string[];
4828
+ objectIds?: string[];
4829
+ text?: string;
4830
+ range?: {
4831
+ from: number;
4832
+ to: number;
4833
+ };
4834
+ };
4835
+ type AiAgentRunSelectionTurnInput = Omit<AiAgentRunTurnInput, 'content'> & {
4836
+ instruction: string;
4837
+ selection: AiAgentSelectionContext;
4838
+ };
4839
+ type AiAgentRunTurnResult = {
4840
+ runId: string;
4841
+ userTurn: AiAgentTurn;
4842
+ assistantTurn: AiAgentTurn;
4843
+ };
4844
+ type AiAgentApprovalRequestInput = {
4845
+ threadId: string;
4846
+ turnId?: string;
4847
+ plan: AiMutationPlan;
4848
+ };
4849
+ type AiAgentApprovalResolveInput = {
4850
+ approvalId: string;
4851
+ status: Exclude<AiAgentApprovalStatus, 'pending'>;
4852
+ note?: string;
4853
+ };
4854
+ type AiAgentBackgroundJobInput = {
4855
+ kind: AiAgentBackgroundJob['kind'];
4856
+ title: string;
4857
+ metadata?: Record<string, unknown>;
4858
+ };
4859
+ type AiAgentBackgroundJobRunner = (signal: AbortSignal) => Promise<unknown>;
4860
+ type AiAgentRuntimeListener = (event: AiAgentEvent, snapshot: AiAgentRuntimeSnapshot) => void;
4861
+ type AiAgentDisplayStateKind = 'read-only-answer' | 'proposed-change' | 'applied-change';
4862
+ type AiAgentDisplayState = {
4863
+ kind: AiAgentDisplayStateKind;
4864
+ label: string;
4865
+ planId?: string;
4866
+ approvalId?: string;
4867
+ auditEventId?: string;
4868
+ };
4869
+ declare class AiAgentRuntime {
4870
+ private readonly config;
4871
+ private snapshot;
4872
+ private readonly storage;
4873
+ private readonly clock;
4874
+ private readonly mode;
4875
+ private readonly assistMode;
4876
+ private readonly maxEvents;
4877
+ private sequence;
4878
+ private loaded;
4879
+ private readonly listeners;
4880
+ private readonly activeRuns;
4881
+ private readonly activeJobs;
4882
+ constructor(config: AiAgentRuntimeConfig);
4883
+ /** The active assist mode (`scaffold` by default; `draft` is opt-in only). */
4884
+ getAssistMode(): AiAssistMode;
4885
+ load(): Promise<AiAgentRuntimeSnapshot>;
4886
+ getSnapshot(): AiAgentRuntimeSnapshot;
4887
+ subscribe(listener: AiAgentRuntimeListener): () => void;
4888
+ createThread(input: AiAgentThreadCreateInput): Promise<AiAgentThread>;
4889
+ runTurn(input: AiAgentRunTurnInput): Promise<AiAgentRunTurnResult>;
4890
+ runSelectionTurn(input: AiAgentRunSelectionTurnInput): Promise<AiAgentRunTurnResult>;
4891
+ cancelRun(runId: string): Promise<boolean>;
4892
+ steerRun(runId: string, message: string): Promise<boolean>;
4893
+ requestApproval(input: AiAgentApprovalRequestInput): Promise<AiAgentApproval>;
4894
+ resolveApproval(input: AiAgentApprovalResolveInput): Promise<AiAgentApproval>;
4895
+ startBackgroundJob(input: AiAgentBackgroundJobInput, runner: AiAgentBackgroundJobRunner): Promise<AiAgentBackgroundJob>;
4896
+ cancelBackgroundJob(jobId: string): Promise<boolean>;
4897
+ /**
4898
+ * Compose the messages sent to the provider for a run: the optional system
4899
+ * prompt, then optional grounding context, then the thread's prior
4900
+ * user/assistant turns in chronological order (which already include the
4901
+ * just-created user message). The in-flight (empty) assistant turn is
4902
+ * excluded. Context is best-effort — a throwing provider never fails the run.
4903
+ */
4904
+ private buildRunMessages;
4905
+ private completeRun;
4906
+ private completeStreamingRun;
4907
+ private applyGenerateResponse;
4908
+ private applyStreamChunk;
4909
+ private appendAssistantText;
4910
+ private appendToolCall;
4911
+ private finishRun;
4912
+ private failRun;
4913
+ private completeBackgroundJob;
4914
+ private finishBackgroundJob;
4915
+ private failBackgroundJob;
4916
+ private createTurn;
4917
+ private updateThread;
4918
+ private updateTurn;
4919
+ private updateJob;
4920
+ private emit;
4921
+ private persist;
4922
+ private ensureLoaded;
4923
+ private getThreadOrThrow;
4924
+ private getTurnOrThrow;
4925
+ private nowIso;
4926
+ private nextId;
4927
+ }
4928
+ declare function createAiAgentRuntime(config: AiAgentRuntimeConfig): AiAgentRuntime;
4929
+ declare function createMemoryAiAgentRuntimeStorage(initial?: AiAgentRuntimeSnapshot): AiAgentRuntimeStorage & {
4930
+ snapshot(): AiAgentRuntimeSnapshot;
4931
+ };
4932
+ /** Provenance tag stamped on every assistant turn (xNet trust tiers). */
4933
+ declare const AI_GENERATED_PROVENANCE: "ai-generated";
4934
+ /**
4935
+ * The cognitive-debt guard appended to the system prompt in scaffold mode. It
4936
+ * asks the model to help the user think rather than think for them — an answer
4937
+ * to the deskilling effect of LLM-authored work (MIT, arXiv:2506.08872).
4938
+ */
4939
+ declare const SCAFFOLD_SYSTEM_GUARD: string;
4940
+ /**
4941
+ * Compose the system prompt for an assist mode. In `scaffold` mode the guard is
4942
+ * appended so the assistant scaffolds rather than substitutes; in `draft` mode
4943
+ * the base prompt passes through unchanged. Returned to apps so the runtime's
4944
+ * own message composition stays backward-compatible.
4945
+ */
4946
+ declare function composeAssistSystemPrompt(base: string | undefined, mode: AiAssistMode): string | undefined;
4947
+ /** Metadata stamped on an assistant turn so its provenance is always legible. */
4948
+ declare function assistTurnProvenance(mode: AiAssistMode): {
4949
+ provenance: typeof AI_GENERATED_PROVENANCE;
4950
+ assistMode: AiAssistMode;
4951
+ };
4952
+ declare function renderSelectionPrompt(instruction: string, selection: AiAgentSelectionContext): string;
4953
+ declare function classifyAiAgentDisplayState(input: {
4954
+ plan?: AiMutationPlan;
4955
+ approval?: AiAgentApproval;
4956
+ auditEventId?: string;
4957
+ }): AiAgentDisplayState;
4958
+
4959
+ /**
4960
+ * Bring-Your-Own-Model connector contract (exploration 0174).
4961
+ *
4962
+ * No single transport reaches a model from a sandboxed HTTPS tab everywhere, so
4963
+ * the app probes a tiered set of "connectors" and degrades gracefully. Each
4964
+ * connector ultimately produces an {@link AIProvider} (see `../providers`); this
4965
+ * module only models *detection and selection* — which tiers are usable right
4966
+ * now and how capable each is — so it stays pure and unit-testable without a
4967
+ * GPU, Ollama, a key, or a browser.
4968
+ */
4969
+ /** The model-access tiers, from the exploration's options A–E (+ managed, 0208). */
4970
+ type ConnectorTier = 'managed' | 'webllm' | 'local-server' | 'prompt-api' | 'cloud-key' | 'bridge';
4971
+ /**
4972
+ * How reliably the tier's model can call tools. This is the gate the
4973
+ * exploration identifies: `reliable` → full agentic writes; otherwise writes
4974
+ * must downgrade to "propose a plan, human applies" (see {@link writeModeFor}).
4975
+ */
4976
+ type ToolCallingFidelity = 'reliable' | 'weak' | 'none';
4977
+ /** Whether the agent may apply writes itself, or must only propose them. */
4978
+ type WriteMode = 'agentic' | 'propose-only';
4979
+ /**
4980
+ * Injectable probes so detection is testable and host-agnostic. Every field has
4981
+ * a sensible default in {@link detectConnectors}; tests and non-browser hosts
4982
+ * override what they need.
4983
+ */
4984
+ interface ConnectorEnv {
4985
+ /**
4986
+ * Probe XNet Cloud managed AI: GET `${managedUrl}/ai/health` is `ok` and the
4987
+ * tenant has AI enabled. Default: a same-origin fetch (returns false off-cloud).
4988
+ */
4989
+ probeManaged?: (baseUrl: string) => Promise<boolean>;
4990
+ /** Base URL the hub serves the managed `/ai` routes from. Default: `''` (same origin). */
4991
+ managedUrl?: string;
4992
+ /** WebGPU present (enables in-tab models). Default: `navigator.gpu` check. */
4993
+ hasWebGpu?: () => boolean | Promise<boolean>;
4994
+ /**
4995
+ * Whether the host can actually build an in-tab WebLLM engine (the heavy
4996
+ * `@mlc-ai/web-llm` import is host-supplied, see the panel). The `webllm` tier
4997
+ * is reported available only when this AND {@link hasWebGpu} are true —
4998
+ * detecting WebGPU alone would advertise a tier the host can't instantiate,
4999
+ * leaving the composer silently disabled. Default: `false` (no engine wired).
5000
+ */
5001
+ hasWebLLMEngine?: () => boolean | Promise<boolean>;
5002
+ /**
5003
+ * Chrome built-in `LanguageModel` is present *and the model is ready to use*.
5004
+ * Default: probes `LanguageModel.availability()` and reports ready only for
5005
+ * `'available'` — mere API presence (`'downloadable'`) means a session can't
5006
+ * be created without a user-gesture download first (see the panel's gesture).
5007
+ */
5008
+ hasPromptApi?: () => boolean | Promise<boolean>;
5009
+ /** Local model endpoints to probe, in priority order. Default: Ollama, LM Studio. */
5010
+ localServerProbes?: readonly LocalServerProbe[];
5011
+ /** Whether a cloud API key is stored locally. Default: `false` (host wires this). */
5012
+ hasCloudKey?: () => boolean | Promise<boolean>;
5013
+ /** Probe a local bridge daemon. Default: GET `${bridgeUrl}/health`. */
5014
+ probeBridge?: (baseUrl: string) => Promise<boolean>;
5015
+ /** Bridge daemon base URL. Default: `http://127.0.0.1:31416`. */
5016
+ bridgeUrl?: string;
5017
+ }
5018
+ /** A named local model endpoint and how to detect it. */
5019
+ interface LocalServerProbe {
5020
+ label: string;
5021
+ baseUrl: string;
5022
+ probe: (baseUrl: string) => Promise<boolean>;
5023
+ }
5024
+ /** The result of probing one tier. */
5025
+ interface ConnectorDetection {
5026
+ tier: ConnectorTier;
5027
+ label: string;
5028
+ available: boolean;
5029
+ /** Extra context when available (e.g. the reachable endpoint or model). */
5030
+ detail?: string;
5031
+ /** When unavailable: why, and how to enable it. */
5032
+ setupHint?: string;
5033
+ /** Tool-calling fidelity → gates agentic vs propose-only writes. */
5034
+ toolCalling: ToolCallingFidelity;
5035
+ /** Preference rank; lower is more capable/preferred. */
5036
+ preference: number;
5037
+ }
5038
+ /** Map tool-calling fidelity to the safe write mode for that tier. */
5039
+ declare function writeModeFor(toolCalling: ToolCallingFidelity): WriteMode;
5040
+
5041
+ /**
5042
+ * Connector detection (exploration 0174).
5043
+ *
5044
+ * Probes the model-access tiers in parallel and returns them ranked by
5045
+ * capability so the chat panel can prefer the most capable available tier and
5046
+ * fall back gracefully. Pure given its {@link ConnectorEnv}; the defaults touch
5047
+ * `navigator`/`fetch` but every probe is overridable.
5048
+ */
5049
+
5050
+ interface ConnectorMeta {
5051
+ label: string;
5052
+ toolCalling: ToolCallingFidelity;
5053
+ /** Lower = more capable/preferred. Mirrors the exploration: E > D > B > A > C. */
5054
+ preference: number;
5055
+ }
5056
+ /** Stable per-tier metadata. Keep ordering aligned with the recommendation. */
5057
+ declare const CONNECTOR_META: Record<ConnectorTier, ConnectorMeta>;
5058
+ /** Default local-model endpoints: Ollama (`/api/tags`) and LM Studio (`/v1/models`). */
5059
+ declare function defaultLocalServerProbes(): LocalServerProbe[];
5060
+ /** Probe an OpenAI-compatible server's `/v1/models`. Used for LM Studio etc. */
5061
+ declare function probeOpenAiCompatible(baseUrl: string): Promise<boolean>;
5062
+ /**
5063
+ * Detect which model connectors are usable right now, ranked by preference
5064
+ * (most capable first). Runs all probes concurrently.
5065
+ */
5066
+ declare function detectConnectors(env?: ConnectorEnv): Promise<ConnectorDetection[]>;
5067
+ /** The most-preferred *available* connector, or null if none are usable. */
5068
+ declare function pickBestConnector(detections: readonly ConnectorDetection[]): ConnectorDetection | null;
5069
+
5070
+ /**
5071
+ * WebLLM provider adapter (exploration 0174, tier A).
5072
+ *
5073
+ * Wraps an in-tab WebGPU model (`@mlc-ai/web-llm`) behind the `AIProvider`
5074
+ * contract. To keep `@xnetjs/plugins` dependency-free and node-safe, the heavy
5075
+ * library is **not** a dependency here: the provider runs against a structural
5076
+ * {@link WebLLMEngineLike} interface, and `createWebLLMProvider` takes an
5077
+ * already-created engine. The web app supplies a real engine via a lazy
5078
+ * `import('@mlc-ai/web-llm')` + `CreateMLCEngine(...)` (see the "Connect a
5079
+ * model" guide); tests inject a fake.
5080
+ *
5081
+ * WebLLM exposes an OpenAI-compatible chat-completions surface. In-tab tool
5082
+ * calling is unreliable, so this reports `tools: false` — the chat panel routes
5083
+ * its writes through propose-only mode (see `writeModeFor`).
5084
+ */
5085
+
5086
+ /** Minimal shape of an `@mlc-ai/web-llm` MLCEngine, structurally typed. */
5087
+ interface WebLLMEngineLike {
5088
+ chat: {
5089
+ completions: WebLLMCompletions;
5090
+ };
5091
+ }
5092
+ interface WebLLMCompletions {
5093
+ create(request: WebLLMRequest & {
5094
+ stream?: false;
5095
+ }): Promise<WebLLMResponse>;
5096
+ create(request: WebLLMRequest & {
5097
+ stream: true;
5098
+ }): Promise<AsyncIterable<WebLLMChunk>>;
5099
+ }
5100
+ interface WebLLMRequest {
5101
+ messages: Array<{
5102
+ role: string;
5103
+ content: string;
5104
+ }>;
5105
+ stream?: boolean;
5106
+ temperature?: number;
5107
+ max_tokens?: number;
5108
+ }
5109
+ interface WebLLMResponse {
5110
+ choices: Array<{
5111
+ message: {
5112
+ content: string | null;
5113
+ };
5114
+ }>;
5115
+ usage?: {
5116
+ prompt_tokens?: number;
5117
+ completion_tokens?: number;
5118
+ total_tokens?: number;
5119
+ };
5120
+ }
5121
+ interface WebLLMChunk {
5122
+ choices: Array<{
5123
+ delta: {
5124
+ content?: string;
5125
+ };
5126
+ }>;
5127
+ usage?: {
5128
+ prompt_tokens?: number;
5129
+ completion_tokens?: number;
5130
+ total_tokens?: number;
5131
+ };
5132
+ }
5133
+ interface WebLLMProviderOptions {
5134
+ engine: WebLLMEngineLike;
5135
+ /** Model id loaded into the engine (for reporting). */
5136
+ model: string;
5137
+ /** Context window of the loaded model, for capability reporting. */
5138
+ contextWindow?: number;
5139
+ }
5140
+ declare class WebLLMProvider implements AIProvider {
5141
+ readonly name = "webllm";
5142
+ private readonly engine;
5143
+ private readonly model;
5144
+ private readonly contextWindow;
5145
+ constructor(options: WebLLMProviderOptions);
5146
+ generate(prompt: string): Promise<string>;
5147
+ stream(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
5148
+ getCapabilities(): AIModelCapabilities;
5149
+ }
5150
+ declare function createWebLLMProvider(options: WebLLMProviderOptions): WebLLMProvider;
5151
+
5152
+ /**
5153
+ * Chrome built-in AI provider adapter (exploration 0174, tier C).
5154
+ *
5155
+ * Wraps the browser's on-device `LanguageModel` (Gemini Nano) behind the
5156
+ * `AIProvider` contract. The API is Chrome-only and not yet in the DOM lib
5157
+ * types, so it is structurally typed here ({@link LanguageModelLike}); the
5158
+ * provider takes an injected session so it's testable without a browser, and
5159
+ * `createPromptApiProvider` resolves one from `globalThis.LanguageModel`.
5160
+ *
5161
+ * The Prompt API has no tool calling, so this reports `tools: false` →
5162
+ * propose-only writes (see `writeModeFor`).
5163
+ */
5164
+
5165
+ /** Minimal shape of a Chrome `LanguageModel` session. */
5166
+ interface LanguageModelSessionLike {
5167
+ prompt(input: string): Promise<string>;
5168
+ promptStreaming(input: string): AsyncIterable<string>;
5169
+ }
5170
+ /** The four states Chrome's `LanguageModel.availability()` can report. */
5171
+ type PromptApiAvailability = 'unavailable' | 'downloadable' | 'downloading' | 'available';
5172
+ /** A download-progress monitor passed to `create({ monitor })`. */
5173
+ interface LanguageModelMonitor {
5174
+ addEventListener(type: 'downloadprogress', listener: (event: {
5175
+ loaded: number;
5176
+ }) => void): void;
5177
+ }
5178
+ /** Minimal shape of the global `LanguageModel` factory. */
5179
+ interface LanguageModelLike {
5180
+ availability(): Promise<PromptApiAvailability>;
5181
+ create(options?: {
5182
+ initialPrompts?: Array<{
5183
+ role: string;
5184
+ content: string;
5185
+ }>;
5186
+ /** Observe the on-device model download (fires `downloadprogress`). */
5187
+ monitor?: (monitor: LanguageModelMonitor) => void;
5188
+ }): Promise<LanguageModelSessionLike>;
5189
+ }
5190
+ interface PromptApiProviderOptions {
5191
+ session: LanguageModelSessionLike;
5192
+ model?: string;
5193
+ }
5194
+ declare class PromptApiProvider implements AIProvider {
5195
+ readonly name = "prompt-api";
5196
+ private readonly session;
5197
+ private readonly model;
5198
+ constructor(options: PromptApiProviderOptions);
5199
+ generate(prompt: string): Promise<string>;
5200
+ stream(request: AIGenerateRequest): AsyncIterable<AIStreamChunk>;
5201
+ getCapabilities(): AIModelCapabilities;
5202
+ }
5203
+ /**
5204
+ * Resolve a provider from the global `LanguageModel`, downloading the model if
5205
+ * needed. Returns null if the API is unavailable (not Chrome, or no model).
5206
+ */
5207
+ declare function createPromptApiProvider(factory?: LanguageModelLike): Promise<PromptApiProvider | null>;
5208
+ /**
5209
+ * Raw availability of the on-device model, or `'unavailable'` when the API is
5210
+ * absent (not Chrome) or the probe throws. Unlike `createPromptApiProvider`,
5211
+ * this distinguishes `'downloadable'`/`'downloading'` from `'available'` so the
5212
+ * UI can offer a download gesture instead of silently reporting "unavailable".
5213
+ */
5214
+ declare function promptApiAvailability(factory?: LanguageModelLike): Promise<PromptApiAvailability>;
5215
+ /**
5216
+ * Trigger the on-device model download from a **user gesture**, reporting
5217
+ * progress as a fraction in [0, 1]. Chrome gates the download behind a user
5218
+ * activation, so this must be called from a click handler — not an effect.
5219
+ * Resolves `true` once a session is creatable; the runtime rebuilds its own
5220
+ * session once detection re-flips to `'available'`. Returns `false` when the
5221
+ * API is absent; throws (for the caller to `.catch`) if `create()` rejects.
5222
+ */
5223
+ declare function downloadPromptApiModel(onProgress?: (fraction: number) => void, factory?: LanguageModelLike): Promise<boolean>;
5224
+
1501
5225
  /**
1502
5226
  * Service Plugin Types
1503
5227
  *
@@ -1692,93 +5416,6 @@ interface IProcessManager {
1692
5416
  off<K extends keyof ProcessManagerEvents>(event: K, listener: ProcessManagerEvents[K]): void;
1693
5417
  }
1694
5418
 
1695
- /**
1696
- * Local HTTP API Server
1697
- *
1698
- * Provides a REST API for external integrations (N8N, MCP, etc.) to interact
1699
- * with xNet data. Runs on localhost only (port 31415 by default).
1700
- *
1701
- * This is designed to run in the Electron main process.
1702
- */
1703
- /**
1704
- * Node store interface (minimal subset needed by API)
1705
- */
1706
- interface NodeStoreAPI {
1707
- get(id: string): Promise<NodeData | null>;
1708
- list(options?: {
1709
- schemaId?: string;
1710
- limit?: number;
1711
- offset?: number;
1712
- }): Promise<NodeData[]>;
1713
- create(options: {
1714
- schemaId: string;
1715
- properties: Record<string, unknown>;
1716
- }): Promise<NodeData>;
1717
- update(id: string, options: {
1718
- properties: Record<string, unknown>;
1719
- }): Promise<NodeData>;
1720
- delete(id: string): Promise<void>;
1721
- subscribe(listener: (event: NodeChangeEventData) => void): () => void;
1722
- }
1723
- /**
1724
- * Schema registry interface (minimal subset needed by API)
1725
- */
1726
- interface SchemaRegistryAPI {
1727
- getAllIRIs(): string[];
1728
- get(iri: string): Promise<SchemaData | null>;
1729
- }
1730
- /**
1731
- * Node data returned by API
1732
- */
1733
- interface NodeData {
1734
- id: string;
1735
- schemaId: string;
1736
- properties: Record<string, unknown>;
1737
- deleted: boolean;
1738
- createdAt: number;
1739
- updatedAt: number;
1740
- }
1741
- /**
1742
- * Schema data returned by API
1743
- */
1744
- interface SchemaData {
1745
- iri: string;
1746
- name: string;
1747
- properties: Record<string, unknown>;
1748
- }
1749
- /**
1750
- * Node change event data
1751
- */
1752
- interface NodeChangeEventData {
1753
- change: {
1754
- type: string;
1755
- };
1756
- node: NodeData | null;
1757
- isRemote: boolean;
1758
- }
1759
- /**
1760
- * API configuration
1761
- */
1762
- interface LocalAPIConfig {
1763
- /** Port to listen on (default: 31415) */
1764
- port?: number;
1765
- /** Host to bind to (default: '127.0.0.1') */
1766
- host?: string;
1767
- /** API token for authentication (optional) */
1768
- token?: string;
1769
- /**
1770
- * Allowed CORS origins (default: none).
1771
- * Set to specific origins like ['http://localhost:3000'] for local dev,
1772
- * or ['*'] to allow all origins (not recommended for production).
1773
- * Empty array or undefined disables CORS entirely.
1774
- */
1775
- allowedOrigins?: string[];
1776
- /** NodeStore instance */
1777
- store: NodeStoreAPI;
1778
- /** SchemaRegistry instance */
1779
- schemas: SchemaRegistryAPI;
1780
- }
1781
-
1782
5419
  /**
1783
5420
  * Service Client
1784
5421
  *
@@ -1986,7 +5623,15 @@ declare function createWebhookEmitter(store: NodeStoreAPI): WebhookEmitter;
1986
5623
  */
1987
5624
  interface MCPTool {
1988
5625
  name: string;
5626
+ title?: string;
1989
5627
  description: string;
5628
+ risk?: string;
5629
+ requiredScopes?: string[];
5630
+ /**
5631
+ * Tool Search Tool hint (advanced-tool-use): deferred tools are discovered
5632
+ * on demand instead of paying their definition tokens on every turn.
5633
+ */
5634
+ defer_loading?: boolean;
1990
5635
  inputSchema: {
1991
5636
  type: 'object';
1992
5637
  properties: Record<string, MCPPropertySchema>;
@@ -1999,7 +5644,11 @@ interface MCPTool {
1999
5644
  interface MCPPropertySchema {
2000
5645
  type: 'string' | 'number' | 'boolean' | 'object' | 'array';
2001
5646
  description?: string;
5647
+ enum?: readonly string[];
5648
+ properties?: Record<string, MCPPropertySchema>;
5649
+ required?: string[];
2002
5650
  items?: MCPPropertySchema;
5651
+ additionalProperties?: boolean | MCPPropertySchema;
2003
5652
  }
2004
5653
  /**
2005
5654
  * MCP Resource definition
@@ -2009,6 +5658,9 @@ interface MCPResource {
2009
5658
  name: string;
2010
5659
  description?: string;
2011
5660
  mimeType?: string;
5661
+ risk?: string;
5662
+ requiredScopes?: string[];
5663
+ dynamic?: boolean;
2012
5664
  }
2013
5665
  /**
2014
5666
  * MCP Request from client
@@ -2038,10 +5690,85 @@ interface MCPResponse {
2038
5690
  interface MCPServerConfig {
2039
5691
  store: NodeStoreAPI;
2040
5692
  schemas: SchemaRegistryAPI;
5693
+ /** Shared AI surface service. A default service is created when omitted. */
5694
+ aiSurface?: AiSurfaceService;
5695
+ /** Optional output and pagination limits for the default AI surface. */
5696
+ aiLimits?: Partial<AiSurfaceLimits>;
5697
+ /**
5698
+ * Plugin/connector agent tools to expose (exploration 0196). Folded into the
5699
+ * default AI surface's `extraTools`, so they appear in `tools/list` (deferred,
5700
+ * since not in {@link MCP_CORE_TOOL_NAMES}) and dispatch through `tools/call`.
5701
+ * Ignored when a pre-built `aiSurface` is supplied — wire `extraTools` there.
5702
+ */
5703
+ agentTools?: AgentToolContribution[];
5704
+ /**
5705
+ * Write guardrail for the generic + first-class write tools. A default
5706
+ * guardrail (delete/outward writes need confirmation, cost budget, audit) is
5707
+ * created when omitted. Pass a configured instance to tune it.
5708
+ */
5709
+ guardrail?: McpWriteGuardrail;
2041
5710
  /** Server name (default: 'xnet') */
2042
5711
  name?: string;
2043
5712
  /** Server version (default: '1.0.0') */
2044
5713
  version?: string;
2045
5714
  }
2046
5715
 
2047
- export { AIGenerationError, type AIProvider, type AIProviderConfig, type AIProviderOptions, type AIProviderType, type AIScriptRequest, type AIScriptResponse, AnthropicProvider, type ArrayHelpers, type BlockContribution, type BlockProps, type CommandContribution, ContributionRegistry, type DeliveryResult, type Disposable$1 as Disposable, type EditorContribution, type ExtensionContext, type ExtensionStorage, type FlatNode, type FormatHelpers, type IProcessManager, type LocalAPIConfig, type MCPPropertySchema, type MCPRequest, type MCPResource, type MCPResponse, type MCPServerConfig, type MCPTool, type MathHelpers, MiddlewareChain, type NodeChangeEvent, type NodeChangeEventData, type NodeData, type NodeStoreAPI, type NodeStoreMiddleware, OllamaProvider, OpenAIProvider, type PendingChange, type Platform, type PlatformCapabilities, type PluginContributions, PluginError, type PluginNode, type PluginNodeChangeEvent, type PluginNodeChangeListener, type PluginPermissions, PluginRegistry, PluginSchema, type PluginStatus, PluginValidationError, type ProcessManagerEvents, type PropertyCellProps, type PropertyEditorProps, type PropertyHandler, type PropertyHandlerContribution, type QueryFilter, type RegisteredPlugin, SERVICE_IPC_CHANNELS, type SandboxOptions, type SchemaContribution, type SchemaData, type SchemaDefinition, type SchemaProperty, type SchemaRegistryAPI, type ScriptContext, ScriptError, type ScriptExecutionResult, ScriptGenerationError, ScriptGenerator, type ScriptGeneratorOptions, type ScriptNode, type ScriptNodeChangeEvent, type ScriptOutputType, ScriptRunner, type ScriptRunnerOptions, ScriptSandbox, ScriptSchema, type ScriptStore, ScriptTimeoutError, type ScriptTriggerType, ScriptValidationError, type ServiceClient, type ServiceCommunication, type ServiceDefinition, type ServiceHealthCheck, type ServiceLifecycle, type ServiceOutputEvent, type ServiceProcessConfig, type ServiceProvides, type ServiceState, type ServiceStatus, type ServiceStatusEvent, type SettingContribution, type SettingsPanelProps, ShortcutManager, type SidebarContribution, type SlashCommandContext, type SlashCommandContribution, type TelemetryReporter, type TextHelpers, type ToolbarContribution, TypedRegistry, type ValidationResult, type ViewContribution, type ViewProps, type WebhookConfig, WebhookEmitter, type WebhookPayload, type XNetExtension, buildRetryPrompt, buildScriptPrompt, createAIProvider, createExtensionContext, createExtensionStorage, createScriptContext, createServiceClient, createWebhookEmitter, defineExtension, executeScript, generateScript, getPlatformCapabilities, getShortcutManager, installShortcutHandler, isOllamaAvailable, isScriptNode, isServiceClientAvailable, listOllamaModels, quickSafetyCheck, validateManifest, validateScript, validateScriptAST };
5716
+ /**
5717
+ * Right to Leave (xNet Humane Internet Charter §Exit, exploration 0234).
5718
+ *
5719
+ * Leaving is a right, not a retention funnel. `leaveWithEverything` bundles
5720
+ * everything you'd need to recreate yourself elsewhere — your whole workspace,
5721
+ * your portable identity, and a README on how to re-import — into one archive.
5722
+ * `deleteDay` is the honest, irreversible version: stop feeding the system.
5723
+ *
5724
+ * This module is the orchestration; the app injects the real capabilities
5725
+ * (AiWorkspaceExporter for the workspace, the identity keystore, hub purge, OPFS
5726
+ * destroy, consent-gated telemetry). Keeping them as ports makes the policy —
5727
+ * "no confirmshaming, take everything, the local copy is yours" — testable in
5728
+ * isolation.
5729
+ */
5730
+ /** The capabilities the app wires in. Only `exportWorkspace`/`exportIdentity` are required. */
5731
+ interface RightToLeavePorts {
5732
+ /** The full workspace as relative-path → file-content (e.g. via AiWorkspaceExporter). */
5733
+ exportWorkspace(): Promise<Record<string, string>>;
5734
+ /** The portable identity bundle (did:key + recovery), JSON-serializable. */
5735
+ exportIdentity(): Promise<unknown>;
5736
+ /** Tombstone the user's copies on every connected hub. Absent ⇒ offline-only user. */
5737
+ purgeRemoteCopies?(): Promise<void>;
5738
+ /** Wipe the local master copy (OPFS/SQLite). Absent ⇒ nothing local to wipe. */
5739
+ destroyLocal?(): Promise<void>;
5740
+ /** Record a non-identifying `account.left` signal (consent-gated). */
5741
+ recordLeft?(): void;
5742
+ }
5743
+ interface LeaveBundle {
5744
+ /** relative path → file content; a complete, portable copy of you. */
5745
+ files: Record<string, string>;
5746
+ exportedAt: string;
5747
+ }
5748
+ interface DeleteDayOptions {
5749
+ /** Keep the local master copy on this device (export-and-go) vs. full wipe. */
5750
+ keepLocal: boolean;
5751
+ /** Injected timestamp (ISO) — no implicit clock, so the result is deterministic. */
5752
+ now: string;
5753
+ }
5754
+ interface DeleteDayResult {
5755
+ remotePurged: boolean;
5756
+ localWiped: boolean;
5757
+ recordedLeft: boolean;
5758
+ }
5759
+ declare const LEAVE_README = "# Your xNet data \u2014 yours to keep\n\nThis archive is a complete, portable copy of your workspace.\n\n- `workspace/` \u2014 every page, database, canvas, and node, plus a manifest.\n- `identity.did.json` \u2014 your portable did:key identity and recovery material.\n\nTo continue elsewhere: run your own hub (`xnet-hub start`) or any xNet-compatible\nbackend, then import this archive. Your did:key works on any hub \u2014 there is no\naccount to transfer and nothing held back. You don't need our permission to leave.\n";
5760
+ /**
5761
+ * Everything you'd need to recreate yourself elsewhere, in one bundle. There is
5762
+ * deliberately no "are you sure you'll miss out?" — this is a right, not a funnel.
5763
+ */
5764
+ declare function leaveWithEverything(ports: RightToLeavePorts, opts: {
5765
+ now: string;
5766
+ }): Promise<LeaveBundle>;
5767
+ /**
5768
+ * Delete Day: stop feeding the system, for real. Tombstones remote copies on
5769
+ * every hub and (unless `keepLocal`) wipes the local master too. The only signal
5770
+ * emitted is an anonymous `account.left` — never anything that identifies who.
5771
+ */
5772
+ declare function deleteDay(ports: RightToLeavePorts, opts: DeleteDayOptions): Promise<DeleteDayResult>;
5773
+
5774
+ export { type AIComplexityLevel, type AICostModel, type AIGenerateRequest, type AIGenerateResponse, AIGenerationError, type AIMessage, type AIMessageRole, type AIModelCapabilities, type AIModelQuality, type AIPrivacyLevel, type AIProvider, type AIProviderConfig, type AIProviderOptions, AIProviderRouter, type AIProviderRouterOptions, type AIProviderType, type AIProviderUsage, AIRTABLE_CONNECTOR_ID, type AIRiskLevel, type AIScriptRequest, type AIScriptResponse, type AIStreamChunk, type AIToolCall, type AIToolSpec, type AIUsage, AI_GENERATED_PROVENANCE, AI_RISK_LEVELS, AI_SCOPES, AI_TARGET_KINDS, ALLOWED_PLUGIN_LICENSES, type ActionContext, type ActionDefinition, ActionDefinitionError, ActionDispatchError, type ActionEvent, ActionSsrfError, type ActionTrigger, type AgentToolContribution, type AgentToolInputSchema, type AiAgentApproval, type AiAgentApprovalRequestInput, type AiAgentApprovalResolveInput, type AiAgentApprovalStatus, type AiAgentBackgroundJob, type AiAgentBackgroundJobInput, type AiAgentBackgroundJobRunner, type AiAgentBackgroundJobStatus, type AiAgentDisplayState, type AiAgentDisplayStateKind, type AiAgentEvent, type AiAgentEventType, type AiAgentOrchestratorMode, type AiAgentRunSelectionTurnInput, type AiAgentRunTurnInput, type AiAgentRunTurnResult, AiAgentRuntime, type AiAgentRuntimeConfig, type AiAgentRuntimeListener, type AiAgentRuntimeSnapshot, type AiAgentRuntimeStorage, type AiAgentSelectionContext, type AiAgentSelectionKind, type AiAgentTelemetrySnapshot, type AiAgentThread, type AiAgentThreadCreateInput, type AiAgentThreadStatus, type AiAgentTurn, type AiAgentTurnRole, type AiAgentTurnStatus, type AiAssistMode, type AiAuditEvent, type AiAuthoredPlugin, AiAuthoringError, AiBudgetError, type AiCallableTool, type AiChangeSet, type AiCommandExposure, type AiContextPack, type AiContextPackResource, type AiContextRetriever, type AiContextSeed, type AiDatabaseMutationApplyResult, type AiExtraTool, type AiJsonSchema, type AiJsonSchemaType, type AiMutationPlan, type AiMutationPlanStatus, type AiOperation, type AiPageMarkdownApplyAdapter, type AiPageMarkdownApplyAdapterInput, type AiPageMarkdownApplyAdapterResult, type AiPageMarkdownApplyResult, type AiPageMarkdownRollbackResult, type AiPluginPipelineInput, type AiPluginPipelinePorts, type AiPluginPipelineResult, type AiResource, type AiResourceContent, type AiRetrievedNode, type AiRiskLevel, type AiScope, type AiSearchOptions, type AiSearchResult, type AiSurfaceLimits, AiSurfaceService, type AiSurfaceServiceConfig, type AiTargetKind, type AiToolCallResult, type AiToolDefinition, type AiValidationResult, type AirtableConnectorOptions, type AllowedPluginLicense, AnthropicProvider, type ArrayHelpers, type BlockContribution, type BlockProps, CANVAS_PLUGIN_FIXTURES, CHANNEL_SCHEMA, CHAT_MESSAGE_SCHEMA, CONNECTOR_CATEGORY, CONNECTOR_META, CRM_CANVAS_PLUGIN_FIXTURE, type CanvasCardContribution, type CanvasContribution, type CanvasContributionBase, type CanvasContributionPermission, type CanvasEdgeContribution, type CanvasErpPrototypeAuditEntry, type CanvasErpPrototypeAuditOperation, type CanvasErpPrototypeAuditSource, type CanvasErpPrototypeCard, type CanvasErpPrototypeCommand, type CanvasErpPrototypeEdge, type CanvasErpPrototypeEntityKind, type CanvasErpPrototypeLayoutKind, type CanvasErpPrototypeQueryFrame, type CanvasErpPrototypeQueryPredicate, type CanvasErpPrototypeRect, type CanvasErpPrototypeRisk, type CanvasErpPrototypeRiskSummary, type CanvasErpPrototypeScenario, type CanvasErpPrototypeStatus, type CanvasIngestInputKind, type CanvasIngestorContribution, type CanvasInspectorContribution, type CanvasInspectorPlacement, type CanvasLayoutContribution, type CanvasLayoutScope, type CanvasPluginFixture, type CanvasPluginFixtureCardSample, type CanvasPluginFixtureKind, type CanvasPluginPermissionDecisionStatus, type CanvasPluginPermissionGateDecision, type CanvasPluginPermissionGateInput, type CanvasPluginPermissionPrompt, type CanvasPluginPermissionPromptOption, type CanvasPluginPromptMode, type CanvasPluginSandboxDecision, type CanvasPluginSandboxDomAccess, type CanvasPluginSandboxKind, type CanvasPluginSandboxMutationAccess, type CanvasPluginSandboxNetworkAccess, type CanvasPluginSandboxOutput, type CanvasPluginSandboxOutputKind, type CanvasPluginSandboxOutputValidation, type CanvasPluginSandboxPolicy, type CanvasPluginSandboxRequest, type CanvasPluginWorkspacePolicy, type CanvasPreviewTier, type CanvasRendererSandboxContribution, type CanvasTemplateCategory, type CanvasTemplateContribution, type CanvasToolContribution, type CanvasToolGroup, CapabilityError, type CommandContext, type CommandContribution, CommandRegistry, type CommandScope, type ConnectorArtifacts, type ConnectorCadence, type ConnectorDefinition, ConnectorDefinitionError, type ConnectorDetection, type ConnectorEnv, type ConnectorFetch, type ConnectorInstallGate, type ConnectorStore, type ConnectorSyncContext, ConnectorSyncError, type ConnectorSyncResult, type ConnectorSyncSpec, type ConnectorTier, type ConnectorToolDescriptor, type ConsentDecision, type ConsentLine, ContributionRegistry, DEFAULT_PLUGIN_LICENSE, type DefinedAction, type DefinedConnector, type DeleteDayOptions, type DeleteDayResult, type DeliveryResult, DependencyCycleError, type DependencyNode, type Disposable$1 as Disposable, ERP_CANVAS_PLUGIN_FIXTURE, EXTERNAL_ITEM_SCHEMA, type EditorContribution, type EditorSchemaRisk, type EmailActionOptions, type ExtensionContext, type ExtensionStorage, FEED_ITEM_SCHEMA, type FeatureModule, type FeedEntry, type FetchJson, type FetchLike, type FlatNode, type FormatHelpers, GITHUB_CONNECTOR_ID, type GeneratedScript, type GithubConnectorOptions, type GuardableConnectorStore, type IProcessManager, type ImporterContribution, type InstallOptions, LEAVE_README, LINEAR_CONNECTOR_ID, type LabRunOutcome, type LadderRuntimeTier, type LanguageModelLike, type LanguageModelMonitor, type LanguageModelSessionLike, type LeaveBundle, type LicenseCheckResult, LicenseRequiredError, type LinearConnectorOptions, type LocalAPIConfig, type LocalAPITokenConfig, type LocalAPITokenScope, type LocalAPITokenSummary, type LocalServerProbe, MARKETPLACE_PROVENANCE, type MCPPropertySchema, type MCPRequest, type MCPResource, type MCPResponse, type MCPServerConfig, type MCPTool, MEDIA_PROVIDER_CANVAS_PLUGIN_FIXTURE, type ManagedBudgetSnapshot, ManagedProvider, type ManagedProviderOptions, MarketplaceClient, type MarketplaceClientOptions, type MarketplaceEntry, type MarketplaceSort, type MathHelpers, type MentionProviderContribution, type MentionSuggestion, MiddlewareChain, type MissingDependency, type ModuleCapabilities, NOTION_CONNECTOR_ID, type NodeChangeEvent, type NodeChangeEventData, type NodeData, type NodeStoreAPI, type NodeStoreMiddleware, type NotionConnectorOptions, OllamaProvider, OpenAICompatibleProvider, type OpenAICompatibleProviderOptions, OpenAIProvider, type PendingChange, type Platform, type PlatformCapabilities, type PluginContributions, PluginError, type PluginNode, type PluginNodeChangeEvent, type PluginNodeChangeListener, type PluginPermissions, type PluginPricing, type PluginRating, PluginRegistry, type PluginRunInput, type PluginRunResult, PluginRuntimeError, type PluginRuntimeLadder, PluginSchema, type PluginStatus, PluginValidationError, type ProcessManagerEvents, type PromptApiAvailability, PromptApiProvider, type PromptApiProviderOptions, type PropertyCellProps, type PropertyEditorProps, type PropertyHandler, type PropertyHandlerContribution, type Provenance, type ProvenanceResult, type ProvenanceVerifier, type QueryFilter, RSS_CONNECTOR_ID, type RatingSummary, type RecommendOptions, type RegisteredPlugin, type ResolveMentionOptions, type RightToLeavePorts, type RssConnectorOptions, type RunActionPorts, type RunConnectorSyncPorts, type RunPluginCodeInput, SCAFFOLD_SYSTEM_GUARD, SERVICE_IPC_CHANNELS, SLACK_CONNECTOR_ID, type SandboxOptions, ScaffoldError, type ScaffoldResult, type ScaffoldSpec, type ScaffoldTemplate, type SchemaContribution, type SchemaData, type SchemaDefinition, type SchemaProperty, type SchemaRegistryAPI, type ScriptContext, ScriptError, type ScriptExecutionResult, type ScriptExecutor, ScriptGenerationError, ScriptGenerator, type ScriptGeneratorOptions, type ScriptNode, type ScriptNodeChangeEvent, type ScriptOutputType, ScriptRunner, type ScriptRunnerOptions, ScriptSandbox, ScriptSchema, type ScriptStore, ScriptTimeoutError, type ScriptToManifestInput, type ScriptTriggerType, ScriptValidationError, type SemVer, type ServiceClient, type ServiceCommunication, type ServiceDefinition, type ServiceHealthCheck, type ServiceLifecycle, type ServiceOutputEvent, type ServiceProcessConfig, type ServiceProvides, type ServiceState, type ServiceStatus, type ServiceStatusEvent, type SettingContribution, type SettingsPanelProps, ShortcutManager, type SidebarContribution, type SlackConnectorOptions, type SlashCommandContext, type SlashCommandContribution, type StatusBarContribution, type TelemetryReporter, type TestHarnessOptions, type TestNodeStore, type TestPluginHarness, type TextHelpers, type ToolCallingFidelity, type ToolbarContribution, TypedRegistry, type UsageSignal, type ValidationResult, type VerifyProvenanceInput, type ViewContribution, type ViewProps, type WebLLMEngineLike, WebLLMProvider, type WebLLMProviderOptions, type WebhookConfig, WebhookEmitter, type WebhookOutOptions, type WebhookPayload, type WidgetContribution, type WidgetContributionConfigField, type WidgetContributionProps, type WorkspaceCommand, type WrapCliConnectorOptions, type WriteMode, XNET_MARKDOWN_DIRECTIVE_SPECS, type XNetExtension, type XNetMarkdownDiffLine, type XNetMarkdownDiffLineKind, type XNetMarkdownDirective, type XNetMarkdownDirectiveSpec, type XNetMarkdownReviewDiff, type XNetPageMarkdownFrontmatter, type XNetPageMarkdownValidation, type XNetPageMarkdownValidationOptions, agentToolToExtraTool, agentToolsAsExtraTools, aggregateRatings, assertNetwork, assertPublicUrl, assertSchemaWrite, assistTurnProvenance, attachAiPlanValidation, buildAirtableConnector, buildDiscordAction, buildEmailAction, buildGithubConnector, buildLinearConnector, buildNotionConnector, buildRetryPrompt, buildRssConnector, buildScriptPrompt, buildSlackConnector, buildSlackWebhookAction, buildTelegramAction, buildWebhookOutAction, classifyAiAgentDisplayState, compareVersions, composeAssistSystemPrompt, connectorAsImporter, connectorMarketplaceEntry, contributionsAsAiTools, createAIProvider, createAIProviderRouter, createAiAgentRuntime, createAiChangeSet, createAiOperation, createAiSurfaceService, createAiValidationResult, createCanvasErpPrototypeRiskSummary, createCanvasErpPrototypeScenario, createCanvasPluginFixtureCards, createCanvasPluginFixtureManifests, createCanvasPluginPermissionPrompt, createCanvasPluginSandboxPolicy, createCanvasPreviewSandboxRequest, createCanvasRendererSandboxRequest, createExtensionContext, createExtensionStorage, createManagedProvider, createMemoryAiAgentRuntimeStorage, createPromptApiProvider, createScriptContext, createServiceClient, createTestNodeStore, createTestPluginHarness, createWebLLMProvider, createWebhookEmitter, defaultLocalServerProbes, defineAction, defineConnector, defineExtension, defineFeatureModule, deleteDay, describeCapabilities, detectConnectors, downloadPromptApiModel, emitConnectorArtifacts, evaluateCanvasPluginPermissionGate, evaluateCanvasPluginSandboxRequest, evaluateConnectorInstall, evaluateInstallConsent, executeScript, failClosedVerifier, filterByCategory, findEditorSchemaRisks, findMissingDependencies, generateScript, getCanvasErpPrototypeAuditEntriesForCard, getCanvasErpPrototypeCardsForFrame, getCanvasPluginFixture, getCommandRegistry, getPlatformCapabilities, getShortcutManager, getXNetMarkdownDirectiveSpecs, guardStore, guardedActionFetch, guardedFetch, hasUpdate, importerAdapters, installCommandHandler, installShortcutHandler, isAiRiskLevel, isAiScope, isAiTargetKind, isAllowedPluginLicense, isHostCompatible, isNetworkAllowed, isOllamaAvailable, isPaidPricing, isSchemaDefiningExtension, isSchemaReadAllowed, isSchemaWriteAllowed, isScriptNode, isServiceClientAvailable, ladderTierForTrust, leaveWithEverything, listOllamaModels, matchSchemaIri, normalizeCanvasPluginWorkspacePolicy, packageName, parseAiMutationPlan, parseFeed, parseVersion, parseXNetPageFrontmatter, pascalCase, pickBestConnector, pluginLicenseText, probeOpenAiCompatible, promptApiAvailability, quickSafetyCheck, recommendExtensions, renderEvent, renderMarkdownLineDiff, renderMarkdownReviewDiff, renderSelectionPrompt, resolveImporters, resolveInstallOrder, resolveMentionProviders, runAction, runAiPluginPipeline, runConnectorSync, runPluginCode, satisfiesRange, scaffoldPlugin, scriptToPluginManifest, searchMarketplace, serializeAiMutationPlan, shortSchemaName, shouldDispatch, sortMarketplace, stripXNetPageFrontmatter, summarizeProvenance, validateAiMutationPlan, validateCanvasPluginSandboxOutput, validateManifest, validateScript, validateScriptAST, validateXNetPageMarkdown, verifyProvenance, warnOnEditorSchemaRisks, wrapCliConnector, writeModeFor };