@vellumai/assistant 0.3.27 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +81 -4
- package/Dockerfile +2 -2
- package/bun.lock +4 -1
- package/docs/trusted-contact-access.md +9 -2
- package/package.json +6 -3
- package/scripts/ipc/generate-swift.ts +9 -5
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +80 -0
- package/src/__tests__/agent-loop-thinking.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +119 -0
- package/src/__tests__/approval-routes-http.test.ts +13 -5
- package/src/__tests__/asset-materialize-tool.test.ts +2 -0
- package/src/__tests__/asset-search-tool.test.ts +2 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +4 -2
- package/src/__tests__/attachments-store.test.ts +2 -0
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/bundled-asset.test.ts +107 -0
- package/src/__tests__/call-controller.test.ts +30 -29
- package/src/__tests__/call-routes-http.test.ts +34 -32
- package/src/__tests__/call-start-guardian-guard.test.ts +2 -0
- package/src/__tests__/canonical-guardian-store.test.ts +636 -0
- package/src/__tests__/channel-approval-routes.test.ts +174 -1
- package/src/__tests__/channel-invite-transport.test.ts +6 -6
- package/src/__tests__/channel-reply-delivery.test.ts +19 -0
- package/src/__tests__/channel-retry-sweep.test.ts +130 -0
- package/src/__tests__/clarification-resolver.test.ts +2 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -1
- package/src/__tests__/computer-use-session-lifecycle.test.ts +2 -0
- package/src/__tests__/computer-use-session-working-dir.test.ts +1 -0
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +2 -0
- package/src/__tests__/config-schema.test.ts +5 -5
- package/src/__tests__/config-watcher.test.ts +3 -1
- package/src/__tests__/connection-policy.test.ts +14 -5
- package/src/__tests__/contacts-tools.test.ts +3 -1
- package/src/__tests__/contradiction-checker.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +10 -0
- package/src/__tests__/conversation-routes.test.ts +1 -1
- package/src/__tests__/credential-security-invariants.test.ts +16 -6
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/credential-vault.test.ts +5 -4
- package/src/__tests__/daemon-lifecycle.test.ts +9 -0
- package/src/__tests__/daemon-server-session-init.test.ts +27 -0
- package/src/__tests__/elevenlabs-config.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +43 -1
- package/src/__tests__/encrypted-store.test.ts +10 -5
- package/src/__tests__/followup-tools.test.ts +3 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +21 -21
- package/src/__tests__/gmail-integration.test.ts +0 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +205 -345
- package/src/__tests__/guardian-control-plane-policy.test.ts +19 -19
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +599 -0
- package/src/__tests__/guardian-dispatch.test.ts +21 -19
- package/src/__tests__/guardian-grant-minting.test.ts +68 -1
- package/src/__tests__/guardian-outbound-http.test.ts +12 -9
- package/src/__tests__/guardian-routing-invariants.test.ts +1092 -0
- package/src/__tests__/handle-user-message-secret-resume.test.ts +1 -0
- package/src/__tests__/handlers-slack-config.test.ts +3 -1
- package/src/__tests__/handlers-telegram-config.test.ts +3 -1
- package/src/__tests__/handlers-twilio-config.test.ts +3 -1
- package/src/__tests__/handlers-twitter-config.test.ts +3 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +318 -0
- package/src/__tests__/heartbeat-service.test.ts +20 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +33 -0
- package/src/__tests__/ingress-reconcile.test.ts +3 -1
- package/src/__tests__/ingress-routes-http.test.ts +231 -4
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +13 -0
- package/src/__tests__/mcp-cli.test.ts +77 -0
- package/src/__tests__/media-generate-image.test.ts +21 -0
- package/src/__tests__/media-reuse-story.e2e.test.ts +2 -0
- package/src/__tests__/memory-regressions.test.ts +20 -20
- package/src/__tests__/non-member-access-request.test.ts +212 -36
- package/src/__tests__/notification-decision-fallback.test.ts +63 -3
- package/src/__tests__/notification-decision-strategy.test.ts +78 -0
- package/src/__tests__/notification-guardian-path.test.ts +15 -15
- package/src/__tests__/oauth-connect-handler.test.ts +3 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +4 -4
- package/src/__tests__/onboarding-template-contract.test.ts +116 -21
- package/src/__tests__/pairing-routes.test.ts +171 -0
- package/src/__tests__/playbook-execution.test.ts +3 -1
- package/src/__tests__/playbook-tools.test.ts +3 -1
- package/src/__tests__/provider-error-scenarios.test.ts +59 -8
- package/src/__tests__/proxy-approval-callback.test.ts +2 -0
- package/src/__tests__/recording-handler.test.ts +11 -0
- package/src/__tests__/recording-intent-handler.test.ts +15 -0
- package/src/__tests__/recording-state-machine.test.ts +13 -2
- package/src/__tests__/registry.test.ts +7 -3
- package/src/__tests__/relay-server.test.ts +148 -28
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +21 -0
- package/src/__tests__/runtime-events-sse.test.ts +4 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -0
- package/src/__tests__/schedule-tools.test.ts +3 -1
- package/src/__tests__/secret-scanner-executor.test.ts +59 -0
- package/src/__tests__/secret-scanner.test.ts +8 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -0
- package/src/__tests__/sensitive-output-placeholders.test.ts +208 -0
- package/src/__tests__/session-abort-tool-results.test.ts +23 -0
- package/src/__tests__/session-agent-loop.test.ts +16 -0
- package/src/__tests__/session-conflict-gate.test.ts +21 -0
- package/src/__tests__/session-load-history-repair.test.ts +27 -17
- package/src/__tests__/session-pre-run-repair.test.ts +23 -0
- package/src/__tests__/session-profile-injection.test.ts +21 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +20 -0
- package/src/__tests__/session-queue.test.ts +23 -0
- package/src/__tests__/session-runtime-assembly.test.ts +126 -59
- package/src/__tests__/session-skill-tools.test.ts +27 -5
- package/src/__tests__/session-slash-known.test.ts +23 -0
- package/src/__tests__/session-slash-queue.test.ts +23 -0
- package/src/__tests__/session-slash-unknown.test.ts +23 -0
- package/src/__tests__/session-workspace-cache-state.test.ts +7 -0
- package/src/__tests__/session-workspace-injection.test.ts +21 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +21 -0
- package/src/__tests__/shell-credential-ref.test.ts +2 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -4
- package/src/__tests__/skill-projection-feature-flag.test.ts +22 -0
- package/src/__tests__/skills.test.ts +8 -4
- package/src/__tests__/slack-channel-config.test.ts +3 -1
- package/src/__tests__/subagent-tools.test.ts +19 -0
- package/src/__tests__/swarm-recursion.test.ts +2 -0
- package/src/__tests__/swarm-session-integration.test.ts +2 -0
- package/src/__tests__/swarm-tool.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +3 -1
- package/src/__tests__/task-compiler.test.ts +3 -1
- package/src/__tests__/task-management-tools.test.ts +3 -1
- package/src/__tests__/task-tools.test.ts +3 -1
- package/src/__tests__/terminal-sandbox.test.ts +13 -12
- package/src/__tests__/terminal-tools.test.ts +2 -0
- package/src/__tests__/tool-approval-handler.test.ts +15 -15
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -0
- package/src/__tests__/tool-grant-request-escalation.test.ts +497 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +48 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +22 -19
- package/src/__tests__/trusted-contact-verification.test.ts +91 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +2 -0
- package/src/__tests__/twitter-auth-handler.test.ts +3 -1
- package/src/__tests__/twitter-cli-routing.test.ts +3 -1
- package/src/__tests__/view-image-tool.test.ts +3 -1
- package/src/__tests__/voice-invite-redemption.test.ts +329 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +7 -5
- package/src/__tests__/voice-session-bridge.test.ts +10 -10
- package/src/__tests__/work-item-output.test.ts +3 -1
- package/src/__tests__/workspace-lifecycle.test.ts +13 -2
- package/src/agent/loop.ts +46 -3
- package/src/approvals/guardian-decision-primitive.ts +285 -0
- package/src/approvals/guardian-request-resolvers.ts +539 -0
- package/src/calls/call-controller.ts +26 -23
- package/src/calls/guardian-action-sweep.ts +10 -2
- package/src/calls/guardian-dispatch.ts +46 -40
- package/src/calls/relay-server.ts +358 -24
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +3 -3
- package/src/cli.ts +12 -0
- package/src/config/agent-schema.ts +14 -3
- package/src/config/calls-schema.ts +6 -6
- package/src/config/core-schema.ts +3 -3
- package/src/config/feature-flag-registry.json +8 -0
- package/src/config/mcp-schema.ts +1 -1
- package/src/config/memory-schema.ts +27 -19
- package/src/config/schema.ts +21 -21
- package/src/config/skills-schema.ts +7 -7
- package/src/config/system-prompt.ts +2 -1
- package/src/config/templates/BOOTSTRAP.md +47 -31
- package/src/config/templates/USER.md +5 -0
- package/src/config/update-bulletin-template-path.ts +4 -1
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +149 -21
- package/src/daemon/handlers/config-inbox.ts +4 -4
- package/src/daemon/handlers/guardian-actions.ts +45 -66
- package/src/daemon/handlers/sessions.ts +148 -4
- package/src/daemon/ipc-contract/guardian-actions.ts +7 -0
- package/src/daemon/ipc-contract/messages.ts +16 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +22 -16
- package/src/daemon/pairing-store.ts +86 -3
- package/src/daemon/server.ts +18 -0
- package/src/daemon/session-agent-loop-handlers.ts +5 -4
- package/src/daemon/session-agent-loop.ts +33 -6
- package/src/daemon/session-lifecycle.ts +25 -17
- package/src/daemon/session-memory.ts +2 -2
- package/src/daemon/session-process.ts +68 -326
- package/src/daemon/session-runtime-assembly.ts +119 -25
- package/src/daemon/session-tool-setup.ts +3 -2
- package/src/daemon/session.ts +4 -3
- package/src/home-base/prebuilt/seed.ts +2 -1
- package/src/hooks/templates.ts +2 -1
- package/src/memory/canonical-guardian-store.ts +586 -0
- package/src/memory/channel-guardian-store.ts +2 -0
- package/src/memory/conversation-crud.ts +7 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/embedding-local.ts +257 -39
- package/src/memory/embedding-runtime-manager.ts +471 -0
- package/src/memory/guardian-action-store.ts +7 -60
- package/src/memory/guardian-approvals.ts +9 -4
- package/src/memory/guardian-bindings.ts +25 -1
- package/src/memory/indexer.ts +3 -3
- package/src/memory/ingress-invite-store.ts +45 -0
- package/src/memory/job-handlers/backfill.ts +16 -9
- package/src/memory/migrations/036-normalize-phone-identities.ts +289 -0
- package/src/memory/migrations/037-voice-invite-columns.ts +16 -0
- package/src/memory/migrations/118-reminder-routing-intent.ts +3 -3
- package/src/memory/migrations/121-canonical-guardian-requests.ts +59 -0
- package/src/memory/migrations/122-canonical-guardian-requester-chat-id.ts +15 -0
- package/src/memory/migrations/123-canonical-guardian-deliveries-destination-index.ts +15 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +5 -0
- package/src/memory/qdrant-client.ts +31 -22
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/schema.ts +56 -0
- package/src/notifications/copy-composer.ts +31 -4
- package/src/notifications/decision-engine.ts +57 -0
- package/src/permissions/defaults.ts +2 -0
- package/src/runtime/access-request-helper.ts +173 -0
- package/src/runtime/actor-trust-resolver.ts +221 -0
- package/src/runtime/channel-guardian-service.ts +12 -4
- package/src/runtime/channel-invite-transports/voice.ts +58 -0
- package/src/runtime/channel-retry-sweep.ts +18 -6
- package/src/runtime/guardian-context-resolver.ts +38 -71
- package/src/runtime/guardian-decision-types.ts +6 -0
- package/src/runtime/guardian-reply-router.ts +717 -0
- package/src/runtime/http-server.ts +8 -0
- package/src/runtime/ingress-service.ts +80 -3
- package/src/runtime/invite-redemption-service.ts +141 -2
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +116 -0
- package/src/runtime/routes/channel-route-shared.ts +1 -1
- package/src/runtime/routes/channel-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +20 -2
- package/src/runtime/routes/guardian-action-routes.ts +100 -109
- package/src/runtime/routes/guardian-approval-interception.ts +17 -6
- package/src/runtime/routes/inbound-message-handler.ts +205 -529
- package/src/runtime/routes/ingress-routes.ts +52 -4
- package/src/runtime/routes/pairing-routes.ts +3 -0
- package/src/runtime/tool-grant-request-helper.ts +195 -0
- package/src/tools/executor.ts +13 -1
- package/src/tools/guardian-control-plane-policy.ts +2 -2
- package/src/tools/sensitive-output-placeholders.ts +203 -0
- package/src/tools/tool-approval-handler.ts +53 -10
- package/src/tools/types.ts +13 -2
- package/src/util/bundled-asset.ts +31 -0
- package/src/util/canonicalize-identity.ts +52 -0
- package/src/util/logger.ts +20 -8
- package/src/util/platform.ts +10 -0
- package/src/util/voice-code.ts +29 -0
- package/src/daemon/guardian-invite-intent.ts +0 -124
|
@@ -20,7 +20,7 @@ let currentConfig: Record<string, unknown> = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const DECLARED_SKILL_ID = 'hatch-new-assistant';
|
|
23
|
-
const
|
|
23
|
+
const DECLARED_FLAG_KEY = 'feature_flags.hatch-new-assistant.enabled';
|
|
24
24
|
|
|
25
25
|
mock.module('../util/platform.js', () => ({
|
|
26
26
|
getRootDir: () => TEST_DIR,
|
|
@@ -121,7 +121,7 @@ describe('buildSystemPrompt feature flag filtering', () => {
|
|
|
121
121
|
|
|
122
122
|
currentConfig = {
|
|
123
123
|
sandbox: { enabled: false, backend: 'native' },
|
|
124
|
-
|
|
124
|
+
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: false },
|
|
125
125
|
};
|
|
126
126
|
|
|
127
127
|
const result = buildSystemPrompt();
|
|
@@ -137,7 +137,7 @@ describe('buildSystemPrompt feature flag filtering', () => {
|
|
|
137
137
|
|
|
138
138
|
currentConfig = {
|
|
139
139
|
sandbox: { enabled: false, backend: 'native' },
|
|
140
|
-
|
|
140
|
+
assistantFeatureFlagValues: {},
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
const result = buildSystemPrompt();
|
|
@@ -152,9 +152,9 @@ describe('buildSystemPrompt feature flag filtering', () => {
|
|
|
152
152
|
|
|
153
153
|
currentConfig = {
|
|
154
154
|
sandbox: { enabled: false, backend: 'native' },
|
|
155
|
-
|
|
156
|
-
[
|
|
157
|
-
'
|
|
155
|
+
assistantFeatureFlagValues: {
|
|
156
|
+
[DECLARED_FLAG_KEY]: false,
|
|
157
|
+
'feature_flags.twitter.enabled': false,
|
|
158
158
|
},
|
|
159
159
|
};
|
|
160
160
|
|
|
@@ -15,7 +15,7 @@ let currentConfig: Record<string, unknown> = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
const DECLARED_SKILL_ID = 'hatch-new-assistant';
|
|
18
|
-
const
|
|
18
|
+
const DECLARED_FLAG_KEY = 'feature_flags.hatch-new-assistant.enabled';
|
|
19
19
|
|
|
20
20
|
const platformOverrides: Record<string, (...args: unknown[]) => unknown> = {
|
|
21
21
|
getRootDir: () => TEST_DIR,
|
|
@@ -54,6 +54,7 @@ mock.module('../util/logger.js', () => ({
|
|
|
54
54
|
getLogger: () => new Proxy({} as Record<string, unknown>, {
|
|
55
55
|
get: () => () => {},
|
|
56
56
|
}),
|
|
57
|
+
isDebug: () => false,
|
|
57
58
|
}));
|
|
58
59
|
|
|
59
60
|
mock.module('../config/loader.js', () => ({
|
|
@@ -101,7 +102,7 @@ describe('skill_load feature flag enforcement', () => {
|
|
|
101
102
|
writeFileSync(join(TEST_DIR, 'skills', 'SKILLS.md'), `- ${DECLARED_SKILL_ID}\n`);
|
|
102
103
|
|
|
103
104
|
currentConfig = {
|
|
104
|
-
|
|
105
|
+
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: false },
|
|
105
106
|
};
|
|
106
107
|
|
|
107
108
|
const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
|
|
@@ -116,7 +117,7 @@ describe('skill_load feature flag enforcement', () => {
|
|
|
116
117
|
writeFileSync(join(TEST_DIR, 'skills', 'SKILLS.md'), `- ${DECLARED_SKILL_ID}\n`);
|
|
117
118
|
|
|
118
119
|
currentConfig = {
|
|
119
|
-
|
|
120
|
+
assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: true },
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
|
|
@@ -130,7 +131,7 @@ describe('skill_load feature flag enforcement', () => {
|
|
|
130
131
|
writeFileSync(join(TEST_DIR, 'skills', 'SKILLS.md'), `- ${DECLARED_SKILL_ID}\n`);
|
|
131
132
|
|
|
132
133
|
currentConfig = {
|
|
133
|
-
|
|
134
|
+
assistantFeatureFlagValues: {},
|
|
134
135
|
};
|
|
135
136
|
|
|
136
137
|
const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
|
|
@@ -31,10 +31,31 @@ const DECLARED_LEGACY_KEY = 'skills.hatch-new-assistant.enabled';
|
|
|
31
31
|
|
|
32
32
|
mock.module('../config/skills.js', () => ({
|
|
33
33
|
loadSkillCatalog: () => mockCatalog,
|
|
34
|
+
checkSkillRequirements: () => ({ satisfied: true, missing: [] }),
|
|
34
35
|
}));
|
|
35
36
|
|
|
36
37
|
mock.module('../config/loader.js', () => ({
|
|
37
38
|
getConfig: () => currentConfig,
|
|
39
|
+
loadConfig: () => currentConfig,
|
|
40
|
+
invalidateConfigCache: () => {},
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
mock.module('../config/assistant-feature-flags.js', () => ({
|
|
44
|
+
isAssistantFeatureFlagEnabled: (key: string, config: Record<string, unknown>) => {
|
|
45
|
+
const vals = (config as { assistantFeatureFlagValues?: Record<string, boolean> }).assistantFeatureFlagValues;
|
|
46
|
+
if (vals && typeof vals[key] === 'boolean') return vals[key];
|
|
47
|
+
// Check legacy featureFlags too
|
|
48
|
+
const legacy = (config as { featureFlags?: Record<string, boolean> }).featureFlags;
|
|
49
|
+
if (legacy && typeof legacy[key] === 'boolean') return legacy[key];
|
|
50
|
+
return true; // default enabled
|
|
51
|
+
},
|
|
52
|
+
loadDefaultsRegistry: () => ({}),
|
|
53
|
+
getAssistantFeatureFlagDefaults: () => ({}),
|
|
54
|
+
_resetDefaultsCache: () => {},
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
mock.module('../config/skill-state.js', () => ({
|
|
58
|
+
skillFlagKey: (skillId: string) => `skills.${skillId}.enabled`,
|
|
38
59
|
}));
|
|
39
60
|
|
|
40
61
|
mock.module('../skills/active-skill-tools.js', () => {
|
|
@@ -184,6 +205,7 @@ mock.module('../util/logger.js', () => ({
|
|
|
184
205
|
debug: () => {},
|
|
185
206
|
error: () => {},
|
|
186
207
|
}),
|
|
208
|
+
isDebug: () => false,
|
|
187
209
|
}));
|
|
188
210
|
|
|
189
211
|
// ---------------------------------------------------------------------------
|
|
@@ -518,14 +518,14 @@ describe('bundled browser skill', () => {
|
|
|
518
518
|
expect(browserSkill!.disableModelInvocation).toBe(false);
|
|
519
519
|
});
|
|
520
520
|
|
|
521
|
-
test('browser skill has a valid tool manifest with
|
|
521
|
+
test('browser skill has a valid tool manifest with 14 tools', () => {
|
|
522
522
|
const catalog = loadSkillCatalog();
|
|
523
523
|
const browserSkill = catalog.find((s) => s.id === 'browser');
|
|
524
524
|
expect(browserSkill).toBeDefined();
|
|
525
525
|
expect(browserSkill!.toolManifest).toBeDefined();
|
|
526
526
|
expect(browserSkill!.toolManifest!.present).toBe(true);
|
|
527
527
|
expect(browserSkill!.toolManifest!.valid).toBe(true);
|
|
528
|
-
expect(browserSkill!.toolManifest!.toolCount).toBe(
|
|
528
|
+
expect(browserSkill!.toolManifest!.toolCount).toBe(14);
|
|
529
529
|
expect(browserSkill!.toolManifest!.toolNames).toEqual([
|
|
530
530
|
'browser_navigate',
|
|
531
531
|
'browser_snapshot',
|
|
@@ -534,8 +534,12 @@ describe('bundled browser skill', () => {
|
|
|
534
534
|
'browser_click',
|
|
535
535
|
'browser_type',
|
|
536
536
|
'browser_press_key',
|
|
537
|
+
'browser_scroll',
|
|
538
|
+
'browser_select_option',
|
|
539
|
+
'browser_hover',
|
|
537
540
|
'browser_wait_for',
|
|
538
541
|
'browser_extract',
|
|
542
|
+
'browser_wait_for_download',
|
|
539
543
|
'browser_fill_credential',
|
|
540
544
|
]);
|
|
541
545
|
});
|
|
@@ -618,10 +622,10 @@ describe('ingress-dependent setup skills declare public-ingress', () => {
|
|
|
618
622
|
expect(includes).toContain('public-ingress');
|
|
619
623
|
});
|
|
620
624
|
|
|
621
|
-
test('slack-oauth-setup includes
|
|
625
|
+
test('slack-oauth-setup includes browser', () => {
|
|
622
626
|
const includes = readSkillIncludes(VELLUM_SKILLS_DIR, 'slack-oauth-setup');
|
|
623
627
|
expect(includes).toBeDefined();
|
|
624
|
-
expect(includes).toContain('
|
|
628
|
+
expect(includes).toContain('browser');
|
|
625
629
|
});
|
|
626
630
|
});
|
|
627
631
|
|
|
@@ -7,7 +7,9 @@ import { afterAll, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
|
7
7
|
const testDir = mkdtempSync(join(tmpdir(), 'slack-channel-cfg-test-'));
|
|
8
8
|
|
|
9
9
|
mock.module('../config/loader.js', () => ({
|
|
10
|
-
getConfig: () => ({
|
|
10
|
+
getConfig: () => ({
|
|
11
|
+
ui: {},
|
|
12
|
+
}),
|
|
11
13
|
loadConfig: () => ({}),
|
|
12
14
|
loadRawConfig: () => ({}),
|
|
13
15
|
saveRawConfig: () => {},
|
|
@@ -6,6 +6,25 @@ import { describe, expect, mock, test } from 'bun:test';
|
|
|
6
6
|
// Mock conversation-store before importing tool executors that depend on it.
|
|
7
7
|
let mockGetMessages: (conversationId: string) => Array<{ role: string; content: string }> | null = () => null;
|
|
8
8
|
mock.module('../memory/conversation-store.js', () => ({
|
|
9
|
+
getConversationThreadType: () => 'default',
|
|
10
|
+
setConversationOriginChannelIfUnset: () => {},
|
|
11
|
+
updateConversationContextWindow: () => {},
|
|
12
|
+
deleteMessageById: () => {},
|
|
13
|
+
updateConversationTitle: () => {},
|
|
14
|
+
updateConversationUsage: () => {},
|
|
15
|
+
addMessage: () => ({ id: 'mock-msg-id' }),
|
|
16
|
+
getConversation: () => ({
|
|
17
|
+
id: 'conv-1',
|
|
18
|
+
contextSummary: null,
|
|
19
|
+
contextCompactedMessageCount: 0,
|
|
20
|
+
totalInputTokens: 0,
|
|
21
|
+
totalOutputTokens: 0,
|
|
22
|
+
totalEstimatedCost: 0,
|
|
23
|
+
title: null,
|
|
24
|
+
}),
|
|
25
|
+
provenanceFromGuardianContext: () => ({ source: 'user', guardianContext: undefined }),
|
|
26
|
+
getConversationOriginInterface: () => null,
|
|
27
|
+
getConversationOriginChannel: () => null,
|
|
9
28
|
getMessages: (conversationId: string) => mockGetMessages(conversationId),
|
|
10
29
|
createConversation: () => ({ id: 'mock-conv' }),
|
|
11
30
|
}));
|
|
@@ -49,6 +49,8 @@ mock.module('../util/logger.js', () => ({
|
|
|
49
49
|
|
|
50
50
|
mock.module('../config/loader.js', () => ({
|
|
51
51
|
getConfig: () => ({
|
|
52
|
+
ui: {},
|
|
53
|
+
|
|
52
54
|
sandbox: { enabled: true },
|
|
53
55
|
}),
|
|
54
56
|
}));
|
|
@@ -203,7 +205,7 @@ describe('buildSystemPrompt', () => {
|
|
|
203
205
|
|
|
204
206
|
test('config section uses workspace directory from platform util', () => {
|
|
205
207
|
const result = buildSystemPrompt();
|
|
206
|
-
expect(result).toContain(`Your
|
|
208
|
+
expect(result).toContain(`Your configuration directory is \`${TEST_DIR}/\`.`);
|
|
207
209
|
});
|
|
208
210
|
|
|
209
211
|
test('omits user skills from catalog when none are configured', () => {
|
|
@@ -177,20 +177,21 @@ describe('terminal sandbox — macOS sandbox-exec behavior', () => {
|
|
|
177
177
|
expect(result.args.slice(2)).toEqual(['bash', '-c', '--', 'echo hello']);
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
test('
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
expect(
|
|
180
|
+
test('escapes SBPL metacharacters in working dirs instead of throwing', () => {
|
|
181
|
+
// The sandbox now escapes metacharacters rather than rejecting them
|
|
182
|
+
const result1 = wrapCommand('pwd', '/tmp/bad"dir', nativeConfig());
|
|
183
|
+
expect(result1.sandboxed).toBe(true);
|
|
184
|
+
const result2 = wrapCommand('pwd', '/tmp/bad(dir', nativeConfig());
|
|
185
|
+
expect(result2.sandboxed).toBe(true);
|
|
186
|
+
const result3 = wrapCommand('pwd', '/tmp/bad;dir', nativeConfig());
|
|
187
|
+
expect(result3.sandboxed).toBe(true);
|
|
184
188
|
});
|
|
185
189
|
|
|
186
|
-
test('SBPL
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
expect(err).toBeInstanceOf(ToolError);
|
|
192
|
-
expect((err as Error).message).toContain('SBPL metacharacters');
|
|
193
|
-
}
|
|
190
|
+
test('SBPL profile escapes metacharacters in working dir path', () => {
|
|
191
|
+
// Verify the sandbox profile is written with escaped chars
|
|
192
|
+
wrapCommand('pwd', '/tmp/bad"dir', nativeConfig());
|
|
193
|
+
const profileContent = writeFileSyncMock.mock.calls[0]?.[1] as string;
|
|
194
|
+
expect(profileContent).toContain('bad\\"dir');
|
|
194
195
|
});
|
|
195
196
|
});
|
|
196
197
|
|
|
@@ -100,7 +100,7 @@ function makeContext(overrides: Partial<ToolContext> = {}): ToolContext {
|
|
|
100
100
|
conversationId: 'conv-1',
|
|
101
101
|
assistantId: 'self',
|
|
102
102
|
requestId: 'req-1',
|
|
103
|
-
|
|
103
|
+
guardianTrustClass: 'trusted_contact',
|
|
104
104
|
...overrides,
|
|
105
105
|
};
|
|
106
106
|
}
|
|
@@ -134,7 +134,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
134
134
|
);
|
|
135
135
|
expect(mintResult.ok).toBe(true);
|
|
136
136
|
|
|
137
|
-
const context = makeContext({
|
|
137
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact' });
|
|
138
138
|
const result = await handler.checkPreExecutionGates(
|
|
139
139
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
140
140
|
);
|
|
@@ -149,7 +149,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
149
149
|
const toolName = 'bash';
|
|
150
150
|
const input = { command: 'rm -rf /' };
|
|
151
151
|
|
|
152
|
-
const context = makeContext({
|
|
152
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact' });
|
|
153
153
|
const result = await handler.checkPreExecutionGates(
|
|
154
154
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
155
155
|
);
|
|
@@ -177,7 +177,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
177
177
|
}),
|
|
178
178
|
);
|
|
179
179
|
|
|
180
|
-
const context = makeContext({
|
|
180
|
+
const context = makeContext({ guardianTrustClass: 'unknown' });
|
|
181
181
|
const result = await handler.checkPreExecutionGates(
|
|
182
182
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
183
183
|
);
|
|
@@ -189,7 +189,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
189
189
|
const toolName = 'bash';
|
|
190
190
|
const input = { command: 'deploy' };
|
|
191
191
|
|
|
192
|
-
const context = makeContext({
|
|
192
|
+
const context = makeContext({ guardianTrustClass: 'unknown' });
|
|
193
193
|
const result = await handler.checkPreExecutionGates(
|
|
194
194
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
195
195
|
);
|
|
@@ -212,7 +212,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
212
212
|
}),
|
|
213
213
|
);
|
|
214
214
|
|
|
215
|
-
const context = makeContext({
|
|
215
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact' });
|
|
216
216
|
|
|
217
217
|
// First invocation — should consume the grant and allow
|
|
218
218
|
const first = await handler.checkPreExecutionGates(
|
|
@@ -241,7 +241,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
241
241
|
}),
|
|
242
242
|
);
|
|
243
243
|
|
|
244
|
-
const context = makeContext({
|
|
244
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact' });
|
|
245
245
|
const result = await handler.checkPreExecutionGates(
|
|
246
246
|
toolName, invokeInput, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
247
247
|
);
|
|
@@ -264,7 +264,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
264
264
|
}),
|
|
265
265
|
);
|
|
266
266
|
|
|
267
|
-
const context = makeContext({
|
|
267
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact' });
|
|
268
268
|
const result = await handler.checkPreExecutionGates(
|
|
269
269
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
270
270
|
);
|
|
@@ -277,7 +277,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
277
277
|
const input = { command: 'deploy' };
|
|
278
278
|
|
|
279
279
|
// No grants minted at all
|
|
280
|
-
const context = makeContext({
|
|
280
|
+
const context = makeContext({ guardianTrustClass: 'guardian' });
|
|
281
281
|
const result = await handler.checkPreExecutionGates(
|
|
282
282
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
283
283
|
);
|
|
@@ -290,7 +290,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
290
290
|
const toolName = 'bash';
|
|
291
291
|
const input = { command: 'deploy' };
|
|
292
292
|
|
|
293
|
-
const context = makeContext({
|
|
293
|
+
const context = makeContext({ guardianTrustClass: undefined });
|
|
294
294
|
const result = await handler.checkPreExecutionGates(
|
|
295
295
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
296
296
|
);
|
|
@@ -309,7 +309,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
309
309
|
}),
|
|
310
310
|
);
|
|
311
311
|
|
|
312
|
-
const context = makeContext({
|
|
312
|
+
const context = makeContext({ guardianTrustClass: 'trusted_contact', requestId: 'req-1' });
|
|
313
313
|
const result = await handler.checkPreExecutionGates(
|
|
314
314
|
toolName, input, context, 'host', 'high', Date.now(), emitLifecycleEvent,
|
|
315
315
|
);
|
|
@@ -333,7 +333,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
333
333
|
|
|
334
334
|
// Context conversationId does not match the grant's conversationId
|
|
335
335
|
const context = makeContext({
|
|
336
|
-
|
|
336
|
+
guardianTrustClass: 'trusted_contact',
|
|
337
337
|
conversationId: 'conv-1',
|
|
338
338
|
});
|
|
339
339
|
const result = await handler.checkPreExecutionGates(
|
|
@@ -349,7 +349,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
349
349
|
|
|
350
350
|
// executionChannel defaults to undefined (non-voice)
|
|
351
351
|
const context = makeContext({
|
|
352
|
-
|
|
352
|
+
guardianTrustClass: 'trusted_contact',
|
|
353
353
|
executionChannel: 'telegram',
|
|
354
354
|
});
|
|
355
355
|
|
|
@@ -383,7 +383,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
383
383
|
}, 300);
|
|
384
384
|
|
|
385
385
|
const context = makeContext({
|
|
386
|
-
|
|
386
|
+
guardianTrustClass: 'trusted_contact',
|
|
387
387
|
executionChannel: 'voice',
|
|
388
388
|
});
|
|
389
389
|
|
|
@@ -408,7 +408,7 @@ describe('ToolApprovalHandler / pre-exec gate grant check', () => {
|
|
|
408
408
|
setTimeout(() => controller.abort(), 200);
|
|
409
409
|
|
|
410
410
|
const context = makeContext({
|
|
411
|
-
|
|
411
|
+
guardianTrustClass: 'trusted_contact',
|
|
412
412
|
executionChannel: 'voice',
|
|
413
413
|
signal: controller.signal,
|
|
414
414
|
});
|
|
@@ -59,6 +59,8 @@ mock.module('../permissions/trust-store.js', () => ({
|
|
|
59
59
|
|
|
60
60
|
mock.module('../config/loader.js', () => ({
|
|
61
61
|
getConfig: () => ({
|
|
62
|
+
ui: {},
|
|
63
|
+
|
|
62
64
|
provider: 'mock-provider',
|
|
63
65
|
timeouts: { permissionTimeoutSec: 5, toolExecutionTimeoutSec: 120 },
|
|
64
66
|
permissions: { mode: 'legacy' },
|