agent-relay 4.0.2 → 4.0.4

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 (175) hide show
  1. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  2. package/bin/agent-relay-broker-darwin-x64 +0 -0
  3. package/bin/agent-relay-broker-linux-arm64 +0 -0
  4. package/bin/agent-relay-broker-linux-x64 +0 -0
  5. package/dist/index.cjs +7906 -2084
  6. package/dist/packages/sdk/src/provisioner/seeder.d.ts +17 -0
  7. package/dist/packages/sdk/src/provisioner/seeder.d.ts.map +1 -0
  8. package/dist/packages/sdk/src/provisioner/seeder.js +419 -0
  9. package/dist/packages/sdk/src/provisioner/seeder.js.map +1 -0
  10. package/dist/packages/sdk/src/provisioner/token.d.ts +38 -0
  11. package/dist/packages/sdk/src/provisioner/token.d.ts.map +1 -0
  12. package/dist/packages/sdk/src/provisioner/token.js +74 -0
  13. package/dist/packages/sdk/src/provisioner/token.js.map +1 -0
  14. package/dist/src/cli/commands/core.d.ts.map +1 -1
  15. package/dist/src/cli/commands/core.js +7 -3
  16. package/dist/src/cli/commands/core.js.map +1 -1
  17. package/dist/src/cli/commands/on/provision.d.ts.map +1 -1
  18. package/dist/src/cli/commands/on/provision.js +8 -3
  19. package/dist/src/cli/commands/on/provision.js.map +1 -1
  20. package/dist/src/cli/commands/on/start.d.ts +5 -0
  21. package/dist/src/cli/commands/on/start.d.ts.map +1 -1
  22. package/dist/src/cli/commands/on/start.js +126 -88
  23. package/dist/src/cli/commands/on/start.js.map +1 -1
  24. package/dist/src/cli/commands/on/symlink-mount.d.ts +12 -0
  25. package/dist/src/cli/commands/on/symlink-mount.d.ts.map +1 -0
  26. package/dist/src/cli/commands/on/symlink-mount.js +304 -0
  27. package/dist/src/cli/commands/on/symlink-mount.js.map +1 -0
  28. package/dist/src/cli/commands/on.d.ts.map +1 -1
  29. package/dist/src/cli/commands/on.js +3 -0
  30. package/dist/src/cli/commands/on.js.map +1 -1
  31. package/install.sh +4 -0
  32. package/package.json +9 -9
  33. package/packages/acp-bridge/package.json +2 -2
  34. package/packages/brand/package.json +1 -1
  35. package/packages/cloud/package.json +2 -2
  36. package/packages/config/package.json +1 -1
  37. package/packages/hooks/package.json +4 -4
  38. package/packages/memory/package.json +2 -2
  39. package/packages/openclaw/package.json +2 -2
  40. package/packages/policy/package.json +2 -2
  41. package/packages/sdk/dist/client.d.ts +3 -10
  42. package/packages/sdk/dist/client.d.ts.map +1 -1
  43. package/packages/sdk/dist/client.js +2 -0
  44. package/packages/sdk/dist/client.js.map +1 -1
  45. package/packages/sdk/dist/provisioner/__tests__/audit.test.d.ts +2 -0
  46. package/packages/sdk/dist/provisioner/__tests__/audit.test.d.ts.map +1 -0
  47. package/packages/sdk/dist/provisioner/__tests__/audit.test.js +45 -0
  48. package/packages/sdk/dist/provisioner/__tests__/audit.test.js.map +1 -0
  49. package/packages/sdk/dist/provisioner/__tests__/compiler.test.d.ts +2 -0
  50. package/packages/sdk/dist/provisioner/__tests__/compiler.test.d.ts.map +1 -0
  51. package/packages/sdk/dist/provisioner/__tests__/compiler.test.js +345 -0
  52. package/packages/sdk/dist/provisioner/__tests__/compiler.test.js.map +1 -0
  53. package/packages/sdk/dist/provisioner/__tests__/presets.test.d.ts +2 -0
  54. package/packages/sdk/dist/provisioner/__tests__/presets.test.d.ts.map +1 -0
  55. package/packages/sdk/dist/provisioner/__tests__/presets.test.js +23 -0
  56. package/packages/sdk/dist/provisioner/__tests__/presets.test.js.map +1 -0
  57. package/packages/sdk/dist/provisioner/__tests__/seeder.test.d.ts +2 -0
  58. package/packages/sdk/dist/provisioner/__tests__/seeder.test.d.ts.map +1 -0
  59. package/packages/sdk/dist/provisioner/__tests__/seeder.test.js +224 -0
  60. package/packages/sdk/dist/provisioner/__tests__/seeder.test.js.map +1 -0
  61. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.d.ts +2 -0
  62. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.d.ts.map +1 -0
  63. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.js +191 -0
  64. package/packages/sdk/dist/provisioner/__tests__/tar-seeder.test.js.map +1 -0
  65. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.d.ts +2 -0
  66. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.d.ts.map +1 -0
  67. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js +127 -0
  68. package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js.map +1 -0
  69. package/packages/sdk/dist/provisioner/__tests__/token.test.d.ts +2 -0
  70. package/packages/sdk/dist/provisioner/__tests__/token.test.d.ts.map +1 -0
  71. package/packages/sdk/dist/provisioner/__tests__/token.test.js +44 -0
  72. package/packages/sdk/dist/provisioner/__tests__/token.test.js.map +1 -0
  73. package/packages/sdk/dist/provisioner/audit.d.ts +19 -0
  74. package/packages/sdk/dist/provisioner/audit.d.ts.map +1 -0
  75. package/packages/sdk/dist/provisioner/audit.js +74 -0
  76. package/packages/sdk/dist/provisioner/audit.js.map +1 -0
  77. package/packages/sdk/dist/provisioner/compiler.d.ts +23 -0
  78. package/packages/sdk/dist/provisioner/compiler.d.ts.map +1 -0
  79. package/packages/sdk/dist/provisioner/compiler.js +355 -0
  80. package/packages/sdk/dist/provisioner/compiler.js.map +1 -0
  81. package/packages/sdk/dist/provisioner/index.d.ts +9 -0
  82. package/packages/sdk/dist/provisioner/index.d.ts.map +1 -0
  83. package/packages/sdk/dist/provisioner/index.js +266 -0
  84. package/packages/sdk/dist/provisioner/index.js.map +1 -0
  85. package/packages/sdk/dist/provisioner/mount.d.ts +14 -0
  86. package/packages/sdk/dist/provisioner/mount.d.ts.map +1 -0
  87. package/packages/sdk/dist/provisioner/mount.js +329 -0
  88. package/packages/sdk/dist/provisioner/mount.js.map +1 -0
  89. package/packages/sdk/dist/provisioner/seeder.d.ts +17 -0
  90. package/packages/sdk/dist/provisioner/seeder.d.ts.map +1 -0
  91. package/packages/sdk/dist/provisioner/seeder.js +419 -0
  92. package/packages/sdk/dist/provisioner/seeder.js.map +1 -0
  93. package/packages/sdk/dist/provisioner/token.d.ts +38 -0
  94. package/packages/sdk/dist/provisioner/token.d.ts.map +1 -0
  95. package/packages/sdk/dist/provisioner/token.js +74 -0
  96. package/packages/sdk/dist/provisioner/token.js.map +1 -0
  97. package/packages/sdk/dist/provisioner/types.d.ts +133 -0
  98. package/packages/sdk/dist/provisioner/types.d.ts.map +1 -0
  99. package/packages/sdk/dist/provisioner/types.js +2 -0
  100. package/packages/sdk/dist/provisioner/types.js.map +1 -0
  101. package/packages/sdk/dist/relay.d.ts +6 -0
  102. package/packages/sdk/dist/relay.d.ts.map +1 -1
  103. package/packages/sdk/dist/relay.js +17 -5
  104. package/packages/sdk/dist/relay.js.map +1 -1
  105. package/packages/sdk/dist/types.d.ts +9 -0
  106. package/packages/sdk/dist/types.d.ts.map +1 -1
  107. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.d.ts +2 -0
  108. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.d.ts.map +1 -0
  109. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.js +331 -0
  110. package/packages/sdk/dist/workflows/__tests__/e2e-permissions.test.js.map +1 -0
  111. package/packages/sdk/dist/workflows/__tests__/permission-types.test.d.ts +2 -0
  112. package/packages/sdk/dist/workflows/__tests__/permission-types.test.d.ts.map +1 -0
  113. package/packages/sdk/dist/workflows/__tests__/permission-types.test.js +124 -0
  114. package/packages/sdk/dist/workflows/__tests__/permission-types.test.js.map +1 -0
  115. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.d.ts +2 -0
  116. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.d.ts.map +1 -0
  117. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.js +526 -0
  118. package/packages/sdk/dist/workflows/__tests__/permissions-integration.test.js.map +1 -0
  119. package/packages/sdk/dist/workflows/dry-run-format.d.ts.map +1 -1
  120. package/packages/sdk/dist/workflows/dry-run-format.js +8 -0
  121. package/packages/sdk/dist/workflows/dry-run-format.js.map +1 -1
  122. package/packages/sdk/dist/workflows/runner.d.ts +14 -0
  123. package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
  124. package/packages/sdk/dist/workflows/runner.js +455 -6
  125. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  126. package/packages/sdk/dist/workflows/types.d.ts +190 -0
  127. package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
  128. package/packages/sdk/dist/workflows/types.js +29 -0
  129. package/packages/sdk/dist/workflows/types.js.map +1 -1
  130. package/packages/sdk/package.json +6 -2
  131. package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +123 -1
  132. package/packages/sdk/src/__tests__/provisioner-mount.test.ts +126 -0
  133. package/packages/sdk/src/__tests__/spawn-token.test.ts +41 -0
  134. package/packages/sdk/src/__tests__/workflow-runner.test.ts +77 -45
  135. package/packages/sdk/src/client.ts +4 -8
  136. package/packages/sdk/src/provisioner/__tests__/audit.test.ts +62 -0
  137. package/packages/sdk/src/provisioner/__tests__/compiler.test.ts +369 -0
  138. package/packages/sdk/src/provisioner/__tests__/presets.test.ts +25 -0
  139. package/packages/sdk/src/provisioner/__tests__/seeder.test.ts +284 -0
  140. package/packages/sdk/src/provisioner/__tests__/tar-seeder.test.ts +249 -0
  141. package/packages/sdk/src/provisioner/__tests__/token-factory.test.ts +172 -0
  142. package/packages/sdk/src/provisioner/__tests__/token.test.ts +53 -0
  143. package/packages/sdk/src/provisioner/audit.ts +104 -0
  144. package/packages/sdk/src/provisioner/compiler.ts +498 -0
  145. package/packages/sdk/src/provisioner/index.ts +332 -0
  146. package/packages/sdk/src/provisioner/mount.ts +419 -0
  147. package/packages/sdk/src/provisioner/seeder.ts +571 -0
  148. package/packages/sdk/src/provisioner/token.ts +112 -0
  149. package/packages/sdk/src/provisioner/types.ts +188 -0
  150. package/packages/sdk/src/relay.ts +31 -9
  151. package/packages/sdk/src/types.ts +9 -0
  152. package/packages/sdk/src/workflows/__tests__/e2e-permissions.test.ts +407 -0
  153. package/packages/sdk/src/workflows/__tests__/fixtures/.agentignore +2 -0
  154. package/packages/sdk/src/workflows/__tests__/fixtures/.reader.agentreadonly +2 -0
  155. package/packages/sdk/src/workflows/__tests__/fixtures/permission-test.yaml +42 -0
  156. package/packages/sdk/src/workflows/__tests__/permission-types.test.ts +154 -0
  157. package/packages/sdk/src/workflows/__tests__/permissions-integration.test.ts +649 -0
  158. package/packages/sdk/src/workflows/builtin-templates/bug-fix.yaml +13 -9
  159. package/packages/sdk/src/workflows/builtin-templates/code-review.yaml +12 -8
  160. package/packages/sdk/src/workflows/builtin-templates/competitive.yaml +11 -7
  161. package/packages/sdk/src/workflows/builtin-templates/documentation.yaml +16 -8
  162. package/packages/sdk/src/workflows/builtin-templates/feature-dev.yaml +13 -9
  163. package/packages/sdk/src/workflows/builtin-templates/refactor.yaml +13 -9
  164. package/packages/sdk/src/workflows/builtin-templates/review-loop.yaml +14 -10
  165. package/packages/sdk/src/workflows/builtin-templates/security-audit.yaml +19 -9
  166. package/packages/sdk/src/workflows/dry-run-format.ts +14 -1
  167. package/packages/sdk/src/workflows/runner.ts +559 -6
  168. package/packages/sdk/src/workflows/schema.json +204 -114
  169. package/packages/sdk/src/workflows/types.ts +266 -1
  170. package/packages/sdk/vitest.config.ts +5 -1
  171. package/packages/sdk-py/pyproject.toml +1 -1
  172. package/packages/telemetry/package.json +1 -1
  173. package/packages/trajectory/package.json +2 -2
  174. package/packages/user-directory/package.json +2 -2
  175. package/packages/utils/package.json +2 -2
