vellum 0.2.13 → 0.2.14
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.
- package/README.md +32 -0
- package/bun.lock +2 -2
- package/docs/skills.md +4 -4
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +213 -3
- package/src/__tests__/app-git-history.test.ts +176 -0
- package/src/__tests__/app-git-service.test.ts +169 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +315 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +8 -8
- package/src/__tests__/browser-skill-endstate.test.ts +6 -6
- package/src/__tests__/call-bridge.test.ts +105 -13
- package/src/__tests__/call-domain.test.ts +163 -0
- package/src/__tests__/call-orchestrator.test.ts +113 -0
- package/src/__tests__/call-routes-http.test.ts +246 -6
- package/src/__tests__/channel-approval-routes.test.ts +438 -0
- package/src/__tests__/channel-approval.test.ts +266 -0
- package/src/__tests__/channel-approvals.test.ts +393 -0
- package/src/__tests__/channel-delivery-store.test.ts +447 -0
- package/src/__tests__/checker.test.ts +607 -1048
- package/src/__tests__/cli.test.ts +1 -56
- package/src/__tests__/config-schema.test.ts +137 -18
- package/src/__tests__/conflict-intent-tokenization.test.ts +141 -0
- package/src/__tests__/conflict-policy.test.ts +121 -0
- package/src/__tests__/conflict-store.test.ts +2 -0
- package/src/__tests__/contacts-tools.test.ts +3 -3
- package/src/__tests__/contradiction-checker.test.ts +99 -1
- package/src/__tests__/credential-security-invariants.test.ts +22 -6
- package/src/__tests__/credential-vault-unit.test.ts +780 -0
- package/src/__tests__/elevenlabs-client.test.ts +62 -0
- package/src/__tests__/ephemeral-permissions.test.ts +73 -23
- package/src/__tests__/filesystem-tools.test.ts +579 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +114 -4
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +202 -0
- package/src/__tests__/handlers-cu-observation-blob.test.ts +2 -1
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +2 -1
- package/src/__tests__/handlers-slack-config.test.ts +2 -1
- package/src/__tests__/handlers-telegram-config.test.ts +855 -0
- package/src/__tests__/handlers-twitter-config.test.ts +141 -1
- package/src/__tests__/hooks-runner.test.ts +6 -2
- package/src/__tests__/host-file-edit-tool.test.ts +124 -0
- package/src/__tests__/host-file-read-tool.test.ts +62 -0
- package/src/__tests__/host-file-write-tool.test.ts +59 -0
- package/src/__tests__/host-shell-tool.test.ts +251 -0
- package/src/__tests__/ingress-reconcile.test.ts +581 -0
- package/src/__tests__/ipc-snapshot.test.ts +100 -41
- package/src/__tests__/ipc-validate.test.ts +50 -0
- package/src/__tests__/key-migration.test.ts +23 -0
- package/src/__tests__/memory-regressions.test.ts +99 -0
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/oauth-callback-registry.test.ts +11 -4
- package/src/__tests__/playbook-execution.test.ts +502 -0
- package/src/__tests__/playbook-tools.test.ts +4 -6
- package/src/__tests__/public-ingress-urls.test.ts +34 -0
- package/src/__tests__/qdrant-manager.test.ts +267 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +97 -0
- package/src/__tests__/recurrence-engine.test.ts +9 -0
- package/src/__tests__/recurrence-types.test.ts +8 -0
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/runtime-runs.test.ts +1 -25
- package/src/__tests__/schedule-store.test.ts +16 -14
- package/src/__tests__/schedule-tools.test.ts +83 -0
- package/src/__tests__/scheduler-recurrence.test.ts +111 -10
- package/src/__tests__/secret-allowlist.test.ts +18 -17
- package/src/__tests__/secret-ingress-handler.test.ts +11 -0
- package/src/__tests__/secret-scanner.test.ts +43 -0
- package/src/__tests__/session-conflict-gate.test.ts +442 -6
- package/src/__tests__/session-init.benchmark.test.ts +3 -0
- package/src/__tests__/session-process-bridge.test.ts +242 -0
- package/src/__tests__/session-skill-tools.test.ts +1 -1
- package/src/__tests__/shell-identity.test.ts +256 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +11 -1
- package/src/__tests__/subagent-tools.test.ts +637 -54
- package/src/__tests__/task-management-tools.test.ts +936 -0
- package/src/__tests__/task-runner.test.ts +2 -2
- package/src/__tests__/terminal-tools.test.ts +840 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +301 -0
- package/src/__tests__/tool-executor.test.ts +85 -151
- package/src/__tests__/tool-permission-simulate-handler.test.ts +336 -0
- package/src/__tests__/trust-store.test.ts +27 -453
- package/src/__tests__/twilio-provider.test.ts +153 -3
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +375 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +4 -4
- package/src/__tests__/twilio-routes.test.ts +17 -262
- package/src/__tests__/twitter-auth-handler.test.ts +2 -1
- package/src/__tests__/twitter-cli-error-shaping.test.ts +208 -0
- package/src/__tests__/twitter-cli-routing.test.ts +252 -0
- package/src/__tests__/twitter-oauth-client.test.ts +209 -0
- package/src/__tests__/workspace-policy.test.ts +213 -0
- package/src/calls/call-bridge.ts +92 -19
- package/src/calls/call-domain.ts +157 -5
- package/src/calls/call-orchestrator.ts +93 -7
- package/src/calls/call-store.ts +6 -0
- package/src/calls/elevenlabs-client.ts +8 -0
- package/src/calls/elevenlabs-config.ts +7 -5
- package/src/calls/twilio-provider.ts +91 -0
- package/src/calls/twilio-routes.ts +32 -37
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-quality.ts +29 -7
- package/src/cli/twitter.ts +200 -21
- package/src/cli.ts +1 -20
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +52 -4
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +55 -4
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +61 -4
- package/src/config/bundled-skills/messaging/SKILL.md +17 -2
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +4 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +5 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +142 -34
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +95 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +51 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +73 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +110 -6
- package/src/config/bundled-skills/public-ingress/SKILL.md +22 -5
- package/src/config/bundled-skills/twitter/SKILL.md +103 -17
- package/src/config/defaults.ts +10 -4
- package/src/config/schema.ts +80 -21
- package/src/config/types.ts +1 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +56 -61
- package/src/daemon/assistant-attachments.ts +4 -2
- package/src/daemon/handlers/apps.ts +69 -0
- package/src/daemon/handlers/config.ts +543 -24
- package/src/daemon/handlers/index.ts +1 -0
- package/src/daemon/handlers/sessions.ts +22 -6
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +5 -20
- package/src/daemon/ipc-contract-inventory.json +28 -0
- package/src/daemon/ipc-contract.ts +168 -10
- package/src/daemon/ipc-validate.ts +17 -0
- package/src/daemon/lifecycle.ts +2 -0
- package/src/daemon/server.ts +78 -72
- package/src/daemon/session-attachments.ts +1 -1
- package/src/daemon/session-conflict-gate.ts +62 -6
- package/src/daemon/session-notifiers.ts +1 -1
- package/src/daemon/session-process.ts +62 -3
- package/src/daemon/session-tool-setup.ts +1 -2
- package/src/daemon/tls-certs.ts +189 -0
- package/src/daemon/video-thumbnail.ts +5 -3
- package/src/hooks/manager.ts +5 -9
- package/src/memory/app-git-service.ts +295 -0
- package/src/memory/app-store.ts +21 -0
- package/src/memory/conflict-intent.ts +47 -4
- package/src/memory/conflict-policy.ts +73 -0
- package/src/memory/conflict-store.ts +9 -1
- package/src/memory/contradiction-checker.ts +28 -0
- package/src/memory/conversation-key-store.ts +15 -0
- package/src/memory/db.ts +81 -0
- package/src/memory/embedding-local.ts +3 -13
- package/src/memory/external-conversation-store.ts +234 -0
- package/src/memory/job-handlers/conflict.ts +22 -2
- package/src/memory/jobs-worker.ts +67 -28
- package/src/memory/runs-store.ts +54 -7
- package/src/memory/schema.ts +20 -0
- package/src/messaging/provider.ts +9 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +162 -0
- package/src/messaging/providers/telegram-bot/client.ts +104 -0
- package/src/messaging/providers/telegram-bot/types.ts +15 -0
- package/src/messaging/registry.ts +1 -0
- package/src/permissions/checker.ts +48 -44
- package/src/permissions/prompter.ts +0 -4
- package/src/permissions/shell-identity.ts +227 -0
- package/src/permissions/trust-store.ts +76 -53
- package/src/permissions/types.ts +0 -19
- package/src/permissions/workspace-policy.ts +114 -0
- package/src/providers/retry.ts +12 -37
- package/src/runtime/assistant-event-hub.ts +41 -4
- package/src/runtime/channel-approval-parser.ts +60 -0
- package/src/runtime/channel-approval-types.ts +71 -0
- package/src/runtime/channel-approvals.ts +145 -0
- package/src/runtime/gateway-client.ts +16 -0
- package/src/runtime/http-server.ts +29 -9
- package/src/runtime/routes/call-routes.ts +52 -2
- package/src/runtime/routes/channel-routes.ts +296 -16
- package/src/runtime/routes/events-routes.ts +97 -28
- package/src/runtime/routes/run-routes.ts +2 -7
- package/src/runtime/run-orchestrator.ts +0 -3
- package/src/schedule/recurrence-engine.ts +26 -2
- package/src/schedule/recurrence-types.ts +1 -1
- package/src/schedule/schedule-store.ts +12 -3
- package/src/security/secret-scanner.ts +7 -0
- package/src/tasks/ephemeral-permissions.ts +0 -2
- package/src/tasks/task-scheduler.ts +2 -1
- package/src/tools/calls/call-start.ts +8 -0
- package/src/tools/execution-target.ts +21 -0
- package/src/tools/execution-timeout.ts +49 -0
- package/src/tools/executor.ts +6 -135
- package/src/tools/network/web-search.ts +9 -32
- package/src/tools/policy-context.ts +29 -0
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/terminal/parser.ts +16 -18
- package/src/tools/types.ts +4 -11
- package/src/twitter/oauth-client.ts +102 -0
- package/src/twitter/router.ts +101 -0
- package/src/util/debounce.ts +88 -0
- package/src/util/network-info.ts +47 -0
- package/src/util/platform.ts +29 -4
- package/src/util/promise-guard.ts +37 -0
- package/src/util/retry.ts +98 -0
- package/src/util/truncate.ts +1 -1
- package/src/workspace/git-service.ts +129 -112
- package/src/tools/contacts/contact-merge.ts +0 -55
- package/src/tools/contacts/contact-search.ts +0 -58
- package/src/tools/contacts/contact-upsert.ts +0 -64
- package/src/tools/playbooks/index.ts +0 -4
- package/src/tools/playbooks/playbook-create.ts +0 -96
- package/src/tools/playbooks/playbook-delete.ts +0 -52
- package/src/tools/playbooks/playbook-list.ts +0 -74
- package/src/tools/playbooks/playbook-update.ts +0 -111
|
@@ -189,6 +189,68 @@ describe('ElevenLabsClient', () => {
|
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
192
|
+
test('valid TwiML with XML declaration passes validation', async () => {
|
|
193
|
+
const twiml = '<?xml version="1.0"?><Response><Connect><Stream url="wss://el.io/stream"/></Connect></Response>';
|
|
194
|
+
|
|
195
|
+
globalThis.fetch = mock(async () =>
|
|
196
|
+
new Response(twiml, { status: 200 }),
|
|
197
|
+
) as unknown as typeof globalThis.fetch;
|
|
198
|
+
|
|
199
|
+
const client = new ElevenLabsClient(DEFAULT_OPTIONS);
|
|
200
|
+
const result = await client.registerCall(DEFAULT_REQUEST);
|
|
201
|
+
|
|
202
|
+
expect(result.twiml).toBe(twiml);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('valid TwiML with Response tag but no XML declaration passes validation', async () => {
|
|
206
|
+
const twiml = '<Response><Connect><Stream url="wss://el.io/stream"/></Connect></Response>';
|
|
207
|
+
|
|
208
|
+
globalThis.fetch = mock(async () =>
|
|
209
|
+
new Response(twiml, { status: 200 }),
|
|
210
|
+
) as unknown as typeof globalThis.fetch;
|
|
211
|
+
|
|
212
|
+
const client = new ElevenLabsClient(DEFAULT_OPTIONS);
|
|
213
|
+
const result = await client.registerCall(DEFAULT_REQUEST);
|
|
214
|
+
|
|
215
|
+
expect(result.twiml).toBe(twiml);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('non-XML response throws ELEVENLABS_INVALID_RESPONSE', async () => {
|
|
219
|
+
globalThis.fetch = mock(async () =>
|
|
220
|
+
new Response('{"error": "invalid"}', { status: 200 }),
|
|
221
|
+
) as unknown as typeof globalThis.fetch;
|
|
222
|
+
|
|
223
|
+
const client = new ElevenLabsClient(DEFAULT_OPTIONS);
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
await client.registerCall(DEFAULT_REQUEST);
|
|
227
|
+
expect(true).toBe(false); // Should not reach here
|
|
228
|
+
} catch (err) {
|
|
229
|
+
expect(err).toBeInstanceOf(ElevenLabsError);
|
|
230
|
+
const elErr = err as ElevenLabsError;
|
|
231
|
+
expect(elErr.code).toBe('ELEVENLABS_INVALID_RESPONSE');
|
|
232
|
+
expect(elErr.message).toContain('not valid TwiML/XML');
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('plain text response throws ELEVENLABS_INVALID_RESPONSE', async () => {
|
|
237
|
+
globalThis.fetch = mock(async () =>
|
|
238
|
+
new Response('some random text', { status: 200 }),
|
|
239
|
+
) as unknown as typeof globalThis.fetch;
|
|
240
|
+
|
|
241
|
+
const client = new ElevenLabsClient(DEFAULT_OPTIONS);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
await client.registerCall(DEFAULT_REQUEST);
|
|
245
|
+
expect(true).toBe(false); // Should not reach here
|
|
246
|
+
} catch (err) {
|
|
247
|
+
expect(err).toBeInstanceOf(ElevenLabsError);
|
|
248
|
+
const elErr = err as ElevenLabsError;
|
|
249
|
+
expect(elErr.code).toBe('ELEVENLABS_INVALID_RESPONSE');
|
|
250
|
+
expect(elErr.message).toContain('not valid TwiML/XML');
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
192
254
|
test('API key is not included in logged data', async () => {
|
|
193
255
|
// The ElevenLabsClient logs agent_id and direction, but should never log the API key.
|
|
194
256
|
// We verify this by checking the request structure, not the log output.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect, beforeAll, beforeEach, mock } from 'bun:test';
|
|
1
|
+
import { describe, test, expect, beforeAll, beforeEach, afterEach, mock } from 'bun:test';
|
|
2
2
|
import { mkdtempSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
@@ -28,7 +28,7 @@ mock.module('../util/logger.js', () => ({
|
|
|
28
28
|
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
30
|
const testConfig: Record<string, any> = {
|
|
31
|
-
permissions: { mode: 'legacy' as 'legacy' | 'strict' },
|
|
31
|
+
permissions: { mode: 'legacy' as 'legacy' | 'strict' | 'workspace' },
|
|
32
32
|
skills: { load: { extraDirs: [] as string[] } },
|
|
33
33
|
sandbox: { enabled: false },
|
|
34
34
|
};
|
|
@@ -73,15 +73,14 @@ describe('ephemeral-permissions', () => {
|
|
|
73
73
|
expect(fileReadRule.id).toBe('ephemeral:run-123:file_read');
|
|
74
74
|
expect(fileReadRule.tool).toBe('file_read');
|
|
75
75
|
expect(fileReadRule.pattern).toBe('**');
|
|
76
|
-
expect(fileReadRule.scope).toBe('
|
|
76
|
+
expect(fileReadRule.scope).toBe('everywhere');
|
|
77
77
|
expect(fileReadRule.decision).toBe('allow');
|
|
78
|
-
expect(fileReadRule.priority).toBe(
|
|
79
|
-
expect(fileReadRule.principalKind).toBe('task');
|
|
80
|
-
expect(fileReadRule.principalId).toBe('run-123');
|
|
78
|
+
expect(fileReadRule.priority).toBe(75);
|
|
81
79
|
expect(fileReadRule.createdAt).toBeGreaterThan(0);
|
|
82
80
|
|
|
83
|
-
//
|
|
84
|
-
|
|
81
|
+
// allowHighRisk is set because task runs execute asynchronously
|
|
82
|
+
// without interactive confirmation — pre-approved via preflight
|
|
83
|
+
expect(fileReadRule.allowHighRisk).toBe(true);
|
|
85
84
|
|
|
86
85
|
// Check other rules have correct tool names
|
|
87
86
|
expect(rules[1].tool).toBe('bash');
|
|
@@ -155,12 +154,9 @@ describe('ephemeral-permissions', () => {
|
|
|
155
154
|
decision: 'allow',
|
|
156
155
|
priority: 50,
|
|
157
156
|
createdAt: Date.now(),
|
|
158
|
-
principalKind: 'task',
|
|
159
|
-
principalId: 'run-1',
|
|
160
157
|
}];
|
|
161
158
|
|
|
162
159
|
const ctx: PolicyContext = {
|
|
163
|
-
principal: { kind: 'task', id: 'run-1' },
|
|
164
160
|
ephemeralRules,
|
|
165
161
|
};
|
|
166
162
|
|
|
@@ -188,12 +184,9 @@ describe('ephemeral-permissions', () => {
|
|
|
188
184
|
decision: 'allow',
|
|
189
185
|
priority: 50,
|
|
190
186
|
createdAt: Date.now(),
|
|
191
|
-
principalKind: 'task',
|
|
192
|
-
principalId: 'run-1',
|
|
193
187
|
}];
|
|
194
188
|
|
|
195
189
|
const ctx: PolicyContext = {
|
|
196
|
-
principal: { kind: 'task', id: 'run-1' },
|
|
197
190
|
ephemeralRules,
|
|
198
191
|
};
|
|
199
192
|
|
|
@@ -218,12 +211,9 @@ describe('ephemeral-permissions', () => {
|
|
|
218
211
|
decision: 'allow',
|
|
219
212
|
priority: 50,
|
|
220
213
|
createdAt: Date.now(),
|
|
221
|
-
principalKind: 'task',
|
|
222
|
-
principalId: 'run-1',
|
|
223
214
|
}];
|
|
224
215
|
|
|
225
216
|
const ctx: PolicyContext = {
|
|
226
|
-
principal: { kind: 'task', id: 'run-1' },
|
|
227
217
|
ephemeralRules,
|
|
228
218
|
};
|
|
229
219
|
|
|
@@ -258,12 +248,9 @@ describe('ephemeral-permissions', () => {
|
|
|
258
248
|
decision: 'allow',
|
|
259
249
|
priority: 50,
|
|
260
250
|
createdAt: Date.now(),
|
|
261
|
-
principalKind: 'task',
|
|
262
|
-
principalId: 'run-1',
|
|
263
251
|
}];
|
|
264
252
|
|
|
265
253
|
const ctx: PolicyContext = {
|
|
266
|
-
principal: { kind: 'task', id: 'run-1' },
|
|
267
254
|
ephemeralRules,
|
|
268
255
|
};
|
|
269
256
|
|
|
@@ -288,13 +275,10 @@ describe('ephemeral-permissions', () => {
|
|
|
288
275
|
decision: 'allow',
|
|
289
276
|
priority: 50,
|
|
290
277
|
createdAt: Date.now(),
|
|
291
|
-
principalKind: 'task',
|
|
292
|
-
principalId: 'run-1',
|
|
293
278
|
// Note: allowHighRisk is NOT set
|
|
294
279
|
}];
|
|
295
280
|
|
|
296
281
|
const ctx: PolicyContext = {
|
|
297
|
-
principal: { kind: 'task', id: 'run-1' },
|
|
298
282
|
ephemeralRules,
|
|
299
283
|
};
|
|
300
284
|
|
|
@@ -309,4 +293,70 @@ describe('ephemeral-permissions', () => {
|
|
|
309
293
|
expect(result.decision).toBe('prompt');
|
|
310
294
|
});
|
|
311
295
|
});
|
|
296
|
+
|
|
297
|
+
describe('workspace mode interactions', () => {
|
|
298
|
+
beforeEach(() => {
|
|
299
|
+
clearCache();
|
|
300
|
+
testConfig.permissions.mode = 'workspace';
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
afterEach(() => {
|
|
304
|
+
testConfig.permissions.mode = 'legacy';
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('workspace mode auto-allows workspace-scoped file_write (medium risk)', async () => {
|
|
308
|
+
const filePath = join(testDir, 'workspace-test-file.txt');
|
|
309
|
+
const result = await check('file_write', { path: filePath }, testDir);
|
|
310
|
+
expect(result.decision).toBe('allow');
|
|
311
|
+
expect(result.reason).toContain('Workspace mode');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('workspace mode still prompts for file_write outside workspace', async () => {
|
|
315
|
+
const result = await check('file_write', { path: '/etc/config' }, testDir);
|
|
316
|
+
expect(result.decision).toBe('prompt');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('explicit deny rule overrides workspace mode auto-allow', async () => {
|
|
320
|
+
addRule('file_write', '**', testDir, 'deny', 100);
|
|
321
|
+
const filePath = join(testDir, 'should-be-denied.txt');
|
|
322
|
+
const result = await check('file_write', { path: filePath }, testDir);
|
|
323
|
+
expect(result.decision).toBe('deny');
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('proxied bash still prompts in workspace mode', async () => {
|
|
327
|
+
const result = await check(
|
|
328
|
+
'bash',
|
|
329
|
+
{ command: 'echo hello', network_mode: 'proxied' },
|
|
330
|
+
testDir,
|
|
331
|
+
);
|
|
332
|
+
expect(result.decision).toBe('prompt');
|
|
333
|
+
expect(result.reason).toContain('Proxied');
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test('ephemeral task rules + workspace mode: deny rule wins', async () => {
|
|
337
|
+
// Add a persistent deny rule for file_write in the workspace
|
|
338
|
+
addRule('file_write', '**', testDir, 'deny', 100);
|
|
339
|
+
|
|
340
|
+
// Create ephemeral allow rules (lower priority than deny)
|
|
341
|
+
const ephemeralRules: TrustRule[] = [{
|
|
342
|
+
id: 'ephemeral:run-ws:file_write',
|
|
343
|
+
tool: 'file_write',
|
|
344
|
+
pattern: '**',
|
|
345
|
+
scope: testDir,
|
|
346
|
+
decision: 'allow',
|
|
347
|
+
priority: 50,
|
|
348
|
+
createdAt: Date.now(),
|
|
349
|
+
}];
|
|
350
|
+
|
|
351
|
+
const ctx: PolicyContext = {
|
|
352
|
+
ephemeralRules,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const filePath = join(testDir, 'task-file.txt');
|
|
356
|
+
const result = await check('file_write', { path: filePath }, testDir, ctx);
|
|
357
|
+
// The persistent deny rule (priority 100) should override
|
|
358
|
+
// both the ephemeral allow (priority 50) and workspace mode auto-allow
|
|
359
|
+
expect(result.decision).toBe('deny');
|
|
360
|
+
});
|
|
361
|
+
});
|
|
312
362
|
});
|