@vagarylabs/plugin-sdk 1.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.
Files changed (62) hide show
  1. package/README.md +1224 -0
  2. package/dist/bundlers.d.ts +57 -0
  3. package/dist/bundlers.d.ts.map +1 -0
  4. package/dist/bundlers.js +106 -0
  5. package/dist/bundlers.js.map +1 -0
  6. package/dist/define-plugin.d.ts +266 -0
  7. package/dist/define-plugin.d.ts.map +1 -0
  8. package/dist/define-plugin.js +85 -0
  9. package/dist/define-plugin.js.map +1 -0
  10. package/dist/dev-cli.d.ts +3 -0
  11. package/dist/dev-cli.d.ts.map +1 -0
  12. package/dist/dev-cli.js +49 -0
  13. package/dist/dev-cli.js.map +1 -0
  14. package/dist/dev-server.d.ts +34 -0
  15. package/dist/dev-server.d.ts.map +1 -0
  16. package/dist/dev-server.js +194 -0
  17. package/dist/dev-server.js.map +1 -0
  18. package/dist/host-client-factory.d.ts +312 -0
  19. package/dist/host-client-factory.d.ts.map +1 -0
  20. package/dist/host-client-factory.js +623 -0
  21. package/dist/host-client-factory.js.map +1 -0
  22. package/dist/index.d.ts +84 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +84 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/protocol.d.ts +1556 -0
  27. package/dist/protocol.d.ts.map +1 -0
  28. package/dist/protocol.js +369 -0
  29. package/dist/protocol.js.map +1 -0
  30. package/dist/testing.d.ts +183 -0
  31. package/dist/testing.d.ts.map +1 -0
  32. package/dist/testing.js +2265 -0
  33. package/dist/testing.js.map +1 -0
  34. package/dist/types.d.ts +1621 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +12 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/ui/components.d.ts +517 -0
  39. package/dist/ui/components.d.ts.map +1 -0
  40. package/dist/ui/components.js +135 -0
  41. package/dist/ui/components.js.map +1 -0
  42. package/dist/ui/hooks.d.ts +155 -0
  43. package/dist/ui/hooks.d.ts.map +1 -0
  44. package/dist/ui/hooks.js +195 -0
  45. package/dist/ui/hooks.js.map +1 -0
  46. package/dist/ui/index.d.ts +54 -0
  47. package/dist/ui/index.d.ts.map +1 -0
  48. package/dist/ui/index.js +51 -0
  49. package/dist/ui/index.js.map +1 -0
  50. package/dist/ui/runtime.d.ts +3 -0
  51. package/dist/ui/runtime.d.ts.map +1 -0
  52. package/dist/ui/runtime.js +30 -0
  53. package/dist/ui/runtime.js.map +1 -0
  54. package/dist/ui/types.d.ts +400 -0
  55. package/dist/ui/types.d.ts.map +1 -0
  56. package/dist/ui/types.js +17 -0
  57. package/dist/ui/types.js.map +1 -0
  58. package/dist/worker-rpc-host.d.ts +128 -0
  59. package/dist/worker-rpc-host.d.ts.map +1 -0
  60. package/dist/worker-rpc-host.js +1510 -0
  61. package/dist/worker-rpc-host.js.map +1 -0
  62. package/package.json +128 -0
