@vellumai/assistant 0.3.16 → 0.3.19
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/ARCHITECTURE.md +74 -13
- package/README.md +6 -0
- package/docs/architecture/http-token-refresh.md +23 -1
- package/docs/architecture/security.md +80 -0
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -0
- package/src/__tests__/access-request-decision.test.ts +4 -7
- package/src/__tests__/call-controller.test.ts +170 -0
- package/src/__tests__/channel-guardian.test.ts +3 -1
- package/src/__tests__/checker.test.ts +139 -48
- package/src/__tests__/config-watcher.test.ts +11 -13
- package/src/__tests__/conversation-pairing.test.ts +103 -3
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -1
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +511 -0
- package/src/__tests__/guardian-action-late-reply.test.ts +131 -0
- package/src/__tests__/guardian-action-store.test.ts +182 -0
- package/src/__tests__/guardian-dispatch.test.ts +180 -0
- package/src/__tests__/guardian-grant-minting.test.ts +543 -0
- package/src/__tests__/ipc-snapshot.test.ts +22 -0
- package/src/__tests__/non-member-access-request.test.ts +1 -2
- package/src/__tests__/notification-broadcaster.test.ts +115 -4
- package/src/__tests__/notification-decision-strategy.test.ts +2 -1
- package/src/__tests__/notification-deep-link.test.ts +44 -1
- package/src/__tests__/notification-guardian-path.test.ts +157 -0
- package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
- package/src/__tests__/remote-skill-policy.test.ts +215 -0
- package/src/__tests__/scoped-approval-grants.test.ts +521 -0
- package/src/__tests__/scoped-grant-security-matrix.test.ts +443 -0
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/trust-store.test.ts +23 -21
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +5 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +9 -9
- package/src/__tests__/update-bulletin-state.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +66 -3
- package/src/__tests__/update-template-contract.test.ts +6 -11
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +571 -0
- package/src/__tests__/voice-session-bridge.test.ts +109 -9
- package/src/calls/call-controller.ts +150 -8
- package/src/calls/call-domain.ts +12 -0
- package/src/calls/guardian-action-sweep.ts +1 -1
- package/src/calls/guardian-dispatch.ts +16 -0
- package/src/calls/relay-server.ts +13 -0
- package/src/calls/voice-session-bridge.ts +46 -5
- package/src/cli/core-commands.ts +41 -1
- package/src/config/bundled-skills/notifications/SKILL.md +18 -0
- package/src/config/schema.ts +6 -0
- package/src/config/skills-schema.ts +27 -0
- package/src/config/templates/UPDATES.md +5 -6
- package/src/config/update-bulletin-format.ts +2 -0
- package/src/config/update-bulletin-state.ts +1 -1
- package/src/config/update-bulletin-template-path.ts +6 -0
- package/src/config/update-bulletin.ts +21 -6
- package/src/daemon/config-watcher.ts +3 -2
- package/src/daemon/daemon-control.ts +64 -10
- package/src/daemon/handlers/config-channels.ts +18 -0
- package/src/daemon/handlers/config-slack-channel.ts +1 -1
- package/src/daemon/handlers/identity.ts +45 -25
- package/src/daemon/handlers/sessions.ts +1 -1
- package/src/daemon/handlers/skills.ts +45 -2
- package/src/daemon/ipc-contract/sessions.ts +1 -1
- package/src/daemon/ipc-contract/skills.ts +1 -0
- package/src/daemon/ipc-contract/workspace.ts +12 -1
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +8 -0
- package/src/daemon/server.ts +25 -3
- package/src/daemon/session-process.ts +450 -184
- package/src/daemon/tls-certs.ts +17 -12
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/memory/channel-delivery-store.ts +18 -20
- package/src/memory/channel-guardian-store.ts +39 -42
- package/src/memory/conversation-crud.ts +2 -2
- package/src/memory/conversation-queries.ts +2 -2
- package/src/memory/conversation-store.ts +24 -25
- package/src/memory/db-init.ts +17 -1
- package/src/memory/embedding-local.ts +16 -7
- package/src/memory/fts-reconciler.ts +41 -26
- package/src/memory/guardian-action-store.ts +65 -7
- package/src/memory/guardian-verification.ts +1 -0
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
- package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
- package/src/memory/migrations/033-scoped-approval-grants.ts +51 -0
- package/src/memory/migrations/034-guardian-action-tool-metadata.ts +12 -0
- package/src/memory/migrations/index.ts +6 -2
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/schema.ts +36 -1
- package/src/memory/scoped-approval-grants.ts +509 -0
- package/src/memory/search/semantic.ts +3 -3
- package/src/notifications/README.md +158 -17
- package/src/notifications/broadcaster.ts +68 -50
- package/src/notifications/conversation-pairing.ts +96 -18
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/deliveries-store.ts +12 -0
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/thread-candidates.ts +60 -25
- package/src/notifications/types.ts +2 -1
- package/src/permissions/checker.ts +28 -16
- package/src/permissions/defaults.ts +14 -4
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-grant-minter.ts +97 -0
- package/src/runtime/http-server.ts +11 -11
- package/src/runtime/routes/access-request-decision.ts +1 -1
- package/src/runtime/routes/debug-routes.ts +4 -4
- package/src/runtime/routes/guardian-approval-interception.ts +120 -4
- package/src/runtime/routes/inbound-message-handler.ts +100 -33
- package/src/runtime/routes/integration-routes.ts +2 -2
- package/src/security/tool-approval-digest.ts +67 -0
- package/src/skills/remote-skill-policy.ts +131 -0
- package/src/tools/permission-checker.ts +1 -2
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/system/voice-config.ts +1 -1
- package/src/version.ts +29 -2
|
@@ -42,20 +42,20 @@ mock.module('../util/logger.js', () => ({
|
|
|
42
42
|
}),
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
|
-
import { initializeDb, resetDb } from '../memory/db.js';
|
|
46
45
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} from '../
|
|
46
|
+
createBinding,
|
|
47
|
+
getActiveBinding,
|
|
48
|
+
} from '../memory/channel-guardian-store.js';
|
|
49
|
+
import { getDb, initializeDb, resetDb } from '../memory/db.js';
|
|
50
50
|
import {
|
|
51
51
|
findMember,
|
|
52
|
-
upsertMember,
|
|
53
52
|
revokeMember,
|
|
53
|
+
upsertMember,
|
|
54
54
|
} from '../memory/ingress-member-store.js';
|
|
55
55
|
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} from '../
|
|
56
|
+
createOutboundSession,
|
|
57
|
+
validateAndConsumeChallenge,
|
|
58
|
+
} from '../runtime/channel-guardian-service.js';
|
|
59
59
|
|
|
60
60
|
initializeDb();
|
|
61
61
|
|
|
@@ -69,7 +69,6 @@ afterAll(() => {
|
|
|
69
69
|
// ---------------------------------------------------------------------------
|
|
70
70
|
|
|
71
71
|
function resetTables(): void {
|
|
72
|
-
const { getDb } = require('../memory/db.js');
|
|
73
72
|
const db = getDb();
|
|
74
73
|
db.run('DELETE FROM channel_guardian_verification_challenges');
|
|
75
74
|
db.run('DELETE FROM channel_guardian_bindings');
|
|
@@ -339,6 +338,7 @@ describe('trusted contact verification → member activation', () => {
|
|
|
339
338
|
|
|
340
339
|
test('guardian inbound verification still creates binding (backward compat)', () => {
|
|
341
340
|
// Create an inbound challenge (no expected identity — guardian flow)
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
342
342
|
const { createVerificationChallenge } = require('../runtime/channel-guardian-service.js');
|
|
343
343
|
const { secret } = createVerificationChallenge('self', 'telegram');
|
|
344
344
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { existsSync, mkdirSync,
|
|
3
|
-
import { join } from 'node:path';
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
3
|
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test';
|
|
5
7
|
|
|
6
8
|
// --- In-memory checkpoint store ---
|
|
7
9
|
const store = new Map<string, string>();
|
|
@@ -14,6 +16,12 @@ mock.module('../memory/checkpoints.js', () => ({
|
|
|
14
16
|
// --- Temp directory for workspace paths ---
|
|
15
17
|
let tempDir: string;
|
|
16
18
|
|
|
19
|
+
// --- Temp directory for template files ---
|
|
20
|
+
// Avoids mutating the real source-controlled UPDATES.md template, preventing
|
|
21
|
+
// race conditions with parallel test execution and working tree corruption
|
|
22
|
+
// if the test process crashes.
|
|
23
|
+
let tempTemplateDir: string;
|
|
24
|
+
|
|
17
25
|
// Mock platform to avoid env-registry transitive imports.
|
|
18
26
|
// All needed exports are stubbed; getWorkspacePromptPath is the only one
|
|
19
27
|
// exercised by update-bulletin.ts.
|
|
@@ -92,17 +100,31 @@ mock.module('../version.js', () => ({
|
|
|
92
100
|
APP_VERSION: '1.0.0',
|
|
93
101
|
}));
|
|
94
102
|
|
|
103
|
+
// Mock the template path module so tests read from a temp directory instead
|
|
104
|
+
// of the real source-controlled template file.
|
|
105
|
+
mock.module('../config/update-bulletin-template-path.js', () => ({
|
|
106
|
+
getTemplatePath: () => join(tempTemplateDir, 'UPDATES.md'),
|
|
107
|
+
}));
|
|
108
|
+
|
|
95
109
|
const { syncUpdateBulletinOnStartup } = await import('../config/update-bulletin.js');
|
|
96
110
|
|
|
111
|
+
const TEST_TEMPLATE = '## What\'s New\n\nTest release notes.\n';
|
|
112
|
+
const COMMENT_ONLY_TEMPLATE = '_ This is a comment-only template.\n_ No real content here.\n';
|
|
113
|
+
|
|
97
114
|
describe('syncUpdateBulletinOnStartup', () => {
|
|
98
115
|
beforeEach(() => {
|
|
99
116
|
store.clear();
|
|
100
117
|
tempDir = join(tmpdir(), `update-bulletin-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
101
118
|
mkdirSync(tempDir, { recursive: true });
|
|
119
|
+
tempTemplateDir = join(tmpdir(), `update-bulletin-tpl-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
120
|
+
mkdirSync(tempTemplateDir, { recursive: true });
|
|
121
|
+
// Write a test template with real content so materialization proceeds
|
|
122
|
+
writeFileSync(join(tempTemplateDir, 'UPDATES.md'), TEST_TEMPLATE, 'utf-8');
|
|
102
123
|
});
|
|
103
124
|
|
|
104
125
|
afterEach(() => {
|
|
105
126
|
rmSync(tempDir, { recursive: true, force: true });
|
|
127
|
+
rmSync(tempTemplateDir, { recursive: true, force: true });
|
|
106
128
|
});
|
|
107
129
|
|
|
108
130
|
it('creates workspace file on first eligible run', () => {
|
|
@@ -257,4 +279,45 @@ describe('syncUpdateBulletinOnStartup', () => {
|
|
|
257
279
|
const tmpFiles = entries.filter((e) => e.includes('.tmp.'));
|
|
258
280
|
expect(tmpFiles).toHaveLength(0);
|
|
259
281
|
});
|
|
282
|
+
|
|
283
|
+
it('skips materialization when template is comment-only', () => {
|
|
284
|
+
// Write a comment-only template fixture (no real content after stripping)
|
|
285
|
+
writeFileSync(join(tempTemplateDir, 'UPDATES.md'), COMMENT_ONLY_TEMPLATE, 'utf-8');
|
|
286
|
+
|
|
287
|
+
const workspacePath = join(tempDir, 'UPDATES.md');
|
|
288
|
+
syncUpdateBulletinOnStartup();
|
|
289
|
+
|
|
290
|
+
expect(existsSync(workspacePath)).toBe(false);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('preserves existing file when atomic write fails', () => {
|
|
294
|
+
const workspacePath = join(tempDir, 'UPDATES.md');
|
|
295
|
+
const originalContent = '<!-- vellum-update-release:0.9.0 -->\nOriginal content.\n';
|
|
296
|
+
writeFileSync(workspacePath, originalContent, 'utf-8');
|
|
297
|
+
|
|
298
|
+
// Mock writeFileSync to throw when writing the temp file, simulating a
|
|
299
|
+
// disk-full or permission error deterministically (chmod-based approaches
|
|
300
|
+
// are unreliable when running as root or with CAP_DAC_OVERRIDE).
|
|
301
|
+
const originalWriteFileSync = fs.writeFileSync;
|
|
302
|
+
const spy = spyOn(fs, 'writeFileSync').mockImplementation((...args: Parameters<typeof fs.writeFileSync>) => {
|
|
303
|
+
if (typeof args[0] === 'string' && args[0].includes('.tmp.')) {
|
|
304
|
+
throw new Error('Simulated write failure');
|
|
305
|
+
}
|
|
306
|
+
return originalWriteFileSync(...args);
|
|
307
|
+
});
|
|
308
|
+
try {
|
|
309
|
+
expect(() => syncUpdateBulletinOnStartup()).toThrow('Simulated write failure');
|
|
310
|
+
} finally {
|
|
311
|
+
spy.mockRestore();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Original content should be preserved (atomic write never renamed over it)
|
|
315
|
+
const content = readFileSync(workspacePath, 'utf-8');
|
|
316
|
+
expect(content).toBe(originalContent);
|
|
317
|
+
|
|
318
|
+
// No temp file leftovers
|
|
319
|
+
const entries = readdirSync(tempDir);
|
|
320
|
+
const tmpFiles = entries.filter((e: string) => e.includes('.tmp.'));
|
|
321
|
+
expect(tmpFiles).toHaveLength(0);
|
|
322
|
+
});
|
|
260
323
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Contract test: ensures the bundled UPDATES.md template exists and
|
|
3
|
-
* the format expectations that the bulletin system depends on at runtime.
|
|
2
|
+
* Contract test: ensures the bundled UPDATES.md template exists and is readable.
|
|
4
3
|
*
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
4
|
+
* The template may be comment-only (no real content) for no-op releases —
|
|
5
|
+
* the bulletin system treats an empty-after-stripping template as a skip signal.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { existsSync, readFileSync } from 'node:fs';
|
|
10
9
|
import { join } from 'node:path';
|
|
10
|
+
|
|
11
11
|
import { describe, expect, test } from 'bun:test';
|
|
12
12
|
|
|
13
13
|
const TEMPLATE_PATH = join(import.meta.dirname, '..', 'config', 'templates', 'UPDATES.md');
|
|
@@ -17,13 +17,8 @@ describe('UPDATES.md template contract', () => {
|
|
|
17
17
|
expect(existsSync(TEMPLATE_PATH)).toBe(true);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
test('template
|
|
21
|
-
const content = readFileSync(TEMPLATE_PATH, 'utf-8');
|
|
22
|
-
expect(content.trim().length).toBeGreaterThan(0);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('template contains the "## What\'s New" heading', () => {
|
|
20
|
+
test('template is a readable UTF-8 file', () => {
|
|
26
21
|
const content = readFileSync(TEMPLATE_PATH, 'utf-8');
|
|
27
|
-
expect(content).
|
|
22
|
+
expect(typeof content).toBe('string');
|
|
28
23
|
});
|
|
29
24
|
});
|