@vellumai/assistant 0.4.53 → 0.4.55
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/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +94 -29
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +1 -6
- package/src/__tests__/call-domain.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -1
- package/src/__tests__/claude-code-tool-profiles.test.ts +1 -2
- package/src/__tests__/config-loader-backfill.test.ts +0 -3
- package/src/__tests__/config-schema.test.ts +3 -9
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +60 -24
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +13 -8
- package/src/__tests__/credential-vault-unit.test.ts +28 -12
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -21
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/list-messages-attachments.test.ts +4 -4
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/media-generate-image.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -6
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -27
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +1 -4
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -223
- package/src/__tests__/provider-commit-message-generator.test.ts +1 -4
- package/src/__tests__/provider-fail-open-selection.test.ts +58 -54
- package/src/__tests__/provider-managed-proxy-integration.test.ts +63 -63
- package/src/__tests__/provider-registry-ollama.test.ts +3 -3
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -13
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/session-abort-tool-results.test.ts +0 -1
- package/src/__tests__/session-confirmation-signals.test.ts +0 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +0 -1
- package/src/__tests__/session-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/session-queue.test.ts +2 -4
- package/src/__tests__/session-slash-known.test.ts +0 -1
- package/src/__tests__/session-slash-queue.test.ts +0 -1
- package/src/__tests__/session-slash-unknown.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/swarm-recursion.test.ts +0 -1
- package/src/__tests__/swarm-session-integration.test.ts +0 -1
- package/src/__tests__/swarm-tool.test.ts +0 -1
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trust-store.test.ts +3 -82
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/cli/commands/bash.ts +3 -0
- package/src/cli/commands/doctor.ts +10 -34
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +2 -0
- package/src/cli/reference.ts +1 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +9 -41
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +0 -50
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +14 -8
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/lifecycle.ts +13 -15
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +20 -3
- package/src/daemon/session-slash.ts +2 -2
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +2 -2
- package/src/media/avatar-router.ts +2 -2
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/embedding-backend.ts +30 -26
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.ts +1 -1
- package/src/memory/schema/calls.ts +0 -67
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/provider-behaviors.ts +1 -1
- package/src/permissions/checker.ts +1 -1
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/system-prompt.ts +4 -4
- package/src/prompts/templates/SOUL.md +1 -1
- package/src/providers/managed-proxy/constants.ts +8 -10
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +16 -50
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/auth/__tests__/guard-tests.test.ts +64 -0
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +32 -11
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/session-management-routes.ts +27 -0
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +1 -1
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/bash.ts +33 -0
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/subagent/manager.ts +8 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/credentials/vault.ts +8 -4
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +24 -7
- package/src/util/pricing.ts +0 -38
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +1 -1
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
|
@@ -23,49 +23,6 @@ const GENERATION_TIMEOUT_MS = 5_000;
|
|
|
23
23
|
/** Maximum allowed length for a generated instruction. */
|
|
24
24
|
const MAX_INSTRUCTION_LENGTH = 500;
|
|
25
25
|
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// Channel display label
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
|
-
/** Human-readable label for a channel type. */
|
|
31
|
-
export function channelDisplayLabel(type: string): string {
|
|
32
|
-
switch (type) {
|
|
33
|
-
case "telegram":
|
|
34
|
-
return "Telegram";
|
|
35
|
-
case "email":
|
|
36
|
-
return "Email";
|
|
37
|
-
case "slack":
|
|
38
|
-
return "Slack";
|
|
39
|
-
case "phone":
|
|
40
|
-
return "Voice";
|
|
41
|
-
default:
|
|
42
|
-
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// Deterministic fallback
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Build a deterministic fallback instruction when the LLM is unavailable.
|
|
52
|
-
*/
|
|
53
|
-
export function buildFallbackInstruction(params: {
|
|
54
|
-
contactName?: string;
|
|
55
|
-
channelLabel: string;
|
|
56
|
-
channelHandle?: string;
|
|
57
|
-
shareUrl?: string;
|
|
58
|
-
}): string {
|
|
59
|
-
const contact = params.contactName || "the contact";
|
|
60
|
-
const handle = params.channelHandle
|
|
61
|
-
? ` at ${params.channelHandle}`
|
|
62
|
-
: ` on ${params.channelLabel}`;
|
|
63
|
-
if (params.shareUrl) {
|
|
64
|
-
return `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`;
|
|
65
|
-
}
|
|
66
|
-
return `Tell ${contact} to message me${handle} with the code below.`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
26
|
// ---------------------------------------------------------------------------
|
|
70
27
|
// LLM-powered generation
|
|
71
28
|
// ---------------------------------------------------------------------------
|
|
@@ -88,15 +45,32 @@ export async function generateInviteInstruction(params: {
|
|
|
88
45
|
*/
|
|
89
46
|
shareUrl?: string;
|
|
90
47
|
}): Promise<string> {
|
|
91
|
-
const channelLabel =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
const channelLabel = (() => {
|
|
49
|
+
switch (params.channelType) {
|
|
50
|
+
case "telegram":
|
|
51
|
+
return "Telegram";
|
|
52
|
+
case "email":
|
|
53
|
+
return "Email";
|
|
54
|
+
case "slack":
|
|
55
|
+
return "Slack";
|
|
56
|
+
case "phone":
|
|
57
|
+
return "Voice";
|
|
58
|
+
default:
|
|
59
|
+
return (
|
|
60
|
+
params.channelType.charAt(0).toUpperCase() +
|
|
61
|
+
params.channelType.slice(1)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
const contact = params.contactName || "the contact";
|
|
66
|
+
const handle = params.channelHandle
|
|
67
|
+
? ` at ${params.channelHandle}`
|
|
68
|
+
: ` on ${channelLabel}`;
|
|
69
|
+
const fallback = params.shareUrl
|
|
70
|
+
? `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`
|
|
71
|
+
: `Tell ${contact} to message me${handle} with the code below.`;
|
|
98
72
|
|
|
99
|
-
const resolved = resolveConfiguredProvider();
|
|
73
|
+
const resolved = await resolveConfiguredProvider();
|
|
100
74
|
if (!resolved) {
|
|
101
75
|
log.debug(
|
|
102
76
|
"No provider available for invite instruction generation, using fallback",
|
|
@@ -34,7 +34,6 @@ import {
|
|
|
34
34
|
} from "./channel-invite-transport.js";
|
|
35
35
|
import { generateInviteInstruction } from "./invite-instruction-generator.js";
|
|
36
36
|
import {
|
|
37
|
-
type InviteRedemptionOutcome,
|
|
38
37
|
redeemInvite as redeemInviteTyped,
|
|
39
38
|
redeemVoiceInviteCode as redeemVoiceInviteCodeTyped,
|
|
40
39
|
type VoiceRedemptionOutcome,
|
|
@@ -372,25 +371,6 @@ export function redeemIngressInvite(params: {
|
|
|
372
371
|
return { ok: true, data: inviteToResponse(inv) };
|
|
373
372
|
}
|
|
374
373
|
|
|
375
|
-
// ---------------------------------------------------------------------------
|
|
376
|
-
// Typed invite redemption — preferred entry point for new callers
|
|
377
|
-
// ---------------------------------------------------------------------------
|
|
378
|
-
|
|
379
|
-
export { type InviteRedemptionOutcome } from "./invite-redemption-service.js";
|
|
380
|
-
export { type VoiceRedemptionOutcome } from "./invite-redemption-service.js";
|
|
381
|
-
|
|
382
|
-
export function redeemIngressInviteTyped(params: {
|
|
383
|
-
rawToken: string;
|
|
384
|
-
sourceChannel: string;
|
|
385
|
-
externalUserId?: string;
|
|
386
|
-
externalChatId?: string;
|
|
387
|
-
displayName?: string;
|
|
388
|
-
username?: string;
|
|
389
|
-
assistantId?: string;
|
|
390
|
-
}): InviteRedemptionOutcome {
|
|
391
|
-
return redeemInviteTyped(params);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
374
|
export function redeemVoiceInviteCode(params: {
|
|
395
375
|
assistantId?: string;
|
|
396
376
|
callerExternalUserId: string;
|
|
@@ -101,7 +101,7 @@ export async function handleDeleteAttachment(req: Request): Promise<Response> {
|
|
|
101
101
|
return new Response(null, { status: 204 });
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
function handleGetAttachment(attachmentId: string): Response {
|
|
105
105
|
const attachment = attachmentsStore.getAttachmentById(attachmentId);
|
|
106
106
|
if (!attachment) {
|
|
107
107
|
return httpError("NOT_FOUND", "Attachment not found", 404);
|
|
@@ -148,7 +148,7 @@ export async function handleStartCall(
|
|
|
148
148
|
/**
|
|
149
149
|
* GET /v1/calls/:callSessionId
|
|
150
150
|
*/
|
|
151
|
-
|
|
151
|
+
function handleGetCallStatus(callSessionId: string): Response {
|
|
152
152
|
const result = getCallStatus(callSessionId);
|
|
153
153
|
|
|
154
154
|
if (!result.ok) {
|
|
@@ -352,16 +352,37 @@ export function handleListMessages(
|
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
354
|
} else {
|
|
355
|
-
const linked =
|
|
355
|
+
const linked =
|
|
356
|
+
attachmentsStore.getAttachmentMetadataForMessage(m.id);
|
|
356
357
|
if (linked.length > 0) {
|
|
357
|
-
msgAttachments = linked.map((a) =>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
358
|
+
msgAttachments = linked.map((a) => {
|
|
359
|
+
if (a.mimeType.startsWith("image/")) {
|
|
360
|
+
const full = attachmentsStore.getAttachmentById(a.id);
|
|
361
|
+
return {
|
|
362
|
+
id: a.id,
|
|
363
|
+
filename: a.originalFilename,
|
|
364
|
+
mimeType: a.mimeType,
|
|
365
|
+
sizeBytes: a.sizeBytes,
|
|
366
|
+
kind: a.kind,
|
|
367
|
+
...(full?.dataBase64
|
|
368
|
+
? { data: full.dataBase64 }
|
|
369
|
+
: {}),
|
|
370
|
+
...(a.thumbnailBase64
|
|
371
|
+
? { thumbnailData: a.thumbnailBase64 }
|
|
372
|
+
: {}),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
id: a.id,
|
|
377
|
+
filename: a.originalFilename,
|
|
378
|
+
mimeType: a.mimeType,
|
|
379
|
+
sizeBytes: a.sizeBytes,
|
|
380
|
+
kind: a.kind,
|
|
381
|
+
...(a.thumbnailBase64
|
|
382
|
+
? { thumbnailData: a.thumbnailBase64 }
|
|
383
|
+
: {}),
|
|
384
|
+
};
|
|
385
|
+
});
|
|
365
386
|
}
|
|
366
387
|
}
|
|
367
388
|
}
|
|
@@ -1116,7 +1137,7 @@ export async function handleGetSuggestion(
|
|
|
1116
1137
|
}
|
|
1117
1138
|
|
|
1118
1139
|
// Try LLM suggestion using the configured provider
|
|
1119
|
-
const provider = getConfiguredProvider();
|
|
1140
|
+
const provider = await getConfiguredProvider();
|
|
1120
1141
|
if (provider) {
|
|
1121
1142
|
try {
|
|
1122
1143
|
// Deduplicate concurrent requests
|
|
@@ -1169,7 +1190,7 @@ export async function handleGetSuggestion(
|
|
|
1169
1190
|
* Full-text search across all conversation threads (message content + titles).
|
|
1170
1191
|
* Returns ranked results grouped by conversation, each with matching message excerpts.
|
|
1171
1192
|
*/
|
|
1172
|
-
|
|
1193
|
+
function handleSearchConversations(url: URL): Response {
|
|
1173
1194
|
const query = url.searchParams.get("q") ?? "";
|
|
1174
1195
|
if (!query.trim()) {
|
|
1175
1196
|
return httpError("BAD_REQUEST", "q query parameter is required", 400);
|
|
@@ -684,7 +684,7 @@ async function handleDictation(body: DictationBody): Promise<Response> {
|
|
|
684
684
|
const transcription = expandSnippets(body.transcription, profile.snippets);
|
|
685
685
|
|
|
686
686
|
try {
|
|
687
|
-
const provider = getConfiguredProvider();
|
|
687
|
+
const provider = await getConfiguredProvider();
|
|
688
688
|
if (!provider) {
|
|
689
689
|
log.warn(
|
|
690
690
|
"Dictation: no provider available, using heuristic + raw transcription",
|
|
@@ -843,7 +843,7 @@ async function handleCommandMode(
|
|
|
843
843
|
const maxTokens = Math.max(1024, computeMaxTokens(inputLength));
|
|
844
844
|
|
|
845
845
|
try {
|
|
846
|
-
const provider = getConfiguredProvider();
|
|
846
|
+
const provider = await getConfiguredProvider();
|
|
847
847
|
if (!provider) {
|
|
848
848
|
log.warn("Command mode: no provider available, returning selected text");
|
|
849
849
|
const normalizedText = applyDictionary(
|
|
@@ -27,7 +27,7 @@ type DocumentListRow = Omit<DocumentRow, "content">;
|
|
|
27
27
|
// Shared business logic (used by both message handlers and HTTP routes)
|
|
28
28
|
// ---------------------------------------------------------------------------
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
function saveDocument(params: {
|
|
31
31
|
surfaceId: string;
|
|
32
32
|
conversationId: string;
|
|
33
33
|
title: string;
|
|
@@ -66,7 +66,7 @@ export function saveDocument(params: {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
function loadDocument(surfaceId: string):
|
|
70
70
|
| {
|
|
71
71
|
success: true;
|
|
72
72
|
surfaceId: string;
|
|
@@ -112,7 +112,7 @@ export function loadDocument(surfaceId: string):
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
function listDocuments(conversationId?: string): Array<{
|
|
116
116
|
surfaceId: string;
|
|
117
117
|
conversationId: string;
|
|
118
118
|
title: string;
|
|
@@ -134,7 +134,7 @@ async function searchMemoriesSemantic(
|
|
|
134
134
|
existingIds: Set<string>,
|
|
135
135
|
): Promise<GlobalSearchMemory[]> {
|
|
136
136
|
const config = getConfig();
|
|
137
|
-
const backendStatus = getMemoryBackendStatus(config);
|
|
137
|
+
const backendStatus = await getMemoryBackendStatus(config);
|
|
138
138
|
if (!backendStatus.provider) return [];
|
|
139
139
|
|
|
140
140
|
try {
|
|
@@ -19,7 +19,6 @@ import { getLogger } from "../../util/logger.js";
|
|
|
19
19
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
20
20
|
import { mintCredentialPair } from "../auth/credential-service.js";
|
|
21
21
|
import { httpError } from "../http-errors.js";
|
|
22
|
-
import type { RouteDefinition } from "../http-router.js";
|
|
23
22
|
|
|
24
23
|
/** Bun server shape needed for requestIP -- avoids importing the full Bun type. */
|
|
25
24
|
type ServerWithRequestIP = {
|
|
@@ -154,22 +153,3 @@ export async function handleGuardianBootstrap(
|
|
|
154
153
|
return httpError("INTERNAL_ERROR", "Internal server error", 500);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
|
-
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
// Route definitions
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Guardian bootstrap is a pre-auth endpoint (handled before JWT auth in
|
|
164
|
-
* http-server.ts), so these definitions are exported for completeness but
|
|
165
|
-
* are not added to the authenticated route table.
|
|
166
|
-
*/
|
|
167
|
-
export function guardianBootstrapRouteDefinitions(): RouteDefinition[] {
|
|
168
|
-
return [
|
|
169
|
-
{
|
|
170
|
-
endpoint: "guardian/init",
|
|
171
|
-
method: "POST",
|
|
172
|
-
handler: async ({ req, server }) => handleGuardianBootstrap(req, server),
|
|
173
|
-
},
|
|
174
|
-
];
|
|
175
|
-
}
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import { getLogger } from "../../util/logger.js";
|
|
9
9
|
import { rotateCredentials } from "../auth/credential-service.js";
|
|
10
10
|
import { httpError } from "../http-errors.js";
|
|
11
|
-
import type { RouteDefinition } from "../http-router.js";
|
|
12
11
|
|
|
13
12
|
const log = getLogger("guardian-refresh");
|
|
14
13
|
|
|
@@ -78,22 +77,3 @@ export async function handleGuardianRefresh(req: Request): Promise<Response> {
|
|
|
78
77
|
return httpError("INTERNAL_ERROR", "Internal server error", 500);
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
|
-
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
// Route definitions
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Guardian refresh is a pre-auth endpoint (handled before JWT auth in
|
|
88
|
-
* http-server.ts), so these definitions are exported for completeness but
|
|
89
|
-
* are not added to the authenticated route table.
|
|
90
|
-
*/
|
|
91
|
-
export function guardianRefreshRouteDefinitions(): RouteDefinition[] {
|
|
92
|
-
return [
|
|
93
|
-
{
|
|
94
|
-
endpoint: "guardian/refresh",
|
|
95
|
-
method: "POST",
|
|
96
|
-
handler: async ({ req }) => handleGuardianRefresh(req),
|
|
97
|
-
},
|
|
98
|
-
];
|
|
99
|
-
}
|
|
@@ -73,7 +73,7 @@ export async function handleAddSecret(req: Request): Promise<Response> {
|
|
|
73
73
|
);
|
|
74
74
|
}
|
|
75
75
|
invalidateConfigCache();
|
|
76
|
-
initializeProviders(getConfig());
|
|
76
|
+
await initializeProviders(getConfig());
|
|
77
77
|
log.info({ provider: name }, "API key updated via HTTP");
|
|
78
78
|
return Response.json({ success: true, type, name }, { status: 201 });
|
|
79
79
|
}
|
|
@@ -101,7 +101,7 @@ export async function handleAddSecret(req: Request): Promise<Response> {
|
|
|
101
101
|
}
|
|
102
102
|
upsertCredentialMetadata(service, field, {});
|
|
103
103
|
if (isManagedProxyCredential(service, field)) {
|
|
104
|
-
initializeProviders(getConfig());
|
|
104
|
+
await initializeProviders(getConfig());
|
|
105
105
|
}
|
|
106
106
|
log.info({ service, field }, "Credential added via HTTP");
|
|
107
107
|
return Response.json({ success: true, type, name }, { status: 201 });
|
|
@@ -162,7 +162,7 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
|
|
|
162
162
|
);
|
|
163
163
|
}
|
|
164
164
|
invalidateConfigCache();
|
|
165
|
-
initializeProviders(getConfig());
|
|
165
|
+
await initializeProviders(getConfig());
|
|
166
166
|
log.info({ provider: name }, "API key deleted via HTTP");
|
|
167
167
|
return Response.json({ success: true, type, name });
|
|
168
168
|
}
|
|
@@ -196,7 +196,7 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
|
|
|
196
196
|
}
|
|
197
197
|
deleteCredentialMetadata(service, field);
|
|
198
198
|
if (isManagedProxyCredential(service, field)) {
|
|
199
|
-
initializeProviders(getConfig());
|
|
199
|
+
await initializeProviders(getConfig());
|
|
200
200
|
}
|
|
201
201
|
log.info({ service, field }, "Credential deleted via HTTP");
|
|
202
202
|
return Response.json({ success: true, type, name });
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* POST /v1/conversations/:id/cancel — cancel generation
|
|
8
8
|
* POST /v1/conversations/:id/undo — undo last message
|
|
9
9
|
* POST /v1/conversations/:id/regenerate — regenerate last assistant response
|
|
10
|
+
* POST /v1/sessions/reorder — reorder / pin sessions
|
|
10
11
|
*/
|
|
11
12
|
|
|
13
|
+
import { batchSetDisplayOrders } from "../../memory/conversation-crud.js";
|
|
12
14
|
import { setConversationKeyIfAbsent } from "../../memory/conversation-key-store.js";
|
|
13
15
|
import { getLogger } from "../../util/logger.js";
|
|
14
16
|
import { httpError } from "../http-errors.js";
|
|
@@ -159,5 +161,30 @@ export function sessionManagementRouteDefinitions(
|
|
|
159
161
|
}
|
|
160
162
|
},
|
|
161
163
|
},
|
|
164
|
+
{
|
|
165
|
+
endpoint: "sessions/reorder",
|
|
166
|
+
method: "POST",
|
|
167
|
+
policyKey: "sessions/reorder",
|
|
168
|
+
handler: async ({ req }) => {
|
|
169
|
+
const body = (await req.json()) as {
|
|
170
|
+
updates?: Array<{
|
|
171
|
+
sessionId: string;
|
|
172
|
+
displayOrder?: number;
|
|
173
|
+
isPinned?: boolean;
|
|
174
|
+
}>;
|
|
175
|
+
};
|
|
176
|
+
if (!Array.isArray(body.updates)) {
|
|
177
|
+
return httpError("BAD_REQUEST", "Missing updates array", 400);
|
|
178
|
+
}
|
|
179
|
+
batchSetDisplayOrders(
|
|
180
|
+
body.updates.map((u) => ({
|
|
181
|
+
id: u.sessionId,
|
|
182
|
+
displayOrder: u.displayOrder ?? null,
|
|
183
|
+
isPinned: u.isPinned ?? false,
|
|
184
|
+
})),
|
|
185
|
+
);
|
|
186
|
+
return Response.json({ ok: true });
|
|
187
|
+
},
|
|
188
|
+
},
|
|
162
189
|
];
|
|
163
190
|
}
|
|
@@ -20,7 +20,7 @@ const log = getLogger("trust-rules-routes");
|
|
|
20
20
|
/**
|
|
21
21
|
* GET /v1/trust-rules/manage — list all trust rules.
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
function handleListTrustRules(): Response {
|
|
24
24
|
const rules = getAllRules();
|
|
25
25
|
return Response.json({ type: "trust_rules_list_response", rules });
|
|
26
26
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CredentialBackend interface and adapters — abstracts credential storage
|
|
3
|
+
* behind a unified async API so callers don't need to know which backend
|
|
4
|
+
* (macOS Keychain, encrypted file store, etc.) is in use.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as encryptedStore from "./encrypted-store.js";
|
|
8
|
+
import type { KeychainBrokerClient } from "./keychain-broker-client.js";
|
|
9
|
+
import { createBrokerClient } from "./keychain-broker-client.js";
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Types
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
/** Result of a delete operation — distinguishes success, not-found, and error. */
|
|
16
|
+
export type DeleteResult = "deleted" | "not-found" | "error";
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Interface
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
export interface CredentialBackend {
|
|
23
|
+
/** Human-readable name for logging (e.g. "keychain", "encrypted-store"). */
|
|
24
|
+
readonly name: string;
|
|
25
|
+
|
|
26
|
+
/** Whether this backend is currently reachable. Sync and cheap. */
|
|
27
|
+
isAvailable(): boolean;
|
|
28
|
+
|
|
29
|
+
/** Retrieve a secret. Returns undefined if not found or on error. */
|
|
30
|
+
get(account: string): Promise<string | undefined>;
|
|
31
|
+
|
|
32
|
+
/** Store a secret. Returns true on success. */
|
|
33
|
+
set(account: string, value: string): Promise<boolean>;
|
|
34
|
+
|
|
35
|
+
/** Delete a secret. */
|
|
36
|
+
delete(account: string): Promise<DeleteResult>;
|
|
37
|
+
|
|
38
|
+
/** List all account names. */
|
|
39
|
+
list(): Promise<string[]>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// KeychainBackend
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
export class KeychainBackend implements CredentialBackend {
|
|
47
|
+
readonly name = "keychain";
|
|
48
|
+
|
|
49
|
+
constructor(private readonly client: KeychainBrokerClient) {}
|
|
50
|
+
|
|
51
|
+
isAvailable(): boolean {
|
|
52
|
+
return this.client.isAvailable();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async get(account: string): Promise<string | undefined> {
|
|
56
|
+
try {
|
|
57
|
+
const result = await this.client.get(account);
|
|
58
|
+
if (result == null || !result.found) return undefined;
|
|
59
|
+
return result.value;
|
|
60
|
+
} catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async set(account: string, value: string): Promise<boolean> {
|
|
66
|
+
try {
|
|
67
|
+
const result = await this.client.set(account, value);
|
|
68
|
+
return result.status === "ok";
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async delete(account: string): Promise<DeleteResult> {
|
|
75
|
+
try {
|
|
76
|
+
const ok = await this.client.del(account);
|
|
77
|
+
// The keychain broker returns a boolean — it does not distinguish
|
|
78
|
+
// "not found" from a genuine error, so we map false → "error".
|
|
79
|
+
return ok ? "deleted" : "error";
|
|
80
|
+
} catch {
|
|
81
|
+
return "error";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async list(): Promise<string[]> {
|
|
86
|
+
try {
|
|
87
|
+
return await this.client.list();
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// EncryptedStoreBackend
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
export class EncryptedStoreBackend implements CredentialBackend {
|
|
99
|
+
readonly name = "encrypted-store";
|
|
100
|
+
|
|
101
|
+
isAvailable(): boolean {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async get(account: string): Promise<string | undefined> {
|
|
106
|
+
try {
|
|
107
|
+
return encryptedStore.getKey(account);
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async set(account: string, value: string): Promise<boolean> {
|
|
114
|
+
try {
|
|
115
|
+
return encryptedStore.setKey(account, value);
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async delete(account: string): Promise<DeleteResult> {
|
|
122
|
+
try {
|
|
123
|
+
return encryptedStore.deleteKey(account);
|
|
124
|
+
} catch {
|
|
125
|
+
return "error";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async list(): Promise<string[]> {
|
|
130
|
+
try {
|
|
131
|
+
return encryptedStore.listKeys();
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Factory functions
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
export function createKeychainBackend(): KeychainBackend {
|
|
143
|
+
return new KeychainBackend(createBrokerClient());
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function createEncryptedStoreBackend(): EncryptedStoreBackend {
|
|
147
|
+
return new EncryptedStoreBackend();
|
|
148
|
+
}
|
package/src/security/oauth2.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface OAuth2Config {
|
|
|
45
45
|
* How the client authenticates at the token endpoint when a clientSecret is present.
|
|
46
46
|
* - `client_secret_post`: Send client_id and client_secret in the POST body (default).
|
|
47
47
|
* - `client_secret_basic`: Send an HTTP Basic Auth header with base64(client_id:client_secret).
|
|
48
|
-
* Defaults to `client_secret_post
|
|
48
|
+
* Defaults to `client_secret_post`.
|
|
49
49
|
*/
|
|
50
50
|
tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
|
|
51
51
|
}
|
|
@@ -131,7 +131,7 @@ export interface AllowlistValidationError {
|
|
|
131
131
|
* Validate all regex patterns in an allowlist config without loading them.
|
|
132
132
|
* Returns an array of validation errors (empty = all valid).
|
|
133
133
|
*/
|
|
134
|
-
|
|
134
|
+
function validateAllowlist(
|
|
135
135
|
config: AllowlistConfig,
|
|
136
136
|
): AllowlistValidationError[] {
|
|
137
137
|
const errors: AllowlistValidationError[] = [];
|