@joeybuilt/plexo-sdk 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.
@@ -0,0 +1,1925 @@
1
+ /**
2
+ * PEX — Context Layer Types
3
+ * Corresponds to §7.7 of the Plexo Extension Protocol (PEX) Specification v0.4.0
4
+ *
5
+ * Push-based mechanism for extensions to contribute structured, prioritized
6
+ * context blocks injected into LLM system prompt at execution time.
7
+ */
8
+ /**
9
+ * A context block contributed by an extension.
10
+ * Declared in the extension manifest's `contexts` array or registered at runtime.
11
+ */
12
+ interface ContextArtifact {
13
+ /** Unique within the extension */
14
+ id: string;
15
+ /** Max 100 chars */
16
+ name: string;
17
+ /** Max 500 chars */
18
+ description: string;
19
+ /** Static text or {{variable}} template */
20
+ content: string;
21
+ /** MIME type; default: "text/plain" */
22
+ contentType?: string;
23
+ /** Injection priority */
24
+ priority: 'low' | 'normal' | 'high' | 'critical';
25
+ /** Seconds; undefined = no expiry */
26
+ ttl?: number;
27
+ /** Max 10, for discovery */
28
+ tags?: string[];
29
+ /** For budget allocation */
30
+ estimatedTokens?: number;
31
+ }
32
+ /**
33
+ * Registration payload passed to sdk.registerContext() during activate().
34
+ */
35
+ interface ContextRegistration {
36
+ id: string;
37
+ name: string;
38
+ description: string;
39
+ content: string;
40
+ contentType?: string;
41
+ priority: 'low' | 'normal' | 'high' | 'critical';
42
+ ttl?: number;
43
+ tags?: string[];
44
+ estimatedTokens?: number;
45
+ }
46
+ /**
47
+ * Summary returned by sdk.context.list().
48
+ */
49
+ interface ContextSummary {
50
+ id: string;
51
+ name: string;
52
+ description: string;
53
+ priority: 'low' | 'normal' | 'high' | 'critical';
54
+ ownerExtension: string;
55
+ enabled: boolean;
56
+ estimatedTokens?: number;
57
+ expired: boolean;
58
+ }
59
+
60
+ /**
61
+ * PEX — Prompt Library Types
62
+ * Corresponds to §7.6 of the Plexo Extension Protocol (PEX) Specification v0.4.0
63
+ *
64
+ * Extension-contributed prompt templates that are versioned, discovered,
65
+ * and resolved across extensions. Host maps these to LLM prompt primitives
66
+ * at runtime.
67
+ */
68
+ /**
69
+ * A prompt template contributed by an extension.
70
+ * Declared in the extension manifest's `prompts` array.
71
+ */
72
+ interface PromptArtifact {
73
+ /** Unique within the extension */
74
+ id: string;
75
+ /** Max 100 chars */
76
+ name: string;
77
+ /** Max 500 chars */
78
+ description: string;
79
+ /** Template text with {{variable}} placeholders */
80
+ template: string;
81
+ /** Variables the host must resolve before injection */
82
+ variables?: PromptVariable[];
83
+ /** Max 10, for discovery */
84
+ tags?: string[];
85
+ /** Semver, independent of extension version */
86
+ version: string;
87
+ /** Injection priority */
88
+ priority?: 'low' | 'normal' | 'high' | 'critical';
89
+ /** Format: "context:<name>:<id>" */
90
+ dependencies?: string[];
91
+ }
92
+ /**
93
+ * A variable declared in a prompt template.
94
+ * Host resolves all required variables before injection.
95
+ */
96
+ interface PromptVariable {
97
+ name: string;
98
+ description: string;
99
+ type: 'string' | 'number' | 'boolean' | 'enum';
100
+ /** Default: true */
101
+ required?: boolean;
102
+ default?: string | number | boolean;
103
+ enum?: string[];
104
+ }
105
+ /**
106
+ * Registration payload passed to sdk.registerPrompt() during activate().
107
+ */
108
+ interface PromptRegistration {
109
+ id: string;
110
+ name: string;
111
+ description: string;
112
+ template: string;
113
+ variables?: PromptVariable[];
114
+ tags?: string[];
115
+ version: string;
116
+ priority?: 'low' | 'normal' | 'high' | 'critical';
117
+ dependencies?: string[];
118
+ }
119
+ /**
120
+ * Summary returned by sdk.prompts.list().
121
+ */
122
+ interface PromptSummary {
123
+ id: string;
124
+ name: string;
125
+ description: string;
126
+ tags?: string[];
127
+ version: string;
128
+ priority: 'low' | 'normal' | 'high' | 'critical';
129
+ ownerExtension: string;
130
+ enabled: boolean;
131
+ }
132
+
133
+ /**
134
+ * §17 — Extension Trust Tiers
135
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
136
+ *
137
+ * Tier | Who | Capability Ceiling
138
+ * ---------|------------------------------------------|------------------------------------
139
+ * owner | Built and signed by the host operator | Full: memory:read:*, audit:read,
140
+ * | | entity creation, model:override
141
+ * verified | Reviewed and signed by Plexo extension registry | Standard caps, no wildcard memory,
142
+ * | | no audit access
143
+ * community| Unreviewed public extensions | Restricted caps, explicit user
144
+ * | | approval per capability token
145
+ *
146
+ * Rules:
147
+ * - Hosts MUST declare trust tier policy in their compliance declaration
148
+ * - plexo.json MAY declare trust: 'owner' — host validates against signing key
149
+ * - Capability tokens exceeding declared tier MUST be rejected at install, not runtime
150
+ */
151
+ type TrustTier = 'owner' | 'verified' | 'community';
152
+ interface TrustTierPolicy {
153
+ /** Default tier for extensions without explicit trust declaration */
154
+ defaultTier: TrustTier;
155
+ /** Whether the host enforces tier-based capability ceilings */
156
+ enforceTierCeilings: boolean;
157
+ /** Signing key fingerprint for owner-tier validation */
158
+ ownerSigningKeyId?: string;
159
+ /** Registry endpoint for verified-tier signature checks */
160
+ registryEndpoint?: string;
161
+ }
162
+ /**
163
+ * Capability ceilings per trust tier.
164
+ * Hosts use this to reject capabilities that exceed an extension's tier.
165
+ */
166
+ interface TrustTierCeilings {
167
+ owner: {
168
+ allowWildcardMemory: true;
169
+ allowAuditRead: true;
170
+ allowEntityCreation: true;
171
+ allowModelOverride: true;
172
+ };
173
+ verified: {
174
+ allowWildcardMemory: false;
175
+ allowAuditRead: false;
176
+ allowEntityCreation: true;
177
+ allowModelOverride: false;
178
+ };
179
+ community: {
180
+ allowWildcardMemory: false;
181
+ allowAuditRead: false;
182
+ allowEntityCreation: false;
183
+ allowModelOverride: false;
184
+ };
185
+ }
186
+
187
+ /**
188
+ * §19 — Data Residency Declaration
189
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
190
+ *
191
+ * All Extensions MUST declare external data destinations in their manifest.
192
+ *
193
+ * Rules:
194
+ * - Extensions with sendsDataExternally: false making external HTTP calls
195
+ * MUST be flagged non-compliant at runtime
196
+ * - Hosts MAY enforce an allowlist of permitted external destinations
197
+ * - Omitted dataResidency field treated as sendsDataExternally: true with
198
+ * unknown destinations — blocked at Full compliance
199
+ * - Declaration surfaced verbatim to users at install
200
+ */
201
+
202
+ interface ExternalDestination {
203
+ /** Hostname of the external service */
204
+ host: string;
205
+ /** Human-readable purpose */
206
+ purpose: string;
207
+ /** Entity types sent to this destination */
208
+ dataTypes?: EntityTypeName[];
209
+ }
210
+ interface DataResidencyDeclaration {
211
+ sendsDataExternally: boolean;
212
+ externalDestinations?: ExternalDestination[];
213
+ }
214
+
215
+ /**
216
+ * §23 — Human Oversight & Escalation Contract
217
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
218
+ *
219
+ * A formal escalation contract all Agent implementations MUST support.
220
+ * No formal spec for when an Agent must pause and request human approval
221
+ * is a critical governance gap for autonomous software touching email,
222
+ * calendar, and finances.
223
+ *
224
+ * Rules:
225
+ * - Hosts MUST implement at minimum IRREVERSIBLE_ACTION and CAPABILITY_EXPANSION triggers
226
+ * - Extensions MUST NOT bypass escalation by splitting irreversible actions
227
+ * into smaller reversible steps — host detects composite irreversibility
228
+ * - Standing approval rules are user-owned — Extensions cannot create or modify them
229
+ * - Escalation timeout: no user response within configured window → action denied and logged
230
+ */
231
+ type EscalationTrigger = 'HIGH_VALUE_ACTION' | 'IRREVERSIBLE_ACTION' | 'NOVEL_PATTERN' | 'CONFIDENCE_BELOW' | 'CROSS_BOUNDARY' | 'CAPABILITY_EXPANSION';
232
+ interface EscalationRequest {
233
+ trigger: EscalationTrigger;
234
+ /** The action the agent wants to take */
235
+ action: string;
236
+ /** Relevant context for the user to make a decision */
237
+ context: unknown;
238
+ /** Agent's recommendation (approve/deny) with reasoning */
239
+ recommendation?: {
240
+ decision: 'approve' | 'deny';
241
+ reasoning: string;
242
+ };
243
+ }
244
+ type EscalationUserResponse = 'approve' | 'deny' | 'approve-and-remember';
245
+ interface EscalationResult {
246
+ response: EscalationUserResponse;
247
+ /** User-provided feedback or instructions */
248
+ feedback?: string;
249
+ /** Timestamp of user response */
250
+ respondedAt: string;
251
+ }
252
+ interface StandingApproval {
253
+ id: string;
254
+ /** The trigger type this approval covers */
255
+ trigger: EscalationTrigger;
256
+ /** Pattern matching for the action (e.g. 'channel:send to @internal/*') */
257
+ actionPattern: string;
258
+ /** Who created this rule */
259
+ createdBy: 'user';
260
+ createdAt: string;
261
+ /** Optional expiration */
262
+ expiresAt?: string;
263
+ }
264
+ interface EscalationDeclaration {
265
+ /** Actions this extension considers irreversible */
266
+ irreversibleActions?: string[];
267
+ /** Whether this extension requests standing approval capability */
268
+ requestsStandingApprovals?: boolean;
269
+ }
270
+
271
+ /**
272
+ * §24 — LLM Identity & Model Context
273
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
274
+ *
275
+ * When an Agent acts, the LLM powering it must be visible to the audit trail,
276
+ * the user, and the system. Different models produce different decisions from
277
+ * the same Agent — making logs incomplete, behavior non-reproducible, and
278
+ * user consent uninformed.
279
+ *
280
+ * Rules:
281
+ * - If localModelAcceptable: false and host resolves to a cloud provider,
282
+ * the Agent's dataResidency MUST list that provider as an external destination
283
+ * - If host policy prohibits external model calls, Agents declaring
284
+ * localModelAcceptable: false MUST be rejected at install
285
+ * - Hosts MUST surface which model powers each active Agent in admin UI
286
+ * - Model changes require user re-acknowledgment for Agents with
287
+ * IRREVERSIBLE_ACTION escalation triggers
288
+ * - Capability token: model:override — required for Agents that dynamically
289
+ * select their own model at runtime
290
+ */
291
+ interface ModelRequirements {
292
+ minimumContextWindow?: number;
293
+ requiresFunctionCalling?: boolean;
294
+ localModelAcceptable?: boolean;
295
+ prohibitedProviders?: string[];
296
+ preferredProviders?: string[];
297
+ }
298
+ interface ModelContextEntry {
299
+ /** e.g. 'claude-sonnet-4-5', 'llama-3.3-70b' */
300
+ modelId: string;
301
+ /** Exact version or hash if self-hosted */
302
+ modelVersion?: string;
303
+ /** e.g. 'anthropic', 'openai', 'ollama', 'self-hosted' */
304
+ modelProvider: string;
305
+ /** true if on-host, false if external API */
306
+ isLocal: boolean;
307
+ /** Tokens used in this call */
308
+ contextWindowUsed?: number;
309
+ }
310
+
311
+ /**
312
+ * Extension subtypes — filter badges in host UI.
313
+ * 'skill' — composite capability package (registers tools, schedules, widgets)
314
+ * 'channel' — messaging bridge (inbound/outbound)
315
+ * 'tool' — stateless, single-purpose, called on demand
316
+ * 'connector' — bridges an external MCP server (formerly 'mcp-server')
317
+ */
318
+ type ExtensionSubtype = 'skill' | 'channel' | 'tool' | 'connector';
319
+ /**
320
+ * The manifest type field — covers both Extensions and Agents.
321
+ * Agent is a separate pillar but shares the manifest format.
322
+ */
323
+ type ManifestType = ExtensionSubtype | 'agent';
324
+ /**
325
+ * @deprecated Use ManifestType instead. Kept for backwards compatibility with pre-0.4.0 manifests.
326
+ */
327
+ type ExtensionType = ManifestType | 'function' | 'mcp-server';
328
+ /** Personal entity types defined in §16 */
329
+ type EntityTypeName = 'person' | 'task' | 'thread' | 'note' | 'transaction' | 'calendar_event' | 'file';
330
+ /**
331
+ * Capability tokens an extension or agent declares in its manifest.
332
+ *
333
+ * Memory capabilities are now entity-scoped (§4 edit):
334
+ * memory:read:<entity_type> — read access to a specific entity type
335
+ * memory:write:<entity_type> — write access to a specific entity type
336
+ *
337
+ * Unscoped memory:read / memory:write are DEPRECATED at Standard + Full compliance.
338
+ * memory:read:* wildcard is allowed only at trust tier: owner.
339
+ */
340
+ type CapabilityToken = `memory:read:${EntityTypeName}` | `memory:write:${EntityTypeName}` | 'memory:read:*' | 'memory:write:*' | 'memory:read' | 'memory:write' | 'memory:delete' | 'channel:send' | 'channel:send-direct' | 'channel:receive' | 'schedule:register' | 'schedule:manage' | 'ui:register-widget' | 'ui:notify' | 'tasks:create' | 'tasks:read' | 'tasks:read-all' | 'events:subscribe' | 'events:publish' | 'storage:read' | 'storage:write' | 'prompts:register' | 'prompts:read' | 'context:register' | 'context:write' | 'context:read' | `connections:${string}` | `host:${string}:${string}` | 'self:read' | 'self:write' | 'audit:read' | 'identity:present' | 'a2a:delegate' | 'model:override' | `entity:create:${EntityTypeName}` | `entity:modify:${EntityTypeName}` | `entity:delete:${EntityTypeName}`;
341
+ type HostComplianceLevel = 'core' | 'standard' | 'full';
342
+ interface MCPServerConfig {
343
+ transport: 'stdio' | 'sse';
344
+ /** Required when transport is 'stdio' */
345
+ command?: string;
346
+ /** Required when transport is 'sse' */
347
+ url?: string;
348
+ }
349
+ interface AgentHints {
350
+ taskTypes?: string[];
351
+ minConfidence?: number;
352
+ }
353
+ interface ResourceHints {
354
+ maxMemoryMB?: number;
355
+ maxCpuShares?: number;
356
+ maxInvocationMs?: number;
357
+ }
358
+ interface JSONSchema {
359
+ type: string;
360
+ properties?: Record<string, JSONSchema>;
361
+ required?: string[];
362
+ description?: string;
363
+ default?: unknown;
364
+ enum?: unknown[];
365
+ items?: JSONSchema;
366
+ [key: string]: unknown;
367
+ }
368
+ interface BehaviorRuleDefinition {
369
+ key: string;
370
+ label: string;
371
+ description: string;
372
+ type: 'safety_constraint' | 'operational_rule' | 'communication_style' | 'domain_knowledge' | 'persona_trait' | 'tool_preference' | 'quality_gate';
373
+ defaultValue: {
374
+ type: 'boolean' | 'string' | 'number' | 'enum' | 'text_block' | 'json';
375
+ value: unknown;
376
+ options?: string[];
377
+ min?: number;
378
+ max?: number;
379
+ };
380
+ locked: boolean;
381
+ }
382
+ /**
383
+ * The Plexo extension manifest — shared by both Extensions and Agents.
384
+ *
385
+ * v0.4.0 additions:
386
+ * - prompts / contexts (§7.6, §7.7)
387
+ * - 'connector' type (replaces 'mcp-server')
388
+ * - 5-type model (agent, skill, channel, tool, connector)
389
+ *
390
+ * v0.3.0 additions:
391
+ * - trust (§17)
392
+ * - dataResidency (§19)
393
+ * - escalation (§23)
394
+ * - modelRequirements (§24)
395
+ * - did (§21)
396
+ */
397
+ interface ExtensionManifest {
398
+ /** PEX spec version this manifest targets. Must be valid semver. */
399
+ plexo: string;
400
+ /** Scoped package name. Must match @scope/name format. */
401
+ name: string;
402
+ /** Extension or Agent version. Must be valid semver. */
403
+ version: string;
404
+ /** Manifest type — 'function' | 'channel' | 'mcp-server' for Extensions, 'agent' for Agents. */
405
+ type: ManifestType;
406
+ /** Relative path to entry point from package root. */
407
+ entry: string;
408
+ /** Capability tokens this extension or agent requires. */
409
+ capabilities: CapabilityToken[];
410
+ /** Human-readable name shown in host UI and registry. Max 50 chars. */
411
+ displayName: string;
412
+ /** Short description. Max 280 characters. */
413
+ description: string;
414
+ /** Publisher name or organization. */
415
+ author: string;
416
+ /** SPDX license identifier. */
417
+ license: string;
418
+ minHostLevel?: HostComplianceLevel;
419
+ minPexVersion?: string;
420
+ homepage?: string;
421
+ repository?: string;
422
+ keywords?: string[];
423
+ icon?: string;
424
+ screenshots?: string[];
425
+ /** For connector type extensions (bridges an MCP server). */
426
+ mcpServer?: MCPServerConfig;
427
+ /** For agent type only. */
428
+ agentHints?: AgentHints;
429
+ /** For channel type only. Rendered as setup form. */
430
+ channelConfig?: JSONSchema;
431
+ /**
432
+ * Channel transport mode (channel type only).
433
+ * 'worker' — extension runs in a sandboxed worker with a channel adapter (default)
434
+ * 'api' — the app uses Plexo's chat API for transport; no worker needed
435
+ */
436
+ channelTransport?: 'worker' | 'api';
437
+ /** For skill type only. Rendered as settings form. */
438
+ skillConfig?: JSONSchema;
439
+ /** For tool type only. Rendered as settings form. */
440
+ toolConfig?: JSONSchema;
441
+ resourceHints?: ResourceHints;
442
+ peerExtensions?: string[];
443
+ behaviorRules?: BehaviorRuleDefinition[];
444
+ /** §7.6 — Prompt templates contributed by this extension. */
445
+ prompts?: PromptArtifact[];
446
+ /** §7.7 — Context blocks contributed by this extension. */
447
+ contexts?: ContextArtifact[];
448
+ /** §7.7 — Context dependencies required by this extension. */
449
+ contextDependencies?: string[];
450
+ /**
451
+ * §3.3 — Per-capability human-readable rationale surfaced in the install dialog.
452
+ *
453
+ * Keys MUST be entries in `capabilities[]`. Values are short (max 200 chars)
454
+ * plain-text explanations shown to the user alongside each requested capability
455
+ * so they can judge whether the request is appropriate.
456
+ *
457
+ * Enforcement:
458
+ * - `trust: 'owner' | 'verified'` — REQUIRED. Publish is rejected if any
459
+ * declared capability is missing a rationale entry (warning entries for
460
+ * universally-safe caps like `storage:read` are tolerated; see validator).
461
+ * - `trust: 'community'` — optional but strongly encouraged. UI renders a
462
+ * "no explanation provided" warning per capability without one.
463
+ * - `trust: 'local'` (sideload) — optional, never enforced.
464
+ */
465
+ capabilitiesRationale?: Record<string, string>;
466
+ /** §17 — Trust tier declaration. Host validates against signing key. */
467
+ trust?: TrustTier;
468
+ /** §19 — Data residency declaration. External data destinations. */
469
+ dataResidency?: DataResidencyDeclaration;
470
+ /** §21 — W3C Decentralized Identifier for cross-host identity. */
471
+ did?: string;
472
+ /** §23 — Escalation contract for human oversight. */
473
+ escalation?: EscalationDeclaration;
474
+ /** §24 — LLM model requirements for agent-type manifests. */
475
+ modelRequirements?: ModelRequirements;
476
+ /**
477
+ * @deprecated Use skillConfig instead. Kept for backwards compatibility with pre-0.4.0 manifests.
478
+ */
479
+ functionConfig?: JSONSchema;
480
+ }
481
+ /**
482
+ * An Agent Stack is a pre-configured Agent + Extension bundle.
483
+ * Hosts SHOULD surface these as saveable, nameable presets.
484
+ */
485
+ interface AgentStackManifest {
486
+ /** PEX spec version. */
487
+ plexo: string;
488
+ /** Scoped package name for the stack. */
489
+ name: string;
490
+ version: string;
491
+ displayName: string;
492
+ description: string;
493
+ author: string;
494
+ license: string;
495
+ /** The Agent manifest name this stack configures. */
496
+ agent: string;
497
+ /** Extension manifest names this Agent should load. */
498
+ extensions: string[];
499
+ /** Connection service identifiers required by this stack. */
500
+ connections: string[];
501
+ /** Optional pre-configured behavior rules for the Agent. */
502
+ behaviorOverrides?: Record<string, unknown>;
503
+ }
504
+
505
+ /**
506
+ * §16 — Personal Entity Schema
507
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
508
+ *
509
+ * Standard first-class personal entity types all PEX-compatible hosts
510
+ * must support. When multiple Extensions reference the same person, task,
511
+ * or thread, entity resolution prevents fragmentation.
512
+ *
513
+ * Rules:
514
+ * - Entity IDs are host-scoped UUIDs, stable across sessions
515
+ * - Extensions reference entities by ID — no duplicating entity data
516
+ * - Hosts MUST provide a resolution API: entities.resolve(type, id)
517
+ * and entities.search(type, query)
518
+ * - Extensions MAY create new entities with entity:create:<type> capability
519
+ * - Cross-entity linking uses linkedEntities[] — a typed reference array
520
+ */
521
+
522
+ interface LinkedEntity {
523
+ type: EntityTypeName;
524
+ id: string;
525
+ }
526
+ interface PersonEntity {
527
+ id: string;
528
+ name: string;
529
+ email?: string[];
530
+ phone?: string[];
531
+ tags?: string[];
532
+ source?: string;
533
+ linkedEntities?: LinkedEntity[];
534
+ }
535
+ interface TaskEntity {
536
+ id: string;
537
+ title: string;
538
+ status: string;
539
+ due?: string;
540
+ assignee?: string;
541
+ tags?: string[];
542
+ linkedEntities?: LinkedEntity[];
543
+ }
544
+ interface ThreadEntity {
545
+ id: string;
546
+ participants: string[];
547
+ subject?: string;
548
+ channel?: string;
549
+ lastActivity?: string;
550
+ linkedEntities?: LinkedEntity[];
551
+ }
552
+ interface NoteEntity {
553
+ id: string;
554
+ body: string;
555
+ tags?: string[];
556
+ createdAt: string;
557
+ linkedEntities?: LinkedEntity[];
558
+ }
559
+ interface TransactionEntity {
560
+ id: string;
561
+ amount: number;
562
+ currency: string;
563
+ direction: 'inbound' | 'outbound';
564
+ merchant?: string;
565
+ category?: string;
566
+ date: string;
567
+ tags?: string[];
568
+ linkedEntities?: LinkedEntity[];
569
+ }
570
+ interface CalendarEventEntity {
571
+ id: string;
572
+ title: string;
573
+ start: string;
574
+ end: string;
575
+ attendees?: string[];
576
+ location?: string;
577
+ tags?: string[];
578
+ linkedEntities?: LinkedEntity[];
579
+ }
580
+ interface FileEntity {
581
+ id: string;
582
+ name: string;
583
+ type: string;
584
+ mimeType?: string;
585
+ sizeBytes?: number;
586
+ capturedAt?: string;
587
+ source?: string;
588
+ tags?: string[];
589
+ storageUri?: string;
590
+ checksum?: string;
591
+ linkedEntities?: LinkedEntity[];
592
+ }
593
+ type PlexoEntity = PersonEntity | TaskEntity | ThreadEntity | NoteEntity | TransactionEntity | CalendarEventEntity | FileEntity;
594
+ /** Maps entity type names to their TypeScript interfaces */
595
+ interface EntityTypeMap {
596
+ person: PersonEntity;
597
+ task: TaskEntity;
598
+ thread: ThreadEntity;
599
+ note: NoteEntity;
600
+ transaction: TransactionEntity;
601
+ calendar_event: CalendarEventEntity;
602
+ file: FileEntity;
603
+ }
604
+ interface EntitySearchQuery {
605
+ text?: string;
606
+ tags?: string[];
607
+ limit?: number;
608
+ offset?: number;
609
+ }
610
+ interface EntitySearchResult<T extends PlexoEntity = PlexoEntity> {
611
+ entities: T[];
612
+ total: number;
613
+ hasMore: boolean;
614
+ }
615
+
616
+ /**
617
+ * §20 — Persistent UserSelf
618
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
619
+ *
620
+ * A host-managed UserSelf graph readable by owner and verified Extensions
621
+ * with field-level scoping. Persists across sessions and survives Extension uninstall.
622
+ *
623
+ * Rules:
624
+ * - UserSelf is host-owned — no Extension owns it
625
+ * - Extensions contribute via structured proposals: self.propose(...)
626
+ * - Host resolves conflicts (last-write, confidence-weighted, or user-confirmed)
627
+ * - Extensions read via self.read(fields[]) — field-level scoping
628
+ * - Capability tokens: self:read, self:write
629
+ * - Agents read UserSelf to personalize behavior across all their loaded Extensions
630
+ */
631
+ interface UserIdentity {
632
+ name?: string;
633
+ timezone?: string;
634
+ locale?: string;
635
+ primaryEmail?: string;
636
+ }
637
+ interface UserCommunicationStyle {
638
+ formality?: 'casual' | 'neutral' | 'formal';
639
+ verbosity?: 'concise' | 'moderate' | 'detailed';
640
+ preferredChannels?: string[];
641
+ }
642
+ interface UserContext {
643
+ summary: string;
644
+ lastUpdated: string;
645
+ }
646
+ type UserSelfField = 'identity' | 'preferences' | 'relationships' | 'contexts' | 'communicationStyle';
647
+ interface UserSelf {
648
+ identity: UserIdentity;
649
+ /** Extensible, typed key-value preferences */
650
+ preferences: Record<string, unknown>;
651
+ /** Person entity IDs, ranked by recency/frequency */
652
+ relationships: string[];
653
+ /** Named contexts: work, finance, health, etc. */
654
+ contexts: Record<string, UserContext>;
655
+ communicationStyle: UserCommunicationStyle;
656
+ }
657
+ interface UserSelfProposal {
658
+ field: UserSelfField;
659
+ /** Dot-path within the field, e.g. 'identity.timezone' */
660
+ path?: string;
661
+ value: unknown;
662
+ /** Extension proposing this change */
663
+ source: string;
664
+ /** 0–1 confidence in the proposed value */
665
+ confidence: number;
666
+ }
667
+ type UserSelfConflictResolution = 'last-write' | 'confidence-weighted' | 'user-confirmed';
668
+
669
+ /**
670
+ * §18 — Audit Trail Requirement
671
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
672
+ *
673
+ * Standard and Full compliance hosts MUST maintain an immutable audit ledger
674
+ * per Extension per session. Trust in autonomous behavior requires verifiability.
675
+ *
676
+ * Rules:
677
+ * - audit:read capability required for owner-tier Extensions to query the ledger
678
+ * - Ledger entries MUST be immutable — no Extension modifies or deletes its own trail
679
+ * - Hosts MUST surface audit logs in admin UI
680
+ * - Audit log data residency follows host's declared policy (§19)
681
+ */
682
+
683
+ type AuditAction = 'function_invoked' | 'memory_read' | 'memory_write' | 'channel_send' | 'schedule_fired' | 'entity_created' | 'entity_modified' | 'external_request' | 'escalation_triggered' | 'escalation_resolved';
684
+ type AuditOutcome = 'success' | 'failure' | 'denied';
685
+ type EscalationOutcome = 'approve' | 'deny' | 'approve-and-remember';
686
+ interface AuditEntry {
687
+ extensionId: string;
688
+ /** Which Agent invoked this Extension, if any */
689
+ agentId?: string;
690
+ sessionId: string;
691
+ /** ISO 8601 timestamp */
692
+ timestamp: string;
693
+ action: AuditAction;
694
+ /** Entity ID, function name, channel, or external URL */
695
+ target: string;
696
+ /** SHA-256 of input — not plaintext */
697
+ payloadHash: string;
698
+ outcome: AuditOutcome;
699
+ /** §24 — LLM model context for this action */
700
+ modelContext?: ModelContextEntry;
701
+ /** Escalation outcome if applicable */
702
+ escalationOutcome?: EscalationOutcome;
703
+ }
704
+ interface AuditQuery {
705
+ extensionId?: string;
706
+ agentId?: string;
707
+ sessionId?: string;
708
+ action?: AuditAction;
709
+ outcome?: AuditOutcome;
710
+ /** ISO 8601 range start */
711
+ from?: string;
712
+ /** ISO 8601 range end */
713
+ to?: string;
714
+ limit?: number;
715
+ offset?: number;
716
+ }
717
+ interface AuditQueryResult {
718
+ entries: AuditEntry[];
719
+ total: number;
720
+ hasMore: boolean;
721
+ }
722
+
723
+ /**
724
+ * §22 — A2A Bridge Layer
725
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
726
+ *
727
+ * Full compliance hosts MUST expose an A2A-compatible endpoint for each Agent,
728
+ * enabling external A2A clients to discover and invoke Plexo Agents as remote agents.
729
+ *
730
+ * Protocol stack:
731
+ * MCP : agent ↔ tool (already supported via MCP Server Extension type)
732
+ * A2A : agent ↔ agent (this section)
733
+ * PEX : defines how all of the above is packaged, permissioned, isolated, and managed
734
+ *
735
+ * Rules:
736
+ * - External agents treated as community trust tier unless presenting a VC elevating trust
737
+ * - Inbound tasks route through host's Task Router — same isolation and enforcement
738
+ * - Delegated tasks logged in audit trail with external agent's DID or endpoint
739
+ * - Requires a2a:delegate capability token for outbound delegation
740
+ */
741
+ /**
742
+ * A2A Agent Card generated from a Plexo Agent manifest.
743
+ * Conforms to the A2A (Agentic AI Foundation) Agent Card specification.
744
+ */
745
+ interface A2AAgentCard {
746
+ name: string;
747
+ description: string;
748
+ version: string;
749
+ /** A2A endpoint URL: https://host/a2a/agents/:id */
750
+ endpoint: string;
751
+ capabilities: Record<string, unknown>;
752
+ authentication: {
753
+ schemes: ('oauth2' | 'did' | 'api_key')[];
754
+ };
755
+ /** Plexo DID for this agent, if assigned */
756
+ plexoDID?: string;
757
+ }
758
+ /**
759
+ * A2A Task — represents work delegated between agents.
760
+ */
761
+ interface A2ATask {
762
+ id: string;
763
+ /** Human-readable task description */
764
+ description: string;
765
+ /** Structured input data */
766
+ input: unknown;
767
+ /** Expected output schema, if known */
768
+ expectedOutput?: Record<string, unknown>;
769
+ }
770
+ type A2ATaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
771
+ interface A2ATaskResult {
772
+ taskId: string;
773
+ status: A2ATaskStatus;
774
+ output?: unknown;
775
+ error?: string;
776
+ }
777
+ /**
778
+ * Inbound A2A request — external agent invoking a Plexo Agent.
779
+ */
780
+ interface A2AInboundRequest {
781
+ /** A2A Agent Card of the requesting agent */
782
+ sourceAgent: A2AAgentCard;
783
+ task: A2ATask;
784
+ /** Verifiable credential if presenting elevated trust */
785
+ credential?: unknown;
786
+ }
787
+ /**
788
+ * Outbound A2A delegation — Plexo Agent delegating to an external A2A agent.
789
+ * Requires a2a:delegate capability.
790
+ */
791
+ interface A2ADelegation {
792
+ /** A2A endpoint of the target agent */
793
+ targetEndpoint: string;
794
+ task: A2ATask;
795
+ /** Timeout in milliseconds for the delegated task */
796
+ timeoutMs?: number;
797
+ }
798
+
799
+ /**
800
+ * Plexo SDK Interface
801
+ * The complete API available to extensions and agents via the sdk parameter in activate()
802
+ * Corresponds to Appendix A of the Plexo Extension Protocol (PEX) Specification v0.4.0
803
+ */
804
+
805
+ type NotificationLevel = 'info' | 'warning' | 'error';
806
+ type ExtensionName = `@${string}/${string}`;
807
+ interface HostInfo {
808
+ pexVersion: string;
809
+ complianceLevel: HostComplianceLevel;
810
+ name: string;
811
+ version: string;
812
+ capabilities?: {
813
+ prompts?: boolean;
814
+ context?: boolean;
815
+ tokenBudgeting?: boolean;
816
+ };
817
+ }
818
+ interface MemoryEntry {
819
+ id: string;
820
+ content: string;
821
+ tags?: string[];
822
+ authorExtension: ExtensionName | 'host';
823
+ metadata?: Record<string, unknown>;
824
+ createdAt: number;
825
+ updatedAt: number;
826
+ ttl?: number;
827
+ /** §16 — Entity type this memory entry relates to, if any */
828
+ entityType?: EntityTypeName;
829
+ /** §16 — Entity ID this memory entry relates to, if any */
830
+ entityId?: string;
831
+ }
832
+ interface ConnectionCredentials {
833
+ type: 'api_key' | 'oauth2' | 'basic' | 'webhook';
834
+ data: Record<string, string>;
835
+ }
836
+ interface ScheduleRegistration {
837
+ name: string;
838
+ /** 5-field cron expression */
839
+ schedule: string;
840
+ /** IANA timezone string. Defaults to 'UTC'. */
841
+ timezone?: string;
842
+ handler(): Promise<void>;
843
+ }
844
+ interface WidgetRegistration {
845
+ name: string;
846
+ displayName: string;
847
+ displayType: 'metric' | 'chart' | 'list' | 'status' | 'custom';
848
+ /** Refresh interval in seconds */
849
+ refreshInterval: number;
850
+ dataHandler(config: unknown): Promise<unknown>;
851
+ }
852
+ interface ToolRegistration {
853
+ /** Alphanumeric and underscores. Unique within the extension. */
854
+ name: string;
855
+ /** Max 500 characters. Shown to agents. */
856
+ description: string;
857
+ /** Must be type "object" at top level. */
858
+ parameters: JSONSchema;
859
+ hints?: {
860
+ estimatedMs?: number;
861
+ /** Hard timeout in ms. Host will abort if exceeded. Defaults to 30_000. */
862
+ timeoutMs?: number;
863
+ hasSideEffects?: boolean;
864
+ idempotent?: boolean;
865
+ };
866
+ handler(params: unknown, context: InvokeContext): Promise<unknown>;
867
+ }
868
+ interface InvokeContext {
869
+ workspaceId: string;
870
+ taskId?: string;
871
+ requestId: string;
872
+ /** Better Auth tenant identifier (multi-tenant scoping). */
873
+ tenantId: string;
874
+ /** Authenticated user the invocation is acting on behalf of. */
875
+ userId: string;
876
+ /** ADR-09 — per-tool OAuth account override (optional). */
877
+ accountId?: string;
878
+ /** Conversation continuity identifier across multi-turn flows (optional). */
879
+ sessionId?: string;
880
+ /** ADR-04 — links to run_id for cross-system tracing. */
881
+ traceId: string;
882
+ }
883
+ interface ToolSummary {
884
+ name: string;
885
+ description: string;
886
+ ownerExtension: string;
887
+ }
888
+ interface TaskCreateOptions {
889
+ title: string;
890
+ type: string;
891
+ context?: unknown;
892
+ }
893
+ interface TaskFilter {
894
+ status?: string;
895
+ type?: string;
896
+ }
897
+ /**
898
+ * The complete Plexo SDK interface.
899
+ * All extension and agent types receive this in their activate() call.
900
+ * Methods only work if the corresponding capability is declared in plexo.json.
901
+ *
902
+ * v0.4.0 additions:
903
+ * - registerPrompt / registerContext (§7.6, §7.7)
904
+ * - prompts.* (§7.6 — Prompt Library)
905
+ * - context.* (§7.7 — Context Layer)
906
+ *
907
+ * v0.3.0 additions:
908
+ * - entities.* (§16 — Personal Entity Schema)
909
+ * - self.* (§20 — Persistent UserSelf)
910
+ * - audit.* (§18 — Audit Trail)
911
+ * - escalate() (§23 — Escalation Contract)
912
+ * - a2a.* (§22 — A2A Bridge Layer)
913
+ */
914
+ interface PlexoSDK {
915
+ /** Information about the host runtime */
916
+ host: HostInfo;
917
+ /**
918
+ * Registration methods. Only valid during activate().
919
+ * Calling after activation completes is a no-op with a warning.
920
+ */
921
+ registerTool(tool: ToolRegistration): void;
922
+ registerSchedule(job: ScheduleRegistration): void;
923
+ registerWidget(widget: WidgetRegistration): void;
924
+ /** §7.6 — Requires prompts:register */
925
+ registerPrompt(prompt: PromptRegistration): void;
926
+ /** §7.7 — Requires context:register */
927
+ registerContext(context: ContextRegistration): void;
928
+ memory: {
929
+ /** Requires memory:read or memory:read:<entity_type> */
930
+ read(query: string, options?: {
931
+ tags?: string[];
932
+ limit?: number;
933
+ /** §16 — Filter to a specific entity type */
934
+ entityType?: EntityTypeName;
935
+ }): Promise<MemoryEntry[]>;
936
+ /** Requires memory:write or memory:write:<entity_type> */
937
+ write(entry: {
938
+ content: string;
939
+ tags?: string[];
940
+ metadata?: Record<string, unknown>;
941
+ ttl?: number;
942
+ /** §16 — Associate with an entity type */
943
+ entityType?: EntityTypeName;
944
+ /** §16 — Associate with an entity ID */
945
+ entityId?: string;
946
+ }): Promise<MemoryEntry>;
947
+ /** Requires memory:delete */
948
+ delete(id: string): Promise<void>;
949
+ };
950
+ entities: {
951
+ /**
952
+ * Resolve a single entity by type and ID.
953
+ * Requires memory:read:<entity_type>
954
+ */
955
+ resolve<T extends EntityTypeName>(type: T, id: string): Promise<EntityTypeMap[T] | null>;
956
+ /**
957
+ * Search for entities of a given type.
958
+ * Requires memory:read:<entity_type>
959
+ */
960
+ search<T extends EntityTypeName>(type: T, query: EntitySearchQuery): Promise<EntitySearchResult<EntityTypeMap[T]>>;
961
+ /**
962
+ * Create a new entity.
963
+ * Requires entity:create:<entity_type>
964
+ */
965
+ create<T extends EntityTypeName>(type: T, data: Omit<EntityTypeMap[T], 'id'>): Promise<EntityTypeMap[T]>;
966
+ /**
967
+ * Link two entities together.
968
+ * Requires entity:modify:<entity_type> for the source entity.
969
+ */
970
+ link(source: {
971
+ type: EntityTypeName;
972
+ id: string;
973
+ }, target: LinkedEntity): Promise<void>;
974
+ };
975
+ connections: {
976
+ /** Requires connections:<service> */
977
+ getCredentials(service: string): Promise<ConnectionCredentials>;
978
+ isConnected(service: string): Promise<boolean>;
979
+ };
980
+ channel: {
981
+ /** Requires channel:send */
982
+ send(message: {
983
+ text: string;
984
+ priority?: 'normal' | 'high' | 'urgent';
985
+ attachments?: unknown[];
986
+ }): Promise<void>;
987
+ /** Requires channel:send-direct */
988
+ sendDirect(channelId: string, message: unknown): Promise<void>;
989
+ /**
990
+ * §9 — Multi-channel dispatch with idempotency (ADR-15) and per-tool scope override.
991
+ * Requires channel:dispatch.
992
+ * `channel` is one of: 'telegram' | 'email' | 'push' | 'sms'.
993
+ * `idempotencyKey` MUST be unique per logical send; same key returns the prior result.
994
+ * `scopeOverrides` narrows OAuth scopes for this dispatch (ADR-09).
995
+ */
996
+ dispatch(params: {
997
+ channel: string;
998
+ recipientUserId: string;
999
+ message: {
1000
+ text: string;
1001
+ attachments?: unknown[];
1002
+ metadata?: Record<string, unknown>;
1003
+ };
1004
+ idempotencyKey: string;
1005
+ scopeOverrides?: string[];
1006
+ }): Promise<{
1007
+ messageId?: string;
1008
+ deliveryStatus?: string;
1009
+ }>;
1010
+ };
1011
+ tasks: {
1012
+ /** Requires tasks:create (agent type only) */
1013
+ create(options: TaskCreateOptions): Promise<{
1014
+ taskId: string;
1015
+ }>;
1016
+ /** Requires tasks:read */
1017
+ get(taskId: string): Promise<unknown>;
1018
+ /** Requires tasks:read-all */
1019
+ list(filter?: TaskFilter): Promise<unknown[]>;
1020
+ };
1021
+ events: {
1022
+ /** Requires events:subscribe */
1023
+ subscribe(topic: string, handler: (payload: unknown) => void): void;
1024
+ /** Requires events:publish — namespace enforced to ext.<scope>.* */
1025
+ publish(topic: string, payload: unknown): Promise<void>;
1026
+ };
1027
+ storage: {
1028
+ /** Requires storage:read */
1029
+ get(key: string): Promise<string | null>;
1030
+ /** Requires storage:write */
1031
+ set(key: string, value: string, options?: {
1032
+ ttlSeconds?: number;
1033
+ }): Promise<void>;
1034
+ /** Requires storage:write */
1035
+ delete(key: string): Promise<void>;
1036
+ };
1037
+ ui: {
1038
+ /** Requires ui:notify */
1039
+ notify(message: string, level?: NotificationLevel): Promise<void>;
1040
+ };
1041
+ self: {
1042
+ /** Requires self:read — field-level scoping */
1043
+ read(fields: UserSelfField[]): Promise<Partial<UserSelf>>;
1044
+ /** Requires self:write — contribute via structured proposals */
1045
+ propose(proposal: UserSelfProposal): Promise<void>;
1046
+ };
1047
+ audit: {
1048
+ /** Requires audit:read — query the immutable audit ledger */
1049
+ query(query: AuditQuery): Promise<AuditQueryResult>;
1050
+ };
1051
+ /**
1052
+ * Signal that the agent needs human approval before proceeding.
1053
+ * Host pauses execution, notifies user, and returns the result.
1054
+ * Requires agent type.
1055
+ */
1056
+ escalate(request: EscalationRequest): Promise<EscalationResult>;
1057
+ a2a: {
1058
+ /**
1059
+ * Discover external A2A agents by endpoint.
1060
+ * Requires a2a:delegate
1061
+ */
1062
+ discover(endpoint: string): Promise<A2AAgentCard | null>;
1063
+ /**
1064
+ * Delegate a task to an external A2A agent.
1065
+ * Requires a2a:delegate. Logged in audit trail.
1066
+ */
1067
+ delegate(delegation: A2ADelegation): Promise<A2ATaskResult>;
1068
+ };
1069
+ prompts: {
1070
+ /** Requires prompts:read */
1071
+ list(options?: {
1072
+ tags?: string[];
1073
+ }): Promise<PromptSummary[]>;
1074
+ /** Requires prompts:read — resolve template with variables */
1075
+ resolve(promptId: string, variables?: Record<string, unknown>): Promise<string>;
1076
+ };
1077
+ context: {
1078
+ /** Requires context:write — update content for a registered context block */
1079
+ update(contextId: string, content: string, options?: {
1080
+ ttl?: number;
1081
+ estimatedTokens?: number;
1082
+ }): Promise<void>;
1083
+ /** Requires context:read */
1084
+ list(): Promise<ContextSummary[]>;
1085
+ };
1086
+ }
1087
+
1088
+ /**
1089
+ * PEX Message Protocol Types
1090
+ * Corresponds to §6 of the Plexo Extension Protocol (PEX) Specification v0.4.0
1091
+ */
1092
+ type MessageType = 'invoke' | 'invoke_result' | 'event' | 'channel_message' | 'channel_send' | 'channel_health_check' | 'channel_health_result' | 'activate' | 'deactivate' | 'error' | 'escalation_request' | 'escalation_response' | 'entity_resolve' | 'entity_resolve_result' | 'a2a_inbound' | 'a2a_outbound' | 'self_read' | 'self_propose' | 'audit_query';
1093
+ type ErrorCode = 'CAPABILITY_DENIED' | 'TOOL_NOT_FOUND' | 'TIMEOUT' | 'INTERNAL_ERROR' | 'INVALID_PARAMS' | 'COMPLIANCE_INSUFFICIENT' | 'NOT_IMPLEMENTED' | 'EXTENSION_CRASHED' | 'TRUST_TIER_EXCEEDED' | 'DATA_RESIDENCY_VIOLATION' | 'ESCALATION_DENIED' | 'ESCALATION_TIMEOUT' | 'ENTITY_NOT_FOUND' | 'DID_VERIFICATION_FAILED' | 'MODEL_REQUIREMENTS_UNMET' | 'SCOPED_MEMORY_REQUIRED';
1094
+ interface PexMessage {
1095
+ /** PEX spec version */
1096
+ plexo: '0.4.0';
1097
+ /** Unique message ID */
1098
+ id: string;
1099
+ type: MessageType;
1100
+ /** ISO 8601 timestamp */
1101
+ timestamp: string;
1102
+ payload: unknown;
1103
+ }
1104
+ interface PexError {
1105
+ code: ErrorCode;
1106
+ message: string;
1107
+ /** Original error detail — stripped in production */
1108
+ detail?: string;
1109
+ retryable: boolean;
1110
+ }
1111
+ type MessagePriority = 'normal' | 'high' | 'urgent';
1112
+ interface Attachment {
1113
+ type: 'image' | 'file' | 'link';
1114
+ url: string;
1115
+ name?: string;
1116
+ mimeType?: string;
1117
+ }
1118
+ interface InboundMessage {
1119
+ id: string;
1120
+ channelId: string;
1121
+ senderId: string;
1122
+ text: string;
1123
+ priority: MessagePriority;
1124
+ attachments?: Attachment[];
1125
+ replyToId?: string;
1126
+ timestamp: string;
1127
+ }
1128
+
1129
+ /**
1130
+ * PEX Agent Contract Types
1131
+ * Corresponds to §8 of the Plexo Extension Protocol (PEX) Specification v0.4.0
1132
+ *
1133
+ * An Agent is NOT a subtype of Extension. An Agent is an autonomous actor
1134
+ * with a goal, a planning loop, and an identity. It orchestrates any number
1135
+ * of Extensions to accomplish work.
1136
+ *
1137
+ * §23 integration: Agents MUST implement escalate() for human oversight.
1138
+ * §22 integration: Agents MAY delegate to external A2A agents.
1139
+ * §24 integration: Agent actions are logged with model context in the audit trail.
1140
+ */
1141
+
1142
+ type OneWayDoorType = 'irreversible_action' | 'external_write' | 'financial_transaction' | 'data_deletion' | 'permission_escalation';
1143
+ interface OneWayDoor {
1144
+ type: OneWayDoorType;
1145
+ description: string;
1146
+ /** What the agent intends to do */
1147
+ action: string;
1148
+ /** What happens if user approves */
1149
+ consequence: string;
1150
+ /** Whether this can be undone */
1151
+ reversible: false;
1152
+ }
1153
+ type EscalationReason = 'ambiguous_goal' | 'one_way_door' | 'low_confidence' | 'missing_capability' | 'recovery_needed' | 'max_retries_exceeded' | 'high_value_action' | 'irreversible_action' | 'novel_pattern' | 'confidence_below_threshold' | 'cross_boundary' | 'capability_expansion';
1154
+ interface ToolCall {
1155
+ tool: string;
1156
+ params: Record<string, unknown>;
1157
+ }
1158
+ interface PlanStep {
1159
+ id: string;
1160
+ description: string;
1161
+ toolCall?: ToolCall;
1162
+ /** If true, requires approval before execution */
1163
+ oneWayDoor?: OneWayDoor;
1164
+ dependsOn?: string[];
1165
+ /** §23 — Escalation trigger type, if this step requires escalation */
1166
+ escalationTrigger?: EscalationTrigger;
1167
+ }
1168
+ interface Plan {
1169
+ steps: PlanStep[];
1170
+ /** Estimated total duration in seconds */
1171
+ estimatedSeconds?: number;
1172
+ confidence: number;
1173
+ }
1174
+ interface StepResult {
1175
+ stepId: string;
1176
+ ok: boolean;
1177
+ output?: unknown;
1178
+ error?: string;
1179
+ durationMs: number;
1180
+ }
1181
+ interface ShouldActivateResult {
1182
+ activate: boolean;
1183
+ confidence: number;
1184
+ reasoning?: string;
1185
+ }
1186
+ interface EscalationResponse {
1187
+ approved: boolean;
1188
+ feedback?: string;
1189
+ }
1190
+ interface AgentExtension {
1191
+ /**
1192
+ * Called for every new task. Return { activate: true, confidence: 0–1 }
1193
+ * to claim the task. Host routes to highest-confidence claimant.
1194
+ */
1195
+ shouldActivate(task: {
1196
+ title: string;
1197
+ type: string;
1198
+ context?: unknown;
1199
+ }): Promise<ShouldActivateResult>;
1200
+ /**
1201
+ * Called if agent wins the routing competition.
1202
+ * Must return a plan — array of steps to execute.
1203
+ */
1204
+ plan(task: {
1205
+ title: string;
1206
+ type: string;
1207
+ context?: unknown;
1208
+ }): Promise<Plan>;
1209
+ /**
1210
+ * Called once per step by the host executor.
1211
+ */
1212
+ executeStep(step: PlanStep, context: {
1213
+ workspaceId: string;
1214
+ taskId: string;
1215
+ }): Promise<StepResult>;
1216
+ /**
1217
+ * Called after each step. Return { ok: true } to continue, { ok: false } to escalate.
1218
+ */
1219
+ verifyStep(result: StepResult): Promise<{
1220
+ ok: boolean;
1221
+ reason?: string;
1222
+ }>;
1223
+ /**
1224
+ * §23 — Called when escalation is required.
1225
+ * Agent MUST pause and wait for host to relay user response.
1226
+ * Hosts MUST implement at minimum IRREVERSIBLE_ACTION and CAPABILITY_EXPANSION triggers.
1227
+ */
1228
+ onEscalation(reason: EscalationReason, context: unknown): Promise<EscalationResponse>;
1229
+ }
1230
+
1231
+ /**
1232
+ * PEX Channel Contract Types
1233
+ * Corresponds to §2.3 and §9.2 of the Plexo Extension Protocol (PEX) Specification v0.4.0
1234
+ */
1235
+
1236
+ interface ChannelHealthResult {
1237
+ healthy: boolean;
1238
+ latencyMs?: number;
1239
+ error?: string;
1240
+ }
1241
+ interface ChannelSendResult {
1242
+ ok: boolean;
1243
+ messageId?: string;
1244
+ error?: string;
1245
+ }
1246
+ interface ChannelExtension {
1247
+ /**
1248
+ * Called on activate. Channel should start listening for inbound messages
1249
+ * and call sdk.channel.send() to route them into the host.
1250
+ */
1251
+ onActivate(): Promise<void>;
1252
+ /**
1253
+ * Called when the host wants to send a message via this channel.
1254
+ * Requires channel:receive capability.
1255
+ */
1256
+ onMessage(message: InboundMessage): Promise<void>;
1257
+ /**
1258
+ * Called periodically by the host Channel Router.
1259
+ * Return healthy: false to trigger failover.
1260
+ */
1261
+ healthCheck(): Promise<ChannelHealthResult>;
1262
+ /**
1263
+ * Called on deactivate. Channel should stop listeners and clean up.
1264
+ */
1265
+ onDeactivate(): Promise<void>;
1266
+ }
1267
+ type PexVersion = '0.4.0';
1268
+ declare const PEX_VERSION: PexVersion;
1269
+ /** Channel runtime types known to Plexo. Mirrors `channel_type` enum. */
1270
+ type ChannelType = 'telegram' | 'slack' | 'discord' | 'whatsapp' | 'signal' | 'matrix' | 'irc' | 'webchat' | 'twilio' | 'gmail' | 'gmessages';
1271
+ /** Connection state machine surfaced to subscribers (ADR-0004 + ADR-0005). */
1272
+ type ConnectionState = 'paired' | 'active' | 'refreshing' | 'expired' | 'revoked' | 'errored';
1273
+ interface ChannelAttachmentRef {
1274
+ /** Plexo-side attachment ID, resolvable via the attachment store. */
1275
+ id: string;
1276
+ /** MIME type if known. */
1277
+ mimeType?: string;
1278
+ /** Display filename. */
1279
+ filename?: string;
1280
+ /** Size in bytes if known. */
1281
+ sizeBytes?: number;
1282
+ /** Optional thumbnail attachment ID for image/video previews. */
1283
+ thumbnailId?: string;
1284
+ }
1285
+ /** Canonical message envelope used by every Channel subscriber. */
1286
+ interface ChannelMessage {
1287
+ id: string;
1288
+ channelId: string;
1289
+ threadId: string;
1290
+ direction: 'inbound' | 'outbound';
1291
+ /** Plain text body. Markdown is not parsed by Plexo's generic viewer. */
1292
+ text: string;
1293
+ attachments?: ChannelAttachmentRef[];
1294
+ /** Sender identifier in the channel's native namespace (phone number, handle, etc.). */
1295
+ senderId: string;
1296
+ /** Display name if the channel resolves one. */
1297
+ senderName?: string;
1298
+ /** ISO-8601 timestamp when the message was authored upstream. */
1299
+ sentAt: string;
1300
+ /** Channel-specific metadata — RCS feature flags, read receipts, etc. */
1301
+ metadata?: Record<string, unknown>;
1302
+ pexVersion: PexVersion;
1303
+ }
1304
+ interface ChannelThread {
1305
+ id: string;
1306
+ channelId: string;
1307
+ /** Display title. For 1:1 threads this is typically the contact name or phone. */
1308
+ title: string;
1309
+ /** Most recent message preview (text-only, truncated). */
1310
+ lastMessagePreview?: string;
1311
+ lastMessageAt?: string;
1312
+ unreadCount?: number;
1313
+ /** Channel-specific metadata (e.g., is_rcs flag for gmessages). */
1314
+ metadata?: Record<string, unknown>;
1315
+ pexVersion: PexVersion;
1316
+ }
1317
+ interface ChannelDescriptor {
1318
+ id: string;
1319
+ workspaceId: string;
1320
+ type: ChannelType;
1321
+ name: string;
1322
+ state: ConnectionState;
1323
+ enabled: boolean;
1324
+ lastMessageAt?: string;
1325
+ pexVersion: PexVersion;
1326
+ }
1327
+ /** Connection (credential) descriptor — paired-session metadata, no secrets. */
1328
+ interface PairedConnectionDescriptor {
1329
+ id: string;
1330
+ workspaceId: string;
1331
+ registryId: string;
1332
+ state: ConnectionState;
1333
+ pairedAt?: string;
1334
+ lastInboundAt?: string;
1335
+ decodeErrorCount?: number;
1336
+ pexVersion: PexVersion;
1337
+ }
1338
+ type ChannelEvent = {
1339
+ type: 'message.received';
1340
+ channelId: string;
1341
+ threadId: string;
1342
+ message: ChannelMessage;
1343
+ pexVersion: PexVersion;
1344
+ } | {
1345
+ type: 'message.sent';
1346
+ channelId: string;
1347
+ threadId: string;
1348
+ message: ChannelMessage;
1349
+ pexVersion: PexVersion;
1350
+ } | {
1351
+ type: 'connection.state_changed';
1352
+ channelId: string;
1353
+ state: ConnectionState;
1354
+ pexVersion: PexVersion;
1355
+ };
1356
+ interface ChannelSubscription {
1357
+ id: string;
1358
+ appId: string;
1359
+ channelId: string;
1360
+ scopes: ChannelScope[];
1361
+ createdAt: string;
1362
+ }
1363
+ type ChannelScope = 'channels:list' | 'channels:subscribe' | 'channels:read' | 'channels:send' | 'channels:events';
1364
+ interface ChannelSendRequest {
1365
+ text: string;
1366
+ attachments?: ChannelAttachmentRef[];
1367
+ /** Caller-supplied idempotency token; Plexo dedupes within a 24h window. */
1368
+ idempotencyKey: string;
1369
+ }
1370
+ interface ChannelMessagePage {
1371
+ messages: ChannelMessage[];
1372
+ nextCursor?: string;
1373
+ }
1374
+ interface ChannelThreadPage {
1375
+ threads: ChannelThread[];
1376
+ nextCursor?: string;
1377
+ }
1378
+
1379
+ interface ChannelClientOptions {
1380
+ /** Plexo Core base URL (e.g. https://plexo.example.com). No trailing slash required. */
1381
+ baseUrl: string;
1382
+ /** PLEXO_SERVICE_KEY shared secret used for HMAC body signatures. */
1383
+ serviceKey: string;
1384
+ /** App identity, mirrored back to Plexo via X-App-Id for scope enforcement. */
1385
+ appId: string;
1386
+ /** Optional fetch override (testing, custom retry). */
1387
+ fetchImpl?: typeof fetch;
1388
+ }
1389
+ interface ChannelClient {
1390
+ list(opts?: {
1391
+ workspaceId?: string;
1392
+ }): Promise<ChannelDescriptor[]>;
1393
+ subscribe(channelId: string, scopes: ChannelScope[]): Promise<ChannelSubscription>;
1394
+ unsubscribe(channelId: string, subscriptionId: string): Promise<void>;
1395
+ threads(channelId: string, opts?: {
1396
+ cursor?: string;
1397
+ limit?: number;
1398
+ }): Promise<ChannelThreadPage>;
1399
+ messages(channelId: string, threadId: string, opts?: {
1400
+ cursor?: string;
1401
+ limit?: number;
1402
+ }): Promise<ChannelMessagePage>;
1403
+ send(channelId: string, threadId: string, payload: ChannelSendRequest): Promise<ChannelMessage>;
1404
+ /**
1405
+ * Async iterator over the SSE event stream. The generator reconnects with
1406
+ * `Last-Event-ID` on transient drops; callers may abort via the
1407
+ * `AbortSignal` parameter.
1408
+ */
1409
+ events(channelId: string, opts?: {
1410
+ signal?: AbortSignal;
1411
+ lastEventId?: string;
1412
+ }): AsyncIterable<ChannelEvent>;
1413
+ }
1414
+ declare function createChannelClient(opts: ChannelClientOptions): ChannelClient;
1415
+
1416
+ /**
1417
+ * PEX Event Bus Types
1418
+ * Corresponds to §7.4 of the Plexo Extension Protocol (PEX) Specification v0.4.0
1419
+ *
1420
+ * Extensions may only publish to ext.<scope>.* namespace.
1421
+ * Standard topics are published by the host.
1422
+ *
1423
+ * v0.4.0 additions:
1424
+ * - prompt.* topics (§7.6)
1425
+ * - context.* topics (§7.7)
1426
+ *
1427
+ * v0.3.0 additions:
1428
+ * - entity.* topics (§16)
1429
+ * - escalation.* topics (§23)
1430
+ * - audit.* topics (§18)
1431
+ * - self.* topics (§20)
1432
+ * - agent.* topics (core architecture)
1433
+ * - a2a.* topics (§22)
1434
+ */
1435
+
1436
+ /** Standard topics published by the host. Extensions subscribe but cannot publish these. */
1437
+ declare const TOPICS: {
1438
+ readonly TASK_CREATED: "task.created";
1439
+ readonly TASK_COMPLETED: "task.completed";
1440
+ readonly TASK_FAILED: "task.failed";
1441
+ readonly TASK_BLOCKED: "task.blocked";
1442
+ readonly CHANNEL_MESSAGE_RECEIVED: "channel.message.received";
1443
+ readonly CHANNEL_HEALTH_CHANGED: "channel.health.changed";
1444
+ readonly EXTENSION_ACTIVATED: "extension.activated";
1445
+ readonly EXTENSION_DEACTIVATED: "extension.deactivated";
1446
+ readonly EXTENSION_CRASHED: "extension.crashed";
1447
+ readonly CONNECTION_ADDED: "connection.added";
1448
+ readonly CONNECTION_REMOVED: "connection.removed";
1449
+ readonly MEMORY_WRITTEN: "memory.written";
1450
+ readonly ENTITY_CREATED: "entity.created";
1451
+ readonly ENTITY_MODIFIED: "entity.modified";
1452
+ readonly ENTITY_DELETED: "entity.deleted";
1453
+ readonly ENTITY_LINKED: "entity.linked";
1454
+ readonly ESCALATION_TRIGGERED: "escalation.triggered";
1455
+ readonly ESCALATION_RESOLVED: "escalation.resolved";
1456
+ readonly ESCALATION_TIMED_OUT: "escalation.timed_out";
1457
+ readonly AUDIT_ENTRY_CREATED: "audit.entry.created";
1458
+ readonly SELF_UPDATED: "self.updated";
1459
+ readonly SELF_PROPOSAL_RECEIVED: "self.proposal.received";
1460
+ readonly AGENT_ACTIVATED: "agent.activated";
1461
+ readonly AGENT_DEACTIVATED: "agent.deactivated";
1462
+ readonly AGENT_PLAN_CREATED: "agent.plan.created";
1463
+ readonly AGENT_STEP_COMPLETED: "agent.step.completed";
1464
+ readonly AGENT_STEP_FAILED: "agent.step.failed";
1465
+ readonly A2A_INBOUND_RECEIVED: "a2a.inbound.received";
1466
+ readonly A2A_DELEGATION_SENT: "a2a.delegation.sent";
1467
+ readonly A2A_DELEGATION_COMPLETED: "a2a.delegation.completed";
1468
+ readonly PROMPT_REGISTERED: "prompt.registered";
1469
+ readonly PROMPT_ENABLED: "prompt.enabled";
1470
+ readonly CONTEXT_REGISTERED: "context.registered";
1471
+ readonly CONTEXT_UPDATED: "context.updated";
1472
+ readonly CONTEXT_EXPIRED: "context.expired";
1473
+ };
1474
+ type StandardTopic = (typeof TOPICS)[keyof typeof TOPICS];
1475
+ /**
1476
+ * Build an extension-scoped topic name.
1477
+ * Extensions MUST use this for events they publish.
1478
+ * @example customTopic('acme', 'stripe-monitor', 'mrr.updated')
1479
+ * // => 'ext.acme.stripe-monitor.mrr.updated'
1480
+ */
1481
+ declare function customTopic(scope: string, name: string, event: string): string;
1482
+ interface TaskCreatedPayload {
1483
+ taskId: string;
1484
+ title: string;
1485
+ type: string;
1486
+ workspaceId: string;
1487
+ }
1488
+ interface TaskCompletedPayload {
1489
+ taskId: string;
1490
+ durationMs: number;
1491
+ workspaceId: string;
1492
+ /** Domain mastery: inferred domain tag for this task. Null when domain mastery is off. */
1493
+ domainTag?: string | null;
1494
+ /** Quality score from the quality judge (0-1). */
1495
+ qualityScore?: number | null;
1496
+ /** Task type classification. */
1497
+ taskType?: string;
1498
+ }
1499
+ interface TaskFailedPayload {
1500
+ taskId: string;
1501
+ error: string;
1502
+ workspaceId: string;
1503
+ }
1504
+ interface TaskBlockedPayload {
1505
+ taskId: string;
1506
+ reason: string;
1507
+ workspaceId: string;
1508
+ }
1509
+ interface ChannelMessageReceivedPayload {
1510
+ channelId: string;
1511
+ messageId: string;
1512
+ senderId: string;
1513
+ }
1514
+ interface ChannelHealthChangedPayload {
1515
+ channelId: string;
1516
+ healthy: boolean;
1517
+ latencyMs?: number;
1518
+ }
1519
+ interface ExtensionActivatedPayload {
1520
+ name: string;
1521
+ version: string;
1522
+ type: string;
1523
+ workspaceId: string;
1524
+ }
1525
+ interface ExtensionDeactivatedPayload {
1526
+ name: string;
1527
+ workspaceId: string;
1528
+ }
1529
+ interface ExtensionCrashedPayload {
1530
+ name: string;
1531
+ error: string;
1532
+ workspaceId: string;
1533
+ }
1534
+ interface ConnectionAddedPayload {
1535
+ service: string;
1536
+ workspaceId: string;
1537
+ }
1538
+ interface ConnectionRemovedPayload {
1539
+ service: string;
1540
+ workspaceId: string;
1541
+ }
1542
+ interface MemoryWrittenPayload {
1543
+ id: string;
1544
+ tags?: string[];
1545
+ authorExtension: string;
1546
+ workspaceId: string;
1547
+ }
1548
+ interface EntityCreatedPayload {
1549
+ entityType: EntityTypeName;
1550
+ entityId: string;
1551
+ createdBy: string;
1552
+ workspaceId: string;
1553
+ }
1554
+ interface EntityModifiedPayload {
1555
+ entityType: EntityTypeName;
1556
+ entityId: string;
1557
+ modifiedBy: string;
1558
+ workspaceId: string;
1559
+ }
1560
+ interface EntityDeletedPayload {
1561
+ entityType: EntityTypeName;
1562
+ entityId: string;
1563
+ deletedBy: string;
1564
+ workspaceId: string;
1565
+ }
1566
+ interface EntityLinkedPayload {
1567
+ sourceType: EntityTypeName;
1568
+ sourceId: string;
1569
+ targetType: EntityTypeName;
1570
+ targetId: string;
1571
+ workspaceId: string;
1572
+ }
1573
+ interface EscalationTriggeredPayload {
1574
+ extensionId: string;
1575
+ agentId?: string;
1576
+ trigger: EscalationTrigger;
1577
+ action: string;
1578
+ workspaceId: string;
1579
+ }
1580
+ interface EscalationResolvedPayload {
1581
+ extensionId: string;
1582
+ agentId?: string;
1583
+ trigger: EscalationTrigger;
1584
+ response: EscalationUserResponse;
1585
+ workspaceId: string;
1586
+ }
1587
+ interface EscalationTimedOutPayload {
1588
+ extensionId: string;
1589
+ agentId?: string;
1590
+ trigger: EscalationTrigger;
1591
+ workspaceId: string;
1592
+ }
1593
+ interface AuditEntryCreatedPayload {
1594
+ extensionId: string;
1595
+ action: AuditAction;
1596
+ outcome: AuditOutcome;
1597
+ target: string;
1598
+ }
1599
+ interface SelfUpdatedPayload {
1600
+ field: string;
1601
+ updatedBy: string;
1602
+ }
1603
+ interface SelfProposalReceivedPayload {
1604
+ field: string;
1605
+ source: string;
1606
+ confidence: number;
1607
+ }
1608
+ interface AgentActivatedPayload {
1609
+ name: string;
1610
+ version: string;
1611
+ workspaceId: string;
1612
+ }
1613
+ interface AgentDeactivatedPayload {
1614
+ name: string;
1615
+ workspaceId: string;
1616
+ }
1617
+ interface AgentPlanCreatedPayload {
1618
+ agentName: string;
1619
+ taskId: string;
1620
+ stepCount: number;
1621
+ workspaceId: string;
1622
+ }
1623
+ interface AgentStepCompletedPayload {
1624
+ agentName: string;
1625
+ taskId: string;
1626
+ stepId: string;
1627
+ durationMs: number;
1628
+ workspaceId: string;
1629
+ }
1630
+ interface AgentStepFailedPayload {
1631
+ agentName: string;
1632
+ taskId: string;
1633
+ stepId: string;
1634
+ error: string;
1635
+ workspaceId: string;
1636
+ }
1637
+ interface A2AInboundReceivedPayload {
1638
+ sourceEndpoint: string;
1639
+ taskId: string;
1640
+ workspaceId: string;
1641
+ }
1642
+ interface A2ADelegationSentPayload {
1643
+ targetEndpoint: string;
1644
+ taskId: string;
1645
+ agentName: string;
1646
+ workspaceId: string;
1647
+ }
1648
+ interface A2ADelegationCompletedPayload {
1649
+ targetEndpoint: string;
1650
+ taskId: string;
1651
+ status: string;
1652
+ workspaceId: string;
1653
+ }
1654
+ interface PromptRegisteredPayload {
1655
+ extensionName: string;
1656
+ promptId: string;
1657
+ workspaceId: string;
1658
+ }
1659
+ interface PromptEnabledPayload {
1660
+ promptId: string;
1661
+ workspaceId: string;
1662
+ }
1663
+ interface ContextRegisteredPayload {
1664
+ extensionName: string;
1665
+ contextId: string;
1666
+ workspaceId: string;
1667
+ }
1668
+ interface ContextUpdatedPayload {
1669
+ extensionName: string;
1670
+ contextId: string;
1671
+ workspaceId: string;
1672
+ }
1673
+ interface ContextExpiredPayload {
1674
+ extensionName: string;
1675
+ contextId: string;
1676
+ workspaceId: string;
1677
+ }
1678
+
1679
+ /**
1680
+ * §21 — Agent + Extension Identity via DID + Verifiable Credentials
1681
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
1682
+ *
1683
+ * Each Extension and Agent MAY be assigned a W3C Decentralized Identifier (DID).
1684
+ * Full compliance hosts MUST assign DIDs for any cross-host interaction.
1685
+ *
1686
+ * Rules:
1687
+ * - Extension and Agent DIDs resolvable via Extension Registry Protocol (§12)
1688
+ * - Cross-host actions MUST be signed with the Extension's or Agent's private key
1689
+ * - Revoked or expired VCs cause immediate capability rejection
1690
+ * - Capability token: identity:present — required for cross-host interactions
1691
+ */
1692
+
1693
+ /**
1694
+ * W3C DID Document fields for Plexo entities.
1695
+ * @example did:plexo:host-id:extension-id
1696
+ */
1697
+ interface PlexoDIDDocument {
1698
+ /** W3C DID string, e.g. 'did:plexo:host-id:extension-id' */
1699
+ did: string;
1700
+ /** Public key for signing agent actions and authenticating */
1701
+ publicKey: string;
1702
+ /** Communication endpoints */
1703
+ serviceEndpoints?: Record<string, string>;
1704
+ /** URL that resolves to plexo.json */
1705
+ extensionManifest?: string;
1706
+ /** Trust tier of this entity */
1707
+ trustTier: TrustTier;
1708
+ /** DID of the issuing host or registry */
1709
+ issuedBy: string;
1710
+ }
1711
+ /**
1712
+ * Verifiable Credential issued by a host at install time,
1713
+ * attesting to capability grants.
1714
+ */
1715
+ interface PlexoVerifiableCredential {
1716
+ /** Unique credential ID */
1717
+ id: string;
1718
+ /** W3C VC type */
1719
+ type: 'VerifiableCredential' | 'PlexoCapabilityCredential';
1720
+ /** DID of the issuer (host) */
1721
+ issuer: string;
1722
+ /** DID of the subject (extension or agent) */
1723
+ subject: string;
1724
+ /** ISO 8601 issuance date */
1725
+ issuanceDate: string;
1726
+ /** ISO 8601 expiration date */
1727
+ expirationDate?: string;
1728
+ /** Granted capability tokens */
1729
+ capabilities: string[];
1730
+ /** Cryptographic proof */
1731
+ proof: {
1732
+ type: string;
1733
+ created: string;
1734
+ proofPurpose: string;
1735
+ verificationMethod: string;
1736
+ /** Signature value */
1737
+ jws: string;
1738
+ };
1739
+ }
1740
+ /**
1741
+ * Selective disclosure request — prove a specific capability
1742
+ * without revealing all grants.
1743
+ */
1744
+ interface SelectiveDisclosureRequest {
1745
+ /** Capabilities the verifier wants proof of */
1746
+ requestedCapabilities: string[];
1747
+ /** DID of the requesting party */
1748
+ verifier: string;
1749
+ /** Nonce to prevent replay */
1750
+ nonce: string;
1751
+ }
1752
+ interface SelectiveDisclosureResponse {
1753
+ /** Derived credential proving only requested capabilities */
1754
+ credential: PlexoVerifiableCredential;
1755
+ /** Nonce from the request */
1756
+ nonce: string;
1757
+ }
1758
+
1759
+ /**
1760
+ * §25 — Plexo-Native Service Discovery
1761
+ * Plexo Extension Protocol (PEX) Specification v0.4.0
1762
+ *
1763
+ * Any service supporting Plexo natively MUST expose:
1764
+ * GET /.well-known/plexo.json
1765
+ *
1766
+ * Detection flow:
1767
+ * 1. User initiates a Connection to an external service
1768
+ * 2. Host pings {serviceBaseUrl}/.well-known/plexo.json
1769
+ * 3. Valid manifest → Plexo-native: verify DID, surface shield badge,
1770
+ * full disclosure pre-connection
1771
+ * 4. 404 or invalid → Standard Connection: no badge, OAuth/API key fallback,
1772
+ * manual trust assumed
1773
+ *
1774
+ * Rules:
1775
+ * - MUST be served over HTTPS, publicly accessible, no authentication required
1776
+ * - MUST return Content-Type: application/json
1777
+ * - Response MUST validate against Plexo manifest schema
1778
+ * - DID MUST resolve via Extension Registry
1779
+ */
1780
+ interface WellKnownPlexo {
1781
+ /** PEX spec version (e.g. '0.4.0') */
1782
+ plexo: string;
1783
+ /** Service display name */
1784
+ name: string;
1785
+ /** W3C DID of this service */
1786
+ did?: string;
1787
+ capabilities: {
1788
+ /** Connection identifiers offered by this service */
1789
+ offered?: string[];
1790
+ /** Pre-built extensions available for this service */
1791
+ extensions?: WellKnownExtensionRef[];
1792
+ };
1793
+ dataResidency?: {
1794
+ sendsDataExternally: boolean;
1795
+ regions?: string[];
1796
+ };
1797
+ auth: {
1798
+ schemes: ('oauth2' | 'api_key' | 'did')[];
1799
+ oauth2?: {
1800
+ authorizationUrl: string;
1801
+ tokenUrl: string;
1802
+ };
1803
+ };
1804
+ escalation?: {
1805
+ /** Whether this service supports escalation callback webhooks */
1806
+ supportsEscalationCallbacks: boolean;
1807
+ webhookEndpoint?: string;
1808
+ };
1809
+ }
1810
+ interface WellKnownExtensionRef {
1811
+ /** Scoped extension name */
1812
+ name: string;
1813
+ /** Registry URL where the extension is published */
1814
+ registry: string;
1815
+ }
1816
+ /**
1817
+ * Result of a Plexo-native service discovery probe.
1818
+ */
1819
+ type ServiceDiscoveryResult = {
1820
+ native: true;
1821
+ manifest: WellKnownPlexo;
1822
+ } | {
1823
+ native: false;
1824
+ reason: 'not_found' | 'invalid_manifest' | 'network_error';
1825
+ };
1826
+
1827
+ /**
1828
+ * PEX manifest validation
1829
+ * Corresponds to §3.3 of the Plexo Extension Protocol (PEX) Specification v0.4.0
1830
+ *
1831
+ * Used by:
1832
+ * - POST /api/plugins (host install validation)
1833
+ * - @plexo/cli (publish validation)
1834
+ */
1835
+
1836
+ interface ValidationError {
1837
+ field: string;
1838
+ message: string;
1839
+ severity?: 'error' | 'warning';
1840
+ }
1841
+ interface ValidationResult {
1842
+ valid: boolean;
1843
+ errors: ValidationError[];
1844
+ }
1845
+ interface ValidationOptions {
1846
+ /** Host compliance level — affects which capabilities are valid */
1847
+ hostComplianceLevel?: HostComplianceLevel;
1848
+ /**
1849
+ * Install source context. When set to `'sideload'` the validator applies
1850
+ * the sideload capability ceiling (rejects owner-only tokens regardless of
1851
+ * the declared `trust` value). Default: `'registry'`.
1852
+ */
1853
+ source?: 'registry' | 'sideload';
1854
+ }
1855
+ declare function validateManifest(raw: unknown, options?: ValidationOptions): ValidationResult;
1856
+
1857
+ /**
1858
+ * Extension package signature verification.
1859
+ *
1860
+ * v1 strategy (documented in `docs/security/extension-security-model.md` §Q1):
1861
+ *
1862
+ * Sigstore / cosign with GitHub OIDC — keyless signing tied to the GitHub
1863
+ * Actions workflow that publishes to the registry. No long-lived keys to
1864
+ * lose. The host verifies that the signature's signer identity resolves to
1865
+ * `*@joeybuilt-official` (the owner) or to a key on the verified-publisher
1866
+ * key set.
1867
+ *
1868
+ * This module ships a **stub** verifier for v1. The intent is:
1869
+ *
1870
+ * - The registry stores signature metadata alongside every manifest row
1871
+ * (`signature`, `signature_type`, `signer_identity`, `signed_at`) — see
1872
+ * migration 0068.
1873
+ * - Install-time, the host calls `verifySignature(manifest, meta)` and, on
1874
+ * success, trusts the manifest's declared `trust` value.
1875
+ * - On failure (or `null` meta), the host downgrades `trust` to `community`
1876
+ * (for registry installs) or `local` (for sideloads), exactly as §8.4 of
1877
+ * the security spec requires.
1878
+ *
1879
+ * The real verifier will call `cosign verify` against the Rekor log, or fall
1880
+ * back to a local ECDSA P-256 public key check. For v1 we accept any Sigstore
1881
+ * metadata whose `signer_identity` ends in `@joeybuilt-official` — this lets
1882
+ * us wire the call sites now and swap the implementation later without
1883
+ * churning the install route.
1884
+ */
1885
+
1886
+ type SignatureType = 'sigstore' | 'ecdsa-p256' | 'none';
1887
+ interface SignatureMetadata {
1888
+ /** Opaque signature payload (base64 for ECDSA, JSON bundle for Sigstore). */
1889
+ signature: string;
1890
+ /** Discriminator for which verifier to use. */
1891
+ signatureType: SignatureType;
1892
+ /** Human-readable signer identity (e.g. `plexo-bot@joeybuilt-official`). */
1893
+ signerIdentity: string;
1894
+ /** ISO 8601 timestamp the signature was produced. */
1895
+ signedAt: string;
1896
+ }
1897
+ interface SignatureVerificationResult {
1898
+ ok: boolean;
1899
+ /** Matches the input `signatureType` on success, `'none'` on failure. */
1900
+ signatureType: SignatureType;
1901
+ /** Signer identity extracted from the verified signature, if any. */
1902
+ signerIdentity: string | null;
1903
+ /** The effective trust tier AFTER verification. May downgrade to 'community'. */
1904
+ effectiveTrust: TrustTier;
1905
+ /** Human-readable message for the UI. */
1906
+ message: string;
1907
+ }
1908
+ /**
1909
+ * Verify a manifest's signature. v1 stub behavior:
1910
+ *
1911
+ * - `meta === null` → unsigned, downgrade to `community`
1912
+ * - `signatureType === 'none'` → unsigned, downgrade to `community`
1913
+ * - `signatureType === 'sigstore'`:
1914
+ * - signer_identity endsWith an OWNER_IDENTITY_SUFFIX → accept as `owner`
1915
+ * - otherwise → accept as `verified`
1916
+ * - `signatureType === 'ecdsa-p256'`:
1917
+ * - accept as `verified` (real verification TBD)
1918
+ *
1919
+ * The declared `manifest.trust` is the ceiling — we never upgrade above it.
1920
+ * An unsigned extension claiming `owner` is downgraded to `community`. A
1921
+ * signed extension claiming `community` stays at `community`.
1922
+ */
1923
+ declare function verifySignature(manifest: ExtensionManifest, meta: SignatureMetadata | null): SignatureVerificationResult;
1924
+
1925
+ export { type A2AAgentCard, type A2ADelegation, type A2ADelegationCompletedPayload, type A2ADelegationSentPayload, type A2AInboundReceivedPayload, type A2AInboundRequest, type A2ATask, type A2ATaskResult, type A2ATaskStatus, type AgentActivatedPayload, type AgentDeactivatedPayload, type AgentExtension, type AgentHints, type AgentPlanCreatedPayload, type AgentStackManifest, type AgentStepCompletedPayload, type AgentStepFailedPayload, type AuditAction, type AuditEntry, type AuditEntryCreatedPayload, type AuditOutcome, type AuditQuery, type AuditQueryResult, type BehaviorRuleDefinition, type CalendarEventEntity, type CapabilityToken, type ChannelAttachmentRef, type ChannelClient, type ChannelClientOptions, type ChannelDescriptor, type ChannelEvent, type ChannelExtension, type ChannelHealthChangedPayload, type ChannelHealthResult, type ChannelMessage, type ChannelMessagePage, type ChannelMessageReceivedPayload, type ChannelScope, type ChannelSendRequest, type ChannelSendResult, type ChannelSubscription, type ChannelThread, type ChannelThreadPage, type ChannelType, type ConnectionAddedPayload, type ConnectionCredentials, type ConnectionRemovedPayload, type ConnectionState, type ContextArtifact, type ContextExpiredPayload, type ContextRegisteredPayload, type ContextRegistration, type ContextSummary, type ContextUpdatedPayload, type DataResidencyDeclaration, type EntityCreatedPayload, type EntityDeletedPayload, type EntityLinkedPayload, type EntityModifiedPayload, type EntitySearchQuery, type EntitySearchResult, type EntityTypeMap, type EntityTypeName, type ErrorCode, type EscalationDeclaration, type EscalationOutcome, type EscalationReason, type EscalationRequest, type EscalationResolvedPayload, type EscalationResponse, type EscalationResult, type EscalationTimedOutPayload, type EscalationTrigger, type EscalationTriggeredPayload, type EscalationUserResponse, type ExtensionActivatedPayload, type ExtensionCrashedPayload, type ExtensionDeactivatedPayload, type ExtensionManifest, type ExtensionSubtype, type ExtensionType, type ExternalDestination, type FileEntity, type HostComplianceLevel, type HostInfo, type InvokeContext, type JSONSchema, type LinkedEntity, type MCPServerConfig, type ManifestType, type MemoryEntry, type MemoryWrittenPayload, type MessageType, type ModelContextEntry, type ModelRequirements, type NoteEntity, type NotificationLevel, type OneWayDoor, type OneWayDoorType, PEX_VERSION, type PairedConnectionDescriptor, type PersonEntity, type PexError, type PexMessage, type PexVersion, type Plan, type PlanStep, type PlexoDIDDocument, type PlexoEntity, type PlexoSDK, type PlexoVerifiableCredential, type PromptArtifact, type PromptEnabledPayload, type PromptRegisteredPayload, type PromptRegistration, type PromptSummary, type PromptVariable, type ResourceHints, type ScheduleRegistration, type SelectiveDisclosureRequest, type SelectiveDisclosureResponse, type SelfProposalReceivedPayload, type SelfUpdatedPayload, type ServiceDiscoveryResult, type ShouldActivateResult, type SignatureMetadata, type SignatureType, type SignatureVerificationResult, type StandardTopic, type StandingApproval, type StepResult, TOPICS, type TaskBlockedPayload, type TaskCompletedPayload, type TaskCreateOptions, type TaskCreatedPayload, type TaskEntity, type TaskFailedPayload, type TaskFilter, type ThreadEntity, type ToolCall, type ToolRegistration, type ToolSummary, type TransactionEntity, type TrustTier, type TrustTierCeilings, type TrustTierPolicy, type UserCommunicationStyle, type UserContext, type UserIdentity, type UserSelf, type UserSelfConflictResolution, type UserSelfField, type UserSelfProposal, type ValidationError, type ValidationOptions, type ValidationResult, type WellKnownExtensionRef, type WellKnownPlexo, type WidgetRegistration, createChannelClient, customTopic, validateManifest, verifySignature };