@tickernelz/paperclip-pro-plugin-sdk 2026.925.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 (72) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +1307 -0
  3. package/dist/.paperclip-build-complete +1 -0
  4. package/dist/bundlers.d.ts +57 -0
  5. package/dist/bundlers.d.ts.map +1 -0
  6. package/dist/bundlers.js +106 -0
  7. package/dist/bundlers.js.map +1 -0
  8. package/dist/define-plugin.d.ts +396 -0
  9. package/dist/define-plugin.d.ts.map +1 -0
  10. package/dist/define-plugin.js +87 -0
  11. package/dist/define-plugin.js.map +1 -0
  12. package/dist/dev-cli.d.ts +3 -0
  13. package/dist/dev-cli.d.ts.map +1 -0
  14. package/dist/dev-cli.js +49 -0
  15. package/dist/dev-cli.js.map +1 -0
  16. package/dist/dev-server.d.ts +34 -0
  17. package/dist/dev-server.d.ts.map +1 -0
  18. package/dist/dev-server.js +194 -0
  19. package/dist/dev-server.js.map +1 -0
  20. package/dist/host-client-factory.d.ts +326 -0
  21. package/dist/host-client-factory.d.ts.map +1 -0
  22. package/dist/host-client-factory.js +688 -0
  23. package/dist/host-client-factory.js.map +1 -0
  24. package/dist/index.d.ts +85 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +86 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/protocol.d.ts +2333 -0
  29. package/dist/protocol.d.ts.map +1 -0
  30. package/dist/protocol.duplex-channel.test.d.ts +2 -0
  31. package/dist/protocol.duplex-channel.test.d.ts.map +1 -0
  32. package/dist/protocol.duplex-channel.test.js +229 -0
  33. package/dist/protocol.duplex-channel.test.js.map +1 -0
  34. package/dist/protocol.js +364 -0
  35. package/dist/protocol.js.map +1 -0
  36. package/dist/testing.d.ts +203 -0
  37. package/dist/testing.d.ts.map +1 -0
  38. package/dist/testing.js +2475 -0
  39. package/dist/testing.js.map +1 -0
  40. package/dist/types.d.ts +1837 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +23 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/ui/clipboard.d.ts +8 -0
  45. package/dist/ui/clipboard.d.ts.map +1 -0
  46. package/dist/ui/clipboard.js +12 -0
  47. package/dist/ui/clipboard.js.map +1 -0
  48. package/dist/ui/components.d.ts +518 -0
  49. package/dist/ui/components.d.ts.map +1 -0
  50. package/dist/ui/components.js +135 -0
  51. package/dist/ui/components.js.map +1 -0
  52. package/dist/ui/hooks.d.ts +155 -0
  53. package/dist/ui/hooks.d.ts.map +1 -0
  54. package/dist/ui/hooks.js +195 -0
  55. package/dist/ui/hooks.js.map +1 -0
  56. package/dist/ui/index.d.ts +55 -0
  57. package/dist/ui/index.d.ts.map +1 -0
  58. package/dist/ui/index.js +52 -0
  59. package/dist/ui/index.js.map +1 -0
  60. package/dist/ui/runtime.d.ts +3 -0
  61. package/dist/ui/runtime.d.ts.map +1 -0
  62. package/dist/ui/runtime.js +30 -0
  63. package/dist/ui/runtime.js.map +1 -0
  64. package/dist/ui/types.d.ts +423 -0
  65. package/dist/ui/types.d.ts.map +1 -0
  66. package/dist/ui/types.js +17 -0
  67. package/dist/ui/types.js.map +1 -0
  68. package/dist/worker-rpc-host.d.ts +128 -0
  69. package/dist/worker-rpc-host.d.ts.map +1 -0
  70. package/dist/worker-rpc-host.js +1877 -0
  71. package/dist/worker-rpc-host.js.map +1 -0
  72. package/package.json +92 -0
