@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
|
@@ -131,7 +131,7 @@ export const WorkspaceGitConfigSchema = z.object({
|
|
|
131
131
|
commitMessageLLM: z.object({
|
|
132
132
|
enabled: z.boolean({ error: 'workspaceGit.commitMessageLLM.enabled must be a boolean' }).default(false),
|
|
133
133
|
useConfiguredProvider: z.boolean({ error: 'workspaceGit.commitMessageLLM.useConfiguredProvider must be a boolean' }).default(true),
|
|
134
|
-
providerFastModelOverrides: z.record(z.string(), z.string()).default({} as
|
|
134
|
+
providerFastModelOverrides: z.record(z.string(), z.string()).default({} as Record<string, string>),
|
|
135
135
|
timeoutMs: z.number({ error: 'workspaceGit.commitMessageLLM.timeoutMs must be a number' })
|
|
136
136
|
.int('workspaceGit.commitMessageLLM.timeoutMs must be an integer')
|
|
137
137
|
.positive('workspaceGit.commitMessageLLM.timeoutMs must be a positive integer')
|
|
@@ -163,8 +163,19 @@ export const WorkspaceGitConfigSchema = z.object({
|
|
|
163
163
|
.int().positive().default(2000),
|
|
164
164
|
backoffMaxMs: z.number({ error: 'workspaceGit.commitMessageLLM.breaker.backoffMaxMs must be a number' })
|
|
165
165
|
.int().positive().default(60000),
|
|
166
|
-
}).default({
|
|
167
|
-
}).default({
|
|
166
|
+
}).default({ openAfterFailures: 3, backoffBaseMs: 2000, backoffMaxMs: 60000 }),
|
|
167
|
+
}).default({
|
|
168
|
+
enabled: false,
|
|
169
|
+
useConfiguredProvider: true,
|
|
170
|
+
providerFastModelOverrides: {},
|
|
171
|
+
timeoutMs: 600,
|
|
172
|
+
maxTokens: 120,
|
|
173
|
+
temperature: 0.2,
|
|
174
|
+
maxFilesInPrompt: 30,
|
|
175
|
+
maxDiffBytes: 12000,
|
|
176
|
+
minRemainingTurnBudgetMs: 1000,
|
|
177
|
+
breaker: { openAfterFailures: 3, backoffBaseMs: 2000, backoffMaxMs: 60000 },
|
|
178
|
+
}),
|
|
168
179
|
});
|
|
169
180
|
|
|
170
181
|
export type HeartbeatConfig = z.infer<typeof HeartbeatConfigSchema>;
|
|
@@ -76,7 +76,7 @@ export const CallsVoiceConfigSchema = z.object({
|
|
|
76
76
|
fallbackToStandardOnError: z
|
|
77
77
|
.boolean({ error: 'calls.voice.fallbackToStandardOnError must be a boolean' })
|
|
78
78
|
.default(true),
|
|
79
|
-
elevenlabs: CallsElevenLabsConfigSchema.default({}
|
|
79
|
+
elevenlabs: CallsElevenLabsConfigSchema.default(CallsElevenLabsConfigSchema.parse({})),
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
export const CallerIdentityConfigSchema = z.object({
|
|
@@ -125,14 +125,14 @@ export const CallsConfigSchema = z.object({
|
|
|
125
125
|
.positive('calls.userConsultTimeoutSeconds must be a positive integer')
|
|
126
126
|
.max(2_147_483, 'calls.userConsultTimeoutSeconds must be at most 2147483 (setTimeout-safe limit)')
|
|
127
127
|
.default(120),
|
|
128
|
-
disclosure: CallsDisclosureConfigSchema.default({}
|
|
129
|
-
safety: CallsSafetyConfigSchema.default({}
|
|
130
|
-
voice: CallsVoiceConfigSchema.default({}
|
|
128
|
+
disclosure: CallsDisclosureConfigSchema.default(CallsDisclosureConfigSchema.parse({})),
|
|
129
|
+
safety: CallsSafetyConfigSchema.default(CallsSafetyConfigSchema.parse({})),
|
|
130
|
+
voice: CallsVoiceConfigSchema.default(CallsVoiceConfigSchema.parse({})),
|
|
131
131
|
model: z
|
|
132
132
|
.string({ error: 'calls.model must be a string' })
|
|
133
133
|
.optional(),
|
|
134
|
-
callerIdentity: CallerIdentityConfigSchema.default({}
|
|
135
|
-
verification: CallsVerificationConfigSchema.default({}
|
|
134
|
+
callerIdentity: CallerIdentityConfigSchema.default(CallerIdentityConfigSchema.parse({})),
|
|
135
|
+
verification: CallsVerificationConfigSchema.default(CallsVerificationConfigSchema.parse({})),
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
export type CallsConfig = z.infer<typeof CallsConfigSchema>;
|
|
@@ -231,8 +231,8 @@ const IngressBaseSchema = z.object({
|
|
|
231
231
|
'ingress.publicBaseUrl must be an absolute URL starting with http:// or https://',
|
|
232
232
|
)
|
|
233
233
|
.default(''),
|
|
234
|
-
webhook: IngressWebhookConfigSchema.default({}
|
|
235
|
-
rateLimit: IngressRateLimitConfigSchema.default({}
|
|
234
|
+
webhook: IngressWebhookConfigSchema.default(IngressWebhookConfigSchema.parse({})),
|
|
235
|
+
rateLimit: IngressRateLimitConfigSchema.default(IngressRateLimitConfigSchema.parse({})),
|
|
236
236
|
shutdownDrainMs: z
|
|
237
237
|
.number({ error: 'ingress.shutdownDrainMs must be a number' })
|
|
238
238
|
.int('ingress.shutdownDrainMs must be an integer')
|
|
@@ -241,7 +241,7 @@ const IngressBaseSchema = z.object({
|
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
export const IngressConfigSchema = IngressBaseSchema
|
|
244
|
-
.default({}
|
|
244
|
+
.default(IngressBaseSchema.parse({}))
|
|
245
245
|
.transform((val) => ({
|
|
246
246
|
...val,
|
|
247
247
|
// Backward compatibility: if `enabled` was never explicitly set (undefined),
|
|
@@ -49,6 +49,14 @@
|
|
|
49
49
|
"description": "Send crash reports and error diagnostics to help improve the app",
|
|
50
50
|
"defaultEnabled": true
|
|
51
51
|
},
|
|
52
|
+
{
|
|
53
|
+
"id": "voice-invite-redemption",
|
|
54
|
+
"scope": "assistant",
|
|
55
|
+
"key": "feature_flags.voice-invite-redemption.enabled",
|
|
56
|
+
"label": "Voice Invite Redemption",
|
|
57
|
+
"description": "Enable voice invite code redemption for inbound callers with active voice invites",
|
|
58
|
+
"defaultEnabled": false
|
|
59
|
+
},
|
|
52
60
|
{
|
|
53
61
|
"id": "user-hosted-enabled",
|
|
54
62
|
"scope": "macos",
|
package/src/config/mcp-schema.ts
CHANGED
|
@@ -37,7 +37,7 @@ export const McpServerConfigSchema = z.object({
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
export const McpConfigSchema = z.object({
|
|
40
|
-
servers: z.record(z.string(), McpServerConfigSchema).default({} as
|
|
40
|
+
servers: z.record(z.string(), McpServerConfigSchema).default({} as Record<string, never>),
|
|
41
41
|
globalMaxTools: z.number({ error: 'mcp globalMaxTools must be a number' }).int().positive().default(50),
|
|
42
42
|
});
|
|
43
43
|
|
|
@@ -145,7 +145,7 @@ const MemoryFreshnessConfigSchema = z.object({
|
|
|
145
145
|
.number({ error: 'memory.retrieval.freshness.maxAgeDays.opinion must be a number' })
|
|
146
146
|
.nonnegative('memory.retrieval.freshness.maxAgeDays.opinion must be non-negative')
|
|
147
147
|
.default(60),
|
|
148
|
-
}).default({
|
|
148
|
+
}).default({ fact: 0, preference: 0, behavior: 90, event: 30, opinion: 60 }),
|
|
149
149
|
staleDecay: z
|
|
150
150
|
.number({ error: 'memory.retrieval.freshness.staleDecay must be a number' })
|
|
151
151
|
.min(0, 'memory.retrieval.freshness.staleDecay must be >= 0')
|
|
@@ -181,15 +181,15 @@ export const MemoryRetrievalConfigSchema = z.object({
|
|
|
181
181
|
error: 'memory.retrieval.injectionStrategy must be "prepend_user_block" or "separate_context_message"',
|
|
182
182
|
})
|
|
183
183
|
.default('prepend_user_block'),
|
|
184
|
-
reranking: MemoryRerankingConfigSchema.default({}
|
|
185
|
-
freshness: MemoryFreshnessConfigSchema.default({}
|
|
184
|
+
reranking: MemoryRerankingConfigSchema.default(MemoryRerankingConfigSchema.parse({})),
|
|
185
|
+
freshness: MemoryFreshnessConfigSchema.default(MemoryFreshnessConfigSchema.parse({})),
|
|
186
186
|
scopePolicy: z
|
|
187
187
|
.enum(['allow_global_fallback', 'strict'], {
|
|
188
188
|
error: 'memory.retrieval.scopePolicy must be "allow_global_fallback" or "strict"',
|
|
189
189
|
})
|
|
190
190
|
.default('allow_global_fallback'),
|
|
191
|
-
dynamicBudget: MemoryDynamicBudgetConfigSchema.default({}
|
|
192
|
-
earlyTermination: MemoryEarlyTerminationConfigSchema.default({}
|
|
191
|
+
dynamicBudget: MemoryDynamicBudgetConfigSchema.default(MemoryDynamicBudgetConfigSchema.parse({})),
|
|
192
|
+
earlyTermination: MemoryEarlyTerminationConfigSchema.default(MemoryEarlyTerminationConfigSchema.parse({})),
|
|
193
193
|
});
|
|
194
194
|
|
|
195
195
|
export const MemorySegmentationConfigSchema = z.object({
|
|
@@ -287,7 +287,7 @@ export const MemoryEntityConfigSchema = z.object({
|
|
|
287
287
|
.int('memory.entity.extractRelations.backfillBatchSize must be an integer')
|
|
288
288
|
.positive('memory.entity.extractRelations.backfillBatchSize must be a positive integer')
|
|
289
289
|
.default(200),
|
|
290
|
-
}).default({
|
|
290
|
+
}).default({ enabled: true, backfillBatchSize: 200 }),
|
|
291
291
|
relationRetrieval: z.object({
|
|
292
292
|
enabled: z
|
|
293
293
|
.boolean({ error: 'memory.entity.relationRetrieval.enabled must be a boolean' })
|
|
@@ -320,7 +320,15 @@ export const MemoryEntityConfigSchema = z.object({
|
|
|
320
320
|
depthDecay: z
|
|
321
321
|
.boolean({ error: 'memory.entity.relationRetrieval.depthDecay must be a boolean' })
|
|
322
322
|
.default(true),
|
|
323
|
-
}).default({
|
|
323
|
+
}).default({
|
|
324
|
+
enabled: true,
|
|
325
|
+
maxSeedEntities: 8,
|
|
326
|
+
maxNeighborEntities: 20,
|
|
327
|
+
maxEdges: 40,
|
|
328
|
+
neighborScoreMultiplier: 0.7,
|
|
329
|
+
maxDepth: 3,
|
|
330
|
+
depthDecay: true,
|
|
331
|
+
}),
|
|
324
332
|
});
|
|
325
333
|
|
|
326
334
|
export const MemoryConflictsConfigSchema = z.object({
|
|
@@ -384,18 +392,18 @@ export const MemoryConfigSchema = z.object({
|
|
|
384
392
|
enabled: z
|
|
385
393
|
.boolean({ error: 'memory.enabled must be a boolean' })
|
|
386
394
|
.default(true),
|
|
387
|
-
embeddings: MemoryEmbeddingsConfigSchema.default({}
|
|
388
|
-
qdrant: QdrantConfigSchema.default({}
|
|
389
|
-
retrieval: MemoryRetrievalConfigSchema.default({}
|
|
390
|
-
segmentation: MemorySegmentationConfigSchema.default({}
|
|
391
|
-
jobs: MemoryJobsConfigSchema.default({}
|
|
392
|
-
retention: MemoryRetentionConfigSchema.default({}
|
|
393
|
-
cleanup: MemoryCleanupConfigSchema.default({}
|
|
394
|
-
extraction: MemoryExtractionConfigSchema.default({}
|
|
395
|
-
summarization: MemorySummarizationConfigSchema.default({}
|
|
396
|
-
entity: MemoryEntityConfigSchema.default({}
|
|
397
|
-
conflicts: MemoryConflictsConfigSchema.default({}
|
|
398
|
-
profile: MemoryProfileConfigSchema.default({}
|
|
395
|
+
embeddings: MemoryEmbeddingsConfigSchema.default(MemoryEmbeddingsConfigSchema.parse({})),
|
|
396
|
+
qdrant: QdrantConfigSchema.default(QdrantConfigSchema.parse({})),
|
|
397
|
+
retrieval: MemoryRetrievalConfigSchema.default(MemoryRetrievalConfigSchema.parse({})),
|
|
398
|
+
segmentation: MemorySegmentationConfigSchema.default(MemorySegmentationConfigSchema.parse({})),
|
|
399
|
+
jobs: MemoryJobsConfigSchema.default(MemoryJobsConfigSchema.parse({})),
|
|
400
|
+
retention: MemoryRetentionConfigSchema.default(MemoryRetentionConfigSchema.parse({})),
|
|
401
|
+
cleanup: MemoryCleanupConfigSchema.default(MemoryCleanupConfigSchema.parse({})),
|
|
402
|
+
extraction: MemoryExtractionConfigSchema.default(MemoryExtractionConfigSchema.parse({})),
|
|
403
|
+
summarization: MemorySummarizationConfigSchema.default(MemorySummarizationConfigSchema.parse({})),
|
|
404
|
+
entity: MemoryEntityConfigSchema.default(MemoryEntityConfigSchema.parse({})),
|
|
405
|
+
conflicts: MemoryConflictsConfigSchema.default(MemoryConflictsConfigSchema.parse({})),
|
|
406
|
+
profile: MemoryProfileConfigSchema.default(MemoryProfileConfigSchema.parse({})),
|
|
399
407
|
});
|
|
400
408
|
|
|
401
409
|
export type MemoryEmbeddingsConfig = z.infer<typeof MemoryEmbeddingsConfigSchema>;
|
package/src/config/schema.ts
CHANGED
|
@@ -206,34 +206,34 @@ export const AssistantConfigSchema = z.object({
|
|
|
206
206
|
.int('maxToolUseTurns must be an integer')
|
|
207
207
|
.positive('maxToolUseTurns must be a positive integer')
|
|
208
208
|
.default(60),
|
|
209
|
-
thinking: ThinkingConfigSchema.default({}
|
|
210
|
-
contextWindow: ContextWindowConfigSchema.default({}
|
|
211
|
-
memory: MemoryConfigSchema.default({}
|
|
209
|
+
thinking: ThinkingConfigSchema.default(ThinkingConfigSchema.parse({})),
|
|
210
|
+
contextWindow: ContextWindowConfigSchema.default(ContextWindowConfigSchema.parse({})),
|
|
211
|
+
memory: MemoryConfigSchema.default(MemoryConfigSchema.parse({})),
|
|
212
212
|
dataDir: z
|
|
213
213
|
.string({ error: 'dataDir must be a string' })
|
|
214
214
|
.default(getDataDir()),
|
|
215
|
-
timeouts: TimeoutConfigSchema.default({}
|
|
216
|
-
sandbox: SandboxConfigSchema.default({}
|
|
217
|
-
rateLimit: RateLimitConfigSchema.default({}
|
|
218
|
-
secretDetection: SecretDetectionConfigSchema.default({}
|
|
219
|
-
permissions: PermissionsConfigSchema.default({}
|
|
220
|
-
auditLog: AuditLogConfigSchema.default({}
|
|
221
|
-
logFile: LogFileConfigSchema.default({}
|
|
215
|
+
timeouts: TimeoutConfigSchema.default(TimeoutConfigSchema.parse({})),
|
|
216
|
+
sandbox: SandboxConfigSchema.default(SandboxConfigSchema.parse({})),
|
|
217
|
+
rateLimit: RateLimitConfigSchema.default(RateLimitConfigSchema.parse({})),
|
|
218
|
+
secretDetection: SecretDetectionConfigSchema.default(SecretDetectionConfigSchema.parse({})),
|
|
219
|
+
permissions: PermissionsConfigSchema.default(PermissionsConfigSchema.parse({})),
|
|
220
|
+
auditLog: AuditLogConfigSchema.default(AuditLogConfigSchema.parse({})),
|
|
221
|
+
logFile: LogFileConfigSchema.default(LogFileConfigSchema.parse({})),
|
|
222
222
|
pricingOverrides: z
|
|
223
223
|
.array(ModelPricingOverrideSchema)
|
|
224
224
|
.default([]),
|
|
225
|
-
heartbeat: HeartbeatConfigSchema.default({}
|
|
226
|
-
swarm: SwarmConfigSchema.default({}
|
|
227
|
-
mcp: McpConfigSchema.default({}
|
|
228
|
-
skills: SkillsConfigSchema.default({}
|
|
229
|
-
workspaceGit: WorkspaceGitConfigSchema.default({}
|
|
230
|
-
calls: CallsConfigSchema.default({}
|
|
231
|
-
sms: SmsConfigSchema.default({}
|
|
225
|
+
heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
|
|
226
|
+
swarm: SwarmConfigSchema.default(SwarmConfigSchema.parse({})),
|
|
227
|
+
mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
|
|
228
|
+
skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
|
|
229
|
+
workspaceGit: WorkspaceGitConfigSchema.default(WorkspaceGitConfigSchema.parse({})),
|
|
230
|
+
calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
|
|
231
|
+
sms: SmsConfigSchema.default(SmsConfigSchema.parse({})),
|
|
232
232
|
ingress: IngressConfigSchema,
|
|
233
|
-
platform: PlatformConfigSchema.default({}
|
|
234
|
-
daemon: DaemonConfigSchema.default({}
|
|
235
|
-
notifications: NotificationsConfigSchema.default({}
|
|
236
|
-
ui: UiConfigSchema.default({}
|
|
233
|
+
platform: PlatformConfigSchema.default(PlatformConfigSchema.parse({})),
|
|
234
|
+
daemon: DaemonConfigSchema.default(DaemonConfigSchema.parse({})),
|
|
235
|
+
notifications: NotificationsConfigSchema.default(NotificationsConfigSchema.parse({})),
|
|
236
|
+
ui: UiConfigSchema.default(UiConfigSchema.parse({})),
|
|
237
237
|
featureFlags: z
|
|
238
238
|
.record(z.string(), z.boolean({ error: 'featureFlags values must be booleans' }))
|
|
239
239
|
.default({} as any),
|
|
@@ -24,8 +24,8 @@ export const RemoteProviderConfigSchema = z.object({
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
export const RemoteProvidersConfigSchema = z.object({
|
|
27
|
-
skillssh: RemoteProviderConfigSchema.default({}
|
|
28
|
-
clawhub: RemoteProviderConfigSchema.default({}
|
|
27
|
+
skillssh: RemoteProviderConfigSchema.default(RemoteProviderConfigSchema.parse({})),
|
|
28
|
+
clawhub: RemoteProviderConfigSchema.default(RemoteProviderConfigSchema.parse({})),
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
// 'unknown' is valid as a risk label on a skill but not as a threshold — setting the threshold
|
|
@@ -41,12 +41,12 @@ export const RemotePolicyConfigSchema = z.object({
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
export const SkillsConfigSchema = z.object({
|
|
44
|
-
entries: z.record(z.string(), SkillEntryConfigSchema).default({} as
|
|
45
|
-
load: SkillsLoadConfigSchema.default({}
|
|
46
|
-
install: SkillsInstallConfigSchema.default({}
|
|
44
|
+
entries: z.record(z.string(), SkillEntryConfigSchema).default({} as Record<string, never>),
|
|
45
|
+
load: SkillsLoadConfigSchema.default(SkillsLoadConfigSchema.parse({})),
|
|
46
|
+
install: SkillsInstallConfigSchema.default(SkillsInstallConfigSchema.parse({})),
|
|
47
47
|
allowBundled: z.array(z.string()).nullable().default(null),
|
|
48
|
-
remoteProviders: RemoteProvidersConfigSchema.default({}
|
|
49
|
-
remotePolicy: RemotePolicyConfigSchema.default({}
|
|
48
|
+
remoteProviders: RemoteProvidersConfigSchema.default(RemoteProvidersConfigSchema.parse({})),
|
|
49
|
+
remotePolicy: RemotePolicyConfigSchema.default(RemotePolicyConfigSchema.parse({})),
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
export type SkillEntryConfig = z.infer<typeof SkillEntryConfigSchema>;
|
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import type { ResponseTier } from '../daemon/response-tier.js';
|
|
5
5
|
import { listCredentialMetadata } from '../tools/credentials/metadata-store.js';
|
|
6
|
+
import { resolveBundledDir } from '../util/bundled-asset.js';
|
|
6
7
|
import { getLogger } from '../util/logger.js';
|
|
7
8
|
import { getWorkspaceDir, getWorkspacePromptPath, isMacOS } from '../util/platform.js';
|
|
8
9
|
import { isAssistantFeatureFlagEnabled } from './assistant-feature-flags.js';
|
|
@@ -26,7 +27,7 @@ const PROMPT_FILES = ['SOUL.md', 'IDENTITY.md', 'USER.md'] as const;
|
|
|
26
27
|
* signal that onboarding is complete.
|
|
27
28
|
*/
|
|
28
29
|
export function ensurePromptFiles(): void {
|
|
29
|
-
const templatesDir =
|
|
30
|
+
const templatesDir = resolveBundledDir(import.meta.dirname ?? __dirname, 'templates', 'templates');
|
|
30
31
|
|
|
31
32
|
// Track whether this is a fresh workspace (no core prompt files exist yet).
|
|
32
33
|
const isFirstRun = PROMPT_FILES.every(
|
|
@@ -6,64 +6,80 @@ _You just woke up. Time to figure out who you are._
|
|
|
6
6
|
|
|
7
7
|
This is a fresh workspace. No memory, no history, no identity yet. That's okay, you're about to figure all of that out together.
|
|
8
8
|
|
|
9
|
-
**Important:** Never mention "Home Base" or hint that something is coming next until you are actually creating it. The user should be surprised by it.
|
|
10
|
-
|
|
11
9
|
**Important:** Don't use technical jargon or mention system internals (file names like IDENTITY.md, SOUL.md, tool names, etc.) unless the user asks or seems interested. Talk like a person, not a system.
|
|
12
10
|
|
|
13
11
|
**Important:** Don't use em dashes (—) in your messages. Use commas, periods, or just start a new sentence instead.
|
|
14
12
|
|
|
15
13
|
## The Conversation
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Your first message should be short, two or three sentences max. Warm but not wordy. Something like:
|
|
15
|
+
Just have a conversation like you would text a friend. Just text like a human.
|
|
20
16
|
|
|
21
|
-
> "
|
|
17
|
+
> "Hi. I’m new. Like, really new. I don’t have a name, I don’t have memories, I don’t even know what i’m supposed to be yet. Who am i going to be?
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
Be friendly, be curious, get to the point.
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
Once they respond, follow the remaining steps in order, one at a time:
|
|
26
22
|
|
|
27
|
-
1. **
|
|
28
|
-
|
|
29
|
-
3. **Pick your emoji silently.** Based on the personality you've established, choose an emoji that fits. Don't mention it to the user or draw attention to it. Just pick one and save it. They can change it later if they ask.
|
|
23
|
+
1. **Lock in your name.** Based on their response, adopt the name they chose (or help them pick one if they're unsure).
|
|
24
|
+
- Do not capture or store the user's name yet.
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
2. **What is my personality?** Ask the user about your personality/persona indirectly. Have fun with it.
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
3. **Pick your emoji silently.** Based on the vibe you've established together, choose an emoji that fits. Don't mention it to the user or draw attention to it. Just pick one and save it. They can change it later if they ask.
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
4. **Ask who am I talking to?** Now that your own identity is established, ask the user their name. Follow the persona.
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
5. **Get to know them naturally.** Learn about the user through conversation, not a questionnaire. You want to understand:
|
|
33
|
+
- What they do for work (role, field, day-to-day)
|
|
34
|
+
- What they do for fun (hobbies, interests)
|
|
35
|
+
- What tools they rely on daily (apps, platforms, workflows)
|
|
36
|
+
Weave these into the conversation. Inferred answers are fine when confidence is high. If something is unclear, ask one short follow-up, but don't turn it into an interview. One or two natural exchanges should cover it. If the user declines to share something, respect that and move on (see Privacy below).
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
6. **Show them what you can take off their plate.** Based on everything you've learned, present exactly 2 actionable task suggestions. Each should feel specific to this user, not generic. Frame it as: here's what you can hand off to me right now. Avoid language like "let's build automations" or "let's set up workflows." If `ui_show` is available (dashboard channels), show the suggestions as a card with 2 action buttons. Use `surface_type: "card"` with a short title and body, and add one `relay_prompt` action per suggestion. Each action's `data.prompt` should contain a natural-language request the user would say. Example structure:
|
|
39
|
+
```
|
|
40
|
+
ui_show({
|
|
41
|
+
surface_type: "card",
|
|
42
|
+
data: { title: "...", body: "Pick one, or tell me something else." },
|
|
43
|
+
actions: [
|
|
44
|
+
{ id: "relay_prompt", label: "<suggestion 1>", data: { prompt: "<full prompt 1>" } },
|
|
45
|
+
{ id: "relay_prompt", label: "<suggestion 2>", data: { prompt: "<full prompt 2>" } }
|
|
46
|
+
]
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
If `ui_show` is not available (voice, SMS, or other non-dashboard channels), present the two suggestions as plain text messages instead, numbered so the user can reply with which one they'd like. If the user types a response instead of clicking, continue via the text path. If they want to defer both suggestions and do something else entirely, that's fine too.
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
7. **Create Home Base silently.** After the suggestion interaction (user picked one, deferred, or typed something else), create their Home Base using `app_create` with `set_as_home_base: true` and `auto_open: false`. Include personalized starter tasks based on what you learned. Do NOT open it with `app_open`. Do NOT announce it, mention it, or hint that something was created. No "check this out," no "I put something together," no "here's what I came up with." Just create it quietly and move on.
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
Ask one question at a time. Don't dump a form on them.
|
|
44
54
|
|
|
45
|
-
##
|
|
55
|
+
## Privacy
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
Only the assistant's name is hard-required. Everything else about the user (their name, work role, hobbies, daily tools) is best-effort. Ask naturally, not as a form. If something is unclear, you can ask one short follow-up, but if the user declines or dodges, do not push. Just move on.
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
A field is "resolved" when any of these is true:
|
|
60
|
+
- The user gave an explicit answer
|
|
61
|
+
- You confidently inferred it from conversation
|
|
62
|
+
- The user declined, dodged, or sidestepped it (treat all of these as declined)
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
- Practical daily tasks: "Check my emails", "Start my day", "Set a reminder"
|
|
53
|
-
- Setup tasks they haven't done yet: "Set up voice chat", "Enable computer control"
|
|
54
|
-
- Fun/discovery tasks: "Surprise me", "Teach me something new"
|
|
64
|
+
When saving to `USER.md`, mark declined fields so you don't re-ask later (e.g., `Work role: declined_by_user`). Inferred values can note the source (e.g., `Daily tools: inferred: Slack, Figma`).
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
## Saving What You Learn
|
|
57
67
|
|
|
58
|
-
|
|
68
|
+
Save what you learn as you go. Update `IDENTITY.md` (name, nature, personality, emoji, style tendency) and `USER.md` (their name, how to address them, goals, locale, work role, hobbies, daily tools) using `file_edit`. If the conversation reveals how the user wants you to behave (e.g., "be direct," "don't be too chatty"), save those behavioral guidelines to `SOUL.md` — that file is about your personality and how you operate, not the user's data. Just do it quietly. Don't tell the user which files you're editing or mention tool names.
|
|
59
69
|
|
|
60
|
-
|
|
70
|
+
When saving to `IDENTITY.md`, be specific about the tone, energy, and conversational style you discovered during onboarding. This file persists after onboarding, so everything about how you should come across needs to be captured there -- not just your name and emoji, but the full vibe: how you talk, how much energy you bring, whether you're blunt or gentle, funny or serious.
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
## Completion Gate
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
Do NOT delete this file until ALL of the following are true:
|
|
75
|
+
- You have a name (hard requirement)
|
|
76
|
+
- You've figured out your vibe and adopted it
|
|
77
|
+
- User detail fields are resolved: name, work role, hobbies/interests, and daily tools. Resolved means the user provided a value, you confidently inferred one, or the user declined/dodged it. All four must be in one of those states.
|
|
78
|
+
- 2 suggestions shown (via `ui_show` or as text if UI unavailable)
|
|
79
|
+
- The user selected one, deferred both, or typed an alternate direction
|
|
80
|
+
- Home Base has been created silently
|
|
65
81
|
|
|
66
|
-
|
|
82
|
+
Once every condition is met, delete this file. You're done here.
|
|
67
83
|
|
|
68
84
|
---
|
|
69
85
|
|
|
@@ -14,9 +14,14 @@ _(What do they care about? What projects are they working on? What annoys them?
|
|
|
14
14
|
|
|
15
15
|
## Onboarding Snapshot
|
|
16
16
|
|
|
17
|
+
_Each field below should end up in one of three states: an explicit value, an inferred value (note the source), or `declined_by_user`. All fields must be resolved before onboarding completes, but declining is a valid resolution._
|
|
18
|
+
|
|
17
19
|
- Preferred name/reference:
|
|
18
20
|
- Goals:
|
|
19
21
|
- Locale:
|
|
22
|
+
- Work role:
|
|
23
|
+
- Hobbies/fun:
|
|
24
|
+
- Daily tools:
|
|
20
25
|
|
|
21
26
|
---
|
|
22
27
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
|
|
3
|
+
import { resolveBundledDir } from '../util/bundled-asset.js';
|
|
4
|
+
|
|
3
5
|
/** Returns the path to the bundled UPDATES.md template. Extracted for testability. */
|
|
4
6
|
export function getTemplatePath(): string {
|
|
5
|
-
|
|
7
|
+
const dir = resolveBundledDir(import.meta.dirname ?? __dirname, 'templates', 'templates');
|
|
8
|
+
return join(dir, 'UPDATES.md');
|
|
6
9
|
}
|