@@ -12,6 +12,8 @@ export interface RelayYamlConfig {
12
12
  version: string;
13
13
  name: string;
14
14
  description?: string;
15
+ /** Reusable permission profiles that agents can reference via permissions.profile. */
16
+ permission_profiles?: Record<string, PermissionProfileDefinition>;
15
17
  /** Named paths to external directories used by this workflow.
16
18
  * The primary working directory defaults to cwd and does not need to be declared.
17
19
  * Use this to declare additional directories so the runner can validate them
@@ -124,6 +126,11 @@ export interface AgentDefinition {
124
126
  task?: string;
125
127
  channels?: string[];
126
128
  constraints?: AgentConstraints;
129
+ /**
130
+ * Permission configuration controlling file access, network, and exec restrictions.
131
+ * Omitting this field preserves the default behavior: inherit dotfiles + readwrite access.
132
+ */
133
+ permissions?: AgentPermissions;
127
134
  /** When false, the agent runs as a non-interactive subprocess (no PTY, no relay messaging).
128
135
  * It receives its task as a CLI prompt argument and returns stdout as output.
129
136
  * Default: true (interactive PTY mode). */
@@ -149,7 +156,18 @@ export interface AgentDefinition {
149
156
  skills?: string;
150
157
  }
151
158
 
152
- export type AgentCli = 'claude' | 'codex' | 'gemini' | 'aider' | 'goose' | 'opencode' | 'droid' | 'cursor' | 'cursor-agent' | 'agent' | 'api';
159
+ export type AgentCli =
160
+ | 'claude'
161
+ | 'codex'
162
+ | 'gemini'
163
+ | 'aider'
164
+ | 'goose'
165
+ | 'opencode'
166
+ | 'droid'
167
+ | 'cursor'
168
+ | 'cursor-agent'
169
+ | 'agent'
170
+ | 'api';
153
171
 
154
172
  /** Resource and behavioral constraints for an agent. */
155
173
  export interface AgentConstraints {
@@ -161,6 +179,244 @@ export interface AgentConstraints {
161
179
  idleThresholdSecs?: number;
162
180
  }
163
181
 
182
+ // ── Permission types ────────────────────────────────────────────────────────
183
+
184
+ /**
185
+ * Access preset for role-based permission shortcuts.
186
+ *
187
+ * readonly → read all non-ignored files, write nothing
188
+ * readwrite → read and write all non-ignored files (default behavior)
189
+ * restricted → read/write only explicitly listed paths
190
+ * full → read and write everything, including normally-ignored files
191
+ */
192
+ export type AccessPreset = 'readonly' | 'readwrite' | 'restricted' | 'full';
193
+
194
+ /** Fine-grained network permission with allowlist/denylist. */
195
+ export interface NetworkPermissions {
196
+ /** Host:port pairs the agent may connect to (e.g. ['registry.npmjs.org:443']). */
197
+ allow?: string[];
198
+ /** Host:port patterns to block (e.g. ['*'] to deny all except allowed). */
199
+ deny?: string[];
200
+ }
201
+
202
+ /** Network permission: boolean to allow/deny all, or object for fine-grained control. */
203
+ export type NetworkPermission = boolean | NetworkPermissions;
204
+
205
+ /** Glob-based file permission scopes for an agent. */
206
+ export interface FilePermissions {
207
+ /** Glob patterns the agent may read (e.g. ['src/**', 'docs/**']). */
208
+ read?: string[];
209
+ /** Glob patterns the agent may write (e.g. ['src/tests/**']). */
210
+ write?: string[];
211
+ /** Glob patterns the agent must never access (e.g. ['.env', 'secrets/**']).
212
+ * Deny rules take precedence over read/write grants. */
213
+ deny?: string[];
214
+ }
215
+
216
+ /** Reusable named permission profile shared by one or more agents. */
217
+ export interface PermissionProfileDefinition {
218
+ /** Human-readable summary of the profile's intended use. */
219
+ description?: string;
220
+
221
+ /** Explain why this profile exists or what constraint it is protecting. */
222
+ why?: string;
223
+
224
+ /** Role-based access preset. Expands into file permission rules.
225
+ * Default: 'readwrite'. */
226
+ access?: AccessPreset;
227
+
228
+ /** Inherit patterns from .agentignore and .agentreadonly dotfiles.
229
+ * Default: true. Set to false to ignore dotfiles for this agent. */
230
+ inherit?: boolean;
231
+
232
+ /** Explicit glob-based file read/write/deny rules.
233
+ * Merged on top of the access preset and inherited dotfile patterns. */
234
+ files?: FilePermissions;
235
+
236
+ /** Raw relayauth scopes appended verbatim to the minted token.
237
+ * For power users who need fine-grained control beyond file globs.
238
+ * Example: ['relayfile:fs:read:/src/**', 'relayfile:fs:write:/tests/**'] */
239
+ scopes?: string[];
240
+
241
+ /** Network access control.
242
+ * - undefined: no restriction
243
+ * - false: deny all network access
244
+ * - { allow, deny }: fine-grained host:port allowlist/denylist */
245
+ network?: NetworkPermission;
246
+
247
+ /** Allowlist of shell commands the agent may execute.
248
+ * When set, only commands matching these prefixes are permitted.
249
+ * Example: ['npm test', 'npm run lint', 'git diff']
250
+ * Default: undefined (no restriction). */
251
+ exec?: string[];
252
+ }
253
+
254
+ /**
255
+ * Permission configuration for a workflow agent.
256
+ *
257
+ * All fields are optional — omitting `permissions` entirely preserves the
258
+ * existing default behavior (inherit dotfiles, readwrite access).
259
+ *
260
+ * Resolution order (later overrides earlier):
261
+ * 1. Dotfile patterns (.agentignore / .agentreadonly) when `inherit` is true
262
+ * 2. `access` preset expands into base file rules
263
+ * 3. Explicit `files` globs merge on top
264
+ * 4. `deny` patterns always win (applied last)
265
+ * 5. `scopes` are appended verbatim to the token
266
+ */
267
+ export interface AgentPermissions {
268
+ /** Human-readable summary of what this permission block is for. */
269
+ description?: string;
270
+
271
+ /** Reference a reusable entry from the top-level `permission_profiles` map. */
272
+ profile?: string;
273
+
274
+ /** Explain why these permissions are needed or intentionally constrained. */
275
+ why?: string;
276
+
277
+ /** Role-based access preset. Expands into file permission rules.
278
+ * Default: 'readwrite'. */
279
+ access?: AccessPreset;
280
+
281
+ /** Inherit patterns from .agentignore and .agentreadonly dotfiles.
282
+ * Default: true. Set to false to ignore dotfiles for this agent. */
283
+ inherit?: boolean;
284
+
285
+ /** Explicit glob-based file read/write/deny rules.
286
+ * Merged on top of the access preset and inherited dotfile patterns. */
287
+ files?: FilePermissions;
288
+
289
+ /** Raw relayauth scopes appended verbatim to the minted token.
290
+ * For power users who need fine-grained control beyond file globs.
291
+ * Example: ['relayfile:fs:read:/src/**', 'relayfile:fs:write:/tests/**'] */
292
+ scopes?: string[];
293
+
294
+ /** Network access control.
295
+ * - undefined: no restriction
296
+ * - false: deny all network access
297
+ * - { allow, deny }: fine-grained host:port allowlist/denylist */
298
+ network?: NetworkPermission;
299
+
300
+ /** Allowlist of shell commands the agent may execute.
301
+ * When set, only commands matching these prefixes are permitted.
302
+ * Example: ['npm test', 'npm run lint', 'git diff']
303
+ * Default: undefined (no restriction). */
304
+ exec?: string[];
305
+ }
306
+
307
+ // ── Compiled / resolved permissions ─────────────────────────────────────────
308
+
309
+ /**
310
+ * Fully resolved agent permissions after merging:
311
+ * dotfile patterns + access preset + explicit YAML file rules + custom scopes
312
+ *
313
+ * Produced by the permission resolver at spawn time and used to:
314
+ * 1. Mint the agent's relayauth token (scopes)
315
+ * 2. Configure the relayfile mount (readonlyPaths, readwritePaths, deniedPaths)
316
+ * 3. Enforce runtime restrictions (network, exec allowlist)
317
+ */
318
+ export interface CompiledAgentPermissions {
319
+ /** Agent this permission set applies to. */
320
+ agentName: string;
321
+
322
+ /** Workspace the agent belongs to. */
323
+ workspace: string;
324
+
325
+ /** The effective access level after resolution. */
326
+ effectiveAccess: AccessPreset;
327
+
328
+ /** Whether dotfile patterns were inherited. */
329
+ inherited: boolean;
330
+
331
+ /** Source of each permission layer for audit/debug. */
332
+ sources: PermissionSource[];
333
+
334
+ // ── Resolved file paths ──────────────────────────────────────────────────
335
+
336
+ /** Glob patterns that resolved to read-only access. */
337
+ readonlyPatterns: string[];
338
+
339
+ /** Glob patterns that resolved to read-write access. */
340
+ readwritePatterns: string[];
341
+
342
+ /** Glob patterns explicitly denied (no access). */
343
+ deniedPatterns: string[];
344
+
345
+ /** Concrete file paths with read-only access (after walking the project). */
346
+ readonlyPaths: string[];
347
+
348
+ /** Concrete file paths with read-write access (after walking the project). */
349
+ readwritePaths: string[];
350
+
351
+ /** Concrete file paths denied to the agent. */
352
+ deniedPaths: string[];
353
+
354
+ // ── Token scopes ─────────────────────────────────────────────────────────
355
+
356
+ /** Merged relayauth scopes for the agent's token.
357
+ * Combines auto-generated file scopes + explicit custom scopes. */
358
+ scopes: string[];
359
+
360
+ // ── Runtime restrictions ─────────────────────────────────────────────────
361
+
362
+ /** Network access control. Undefined means unrestricted. */
363
+ network?: NetworkPermission;
364
+
365
+ /** Allowed exec command prefixes. Undefined means unrestricted. */
366
+ exec?: string[];
367
+
368
+ // ── ACL (for relayfile mount) ────────────────────────────────────────────
369
+
370
+ /** Directory-level ACL rules for the relayfile mount.
371
+ * Keys are normalized directory paths, values are rule arrays. */
372
+ acl: Record<string, string[]>;
373
+
374
+ /** Summary counts for quick inspection. */
375
+ summary: {
376
+ readonly: number;
377
+ readwrite: number;
378
+ denied: number;
379
+ customScopes: number;
380
+ };
381
+ }
382
+
383
+ /** Identifies where a permission rule originated. */
384
+ export interface PermissionSource {
385
+ /** Source type. */
386
+ type: 'dotfile' | 'preset' | 'yaml' | 'scope';
387
+ /** Human-readable description (e.g. '.agentignore', 'access: readonly'). */
388
+ label: string;
389
+ /** Number of rules contributed by this source. */
390
+ ruleCount: number;
391
+ }
392
+
393
+ // ── Type guards ─────────────────────────────────────────────────────────────
394
+
395
+ /**
396
+ * Returns true if the agent has restricted permissions —
397
+ * i.e., it has explicit permissions set AND those permissions limit access
398
+ * beyond the default readwrite+inherit behavior.
399
+ *
400
+ * An agent is considered restricted if any of the following are true:
401
+ * - access is 'readonly' or 'restricted'
402
+ * - files.deny has entries
403
+ * - network is false or an allowlist/denylist object
404
+ * - exec allowlist is set (any commands at all = restricted execution)
405
+ * - inherit is explicitly false (opts out of dotfile protections)
406
+ */
407
+ export function isRestrictedAgent(agent: AgentDefinition): boolean {
408
+ const perms = agent.permissions;
409
+ if (!perms) return false;
410
+
411
+ if (perms.access === 'readonly' || perms.access === 'restricted') return true;
412
+ if (perms.files?.deny && perms.files.deny.length > 0) return true;
413
+ if (perms.network === false || (typeof perms.network === 'object' && perms.network !== null)) return true;
414
+ if (perms.exec && perms.exec.length > 0) return true;
415
+ if (perms.inherit === false) return true;
416
+
417
+ return false;
418
+ }
419
+
164
420
  // ── Workflow definitions ────────────────────────────────────────────────────
165
421
 
166
422
  /** Preflight check that runs before any workflow steps. */
@@ -509,6 +765,15 @@ export interface DryRunReport {
509
765
  description?: string;
510
766
  pattern: string;
511
767
  agents: Array<{ name: string; cli: string; role?: string; cwd?: string; stepCount: number }>;
768
+ permissions?: Array<{
769
+ agent: string;
770
+ access: string;
771
+ readPaths: number;
772
+ writePaths: number;
773
+ denyPaths: number;
774
+ scopes: number;
775
+ source: 'yaml' | 'preset' | 'dotfiles' | 'none';
776
+ }>;
512
777
  waves: DryRunWave[];
513
778
  totalSteps: number;
514
779
  maxConcurrency?: number;
@@ -4,7 +4,11 @@ export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
6
  environment: 'node',
7
- include: ['src/__tests__/**/*.test.ts', 'src/workflows/__tests__/**/*.test.ts'],
7
+ include: [
8
+ 'src/__tests__/**/*.test.ts',
9
+ 'src/workflows/__tests__/**/*.test.ts',
10
+ 'src/provisioner/__tests__/presets.test.ts',
11
+ ],
8
12
  exclude: ['src/__tests__/unit.test.ts'],
9
13
  },
10
14
  });
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-relay-sdk"
7
- version = "4.0.2"
7
+ version = "4.0.4"
8
8
  description = "Python SDK for Agent Relay workflows"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/telemetry",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "Anonymous telemetry for Agent Relay usage analytics",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/trajectory",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "Trajectory integration utilities (trail/PDERO) for Relay",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "test:watch": "vitest"
23
23
  },
24
24
  "dependencies": {
25
- "@agent-relay/config": "4.0.2"
25
+ "@agent-relay/config": "4.0.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.19.3",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/user-directory",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "User directory service for agent-relay (per-user credential storage)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "test:watch": "vitest"
23
23
  },
24
24
  "dependencies": {
25
- "@agent-relay/utils": "4.0.2"
25
+ "@agent-relay/utils": "4.0.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.19.3",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/utils",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "Shared utilities for agent-relay: logging, name generation, command resolution, update checking",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/index.js",
@@ -112,7 +112,7 @@
112
112
  "vitest": "^3.2.4"
113
113
  },
114
114
  "dependencies": {
115
- "@agent-relay/config": "4.0.2",
115
+ "@agent-relay/config": "4.0.4",
116
116
  "compare-versions": "^6.1.1"
117
117
  },
118
118
  "publishConfig": {