@vellumai/assistant 0.3.28 → 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 +33 -3
- package/bun.lock +4 -1
- package/docs/trusted-contact-access.md +9 -2
- package/package.json +6 -3
- package/scripts/ipc/generate-swift.ts +3 -3
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +80 -0
- package/src/__tests__/agent-loop-thinking.test.ts +1 -1
- 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__/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__/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__/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-control-plane-policy.test.ts +19 -19
- package/src/__tests__/guardian-dispatch.test.ts +2 -0
- 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 +138 -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__/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 +183 -9
- package/src/__tests__/notification-decision-fallback.test.ts +2 -0
- package/src/__tests__/notification-decision-strategy.test.ts +61 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -0
- 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__/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__/send-endpoint-busy.test.ts +4 -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 +50 -12
- 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 +7 -7
- 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/calls/call-controller.ts +26 -23
- package/src/calls/guardian-action-sweep.ts +10 -2
- package/src/calls/relay-server.ts +216 -27
- 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/vellum-skills/trusted-contacts/SKILL.md +139 -16
- package/src/daemon/handlers/config-inbox.ts +4 -4
- package/src/daemon/handlers/sessions.ts +148 -4
- package/src/daemon/ipc-contract/messages.ts +16 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +19 -0
- package/src/daemon/pairing-store.ts +86 -3
- package/src/daemon/session-agent-loop.ts +5 -5
- package/src/daemon/session-lifecycle.ts +25 -17
- package/src/daemon/session-memory.ts +2 -2
- package/src/daemon/session-process.ts +1 -20
- package/src/daemon/session-runtime-assembly.ts +28 -22
- package/src/daemon/session-tool-setup.ts +2 -2
- package/src/daemon/session.ts +3 -3
- package/src/memory/canonical-guardian-store.ts +63 -1
- package/src/memory/channel-guardian-store.ts +1 -0
- package/src/memory/conversation-crud.ts +7 -7
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-local.ts +257 -39
- package/src/memory/embedding-runtime-manager.ts +471 -0
- 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/037-voice-invite-columns.ts +16 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/qdrant-client.ts +31 -22
- package/src/memory/schema.ts +4 -0
- package/src/notifications/copy-composer.ts +15 -0
- package/src/runtime/access-request-helper.ts +43 -7
- package/src/runtime/actor-trust-resolver.ts +46 -50
- 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 -96
- package/src/runtime/guardian-reply-router.ts +31 -1
- package/src/runtime/ingress-service.ts +80 -3
- package/src/runtime/invite-redemption-service.ts +141 -2
- 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 +2 -2
- package/src/runtime/routes/guardian-approval-interception.ts +17 -6
- package/src/runtime/routes/inbound-message-handler.ts +41 -10
- package/src/runtime/routes/ingress-routes.ts +52 -4
- package/src/runtime/routes/pairing-routes.ts +3 -0
- package/src/tools/guardian-control-plane-policy.ts +2 -2
- package/src/tools/tool-approval-handler.ts +11 -11
- package/src/tools/types.ts +2 -2
- 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
|
@@ -10,23 +10,25 @@ import { randomInt } from 'node:crypto';
|
|
|
10
10
|
|
|
11
11
|
import type { ServerWebSocket } from 'bun';
|
|
12
12
|
|
|
13
|
+
import { isAssistantFeatureFlagEnabled } from '../config/assistant-feature-flags.js';
|
|
13
14
|
import { getConfig } from '../config/loader.js';
|
|
14
15
|
import * as conversationStore from '../memory/conversation-store.js';
|
|
16
|
+
import { findActiveVoiceInvites } from '../memory/ingress-invite-store.js';
|
|
15
17
|
import { revokeScopedApprovalGrantsForContext } from '../memory/scoped-approval-grants.js';
|
|
16
18
|
import { notifyGuardianOfAccessRequest } from '../runtime/access-request-helper.js';
|
|
17
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
resolveActorTrust,
|
|
21
|
+
toGuardianRuntimeContextFromTrust,
|
|
22
|
+
} from '../runtime/actor-trust-resolver.js';
|
|
18
23
|
import {
|
|
19
24
|
getPendingChallenge,
|
|
20
25
|
validateAndConsumeChallenge,
|
|
21
26
|
} from '../runtime/channel-guardian-service.js';
|
|
22
|
-
import {
|
|
23
|
-
resolveGuardianContext,
|
|
24
|
-
toGuardianRuntimeContext,
|
|
25
|
-
} from '../runtime/guardian-context-resolver.js';
|
|
26
27
|
import {
|
|
27
28
|
composeVerificationVoice,
|
|
28
29
|
GUARDIAN_VERIFY_TEMPLATE_KEYS,
|
|
29
30
|
} from '../runtime/guardian-verification-templates.js';
|
|
31
|
+
import { redeemVoiceInviteCode } from '../runtime/ingress-service.js';
|
|
30
32
|
import { parseJsonSafe } from '../util/json.js';
|
|
31
33
|
import { getLogger } from '../util/logger.js';
|
|
32
34
|
import { normalizeAssistantId } from '../util/platform.js';
|
|
@@ -173,6 +175,12 @@ export class RelayConnection {
|
|
|
173
175
|
// Outbound guardian verification state (system calls the guardian)
|
|
174
176
|
private outboundGuardianVerificationSessionId: string | null = null;
|
|
175
177
|
|
|
178
|
+
// Inbound voice invite redemption state
|
|
179
|
+
private inviteRedemptionActive = false;
|
|
180
|
+
private inviteRedemptionAssistantId: string | null = null;
|
|
181
|
+
private inviteRedemptionFromNumber: string | null = null;
|
|
182
|
+
private inviteRedemptionCodeLength = 6;
|
|
183
|
+
|
|
176
184
|
constructor(ws: ServerWebSocket<RelayWebSocketData>, callSessionId: string) {
|
|
177
185
|
this.ws = ws;
|
|
178
186
|
this.callSessionId = callSessionId;
|
|
@@ -428,15 +436,13 @@ export class RelayConnection {
|
|
|
428
436
|
// calls msg.from is the caller; for outbound calls msg.to is the
|
|
429
437
|
// recipient (msg.from is the assistant's Twilio number).
|
|
430
438
|
const otherPartyNumber = isInbound ? msg.from : msg.to;
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}),
|
|
439
|
-
);
|
|
439
|
+
const initialActorTrust = resolveActorTrust({
|
|
440
|
+
assistantId,
|
|
441
|
+
sourceChannel: 'voice',
|
|
442
|
+
externalChatId: otherPartyNumber,
|
|
443
|
+
senderExternalUserId: otherPartyNumber || undefined,
|
|
444
|
+
});
|
|
445
|
+
const initialGuardianContext = toGuardianRuntimeContextFromTrust(initialActorTrust, otherPartyNumber);
|
|
440
446
|
|
|
441
447
|
const controller = new CallController(this.callSessionId, this, session?.task ?? null, {
|
|
442
448
|
broadcast: globalBroadcast,
|
|
@@ -494,6 +500,41 @@ export class RelayConnection {
|
|
|
494
500
|
const pendingChallenge = getPendingChallenge(assistantId, 'voice');
|
|
495
501
|
|
|
496
502
|
if (actorTrust.trustClass === 'unknown' && !pendingChallenge) {
|
|
503
|
+
// Before denying, check if there is an active voice invite bound
|
|
504
|
+
// to the caller's phone number. If so, enter the invite redemption
|
|
505
|
+
// subflow instead of denying the call outright.
|
|
506
|
+
// Gated behind the voice-invite-redemption feature flag (defaults OFF).
|
|
507
|
+
const voiceInviteEnabled = isAssistantFeatureFlagEnabled(
|
|
508
|
+
'feature_flags.voice-invite-redemption.enabled',
|
|
509
|
+
config,
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
if (voiceInviteEnabled) {
|
|
513
|
+
let voiceInvites: ReturnType<typeof findActiveVoiceInvites> = [];
|
|
514
|
+
try {
|
|
515
|
+
voiceInvites = findActiveVoiceInvites({
|
|
516
|
+
assistantId,
|
|
517
|
+
expectedExternalUserId: msg.from,
|
|
518
|
+
});
|
|
519
|
+
} catch (err) {
|
|
520
|
+
log.warn({ err, callSessionId: this.callSessionId }, 'Failed to check voice invites for unknown caller');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Exclude invites that are past their expiresAt even if the DB
|
|
524
|
+
// status hasn't been lazily flipped to 'expired' yet.
|
|
525
|
+
const now = Date.now();
|
|
526
|
+
const nonExpiredInvites = voiceInvites.filter(i => !i.expiresAt || i.expiresAt > now);
|
|
527
|
+
|
|
528
|
+
if (nonExpiredInvites.length > 0) {
|
|
529
|
+
log.info(
|
|
530
|
+
{ callSessionId: this.callSessionId, from: msg.from },
|
|
531
|
+
'Inbound voice ACL: unknown caller has active voice invite — entering redemption flow',
|
|
532
|
+
);
|
|
533
|
+
this.startInviteRedemption(assistantId, msg.from);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
497
538
|
log.info(
|
|
498
539
|
{ callSessionId: this.callSessionId, from: msg.from, trustClass: actorTrust.trustClass },
|
|
499
540
|
'Inbound voice ACL: unknown caller denied',
|
|
@@ -613,10 +654,7 @@ export class RelayConnection {
|
|
|
613
654
|
// Update the controller's guardian context with the trust-resolved
|
|
614
655
|
// context so downstream policy gates have accurate actor metadata.
|
|
615
656
|
if (this.controller && actorTrust.trustClass !== 'unknown') {
|
|
616
|
-
const resolvedGuardianContext =
|
|
617
|
-
'voice',
|
|
618
|
-
toGuardianContextCompat(actorTrust, msg.from),
|
|
619
|
-
);
|
|
657
|
+
const resolvedGuardianContext = toGuardianRuntimeContextFromTrust(actorTrust, msg.from);
|
|
620
658
|
this.controller.setGuardianContext(resolvedGuardianContext);
|
|
621
659
|
}
|
|
622
660
|
|
|
@@ -876,16 +914,14 @@ export class RelayConnection {
|
|
|
876
914
|
} else {
|
|
877
915
|
// Inbound: proceed to normal call flow
|
|
878
916
|
if (this.controller) {
|
|
917
|
+
const verifiedActorTrust = resolveActorTrust({
|
|
918
|
+
assistantId: this.guardianChallengeAssistantId,
|
|
919
|
+
sourceChannel: 'voice',
|
|
920
|
+
externalChatId: this.guardianVerificationFromNumber,
|
|
921
|
+
senderExternalUserId: this.guardianVerificationFromNumber,
|
|
922
|
+
});
|
|
879
923
|
this.controller.setGuardianContext(
|
|
880
|
-
|
|
881
|
-
'voice',
|
|
882
|
-
resolveGuardianContext({
|
|
883
|
-
assistantId: this.guardianChallengeAssistantId,
|
|
884
|
-
sourceChannel: 'voice',
|
|
885
|
-
externalChatId: this.guardianVerificationFromNumber,
|
|
886
|
-
senderExternalUserId: this.guardianVerificationFromNumber,
|
|
887
|
-
}),
|
|
888
|
-
),
|
|
924
|
+
toGuardianRuntimeContextFromTrust(verifiedActorTrust, this.guardianVerificationFromNumber),
|
|
889
925
|
);
|
|
890
926
|
this.startNormalCallFlow(this.controller, true);
|
|
891
927
|
}
|
|
@@ -960,6 +996,126 @@ export class RelayConnection {
|
|
|
960
996
|
}
|
|
961
997
|
}
|
|
962
998
|
|
|
999
|
+
/**
|
|
1000
|
+
* Enter the invite redemption subflow for an inbound unknown caller
|
|
1001
|
+
* who has an active voice invite. Prompts the caller to enter their
|
|
1002
|
+
* invite code via DTMF or speech.
|
|
1003
|
+
*/
|
|
1004
|
+
private startInviteRedemption(assistantId: string, fromNumber: string): void {
|
|
1005
|
+
this.inviteRedemptionActive = true;
|
|
1006
|
+
this.inviteRedemptionAssistantId = assistantId;
|
|
1007
|
+
this.inviteRedemptionFromNumber = fromNumber;
|
|
1008
|
+
this.connectionState = 'verification_pending';
|
|
1009
|
+
this.verificationAttempts = 0;
|
|
1010
|
+
this.verificationMaxAttempts = 3;
|
|
1011
|
+
this.inviteRedemptionCodeLength = 6;
|
|
1012
|
+
this.dtmfBuffer = '';
|
|
1013
|
+
|
|
1014
|
+
recordCallEvent(this.callSessionId, 'invite_redemption_started', {
|
|
1015
|
+
assistantId,
|
|
1016
|
+
codeLength: 6,
|
|
1017
|
+
maxAttempts: this.verificationMaxAttempts,
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
this.sendTextToken(
|
|
1021
|
+
'Please enter your 6-digit invite code using your keypad, or speak the digits now.',
|
|
1022
|
+
true,
|
|
1023
|
+
);
|
|
1024
|
+
|
|
1025
|
+
log.info(
|
|
1026
|
+
{ callSessionId: this.callSessionId, assistantId },
|
|
1027
|
+
'Inbound voice invite redemption started',
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Validate an entered invite code against active voice invites for the
|
|
1033
|
+
* caller. On success, create/activate the ingress member and transition
|
|
1034
|
+
* to the normal call flow. On failure, allow retries up to max attempts.
|
|
1035
|
+
*/
|
|
1036
|
+
private attemptInviteCodeRedemption(enteredCode: string): void {
|
|
1037
|
+
if (!this.inviteRedemptionAssistantId || !this.inviteRedemptionFromNumber) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const result = redeemVoiceInviteCode({
|
|
1042
|
+
assistantId: this.inviteRedemptionAssistantId,
|
|
1043
|
+
callerExternalUserId: this.inviteRedemptionFromNumber,
|
|
1044
|
+
sourceChannel: 'voice',
|
|
1045
|
+
code: enteredCode,
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
if (result.ok) {
|
|
1049
|
+
this.connectionState = 'connected';
|
|
1050
|
+
this.inviteRedemptionActive = false;
|
|
1051
|
+
this.verificationAttempts = 0;
|
|
1052
|
+
this.dtmfBuffer = '';
|
|
1053
|
+
|
|
1054
|
+
recordCallEvent(this.callSessionId, 'invite_redemption_succeeded', {
|
|
1055
|
+
memberId: result.memberId,
|
|
1056
|
+
...(result.type === 'redeemed' ? { inviteId: result.inviteId } : {}),
|
|
1057
|
+
});
|
|
1058
|
+
log.info(
|
|
1059
|
+
{ callSessionId: this.callSessionId, memberId: result.memberId, type: result.type },
|
|
1060
|
+
'Voice invite redemption succeeded',
|
|
1061
|
+
);
|
|
1062
|
+
|
|
1063
|
+
if (this.controller) {
|
|
1064
|
+
const redeemedActorTrust = resolveActorTrust({
|
|
1065
|
+
assistantId: this.inviteRedemptionAssistantId,
|
|
1066
|
+
sourceChannel: 'voice',
|
|
1067
|
+
externalChatId: this.inviteRedemptionFromNumber,
|
|
1068
|
+
senderExternalUserId: this.inviteRedemptionFromNumber,
|
|
1069
|
+
});
|
|
1070
|
+
this.controller.setGuardianContext(
|
|
1071
|
+
toGuardianRuntimeContextFromTrust(redeemedActorTrust, this.inviteRedemptionFromNumber),
|
|
1072
|
+
);
|
|
1073
|
+
this.startNormalCallFlow(this.controller, true);
|
|
1074
|
+
}
|
|
1075
|
+
} else {
|
|
1076
|
+
this.verificationAttempts++;
|
|
1077
|
+
|
|
1078
|
+
if (this.verificationAttempts >= this.verificationMaxAttempts) {
|
|
1079
|
+
this.inviteRedemptionActive = false;
|
|
1080
|
+
|
|
1081
|
+
recordCallEvent(this.callSessionId, 'invite_redemption_failed', {
|
|
1082
|
+
attempts: this.verificationAttempts,
|
|
1083
|
+
});
|
|
1084
|
+
log.warn(
|
|
1085
|
+
{ callSessionId: this.callSessionId, attempts: this.verificationAttempts },
|
|
1086
|
+
'Voice invite redemption failed — max attempts reached',
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
this.sendTextToken('Too many invalid attempts. Goodbye.', true);
|
|
1090
|
+
|
|
1091
|
+
updateCallSession(this.callSessionId, {
|
|
1092
|
+
status: 'failed',
|
|
1093
|
+
endedAt: Date.now(),
|
|
1094
|
+
lastError: 'Voice invite redemption failed — max attempts exceeded',
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
const failSession = getCallSession(this.callSessionId);
|
|
1098
|
+
if (failSession) {
|
|
1099
|
+
expirePendingQuestions(this.callSessionId);
|
|
1100
|
+
persistCallCompletionMessage(failSession.conversationId, this.callSessionId).catch((err) => {
|
|
1101
|
+
log.error({ err, conversationId: failSession.conversationId, callSessionId: this.callSessionId }, 'Failed to persist call completion message');
|
|
1102
|
+
});
|
|
1103
|
+
fireCallCompletionNotifier(failSession.conversationId, this.callSessionId);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
setTimeout(() => {
|
|
1107
|
+
this.endSession('Invite redemption failed');
|
|
1108
|
+
}, 2000);
|
|
1109
|
+
} else {
|
|
1110
|
+
log.info(
|
|
1111
|
+
{ callSessionId: this.callSessionId, attempt: this.verificationAttempts, maxAttempts: this.verificationMaxAttempts },
|
|
1112
|
+
'Voice invite redemption attempt failed — retrying',
|
|
1113
|
+
);
|
|
1114
|
+
this.sendTextToken('Invalid code. Please try again.', true);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
963
1119
|
private async handlePrompt(msg: RelayPromptMessage): Promise<void> {
|
|
964
1120
|
if (this.connectionState === 'disconnecting') {
|
|
965
1121
|
return;
|
|
@@ -990,6 +1146,26 @@ export class RelayConnection {
|
|
|
990
1146
|
return;
|
|
991
1147
|
}
|
|
992
1148
|
|
|
1149
|
+
// During invite redemption, attempt to parse spoken digits from the
|
|
1150
|
+
// transcript and validate against the caller's active voice invite.
|
|
1151
|
+
if (this.connectionState === 'verification_pending' && this.inviteRedemptionActive) {
|
|
1152
|
+
const spokenDigits = RelayConnection.parseDigitsFromSpeech(msg.voicePrompt);
|
|
1153
|
+
log.info(
|
|
1154
|
+
{ callSessionId: this.callSessionId, transcript: msg.voicePrompt, spokenDigits },
|
|
1155
|
+
'Speech received during invite redemption',
|
|
1156
|
+
);
|
|
1157
|
+
if (spokenDigits.length >= this.inviteRedemptionCodeLength) {
|
|
1158
|
+
const enteredCode = spokenDigits.slice(0, this.inviteRedemptionCodeLength);
|
|
1159
|
+
this.attemptInviteCodeRedemption(enteredCode);
|
|
1160
|
+
} else if (spokenDigits.length > 0) {
|
|
1161
|
+
this.sendTextToken(
|
|
1162
|
+
`I heard ${spokenDigits.length} digits. Please enter all ${this.inviteRedemptionCodeLength} digits of your code.`,
|
|
1163
|
+
true,
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
993
1169
|
// During outbound callee verification, ignore voice prompts — the callee
|
|
994
1170
|
// should be entering DTMF digits, not speaking.
|
|
995
1171
|
if (this.connectionState === 'verification_pending') {
|
|
@@ -1102,6 +1278,19 @@ export class RelayConnection {
|
|
|
1102
1278
|
return;
|
|
1103
1279
|
}
|
|
1104
1280
|
|
|
1281
|
+
// If invite redemption is pending, accumulate digits and validate
|
|
1282
|
+
// the code against the caller's active voice invite.
|
|
1283
|
+
if (this.connectionState === 'verification_pending' && this.inviteRedemptionActive) {
|
|
1284
|
+
this.dtmfBuffer += msg.digit;
|
|
1285
|
+
|
|
1286
|
+
if (this.dtmfBuffer.length >= this.inviteRedemptionCodeLength) {
|
|
1287
|
+
const enteredCode = this.dtmfBuffer.slice(0, this.inviteRedemptionCodeLength);
|
|
1288
|
+
this.dtmfBuffer = '';
|
|
1289
|
+
this.attemptInviteCodeRedemption(enteredCode);
|
|
1290
|
+
}
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1105
1294
|
// If outbound callee verification is pending, accumulate digits and check the code
|
|
1106
1295
|
if (this.connectionState === 'verification_pending' && this.verificationCode) {
|
|
1107
1296
|
this.dtmfBuffer += msg.digit;
|
package/src/calls/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type CallStatus = 'initiated' | 'ringing' | 'in_progress' | 'waiting_on_user' | 'completed' | 'failed' | 'cancelled';
|
|
2
|
-
export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed' | 'callee_verification_started' | 'callee_verification_succeeded' | 'callee_verification_failed' | 'guardian_voice_verification_started' | 'guardian_voice_verification_succeeded' | 'guardian_voice_verification_failed' | 'outbound_guardian_voice_verification_started' | 'outbound_guardian_voice_verification_succeeded' | 'outbound_guardian_voice_verification_failed' | 'guardian_consultation_timed_out' | 'guardian_unavailable_skipped' | 'guardian_consult_deferred' | 'guardian_consult_coalesced' | 'inbound_acl_denied';
|
|
2
|
+
export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed' | 'callee_verification_started' | 'callee_verification_succeeded' | 'callee_verification_failed' | 'guardian_voice_verification_started' | 'guardian_voice_verification_succeeded' | 'guardian_voice_verification_failed' | 'outbound_guardian_voice_verification_started' | 'outbound_guardian_voice_verification_succeeded' | 'outbound_guardian_voice_verification_failed' | 'guardian_consultation_timed_out' | 'guardian_unavailable_skipped' | 'guardian_consult_deferred' | 'guardian_consult_coalesced' | 'inbound_acl_denied' | 'invite_redemption_started' | 'invite_redemption_succeeded' | 'invite_redemption_failed';
|
|
3
3
|
export type PendingQuestionStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -250,8 +250,8 @@ export async function startVoiceTurn(opts: VoiceTurnOptions): Promise<VoiceTurnH
|
|
|
250
250
|
// - guardian: permission prompts auto-allow (parity with guardian chat)
|
|
251
251
|
// - everyone else (including unknown): fail-closed strict side-effects
|
|
252
252
|
// with auto-deny confirmations.
|
|
253
|
-
const
|
|
254
|
-
const isGuardian =
|
|
253
|
+
const trustClass = opts.guardianContext?.trustClass;
|
|
254
|
+
const isGuardian = trustClass === 'guardian';
|
|
255
255
|
const forceStrictSideEffects = isGuardian ? undefined : true;
|
|
256
256
|
|
|
257
257
|
// Replace the [CALL_OPENING] marker with a neutral instruction before
|
|
@@ -264,7 +264,7 @@ export async function startVoiceTurn(opts: VoiceTurnOptions): Promise<VoiceTurnH
|
|
|
264
264
|
|
|
265
265
|
// Build the call-control protocol prompt so the model knows how to emit
|
|
266
266
|
// control markers (ASK_GUARDIAN, END_CALL, etc.) and recognize opener turns.
|
|
267
|
-
const isCallerGuardian = opts.guardianContext?.
|
|
267
|
+
const isCallerGuardian = opts.guardianContext?.trustClass === 'guardian';
|
|
268
268
|
|
|
269
269
|
const voiceCallControlPrompt = buildVoiceCallControlPrompt({
|
|
270
270
|
isInbound: opts.isInbound,
|
package/src/cli.ts
CHANGED
|
@@ -492,6 +492,18 @@ export async function startCli(): Promise<void> {
|
|
|
492
492
|
break;
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
+
case 'message_request_complete': {
|
|
496
|
+
// Request-level terminal for inline approval consumption.
|
|
497
|
+
// When no agent turn remains active, clear busy state and re-prompt.
|
|
498
|
+
if (msg.runStillActive !== true) {
|
|
499
|
+
spinner.stop();
|
|
500
|
+
generating = false;
|
|
501
|
+
process.stdout.write('\n\n');
|
|
502
|
+
prompt();
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
|
|
495
507
|
case 'generation_handoff': {
|
|
496
508
|
// The current request's generation is done; show usage and re-prompt.
|
|
497
509
|
// Always clear `generating` — this CLI client's generation is finished
|
|
@@ -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>;
|