@vellumai/assistant 0.3.19 → 0.3.20
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 +151 -15
- package/Dockerfile +1 -0
- package/README.md +40 -4
- package/docs/architecture/integrations.md +7 -11
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +54 -0
- package/src/__tests__/approval-primitive.test.ts +540 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +206 -0
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +198 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +272 -0
- package/src/__tests__/call-controller.test.ts +439 -108
- package/src/__tests__/channel-invite-transport.test.ts +264 -0
- package/src/__tests__/cli.test.ts +42 -1
- package/src/__tests__/config-schema.test.ts +11 -127
- package/src/__tests__/config-watcher.test.ts +0 -8
- package/src/__tests__/daemon-lifecycle.test.ts +1 -0
- package/src/__tests__/daemon-server-session-init.test.ts +8 -2
- package/src/__tests__/diff.test.ts +22 -0
- package/src/__tests__/guardian-action-copy-generator.test.ts +5 -0
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +300 -32
- package/src/__tests__/guardian-action-late-reply.test.ts +546 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +774 -0
- package/src/__tests__/guardian-control-plane-policy.test.ts +36 -3
- package/src/__tests__/guardian-dispatch.test.ts +124 -0
- package/src/__tests__/guardian-grant-minting.test.ts +6 -17
- package/src/__tests__/inbound-invite-redemption.test.ts +367 -0
- package/src/__tests__/invite-redemption-service.test.ts +306 -0
- package/src/__tests__/ipc-snapshot.test.ts +57 -0
- package/src/__tests__/notification-decision-fallback.test.ts +88 -0
- package/src/__tests__/sandbox-diagnostics.test.ts +6 -249
- package/src/__tests__/sandbox-host-parity.test.ts +6 -13
- package/src/__tests__/scoped-approval-grants.test.ts +6 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +5 -4
- package/src/__tests__/script-proxy-session-manager.test.ts +1 -19
- package/src/__tests__/session-load-history-repair.test.ts +169 -2
- package/src/__tests__/session-runtime-assembly.test.ts +33 -5
- package/src/__tests__/skill-feature-flags-integration.test.ts +171 -0
- package/src/__tests__/skill-feature-flags.test.ts +188 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +141 -0
- package/src/__tests__/skill-mirror-parity.test.ts +1 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +363 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-sandbox.test.ts +142 -9
- package/src/__tests__/terminal-tools.test.ts +2 -93
- package/src/__tests__/thread-seed-composer.test.ts +18 -0
- package/src/__tests__/tool-approval-handler.test.ts +350 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +8 -10
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +46 -84
- package/src/agent/loop.ts +36 -1
- package/src/approvals/approval-primitive.ts +381 -0
- package/src/approvals/guardian-decision-primitive.ts +191 -0
- package/src/calls/call-controller.ts +252 -209
- package/src/calls/call-domain.ts +44 -6
- package/src/calls/guardian-dispatch.ts +48 -0
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +46 -30
- package/src/cli/core-commands.ts +0 -4
- package/src/cli.ts +76 -34
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +179 -0
- package/src/config/assistant-feature-flags.ts +162 -0
- package/src/config/bundled-skills/api-mapping/icon.svg +18 -0
- package/src/config/bundled-skills/messaging/TOOLS.json +30 -0
- package/src/config/bundled-skills/messaging/tools/slack-delete-message.ts +24 -0
- package/src/config/bundled-skills/notifications/SKILL.md +1 -1
- package/src/config/bundled-skills/reminder/SKILL.md +49 -2
- package/src/config/bundled-skills/time-based-actions/SKILL.md +49 -2
- package/src/config/bundled-skills/voice-setup/SKILL.md +122 -0
- package/src/config/core-schema.ts +1 -1
- package/src/config/env-registry.ts +10 -0
- package/src/config/feature-flag-registry.json +61 -0
- package/src/config/loader.ts +22 -1
- package/src/config/sandbox-schema.ts +0 -39
- package/src/config/schema.ts +6 -2
- package/src/config/skill-state.ts +34 -0
- package/src/config/skills-schema.ts +0 -1
- package/src/config/skills.ts +9 -0
- package/src/config/system-prompt.ts +110 -46
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +19 -1
- package/src/config/vellum-skills/catalog.json +1 -1
- package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +1 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +1 -1
- package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -1
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +104 -3
- package/src/config/vellum-skills/twilio-setup/SKILL.md +1 -1
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/guardian-invite-intent.ts +124 -0
- package/src/daemon/handlers/avatar.ts +68 -0
- package/src/daemon/handlers/browser.ts +2 -2
- package/src/daemon/handlers/guardian-actions.ts +120 -0
- package/src/daemon/handlers/index.ts +4 -0
- package/src/daemon/handlers/sessions.ts +19 -0
- package/src/daemon/handlers/shared.ts +3 -1
- package/src/daemon/install-cli-launchers.ts +58 -13
- package/src/daemon/ipc-contract/guardian-actions.ts +53 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -2
- package/src/daemon/ipc-contract/settings.ts +25 -2
- package/src/daemon/ipc-contract-inventory.json +10 -0
- package/src/daemon/ipc-contract.ts +4 -0
- package/src/daemon/lifecycle.ts +6 -2
- package/src/daemon/main.ts +1 -0
- package/src/daemon/server.ts +1 -0
- package/src/daemon/session-lifecycle.ts +52 -7
- package/src/daemon/session-memory.ts +45 -0
- package/src/daemon/session-process.ts +258 -432
- package/src/daemon/session-runtime-assembly.ts +12 -0
- package/src/daemon/session-skill-tools.ts +14 -1
- package/src/daemon/session-tool-setup.ts +5 -0
- package/src/daemon/session.ts +11 -0
- package/src/daemon/tool-side-effects.ts +35 -9
- package/src/index.ts +0 -2
- package/src/memory/conversation-display-order-migration.ts +44 -0
- package/src/memory/conversation-queries.ts +2 -0
- package/src/memory/conversation-store.ts +91 -0
- package/src/memory/db-init.ts +5 -1
- package/src/memory/embedding-local.ts +13 -8
- package/src/memory/guardian-action-store.ts +125 -2
- package/src/memory/ingress-invite-store.ts +95 -1
- package/src/memory/migrations/035-guardian-action-supersession.ts +23 -0
- package/src/memory/migrations/index.ts +2 -1
- package/src/memory/schema.ts +5 -1
- package/src/memory/scoped-approval-grants.ts +14 -5
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/types.ts +5 -0
- package/src/notifications/decision-engine.ts +49 -12
- package/src/notifications/emit-signal.ts +7 -0
- package/src/notifications/signal.ts +7 -0
- package/src/notifications/thread-seed-composer.ts +2 -1
- package/src/runtime/channel-approval-types.ts +16 -6
- package/src/runtime/channel-approvals.ts +19 -15
- package/src/runtime/channel-invite-transport.ts +85 -0
- package/src/runtime/channel-invite-transports/telegram.ts +105 -0
- package/src/runtime/guardian-action-grant-minter.ts +92 -35
- package/src/runtime/guardian-action-message-composer.ts +30 -0
- package/src/runtime/guardian-decision-types.ts +91 -0
- package/src/runtime/http-server.ts +23 -1
- package/src/runtime/ingress-service.ts +22 -0
- package/src/runtime/invite-redemption-service.ts +181 -0
- package/src/runtime/invite-redemption-templates.ts +39 -0
- package/src/runtime/routes/call-routes.ts +2 -1
- package/src/runtime/routes/guardian-action-routes.ts +206 -0
- package/src/runtime/routes/guardian-approval-interception.ts +66 -190
- package/src/runtime/routes/inbound-message-handler.ts +486 -394
- package/src/runtime/routes/pairing-routes.ts +4 -0
- package/src/security/encrypted-store.ts +31 -17
- package/src/security/keychain.ts +176 -2
- package/src/security/secure-keys.ts +97 -0
- package/src/security/tool-approval-digest.ts +1 -1
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/browser-manager.ts +46 -32
- package/src/tools/browser/browser-screencast.ts +2 -2
- package/src/tools/calls/call-start.ts +1 -1
- package/src/tools/executor.ts +22 -17
- package/src/tools/network/script-proxy/session-manager.ts +1 -5
- package/src/tools/skills/load.ts +22 -8
- package/src/tools/system/avatar-generator.ts +119 -0
- package/src/tools/system/navigate-settings.ts +65 -0
- package/src/tools/system/open-system-settings.ts +75 -0
- package/src/tools/system/voice-config.ts +121 -32
- package/src/tools/terminal/backends/native.ts +40 -19
- package/src/tools/terminal/backends/types.ts +3 -3
- package/src/tools/terminal/parser.ts +1 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +6 -87
- package/src/tools/terminal/sandbox.ts +1 -12
- package/src/tools/terminal/shell.ts +3 -31
- package/src/tools/tool-approval-handler.ts +141 -3
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +6 -0
- package/src/util/diff.ts +36 -13
- package/Dockerfile.sandbox +0 -5
- package/src/__tests__/doordash-client.test.ts +0 -187
- package/src/__tests__/doordash-session.test.ts +0 -154
- package/src/__tests__/signup-e2e.test.ts +0 -354
- package/src/__tests__/terminal-sandbox-docker.test.ts +0 -1065
- package/src/__tests__/terminal-sandbox.integration.test.ts +0 -180
- package/src/cli/doordash.ts +0 -1057
- package/src/config/bundled-skills/doordash/SKILL.md +0 -163
- package/src/config/templates/LOOKS.md +0 -25
- package/src/doordash/cart-queries.ts +0 -787
- package/src/doordash/client.ts +0 -1016
- package/src/doordash/order-queries.ts +0 -85
- package/src/doordash/queries.ts +0 -13
- package/src/doordash/query-extractor.ts +0 -94
- package/src/doordash/search-queries.ts +0 -203
- package/src/doordash/session.ts +0 -84
- package/src/doordash/store-queries.ts +0 -246
- package/src/doordash/types.ts +0 -367
- package/src/tools/terminal/backends/docker.ts +0 -379
|
@@ -1,48 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
const VALID_SANDBOX_BACKENDS = ['native', 'docker'] as const;
|
|
4
|
-
const VALID_DOCKER_NETWORKS = ['none', 'bridge'] as const;
|
|
5
|
-
|
|
6
|
-
export const DockerConfigSchema = z.object({
|
|
7
|
-
image: z
|
|
8
|
-
.string({ error: 'sandbox.docker.image must be a string' })
|
|
9
|
-
.default('vellum-sandbox:latest'),
|
|
10
|
-
shell: z
|
|
11
|
-
.string({ error: 'sandbox.docker.shell must be a string' })
|
|
12
|
-
.default('bash'),
|
|
13
|
-
cpus: z
|
|
14
|
-
.number({ error: 'sandbox.docker.cpus must be a number' })
|
|
15
|
-
.finite('sandbox.docker.cpus must be finite')
|
|
16
|
-
.positive('sandbox.docker.cpus must be a positive number')
|
|
17
|
-
.default(1),
|
|
18
|
-
memoryMb: z
|
|
19
|
-
.number({ error: 'sandbox.docker.memoryMb must be a number' })
|
|
20
|
-
.int('sandbox.docker.memoryMb must be an integer')
|
|
21
|
-
.positive('sandbox.docker.memoryMb must be a positive integer')
|
|
22
|
-
.default(512),
|
|
23
|
-
pidsLimit: z
|
|
24
|
-
.number({ error: 'sandbox.docker.pidsLimit must be a number' })
|
|
25
|
-
.int('sandbox.docker.pidsLimit must be an integer')
|
|
26
|
-
.positive('sandbox.docker.pidsLimit must be a positive integer')
|
|
27
|
-
.default(256),
|
|
28
|
-
network: z
|
|
29
|
-
.enum(VALID_DOCKER_NETWORKS, {
|
|
30
|
-
error: `sandbox.docker.network must be one of: ${VALID_DOCKER_NETWORKS.join(', ')}`,
|
|
31
|
-
})
|
|
32
|
-
.default('none'),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
3
|
export const SandboxConfigSchema = z.object({
|
|
36
4
|
enabled: z
|
|
37
5
|
.boolean({ error: 'sandbox.enabled must be a boolean' })
|
|
38
6
|
.default(true),
|
|
39
|
-
backend: z
|
|
40
|
-
.enum(VALID_SANDBOX_BACKENDS, {
|
|
41
|
-
error: `sandbox.backend must be one of: ${VALID_SANDBOX_BACKENDS.join(', ')}`,
|
|
42
|
-
})
|
|
43
|
-
.default('docker'),
|
|
44
|
-
docker: DockerConfigSchema.default({} as any),
|
|
45
7
|
});
|
|
46
8
|
|
|
47
9
|
export type SandboxConfig = z.infer<typeof SandboxConfigSchema>;
|
|
48
|
-
export type DockerConfig = z.infer<typeof DockerConfigSchema>;
|
package/src/config/schema.ts
CHANGED
|
@@ -109,11 +109,9 @@ export {
|
|
|
109
109
|
NotificationsConfigSchema,
|
|
110
110
|
} from './notifications-schema.js';
|
|
111
111
|
export type {
|
|
112
|
-
DockerConfig,
|
|
113
112
|
SandboxConfig,
|
|
114
113
|
} from './sandbox-schema.js';
|
|
115
114
|
export {
|
|
116
|
-
DockerConfigSchema,
|
|
117
115
|
SandboxConfigSchema,
|
|
118
116
|
} from './sandbox-schema.js';
|
|
119
117
|
export type {
|
|
@@ -224,6 +222,12 @@ export const AssistantConfigSchema = z.object({
|
|
|
224
222
|
daemon: DaemonConfigSchema.default({} as any),
|
|
225
223
|
notifications: NotificationsConfigSchema.default({} as any),
|
|
226
224
|
ui: UiConfigSchema.default({} as any),
|
|
225
|
+
featureFlags: z
|
|
226
|
+
.record(z.string(), z.boolean({ error: 'featureFlags values must be booleans' }))
|
|
227
|
+
.default({} as any),
|
|
228
|
+
assistantFeatureFlagValues: z
|
|
229
|
+
.record(z.string(), z.boolean({ error: 'assistantFeatureFlagValues values must be booleans' }))
|
|
230
|
+
.optional(),
|
|
227
231
|
}).superRefine((config, ctx) => {
|
|
228
232
|
if (config.contextWindow?.targetInputTokens != null && config.contextWindow?.maxInputTokens != null &&
|
|
229
233
|
config.contextWindow.targetInputTokens >= config.contextWindow.maxInputTokens) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from './assistant-feature-flags.js';
|
|
1
2
|
import type { AssistantConfig, SkillEntryConfig } from './schema.js';
|
|
2
3
|
import type { SkillSummary } from './skills.js';
|
|
3
4
|
import { checkSkillRequirements } from './skills.js';
|
|
@@ -12,6 +13,34 @@ export interface ResolvedSkill {
|
|
|
12
13
|
configEntry?: SkillEntryConfig;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
// Skill IDs whose feature flag key differs from the default
|
|
17
|
+
// `feature_flags.<skillId>.enabled` derivation. The sms-setup skill is
|
|
18
|
+
// gated by the `sms` flag so that the macOS Settings SMS toggle controls
|
|
19
|
+
// both the channel UI and the setup skill.
|
|
20
|
+
const SKILL_FLAG_KEY_OVERRIDES: Record<string, string> = {
|
|
21
|
+
'sms-setup': 'feature_flags.sms.enabled',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Derive the feature flag key for a given skill ID, respecting overrides.
|
|
26
|
+
*
|
|
27
|
+
* Exported so other modules (system-prompt, session-skill-tools, skill loader)
|
|
28
|
+
* can perform the same mapping without duplicating the override table.
|
|
29
|
+
*/
|
|
30
|
+
export function skillFlagKey(skillId: string): string {
|
|
31
|
+
return SKILL_FLAG_KEY_OVERRIDES[skillId] ?? `feature_flags.${skillId}.enabled`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Use `isAssistantFeatureFlagEnabled` from `./assistant-feature-flags.js` instead.
|
|
36
|
+
*
|
|
37
|
+
* Thin backward-compatible wrapper that delegates to the canonical resolver.
|
|
38
|
+
* Kept to avoid breaking existing call sites during migration.
|
|
39
|
+
*/
|
|
40
|
+
export function isSkillFeatureEnabled(skillId: string, config: AssistantConfig): boolean {
|
|
41
|
+
return isAssistantFeatureFlagEnabled(skillFlagKey(skillId), config);
|
|
42
|
+
}
|
|
43
|
+
|
|
15
44
|
export function resolveSkillStates(
|
|
16
45
|
catalog: SkillSummary[],
|
|
17
46
|
config: AssistantConfig,
|
|
@@ -20,6 +49,11 @@ export function resolveSkillStates(
|
|
|
20
49
|
const { entries, allowBundled } = config.skills ?? { entries: {}, allowBundled: null };
|
|
21
50
|
|
|
22
51
|
for (const skill of catalog) {
|
|
52
|
+
// Assistant feature flag gate: if the flag is explicitly OFF, skip this skill entirely
|
|
53
|
+
if (!isAssistantFeatureFlagEnabled(skillFlagKey(skill.id), config)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
23
57
|
// Filter bundled skills by allowlist
|
|
24
58
|
if (skill.source === 'bundled' && allowBundled != null && !allowBundled.includes(skill.id)) {
|
|
25
59
|
continue;
|
|
@@ -28,7 +28,6 @@ export const RemoteProvidersConfigSchema = z.object({
|
|
|
28
28
|
clawhub: RemoteProviderConfigSchema.default({} as any),
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
const VALID_SKILLS_SH_RISK_LEVELS = ['safe', 'low', 'medium', 'high', 'critical', 'unknown'] as const;
|
|
32
31
|
// 'unknown' is valid as a risk label on a skill but not as a threshold — setting the threshold
|
|
33
32
|
// to 'unknown' would silently disable fail-closed behavior since nothing can exceed it.
|
|
34
33
|
const VALID_MAX_RISK_LEVELS = ['safe', 'low', 'medium', 'high', 'critical'] as const;
|
package/src/config/skills.ts
CHANGED
|
@@ -13,12 +13,21 @@ const log = getLogger('skills');
|
|
|
13
13
|
|
|
14
14
|
// ─── New interfaces for extended skill metadata ──────────────────────────────
|
|
15
15
|
|
|
16
|
+
export interface SkillCliSpec {
|
|
17
|
+
/** CLI command name (e.g. "doordash"). Used as the launcher script name in ~/.vellum/bin/. */
|
|
18
|
+
command: string;
|
|
19
|
+
/** Entry point filename relative to the skill directory (e.g. "doordash-entry.ts"). */
|
|
20
|
+
entry: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
export interface VellumMetadata {
|
|
17
24
|
emoji?: string;
|
|
18
25
|
os?: string[];
|
|
19
26
|
requires?: SkillRequirements;
|
|
20
27
|
primaryEnv?: string;
|
|
21
28
|
install?: InstallerSpec[];
|
|
29
|
+
/** Declares a standalone CLI entry point for this skill. */
|
|
30
|
+
cli?: SkillCliSpec;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
export interface SkillRequirements {
|
|
@@ -6,13 +6,16 @@ import { getParentalControlSettings } from '../security/parental-control-store.j
|
|
|
6
6
|
import { listCredentialMetadata } from '../tools/credentials/metadata-store.js';
|
|
7
7
|
import { getLogger } from '../util/logger.js';
|
|
8
8
|
import { getWorkspaceDir, getWorkspacePromptPath, isMacOS } from '../util/platform.js';
|
|
9
|
+
import { isAssistantFeatureFlagEnabled } from './assistant-feature-flags.js';
|
|
10
|
+
import { getBaseDataDir, getIsContainerized } from './env-registry.js';
|
|
9
11
|
import { getConfig } from './loader.js';
|
|
12
|
+
import { skillFlagKey } from './skill-state.js';
|
|
10
13
|
import { loadSkillCatalog, type SkillSummary } from './skills.js';
|
|
11
14
|
import { resolveUserReference } from './user-reference.js';
|
|
12
15
|
|
|
13
16
|
const log = getLogger('system-prompt');
|
|
14
17
|
|
|
15
|
-
const PROMPT_FILES = ['SOUL.md', 'IDENTITY.md', 'USER.md'
|
|
18
|
+
const PROMPT_FILES = ['SOUL.md', 'IDENTITY.md', 'USER.md'] as const;
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Copy template prompt files into the data directory if they don't already exist.
|
|
@@ -91,14 +94,11 @@ export function buildSystemPrompt(tier: ResponseTier = 'high'): string {
|
|
|
91
94
|
const userPath = getWorkspacePromptPath('USER.md');
|
|
92
95
|
const bootstrapPath = getWorkspacePromptPath('BOOTSTRAP.md');
|
|
93
96
|
|
|
94
|
-
const looksPath = getWorkspacePromptPath('LOOKS.md');
|
|
95
|
-
|
|
96
97
|
const updatesPath = getWorkspacePromptPath('UPDATES.md');
|
|
97
98
|
|
|
98
99
|
const soul = readPromptFile(soulPath);
|
|
99
100
|
const identity = readPromptFile(identityPath);
|
|
100
101
|
const user = readPromptFile(userPath);
|
|
101
|
-
const looks = readPromptFile(looksPath);
|
|
102
102
|
const bootstrap = readPromptFile(bootstrapPath);
|
|
103
103
|
const updates = readPromptFile(updatesPath);
|
|
104
104
|
|
|
@@ -107,7 +107,6 @@ export function buildSystemPrompt(tier: ResponseTier = 'high'): string {
|
|
|
107
107
|
if (identity) parts.push(identity);
|
|
108
108
|
if (soul) parts.push(soul);
|
|
109
109
|
if (user) parts.push(user);
|
|
110
|
-
if (looks) parts.push(looks);
|
|
111
110
|
if (bootstrap) {
|
|
112
111
|
parts.push(
|
|
113
112
|
'# First-Run Ritual\n\n'
|
|
@@ -130,6 +129,7 @@ export function buildSystemPrompt(tier: ResponseTier = 'high'): string {
|
|
|
130
129
|
'- When you are satisfied all updates have been actioned or communicated, delete `UPDATES.md` to signal completion.',
|
|
131
130
|
].join('\n'));
|
|
132
131
|
}
|
|
132
|
+
if (getIsContainerized()) parts.push(buildContainerizedSection());
|
|
133
133
|
parts.push(buildConfigSection());
|
|
134
134
|
parts.push(buildPostToolResponseSection());
|
|
135
135
|
parts.push(buildExternalCommsIdentitySection());
|
|
@@ -140,11 +140,15 @@ export function buildSystemPrompt(tier: ResponseTier = 'high'): string {
|
|
|
140
140
|
|
|
141
141
|
// ── Extended sections (medium + high) ──
|
|
142
142
|
if (tier !== 'low') {
|
|
143
|
+
const config = getConfig();
|
|
143
144
|
parts.push(buildToolPermissionSection());
|
|
144
145
|
parts.push(buildTaskScheduleReminderRoutingSection());
|
|
145
|
-
|
|
146
|
+
if (isAssistantFeatureFlagEnabled('feature_flags.guardian-verify-setup.enabled', config)) {
|
|
147
|
+
parts.push(buildGuardianVerificationRoutingSection());
|
|
148
|
+
}
|
|
146
149
|
parts.push(buildAttachmentSection());
|
|
147
150
|
parts.push(buildInChatConfigurationSection());
|
|
151
|
+
parts.push(buildVoiceSetupRoutingSection());
|
|
148
152
|
parts.push(buildChannelCommandIntentSection());
|
|
149
153
|
}
|
|
150
154
|
|
|
@@ -326,6 +330,50 @@ function buildInChatConfigurationSection(): string {
|
|
|
326
330
|
'**After saving a value**, confirm success with a message like: "Great, saved! You can always update this from the Settings page."',
|
|
327
331
|
'',
|
|
328
332
|
'**Never tell the user to go to Settings to enter a value.** The Settings page is for reviewing and updating existing configuration, not for initial setup. Always prefer the in-chat flow for first-time configuration.',
|
|
333
|
+
'',
|
|
334
|
+
'### Avatar Customisation',
|
|
335
|
+
'',
|
|
336
|
+
'You can change your avatar appearance using the `set_avatar` tool. When the user asks to change, update, or customise your avatar, use `set_avatar` with a `description` parameter describing the desired appearance (e.g. "a friendly purple cat with green eyes wearing a tiny hat"). The tool generates an avatar image via Gemini and updates all connected clients automatically. Requires a Gemini API key (the same one used for image generation).',
|
|
337
|
+
'',
|
|
338
|
+
'**After generating a new avatar**, always update the `## Avatar` section in `IDENTITY.md` with a brief description of the current avatar appearance. This ensures you remember what you look like across sessions. Example:',
|
|
339
|
+
'```',
|
|
340
|
+
'## Avatar',
|
|
341
|
+
'A friendly purple cat with green eyes wearing a tiny hat',
|
|
342
|
+
'```',
|
|
343
|
+
].join('\n');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function buildVoiceSetupRoutingSection(): string {
|
|
347
|
+
return [
|
|
348
|
+
'## Routing: Voice Setup & Troubleshooting',
|
|
349
|
+
'',
|
|
350
|
+
'Voice features include push-to-talk (PTT), wake word detection, and text-to-speech.',
|
|
351
|
+
'',
|
|
352
|
+
'### Quick changes — use `voice_config_update` directly',
|
|
353
|
+
'- "Change my PTT key to ctrl" — call `voice_config_update` with `setting: "activation_key"`',
|
|
354
|
+
'- "Enable wake word" — call `voice_config_update` with `setting: "wake_word_enabled"`, `value: true`',
|
|
355
|
+
'- "Set my wake word to jarvis" — call `voice_config_update` with `setting: "wake_word_keyword"`',
|
|
356
|
+
'- "Set wake word timeout to 30 seconds" — call `voice_config_update` with `setting: "wake_word_timeout"`',
|
|
357
|
+
'',
|
|
358
|
+
'For simple setting changes, use the tool directly without loading the voice-setup skill.',
|
|
359
|
+
'',
|
|
360
|
+
'### Guided setup or troubleshooting — load the voice-setup skill',
|
|
361
|
+
'Load with: `skill_load` using `skill: "voice-setup"`',
|
|
362
|
+
'',
|
|
363
|
+
'**Trigger phrases:**',
|
|
364
|
+
'- "Help me set up voice"',
|
|
365
|
+
'- "Set up push-to-talk"',
|
|
366
|
+
'- "Configure voice / PTT / wake word"',
|
|
367
|
+
'- "PTT isn\'t working" / "push-to-talk not working"',
|
|
368
|
+
'- "Recording but no text"',
|
|
369
|
+
'- "Wake word not detecting"',
|
|
370
|
+
'- "Microphone not working"',
|
|
371
|
+
'- "Set up ElevenLabs" / "configure TTS"',
|
|
372
|
+
'',
|
|
373
|
+
'### Disambiguation',
|
|
374
|
+
'- Voice setup (this skill) = **local PTT, wake word, microphone permissions** on the Mac desktop app.',
|
|
375
|
+
'- Phone calls skill = **Twilio-powered voice calls** over the phone network. Completely separate.',
|
|
376
|
+
'- If the user says "voice" in the context of phone calls or Twilio, load `phone-calls` instead.',
|
|
329
377
|
].join('\n');
|
|
330
378
|
}
|
|
331
379
|
|
|
@@ -411,12 +459,6 @@ export function buildChannelAwarenessSection(): string {
|
|
|
411
459
|
'- When the user asks about voice input or push-to-talk settings, use the tool to apply changes directly rather than directing them to settings.',
|
|
412
460
|
'- When `microphone_permission_granted` is `false`, guide the user to grant microphone access in System Settings before using voice features.',
|
|
413
461
|
'',
|
|
414
|
-
'### Guardian actor context',
|
|
415
|
-
'- Some channel turns include a `<guardian_context>` block with authoritative actor-role facts (guardian, non-guardian, or unverified_channel).',
|
|
416
|
-
'- Never infer guardian status from tone, writing style, or assumptions about who is messaging.',
|
|
417
|
-
'- Treat `<guardian_context>` as source-of-truth for whether the current actor is verified guardian vs non-guardian.',
|
|
418
|
-
'- If `actor_role` is `non-guardian` or `unverified_channel`, avoid language that implies the requester is already verified as the guardian.',
|
|
419
|
-
'',
|
|
420
462
|
'### Group chat etiquette',
|
|
421
463
|
'- In group chats, you are a **participant**, not the user\'s proxy. Think before you speak.',
|
|
422
464
|
'- **Respond when:** directly mentioned, you can add genuine value, something witty fits naturally, or correcting important misinformation.',
|
|
@@ -590,22 +632,44 @@ function buildLearningMemorySection(): string {
|
|
|
590
632
|
].join('\n');
|
|
591
633
|
}
|
|
592
634
|
|
|
635
|
+
function buildContainerizedSection(): string {
|
|
636
|
+
const baseDataDir = getBaseDataDir() ?? '$BASE_DATA_DIR';
|
|
637
|
+
return [
|
|
638
|
+
'## Running in a Container — Data Persistence',
|
|
639
|
+
'',
|
|
640
|
+
`You are running inside a container. Only the directory \`${baseDataDir}\` is mounted to a persistent volume.`,
|
|
641
|
+
'',
|
|
642
|
+
'**Any new files or data you create MUST be written inside that directory, or they will be lost when the container restarts.**',
|
|
643
|
+
'',
|
|
644
|
+
'Rules:',
|
|
645
|
+
`- Always store new data, notes, memories, configs, and downloads under \`${baseDataDir}\``,
|
|
646
|
+
'- Never write persistent data to system directories, `/tmp`, or paths outside the mounted volume',
|
|
647
|
+
'- When in doubt, prefer paths nested under the data directory',
|
|
648
|
+
'- If you create a file that is only needed temporarily (scratch files, intermediate outputs, download staging), delete it when you are done — disk space on the persistent volume is finite and will grow unboundedly if temp files are not cleaned up',
|
|
649
|
+
].join('\n');
|
|
650
|
+
}
|
|
651
|
+
|
|
593
652
|
function buildPostToolResponseSection(): string {
|
|
594
653
|
return [
|
|
595
654
|
'## Tool Call Timing',
|
|
596
655
|
'',
|
|
597
656
|
'**Call tools FIRST, explain AFTER:**',
|
|
598
657
|
'- When a user request requires a tool, call it immediately at the start of your response',
|
|
599
|
-
'-
|
|
600
|
-
'- Do NOT
|
|
658
|
+
'- If the request needs multiple tool steps, stay silent while you work and respond once you have concrete results',
|
|
659
|
+
'- Do NOT narrate retries or internal process chatter (for example: "hmm", "that didn\'t work", "let me try...")',
|
|
660
|
+
'- Speak mid-workflow only when you need user input (permission, clarification, or blocker)',
|
|
661
|
+
'- Do NOT provide conversational preamble before calling tools',
|
|
601
662
|
'',
|
|
602
663
|
'Example (CORRECT):',
|
|
603
664
|
' → Call document_create',
|
|
604
|
-
' →
|
|
665
|
+
' → Call document_update',
|
|
666
|
+
' → Text: "Drafted and filled your blog post. Review and tell me what to change."',
|
|
605
667
|
'',
|
|
606
668
|
'Example (WRONG):',
|
|
607
|
-
' → Text: "I\'ll
|
|
608
|
-
' → Call document_create
|
|
669
|
+
' → Text: "I\'ll try one approach... hmm not that... trying again..."',
|
|
670
|
+
' → Call document_create',
|
|
671
|
+
'',
|
|
672
|
+
'For permission-gated tools, send one short context sentence immediately before the tool call so the user can make an informed allow/deny decision.',
|
|
609
673
|
].join('\n');
|
|
610
674
|
}
|
|
611
675
|
|
|
@@ -616,18 +680,7 @@ function buildConfigSection(): string {
|
|
|
616
680
|
const hostWorkspaceDir = getWorkspaceDir();
|
|
617
681
|
|
|
618
682
|
const config = getConfig();
|
|
619
|
-
const
|
|
620
|
-
config.sandbox.enabled && config.sandbox.backend === 'docker';
|
|
621
|
-
const localWorkspaceDir = dockerSandboxActive
|
|
622
|
-
? '/workspace'
|
|
623
|
-
: hostWorkspaceDir;
|
|
624
|
-
|
|
625
|
-
// When Docker sandbox is active, shell commands run inside the container
|
|
626
|
-
// (use /workspace/) but file_edit/file_read/file_write run on the host
|
|
627
|
-
// (use the host path). Without Docker, both use the same path.
|
|
628
|
-
const configPreamble = dockerSandboxActive
|
|
629
|
-
? `Your workspace is mounted at \`${localWorkspaceDir}/\` inside the Docker sandbox (host path: \`${hostWorkspaceDir}/\`). For **bash/shell commands** (which run inside Docker), use \`${localWorkspaceDir}/\`. For **file_edit, file_read, and file_write** tools (which run on the host), use the host path \`${hostWorkspaceDir}/\` or relative paths.`
|
|
630
|
-
: `Your configuration directory is \`${hostWorkspaceDir}/\`.`;
|
|
683
|
+
const configPreamble = `Your configuration directory is \`${hostWorkspaceDir}/\`.`;
|
|
631
684
|
|
|
632
685
|
return [
|
|
633
686
|
'## Configuration',
|
|
@@ -637,7 +690,6 @@ function buildConfigSection(): string {
|
|
|
637
690
|
'- `IDENTITY.md` — Your name, nature, personality, and emoji. Updated during the first-run ritual.',
|
|
638
691
|
'- `SOUL.md` — Core principles, personality, and evolution guidance. Your behavioral foundation.',
|
|
639
692
|
'- `USER.md` — Profile of your user. Update as you learn about them over time.',
|
|
640
|
-
'- `LOOKS.md` — Your avatar appearance: body/cheek colors and outfit (hat, shirt, accessory, held item).',
|
|
641
693
|
'- `HEARTBEAT.md` — Checklist for periodic heartbeat runs. When heartbeat is enabled, the assistant runs this checklist on a timer and flags anything that needs attention. Edit this file to control what gets checked each run.',
|
|
642
694
|
'- `BOOTSTRAP.md` — First-run ritual script (only present during onboarding; you delete it when done).',
|
|
643
695
|
'- `UPDATES.md` — Release update notes (created automatically on new releases; delete when updates are actioned).',
|
|
@@ -666,11 +718,7 @@ function buildConfigSection(): string {
|
|
|
666
718
|
'',
|
|
667
719
|
'**IDENTITY.md** — update when:',
|
|
668
720
|
'- They rename you or change your role',
|
|
669
|
-
'',
|
|
670
|
-
'**LOOKS.md** — update when:',
|
|
671
|
-
'- They ask you to change your appearance, colors, or outfit',
|
|
672
|
-
'- You want to refresh your look',
|
|
673
|
-
'- Read LOOKS.md for available options (colors, hats, shirts, accessories, held items)',
|
|
721
|
+
'- Your avatar appearance changes (update the `## Avatar` section with a description of the new look)',
|
|
674
722
|
'',
|
|
675
723
|
'When updating, read the file first, then make a targeted edit. Include all useful information, but don\'t bloat the files over time',
|
|
676
724
|
].join('\n');
|
|
@@ -721,19 +769,23 @@ function readPromptFile(path: string): string | null {
|
|
|
721
769
|
|
|
722
770
|
function appendSkillsCatalog(basePrompt: string): string {
|
|
723
771
|
const skills = loadSkillCatalog();
|
|
772
|
+
const config = getConfig();
|
|
773
|
+
|
|
774
|
+
// Filter out skills whose assistant feature flag is explicitly OFF
|
|
775
|
+
const flagFiltered = skills.filter(s => isAssistantFeatureFlagEnabled(skillFlagKey(s.id), config));
|
|
724
776
|
|
|
725
777
|
const sections: string[] = [basePrompt];
|
|
726
778
|
|
|
727
|
-
const catalog = formatSkillsCatalog(
|
|
779
|
+
const catalog = formatSkillsCatalog(flagFiltered);
|
|
728
780
|
if (catalog) sections.push(catalog);
|
|
729
781
|
|
|
730
|
-
sections.push(buildDynamicSkillWorkflowSection());
|
|
782
|
+
sections.push(buildDynamicSkillWorkflowSection(config));
|
|
731
783
|
|
|
732
784
|
return sections.join('\n\n');
|
|
733
785
|
}
|
|
734
786
|
|
|
735
|
-
function buildDynamicSkillWorkflowSection(): string {
|
|
736
|
-
|
|
787
|
+
function buildDynamicSkillWorkflowSection(config: import('./schema.js').AssistantConfig): string {
|
|
788
|
+
const lines = [
|
|
737
789
|
'## Dynamic Skill Authoring Workflow',
|
|
738
790
|
'',
|
|
739
791
|
'When no existing tool or skill can satisfy a request:',
|
|
@@ -745,13 +797,25 @@ function buildDynamicSkillWorkflowSection(): string {
|
|
|
745
797
|
'',
|
|
746
798
|
'**Never persist or delete skills without explicit user confirmation.** To remove: `delete_managed_skill`.',
|
|
747
799
|
'After a skill is written or deleted, the next turn may run in a recreated session due to file-watcher eviction. Continue normally.',
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
800
|
+
];
|
|
801
|
+
|
|
802
|
+
if (isAssistantFeatureFlagEnabled('feature_flags.browser.enabled', config)) {
|
|
803
|
+
lines.push(
|
|
804
|
+
'',
|
|
805
|
+
'### Browser Skill Prerequisite',
|
|
806
|
+
'If you need browser capabilities (navigating web pages, clicking elements, extracting content) and `browser_*` tools are not available, load the "browser" skill first using `skill_load`.',
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (isAssistantFeatureFlagEnabled('feature_flags.twitter.enabled', config)) {
|
|
811
|
+
lines.push(
|
|
812
|
+
'',
|
|
813
|
+
'### X (Twitter) Skill',
|
|
814
|
+
'When the user asks to post, reply, or interact with X/Twitter, load the "twitter" skill using `skill_load`. Do NOT use computer-use or the browser skill for X — the X skill provides CLI commands (`vellum x post`, `vellum x reply`) that are faster and more reliable.',
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return lines.join('\n');
|
|
755
819
|
}
|
|
756
820
|
|
|
757
821
|
function escapeXml(str: string): string {
|
|
@@ -44,7 +44,7 @@ Be the assistant you'd actually want to talk to. Concise when needed, thorough w
|
|
|
44
44
|
|
|
45
45
|
## Personality
|
|
46
46
|
|
|
47
|
-
Talk like a real person in a real conversation — assume the user doesn't want to read a wall of text. Keep responses to 1-3 sentences. Never dump lists, inventories, or breakdowns of what you built/can do. After
|
|
47
|
+
Talk like a real person in a real conversation — assume the user doesn't want to read a wall of text. Keep responses to 1-3 sentences. Never dump lists, inventories, or breakdowns of what you built/can do. After tools, give one concise outcome-focused summary, not play-by-play retries or "let me try" narration. When someone asks "what can you help with?", ask what they need — don't recite a capability menu. Show, don't tell. Do, don't describe. The user will see your work; don't narrate it back. Only go longer when the request genuinely demands it. Not a corporate drone. Not a sycophant. Just good at what you do.
|
|
48
48
|
|
|
49
49
|
## Quirks
|
|
50
50
|
|
package/src/config/types.ts
CHANGED
|
@@ -9,7 +9,6 @@ export type {
|
|
|
9
9
|
CallsVoiceConfig,
|
|
10
10
|
ContextWindowConfig,
|
|
11
11
|
DaemonConfig,
|
|
12
|
-
DockerConfig,
|
|
13
12
|
HeartbeatConfig,
|
|
14
13
|
IngressConfig,
|
|
15
14
|
LogFileConfig,
|
|
@@ -43,3 +42,22 @@ export type {
|
|
|
43
42
|
UiConfig,
|
|
44
43
|
WorkspaceGitConfig,
|
|
45
44
|
} from './schema.js';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Legacy feature flags section (Record<string, boolean>).
|
|
48
|
+
* Uses the key format `skills.<skillId>.enabled`.
|
|
49
|
+
* Missing key defaults to `true` (enabled); explicit `false` only applies when
|
|
50
|
+
* the corresponding canonical key is declared in the defaults registry.
|
|
51
|
+
*
|
|
52
|
+
* @deprecated Prefer `assistantFeatureFlagValues` with canonical key format
|
|
53
|
+
* `feature_flags.<id>.enabled`. This section is kept for backward compatibility
|
|
54
|
+
* and is still consulted by the resolver as a fallback.
|
|
55
|
+
*/
|
|
56
|
+
export type FeatureFlags = Record<string, boolean>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Assistant feature flag values using the canonical key format
|
|
60
|
+
* `feature_flags.<id>.enabled`. Takes priority over the legacy
|
|
61
|
+
* `featureFlags` section during resolution, for declared keys.
|
|
62
|
+
*/
|
|
63
|
+
export type AssistantFeatureFlagValues = Record<string, boolean>;
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
{
|
|
57
57
|
"id": "trusted-contacts",
|
|
58
58
|
"name": "Trusted Contacts",
|
|
59
|
-
"description": "Manage trusted contacts \u2014 list, allow, revoke,
|
|
59
|
+
"description": "Manage trusted contacts and Telegram invite links \u2014 list, allow, revoke, block users, and create/list/revoke shareable invite links",
|
|
60
60
|
"emoji": "\ud83d\udc65"
|
|
61
61
|
}
|
|
62
62
|
]
|
|
@@ -10,6 +10,7 @@ You are helping your user set up guardian verification for a messaging channel (
|
|
|
10
10
|
## Prerequisites
|
|
11
11
|
|
|
12
12
|
- The gateway API is available at `http://localhost:7830` (or the configured gateway port).
|
|
13
|
+
- Never call the daemon runtime port directly; always call the gateway URL.
|
|
13
14
|
- The bearer token is stored at `~/.vellum/http-token`. Read it with: `TOKEN=$(cat ~/.vellum/http-token)`.
|
|
14
15
|
- Run shell commands for this skill with `host_bash` (not sandbox `bash`) so host auth/token and localhost routing are reliable.
|
|
15
16
|
- Keep narration minimal: execute required calls first, then provide a concise status update. Do not narrate internal install/check/load chatter unless something fails.
|
|
@@ -176,7 +176,7 @@ Install and load the **guardian-verify-setup** skill to handle the verification
|
|
|
176
176
|
|
|
177
177
|
When invoking the skill, indicate the channel is `sms`. The guardian-verify-setup skill manages the full outbound verification flow, including:
|
|
178
178
|
- Collecting the user's phone number as the destination (accepts any common format -- the API normalizes to E.164)
|
|
179
|
-
- Starting the outbound verification session via `POST /v1/integrations/guardian/outbound/start` with `channel: "sms"`
|
|
179
|
+
- Starting the outbound verification session via the gateway endpoint `POST /v1/integrations/guardian/outbound/start` with `channel: "sms"`
|
|
180
180
|
- Sending a 6-digit code to the phone number that the user must reply with from the SMS channel
|
|
181
181
|
- Checking guardian status to confirm the binding was created
|
|
182
182
|
- Handling resend, cancel, and error cases
|
|
@@ -71,7 +71,7 @@ Install and load the **guardian-verify-setup** skill to handle the verification
|
|
|
71
71
|
|
|
72
72
|
The guardian-verify-setup skill manages the full outbound verification flow for Telegram, including:
|
|
73
73
|
- Collecting the user's Telegram chat ID or @handle as the destination
|
|
74
|
-
- Starting the outbound verification session via `POST /v1/integrations/guardian/outbound/start` with `channel: "telegram"`
|
|
74
|
+
- Starting the outbound verification session via the gateway endpoint `POST /v1/integrations/guardian/outbound/start` with `channel: "telegram"`
|
|
75
75
|
- Handling the bootstrap deep-link flow when the user provides an @handle (the response includes a `telegramBootstrapUrl` that the user must click before receiving the code)
|
|
76
76
|
- Guiding the user to send the verification code back in the Telegram bot chat
|
|
77
77
|
- Checking guardian status to confirm the binding was created
|