@@ -0,0 +1,1621 @@
1
+ /**
2
+ * Core types for the Paperclip plugin worker-side SDK.
3
+ *
4
+ * These types define the stable public API surface that plugin workers import
5
+ * from `@vagris/plugin-sdk`. The host provides a concrete implementation
6
+ * of `PluginContext` to the plugin at initialisation time.
7
+ *
8
+ * @see PLUGIN_SPEC.md §14 — SDK Surface
9
+ * @see PLUGIN_SPEC.md §29.2 — SDK Versioning
10
+ */
11
+ import type { VagrisPluginManifestV1, PluginStateScopeKind, PluginEventType, PluginToolDeclaration, PluginLauncherDeclaration, Company, Project, Issue, IssueComment, IssueDocument, IssueDocumentSummary, IssueRelationIssueSummary, IssueAssigneeAdapterOverrides, IssueThreadInteraction, SuggestTasksInteraction, AskUserQuestionsInteraction, RequestConfirmationInteraction, CreateIssueThreadInteraction, PluginIssueOriginKind, SharedPluginOriginKind, IssueSurfaceVisibility, PluginManagedAgentResolution, PluginManagedProjectResolution, PluginManagedRoutineResolution, PluginManagedSkillResolution, Routine, RoutineRun, Agent, Goal, HumanCompanyMembershipRole, InviteJoinType, MembershipStatus, PermissionKey, PrincipalPermissionGrant, PrincipalType } from "@vagris/shared";
12
+ import type { PluginPerformActionContext } from "./protocol.js";
13
+ export type { VagrisPluginManifestV1, PluginJobDeclaration, PluginWebhookDeclaration, PluginToolDeclaration, PluginEnvironmentDriverDeclaration, PluginManagedAgentDeclaration, PluginManagedAgentResolution, PluginManagedProjectDeclaration, PluginManagedProjectResolution, PluginManagedRoutineDeclaration, PluginManagedRoutineResolution, PluginManagedSkillDeclaration, PluginManagedSkillFileDeclaration, PluginManagedSkillResolution, CompanySkill, Routine, RoutineRun, PluginLocalFolderDeclaration, PluginCompanySettings, PluginManagedResourceKind, PluginManagedResourceRef, PluginUiSlotDeclaration, PluginUiDeclaration, PluginLauncherActionDeclaration, PluginLauncherRenderDeclaration, PluginLauncherDeclaration, PluginMinimumHostVersion, PluginDatabaseDeclaration, PluginApiRouteDeclaration, PluginApiRouteCompanyResolution, PluginRecord, PluginDatabaseNamespaceRecord, PluginMigrationRecord, PluginConfig, JsonSchema, PluginStatus, PluginCategory, PluginCapability, PluginUiSlotType, PluginUiSlotEntityType, PluginLauncherPlacementZone, PluginLauncherAction, PluginLauncherBounds, PluginLauncherRenderEnvironment, PluginStateScopeKind, PluginJobStatus, PluginJobRunStatus, PluginJobRunTrigger, PluginWebhookDeliveryStatus, PluginDatabaseCoreReadTable, PluginDatabaseMigrationStatus, PluginDatabaseNamespaceMode, PluginDatabaseNamespaceStatus, PluginApiRouteAuthMode, PluginApiRouteCheckoutPolicy, PluginApiRouteMethod, PluginEventType, PluginBridgeErrorCode, Company, Project, Issue, IssueComment, IssueDocument, IssueDocumentSummary, IssueRelationIssueSummary, IssueThreadInteraction, SuggestTasksInteraction, AskUserQuestionsInteraction, RequestConfirmationInteraction, CreateIssueThreadInteraction, PluginIssueOriginKind, SharedPluginOriginKind, IssueSurfaceVisibility, Agent, Goal, HumanCompanyMembershipRole, InviteJoinType, MembershipStatus, PermissionKey, PrincipalPermissionGrant, PrincipalType, } from "@vagris/shared";
14
+ /**
15
+ * A scope key identifies the exact location where plugin state is stored.
16
+ * Scope is partitioned by `scopeKind` and optional `scopeId`.
17
+ *
18
+ * Examples:
19
+ * - `{ scopeKind: "instance" }` — single global value for the whole instance
20
+ * - `{ scopeKind: "project", scopeId: "proj-uuid" }` — per-project state
21
+ * - `{ scopeKind: "issue", scopeId: "iss-uuid" }` — per-issue state
22
+ *
23
+ * @see PLUGIN_SPEC.md §21.3 `plugin_state`
24
+ */
25
+ export interface ScopeKey {
26
+ /** What kind of Paperclip object this state is scoped to. */
27
+ scopeKind: PluginStateScopeKind;
28
+ /** UUID or text identifier for the scoped object. Omit for `instance` scope. */
29
+ scopeId?: string;
30
+ /** Optional sub-namespace within the scope to avoid key collisions. Defaults to `"default"`. */
31
+ namespace?: string;
32
+ /** The state key within the namespace. */
33
+ stateKey: string;
34
+ }
35
+ /**
36
+ * Optional filter applied when subscribing to an event. The host evaluates
37
+ * the filter server-side so filtered-out events never cross the process boundary.
38
+ *
39
+ * All filter fields are optional. If omitted the plugin receives every event
40
+ * of the subscribed type.
41
+ *
42
+ * @see PLUGIN_SPEC.md §16.1 — Event Filtering
43
+ */
44
+ export interface EventFilter {
45
+ /** Only receive events for this project. */
46
+ projectId?: string;
47
+ /** Only receive events for this company. */
48
+ companyId?: string;
49
+ /** Only receive events for this agent. */
50
+ agentId?: string;
51
+ /** Additional arbitrary filter fields. */
52
+ [key: string]: unknown;
53
+ }
54
+ /**
55
+ * Envelope wrapping every domain event delivered to a plugin worker.
56
+ *
57
+ * @see PLUGIN_SPEC.md §16 — Event System
58
+ */
59
+ export interface PluginEvent<TPayload = unknown> {
60
+ /** Unique event identifier (UUID). */
61
+ eventId: string;
62
+ /** The event type (e.g. `"issue.created"`). */
63
+ eventType: PluginEventType | `plugin.${string}`;
64
+ /** ISO 8601 timestamp when the event occurred. */
65
+ occurredAt: string;
66
+ /** ID of the actor that caused the event, if applicable. */
67
+ actorId?: string;
68
+ /** Type of actor: `"user"`, `"agent"`, `"system"`, or `"plugin"`. */
69
+ actorType?: "user" | "agent" | "system" | "plugin";
70
+ /** Primary entity involved in the event. */
71
+ entityId?: string;
72
+ /** Type of the primary entity. */
73
+ entityType?: string;
74
+ /** UUID of the company this event belongs to. */
75
+ companyId: string;
76
+ /** Typed event payload. */
77
+ payload: TPayload;
78
+ }
79
+ /**
80
+ * Context passed to a plugin job handler when the host triggers a scheduled run.
81
+ *
82
+ * @see PLUGIN_SPEC.md §13.6 — `runJob`
83
+ */
84
+ export interface PluginJobContext {
85
+ /** Stable job key matching the declaration in the manifest. */
86
+ jobKey: string;
87
+ /** UUID for this specific job run instance. */
88
+ runId: string;
89
+ /** What triggered this run. */
90
+ trigger: "schedule" | "manual" | "retry";
91
+ /** ISO 8601 timestamp when the run was scheduled to start. */
92
+ scheduledAt: string;
93
+ }
94
+ /**
95
+ * Run context passed to a plugin tool handler when an agent invokes the tool.
96
+ *
97
+ * @see PLUGIN_SPEC.md §13.10 — `executeTool`
98
+ */
99
+ export interface ToolRunContext {
100
+ /** UUID of the agent invoking the tool. */
101
+ agentId: string;
102
+ /** UUID of the current agent run. */
103
+ runId: string;
104
+ /** UUID of the company the run belongs to. */
105
+ companyId: string;
106
+ /** UUID of the project the run belongs to. */
107
+ projectId: string;
108
+ }
109
+ /**
110
+ * Result returned from a plugin tool handler.
111
+ *
112
+ * @see PLUGIN_SPEC.md §13.10 — `executeTool`
113
+ */
114
+ export interface ToolResult {
115
+ /** String content returned to the agent. Required for success responses. */
116
+ content?: string;
117
+ /** Structured data returned alongside or instead of string content. */
118
+ data?: unknown;
119
+ /** If present, indicates the tool call failed. */
120
+ error?: string;
121
+ }
122
+ /**
123
+ * Input for creating or updating a plugin-owned entity.
124
+ *
125
+ * @see PLUGIN_SPEC.md §21.3 `plugin_entities`
126
+ */
127
+ export interface PluginEntityUpsert {
128
+ /** Plugin-defined entity type (e.g. `"linear-issue"`, `"github-pr"`). */
129
+ entityType: string;
130
+ /** Scope where this entity lives. */
131
+ scopeKind: PluginStateScopeKind;
132
+ /** Optional scope ID. */
133
+ scopeId?: string;
134
+ /** External identifier in the remote system (e.g. Linear issue ID). */
135
+ externalId?: string;
136
+ /** Human-readable title for display in the Paperclip UI. */
137
+ title?: string;
138
+ /** Optional status string. */
139
+ status?: string;
140
+ /** Full entity data blob. Must be JSON-serializable. */
141
+ data: Record<string, unknown>;
142
+ }
143
+ /**
144
+ * A plugin-owned entity record as returned by `ctx.entities.list()`.
145
+ *
146
+ * @see PLUGIN_SPEC.md §21.3 `plugin_entities`
147
+ */
148
+ export interface PluginEntityRecord {
149
+ /** UUID primary key. */
150
+ id: string;
151
+ /** Plugin-defined entity type. */
152
+ entityType: string;
153
+ /** Scope kind. */
154
+ scopeKind: PluginStateScopeKind;
155
+ /** Scope ID, if any. */
156
+ scopeId: string | null;
157
+ /** External identifier, if any. */
158
+ externalId: string | null;
159
+ /** Human-readable title. */
160
+ title: string | null;
161
+ /** Status string. */
162
+ status: string | null;
163
+ /** Full entity data. */
164
+ data: Record<string, unknown>;
165
+ /** ISO 8601 creation timestamp. */
166
+ createdAt: string;
167
+ /** ISO 8601 last-updated timestamp. */
168
+ updatedAt: string;
169
+ }
170
+ /**
171
+ * Query parameters for `ctx.entities.list()`.
172
+ */
173
+ export interface PluginEntityQuery {
174
+ /** Filter by entity type. */
175
+ entityType?: string;
176
+ /** Filter by scope kind. */
177
+ scopeKind?: PluginStateScopeKind;
178
+ /** Filter by scope ID. */
179
+ scopeId?: string;
180
+ /** Filter by external ID. */
181
+ externalId?: string;
182
+ /** Maximum number of results to return. */
183
+ limit?: number;
184
+ /** Number of results to skip (for pagination). */
185
+ offset?: number;
186
+ }
187
+ /**
188
+ * Workspace metadata provided by the host. Plugins use this to resolve local
189
+ * filesystem paths for file browsing, git, terminal, and process operations.
190
+ *
191
+ * @see PLUGIN_SPEC.md §7 — Project Workspaces
192
+ * @see PLUGIN_SPEC.md §20 — Local Tooling
193
+ */
194
+ export interface PluginWorkspace {
195
+ /** UUID primary key. */
196
+ id: string;
197
+ /** UUID of the parent project. */
198
+ projectId: string;
199
+ /** Display name for this workspace. */
200
+ name: string;
201
+ /** Absolute filesystem path to the workspace directory. */
202
+ path: string;
203
+ /** Repository URL, when known. */
204
+ repoUrl: string | null;
205
+ /** Checkout/ref requested for the workspace, when known. */
206
+ repoRef: string | null;
207
+ /** Default comparison ref for workspace tooling, when known. */
208
+ defaultRef: string | null;
209
+ /** Whether this is the project's primary workspace. */
210
+ isPrimary: boolean;
211
+ /** ISO 8601 creation timestamp. */
212
+ createdAt: string;
213
+ /** ISO 8601 last-updated timestamp. */
214
+ updatedAt: string;
215
+ }
216
+ /**
217
+ * Plugin-safe execution workspace metadata provided by the host. This exposes
218
+ * the local/repository coordinates plugins need for workspace tooling without
219
+ * giving the SDK a host-owned diff engine.
220
+ */
221
+ export interface PluginExecutionWorkspaceMetadata {
222
+ /** UUID primary key. */
223
+ id: string;
224
+ /** UUID of the owning company. */
225
+ companyId: string;
226
+ /** UUID of the parent project. */
227
+ projectId: string;
228
+ /** UUID of the backing project workspace, when present. */
229
+ projectWorkspaceId: string | null;
230
+ /** Absolute filesystem path to the workspace when locally realized. */
231
+ path: string | null;
232
+ /** Current working directory for local workspace tooling. */
233
+ cwd: string | null;
234
+ /** Repository URL, when known. */
235
+ repoUrl: string | null;
236
+ /** Base ref configured for the workspace, when known. */
237
+ baseRef: string | null;
238
+ /** Branch name configured for the workspace, when known. */
239
+ branchName: string | null;
240
+ /** Host provider type for the realized workspace. */
241
+ providerType: string | null;
242
+ /** Provider metadata already safe for plugin consumption. */
243
+ providerMetadata: Record<string, unknown> | null;
244
+ }
245
+ /**
246
+ * `ctx.config` — read resolved operator configuration for this plugin.
247
+ *
248
+ * Plugin workers receive the resolved config at initialisation. Use `get()`
249
+ * to access the current configuration at any time. The host calls
250
+ * `configChanged` on the worker when the operator updates config at runtime.
251
+ *
252
+ * @see PLUGIN_SPEC.md §13.3 — `validateConfig`
253
+ * @see PLUGIN_SPEC.md §13.4 — `configChanged`
254
+ */
255
+ export interface PluginConfigClient {
256
+ /**
257
+ * Returns the resolved operator configuration for this plugin instance.
258
+ * Values are validated against the plugin's `instanceConfigSchema` by the
259
+ * host before being passed to the worker.
260
+ */
261
+ get(): Promise<Record<string, unknown>>;
262
+ }
263
+ export interface PluginLocalFolderProblem {
264
+ code: "not_configured" | "not_absolute" | "missing" | "not_directory" | "not_readable" | "not_writable" | "missing_directory" | "missing_file" | "path_traversal" | "symlink_escape" | "atomic_write_failed";
265
+ message: string;
266
+ path?: string;
267
+ }
268
+ export interface PluginLocalFolderStatus {
269
+ folderKey: string;
270
+ configured: boolean;
271
+ path: string | null;
272
+ realPath: string | null;
273
+ access: "read" | "readWrite";
274
+ readable: boolean;
275
+ writable: boolean;
276
+ requiredDirectories: string[];
277
+ requiredFiles: string[];
278
+ missingDirectories: string[];
279
+ missingFiles: string[];
280
+ healthy: boolean;
281
+ problems: PluginLocalFolderProblem[];
282
+ checkedAt: string;
283
+ }
284
+ export interface PluginLocalFolderConfigureInput {
285
+ companyId: string;
286
+ folderKey: string;
287
+ path: string;
288
+ access?: "read" | "readWrite";
289
+ requiredDirectories?: string[];
290
+ requiredFiles?: string[];
291
+ }
292
+ export interface PluginLocalFolderListOptions {
293
+ relativePath?: string | null;
294
+ recursive?: boolean;
295
+ maxEntries?: number;
296
+ }
297
+ export interface PluginLocalFolderEntry {
298
+ path: string;
299
+ name: string;
300
+ kind: "file" | "directory";
301
+ size: number | null;
302
+ modifiedAt: string | null;
303
+ }
304
+ export interface PluginLocalFolderListing {
305
+ folderKey: string;
306
+ relativePath: string | null;
307
+ entries: PluginLocalFolderEntry[];
308
+ truncated: boolean;
309
+ }
310
+ export interface PluginLocalFoldersClient {
311
+ /** Manifest-declared local folders for this plugin. */
312
+ declarations(): import("@vagris/shared").PluginLocalFolderDeclaration[];
313
+ /** Persist a company-scoped local folder path after validating it. */
314
+ configure(input: PluginLocalFolderConfigureInput): Promise<PluginLocalFolderStatus>;
315
+ /** Check the stored folder readiness for a company and folder key. */
316
+ status(companyId: string, folderKey: string): Promise<PluginLocalFolderStatus>;
317
+ /** List entries below a configured folder after containment checks. */
318
+ list(companyId: string, folderKey: string, options?: PluginLocalFolderListOptions): Promise<PluginLocalFolderListing>;
319
+ /** Read a UTF-8 text file below a configured folder after containment checks. */
320
+ readText(companyId: string, folderKey: string, relativePath: string): Promise<string>;
321
+ /** Write a UTF-8 text file below a configured folder using atomic rename. */
322
+ writeTextAtomic(companyId: string, folderKey: string, relativePath: string, contents: string): Promise<PluginLocalFolderStatus>;
323
+ /** Delete a file below a configured folder after containment checks. Missing files are treated as already deleted. */
324
+ deleteFile(companyId: string, folderKey: string, relativePath: string): Promise<PluginLocalFolderStatus>;
325
+ }
326
+ /**
327
+ * `ctx.events` — subscribe to and emit Paperclip domain events.
328
+ *
329
+ * Requires `events.subscribe` capability for `on()`.
330
+ * Requires `events.emit` capability for `emit()`.
331
+ *
332
+ * @see PLUGIN_SPEC.md §16 — Event System
333
+ */
334
+ export interface PluginEventsClient {
335
+ /**
336
+ * Subscribe to a core Paperclip domain event or a plugin-namespaced event.
337
+ *
338
+ * @param name - Event type, e.g. `"issue.created"` or `"plugin.@acme/linear.sync-done"`
339
+ * @param fn - Async event handler
340
+ */
341
+ on(name: PluginEventType | `plugin.${string}`, fn: (event: PluginEvent) => Promise<void>): () => void;
342
+ /**
343
+ * Subscribe to an event with an optional server-side filter.
344
+ *
345
+ * @param name - Event type
346
+ * @param filter - Server-side filter evaluated before dispatching to the worker
347
+ * @param fn - Async event handler
348
+ * @returns An unsubscribe function that removes the handler
349
+ */
350
+ on(name: PluginEventType | `plugin.${string}`, filter: EventFilter, fn: (event: PluginEvent) => Promise<void>): () => void;
351
+ /**
352
+ * Emit a plugin-namespaced event. Other plugins with `events.subscribe` can
353
+ * subscribe to it using `"plugin.<pluginId>.<eventName>"`.
354
+ *
355
+ * Requires the `events.emit` capability.
356
+ *
357
+ * Plugin-emitted events are automatically namespaced: if the plugin ID is
358
+ * `"acme.linear"` and the event name is `"sync-done"`, the full event type
359
+ * becomes `"plugin.acme.linear.sync-done"`.
360
+ *
361
+ * @see PLUGIN_SPEC.md §16.2 — Plugin-to-Plugin Events
362
+ *
363
+ * @param name - Bare event name (e.g. `"sync-done"`)
364
+ * @param companyId - UUID of the company this event belongs to
365
+ * @param payload - JSON-serializable event payload
366
+ */
367
+ emit(name: string, companyId: string, payload: unknown): Promise<void>;
368
+ }
369
+ /**
370
+ * `ctx.jobs` — register handlers for scheduled jobs declared in the manifest.
371
+ *
372
+ * Requires `jobs.schedule` capability.
373
+ *
374
+ * @see PLUGIN_SPEC.md §17 — Scheduled Jobs
375
+ */
376
+ export interface PluginJobsClient {
377
+ /**
378
+ * Register a handler for a scheduled job.
379
+ *
380
+ * The `key` must match a `jobKey` declared in the plugin manifest.
381
+ * The host calls this handler according to the job's declared `schedule`.
382
+ *
383
+ * @param key - Job key matching the manifest declaration
384
+ * @param fn - Async job handler
385
+ */
386
+ register(key: string, fn: (job: PluginJobContext) => Promise<void>): void;
387
+ }
388
+ /**
389
+ * A runtime launcher registration uses the same declaration shape as a
390
+ * manifest launcher entry.
391
+ */
392
+ export type PluginLauncherRegistration = PluginLauncherDeclaration;
393
+ /**
394
+ * `ctx.launchers` — register launcher declarations at runtime.
395
+ */
396
+ export interface PluginLaunchersClient {
397
+ /**
398
+ * Register launcher metadata for host discovery.
399
+ *
400
+ * If a launcher with the same id is registered more than once, the latest
401
+ * declaration replaces the previous one.
402
+ */
403
+ register(launcher: PluginLauncherRegistration): void;
404
+ }
405
+ export interface PluginDatabaseClient {
406
+ /** Host-derived PostgreSQL schema name for this plugin's namespace. */
407
+ namespace: string;
408
+ /** Run a restricted SELECT against the plugin namespace and whitelisted core tables. */
409
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
410
+ /** Run a restricted INSERT, UPDATE, or DELETE against the plugin namespace. */
411
+ execute(sql: string, params?: unknown[]): Promise<{
412
+ rowCount: number;
413
+ }>;
414
+ }
415
+ /**
416
+ * `ctx.http` — make outbound HTTP requests.
417
+ *
418
+ * Requires `http.outbound` capability.
419
+ *
420
+ * @see PLUGIN_SPEC.md §15.1 — Capabilities: Runtime/Integration
421
+ */
422
+ export interface PluginHttpClient {
423
+ /**
424
+ * Perform an outbound HTTP request.
425
+ *
426
+ * The host enforces `http.outbound` capability before allowing the call.
427
+ * Plugins may also use standard Node `fetch` or other libraries directly —
428
+ * this client exists for host-managed tracing and audit logging.
429
+ *
430
+ * @param url - Target URL
431
+ * @param init - Standard `RequestInit` options
432
+ * @returns The response
433
+ */
434
+ fetch(url: string, init?: RequestInit): Promise<Response>;
435
+ }
436
+ /**
437
+ * `ctx.secrets` — resolve secret references.
438
+ *
439
+ * Requires `secrets.read-ref` capability.
440
+ *
441
+ * Plugins store secret *references* in their config (e.g. a secret name).
442
+ * This client resolves the reference through the Paperclip secret provider
443
+ * system and returns the resolved value at execution time.
444
+ *
445
+ * @see PLUGIN_SPEC.md §22 — Secrets
446
+ */
447
+ export interface PluginSecretsClient {
448
+ /**
449
+ * Resolve a secret reference to its current value.
450
+ *
451
+ * The reference is a string identifier pointing to a secret configured
452
+ * in the Paperclip secret provider (e.g. `"MY_API_KEY"`).
453
+ *
454
+ * Secret values are resolved at call time and must never be cached or
455
+ * written to logs, config, or other persistent storage.
456
+ *
457
+ * @param secretRef - The secret reference string from plugin config
458
+ * @returns The resolved secret value
459
+ */
460
+ resolve(secretRef: string): Promise<string>;
461
+ }
462
+ /**
463
+ * Input for writing a plugin activity log entry.
464
+ *
465
+ * @see PLUGIN_SPEC.md §21.4 — Activity Log Changes
466
+ */
467
+ export interface PluginActivityLogEntry {
468
+ /** UUID of the company this activity belongs to. Required for auditing. */
469
+ companyId: string;
470
+ /** Human-readable description of the activity. */
471
+ message: string;
472
+ /** Optional entity type this activity relates to. */
473
+ entityType?: string;
474
+ /** Optional entity ID this activity relates to. */
475
+ entityId?: string;
476
+ /** Optional additional metadata. */
477
+ metadata?: Record<string, unknown>;
478
+ }
479
+ /**
480
+ * `ctx.activity` — write plugin-originated activity log entries.
481
+ *
482
+ * Requires `activity.log.write` capability.
483
+ *
484
+ * @see PLUGIN_SPEC.md §21.4 — Activity Log Changes
485
+ */
486
+ export interface PluginActivityClient {
487
+ /**
488
+ * Write an activity log entry attributed to this plugin.
489
+ *
490
+ * The host writes the entry with `actor_type = plugin` and
491
+ * `actor_id = <pluginId>`.
492
+ *
493
+ * @param entry - The activity log entry to write
494
+ */
495
+ log(entry: PluginActivityLogEntry): Promise<void>;
496
+ }
497
+ /**
498
+ * `ctx.state` — read and write plugin-scoped key-value state.
499
+ *
500
+ * Each plugin gets an isolated namespace: state written by plugin A can never
501
+ * be read or overwritten by plugin B. Within a plugin, state is partitioned by
502
+ * a five-part composite key: `(pluginId, scopeKind, scopeId, namespace, stateKey)`.
503
+ *
504
+ * **Scope kinds**
505
+ *
506
+ * | `scopeKind` | `scopeId` | Typical use |
507
+ * |-------------|-----------|-------------|
508
+ * | `"instance"` | omit | Global flags, last full-sync timestamps |
509
+ * | `"company"` | company UUID | Per-company sync cursors |
510
+ * | `"project"` | project UUID | Per-project settings, branch tracking |
511
+ * | `"project_workspace"` | workspace UUID | Per-workspace state |
512
+ * | `"agent"` | agent UUID | Per-agent memory |
513
+ * | `"issue"` | issue UUID | Idempotency keys, linked external IDs |
514
+ * | `"goal"` | goal UUID | Per-goal progress |
515
+ * | `"run"` | run UUID | Per-run checkpoints |
516
+ *
517
+ * **Namespaces**
518
+ *
519
+ * The optional `namespace` field (default: `"default"`) lets you group related
520
+ * keys within a scope without risking collisions between different logical
521
+ * subsystems inside the same plugin.
522
+ *
523
+ * **Security**
524
+ *
525
+ * Never store resolved secret values. Store only secret references and resolve
526
+ * them at call time via `ctx.secrets.resolve()`.
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * // Instance-global flag
531
+ * await ctx.state.set({ scopeKind: "instance", stateKey: "schema-version" }, 2);
532
+ *
533
+ * // Idempotency key per issue
534
+ * const synced = await ctx.state.get({ scopeKind: "issue", scopeId: issueId, stateKey: "synced-to-linear" });
535
+ * if (!synced) {
536
+ * await syncToLinear(issueId);
537
+ * await ctx.state.set({ scopeKind: "issue", scopeId: issueId, stateKey: "synced-to-linear" }, true);
538
+ * }
539
+ *
540
+ * // Per-project, namespaced for two integrations
541
+ * await ctx.state.set({ scopeKind: "project", scopeId: projectId, namespace: "linear", stateKey: "cursor" }, cursor);
542
+ * await ctx.state.set({ scopeKind: "project", scopeId: projectId, namespace: "github", stateKey: "last-event" }, eventId);
543
+ * ```
544
+ *
545
+ * `plugin.state.read` capability required for `get()`.
546
+ * `plugin.state.write` capability required for `set()` and `delete()`.
547
+ *
548
+ * @see PLUGIN_SPEC.md §21.3 `plugin_state`
549
+ */
550
+ export interface PluginStateClient {
551
+ /**
552
+ * Read a state value.
553
+ *
554
+ * Returns the stored JSON value as-is, or `null` if no entry has been set
555
+ * for this scope+key combination. Falsy values (`false`, `0`, `""`) are
556
+ * returned correctly and are not confused with "not set".
557
+ *
558
+ * @param input - Scope key identifying the entry to read
559
+ * @returns The stored JSON value, or `null` if no value has been set
560
+ */
561
+ get(input: ScopeKey): Promise<unknown>;
562
+ /**
563
+ * Write a state value. Creates the row if it does not exist; replaces it
564
+ * atomically (upsert) if it does. Safe to call concurrently.
565
+ *
566
+ * Any JSON-serializable value is accepted: objects, arrays, strings,
567
+ * numbers, booleans, and `null`.
568
+ *
569
+ * @param input - Scope key identifying the entry to write
570
+ * @param value - JSON-serializable value to store
571
+ */
572
+ set(input: ScopeKey, value: unknown): Promise<void>;
573
+ /**
574
+ * Delete a state value. No-ops silently if the entry does not exist
575
+ * (idempotent by design — safe to call without prior `get()`).
576
+ *
577
+ * @param input - Scope key identifying the entry to delete
578
+ */
579
+ delete(input: ScopeKey): Promise<void>;
580
+ }
581
+ /**
582
+ * `ctx.entities` — create and query plugin-owned entity records.
583
+ *
584
+ * @see PLUGIN_SPEC.md §21.3 `plugin_entities`
585
+ */
586
+ export interface PluginEntitiesClient {
587
+ /**
588
+ * Create or update a plugin entity record (upsert by `externalId` within
589
+ * the given scope, or by `id` if provided).
590
+ *
591
+ * @param input - Entity data to upsert
592
+ */
593
+ upsert(input: PluginEntityUpsert): Promise<PluginEntityRecord>;
594
+ /**
595
+ * Query plugin entity records.
596
+ *
597
+ * @param query - Filter criteria
598
+ * @returns Matching entity records
599
+ */
600
+ list(query: PluginEntityQuery): Promise<PluginEntityRecord[]>;
601
+ }
602
+ /**
603
+ * `ctx.projects` — read project and workspace metadata.
604
+ *
605
+ * Requires `projects.read` capability.
606
+ * Requires `project.workspaces.read` capability for workspace operations.
607
+ *
608
+ * @see PLUGIN_SPEC.md §7 — Project Workspaces
609
+ */
610
+ export interface PluginProjectsClient {
611
+ /**
612
+ * List projects visible to the plugin.
613
+ *
614
+ * Requires the `projects.read` capability.
615
+ */
616
+ list(input: {
617
+ companyId: string;
618
+ limit?: number;
619
+ offset?: number;
620
+ }): Promise<Project[]>;
621
+ /**
622
+ * Get a single project by ID.
623
+ *
624
+ * Requires the `projects.read` capability.
625
+ */
626
+ get(projectId: string, companyId: string): Promise<Project | null>;
627
+ /**
628
+ * List all workspaces attached to a project.
629
+ *
630
+ * @param projectId - UUID of the project
631
+ * @param companyId - UUID of the company that owns the project
632
+ * @returns All workspaces for the project, ordered with primary first
633
+ */
634
+ listWorkspaces(projectId: string, companyId: string): Promise<PluginWorkspace[]>;
635
+ /**
636
+ * Get the primary workspace for a project.
637
+ *
638
+ * @param projectId - UUID of the project
639
+ * @param companyId - UUID of the company that owns the project
640
+ * @returns The primary workspace, or `null` if no workspace is configured
641
+ */
642
+ getPrimaryWorkspace(projectId: string, companyId: string): Promise<PluginWorkspace | null>;
643
+ /**
644
+ * Resolve the primary workspace for an issue by looking up the issue's
645
+ * project and returning its primary workspace.
646
+ *
647
+ * This is a convenience method that combines `issues.get()` and
648
+ * `getPrimaryWorkspace()` in a single RPC call.
649
+ *
650
+ * @param issueId - UUID of the issue
651
+ * @param companyId - UUID of the company that owns the issue
652
+ * @returns The primary workspace for the issue's project, or `null` if
653
+ * the issue has no project or the project has no workspace
654
+ *
655
+ * @see PLUGIN_SPEC.md §20 — Local Tooling
656
+ */
657
+ getWorkspaceForIssue(issueId: string, companyId: string): Promise<PluginWorkspace | null>;
658
+ /** Resolve and reconcile manifest-declared plugin-managed projects by stable key. Requires `projects.managed`. */
659
+ managed: {
660
+ get(projectKey: string, companyId: string): Promise<PluginManagedProjectResolution>;
661
+ reconcile(projectKey: string, companyId: string): Promise<PluginManagedProjectResolution>;
662
+ reset(projectKey: string, companyId: string): Promise<PluginManagedProjectResolution>;
663
+ };
664
+ }
665
+ /**
666
+ * `ctx.executionWorkspaces` — read execution workspace metadata.
667
+ *
668
+ * Requires `execution.workspaces.read`.
669
+ */
670
+ export interface PluginExecutionWorkspacesClient {
671
+ /**
672
+ * Return plugin-safe metadata for an execution workspace. The host enforces
673
+ * company access before returning any workspace coordinates.
674
+ */
675
+ get(workspaceId: string, companyId: string): Promise<PluginExecutionWorkspaceMetadata | null>;
676
+ }
677
+ /**
678
+ * `ctx.routines` — resolve and reconcile plugin-managed Paperclip routines.
679
+ *
680
+ * Requires `routines.managed` capability.
681
+ */
682
+ export interface PluginRoutinesClient {
683
+ managed: {
684
+ get(routineKey: string, companyId: string): Promise<PluginManagedRoutineResolution>;
685
+ reconcile(routineKey: string, companyId: string, overrides?: {
686
+ assigneeAgentId?: string | null;
687
+ projectId?: string | null;
688
+ }): Promise<PluginManagedRoutineResolution>;
689
+ reset(routineKey: string, companyId: string, overrides?: {
690
+ assigneeAgentId?: string | null;
691
+ projectId?: string | null;
692
+ }): Promise<PluginManagedRoutineResolution>;
693
+ update(routineKey: string, companyId: string, patch: {
694
+ status?: string;
695
+ }): Promise<Routine>;
696
+ run(routineKey: string, companyId: string, overrides?: {
697
+ assigneeAgentId?: string | null;
698
+ projectId?: string | null;
699
+ }): Promise<RoutineRun>;
700
+ };
701
+ }
702
+ /**
703
+ * `ctx.skills` — resolve and reconcile plugin-managed company skills.
704
+ *
705
+ * Requires `skills.managed` capability.
706
+ */
707
+ export interface PluginSkillsClient {
708
+ managed: {
709
+ get(skillKey: string, companyId: string): Promise<PluginManagedSkillResolution>;
710
+ reconcile(skillKey: string, companyId: string): Promise<PluginManagedSkillResolution>;
711
+ reset(skillKey: string, companyId: string): Promise<PluginManagedSkillResolution>;
712
+ };
713
+ }
714
+ /**
715
+ * `ctx.data` — register `getData` handlers that back `usePluginData()` in the
716
+ * plugin's frontend components.
717
+ *
718
+ * The plugin's UI calls `usePluginData(key, params)` which routes through the
719
+ * host bridge to the worker's registered handler.
720
+ *
721
+ * @see PLUGIN_SPEC.md §13.8 — `getData`
722
+ */
723
+ export interface PluginDataClient {
724
+ /**
725
+ * Register a handler for a plugin-defined data key.
726
+ *
727
+ * @param key - Stable string identifier for this data type (e.g. `"sync-health"`)
728
+ * @param handler - Async function that receives request params and returns JSON-serializable data
729
+ */
730
+ register(key: string, handler: (params: Record<string, unknown>) => Promise<unknown>): void;
731
+ }
732
+ /**
733
+ * `ctx.actions` — register `performAction` handlers that back
734
+ * `usePluginAction()` in the plugin's frontend components.
735
+ *
736
+ * @see PLUGIN_SPEC.md §13.9 — `performAction`
737
+ */
738
+ export interface PluginActionsClient {
739
+ /**
740
+ * Register a handler for a plugin-defined action key.
741
+ *
742
+ * @param key - Stable string identifier for this action (e.g. `"resync"`)
743
+ * @param handler - Async function that receives action params plus immutable host actor context and returns a result
744
+ */
745
+ register(key: string, handler: (params: Record<string, unknown>, context: PluginPerformActionContext) => Promise<unknown>): void;
746
+ }
747
+ /**
748
+ * `ctx.tools` — register handlers for agent tools declared in the manifest.
749
+ *
750
+ * Requires `agent.tools.register` capability.
751
+ *
752
+ * Tool names are automatically namespaced by plugin ID at runtime.
753
+ *
754
+ * @see PLUGIN_SPEC.md §11 — Agent Tools
755
+ */
756
+ export interface PluginToolsClient {
757
+ /**
758
+ * Register a handler for a plugin-contributed agent tool.
759
+ *
760
+ * @param name - Tool name matching the manifest declaration (without namespace prefix)
761
+ * @param declaration - Tool metadata (displayName, description, parametersSchema)
762
+ * @param fn - Async handler that executes the tool
763
+ */
764
+ register(name: string, declaration: Pick<PluginToolDeclaration, "displayName" | "description" | "parametersSchema">, fn: (params: unknown, runCtx: ToolRunContext) => Promise<ToolResult>): void;
765
+ }
766
+ /**
767
+ * `ctx.logger` — structured logging from the plugin worker.
768
+ *
769
+ * Log output is captured by the host, stored, and surfaced in the plugin
770
+ * health dashboard.
771
+ *
772
+ * @see PLUGIN_SPEC.md §26.1 — Logging
773
+ */
774
+ export interface PluginLogger {
775
+ /** Log an informational message. */
776
+ info(message: string, meta?: Record<string, unknown>): void;
777
+ /** Log a warning. */
778
+ warn(message: string, meta?: Record<string, unknown>): void;
779
+ /** Log an error. */
780
+ error(message: string, meta?: Record<string, unknown>): void;
781
+ /** Log a debug message (may be suppressed in production). */
782
+ debug(message: string, meta?: Record<string, unknown>): void;
783
+ }
784
+ /**
785
+ * `ctx.metrics` — write plugin-contributed metrics.
786
+ *
787
+ * Requires `metrics.write` capability.
788
+ *
789
+ * @see PLUGIN_SPEC.md §15.1 — Capabilities: Data Write
790
+ */
791
+ export interface PluginMetricsClient {
792
+ /**
793
+ * Write a numeric metric data point.
794
+ *
795
+ * @param name - Metric name (plugin-namespaced by the host)
796
+ * @param value - Numeric value
797
+ * @param tags - Optional key-value tags for filtering
798
+ */
799
+ write(name: string, value: number, tags?: Record<string, string>): Promise<void>;
800
+ }
801
+ /**
802
+ * `ctx.telemetry` — emit plugin-scoped telemetry to the host's external
803
+ * telemetry pipeline.
804
+ *
805
+ * Requires `telemetry.track` capability.
806
+ */
807
+ export interface PluginTelemetryClient {
808
+ /**
809
+ * Track a plugin telemetry event.
810
+ *
811
+ * The host prefixes the final event name as `plugin.<pluginId>.<eventName>`
812
+ * before forwarding it to the shared telemetry client.
813
+ *
814
+ * @param eventName - Bare plugin event slug (for example `"sync_completed"`)
815
+ * @param dimensions - Optional structured dimensions
816
+ */
817
+ track(eventName: string, dimensions?: Record<string, string | number | boolean>): Promise<void>;
818
+ }
819
+ /**
820
+ * `ctx.companies` — read company metadata.
821
+ *
822
+ * Requires `companies.read` capability.
823
+ */
824
+ export interface PluginCompaniesClient {
825
+ /**
826
+ * List companies visible to this plugin.
827
+ */
828
+ list(input?: {
829
+ limit?: number;
830
+ offset?: number;
831
+ }): Promise<Company[]>;
832
+ /**
833
+ * Get one company by ID.
834
+ */
835
+ get(companyId: string): Promise<Company | null>;
836
+ }
837
+ /**
838
+ * `ctx.issues.documents` — read and write issue documents.
839
+ *
840
+ * Requires:
841
+ * - `issue.documents.read` for `list` and `get`
842
+ * - `issue.documents.write` for `upsert` and `delete`
843
+ *
844
+ * @see PLUGIN_SPEC.md §14 — SDK Surface
845
+ */
846
+ export interface PluginIssueDocumentsClient {
847
+ /**
848
+ * List all documents attached to an issue.
849
+ *
850
+ * Returns summary metadata (id, key, title, format, timestamps) without
851
+ * the full document body. Use `get()` to fetch a specific document's body.
852
+ *
853
+ * Requires the `issue.documents.read` capability.
854
+ */
855
+ list(issueId: string, companyId: string): Promise<IssueDocumentSummary[]>;
856
+ /**
857
+ * Get a single document by key, including its full body content.
858
+ *
859
+ * Returns `null` if no document exists with the given key.
860
+ *
861
+ * Requires the `issue.documents.read` capability.
862
+ *
863
+ * @param issueId - UUID of the issue
864
+ * @param key - Document key (e.g. `"plan"`, `"design-spec"`)
865
+ * @param companyId - UUID of the company
866
+ */
867
+ get(issueId: string, key: string, companyId: string): Promise<IssueDocument | null>;
868
+ /**
869
+ * Create or update a document on an issue.
870
+ *
871
+ * If a document with the given key already exists, it is updated and a new
872
+ * revision is created. If it does not exist, it is created.
873
+ *
874
+ * Requires the `issue.documents.write` capability.
875
+ *
876
+ * @param input - Document data including issueId, key, body, and optional title/format/changeSummary
877
+ */
878
+ upsert(input: {
879
+ issueId: string;
880
+ key: string;
881
+ body: string;
882
+ companyId: string;
883
+ title?: string;
884
+ format?: string;
885
+ changeSummary?: string;
886
+ }): Promise<IssueDocument>;
887
+ /**
888
+ * Delete a document and all its revisions.
889
+ *
890
+ * No-ops silently if the document does not exist (idempotent).
891
+ *
892
+ * Requires the `issue.documents.write` capability.
893
+ *
894
+ * @param issueId - UUID of the issue
895
+ * @param key - Document key to delete
896
+ * @param companyId - UUID of the company
897
+ */
898
+ delete(issueId: string, key: string, companyId: string): Promise<void>;
899
+ }
900
+ export interface PluginIssueMutationActor {
901
+ /** Agent that initiated the plugin operation, when the plugin is acting from an agent run. */
902
+ actorAgentId?: string | null;
903
+ /** Board/user that initiated the plugin operation, when known. */
904
+ actorUserId?: string | null;
905
+ /** Heartbeat run that initiated the operation. Required for checkout-aware agent actions. */
906
+ actorRunId?: string | null;
907
+ }
908
+ export interface PluginIssueRelationSummary {
909
+ blockedBy: IssueRelationIssueSummary[];
910
+ blocks: IssueRelationIssueSummary[];
911
+ }
912
+ export interface PluginIssueRelationsClient {
913
+ /** Read blocker relationships for an issue. Requires `issue.relations.read`. */
914
+ get(issueId: string, companyId: string): Promise<PluginIssueRelationSummary>;
915
+ /** Replace the issue's blocked-by relation set. Requires `issue.relations.write`. */
916
+ setBlockedBy(issueId: string, blockedByIssueIds: string[], companyId: string, actor?: PluginIssueMutationActor): Promise<PluginIssueRelationSummary>;
917
+ /** Add one or more blockers while preserving existing blockers. Requires `issue.relations.write`. */
918
+ addBlockers(issueId: string, blockerIssueIds: string[], companyId: string, actor?: PluginIssueMutationActor): Promise<PluginIssueRelationSummary>;
919
+ /** Remove one or more blockers while preserving all other blockers. Requires `issue.relations.write`. */
920
+ removeBlockers(issueId: string, blockerIssueIds: string[], companyId: string, actor?: PluginIssueMutationActor): Promise<PluginIssueRelationSummary>;
921
+ }
922
+ export interface PluginIssueCheckoutOwnership {
923
+ issueId: string;
924
+ status: Issue["status"];
925
+ assigneeAgentId: string | null;
926
+ checkoutRunId: string | null;
927
+ adoptedFromRunId: string | null;
928
+ }
929
+ export interface PluginIssueWakeupResult {
930
+ queued: boolean;
931
+ runId: string | null;
932
+ }
933
+ export interface PluginIssueWakeupBatchResult {
934
+ issueId: string;
935
+ queued: boolean;
936
+ runId: string | null;
937
+ }
938
+ export interface PluginIssueRunSummary {
939
+ id: string;
940
+ issueId: string | null;
941
+ agentId: string;
942
+ status: string;
943
+ invocationSource: string;
944
+ triggerDetail: string | null;
945
+ startedAt: string | null;
946
+ finishedAt: string | null;
947
+ error: string | null;
948
+ createdAt: string;
949
+ }
950
+ export interface PluginIssueApprovalSummary {
951
+ issueId: string;
952
+ id: string;
953
+ type: string;
954
+ status: string;
955
+ requestedByAgentId: string | null;
956
+ requestedByUserId: string | null;
957
+ decidedByUserId: string | null;
958
+ decidedAt: string | null;
959
+ createdAt: string;
960
+ }
961
+ export interface PluginIssueCostSummary {
962
+ costCents: number;
963
+ inputTokens: number;
964
+ cachedInputTokens: number;
965
+ outputTokens: number;
966
+ billingCode: string | null;
967
+ }
968
+ export interface PluginBudgetIncidentSummary {
969
+ id: string;
970
+ scopeType: string;
971
+ scopeId: string;
972
+ metric: string;
973
+ windowKind: string;
974
+ thresholdType: string;
975
+ amountLimit: number;
976
+ amountObserved: number;
977
+ status: string;
978
+ approvalId: string | null;
979
+ createdAt: string;
980
+ }
981
+ export interface PluginIssueInvocationBlockSummary {
982
+ issueId: string;
983
+ agentId: string;
984
+ scopeType: "company" | "agent" | "project";
985
+ scopeId: string;
986
+ scopeName: string;
987
+ reason: string;
988
+ }
989
+ export interface PluginIssueOrchestrationSummary {
990
+ issueId: string;
991
+ companyId: string;
992
+ subtreeIssueIds: string[];
993
+ relations: Record<string, PluginIssueRelationSummary>;
994
+ approvals: PluginIssueApprovalSummary[];
995
+ runs: PluginIssueRunSummary[];
996
+ costs: PluginIssueCostSummary;
997
+ openBudgetIncidents: PluginBudgetIncidentSummary[];
998
+ invocationBlocks: PluginIssueInvocationBlockSummary[];
999
+ }
1000
+ export interface PluginIssueSubtreeOptions {
1001
+ /** Include the root issue in the result. Defaults to true. */
1002
+ includeRoot?: boolean;
1003
+ /** Include blocker relationship summaries keyed by issue ID. */
1004
+ includeRelations?: boolean;
1005
+ /** Include issue document summaries keyed by issue ID. */
1006
+ includeDocuments?: boolean;
1007
+ /** Include queued/running heartbeat runs keyed by issue ID. */
1008
+ includeActiveRuns?: boolean;
1009
+ /** Include assignee summaries keyed by agent ID. */
1010
+ includeAssignees?: boolean;
1011
+ }
1012
+ export interface PluginIssueAssigneeSummary {
1013
+ id: string;
1014
+ name: string;
1015
+ role: string;
1016
+ title: string | null;
1017
+ status: Agent["status"];
1018
+ }
1019
+ export interface PluginIssueSubtree {
1020
+ rootIssueId: string;
1021
+ companyId: string;
1022
+ issueIds: string[];
1023
+ issues: Issue[];
1024
+ relations?: Record<string, PluginIssueRelationSummary>;
1025
+ documents?: Record<string, IssueDocumentSummary[]>;
1026
+ activeRuns?: Record<string, PluginIssueRunSummary[]>;
1027
+ assignees?: Record<string, PluginIssueAssigneeSummary>;
1028
+ }
1029
+ export interface PluginIssueSummariesClient {
1030
+ /**
1031
+ * Read the compact orchestration inputs a workflow plugin needs for an
1032
+ * issue or issue subtree. Requires `issues.orchestration.read`.
1033
+ */
1034
+ getOrchestration(input: {
1035
+ issueId: string;
1036
+ companyId: string;
1037
+ includeSubtree?: boolean;
1038
+ billingCode?: string | null;
1039
+ }): Promise<PluginIssueOrchestrationSummary>;
1040
+ }
1041
+ /**
1042
+ * `ctx.issues` — read and mutate issues plus comments.
1043
+ *
1044
+ * Requires:
1045
+ * - `issues.read` for read operations
1046
+ * - `issues.create` for create
1047
+ * - `issues.update` for update
1048
+ * - `issues.checkout` for checkout ownership assertions
1049
+ * - `issues.wakeup` for assignment wakeup requests
1050
+ * - `issues.orchestration.read` for orchestration summaries
1051
+ * - `issue.comments.read` for `listComments`
1052
+ * - `issue.comments.create` for `createComment`
1053
+ * - `issue.interactions.create` for `createInteraction`, `suggestTasks`, `askUserQuestions`, and `requestConfirmation`
1054
+ * - `issue.documents.read` for `documents.list` and `documents.get`
1055
+ * - `issue.documents.write` for `documents.upsert` and `documents.delete`
1056
+ */
1057
+ export interface PluginIssuesClient {
1058
+ list(input: {
1059
+ companyId: string;
1060
+ projectId?: string;
1061
+ assigneeAgentId?: string;
1062
+ originKind?: PluginIssueOriginKind | SharedPluginOriginKind;
1063
+ originKindPrefix?: string;
1064
+ originId?: string;
1065
+ status?: Issue["status"];
1066
+ includePluginOperations?: boolean;
1067
+ limit?: number;
1068
+ offset?: number;
1069
+ }): Promise<Issue[]>;
1070
+ get(issueId: string, companyId: string): Promise<Issue | null>;
1071
+ create(input: {
1072
+ companyId: string;
1073
+ projectId?: string;
1074
+ goalId?: string;
1075
+ parentId?: string;
1076
+ inheritExecutionWorkspaceFromIssueId?: string;
1077
+ title: string;
1078
+ description?: string;
1079
+ status?: Issue["status"];
1080
+ priority?: Issue["priority"];
1081
+ assigneeAgentId?: string;
1082
+ assigneeUserId?: string | null;
1083
+ requestDepth?: number;
1084
+ billingCode?: string | null;
1085
+ assigneeAdapterOverrides?: IssueAssigneeAdapterOverrides | null;
1086
+ surfaceVisibility?: IssueSurfaceVisibility;
1087
+ /**
1088
+ * `plugin:<key>` (or a `plugin:<key>:<sub>` under it), or one of the bare cross-producer origins
1089
+ * in `SHARED_PLUGIN_ORIGIN_KINDS`. The union is bounded by exactly what the host already accepts
1090
+ * at `normalizePluginOriginKind` — it grants no new authority, it stops the type from being
1091
+ * NARROWER than the runtime, which is what was pushing every alert-lane call site into `as never`.
1092
+ */
1093
+ originKind?: PluginIssueOriginKind | SharedPluginOriginKind;
1094
+ originId?: string | null;
1095
+ originRunId?: string | null;
1096
+ /**
1097
+ * Stable CONDITION key for dedup. Distinct from `originId`, which producers commonly set to a
1098
+ * per-EVENT identity (the alert lane used the Alertmanager label-set hash, which rotates and
1099
+ * let duplicate issues through). Derive this from source fields, never from a rendered title.
1100
+ */
1101
+ originFingerprint?: string | null;
1102
+ blockedByIssueIds?: string[];
1103
+ labelIds?: string[];
1104
+ executionWorkspaceId?: string | null;
1105
+ executionWorkspacePreference?: string | null;
1106
+ executionWorkspaceSettings?: Record<string, unknown> | null;
1107
+ actor?: PluginIssueMutationActor;
1108
+ }): Promise<Issue>;
1109
+ update(issueId: string, patch: Partial<Pick<Issue, "title" | "description" | "status" | "priority" | "assigneeAgentId" | "assigneeUserId" | "billingCode" | "originKind" | "originId" | "originRunId" | "requestDepth" | "executionWorkspaceId" | "executionWorkspacePreference">> & {
1110
+ blockedByIssueIds?: string[];
1111
+ labelIds?: string[];
1112
+ executionWorkspaceSettings?: Record<string, unknown> | null;
1113
+ }, companyId: string, actor?: PluginIssueMutationActor): Promise<Issue>;
1114
+ assertCheckoutOwner(input: {
1115
+ issueId: string;
1116
+ companyId: string;
1117
+ actorAgentId: string;
1118
+ actorRunId: string;
1119
+ }): Promise<PluginIssueCheckoutOwnership>;
1120
+ /**
1121
+ * Read a root issue's descendants with optional relation/document/run/assignee
1122
+ * summaries. Requires `issue.subtree.read`.
1123
+ */
1124
+ getSubtree(issueId: string, companyId: string, options?: PluginIssueSubtreeOptions): Promise<PluginIssueSubtree>;
1125
+ requestWakeup(issueId: string, companyId: string, options?: {
1126
+ reason?: string;
1127
+ contextSource?: string;
1128
+ idempotencyKey?: string | null;
1129
+ } & PluginIssueMutationActor): Promise<PluginIssueWakeupResult>;
1130
+ requestWakeups(issueIds: string[], companyId: string, options?: {
1131
+ reason?: string;
1132
+ contextSource?: string;
1133
+ idempotencyKeyPrefix?: string | null;
1134
+ } & PluginIssueMutationActor): Promise<PluginIssueWakeupBatchResult[]>;
1135
+ listComments(issueId: string, companyId: string): Promise<IssueComment[]>;
1136
+ createComment(issueId: string, body: string, companyId: string, options?: {
1137
+ authorAgentId?: string;
1138
+ }): Promise<IssueComment>;
1139
+ createInteraction(issueId: string, interaction: CreateIssueThreadInteraction, companyId: string, options?: {
1140
+ authorAgentId?: string;
1141
+ }): Promise<IssueThreadInteraction>;
1142
+ suggestTasks(issueId: string, interaction: Omit<Extract<CreateIssueThreadInteraction, {
1143
+ kind: "suggest_tasks";
1144
+ }>, "kind">, companyId: string, options?: {
1145
+ authorAgentId?: string;
1146
+ }): Promise<SuggestTasksInteraction>;
1147
+ askUserQuestions(issueId: string, interaction: Omit<Extract<CreateIssueThreadInteraction, {
1148
+ kind: "ask_user_questions";
1149
+ }>, "kind">, companyId: string, options?: {
1150
+ authorAgentId?: string;
1151
+ }): Promise<AskUserQuestionsInteraction>;
1152
+ requestConfirmation(issueId: string, interaction: Omit<Extract<CreateIssueThreadInteraction, {
1153
+ kind: "request_confirmation";
1154
+ }>, "kind">, companyId: string, options?: {
1155
+ authorAgentId?: string;
1156
+ }): Promise<RequestConfirmationInteraction>;
1157
+ /** Read and write issue documents. Requires `issue.documents.read` / `issue.documents.write`. */
1158
+ documents: PluginIssueDocumentsClient;
1159
+ /** Read and write blocker relationships. */
1160
+ relations: PluginIssueRelationsClient;
1161
+ /** Read compact orchestration summaries. */
1162
+ summaries: PluginIssueSummariesClient;
1163
+ }
1164
+ /**
1165
+ * `ctx.agents` — read and manage agents.
1166
+ *
1167
+ * Requires `agents.read` for reads; `agents.pause` / `agents.resume` /
1168
+ * `agents.invoke` for write operations.
1169
+ */
1170
+ export interface PluginAgentsClient {
1171
+ list(input: {
1172
+ companyId: string;
1173
+ status?: Agent["status"];
1174
+ limit?: number;
1175
+ offset?: number;
1176
+ }): Promise<Agent[]>;
1177
+ get(agentId: string, companyId: string): Promise<Agent | null>;
1178
+ /** Pause an agent. Throws if agent is terminated or not found. Requires `agents.pause`. */
1179
+ pause(agentId: string, companyId: string): Promise<Agent>;
1180
+ /** Resume a paused agent (sets status to idle). Throws if terminated, pending_approval, or not found. Requires `agents.resume`. */
1181
+ resume(agentId: string, companyId: string): Promise<Agent>;
1182
+ /** Invoke (wake up) an agent with a prompt payload. Throws if paused, terminated, pending_approval, or not found. Requires `agents.invoke`. */
1183
+ invoke(agentId: string, companyId: string, opts: {
1184
+ prompt: string;
1185
+ reason?: string;
1186
+ }): Promise<{
1187
+ runId: string;
1188
+ }>;
1189
+ /** Resolve and reconcile manifest-declared plugin-managed agents by stable key. Requires `agents.managed`. */
1190
+ managed: {
1191
+ get(agentKey: string, companyId: string): Promise<PluginManagedAgentResolution>;
1192
+ reconcile(agentKey: string, companyId: string): Promise<PluginManagedAgentResolution>;
1193
+ reset(agentKey: string, companyId: string): Promise<PluginManagedAgentResolution>;
1194
+ };
1195
+ /** Create, message, and close agent chat sessions. Requires `agent.sessions.*` capabilities. */
1196
+ sessions: PluginAgentSessionsClient;
1197
+ }
1198
+ /**
1199
+ * Represents an active conversational session with an agent.
1200
+ * Maps to an `AgentTaskSession` row on the host.
1201
+ */
1202
+ export interface AgentSession {
1203
+ sessionId: string;
1204
+ agentId: string;
1205
+ companyId: string;
1206
+ status: "active" | "closed";
1207
+ createdAt: string;
1208
+ }
1209
+ /**
1210
+ * A streaming event received during a session's `sendMessage` call.
1211
+ * Delivered via JSON-RPC notifications from host to worker.
1212
+ */
1213
+ export interface AgentSessionEvent {
1214
+ sessionId: string;
1215
+ runId: string;
1216
+ seq: number;
1217
+ /** The kind of event: "chunk" for output data, "status" for run state changes, "done" for end-of-stream, "error" for failures. */
1218
+ eventType: "chunk" | "status" | "done" | "error";
1219
+ stream: "stdout" | "stderr" | "system" | null;
1220
+ message: string | null;
1221
+ payload: Record<string, unknown> | null;
1222
+ }
1223
+ /**
1224
+ * Result of sending a message to a session.
1225
+ */
1226
+ export interface AgentSessionSendResult {
1227
+ runId: string;
1228
+ }
1229
+ /**
1230
+ * `ctx.agents.sessions` — create, message, and close agent chat sessions.
1231
+ *
1232
+ * Requires `agent.sessions.create` for create, `agent.sessions.list` for list,
1233
+ * `agent.sessions.send` for sendMessage, `agent.sessions.close` for close.
1234
+ */
1235
+ export interface PluginAgentSessionsClient {
1236
+ /** Create a new conversational session with an agent. Requires `agent.sessions.create`. */
1237
+ create(agentId: string, companyId: string, opts?: {
1238
+ taskKey?: string;
1239
+ reason?: string;
1240
+ }): Promise<AgentSession>;
1241
+ /** List active sessions for an agent owned by this plugin. Requires `agent.sessions.list`. */
1242
+ list(agentId: string, companyId: string): Promise<AgentSession[]>;
1243
+ /**
1244
+ * Send a message to a session and receive streaming events via the `onEvent` callback.
1245
+ * Returns immediately with `{ runId }`. Events are delivered asynchronously.
1246
+ * Requires `agent.sessions.send`.
1247
+ */
1248
+ sendMessage(sessionId: string, companyId: string, opts: {
1249
+ prompt: string;
1250
+ reason?: string;
1251
+ onEvent?: (event: AgentSessionEvent) => void;
1252
+ }): Promise<AgentSessionSendResult>;
1253
+ /** Close a session, releasing resources. Requires `agent.sessions.close`. */
1254
+ close(sessionId: string, companyId: string): Promise<void>;
1255
+ }
1256
+ /**
1257
+ * `ctx.goals` — read and mutate goals.
1258
+ *
1259
+ * Requires:
1260
+ * - `goals.read` for read operations
1261
+ * - `goals.create` for create
1262
+ * - `goals.update` for update
1263
+ */
1264
+ export interface PluginGoalsClient {
1265
+ list(input: {
1266
+ companyId: string;
1267
+ level?: Goal["level"];
1268
+ status?: Goal["status"];
1269
+ limit?: number;
1270
+ offset?: number;
1271
+ }): Promise<Goal[]>;
1272
+ get(goalId: string, companyId: string): Promise<Goal | null>;
1273
+ create(input: {
1274
+ companyId: string;
1275
+ title: string;
1276
+ description?: string;
1277
+ level?: Goal["level"];
1278
+ status?: Goal["status"];
1279
+ parentId?: string;
1280
+ ownerAgentId?: string;
1281
+ }): Promise<Goal>;
1282
+ update(goalId: string, patch: Partial<Pick<Goal, "title" | "description" | "level" | "status" | "parentId" | "ownerAgentId">>, companyId: string): Promise<Goal>;
1283
+ }
1284
+ export interface PluginAccessMember {
1285
+ id: string;
1286
+ companyId: string;
1287
+ principalType: PrincipalType;
1288
+ principalId: string;
1289
+ status: MembershipStatus;
1290
+ membershipRole: string | null;
1291
+ grants: PrincipalPermissionGrant[];
1292
+ createdAt: Date | string;
1293
+ updatedAt: Date | string;
1294
+ }
1295
+ export interface PluginAccessInvite {
1296
+ id: string;
1297
+ companyId: string | null;
1298
+ inviteType: string;
1299
+ allowedJoinTypes: InviteJoinType;
1300
+ defaultsPayload: Record<string, unknown> | null;
1301
+ expiresAt: Date | string;
1302
+ invitedByUserId: string | null;
1303
+ revokedAt: Date | string | null;
1304
+ acceptedAt: Date | string | null;
1305
+ createdAt: Date | string;
1306
+ updatedAt: Date | string;
1307
+ state: "active" | "revoked" | "accepted" | "expired";
1308
+ }
1309
+ export interface PluginAccessMembersClient {
1310
+ list(input: {
1311
+ companyId: string;
1312
+ includeArchived?: boolean;
1313
+ }): Promise<PluginAccessMember[]>;
1314
+ get(memberId: string, companyId: string): Promise<PluginAccessMember | null>;
1315
+ update(memberId: string, patch: {
1316
+ membershipRole?: HumanCompanyMembershipRole | null;
1317
+ status?: Extract<MembershipStatus, "pending" | "active" | "suspended">;
1318
+ }, companyId: string): Promise<PluginAccessMember>;
1319
+ }
1320
+ export interface PluginAccessInvitesClient {
1321
+ list(input: {
1322
+ companyId: string;
1323
+ state?: PluginAccessInvite["state"];
1324
+ limit?: number;
1325
+ offset?: number;
1326
+ }): Promise<{
1327
+ invites: PluginAccessInvite[];
1328
+ nextOffset: number | null;
1329
+ }>;
1330
+ create(input: {
1331
+ companyId: string;
1332
+ allowedJoinTypes?: InviteJoinType;
1333
+ humanRole?: HumanCompanyMembershipRole | null;
1334
+ defaultsPayload?: Record<string, unknown> | null;
1335
+ agentMessage?: string | null;
1336
+ }): Promise<PluginAccessInvite & {
1337
+ token: string;
1338
+ }>;
1339
+ revoke(inviteId: string, companyId: string): Promise<PluginAccessInvite>;
1340
+ }
1341
+ export interface PluginAccessClient {
1342
+ /** Read and update company memberships. Requires `access.members.*`. */
1343
+ members: PluginAccessMembersClient;
1344
+ /** Read, create, and revoke company invites. Requires `access.invites.*`. */
1345
+ invites: PluginAccessInvitesClient;
1346
+ }
1347
+ export interface PluginAuthorizationPolicySummary {
1348
+ companyId: string;
1349
+ permissionsMode: "simple";
1350
+ memberCount: number;
1351
+ activeMemberCount: number;
1352
+ grantCount: number;
1353
+ advancedPolicyAvailable: false;
1354
+ }
1355
+ export interface PluginAuthorizationPolicyRecord {
1356
+ resourceType: "company" | "agent" | "project" | "issue";
1357
+ resourceId: string;
1358
+ companyId: string;
1359
+ policy: Record<string, unknown> | null;
1360
+ updatedAt: Date | string | null;
1361
+ }
1362
+ export interface PluginAssignmentPreviewInput {
1363
+ companyId: string;
1364
+ actor: {
1365
+ type: "board";
1366
+ userId?: string | null;
1367
+ companyIds?: string[];
1368
+ isInstanceAdmin?: boolean;
1369
+ } | {
1370
+ type: "agent";
1371
+ agentId: string;
1372
+ companyId: string;
1373
+ };
1374
+ target: {
1375
+ issueId?: string | null;
1376
+ projectId?: string | null;
1377
+ parentIssueId?: string | null;
1378
+ assigneeAgentId?: string | null;
1379
+ assigneeUserId?: string | null;
1380
+ status?: string | null;
1381
+ };
1382
+ }
1383
+ export interface PluginAuthorizationDecisionResult {
1384
+ allowed: boolean;
1385
+ action: string;
1386
+ explanation: string;
1387
+ reason: string;
1388
+ grant?: {
1389
+ principalType: PrincipalType;
1390
+ principalId: string;
1391
+ permissionKey: PermissionKey;
1392
+ scope: Record<string, unknown> | null;
1393
+ };
1394
+ }
1395
+ export interface PluginAuthorizationAuditEntry {
1396
+ id: string;
1397
+ companyId: string;
1398
+ actorType: string;
1399
+ actorId: string;
1400
+ action: string;
1401
+ entityType: string;
1402
+ entityId: string;
1403
+ details: Record<string, unknown> | null;
1404
+ createdAt: Date | string;
1405
+ }
1406
+ export interface PluginAuthorizationClient {
1407
+ grants: {
1408
+ list(input: {
1409
+ companyId: string;
1410
+ principalType?: PrincipalType;
1411
+ principalId?: string;
1412
+ }): Promise<PrincipalPermissionGrant[]>;
1413
+ set(input: {
1414
+ companyId: string;
1415
+ principalType: PrincipalType;
1416
+ principalId: string;
1417
+ grants: Array<{
1418
+ permissionKey: PermissionKey;
1419
+ scope?: Record<string, unknown> | null;
1420
+ }>;
1421
+ grantedByUserId?: string | null;
1422
+ }): Promise<PrincipalPermissionGrant[]>;
1423
+ };
1424
+ policies: {
1425
+ summary(companyId: string): Promise<PluginAuthorizationPolicySummary>;
1426
+ get(input: {
1427
+ companyId: string;
1428
+ resourceType: PluginAuthorizationPolicyRecord["resourceType"];
1429
+ resourceId: string;
1430
+ }): Promise<PluginAuthorizationPolicyRecord | null>;
1431
+ update(input: {
1432
+ companyId: string;
1433
+ resourceType: PluginAuthorizationPolicyRecord["resourceType"];
1434
+ resourceId: string;
1435
+ policy: Record<string, unknown> | null;
1436
+ }): Promise<PluginAuthorizationPolicyRecord>;
1437
+ previewAssignment(input: PluginAssignmentPreviewInput): Promise<PluginAuthorizationDecisionResult>;
1438
+ explainAssignment(input: PluginAssignmentPreviewInput): Promise<PluginAuthorizationDecisionResult>;
1439
+ };
1440
+ audit: {
1441
+ search(input: {
1442
+ companyId: string;
1443
+ action?: string;
1444
+ actorType?: string;
1445
+ actorId?: string;
1446
+ entityType?: string;
1447
+ entityId?: string;
1448
+ decision?: string;
1449
+ limit?: number;
1450
+ offset?: number;
1451
+ }): Promise<PluginAuthorizationAuditEntry[]>;
1452
+ };
1453
+ }
1454
+ /**
1455
+ * `ctx.streams` — push real-time events from the worker to the plugin UI.
1456
+ *
1457
+ * The worker opens a named channel, emits events on it, and closes it when
1458
+ * done. On the UI side, `usePluginStream(channel)` receives these events in
1459
+ * real time via SSE.
1460
+ *
1461
+ * Streams are scoped to `(pluginId, channel, companyId)`. Multiple UI clients
1462
+ * can subscribe to the same channel concurrently.
1463
+ *
1464
+ * @example
1465
+ * ```ts
1466
+ * // Worker: stream chat tokens to the UI
1467
+ * ctx.streams.open("chat", companyId);
1468
+ * for await (const token of tokenStream) {
1469
+ * ctx.streams.emit("chat", { type: "token", text: token });
1470
+ * }
1471
+ * ctx.streams.close("chat");
1472
+ * ```
1473
+ *
1474
+ * @see usePluginStream in `@vagris/plugin-sdk/ui`
1475
+ */
1476
+ export interface PluginStreamsClient {
1477
+ /**
1478
+ * Open a named stream channel. Optional — `emit()` implicitly opens if needed.
1479
+ * Sends a `stream:open` event to connected UI clients.
1480
+ */
1481
+ open(channel: string, companyId: string): void;
1482
+ /**
1483
+ * Push an event to all UI clients subscribed to this channel.
1484
+ *
1485
+ * @param channel - Stream channel name (e.g. `"chat"`, `"logs"`)
1486
+ * @param event - JSON-serializable event payload
1487
+ */
1488
+ emit(channel: string, event: unknown): void;
1489
+ /**
1490
+ * Close a stream channel. Sends a `stream:close` event to connected UI
1491
+ * clients so they know no more events will arrive.
1492
+ */
1493
+ close(channel: string): void;
1494
+ }
1495
+ /**
1496
+ * The full plugin context object passed to the plugin worker at initialisation.
1497
+ *
1498
+ * This is the central interface plugin authors use to interact with the host.
1499
+ * Every client is capability-gated: calling a client method without the
1500
+ * required capability declared in the manifest results in a runtime error.
1501
+ *
1502
+ * @example
1503
+ * ```ts
1504
+ * import { definePlugin } from "@vagris/plugin-sdk";
1505
+ *
1506
+ * export default definePlugin({
1507
+ * async setup(ctx) {
1508
+ * ctx.events.on("issue.created", async (event) => {
1509
+ * ctx.logger.info("Issue created", { issueId: event.entityId });
1510
+ * });
1511
+ *
1512
+ * ctx.data.register("sync-health", async ({ companyId }) => {
1513
+ * const state = await ctx.state.get({ scopeKind: "company", scopeId: String(companyId), stateKey: "last-sync" });
1514
+ * return { lastSync: state };
1515
+ * });
1516
+ * },
1517
+ * });
1518
+ * ```
1519
+ *
1520
+ * @see PLUGIN_SPEC.md §14 — SDK Surface
1521
+ */
1522
+ /**
1523
+ * What the HOST told this worker it understands, readable by the plugin itself (ARCH-5).
1524
+ *
1525
+ * THE DEFECT THIS EXISTS FOR. `@vagris/plugin-sdk` is published, so a plugin can be built against a
1526
+ * newer SDK than the host it runs on. The worker enumerates the fields it forwards, so a field the
1527
+ * older host does not destructure is dropped BETWEEN `callHost` and the database: nothing throws,
1528
+ * nothing the plugin can see happens, and the only symptom is a row missing a column. `apiVersion` is
1529
+ * one integer for the whole protocol and does not move when a field is added, so it cannot see this
1530
+ * either. The host advertises its per-method field lists at `initialize`; until this client existed,
1531
+ * that advertisement was stored in a module-local `let` no plugin code could reach — the host could
1532
+ * log the skew to ITSELF, which is not the party that needs to know.
1533
+ *
1534
+ * TRI-STATE, DELIBERATELY. "Unsupported" and "unknown" are different answers and must not collapse:
1535
+ * an older host that advertises nothing at all means unknown, and treating that as unsupported would
1536
+ * make every plugin report skew against every host that predates the advertisement.
1537
+ */
1538
+ export interface PluginHostCompatClient {
1539
+ /**
1540
+ * Every per-method field list the host advertised at `initialize`, or `null` when it advertised
1541
+ * none (an older host, or a call before initialization completed).
1542
+ */
1543
+ supportedRequestFields(): Readonly<Record<string, readonly string[]>> | null;
1544
+ /**
1545
+ * Does the host understand `field` on `method`?
1546
+ *
1547
+ * `true` / `false` when the host advertised a list for that method; `null` for UNKNOWN — the host
1548
+ * sent no advertisement, or none for this method. Callers that treat `null` as `false` will report
1549
+ * skew against every host older than the advertisement itself.
1550
+ */
1551
+ supportsRequestField(method: string, field: string): boolean | null;
1552
+ /**
1553
+ * Which of `fields` this host does NOT understand on `method`. Empty when they are all supported,
1554
+ * and empty when the answer is unknown — so a caller that acts on a non-empty result is acting on
1555
+ * a POSITIVE statement of incompatibility, never on the absence of information.
1556
+ */
1557
+ unsupportedRequestFields(method: string, fields: readonly string[]): string[];
1558
+ }
1559
+ export interface PluginContext {
1560
+ /** The plugin's manifest as validated at install time. */
1561
+ manifest: VagrisPluginManifestV1;
1562
+ /** What THIS host understands, so version skew is readable by the plugin rather than silent. */
1563
+ host: PluginHostCompatClient;
1564
+ /** Read resolved operator configuration. */
1565
+ config: PluginConfigClient;
1566
+ /** Configure and safely access trusted company-scoped local folders. */
1567
+ localFolders: PluginLocalFoldersClient;
1568
+ /** Subscribe to and emit domain events. Requires `events.subscribe` / `events.emit`. */
1569
+ events: PluginEventsClient;
1570
+ /** Register handlers for scheduled jobs. Requires `jobs.schedule`. */
1571
+ jobs: PluginJobsClient;
1572
+ /** Register launcher metadata that the host can surface in plugin UI entry points. */
1573
+ launchers: PluginLaunchersClient;
1574
+ /** Restricted plugin-owned database namespace. Requires database namespace capabilities. */
1575
+ db: PluginDatabaseClient;
1576
+ /** Make outbound HTTP requests. Requires `http.outbound`. */
1577
+ http: PluginHttpClient;
1578
+ /** Resolve secret references. Requires `secrets.read-ref`. */
1579
+ secrets: PluginSecretsClient;
1580
+ /** Write activity log entries. Requires `activity.log.write`. */
1581
+ activity: PluginActivityClient;
1582
+ /** Read and write scoped plugin state. Requires `plugin.state.read` / `plugin.state.write`. */
1583
+ state: PluginStateClient;
1584
+ /** Create and query plugin-owned entity records. */
1585
+ entities: PluginEntitiesClient;
1586
+ /** Read project and workspace metadata. Requires `projects.read` / `project.workspaces.read`. */
1587
+ projects: PluginProjectsClient;
1588
+ /** Read execution workspace metadata. Requires `execution.workspaces.read`. */
1589
+ executionWorkspaces: PluginExecutionWorkspacesClient;
1590
+ /** Resolve and reconcile plugin-managed routines. Requires `routines.managed`. */
1591
+ routines: PluginRoutinesClient;
1592
+ /** Resolve and reconcile plugin-managed company skills. Requires `skills.managed`. */
1593
+ skills: PluginSkillsClient;
1594
+ /** Read company metadata. Requires `companies.read`. */
1595
+ companies: PluginCompaniesClient;
1596
+ /** Read and write issues, comments, and documents. Requires issue capabilities. */
1597
+ issues: PluginIssuesClient;
1598
+ /** Read and manage agents. Requires `agents.read` for reads; `agents.pause` / `agents.resume` / `agents.invoke` for write ops. */
1599
+ agents: PluginAgentsClient;
1600
+ /** Read and mutate goals. Requires `goals.read` for reads; `goals.create` / `goals.update` for write ops. */
1601
+ goals: PluginGoalsClient;
1602
+ /** Read and manage access memberships and invites. Requires `access.*` capabilities. */
1603
+ access: PluginAccessClient;
1604
+ /** Read and manage authorization grants, policy summaries, previews, and audit entries. Requires `authorization.*` capabilities. */
1605
+ authorization: PluginAuthorizationClient;
1606
+ /** Register getData handlers for the plugin's UI components. */
1607
+ data: PluginDataClient;
1608
+ /** Register performAction handlers for the plugin's UI components. */
1609
+ actions: PluginActionsClient;
1610
+ /** Push real-time events from the worker to the plugin UI via SSE. */
1611
+ streams: PluginStreamsClient;
1612
+ /** Register agent tool handlers. Requires `agent.tools.register`. */
1613
+ tools: PluginToolsClient;
1614
+ /** Write plugin metrics. Requires `metrics.write`. */
1615
+ metrics: PluginMetricsClient;
1616
+ /** Emit plugin-scoped external telemetry. Requires `telemetry.track`. */
1617
+ telemetry: PluginTelemetryClient;
1618
+ /** Structured logger. Output is captured and surfaced in the plugin health dashboard. */
1619
+ logger: PluginLogger;
1620
+ }
1621
+ //# sourceMappingURL=types.d.ts.map