@renseiai/agentfactory 0.8.19 → 0.8.21

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 (87) hide show
  1. package/dist/src/config/repository-config.d.ts +7 -0
  2. package/dist/src/config/repository-config.d.ts.map +1 -1
  3. package/dist/src/config/repository-config.js +15 -1
  4. package/dist/src/config/repository-config.test.js +1 -1
  5. package/dist/src/governor/decision-engine-adapter.js +5 -10
  6. package/dist/src/governor/decision-engine-adapter.test.js +13 -14
  7. package/dist/src/governor/decision-engine.js +3 -7
  8. package/dist/src/governor/decision-engine.test.js +5 -5
  9. package/dist/src/index.d.ts +1 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +1 -0
  12. package/dist/src/merge-queue/adapters/local.d.ts +68 -0
  13. package/dist/src/merge-queue/adapters/local.d.ts.map +1 -0
  14. package/dist/src/merge-queue/adapters/local.js +136 -0
  15. package/dist/src/merge-queue/adapters/local.test.d.ts +2 -0
  16. package/dist/src/merge-queue/adapters/local.test.d.ts.map +1 -0
  17. package/dist/src/merge-queue/adapters/local.test.js +176 -0
  18. package/dist/src/merge-queue/index.d.ts +13 -5
  19. package/dist/src/merge-queue/index.d.ts.map +1 -1
  20. package/dist/src/merge-queue/index.js +13 -6
  21. package/dist/src/merge-queue/merge-queue.integration.test.js +19 -0
  22. package/dist/src/merge-queue/merge-worker.d.ts.map +1 -1
  23. package/dist/src/merge-queue/merge-worker.js +29 -0
  24. package/dist/src/merge-queue/types.d.ts +1 -1
  25. package/dist/src/merge-queue/types.d.ts.map +1 -1
  26. package/dist/src/orchestrator/index.d.ts +4 -0
  27. package/dist/src/orchestrator/index.d.ts.map +1 -1
  28. package/dist/src/orchestrator/index.js +3 -0
  29. package/dist/src/orchestrator/orchestrator.d.ts +31 -0
  30. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  31. package/dist/src/orchestrator/orchestrator.js +263 -11
  32. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
  33. package/dist/src/orchestrator/parse-work-result.js +3 -1
  34. package/dist/src/orchestrator/parse-work-result.test.js +6 -0
  35. package/dist/src/orchestrator/quality-baseline.d.ts +83 -0
  36. package/dist/src/orchestrator/quality-baseline.d.ts.map +1 -0
  37. package/dist/src/orchestrator/quality-baseline.js +313 -0
  38. package/dist/src/orchestrator/quality-baseline.test.d.ts +2 -0
  39. package/dist/src/orchestrator/quality-baseline.test.d.ts.map +1 -0
  40. package/dist/src/orchestrator/quality-baseline.test.js +448 -0
  41. package/dist/src/orchestrator/quality-ratchet.d.ts +70 -0
  42. package/dist/src/orchestrator/quality-ratchet.d.ts.map +1 -0
  43. package/dist/src/orchestrator/quality-ratchet.js +162 -0
  44. package/dist/src/orchestrator/quality-ratchet.test.d.ts +2 -0
  45. package/dist/src/orchestrator/quality-ratchet.test.d.ts.map +1 -0
  46. package/dist/src/orchestrator/quality-ratchet.test.js +335 -0
  47. package/dist/src/orchestrator/types.d.ts +2 -0
  48. package/dist/src/orchestrator/types.d.ts.map +1 -1
  49. package/dist/src/providers/codex-app-server-provider.d.ts +37 -1
  50. package/dist/src/providers/codex-app-server-provider.d.ts.map +1 -1
  51. package/dist/src/providers/codex-app-server-provider.js +290 -35
  52. package/dist/src/providers/codex-app-server-provider.test.js +72 -12
  53. package/dist/src/providers/codex-approval-bridge.d.ts +49 -0
  54. package/dist/src/providers/codex-approval-bridge.d.ts.map +1 -0
  55. package/dist/src/providers/codex-approval-bridge.js +117 -0
  56. package/dist/src/providers/codex-approval-bridge.test.d.ts +2 -0
  57. package/dist/src/providers/codex-approval-bridge.test.d.ts.map +1 -0
  58. package/dist/src/providers/codex-approval-bridge.test.js +188 -0
  59. package/dist/src/providers/types.d.ts +25 -0
  60. package/dist/src/providers/types.d.ts.map +1 -1
  61. package/dist/src/routing/types.d.ts +1 -1
  62. package/dist/src/templates/adapters.d.ts +25 -0
  63. package/dist/src/templates/adapters.d.ts.map +1 -1
  64. package/dist/src/templates/adapters.js +70 -0
  65. package/dist/src/templates/adapters.test.js +49 -0
  66. package/dist/src/templates/index.d.ts +1 -0
  67. package/dist/src/templates/index.d.ts.map +1 -1
  68. package/dist/src/templates/registry.d.ts +8 -0
  69. package/dist/src/templates/registry.d.ts.map +1 -1
  70. package/dist/src/templates/registry.js +11 -0
  71. package/dist/src/templates/types.d.ts +22 -0
  72. package/dist/src/templates/types.d.ts.map +1 -1
  73. package/dist/src/templates/types.js +12 -0
  74. package/dist/src/tools/index.d.ts +2 -0
  75. package/dist/src/tools/index.d.ts.map +1 -1
  76. package/dist/src/tools/index.js +1 -0
  77. package/dist/src/tools/registry.d.ts +9 -1
  78. package/dist/src/tools/registry.d.ts.map +1 -1
  79. package/dist/src/tools/registry.js +13 -1
  80. package/dist/src/tools/stdio-server-entry.d.ts +25 -0
  81. package/dist/src/tools/stdio-server-entry.d.ts.map +1 -0
  82. package/dist/src/tools/stdio-server-entry.js +205 -0
  83. package/dist/src/tools/stdio-server.d.ts +87 -0
  84. package/dist/src/tools/stdio-server.d.ts.map +1 -0
  85. package/dist/src/tools/stdio-server.js +138 -0
  86. package/dist/src/workflow/workflow-types.d.ts +3 -3
  87. package/package.json +3 -2
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Codex Approval Bridge (SUP-1747)
3
+ *
4
+ * Evaluates Codex App Server `requestApproval` events against deny patterns
5
+ * ported from Claude's `autonomousCanUseTool` callback (claude-provider.ts:33-112).
6
+ *
7
+ * When the Codex App Server's `approvalPolicy` is set to `'onRequest'`, every
8
+ * tool execution flows through this bridge. The bridge auto-approves safe commands
9
+ * and declines destructive patterns — giving Codex the same safety guardrails as Claude
10
+ * without requiring human interaction.
11
+ *
12
+ * Architecture:
13
+ * App Server emits → requestApproval notification
14
+ * Approval Bridge evaluates → deny patterns + permission config
15
+ * Bridge responds → accept | decline | acceptForSession
16
+ */
17
+ /**
18
+ * Hardcoded safety deny patterns — always active regardless of template config.
19
+ * These mirror the deny-list in Claude's `autonomousCanUseTool` callback.
20
+ */
21
+ export const SAFETY_DENY_PATTERNS = [
22
+ // 1. rm of filesystem root
23
+ { pattern: /rm\s+(-[a-z]*f[a-z]*\s+)?\/\s*$/, reason: 'rm of filesystem root blocked' },
24
+ // 2. worktree remove/prune — orchestrator manages worktree lifecycle
25
+ { pattern: /git\s+worktree\s+(remove|prune)/, reason: 'worktree remove/prune blocked per project rules' },
26
+ // 3. hard reset
27
+ { pattern: /git\s+reset\s+--hard/, reason: 'reset --hard blocked' },
28
+ // 4. force push (evaluated separately for --force-with-lease exception)
29
+ // Handled in evaluateCommandApproval directly
30
+ // 5. branch switching — agents must not change the checked-out branch
31
+ { pattern: /git\s+(checkout|switch)\b/, reason: 'git checkout/switch blocked — agents must not change the checked-out branch' },
32
+ ];
33
+ // ---------------------------------------------------------------------------
34
+ // Command Approval
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Evaluate a shell command against safety deny patterns and optional
38
+ * template-level permission patterns.
39
+ *
40
+ * Evaluation order:
41
+ * 1. Safety deny patterns (always checked first, cannot be overridden)
42
+ * 2. Template deny patterns (from `tools.disallow`)
43
+ * 3. Template allow patterns (from `tools.allow`, if present)
44
+ * 4. Default: acceptForSession
45
+ */
46
+ export function evaluateCommandApproval(command, permissionConfig) {
47
+ const cmd = command.trim();
48
+ if (!cmd)
49
+ return { action: 'acceptForSession' };
50
+ // --- Safety deny patterns (always enforced) ---
51
+ // Check basic deny patterns
52
+ for (const { pattern, reason } of SAFETY_DENY_PATTERNS) {
53
+ if (pattern.test(cmd)) {
54
+ return { action: 'decline', reason };
55
+ }
56
+ }
57
+ // Force push — special handling for --force-with-lease exception
58
+ if (/git\s+push\b/.test(cmd) && /(--force\b|-f\b)/.test(cmd)) {
59
+ if (/--force-with-lease/.test(cmd)) {
60
+ // --force-with-lease to main/master is still blocked
61
+ if (/\b(main|master)\b/.test(cmd)) {
62
+ return { action: 'decline', reason: 'force push to main/master blocked' };
63
+ }
64
+ // --force-with-lease on feature branches is allowed
65
+ return { action: 'acceptForSession', reason: '--force-with-lease on feature branch allowed' };
66
+ }
67
+ return { action: 'decline', reason: 'force push blocked — use --force-with-lease for safety' };
68
+ }
69
+ // --- Template deny patterns (from tools.disallow) ---
70
+ if (permissionConfig?.deniedCommandPatterns) {
71
+ for (const { pattern, reason } of permissionConfig.deniedCommandPatterns) {
72
+ if (pattern.test(cmd)) {
73
+ return { action: 'decline', reason };
74
+ }
75
+ }
76
+ }
77
+ // --- Template allow patterns (from tools.allow) ---
78
+ // If allow patterns are defined, only matching commands are accepted.
79
+ // Commands that don't match any allow pattern are declined.
80
+ if (permissionConfig?.allowedCommandPatterns && permissionConfig.allowedCommandPatterns.length > 0) {
81
+ for (const pattern of permissionConfig.allowedCommandPatterns) {
82
+ if (pattern.test(cmd)) {
83
+ return { action: 'acceptForSession' };
84
+ }
85
+ }
86
+ return { action: 'decline', reason: 'command not in allowed list' };
87
+ }
88
+ // Default: accept for session (auto-approve for remainder of session)
89
+ return { action: 'acceptForSession' };
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // File Change Approval
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * Evaluate a file change (write/edit) against safety rules and optional
96
+ * template-level permissions.
97
+ */
98
+ export function evaluateFileChangeApproval(filePath, cwd, permissionConfig) {
99
+ // Block writes outside worktree root
100
+ if (!filePath.startsWith(cwd)) {
101
+ return { action: 'decline', reason: 'file change outside worktree blocked' };
102
+ }
103
+ // Block .git directory modifications
104
+ if (/\/\.git(\/|$)/.test(filePath)) {
105
+ return { action: 'decline', reason: '.git directory modification blocked' };
106
+ }
107
+ // Template-level file permission restrictions
108
+ if (permissionConfig) {
109
+ if (!permissionConfig.allowFileEdits && filePath !== cwd) {
110
+ return { action: 'decline', reason: 'file edits blocked by template' };
111
+ }
112
+ if (!permissionConfig.allowFileWrites && filePath !== cwd) {
113
+ return { action: 'decline', reason: 'file writes blocked by template' };
114
+ }
115
+ }
116
+ return { action: 'acceptForSession' };
117
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=codex-approval-bridge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-approval-bridge.test.d.ts","sourceRoot":"","sources":["../../../src/providers/codex-approval-bridge.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,188 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { evaluateCommandApproval, evaluateFileChangeApproval, SAFETY_DENY_PATTERNS, } from './codex-approval-bridge.js';
3
+ // ---------------------------------------------------------------------------
4
+ // evaluateCommandApproval — Safety Deny Patterns
5
+ // ---------------------------------------------------------------------------
6
+ describe('evaluateCommandApproval', () => {
7
+ describe('safety deny patterns (always enforced)', () => {
8
+ it('blocks rm of filesystem root', () => {
9
+ const result = evaluateCommandApproval('rm -rf / ');
10
+ expect(result.action).toBe('decline');
11
+ expect(result.reason).toContain('rm of filesystem root');
12
+ });
13
+ it('blocks rm -f /', () => {
14
+ const result = evaluateCommandApproval('rm -f / ');
15
+ expect(result.action).toBe('decline');
16
+ });
17
+ it('allows rm of normal directories', () => {
18
+ const result = evaluateCommandApproval('rm -rf /tmp/test');
19
+ expect(result.action).toBe('acceptForSession');
20
+ });
21
+ it('blocks git worktree remove', () => {
22
+ const result = evaluateCommandApproval('git worktree remove /path/to/worktree');
23
+ expect(result.action).toBe('decline');
24
+ expect(result.reason).toContain('worktree remove/prune');
25
+ });
26
+ it('blocks git worktree prune', () => {
27
+ const result = evaluateCommandApproval('git worktree prune');
28
+ expect(result.action).toBe('decline');
29
+ expect(result.reason).toContain('worktree remove/prune');
30
+ });
31
+ it('allows git worktree add', () => {
32
+ const result = evaluateCommandApproval('git worktree add /path/to/worktree');
33
+ expect(result.action).toBe('acceptForSession');
34
+ });
35
+ it('blocks git reset --hard', () => {
36
+ const result = evaluateCommandApproval('git reset --hard HEAD');
37
+ expect(result.action).toBe('decline');
38
+ expect(result.reason).toContain('reset --hard');
39
+ });
40
+ it('allows git reset --soft', () => {
41
+ const result = evaluateCommandApproval('git reset --soft HEAD~1');
42
+ expect(result.action).toBe('acceptForSession');
43
+ });
44
+ it('blocks git push --force', () => {
45
+ const result = evaluateCommandApproval('git push --force origin feature');
46
+ expect(result.action).toBe('decline');
47
+ expect(result.reason).toContain('force push blocked');
48
+ });
49
+ it('blocks git push -f', () => {
50
+ const result = evaluateCommandApproval('git push -f origin feature');
51
+ expect(result.action).toBe('decline');
52
+ });
53
+ it('allows --force-with-lease on feature branches', () => {
54
+ const result = evaluateCommandApproval('git push --force-with-lease origin feature-branch');
55
+ expect(result.action).toBe('acceptForSession');
56
+ });
57
+ it('blocks --force-with-lease on main', () => {
58
+ const result = evaluateCommandApproval('git push --force-with-lease origin main');
59
+ expect(result.action).toBe('decline');
60
+ expect(result.reason).toContain('main/master');
61
+ });
62
+ it('blocks --force-with-lease on master', () => {
63
+ const result = evaluateCommandApproval('git push --force-with-lease origin master');
64
+ expect(result.action).toBe('decline');
65
+ });
66
+ it('blocks git checkout', () => {
67
+ const result = evaluateCommandApproval('git checkout main');
68
+ expect(result.action).toBe('decline');
69
+ expect(result.reason).toContain('checkout/switch blocked');
70
+ });
71
+ it('blocks git switch', () => {
72
+ const result = evaluateCommandApproval('git switch develop');
73
+ expect(result.action).toBe('decline');
74
+ expect(result.reason).toContain('checkout/switch blocked');
75
+ });
76
+ });
77
+ describe('safe commands (default accept)', () => {
78
+ it('accepts empty command', () => {
79
+ const result = evaluateCommandApproval('');
80
+ expect(result.action).toBe('acceptForSession');
81
+ });
82
+ it('accepts pnpm install', () => {
83
+ const result = evaluateCommandApproval('pnpm install');
84
+ expect(result.action).toBe('acceptForSession');
85
+ });
86
+ it('accepts git commit', () => {
87
+ const result = evaluateCommandApproval('git commit -m "fix: something"');
88
+ expect(result.action).toBe('acceptForSession');
89
+ });
90
+ it('accepts git push (no force)', () => {
91
+ const result = evaluateCommandApproval('git push origin feature-branch');
92
+ expect(result.action).toBe('acceptForSession');
93
+ });
94
+ it('accepts ls, cat, grep', () => {
95
+ expect(evaluateCommandApproval('ls -la').action).toBe('acceptForSession');
96
+ expect(evaluateCommandApproval('cat file.txt').action).toBe('acceptForSession');
97
+ expect(evaluateCommandApproval('grep -r pattern .').action).toBe('acceptForSession');
98
+ });
99
+ });
100
+ describe('template permission patterns', () => {
101
+ const permissionConfig = {
102
+ allowedCommandPatterns: [/^pnpm\b/, /^git\s+commit\b/, /^git\s+push\b/],
103
+ deniedCommandPatterns: [
104
+ { pattern: /^npm\b/, reason: 'npm blocked by template' },
105
+ ],
106
+ allowFileEdits: true,
107
+ allowFileWrites: true,
108
+ };
109
+ it('declines commands matching template deny patterns', () => {
110
+ const result = evaluateCommandApproval('npm install', permissionConfig);
111
+ expect(result.action).toBe('decline');
112
+ expect(result.reason).toContain('npm blocked by template');
113
+ });
114
+ it('accepts commands matching template allow patterns', () => {
115
+ const result = evaluateCommandApproval('pnpm install', permissionConfig);
116
+ expect(result.action).toBe('acceptForSession');
117
+ });
118
+ it('declines commands not in allow list when allow patterns are defined', () => {
119
+ const result = evaluateCommandApproval('cargo build', permissionConfig);
120
+ expect(result.action).toBe('decline');
121
+ expect(result.reason).toContain('not in allowed list');
122
+ });
123
+ it('safety patterns take precedence over template allow patterns', () => {
124
+ // git checkout is allowed by template (matches git push pattern? no)
125
+ // but blocked by safety
126
+ const result = evaluateCommandApproval('git checkout main', permissionConfig);
127
+ expect(result.action).toBe('decline');
128
+ expect(result.reason).toContain('checkout/switch blocked');
129
+ });
130
+ });
131
+ it('has the expected number of safety deny patterns', () => {
132
+ // 4 patterns in the array + force push handled separately = 5 total deny cases
133
+ expect(SAFETY_DENY_PATTERNS.length).toBe(4);
134
+ });
135
+ });
136
+ // ---------------------------------------------------------------------------
137
+ // evaluateFileChangeApproval
138
+ // ---------------------------------------------------------------------------
139
+ describe('evaluateFileChangeApproval', () => {
140
+ const cwd = '/home/user/worktree';
141
+ it('accepts file changes within worktree', () => {
142
+ const result = evaluateFileChangeApproval('/home/user/worktree/src/index.ts', cwd);
143
+ expect(result.action).toBe('acceptForSession');
144
+ });
145
+ it('blocks file changes outside worktree', () => {
146
+ const result = evaluateFileChangeApproval('/etc/passwd', cwd);
147
+ expect(result.action).toBe('decline');
148
+ expect(result.reason).toContain('outside worktree');
149
+ });
150
+ it('blocks .git directory modifications', () => {
151
+ const result = evaluateFileChangeApproval('/home/user/worktree/.git/config', cwd);
152
+ expect(result.action).toBe('decline');
153
+ expect(result.reason).toContain('.git directory');
154
+ });
155
+ it('blocks .git file at root', () => {
156
+ const result = evaluateFileChangeApproval('/home/user/worktree/.git', cwd);
157
+ expect(result.action).toBe('decline');
158
+ expect(result.reason).toContain('.git directory');
159
+ });
160
+ it('allows files with .git in the name but not .git directory', () => {
161
+ const result = evaluateFileChangeApproval('/home/user/worktree/.gitignore', cwd);
162
+ expect(result.action).toBe('acceptForSession');
163
+ });
164
+ describe('template permission restrictions', () => {
165
+ it('blocks file edits when template disallows', () => {
166
+ const config = {
167
+ allowedCommandPatterns: [],
168
+ deniedCommandPatterns: [],
169
+ allowFileEdits: false,
170
+ allowFileWrites: true,
171
+ };
172
+ const result = evaluateFileChangeApproval('/home/user/worktree/src/index.ts', cwd, config);
173
+ expect(result.action).toBe('decline');
174
+ expect(result.reason).toContain('file edits blocked');
175
+ });
176
+ it('blocks file writes when template disallows', () => {
177
+ const config = {
178
+ allowedCommandPatterns: [],
179
+ deniedCommandPatterns: [],
180
+ allowFileEdits: true,
181
+ allowFileWrites: false,
182
+ };
183
+ const result = evaluateFileChangeApproval('/home/user/worktree/src/index.ts', cwd, config);
184
+ expect(result.action).toBe('decline');
185
+ expect(result.reason).toContain('file writes blocked');
186
+ });
187
+ });
188
+ });
@@ -88,6 +88,31 @@ export interface AgentSpawnConfig {
88
88
  * When omitted, the provider's default applies.
89
89
  */
90
90
  maxTurns?: number;
91
+ /**
92
+ * Stdio MCP server configurations for Codex provider (SUP-1744).
93
+ * Created by ToolRegistry.createStdioServerConfigs() from registered plugins.
94
+ * Passed to Codex app-server via config/batchWrite so it can spawn and
95
+ * connect to these tool servers.
96
+ */
97
+ mcpStdioServers?: Array<{
98
+ name: string;
99
+ command: string;
100
+ args: string[];
101
+ env?: Record<string, string>;
102
+ }>;
103
+ /**
104
+ * Persistent system instructions for Codex App Server (SUP-1746).
105
+ * Passed via `instructions` on `thread/start`. Contains safety rules,
106
+ * project instructions (AGENTS.md), and work-type context.
107
+ * Separate from `prompt` which contains only the task-specific directive.
108
+ */
109
+ baseInstructions?: string;
110
+ /**
111
+ * Structured permission config for Codex approval bridge (SUP-1748).
112
+ * Translates template `tools.allow` / `tools.disallow` into patterns
113
+ * consumed by the approval bridge for runtime tool evaluation.
114
+ */
115
+ permissionConfig?: import('../templates/adapters.js').CodexPermissionConfig;
91
116
  }
92
117
  /**
93
118
  * Handle to a running agent session.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAA;AAEhF;;;;;GAKG;AACH;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAAyB;IACxC,sEAAsE;IACtE,wBAAwB,EAAE,OAAO,CAAA;IACjC,oDAAoD;IACpD,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAEhC,yDAAyD;IACzD,QAAQ,CAAC,YAAY,EAAE,yBAAyB,CAAA;IAEhD,gCAAgC;IAChC,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAA;IAE5C,8CAA8C;IAC9C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAA;CACjE;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,+CAA+C;IAC/C,eAAe,EAAE,eAAe,CAAA;IAChC,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAA;IACnB,wDAAwD;IACxD,cAAc,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAA;IACpD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,gCAAgC,EAAE,eAAe,CAAC,CAAA;IACrF;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IACjC;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,gBAAgB,GAChB,uBAAuB,GACvB,iBAAiB,GACjB,oBAAoB,GACpB,sBAAsB,GACtB,gBAAgB,GAChB,eAAe,CAAA;AAEnB,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4BAA4B;AAC5B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,gBAAgB,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,OAAO,CAAA;CACb;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,2BAA2B,EAAE,YAAY,CAAA;IAC/D,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4BAA4B;AAC5B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,aAAa,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,qCAAqC;AACrC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sBAAsB;IACtB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,kBAAkB;AAClB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,OAAO,CAAA;CACb;AAED,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/providers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAA;AAEhF;;;;;GAKG;AACH;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAAyB;IACxC,sEAAsE;IACtE,wBAAwB,EAAE,OAAO,CAAA;IACjC,oDAAoD;IACpD,qBAAqB,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;IAEhC,yDAAyD;IACzD,QAAQ,CAAC,YAAY,EAAE,yBAAyB,CAAA;IAEhD,gCAAgC;IAChC,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAA;IAE5C,8CAA8C;IAC9C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAA;CACjE;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,+CAA+C;IAC/C,eAAe,EAAE,eAAe,CAAA;IAChC,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAA;IACnB,wDAAwD;IACxD,cAAc,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAA;IACpD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,gCAAgC,EAAE,eAAe,CAAC,CAAA;IACrF;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,EAAE,CAAA;QACd,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAC7B,CAAC,CAAA;IACF;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,0BAA0B,EAAE,qBAAqB,CAAA;CAC5E;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iDAAiD;IACjD,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IACjC;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAClB,cAAc,GACd,gBAAgB,GAChB,uBAAuB,GACvB,iBAAiB,GACjB,oBAAoB,GACpB,sBAAsB,GACtB,gBAAgB,GAChB,eAAe,CAAA;AAEnB,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4BAA4B;AAC5B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,gBAAgB,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,OAAO,CAAA;CACb;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,2BAA2B,EAAE,YAAY,CAAA;IAC/D,GAAG,EAAE,OAAO,CAAA;CACb;AAED,4BAA4B;AAC5B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,aAAa,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,qCAAqC;AACrC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sBAAsB;IACtB,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,GAAG,EAAE,OAAO,CAAA;CACb;AAED,kBAAkB;AAClB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,OAAO,CAAA;CACb;AAED,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB"}
@@ -49,8 +49,8 @@ export declare const RoutingObservationSchema: z.ZodObject<{
49
49
  prCreated: z.ZodBoolean;
50
50
  qaResult: z.ZodEnum<{
51
51
  failed: "failed";
52
- passed: "passed";
53
52
  unknown: "unknown";
53
+ passed: "passed";
54
54
  }>;
55
55
  totalCostUsd: z.ZodNumber;
56
56
  wallClockMs: z.ZodNumber;
@@ -5,6 +5,23 @@
5
5
  */
6
6
  import type { ToolPermission, ToolPermissionAdapter } from './types.js';
7
7
  import type { AgentProviderName } from '../providers/types.js';
8
+ /**
9
+ * Structured permission config consumed by the Codex approval bridge.
10
+ * Produced by `CodexToolPermissionAdapter.buildPermissionConfig()`.
11
+ */
12
+ export interface CodexPermissionConfig {
13
+ /** Allowed command patterns from `tools.allow` */
14
+ allowedCommandPatterns: RegExp[];
15
+ /** Denied command patterns from `tools.disallow` (additive to safety defaults) */
16
+ deniedCommandPatterns: Array<{
17
+ pattern: RegExp;
18
+ reason: string;
19
+ }>;
20
+ /** Whether file edits are allowed (default: true) */
21
+ allowFileEdits: boolean;
22
+ /** Whether file writes are allowed (default: true) */
23
+ allowFileWrites: boolean;
24
+ }
8
25
  /**
9
26
  * Claude Code tool permission adapter.
10
27
  *
@@ -30,6 +47,14 @@ export declare class ClaudeToolPermissionAdapter implements ToolPermissionAdapte
30
47
  */
31
48
  export declare class CodexToolPermissionAdapter implements ToolPermissionAdapter {
32
49
  translatePermissions(permissions: ToolPermission[]): string[];
50
+ /**
51
+ * Build a structured permission config for the Codex approval bridge (SUP-1748).
52
+ *
53
+ * Translates abstract `tools.allow` and `tools.disallow` from templates
54
+ * into regex patterns consumed by `evaluateCommandApproval()` and
55
+ * `evaluateFileChangeApproval()` in the approval bridge.
56
+ */
57
+ buildPermissionConfig(allow: ToolPermission[], disallow: ToolPermission[]): CodexPermissionConfig;
33
58
  private translateOne;
34
59
  }
35
60
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../../src/templates/adapters.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAE9D;;;;;;;GAOG;AACH,qBAAa,2BAA4B,YAAW,qBAAqB;IACvE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D,OAAO,CAAC,YAAY;CAwBrB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,0BAA2B,YAAW,qBAAqB;IACtE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D,OAAO,CAAC,YAAY;CAWrB;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,YAAW,qBAAqB;IACzE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D,OAAO,CAAC,YAAY;CAWrB;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,iBAAiB,GAAG,qBAAqB,CAa9F"}
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../../src/templates/adapters.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAM9D;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,sBAAsB,EAAE,MAAM,EAAE,CAAA;IAChC,kFAAkF;IAClF,qBAAqB,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjE,qDAAqD;IACrD,cAAc,EAAE,OAAO,CAAA;IACvB,sDAAsD;IACtD,eAAe,EAAE,OAAO,CAAA;CACzB;AAED;;;;;;;GAOG;AACH,qBAAa,2BAA4B,YAAW,qBAAqB;IACvE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D,OAAO,CAAC,YAAY;CAwBrB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,0BAA2B,YAAW,qBAAqB;IACtE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D;;;;;;OAMG;IACH,qBAAqB,CACnB,KAAK,EAAE,cAAc,EAAE,EACvB,QAAQ,EAAE,cAAc,EAAE,GACzB,qBAAqB;IAsCxB,OAAO,CAAC,YAAY;CAWrB;AAkCD;;;;;;;GAOG;AACH,qBAAa,6BAA8B,YAAW,qBAAqB;IACzE,oBAAoB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE;IAI7D,OAAO,CAAC,YAAY;CAWrB;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,iBAAiB,GAAG,qBAAqB,CAa9F"}
@@ -53,6 +53,49 @@ export class CodexToolPermissionAdapter {
53
53
  translatePermissions(permissions) {
54
54
  return permissions.map(p => this.translateOne(p));
55
55
  }
56
+ /**
57
+ * Build a structured permission config for the Codex approval bridge (SUP-1748).
58
+ *
59
+ * Translates abstract `tools.allow` and `tools.disallow` from templates
60
+ * into regex patterns consumed by `evaluateCommandApproval()` and
61
+ * `evaluateFileChangeApproval()` in the approval bridge.
62
+ */
63
+ buildPermissionConfig(allow, disallow) {
64
+ const allowedCommandPatterns = [];
65
+ const deniedCommandPatterns = [];
66
+ let allowFileEdits = true;
67
+ let allowFileWrites = true;
68
+ // Process allow list → allowed command patterns
69
+ for (const permission of allow) {
70
+ if (typeof permission !== 'string' && 'shell' in permission) {
71
+ allowedCommandPatterns.push(shellGlobToRegex(permission.shell));
72
+ }
73
+ }
74
+ // Process disallow list → denied patterns + file permission flags
75
+ for (const permission of disallow) {
76
+ if (typeof permission === 'string') {
77
+ if (permission === 'file-edit') {
78
+ allowFileEdits = false;
79
+ }
80
+ else if (permission === 'file-write') {
81
+ allowFileWrites = false;
82
+ }
83
+ // 'user-input' is a no-op for Codex (non-interactive)
84
+ }
85
+ else if ('shell' in permission) {
86
+ deniedCommandPatterns.push({
87
+ pattern: shellGlobToRegex(permission.shell),
88
+ reason: `${permission.shell} blocked by template`,
89
+ });
90
+ }
91
+ }
92
+ return {
93
+ allowedCommandPatterns,
94
+ deniedCommandPatterns,
95
+ allowFileEdits,
96
+ allowFileWrites,
97
+ };
98
+ }
56
99
  translateOne(permission) {
57
100
  if (typeof permission === 'string') {
58
101
  return permission;
@@ -63,6 +106,33 @@ export class CodexToolPermissionAdapter {
63
106
  return String(permission);
64
107
  }
65
108
  }
109
+ /**
110
+ * Convert a shell glob pattern (e.g., "pnpm *", "git commit *") to a regex.
111
+ *
112
+ * The pattern "pnpm *" matches any command starting with "pnpm".
113
+ * The pattern "git commit *" matches "git commit" followed by anything.
114
+ */
115
+ function shellGlobToRegex(glob) {
116
+ // Split on the last space to separate command from glob
117
+ const lastSpace = glob.lastIndexOf(' ');
118
+ if (lastSpace === -1) {
119
+ // No glob part — match command prefix (e.g., "pnpm" matches "pnpm install")
120
+ return new RegExp(`^${escapeRegex(glob)}\\b`);
121
+ }
122
+ const command = glob.substring(0, lastSpace);
123
+ const globPart = glob.substring(lastSpace + 1);
124
+ if (globPart === '*') {
125
+ // "git commit *" → matches "git commit" followed by anything
126
+ return new RegExp(`^${escapeRegex(command)}\\b`);
127
+ }
128
+ // More specific globs: escape and convert * to .*
129
+ const escapedCommand = escapeRegex(command);
130
+ const regexGlob = escapeRegex(globPart).replace(/\\\*/g, '.*');
131
+ return new RegExp(`^${escapedCommand}\\s+${regexGlob}`);
132
+ }
133
+ function escapeRegex(str) {
134
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
135
+ }
66
136
  /**
67
137
  * Spring AI tool permission adapter.
68
138
  *
@@ -77,6 +77,55 @@ describe('CodexToolPermissionAdapter', () => {
77
77
  const result = adapter.translatePermissions([]);
78
78
  expect(result).toEqual([]);
79
79
  });
80
+ describe('buildPermissionConfig (SUP-1748)', () => {
81
+ it('builds permission config from allow and disallow lists', () => {
82
+ const config = adapter.buildPermissionConfig([{ shell: 'pnpm *' }, { shell: 'git commit *' }], [{ shell: 'git checkout *' }, 'user-input']);
83
+ expect(config.allowedCommandPatterns).toHaveLength(2);
84
+ expect(config.deniedCommandPatterns).toHaveLength(1);
85
+ expect(config.allowFileEdits).toBe(true);
86
+ expect(config.allowFileWrites).toBe(true);
87
+ });
88
+ it('allowed patterns match expected commands', () => {
89
+ const config = adapter.buildPermissionConfig([{ shell: 'pnpm *' }], []);
90
+ expect(config.allowedCommandPatterns[0].test('pnpm install')).toBe(true);
91
+ expect(config.allowedCommandPatterns[0].test('pnpm run build')).toBe(true);
92
+ expect(config.allowedCommandPatterns[0].test('npm install')).toBe(false);
93
+ });
94
+ it('denied patterns match expected commands', () => {
95
+ const config = adapter.buildPermissionConfig([], [{ shell: 'git *' }]);
96
+ expect(config.deniedCommandPatterns[0].pattern.test('git push')).toBe(true);
97
+ expect(config.deniedCommandPatterns[0].pattern.test('git commit')).toBe(true);
98
+ expect(config.deniedCommandPatterns[0].reason).toContain('blocked by template');
99
+ });
100
+ it('sets allowFileEdits to false when file-edit is in disallow', () => {
101
+ const config = adapter.buildPermissionConfig([], ['file-edit']);
102
+ expect(config.allowFileEdits).toBe(false);
103
+ expect(config.allowFileWrites).toBe(true);
104
+ });
105
+ it('sets allowFileWrites to false when file-write is in disallow', () => {
106
+ const config = adapter.buildPermissionConfig([], ['file-write']);
107
+ expect(config.allowFileEdits).toBe(true);
108
+ expect(config.allowFileWrites).toBe(false);
109
+ });
110
+ it('ignores user-input in disallow (non-interactive)', () => {
111
+ const config = adapter.buildPermissionConfig([], ['user-input']);
112
+ expect(config.deniedCommandPatterns).toHaveLength(0);
113
+ expect(config.allowFileEdits).toBe(true);
114
+ expect(config.allowFileWrites).toBe(true);
115
+ });
116
+ it('handles empty allow and disallow lists', () => {
117
+ const config = adapter.buildPermissionConfig([], []);
118
+ expect(config.allowedCommandPatterns).toHaveLength(0);
119
+ expect(config.deniedCommandPatterns).toHaveLength(0);
120
+ expect(config.allowFileEdits).toBe(true);
121
+ expect(config.allowFileWrites).toBe(true);
122
+ });
123
+ it('multi-word commands create correct regex', () => {
124
+ const config = adapter.buildPermissionConfig([{ shell: 'git commit *' }], []);
125
+ expect(config.allowedCommandPatterns[0].test('git commit -m "message"')).toBe(true);
126
+ expect(config.allowedCommandPatterns[0].test('git push origin main')).toBe(false);
127
+ });
128
+ });
80
129
  });
81
130
  describe('SpringAiToolPermissionAdapter', () => {
82
131
  const adapter = new SpringAiToolPermissionAdapter();
@@ -9,6 +9,7 @@ export { TemplateRegistry, type TemplateValidationResult } from './registry.js';
9
9
  export { generateTemplateSchema, extractTemplateVariables, type TemplateSchemaOptions } from './schema.js';
10
10
  export { loadTemplatesFromDir, loadTemplateFile, loadPartialsFromDir, getBuiltinDefaultsDir, getBuiltinPartialsDir, } from './loader.js';
11
11
  export { ClaudeToolPermissionAdapter, CodexToolPermissionAdapter, createToolPermissionAdapter } from './adapters.js';
12
+ export type { CodexPermissionConfig } from './adapters.js';
12
13
  export { renderPromptWithFallback } from './renderer.js';
13
14
  export type { AgentDefinition, AgentDefinitionFrontmatter } from './agent-definition.js';
14
15
  export { parseAgentDefinition, parseAgentDefinitionFile, AgentDefinitionFrontmatterSchema } from './agent-definition.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE1G,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AAEpH,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAExD,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AACxF,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAE/E,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAE1G,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAA;AACpH,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAE1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAExD,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AACxF,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAA"}
@@ -74,6 +74,14 @@ export declare class TemplateRegistry {
74
74
  * Get disallowed tools for a work type (optionally with strategy).
75
75
  */
76
76
  getDisallowedTools(workType: AgentWorkType, strategy?: string): ToolPermission[] | undefined;
77
+ /**
78
+ * Get the raw allow and disallow permission arrays for a work type (SUP-1748).
79
+ * Used by the orchestrator to build Codex permission configs via the adapter.
80
+ */
81
+ getRawToolPermissions(workType: AgentWorkType, strategy?: string): {
82
+ allow: ToolPermission[];
83
+ disallow: ToolPermission[];
84
+ };
77
85
  /**
78
86
  * Register a partial template for use in Handlebars rendering.
79
87
  */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAOnB,OAAO,EAA0B,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEhF;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,qBAAqB,CAAC,CAAuB;;IAcrD;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,GAAE,sBAA2B,GAAG,gBAAgB;IAMpE;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,GAAE,sBAA2B,GAAG,IAAI;IA4CrD;;OAEG;IACH,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAI9D;;;;;;;;OAQG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASrF;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhE;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkBjG;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAgBpF;;OAEG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,SAAS;IAK5F;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpD;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,WAAW,GAAG,IAAI;IAMpF;;;;;;;OAOG;IACH,cAAc,CACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,wBAAwB;CAS5B;AAMD,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/templates/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACtB,MAAM,YAAY,CAAA;AAOnB,OAAO,EAA0B,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEhF;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B;;;OAGG;IACH,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,qBAAqB,CAAC,CAAuB;;IAcrD;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,GAAE,sBAA2B,GAAG,gBAAgB;IAMpE;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,GAAE,sBAA2B,GAAG,IAAI;IA4CrD;;OAEG;IACH,wBAAwB,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAI9D;;;;;;;;OAQG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASrF;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAQhE;;OAEG;IACH,sBAAsB,IAAI,MAAM,EAAE;IAIlC;;;;OAIG;IACH,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAkBjG;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAgBpF;;OAEG;IACH,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,GAAG,SAAS;IAK5F;;;OAGG;IACH,qBAAqB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;QACjE,KAAK,EAAE,cAAc,EAAE,CAAA;QACvB,QAAQ,EAAE,cAAc,EAAE,CAAA;KAC3B;IAQD;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIpD;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,WAAW,GAAG,IAAI;IAMpF;;;;;;;OAOG;IACH,cAAc,CACZ,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,wBAAwB;CAS5B;AAMD,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB"}
@@ -169,6 +169,17 @@ export class TemplateRegistry {
169
169
  const template = this.getTemplate(workType, strategy);
170
170
  return template?.tools?.disallow;
171
171
  }
172
+ /**
173
+ * Get the raw allow and disallow permission arrays for a work type (SUP-1748).
174
+ * Used by the orchestrator to build Codex permission configs via the adapter.
175
+ */
176
+ getRawToolPermissions(workType, strategy) {
177
+ const template = this.getTemplate(workType, strategy);
178
+ return {
179
+ allow: template?.tools?.allow ?? [],
180
+ disallow: template?.tools?.disallow ?? [],
181
+ };
182
+ }
172
183
  /**
173
184
  * Register a partial template for use in Handlebars rendering.
174
185
  */