@@ -0,0 +1,688 @@
1
+ /**
2
+ * Host-side client factory — creates capability-gated handler maps for
3
+ * servicing worker→host JSON-RPC calls.
4
+ *
5
+ * When a plugin worker calls `ctx.state.get(...)` inside its process, the
6
+ * SDK serializes the call as a JSON-RPC request over stdio. On the host side,
7
+ * the `PluginWorkerManager` receives the request and dispatches it to the
8
+ * handler registered for that method. This module provides a factory that
9
+ * creates those handlers for all `WorkerToHostMethods`, with automatic
10
+ * capability enforcement.
11
+ *
12
+ * ## Design
13
+ *
14
+ * 1. **Capability gating**: Each handler checks the plugin's declared
15
+ * capabilities before executing. If the plugin lacks a required capability,
16
+ * the handler throws a `CapabilityDeniedError` (which the worker manager
17
+ * translates into a JSON-RPC error response with code
18
+ * `CAPABILITY_DENIED`).
19
+ *
20
+ * 2. **Service adapters**: The caller provides a `HostServices` object with
21
+ * concrete implementations of each platform service. The factory wires
22
+ * each handler to the appropriate service method.
23
+ *
24
+ * 3. **Type safety**: The returned handler map is typed as
25
+ * `WorkerToHostHandlers` (from `plugin-worker-manager.ts`) so it plugs
26
+ * directly into `WorkerStartOptions.hostHandlers`.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const handlers = createHostClientHandlers({
31
+ * pluginId: "acme.linear",
32
+ * capabilities: manifest.capabilities,
33
+ * services: {
34
+ * config: { get: () => registry.getConfig(pluginId) },
35
+ * state: { get: ..., set: ..., delete: ... },
36
+ * entities: { upsert: ..., list: ... },
37
+ * // ... all services
38
+ * },
39
+ * });
40
+ *
41
+ * await workerManager.startWorker("acme.linear", {
42
+ * // ...
43
+ * hostHandlers: handlers,
44
+ * });
45
+ * ```
46
+ *
47
+ * @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
48
+ * @see PLUGIN_SPEC.md §15 — Capability Model
49
+ */
50
+ import { PLUGIN_RPC_ERROR_CODES } from "./protocol.js";
51
+ // ---------------------------------------------------------------------------
52
+ // Error types
53
+ // ---------------------------------------------------------------------------
54
+ /**
55
+ * Thrown when a plugin calls a host method it does not have the capability for.
56
+ *
57
+ * The `code` field is set to `PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED` so
58
+ * the worker manager can propagate it as the correct JSON-RPC error code.
59
+ */
60
+ export class CapabilityDeniedError extends Error {
61
+ name = "CapabilityDeniedError";
62
+ code = PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED;
63
+ constructor(pluginId, method, capability) {
64
+ super(`Plugin "${pluginId}" is missing required capability "${capability}" for method "${method}"`);
65
+ }
66
+ }
67
+ /**
68
+ * Thrown when a worker→host call asks for company-scoped data outside the
69
+ * company authorized for the current top-level plugin invocation.
70
+ */
71
+ export class InvocationScopeDeniedError extends Error {
72
+ name = "InvocationScopeDeniedError";
73
+ code = PLUGIN_RPC_ERROR_CODES.INVOCATION_SCOPE_DENIED;
74
+ constructor(pluginId, method, message) {
75
+ super(`Plugin "${pluginId}" is not allowed to perform "${method}": ${message}`);
76
+ }
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Capability → method mapping
80
+ // ---------------------------------------------------------------------------
81
+ /**
82
+ * Maps each worker→host RPC method to the capability required to invoke it.
83
+ * Methods without a capability requirement (e.g. `config.get`, `log`) are
84
+ * mapped to `null`.
85
+ *
86
+ * @see PLUGIN_SPEC.md §15 — Capability Model
87
+ */
88
+ const METHOD_CAPABILITY_MAP = {
89
+ // Config — always allowed
90
+ "config.get": null,
91
+ // Trusted local folders
92
+ "localFolders.declarations": null,
93
+ "localFolders.configure": "local.folders",
94
+ "localFolders.status": "local.folders",
95
+ "localFolders.list": "local.folders",
96
+ "localFolders.readText": "local.folders",
97
+ "localFolders.writeTextAtomic": "local.folders",
98
+ "localFolders.deleteFile": "local.folders",
99
+ // State
100
+ "state.get": "plugin.state.read",
101
+ "state.set": "plugin.state.write",
102
+ "state.delete": "plugin.state.write",
103
+ "db.namespace": "database.namespace.read",
104
+ "db.query": "database.namespace.read",
105
+ "db.execute": "database.namespace.write",
106
+ // Entities — no specific capability required (plugin-scoped by design)
107
+ "entities.upsert": null,
108
+ "entities.list": null,
109
+ // Events
110
+ "events.emit": "events.emit",
111
+ "events.subscribe": "events.subscribe",
112
+ // HTTP
113
+ "http.fetch": "http.outbound",
114
+ // Secrets
115
+ "secrets.resolve": "secrets.read-ref",
116
+ // Activity
117
+ "activity.log": "activity.log.write",
118
+ // Metrics
119
+ "metrics.write": "metrics.write",
120
+ // Telemetry
121
+ "telemetry.track": "telemetry.track",
122
+ // Logger — always allowed
123
+ "log": null,
124
+ // Provider span sink — only a plugin that registers environment drivers may
125
+ // emit a provider span. The gate rejects a span from any other plugin.
126
+ "span.record": "environment.drivers.register",
127
+ // Companies
128
+ "companies.list": "companies.read",
129
+ "companies.get": "companies.read",
130
+ // Projects
131
+ "projects.list": "projects.read",
132
+ "projects.get": "projects.read",
133
+ "projects.listWorkspaces": "project.workspaces.read",
134
+ "projects.getPrimaryWorkspace": "project.workspaces.read",
135
+ "projects.getWorkspaceForIssue": "project.workspaces.read",
136
+ "executionWorkspaces.get": "execution.workspaces.read",
137
+ "projects.managed.get": "projects.managed",
138
+ "projects.managed.reconcile": "projects.managed",
139
+ "projects.managed.reset": "projects.managed",
140
+ "routines.managed.get": "routines.managed",
141
+ "routines.managed.reconcile": "routines.managed",
142
+ "routines.managed.reset": "routines.managed",
143
+ "routines.managed.update": "routines.managed",
144
+ "routines.managed.run": "routines.managed",
145
+ "skills.managed.get": "skills.managed",
146
+ "skills.managed.reconcile": "skills.managed",
147
+ "skills.managed.reset": "skills.managed",
148
+ // Issues
149
+ "issues.list": "issues.read",
150
+ "issues.get": "issues.read",
151
+ "issues.create": "issues.create",
152
+ "issues.update": "issues.update",
153
+ "issues.relations.get": "issue.relations.read",
154
+ "issues.relations.setBlockedBy": "issue.relations.write",
155
+ "issues.relations.addBlockers": "issue.relations.write",
156
+ "issues.relations.removeBlockers": "issue.relations.write",
157
+ "issues.assertCheckoutOwner": "issues.checkout",
158
+ "issues.getSubtree": "issue.subtree.read",
159
+ "issues.requestWakeup": "issues.wakeup",
160
+ "issues.requestWakeups": "issues.wakeup",
161
+ "issues.summaries.getOrchestration": "issues.orchestration.read",
162
+ "issues.listComments": "issue.comments.read",
163
+ "issues.createComment": "issue.comments.create",
164
+ "issues.createInteraction": "issue.interactions.create",
165
+ "issues.listInteractions": "issue.interactions.read",
166
+ "issues.respondInteraction": "issue.interactions.respond",
167
+ "issues.listAttachments": "issue.attachments.read",
168
+ "issues.getAttachmentContent": "issue.attachments.read",
169
+ // Approvals
170
+ "approvals.list": "approvals.read",
171
+ "approvals.get": "approvals.read",
172
+ "approvals.decide": "approvals.respond",
173
+ // Issue Documents
174
+ "issues.documents.list": "issue.documents.read",
175
+ "issues.documents.get": "issue.documents.read",
176
+ "issues.documents.upsert": "issue.documents.write",
177
+ "issues.documents.delete": "issue.documents.write",
178
+ // Agents
179
+ "agents.list": "agents.read",
180
+ "agents.get": "agents.read",
181
+ "agents.pause": "agents.pause",
182
+ "agents.resume": "agents.resume",
183
+ "agents.invoke": "agents.invoke",
184
+ "agents.managed.get": "agents.managed",
185
+ "agents.managed.reconcile": "agents.managed",
186
+ "agents.managed.reset": "agents.managed",
187
+ // Agent Sessions
188
+ "agents.sessions.create": "agent.sessions.create",
189
+ "agents.sessions.list": "agent.sessions.list",
190
+ "agents.sessions.sendMessage": "agent.sessions.send",
191
+ "agents.sessions.close": "agent.sessions.close",
192
+ // Goals
193
+ "goals.list": "goals.read",
194
+ "goals.get": "goals.read",
195
+ "goals.create": "goals.create",
196
+ "goals.update": "goals.update",
197
+ // Access
198
+ "access.members.list": "access.members.read",
199
+ "access.members.get": "access.members.read",
200
+ "access.members.update": "access.members.write",
201
+ "access.invites.list": "access.invites.read",
202
+ "access.invites.create": "access.invites.write",
203
+ "access.invites.revoke": "access.invites.write",
204
+ // Authorization
205
+ "authorization.grants.list": "authorization.grants.read",
206
+ "authorization.grants.set": "authorization.grants.write",
207
+ "authorization.policies.summary": "authorization.policies.read",
208
+ "authorization.policies.get": "authorization.policies.read",
209
+ "authorization.policies.update": "authorization.policies.write",
210
+ "authorization.policies.previewAssignment": "authorization.policies.read",
211
+ "authorization.policies.explainAssignment": "authorization.policies.read",
212
+ "authorization.audit.search": "authorization.audit.read",
213
+ };
214
+ // ---------------------------------------------------------------------------
215
+ // Factory
216
+ // ---------------------------------------------------------------------------
217
+ /**
218
+ * Create a complete handler map for all worker→host JSON-RPC methods.
219
+ *
220
+ * Each handler:
221
+ * 1. Checks the plugin's declared capabilities against the required capability
222
+ * for the method (if any).
223
+ * 2. Delegates to the corresponding service adapter method.
224
+ * 3. Returns the service result, which is serialized as the JSON-RPC response
225
+ * by the worker manager.
226
+ *
227
+ * If a capability check fails, the handler throws a `CapabilityDeniedError`
228
+ * with code `CAPABILITY_DENIED`. The worker manager catches this and sends a
229
+ * JSON-RPC error response to the worker, which surfaces as a `JsonRpcCallError`
230
+ * in the plugin's SDK client.
231
+ *
232
+ * @param options - Plugin ID, capabilities, and service adapters
233
+ * @returns A handler map suitable for `WorkerStartOptions.hostHandlers`
234
+ */
235
+ export function createHostClientHandlers(options) {
236
+ const { pluginId, services } = options;
237
+ const capabilitySet = new Set(options.capabilities);
238
+ const noCompanyScope = { kind: "none" };
239
+ function isRecord(value) {
240
+ return typeof value === "object" && value !== null && !Array.isArray(value);
241
+ }
242
+ function readNonEmptyString(value) {
243
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
244
+ }
245
+ function requestedCompanyScope(method, params) {
246
+ if (method === "companies.list")
247
+ return { kind: "all" };
248
+ if (!isRecord(params))
249
+ return noCompanyScope;
250
+ const companyId = readNonEmptyString(params.companyId);
251
+ if (companyId)
252
+ return { kind: "single", companyId };
253
+ if (params.scopeKind === "company") {
254
+ const scopeId = readNonEmptyString(params.scopeId);
255
+ return scopeId ? { kind: "single", companyId: scopeId } : { kind: "all" };
256
+ }
257
+ if (method === "events.subscribe" && isRecord(params.filter)) {
258
+ const filterCompanyId = readNonEmptyString(params.filter.companyId);
259
+ if (filterCompanyId)
260
+ return { kind: "single", companyId: filterCompanyId };
261
+ }
262
+ return noCompanyScope;
263
+ }
264
+ function requireInvocationCompanyScope(method, params, context) {
265
+ const requested = requestedCompanyScope(method, params);
266
+ if (requested.kind === "none")
267
+ return;
268
+ if (context?.invalidInvocationScope) {
269
+ throw new InvocationScopeDeniedError(pluginId, method, "the worker referenced a missing, expired, or unknown invocation scope");
270
+ }
271
+ const allowedCompanyId = readNonEmptyString(context?.invocationScope?.companyId);
272
+ if (requested.kind === "all") {
273
+ if (method === "companies.list")
274
+ return;
275
+ if (!allowedCompanyId) {
276
+ throw new InvocationScopeDeniedError(pluginId, method, "company context is required");
277
+ }
278
+ throw new InvocationScopeDeniedError(pluginId, method, `the current invocation is scoped to company "${allowedCompanyId}"`);
279
+ }
280
+ if (!allowedCompanyId) {
281
+ throw new InvocationScopeDeniedError(pluginId, method, "company context is required");
282
+ }
283
+ if (requested.companyId !== allowedCompanyId) {
284
+ throw new InvocationScopeDeniedError(pluginId, method, `requested company "${requested.companyId}" but the current invocation is scoped to company "${allowedCompanyId}"`);
285
+ }
286
+ }
287
+ function resolveRequiredCompanyId(method, params, context) {
288
+ if (context?.invalidInvocationScope) {
289
+ throw new InvocationScopeDeniedError(pluginId, method, "the worker referenced a missing, expired, or unknown invocation scope");
290
+ }
291
+ const requested = requestedCompanyScope(method, params);
292
+ const scopedCompanyId = readNonEmptyString(context?.invocationScope?.companyId);
293
+ if (requested.kind === "single") {
294
+ if (!scopedCompanyId) {
295
+ throw new InvocationScopeDeniedError(pluginId, method, "company context is required");
296
+ }
297
+ if (requested.companyId !== scopedCompanyId) {
298
+ throw new InvocationScopeDeniedError(pluginId, method, `requested company "${requested.companyId}" but the current invocation is scoped to company "${scopedCompanyId}"`);
299
+ }
300
+ return scopedCompanyId;
301
+ }
302
+ if (scopedCompanyId)
303
+ return scopedCompanyId;
304
+ throw new InvocationScopeDeniedError(pluginId, method, "company context is required");
305
+ }
306
+ /**
307
+ * Assert that the plugin has the required capability for a method.
308
+ * Throws `CapabilityDeniedError` if the capability is missing.
309
+ */
310
+ function requireCapability(method) {
311
+ const required = METHOD_CAPABILITY_MAP[method];
312
+ if (required === null)
313
+ return; // No capability required
314
+ if (capabilitySet.has(required))
315
+ return;
316
+ throw new CapabilityDeniedError(pluginId, method, required);
317
+ }
318
+ /**
319
+ * Create a capability-gated proxy handler for a method.
320
+ *
321
+ * @param method - The RPC method name (used for capability lookup)
322
+ * @param handler - The actual handler implementation
323
+ * @returns A wrapper that checks capabilities before delegating
324
+ */
325
+ function gated(method, handler) {
326
+ return async (params, context) => {
327
+ requireCapability(method);
328
+ requireInvocationCompanyScope(method, params, context);
329
+ return handler(params, context);
330
+ };
331
+ }
332
+ // -------------------------------------------------------------------------
333
+ // Build the complete handler map
334
+ // -------------------------------------------------------------------------
335
+ return {
336
+ // Config
337
+ "config.get": gated("config.get", async (params, context) => {
338
+ const companyId = resolveRequiredCompanyId("config.get", params, context);
339
+ return services.config.get({ ...params, companyId }, context);
340
+ }),
341
+ "localFolders.declarations": gated("localFolders.declarations", async (params) => {
342
+ return services.localFolders.declarations(params);
343
+ }),
344
+ "localFolders.configure": gated("localFolders.configure", async (params) => {
345
+ return services.localFolders.configure(params);
346
+ }),
347
+ "localFolders.status": gated("localFolders.status", async (params) => {
348
+ return services.localFolders.status(params);
349
+ }),
350
+ "localFolders.list": gated("localFolders.list", async (params) => {
351
+ return services.localFolders.list(params);
352
+ }),
353
+ "localFolders.readText": gated("localFolders.readText", async (params) => {
354
+ return services.localFolders.readText(params);
355
+ }),
356
+ "localFolders.writeTextAtomic": gated("localFolders.writeTextAtomic", async (params) => {
357
+ return services.localFolders.writeTextAtomic(params);
358
+ }),
359
+ "localFolders.deleteFile": gated("localFolders.deleteFile", async (params) => {
360
+ return services.localFolders.deleteFile(params);
361
+ }),
362
+ // State
363
+ "state.get": gated("state.get", async (params) => {
364
+ return services.state.get(params);
365
+ }),
366
+ "state.set": gated("state.set", async (params) => {
367
+ return services.state.set(params);
368
+ }),
369
+ "state.delete": gated("state.delete", async (params) => {
370
+ return services.state.delete(params);
371
+ }),
372
+ "db.namespace": gated("db.namespace", async (params) => {
373
+ return services.db.namespace(params);
374
+ }),
375
+ "db.query": gated("db.query", async (params) => {
376
+ return services.db.query(params);
377
+ }),
378
+ "db.execute": gated("db.execute", async (params) => {
379
+ return services.db.execute(params);
380
+ }),
381
+ // Entities
382
+ "entities.upsert": gated("entities.upsert", async (params) => {
383
+ return services.entities.upsert(params);
384
+ }),
385
+ "entities.list": gated("entities.list", async (params) => {
386
+ return services.entities.list(params);
387
+ }),
388
+ // Events
389
+ "events.emit": gated("events.emit", async (params) => {
390
+ return services.events.emit(params);
391
+ }),
392
+ "events.subscribe": gated("events.subscribe", async (params) => {
393
+ return services.events.subscribe(params);
394
+ }),
395
+ // HTTP
396
+ "http.fetch": gated("http.fetch", async (params) => {
397
+ return services.http.fetch(params);
398
+ }),
399
+ // Secrets
400
+ "secrets.resolve": gated("secrets.resolve", async (params, context) => {
401
+ const companyId = resolveRequiredCompanyId("secrets.resolve", params, context);
402
+ return services.secrets.resolve({ ...params, companyId }, context);
403
+ }),
404
+ // Activity
405
+ "activity.log": gated("activity.log", async (params) => {
406
+ return services.activity.log(params);
407
+ }),
408
+ // Metrics
409
+ "metrics.write": gated("metrics.write", async (params) => {
410
+ return services.metrics.write(params);
411
+ }),
412
+ // Telemetry
413
+ "telemetry.track": gated("telemetry.track", async (params) => {
414
+ return services.telemetry.track(params);
415
+ }),
416
+ // Logger
417
+ "log": gated("log", async (params) => {
418
+ return services.logger.log(params);
419
+ }),
420
+ // Provider span sink. The context carries the host-minted `traceparent`.
421
+ "span.record": gated("span.record", async (params, context) => {
422
+ return services.tracer.record(params, context);
423
+ }),
424
+ // Companies
425
+ "companies.list": gated("companies.list", async (params, context) => {
426
+ const rows = await services.companies.list(params);
427
+ const allowedCompanyId = readNonEmptyString(context?.invocationScope?.companyId);
428
+ if (!allowedCompanyId)
429
+ return rows;
430
+ return rows.filter((company) => isRecord(company) && company.id === allowedCompanyId);
431
+ }),
432
+ "companies.get": gated("companies.get", async (params) => {
433
+ return services.companies.get(params);
434
+ }),
435
+ // Projects
436
+ "projects.list": gated("projects.list", async (params) => {
437
+ return services.projects.list(params);
438
+ }),
439
+ "projects.get": gated("projects.get", async (params) => {
440
+ return services.projects.get(params);
441
+ }),
442
+ "projects.listWorkspaces": gated("projects.listWorkspaces", async (params) => {
443
+ return services.projects.listWorkspaces(params);
444
+ }),
445
+ "projects.getPrimaryWorkspace": gated("projects.getPrimaryWorkspace", async (params) => {
446
+ return services.projects.getPrimaryWorkspace(params);
447
+ }),
448
+ "projects.getWorkspaceForIssue": gated("projects.getWorkspaceForIssue", async (params) => {
449
+ return services.projects.getWorkspaceForIssue(params);
450
+ }),
451
+ "executionWorkspaces.get": gated("executionWorkspaces.get", async (params) => {
452
+ return services.executionWorkspaces.get(params);
453
+ }),
454
+ "projects.managed.get": gated("projects.managed.get", async (params) => {
455
+ return services.projects.getManaged(params);
456
+ }),
457
+ "projects.managed.reconcile": gated("projects.managed.reconcile", async (params) => {
458
+ return services.projects.reconcileManaged(params);
459
+ }),
460
+ "projects.managed.reset": gated("projects.managed.reset", async (params) => {
461
+ return services.projects.resetManaged(params);
462
+ }),
463
+ // Routines
464
+ "routines.managed.get": gated("routines.managed.get", async (params) => {
465
+ return services.routines.managedGet(params);
466
+ }),
467
+ "routines.managed.reconcile": gated("routines.managed.reconcile", async (params) => {
468
+ return services.routines.managedReconcile(params);
469
+ }),
470
+ "routines.managed.reset": gated("routines.managed.reset", async (params) => {
471
+ return services.routines.managedReset(params);
472
+ }),
473
+ "routines.managed.update": gated("routines.managed.update", async (params) => {
474
+ return services.routines.managedUpdate(params);
475
+ }),
476
+ "routines.managed.run": gated("routines.managed.run", async (params) => {
477
+ return services.routines.managedRun(params);
478
+ }),
479
+ // Skills
480
+ "skills.managed.get": gated("skills.managed.get", async (params) => {
481
+ return services.skills.managedGet(params);
482
+ }),
483
+ "skills.managed.reconcile": gated("skills.managed.reconcile", async (params) => {
484
+ return services.skills.managedReconcile(params);
485
+ }),
486
+ "skills.managed.reset": gated("skills.managed.reset", async (params) => {
487
+ return services.skills.managedReset(params);
488
+ }),
489
+ // Issues
490
+ "issues.list": gated("issues.list", async (params) => {
491
+ return services.issues.list(params);
492
+ }),
493
+ "issues.get": gated("issues.get", async (params) => {
494
+ return services.issues.get(params);
495
+ }),
496
+ "issues.create": gated("issues.create", async (params) => {
497
+ return services.issues.create(params);
498
+ }),
499
+ "issues.update": gated("issues.update", async (params) => {
500
+ return services.issues.update(params);
501
+ }),
502
+ "issues.relations.get": gated("issues.relations.get", async (params) => {
503
+ return services.issues.getRelations(params);
504
+ }),
505
+ "issues.relations.setBlockedBy": gated("issues.relations.setBlockedBy", async (params) => {
506
+ return services.issues.setBlockedBy(params);
507
+ }),
508
+ "issues.relations.addBlockers": gated("issues.relations.addBlockers", async (params) => {
509
+ return services.issues.addBlockers(params);
510
+ }),
511
+ "issues.relations.removeBlockers": gated("issues.relations.removeBlockers", async (params) => {
512
+ return services.issues.removeBlockers(params);
513
+ }),
514
+ "issues.assertCheckoutOwner": gated("issues.assertCheckoutOwner", async (params) => {
515
+ return services.issues.assertCheckoutOwner(params);
516
+ }),
517
+ "issues.getSubtree": gated("issues.getSubtree", async (params) => {
518
+ return services.issues.getSubtree(params);
519
+ }),
520
+ "issues.requestWakeup": gated("issues.requestWakeup", async (params) => {
521
+ return services.issues.requestWakeup(params);
522
+ }),
523
+ "issues.requestWakeups": gated("issues.requestWakeups", async (params) => {
524
+ return services.issues.requestWakeups(params);
525
+ }),
526
+ "issues.summaries.getOrchestration": gated("issues.summaries.getOrchestration", async (params) => {
527
+ return services.issues.getOrchestrationSummary(params);
528
+ }),
529
+ "issues.listComments": gated("issues.listComments", async (params) => {
530
+ return services.issues.listComments(params);
531
+ }),
532
+ "issues.createComment": gated("issues.createComment", async (params) => {
533
+ if (params.actorUserId && !capabilitySet.has("issue.comments.create_human_attributed")) {
534
+ throw new CapabilityDeniedError(pluginId, "issues.createComment", "issue.comments.create_human_attributed");
535
+ }
536
+ return services.issues.createComment(params);
537
+ }),
538
+ "issues.createInteraction": gated("issues.createInteraction", async (params) => {
539
+ return services.issues.createInteraction(params);
540
+ }),
541
+ "issues.listInteractions": gated("issues.listInteractions", async (params) => {
542
+ return services.issues.listInteractions(params);
543
+ }),
544
+ "issues.respondInteraction": gated("issues.respondInteraction", async (params) => {
545
+ return services.issues.respondInteraction(params);
546
+ }),
547
+ "issues.listAttachments": gated("issues.listAttachments", async (params) => {
548
+ return services.issues.listAttachments(params);
549
+ }),
550
+ "issues.getAttachmentContent": gated("issues.getAttachmentContent", async (params) => {
551
+ return services.issues.getAttachmentContent(params);
552
+ }),
553
+ // Approvals
554
+ "approvals.list": gated("approvals.list", async (params) => {
555
+ return services.approvals.list(params);
556
+ }),
557
+ "approvals.get": gated("approvals.get", async (params) => {
558
+ return services.approvals.get(params);
559
+ }),
560
+ "approvals.decide": gated("approvals.decide", async (params) => {
561
+ return services.approvals.decide(params);
562
+ }),
563
+ // Issue Documents
564
+ "issues.documents.list": gated("issues.documents.list", async (params) => {
565
+ return services.issueDocuments.list(params);
566
+ }),
567
+ "issues.documents.get": gated("issues.documents.get", async (params) => {
568
+ return services.issueDocuments.get(params);
569
+ }),
570
+ "issues.documents.upsert": gated("issues.documents.upsert", async (params) => {
571
+ return services.issueDocuments.upsert(params);
572
+ }),
573
+ "issues.documents.delete": gated("issues.documents.delete", async (params) => {
574
+ return services.issueDocuments.delete(params);
575
+ }),
576
+ // Agents
577
+ "agents.list": gated("agents.list", async (params) => {
578
+ return services.agents.list(params);
579
+ }),
580
+ "agents.get": gated("agents.get", async (params) => {
581
+ return services.agents.get(params);
582
+ }),
583
+ "agents.pause": gated("agents.pause", async (params) => {
584
+ return services.agents.pause(params);
585
+ }),
586
+ "agents.resume": gated("agents.resume", async (params) => {
587
+ return services.agents.resume(params);
588
+ }),
589
+ "agents.invoke": gated("agents.invoke", async (params) => {
590
+ return services.agents.invoke(params);
591
+ }),
592
+ "agents.managed.get": gated("agents.managed.get", async (params) => {
593
+ return services.agents.managedGet(params);
594
+ }),
595
+ "agents.managed.reconcile": gated("agents.managed.reconcile", async (params) => {
596
+ return services.agents.managedReconcile(params);
597
+ }),
598
+ "agents.managed.reset": gated("agents.managed.reset", async (params) => {
599
+ return services.agents.managedReset(params);
600
+ }),
601
+ // Agent Sessions
602
+ "agents.sessions.create": gated("agents.sessions.create", async (params) => {
603
+ return services.agentSessions.create(params);
604
+ }),
605
+ "agents.sessions.list": gated("agents.sessions.list", async (params) => {
606
+ return services.agentSessions.list(params);
607
+ }),
608
+ "agents.sessions.sendMessage": gated("agents.sessions.sendMessage", async (params) => {
609
+ return services.agentSessions.sendMessage(params);
610
+ }),
611
+ "agents.sessions.close": gated("agents.sessions.close", async (params) => {
612
+ return services.agentSessions.close(params);
613
+ }),
614
+ // Goals
615
+ "goals.list": gated("goals.list", async (params) => {
616
+ return services.goals.list(params);
617
+ }),
618
+ "goals.get": gated("goals.get", async (params) => {
619
+ return services.goals.get(params);
620
+ }),
621
+ "goals.create": gated("goals.create", async (params) => {
622
+ return services.goals.create(params);
623
+ }),
624
+ "goals.update": gated("goals.update", async (params) => {
625
+ return services.goals.update(params);
626
+ }),
627
+ // Access
628
+ "access.members.list": gated("access.members.list", async (params) => {
629
+ return services.access.listMembers(params);
630
+ }),
631
+ "access.members.get": gated("access.members.get", async (params) => {
632
+ return services.access.getMember(params);
633
+ }),
634
+ "access.members.update": gated("access.members.update", async (params) => {
635
+ return services.access.updateMember(params);
636
+ }),
637
+ "access.invites.list": gated("access.invites.list", async (params) => {
638
+ return services.access.listInvites(params);
639
+ }),
640
+ "access.invites.create": gated("access.invites.create", async (params) => {
641
+ return services.access.createInvite(params);
642
+ }),
643
+ "access.invites.revoke": gated("access.invites.revoke", async (params) => {
644
+ return services.access.revokeInvite(params);
645
+ }),
646
+ // Authorization
647
+ "authorization.grants.list": gated("authorization.grants.list", async (params) => {
648
+ return services.authorization.listGrants(params);
649
+ }),
650
+ "authorization.grants.set": gated("authorization.grants.set", async (params) => {
651
+ return services.authorization.setGrants(params);
652
+ }),
653
+ "authorization.policies.summary": gated("authorization.policies.summary", async (params) => {
654
+ return services.authorization.policySummary(params);
655
+ }),
656
+ "authorization.policies.get": gated("authorization.policies.get", async (params) => {
657
+ return services.authorization.getPolicy(params);
658
+ }),
659
+ "authorization.policies.update": gated("authorization.policies.update", async (params) => {
660
+ return services.authorization.updatePolicy(params);
661
+ }),
662
+ "authorization.policies.previewAssignment": gated("authorization.policies.previewAssignment", async (params) => {
663
+ return services.authorization.previewAssignment(params);
664
+ }),
665
+ "authorization.policies.explainAssignment": gated("authorization.policies.explainAssignment", async (params) => {
666
+ return services.authorization.explainAssignment(params);
667
+ }),
668
+ "authorization.audit.search": gated("authorization.audit.search", async (params) => {
669
+ return services.authorization.searchAudit(params);
670
+ }),
671
+ };
672
+ }
673
+ // ---------------------------------------------------------------------------
674
+ // Utility: getRequiredCapability
675
+ // ---------------------------------------------------------------------------
676
+ /**
677
+ * Get the capability required for a given worker→host method, or `null` if
678
+ * no capability is required.
679
+ *
680
+ * Useful for inspecting capability requirements without calling the factory.
681
+ *
682
+ * @param method - The worker→host method name
683
+ * @returns The required capability, or `null`
684
+ */
685
+ export function getRequiredCapability(method) {
686
+ return METHOD_CAPABILITY_MAP[method];
687
+ }
688
+ //# sourceMappingURL=host-client-factory.js.map