@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -33,10 +33,6 @@ const WEEKDAY_NAMES = [
|
|
|
33
33
|
"Friday",
|
|
34
34
|
"Saturday",
|
|
35
35
|
] as const;
|
|
36
|
-
const TIMEZONE_SUBJECT_LINE_RE = /^\s*-\s*time\s*zone\s*:\s*(.+)$/i;
|
|
37
|
-
const TIMEZONE_SUBJECT_COMPACT_RE = /^\s*-\s*timezone\s*:\s*(.+)$/i;
|
|
38
|
-
const TIMEZONE_TOKEN_RE =
|
|
39
|
-
/\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\d{1,2}(?::?\d{2})?)?)\b/gi;
|
|
40
36
|
const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
|
|
41
37
|
|
|
42
38
|
function normalizeOffsetToken(offsetToken: string): string {
|
|
@@ -111,6 +107,17 @@ function canonicalizeTimeZone(timeZone: string): string | null {
|
|
|
111
107
|
return null;
|
|
112
108
|
}
|
|
113
109
|
}
|
|
110
|
+
// Check abbreviation mapping before Intl (many abbreviations are not recognized by Intl)
|
|
111
|
+
const abbrIana = TIMEZONE_ABBREVIATIONS[trimmed.toUpperCase()];
|
|
112
|
+
if (abbrIana) {
|
|
113
|
+
try {
|
|
114
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
115
|
+
timeZone: abbrIana,
|
|
116
|
+
}).resolvedOptions().timeZone;
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
114
121
|
try {
|
|
115
122
|
return new Intl.DateTimeFormat("en-US", {
|
|
116
123
|
timeZone: trimmed,
|
|
@@ -120,47 +127,123 @@ function canonicalizeTimeZone(timeZone: string): string | null {
|
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Common timezone abbreviation → IANA identifier mapping.
|
|
132
|
+
* Used as a fallback when `Intl.DateTimeFormat` does not recognize the abbreviation.
|
|
133
|
+
*/
|
|
134
|
+
const TIMEZONE_ABBREVIATIONS: Record<string, string> = {
|
|
135
|
+
// North America
|
|
136
|
+
PST: "America/Los_Angeles",
|
|
137
|
+
PDT: "America/Los_Angeles",
|
|
138
|
+
MST: "America/Denver",
|
|
139
|
+
MDT: "America/Denver",
|
|
140
|
+
CST: "America/Chicago",
|
|
141
|
+
CDT: "America/Chicago",
|
|
142
|
+
EST: "America/New_York",
|
|
143
|
+
EDT: "America/New_York",
|
|
144
|
+
AKST: "America/Anchorage",
|
|
145
|
+
AKDT: "America/Anchorage",
|
|
146
|
+
HST: "Pacific/Honolulu",
|
|
147
|
+
AST: "America/Puerto_Rico",
|
|
148
|
+
NST: "America/St_Johns",
|
|
149
|
+
NDT: "America/St_Johns",
|
|
150
|
+
// Europe
|
|
151
|
+
BST: "Europe/London",
|
|
152
|
+
CET: "Europe/Paris",
|
|
153
|
+
CEST: "Europe/Paris",
|
|
154
|
+
EET: "Europe/Athens",
|
|
155
|
+
EEST: "Europe/Athens",
|
|
156
|
+
WEST: "Europe/Lisbon",
|
|
157
|
+
MSK: "Europe/Moscow",
|
|
158
|
+
// Asia / Oceania
|
|
159
|
+
JST: "Asia/Tokyo",
|
|
160
|
+
KST: "Asia/Seoul",
|
|
161
|
+
HKT: "Asia/Hong_Kong",
|
|
162
|
+
SGT: "Asia/Singapore",
|
|
163
|
+
WIB: "Asia/Jakarta",
|
|
164
|
+
PHT: "Asia/Manila",
|
|
165
|
+
PKT: "Asia/Karachi",
|
|
166
|
+
NPT: "Asia/Kathmandu",
|
|
167
|
+
AEST: "Australia/Sydney",
|
|
168
|
+
AEDT: "Australia/Sydney",
|
|
169
|
+
ACST: "Australia/Adelaide",
|
|
170
|
+
ACDT: "Australia/Adelaide",
|
|
171
|
+
AWST: "Australia/Perth",
|
|
172
|
+
NZST: "Pacific/Auckland",
|
|
173
|
+
NZDT: "Pacific/Auckland",
|
|
174
|
+
// South America
|
|
175
|
+
BRT: "America/Sao_Paulo",
|
|
176
|
+
};
|
|
131
177
|
|
|
132
178
|
/**
|
|
133
|
-
*
|
|
179
|
+
* Regex matching IANA timezone identifiers (e.g. "America/New_York"),
|
|
180
|
+
* UTC/GMT offset tokens (e.g. "UTC+5", "GMT-8:30"), and common
|
|
181
|
+
* timezone abbreviations (e.g. "PST", "EST", "JST").
|
|
134
182
|
*
|
|
135
|
-
*
|
|
136
|
-
* full profile body for valid IANA timezone identifiers.
|
|
183
|
+
* Abbreviation alternation is built from `TIMEZONE_ABBREVIATIONS` keys.
|
|
137
184
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
185
|
+
const TIMEZONE_ABBR_ALTERNATION = Object.keys(TIMEZONE_ABBREVIATIONS).join("|");
|
|
186
|
+
const TIMEZONE_TOKEN_RE = new RegExp(
|
|
187
|
+
`\\b(?:[A-Za-z][A-Za-z0-9_+-]*(?:/[A-Za-z0-9_+-]+)+|(?:UTC|GMT)(?:[+-]\\d{1,2}(?::?\\d{2})?)?|(?:${TIMEZONE_ABBR_ALTERNATION}))\\b`,
|
|
188
|
+
"gi",
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Extract the user's timezone from V2 memory recall injected text.
|
|
193
|
+
*
|
|
194
|
+
* Scans the `<user_identity>` section (if present) for lines containing
|
|
195
|
+
* "timezone" and tries to resolve an IANA identifier. Falls back to
|
|
196
|
+
* scanning the full text body.
|
|
197
|
+
*/
|
|
198
|
+
export function extractUserTimeZoneFromRecall(
|
|
199
|
+
injectedText: string,
|
|
140
200
|
): string | null {
|
|
141
|
-
|
|
142
|
-
if (trimmed.length === 0) return null;
|
|
201
|
+
if (!injectedText || injectedText.trim().length === 0) return null;
|
|
143
202
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
203
|
+
// Prefer lines inside <user_identity> that mention "timezone"
|
|
204
|
+
const identityMatch = injectedText.match(
|
|
205
|
+
/<user_identity>([\s\S]*?)<\/user_identity>/,
|
|
206
|
+
);
|
|
207
|
+
if (identityMatch) {
|
|
208
|
+
const identityBlock = identityMatch[1];
|
|
209
|
+
for (const line of identityBlock.split("\n")) {
|
|
210
|
+
if (/time\s*zone/i.test(line)) {
|
|
211
|
+
for (const token of extractTimeZoneCandidates(line)) {
|
|
212
|
+
const canonical = canonicalizeTimeZone(token);
|
|
213
|
+
if (canonical) return canonical;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
151
216
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
for (const text of candidateTexts) {
|
|
156
|
-
for (const token of extractTimeZoneCandidates(text)) {
|
|
217
|
+
// Scan full identity block for any timezone token
|
|
218
|
+
for (const token of extractTimeZoneCandidates(identityBlock)) {
|
|
157
219
|
const canonical = canonicalizeTimeZone(token);
|
|
158
220
|
if (canonical) return canonical;
|
|
159
221
|
}
|
|
160
222
|
}
|
|
223
|
+
|
|
224
|
+
// Fallback: scan entire injected text for timezone tokens in
|
|
225
|
+
// lines that mention "timezone"
|
|
226
|
+
for (const line of injectedText.split("\n")) {
|
|
227
|
+
if (/time\s*zone/i.test(line)) {
|
|
228
|
+
for (const token of extractTimeZoneCandidates(line)) {
|
|
229
|
+
const canonical = canonicalizeTimeZone(token);
|
|
230
|
+
if (canonical) return canonical;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
161
235
|
return null;
|
|
162
236
|
}
|
|
163
237
|
|
|
238
|
+
function extractTimeZoneCandidates(text: string): string[] {
|
|
239
|
+
const matches = (text.match(TIMEZONE_TOKEN_RE) ?? [])
|
|
240
|
+
.map((token) => token.trim())
|
|
241
|
+
.filter((token) => token.length > 0);
|
|
242
|
+
const ianaTokens = matches.filter((token) => token.includes("/"));
|
|
243
|
+
const offsetTokens = matches.filter((token) => !token.includes("/"));
|
|
244
|
+
return [...ianaTokens, ...offsetTokens];
|
|
245
|
+
}
|
|
246
|
+
|
|
164
247
|
/**
|
|
165
248
|
* Get the local date parts for a given instant in the specified timezone.
|
|
166
249
|
*/
|
|
@@ -161,7 +161,7 @@ export async function handleIngressConfig(
|
|
|
161
161
|
// Best-effort Twilio webhook reconciliation: when ingress is being
|
|
162
162
|
// enabled/updated and Twilio numbers are assigned with valid credentials,
|
|
163
163
|
// push the new webhook URLs to Twilio so calls route correctly.
|
|
164
|
-
if (isEnabled && hasTwilioCredentials()) {
|
|
164
|
+
if (isEnabled && (await hasTwilioCredentials())) {
|
|
165
165
|
const currentConfig = loadRawConfig();
|
|
166
166
|
const twilioConfig = (currentConfig?.twilio ?? {}) as Record<
|
|
167
167
|
string,
|
|
@@ -188,7 +188,7 @@ export async function handleIngressConfig(
|
|
|
188
188
|
|
|
189
189
|
if (assignedNumbers.size > 0) {
|
|
190
190
|
const { accountSid: acctSid, authToken: acctToken } =
|
|
191
|
-
getTwilioCredentials();
|
|
191
|
+
await getTwilioCredentials();
|
|
192
192
|
// Fire-and-forget: webhook sync failure must not block the ingress save.
|
|
193
193
|
// Reconcile every assigned number so assistant-scoped mappings do not
|
|
194
194
|
// retain stale Twilio webhook URLs after ingress URL changes.
|
|
@@ -13,7 +13,7 @@ import { getConnectionByProvider } from "../../oauth/oauth-store.js";
|
|
|
13
13
|
import { credentialKey } from "../../security/credential-key.js";
|
|
14
14
|
import {
|
|
15
15
|
deleteSecureKeyAsync,
|
|
16
|
-
|
|
16
|
+
getSecureKeyAsync,
|
|
17
17
|
setSecureKeyAsync,
|
|
18
18
|
} from "../../security/secure-keys.js";
|
|
19
19
|
import {
|
|
@@ -39,13 +39,13 @@ export interface SlackChannelConfigResult {
|
|
|
39
39
|
|
|
40
40
|
// -- Business logic --
|
|
41
41
|
|
|
42
|
-
export function getSlackChannelConfig(): SlackChannelConfigResult {
|
|
43
|
-
const hasBotToken = !!
|
|
42
|
+
export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult> {
|
|
43
|
+
const hasBotToken = !!(await getSecureKeyAsync(
|
|
44
44
|
credentialKey("slack_channel", "bot_token"),
|
|
45
|
-
);
|
|
46
|
-
const hasAppToken = !!
|
|
45
|
+
));
|
|
46
|
+
const hasAppToken = !!(await getSecureKeyAsync(
|
|
47
47
|
credentialKey("slack_channel", "app_token"),
|
|
48
|
-
);
|
|
48
|
+
));
|
|
49
49
|
const conn = getConnectionByProvider("slack_channel");
|
|
50
50
|
const connected =
|
|
51
51
|
!!(conn && conn.status === "active") && hasBotToken && hasAppToken;
|
|
@@ -91,13 +91,17 @@ export async function setSlackChannelConfig(
|
|
|
91
91
|
user?: string;
|
|
92
92
|
};
|
|
93
93
|
if (!data.ok) {
|
|
94
|
-
const
|
|
95
|
-
|
|
94
|
+
const errHasBotToken = !!(await getSecureKeyAsync(
|
|
95
|
+
credentialKey("slack_channel", "bot_token"),
|
|
96
|
+
));
|
|
97
|
+
const errHasAppToken = !!(await getSecureKeyAsync(
|
|
98
|
+
credentialKey("slack_channel", "app_token"),
|
|
99
|
+
));
|
|
96
100
|
return {
|
|
97
101
|
success: false,
|
|
98
|
-
hasBotToken:
|
|
99
|
-
hasAppToken:
|
|
100
|
-
connected:
|
|
102
|
+
hasBotToken: errHasBotToken,
|
|
103
|
+
hasAppToken: errHasAppToken,
|
|
104
|
+
connected: errHasBotToken && errHasAppToken,
|
|
101
105
|
error: `Slack API validation failed: ${
|
|
102
106
|
data.error ?? "unknown error"
|
|
103
107
|
}`,
|
|
@@ -111,13 +115,17 @@ export async function setSlackChannelConfig(
|
|
|
111
115
|
};
|
|
112
116
|
} catch (err) {
|
|
113
117
|
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
-
const
|
|
115
|
-
|
|
118
|
+
const errHasBotToken = !!(await getSecureKeyAsync(
|
|
119
|
+
credentialKey("slack_channel", "bot_token"),
|
|
120
|
+
));
|
|
121
|
+
const errHasAppToken = !!(await getSecureKeyAsync(
|
|
122
|
+
credentialKey("slack_channel", "app_token"),
|
|
123
|
+
));
|
|
116
124
|
return {
|
|
117
125
|
success: false,
|
|
118
|
-
hasBotToken:
|
|
119
|
-
hasAppToken:
|
|
120
|
-
connected:
|
|
126
|
+
hasBotToken: errHasBotToken,
|
|
127
|
+
hasAppToken: errHasAppToken,
|
|
128
|
+
connected: errHasBotToken && errHasAppToken,
|
|
121
129
|
error: `Failed to validate bot token: ${message}`,
|
|
122
130
|
};
|
|
123
131
|
}
|
|
@@ -127,13 +135,17 @@ export async function setSlackChannelConfig(
|
|
|
127
135
|
botToken,
|
|
128
136
|
);
|
|
129
137
|
if (!stored) {
|
|
130
|
-
const
|
|
131
|
-
|
|
138
|
+
const errHasBotToken = !!(await getSecureKeyAsync(
|
|
139
|
+
credentialKey("slack_channel", "bot_token"),
|
|
140
|
+
));
|
|
141
|
+
const errHasAppToken = !!(await getSecureKeyAsync(
|
|
142
|
+
credentialKey("slack_channel", "app_token"),
|
|
143
|
+
));
|
|
132
144
|
return {
|
|
133
145
|
success: false,
|
|
134
|
-
hasBotToken:
|
|
135
|
-
hasAppToken:
|
|
136
|
-
connected:
|
|
146
|
+
hasBotToken: errHasBotToken,
|
|
147
|
+
hasAppToken: errHasAppToken,
|
|
148
|
+
connected: errHasBotToken && errHasAppToken,
|
|
137
149
|
error: "Failed to store bot token in secure storage",
|
|
138
150
|
};
|
|
139
151
|
}
|
|
@@ -161,13 +173,17 @@ export async function setSlackChannelConfig(
|
|
|
161
173
|
// Validate and store app token
|
|
162
174
|
if (appToken) {
|
|
163
175
|
if (!appToken.startsWith("xapp-")) {
|
|
164
|
-
const
|
|
165
|
-
|
|
176
|
+
const errHasBotToken = !!(await getSecureKeyAsync(
|
|
177
|
+
credentialKey("slack_channel", "bot_token"),
|
|
178
|
+
));
|
|
179
|
+
const errHasAppToken = !!(await getSecureKeyAsync(
|
|
180
|
+
credentialKey("slack_channel", "app_token"),
|
|
181
|
+
));
|
|
166
182
|
return {
|
|
167
183
|
success: false,
|
|
168
|
-
hasBotToken:
|
|
169
|
-
hasAppToken:
|
|
170
|
-
connected:
|
|
184
|
+
hasBotToken: errHasBotToken,
|
|
185
|
+
hasAppToken: errHasAppToken,
|
|
186
|
+
connected: errHasBotToken && errHasAppToken,
|
|
171
187
|
error: 'Invalid app token: must start with "xapp-"',
|
|
172
188
|
};
|
|
173
189
|
}
|
|
@@ -177,13 +193,17 @@ export async function setSlackChannelConfig(
|
|
|
177
193
|
appToken,
|
|
178
194
|
);
|
|
179
195
|
if (!stored) {
|
|
180
|
-
const
|
|
181
|
-
|
|
196
|
+
const errHasBotToken = !!(await getSecureKeyAsync(
|
|
197
|
+
credentialKey("slack_channel", "bot_token"),
|
|
198
|
+
));
|
|
199
|
+
const errHasAppToken = !!(await getSecureKeyAsync(
|
|
200
|
+
credentialKey("slack_channel", "app_token"),
|
|
201
|
+
));
|
|
182
202
|
return {
|
|
183
203
|
success: false,
|
|
184
|
-
hasBotToken:
|
|
185
|
-
hasAppToken:
|
|
186
|
-
connected:
|
|
204
|
+
hasBotToken: errHasBotToken,
|
|
205
|
+
hasAppToken: errHasAppToken,
|
|
206
|
+
connected: errHasBotToken && errHasAppToken,
|
|
187
207
|
error: "Failed to store app token in secure storage",
|
|
188
208
|
};
|
|
189
209
|
}
|
|
@@ -191,12 +211,12 @@ export async function setSlackChannelConfig(
|
|
|
191
211
|
upsertCredentialMetadata("slack_channel", "app_token", {});
|
|
192
212
|
}
|
|
193
213
|
|
|
194
|
-
const hasBotToken = !!
|
|
214
|
+
const hasBotToken = !!(await getSecureKeyAsync(
|
|
195
215
|
credentialKey("slack_channel", "bot_token"),
|
|
196
|
-
);
|
|
197
|
-
const hasAppToken = !!
|
|
216
|
+
));
|
|
217
|
+
const hasAppToken = !!(await getSecureKeyAsync(
|
|
198
218
|
credentialKey("slack_channel", "app_token"),
|
|
199
|
-
);
|
|
219
|
+
));
|
|
200
220
|
|
|
201
221
|
if (hasBotToken && !hasAppToken) {
|
|
202
222
|
warning =
|
|
@@ -237,12 +257,12 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
237
257
|
|
|
238
258
|
if (r1 === "error" || r2 === "error") {
|
|
239
259
|
// Check each key individually so partial deletions report accurate status.
|
|
240
|
-
const hasBotToken = !!
|
|
260
|
+
const hasBotToken = !!(await getSecureKeyAsync(
|
|
241
261
|
credentialKey("slack_channel", "bot_token"),
|
|
242
|
-
);
|
|
243
|
-
const hasAppToken = !!
|
|
262
|
+
));
|
|
263
|
+
const hasAppToken = !!(await getSecureKeyAsync(
|
|
244
264
|
credentialKey("slack_channel", "app_token"),
|
|
245
|
-
);
|
|
265
|
+
));
|
|
246
266
|
return {
|
|
247
267
|
success: false,
|
|
248
268
|
hasBotToken,
|
|
@@ -16,7 +16,7 @@ import { getConnectionByProvider } from "../../oauth/oauth-store.js";
|
|
|
16
16
|
import { credentialKey } from "../../security/credential-key.js";
|
|
17
17
|
import {
|
|
18
18
|
deleteSecureKeyAsync,
|
|
19
|
-
|
|
19
|
+
getSecureKeyAsync,
|
|
20
20
|
setSecureKeyAsync,
|
|
21
21
|
} from "../../security/secure-keys.js";
|
|
22
22
|
import { getTelegramBotUsername } from "../../telegram/bot-username.js";
|
|
@@ -65,11 +65,13 @@ export type TelegramConfigResult = Omit<TelegramConfigResponse, "type">;
|
|
|
65
65
|
|
|
66
66
|
// -- Extracted business logic functions --
|
|
67
67
|
|
|
68
|
-
export function getTelegramConfig(): TelegramConfigResult {
|
|
69
|
-
const hasBotToken = !!
|
|
70
|
-
|
|
68
|
+
export async function getTelegramConfig(): Promise<TelegramConfigResult> {
|
|
69
|
+
const hasBotToken = !!(await getSecureKeyAsync(
|
|
70
|
+
credentialKey("telegram", "bot_token"),
|
|
71
|
+
));
|
|
72
|
+
const hasWebhookSecret = !!(await getSecureKeyAsync(
|
|
71
73
|
credentialKey("telegram", "webhook_secret"),
|
|
72
|
-
);
|
|
74
|
+
));
|
|
73
75
|
const conn = getConnectionByProvider("telegram");
|
|
74
76
|
const connected = !!(conn && conn.status === "active");
|
|
75
77
|
const botUsername = getTelegramBotUsername();
|
|
@@ -89,7 +91,8 @@ export async function setTelegramConfig(
|
|
|
89
91
|
// Track provenance so we only rollback tokens that were freshly provided.
|
|
90
92
|
const isNewToken = !!botToken;
|
|
91
93
|
const resolvedToken =
|
|
92
|
-
botToken ||
|
|
94
|
+
botToken ||
|
|
95
|
+
(await getSecureKeyAsync(credentialKey("telegram", "bot_token")));
|
|
93
96
|
if (!resolvedToken) {
|
|
94
97
|
return {
|
|
95
98
|
success: false,
|
|
@@ -166,9 +169,9 @@ export async function setTelegramConfig(
|
|
|
166
169
|
invalidateConfigCache();
|
|
167
170
|
|
|
168
171
|
// Ensure webhook secret exists (generate if missing)
|
|
169
|
-
let hasWebhookSecret = !!
|
|
172
|
+
let hasWebhookSecret = !!(await getSecureKeyAsync(
|
|
170
173
|
credentialKey("telegram", "webhook_secret"),
|
|
171
|
-
);
|
|
174
|
+
));
|
|
172
175
|
if (!hasWebhookSecret) {
|
|
173
176
|
const { randomUUID } = await import("node:crypto");
|
|
174
177
|
const webhookSecret = randomUUID();
|
|
@@ -241,7 +244,9 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
241
244
|
// The gateway reconcile short-circuits when credentials are absent,
|
|
242
245
|
// so we must call the Telegram API directly while the token is still
|
|
243
246
|
// available.
|
|
244
|
-
const botToken =
|
|
247
|
+
const botToken = await getSecureKeyAsync(
|
|
248
|
+
credentialKey("telegram", "bot_token"),
|
|
249
|
+
);
|
|
245
250
|
if (botToken) {
|
|
246
251
|
try {
|
|
247
252
|
await fetch(`https://api.telegram.org/bot${botToken}/deleteWebhook`);
|
|
@@ -260,10 +265,12 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
260
265
|
|
|
261
266
|
if (r1 === "error" || r2 === "error") {
|
|
262
267
|
// Check each key individually so partial deletions report accurate status.
|
|
263
|
-
const hasBotToken = !!
|
|
264
|
-
|
|
268
|
+
const hasBotToken = !!(await getSecureKeyAsync(
|
|
269
|
+
credentialKey("telegram", "bot_token"),
|
|
270
|
+
));
|
|
271
|
+
const hasWebhookSecret = !!(await getSecureKeyAsync(
|
|
265
272
|
credentialKey("telegram", "webhook_secret"),
|
|
266
|
-
);
|
|
273
|
+
));
|
|
267
274
|
return {
|
|
268
275
|
success: false,
|
|
269
276
|
hasBotToken,
|
|
@@ -297,7 +304,9 @@ export async function clearTelegramConfig(): Promise<TelegramConfigResult> {
|
|
|
297
304
|
export async function setTelegramCommands(
|
|
298
305
|
commands?: Array<{ command: string; description: string }>,
|
|
299
306
|
): Promise<TelegramConfigResult> {
|
|
300
|
-
const storedToken =
|
|
307
|
+
const storedToken = await getSecureKeyAsync(
|
|
308
|
+
credentialKey("telegram", "bot_token"),
|
|
309
|
+
);
|
|
301
310
|
if (!storedToken) {
|
|
302
311
|
return {
|
|
303
312
|
success: false,
|
|
@@ -398,7 +407,7 @@ export async function handleTelegramConfig(
|
|
|
398
407
|
let result: TelegramConfigResult;
|
|
399
408
|
|
|
400
409
|
if (msg.action === "get") {
|
|
401
|
-
result = getTelegramConfig();
|
|
410
|
+
result = await getTelegramConfig();
|
|
402
411
|
} else if (msg.action === "set") {
|
|
403
412
|
result = await setTelegramConfig(msg.botToken);
|
|
404
413
|
} else if (msg.action === "clear") {
|