@vellumai/assistant 0.5.6 → 0.5.7
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/.env.example +16 -2
- package/ARCHITECTURE.md +6 -75
- package/Dockerfile +1 -1
- package/README.md +0 -2
- package/bun.lock +0 -414
- package/docs/architecture/keychain-broker.md +45 -240
- package/docs/architecture/security.md +0 -17
- package/docs/credential-execution-service.md +2 -2
- package/node_modules/@vellumai/ces-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +119 -0
- package/node_modules/@vellumai/credential-storage/package.json +1 -0
- package/node_modules/@vellumai/egress-proxy/package.json +1 -0
- package/package.json +2 -3
- package/src/__tests__/actor-token-service.test.ts +0 -114
- package/src/__tests__/assistant-feature-flags-integration.test.ts +30 -29
- package/src/__tests__/browser-skill-endstate.test.ts +6 -5
- package/src/__tests__/btw-routes.test.ts +0 -39
- package/src/__tests__/call-domain.test.ts +0 -128
- package/src/__tests__/ces-rpc-credential-backend.test.ts +199 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -5
- package/src/__tests__/channel-readiness-service.test.ts +1 -60
- package/src/__tests__/checker.test.ts +4 -2
- package/src/__tests__/cli-command-risk-guard.test.ts +112 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -5
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-skill-tools.test.ts +0 -54
- package/src/__tests__/conversation-title-service.test.ts +87 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +28 -14
- package/src/__tests__/credential-execution-managed-contract.test.ts +33 -18
- package/src/__tests__/credential-security-e2e.test.ts +0 -66
- package/src/__tests__/credential-security-invariants.test.ts +4 -45
- package/src/__tests__/credentials-cli.test.ts +78 -0
- package/src/__tests__/db-migration-rollback.test.ts +2015 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +34 -143
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -4
- package/src/__tests__/guardian-routing-state.test.ts +0 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -7
- package/src/__tests__/http-user-message-parity.test.ts +3 -103
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -4
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -8
- package/src/__tests__/intent-routing.test.ts +0 -13
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +178 -0
- package/src/__tests__/keychain-broker-client.test.ts +161 -22
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +150 -0
- package/src/__tests__/migration-export-http.test.ts +2 -2
- package/src/__tests__/migration-import-commit-http.test.ts +2 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +2 -2
- package/src/__tests__/migration-validate-http.test.ts +2 -2
- package/src/__tests__/non-member-access-request.test.ts +0 -5
- package/src/__tests__/notification-decision-fallback.test.ts +4 -0
- package/src/__tests__/notification-decision-identity.test.ts +4 -0
- package/src/__tests__/permission-types.test.ts +1 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +5 -6
- package/src/__tests__/qdrant-manager.test.ts +28 -2
- package/src/__tests__/registry.test.ts +0 -6
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -4
- package/src/__tests__/secret-routes-managed-proxy.test.ts +0 -4
- package/src/__tests__/secure-keys.test.ts +83 -263
- package/src/__tests__/shell-identity.test.ts +96 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +22 -14
- package/src/__tests__/skill-feature-flags.test.ts +46 -45
- package/src/__tests__/skill-load-feature-flag.test.ts +7 -10
- package/src/__tests__/skill-load-inline-command.test.ts +8 -12
- package/src/__tests__/skill-load-inline-includes.test.ts +6 -10
- package/src/__tests__/skill-load-tool.test.ts +0 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +33 -29
- package/src/__tests__/skills.test.ts +0 -2
- package/src/__tests__/slack-inbound-verification.test.ts +0 -4
- package/src/__tests__/suggestion-routes.test.ts +1 -32
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +5 -3
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -5
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -4
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +6 -9
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -6
- package/src/__tests__/workspace-migration-015-migrate-credentials-to-keychain.test.ts +252 -0
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +218 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +1009 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +114 -0
- package/src/calls/audio-store.test.ts +97 -0
- package/src/calls/audio-store.ts +205 -0
- package/src/calls/call-controller.ts +85 -7
- package/src/calls/call-domain.ts +3 -0
- package/src/calls/call-store.ts +10 -3
- package/src/calls/fish-audio-client.ts +117 -0
- package/src/calls/relay-server.ts +27 -0
- package/src/calls/twilio-routes.ts +2 -1
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-ingress-preflight.ts +0 -42
- package/src/calls/voice-quality.ts +26 -5
- package/src/calls/voice-session-bridge.ts +6 -12
- package/src/cli/commands/config.ts +1 -4
- package/src/cli/commands/credentials.ts +34 -4
- package/src/cli/commands/oauth/index.ts +7 -0
- package/src/cli/commands/oauth/platform.ts +179 -0
- package/src/cli/commands/platform.ts +3 -3
- package/src/config/assistant-feature-flags.ts +186 -5
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/phone-calls/TOOLS.json +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +42 -0
- package/src/config/bundled-tool-registry.ts +1 -11
- package/src/config/env-registry.ts +1 -1
- package/src/config/env.ts +8 -14
- package/src/config/feature-flag-registry.json +48 -8
- package/src/config/loader.ts +98 -31
- package/src/config/schema.ts +4 -13
- package/src/config/schemas/calls.ts +13 -0
- package/src/config/schemas/fish-audio.ts +39 -0
- package/src/config/schemas/security.ts +0 -4
- package/src/config/types.ts +0 -1
- package/src/contacts/contact-store.ts +39 -0
- package/src/contacts/types.ts +2 -0
- package/src/credential-execution/approval-bridge.ts +1 -0
- package/src/credential-execution/executable-discovery.ts +28 -4
- package/src/credential-execution/feature-gates.ts +16 -0
- package/src/credential-execution/process-manager.ts +38 -0
- package/src/daemon/assistant-attachments.ts +9 -0
- package/src/daemon/config-watcher.ts +5 -0
- package/src/daemon/conversation-tool-setup.ts +0 -105
- package/src/daemon/conversation.ts +10 -1
- package/src/daemon/handlers/config-vercel.ts +92 -0
- package/src/daemon/handlers/skills.ts +2 -15
- package/src/daemon/install-symlink.ts +195 -0
- package/src/daemon/lifecycle.ts +227 -51
- package/src/daemon/message-types/conversations.ts +3 -4
- package/src/daemon/message-types/diagnostics.ts +3 -22
- package/src/daemon/message-types/messages.ts +0 -2
- package/src/daemon/message-types/upgrades.ts +8 -0
- package/src/daemon/server.ts +30 -92
- package/src/events/domain-events.ts +2 -1
- package/src/inbound/platform-callback-registration.ts +3 -3
- package/src/instrument.ts +8 -5
- package/src/memory/conversation-title-service.ts +50 -1
- package/src/memory/db-init.ts +12 -0
- package/src/memory/items-extractor.ts +15 -1
- package/src/memory/job-handlers/conversation-starters.ts +4 -1
- package/src/memory/jobs-store.ts +30 -5
- package/src/memory/jobs-worker.ts +31 -7
- package/src/memory/migrations/001-job-deferrals.ts +19 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +10 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +76 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +50 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +10 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +34 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +26 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +10 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +17 -0
- package/src/memory/migrations/019-notification-tables-schema-migration.ts +12 -0
- package/src/memory/migrations/020-rename-macos-ios-channel-to-vellum.ts +121 -0
- package/src/memory/migrations/024-embedding-vector-blob.ts +74 -0
- package/src/memory/migrations/026a-embeddings-nullable-vector-json.ts +82 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +11 -0
- package/src/memory/migrations/116-messages-fts.ts +106 -1
- package/src/memory/migrations/126-backfill-guardian-principal-id.ts +52 -0
- package/src/memory/migrations/127-guardian-principal-id-not-null.ts +77 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +13 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +20 -0
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +13 -0
- package/src/memory/migrations/141-rename-verification-table.ts +54 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +25 -0
- package/src/memory/migrations/143-rename-guardian-verification-values.ts +35 -0
- package/src/memory/migrations/144-rename-voice-to-phone.ts +136 -0
- package/src/memory/migrations/145-drop-accounts-table.ts +32 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +14 -1
- package/src/memory/migrations/148-drop-reminders-table.ts +35 -1
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +69 -1
- package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +290 -0
- package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +51 -1
- package/src/memory/migrations/174-rename-thread-starters-table.ts +47 -1
- package/src/memory/migrations/176-drop-capability-card-state.ts +13 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +16 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +28 -1
- package/src/memory/migrations/190-call-session-skip-disclosure.ts +15 -0
- package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +64 -0
- package/src/memory/migrations/192-contacts-user-file-column.ts +15 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +90 -0
- package/src/memory/migrations/validate-migration-state.ts +137 -11
- package/src/memory/qdrant-circuit-breaker.ts +9 -0
- package/src/memory/qdrant-manager.ts +64 -7
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/notifications/decision-engine.ts +4 -1
- package/src/oauth/connection-resolver.ts +6 -4
- package/src/permissions/checker.ts +0 -38
- package/src/permissions/shell-identity.ts +76 -22
- package/src/permissions/types.ts +4 -2
- package/src/platform/client.ts +35 -7
- package/src/prompts/persona-resolver.ts +138 -0
- package/src/prompts/system-prompt.ts +36 -4
- package/src/prompts/templates/users/default.md +1 -0
- package/src/providers/registry.ts +27 -40
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/external-assistant-id.test.ts +13 -68
- package/src/runtime/auth/external-assistant-id.ts +13 -59
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/token-service.ts +43 -138
- package/src/runtime/channel-readiness-service.ts +1 -16
- package/src/runtime/http-server.ts +27 -2
- package/src/runtime/middleware/error-handler.ts +1 -9
- package/src/runtime/routes/audio-routes.ts +40 -0
- package/src/runtime/routes/btw-routes.ts +0 -17
- package/src/runtime/routes/conversation-query-routes.ts +63 -1
- package/src/runtime/routes/conversation-routes.ts +4 -44
- package/src/runtime/routes/diagnostics-routes.ts +1 -477
- package/src/runtime/routes/identity-routes.ts +18 -29
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +4 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +1 -1
- package/src/runtime/routes/integrations/vercel.ts +89 -0
- package/src/runtime/routes/log-export-routes.ts +5 -0
- package/src/runtime/routes/memory-item-routes.ts +24 -6
- package/src/runtime/routes/migration-rollback-routes.ts +209 -0
- package/src/runtime/routes/migration-routes.ts +17 -1
- package/src/runtime/routes/notification-routes.ts +58 -0
- package/src/runtime/routes/schedule-routes.ts +65 -0
- package/src/runtime/routes/settings-routes.ts +41 -1
- package/src/runtime/routes/tts-routes.ts +86 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +26 -2
- package/src/runtime/routes/workspace-commit-routes.ts +62 -0
- package/src/runtime/routes/workspace-routes.test.ts +22 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +86 -2
- package/src/security/ces-credential-client.ts +59 -22
- package/src/security/ces-rpc-credential-backend.ts +85 -0
- package/src/security/credential-backend.ts +12 -88
- package/src/security/keychain-broker-client.ts +10 -2
- package/src/security/secure-keys.ts +94 -113
- package/src/skills/catalog-install.ts +13 -7
- package/src/telemetry/usage-telemetry-reporter.ts +4 -2
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/executor.ts +0 -4
- package/src/tools/network/script-proxy/session-manager.ts +19 -4
- package/src/tools/network/web-fetch.ts +3 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/types.ts +0 -8
- package/src/util/errors.ts +0 -12
- package/src/util/platform.ts +3 -50
- package/src/workspace/git-service.ts +5 -2
- package/src/workspace/migrations/001-avatar-rename.ts +15 -0
- package/src/workspace/migrations/003-seed-device-id.ts +17 -1
- package/src/workspace/migrations/004-extract-collect-usage-data.ts +33 -0
- package/src/workspace/migrations/005-add-send-diagnostics.ts +3 -0
- package/src/workspace/migrations/006-services-config.ts +49 -0
- package/src/workspace/migrations/007-web-search-provider-rename.ts +27 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +3 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +4 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +78 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +11 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +44 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +5 -0
- package/src/workspace/migrations/015-migrate-credentials-to-keychain.ts +153 -0
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +156 -0
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +150 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +95 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +23 -1
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +106 -2
- package/src/workspace/migrations/types.ts +4 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -206
- package/src/__tests__/claude-code-tool-profiles.test.ts +0 -99
- package/src/__tests__/diagnostics-export.test.ts +0 -288
- package/src/__tests__/local-gateway-health.test.ts +0 -209
- package/src/__tests__/secret-ingress-handler.test.ts +0 -120
- package/src/__tests__/swarm-conversation-integration.test.ts +0 -358
- package/src/__tests__/swarm-dag-pathological.test.ts +0 -547
- package/src/__tests__/swarm-orchestrator.test.ts +0 -463
- package/src/__tests__/swarm-plan-validator.test.ts +0 -384
- package/src/__tests__/swarm-recursion.test.ts +0 -197
- package/src/__tests__/swarm-router-planner.test.ts +0 -234
- package/src/__tests__/swarm-tool.test.ts +0 -185
- package/src/__tests__/swarm-worker-backend.test.ts +0 -144
- package/src/__tests__/swarm-worker-runner.test.ts +0 -288
- package/src/commands/__tests__/cc-command-registry.test.ts +0 -396
- package/src/commands/cc-command-registry.ts +0 -248
- package/src/config/bundled-skills/claude-code/SKILL.md +0 -53
- package/src/config/bundled-skills/claude-code/TOOLS.json +0 -47
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +0 -12
- package/src/config/bundled-skills/orchestration/SKILL.md +0 -33
- package/src/config/bundled-skills/orchestration/TOOLS.json +0 -35
- package/src/config/bundled-skills/orchestration/tools/swarm-delegate.ts +0 -12
- package/src/config/schemas/swarm.ts +0 -82
- package/src/logfire.ts +0 -135
- package/src/runtime/local-gateway-health.ts +0 -275
- package/src/security/secret-ingress.ts +0 -68
- package/src/swarm/backend-claude-code.ts +0 -225
- package/src/swarm/checkpoint.ts +0 -137
- package/src/swarm/graph-utils.ts +0 -53
- package/src/swarm/index.ts +0 -55
- package/src/swarm/limits.ts +0 -66
- package/src/swarm/orchestrator.ts +0 -424
- package/src/swarm/plan-validator.ts +0 -117
- package/src/swarm/router-planner.ts +0 -162
- package/src/swarm/router-prompts.ts +0 -39
- package/src/swarm/synthesizer.ts +0 -81
- package/src/swarm/types.ts +0 -72
- package/src/swarm/worker-backend.ts +0 -131
- package/src/swarm/worker-prompts.ts +0 -80
- package/src/swarm/worker-runner.ts +0 -170
- package/src/tools/claude-code/claude-code.ts +0 -610
- package/src/tools/swarm/delegate.ts +0 -205
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP route handlers for
|
|
3
|
-
*
|
|
4
|
-
* Handles diagnostics export and dictation processing requests.
|
|
2
|
+
* HTTP route handlers for dictation processing.
|
|
5
3
|
*/
|
|
6
4
|
|
|
7
|
-
import { randomBytes } from "node:crypto";
|
|
8
|
-
import {
|
|
9
|
-
createWriteStream,
|
|
10
|
-
mkdirSync,
|
|
11
|
-
readdirSync,
|
|
12
|
-
readFileSync,
|
|
13
|
-
rmSync,
|
|
14
|
-
statSync,
|
|
15
|
-
writeFileSync,
|
|
16
|
-
} from "node:fs";
|
|
17
|
-
import { homedir, tmpdir } from "node:os";
|
|
18
|
-
import { basename, join } from "node:path";
|
|
19
|
-
|
|
20
|
-
import archiver from "archiver";
|
|
21
|
-
import { and, asc, desc, eq, gte, lte } from "drizzle-orm";
|
|
22
|
-
|
|
23
5
|
import {
|
|
24
6
|
type ProfileResolution,
|
|
25
7
|
resolveProfile,
|
|
@@ -31,14 +13,6 @@ import {
|
|
|
31
13
|
import { detectDictationModeHeuristic } from "../../daemon/handlers/dictation.js";
|
|
32
14
|
import type { DictationRequest } from "../../daemon/message-types/diagnostics.js";
|
|
33
15
|
import type { DictationContext } from "../../daemon/message-types/shared.js";
|
|
34
|
-
import { resolveConversationId } from "../../memory/conversation-key-store.js";
|
|
35
|
-
import { getDb } from "../../memory/db.js";
|
|
36
|
-
import {
|
|
37
|
-
llmRequestLogs,
|
|
38
|
-
llmUsageEvents,
|
|
39
|
-
messages,
|
|
40
|
-
toolInvocations,
|
|
41
|
-
} from "../../memory/schema.js";
|
|
42
16
|
import {
|
|
43
17
|
createTimeout,
|
|
44
18
|
extractToolUse,
|
|
@@ -51,444 +25,6 @@ import type { RouteDefinition } from "../http-router.js";
|
|
|
51
25
|
|
|
52
26
|
const log = getLogger("diagnostics-routes");
|
|
53
27
|
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// Diagnostics export — redaction helpers
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
|
|
58
|
-
const MAX_CONTENT_LENGTH = 500;
|
|
59
|
-
|
|
60
|
-
const REDACT_PATTERNS = [
|
|
61
|
-
/\b(sk|key|api[_-]?key|token|secret|password|passwd|credential)[_\-]?[a-zA-Z0-9]{16,}\b/gi,
|
|
62
|
-
/Bearer\s+[A-Za-z0-9\-._~+\/]+=*/gi,
|
|
63
|
-
/[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g,
|
|
64
|
-
/\b(AKIA|ASIA)[A-Z0-9]{16}\b/g,
|
|
65
|
-
/\b[A-Fa-f0-9]{32,}\b/g,
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
function redact(text: string): string {
|
|
69
|
-
let result = text;
|
|
70
|
-
for (const pattern of REDACT_PATTERNS) {
|
|
71
|
-
result = result.replace(pattern, "[REDACTED]");
|
|
72
|
-
}
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function truncateAndRedact(text: string): string {
|
|
77
|
-
const truncated =
|
|
78
|
-
text.length > MAX_CONTENT_LENGTH
|
|
79
|
-
? text.slice(0, MAX_CONTENT_LENGTH) + "...[truncated]"
|
|
80
|
-
: text;
|
|
81
|
-
return redact(truncated);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const SENSITIVE_KEYS = new Set([
|
|
85
|
-
"api_key",
|
|
86
|
-
"apikey",
|
|
87
|
-
"api-key",
|
|
88
|
-
"authorization",
|
|
89
|
-
"x-api-key",
|
|
90
|
-
"secret",
|
|
91
|
-
"password",
|
|
92
|
-
"token",
|
|
93
|
-
"credential",
|
|
94
|
-
"credentials",
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
function redactDeep(value: unknown): unknown {
|
|
98
|
-
if (typeof value === "string") return redact(value);
|
|
99
|
-
if (Array.isArray(value)) return value.map(redactDeep);
|
|
100
|
-
if (value != null && typeof value === "object") {
|
|
101
|
-
const out: Record<string, unknown> = {};
|
|
102
|
-
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
103
|
-
if (SENSITIVE_KEYS.has(k.toLowerCase())) {
|
|
104
|
-
out[k] = "[REDACTED]";
|
|
105
|
-
} else {
|
|
106
|
-
out[k] = redactDeep(v);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return out;
|
|
110
|
-
}
|
|
111
|
-
return value;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ---------------------------------------------------------------------------
|
|
115
|
-
// Crash report discovery
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
|
|
118
|
-
const CRASH_REPORT_EXTENSIONS = new Set([".crash", ".ips", ".diag"]);
|
|
119
|
-
const CRASH_REPORT_TAR_GZ = ".tar.gz";
|
|
120
|
-
const CRASH_REPORT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
121
|
-
|
|
122
|
-
function findRecentCrashReports(): string[] {
|
|
123
|
-
const diagnosticReportsDir = join(
|
|
124
|
-
homedir(),
|
|
125
|
-
"Library",
|
|
126
|
-
"Logs",
|
|
127
|
-
"DiagnosticReports",
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
const entries = readdirSync(diagnosticReportsDir);
|
|
132
|
-
const now = Date.now();
|
|
133
|
-
const results: string[] = [];
|
|
134
|
-
|
|
135
|
-
for (const entry of entries) {
|
|
136
|
-
// Case-insensitive prefix match for "vellum-assistant"
|
|
137
|
-
if (!entry.toLowerCase().startsWith("vellum-assistant")) continue;
|
|
138
|
-
|
|
139
|
-
// Check extension
|
|
140
|
-
const lowerEntry = entry.toLowerCase();
|
|
141
|
-
const hasValidExt =
|
|
142
|
-
CRASH_REPORT_EXTENSIONS.has(
|
|
143
|
-
lowerEntry.slice(lowerEntry.lastIndexOf(".")),
|
|
144
|
-
) || lowerEntry.endsWith(CRASH_REPORT_TAR_GZ);
|
|
145
|
-
|
|
146
|
-
if (!hasValidExt) continue;
|
|
147
|
-
|
|
148
|
-
const filePath = join(diagnosticReportsDir, entry);
|
|
149
|
-
try {
|
|
150
|
-
const stat = statSync(filePath);
|
|
151
|
-
if (!stat.isFile()) continue;
|
|
152
|
-
if (now - stat.mtimeMs > CRASH_REPORT_MAX_AGE_MS) continue;
|
|
153
|
-
results.push(filePath);
|
|
154
|
-
} catch {
|
|
155
|
-
// Skip files we can't stat
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return results;
|
|
160
|
-
} catch {
|
|
161
|
-
// Directory doesn't exist or can't be read — not an error
|
|
162
|
-
return [];
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ---------------------------------------------------------------------------
|
|
167
|
-
// Diagnostics export handler
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
|
|
170
|
-
async function handleDiagnosticsExport(body: {
|
|
171
|
-
conversationId?: string;
|
|
172
|
-
anchorMessageId?: string;
|
|
173
|
-
}): Promise<Response> {
|
|
174
|
-
if (!body.conversationId) {
|
|
175
|
-
return httpError("BAD_REQUEST", "conversationId is required", 400);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// The client may send a conversation key (client-side UUID) rather than
|
|
179
|
-
// the daemon's internal conversation ID. Resolve to the canonical ID.
|
|
180
|
-
const conversationId =
|
|
181
|
-
resolveConversationId(body.conversationId) ?? body.conversationId;
|
|
182
|
-
const { anchorMessageId } = body;
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
const db = getDb();
|
|
186
|
-
|
|
187
|
-
// 1. Find the anchor message.
|
|
188
|
-
// Try in order: specific ID → most recent assistant message → any message.
|
|
189
|
-
// The final fallback handles the race condition where the user clicks
|
|
190
|
-
// "export" before message_complete fires and the assistant message has
|
|
191
|
-
// been persisted — the user message and in-flight tool/usage data are
|
|
192
|
-
// still captured.
|
|
193
|
-
let anchorMessage;
|
|
194
|
-
let anchorIsFallback = false;
|
|
195
|
-
if (anchorMessageId) {
|
|
196
|
-
anchorMessage = db
|
|
197
|
-
.select()
|
|
198
|
-
.from(messages)
|
|
199
|
-
.where(
|
|
200
|
-
and(
|
|
201
|
-
eq(messages.id, anchorMessageId),
|
|
202
|
-
eq(messages.conversationId, conversationId),
|
|
203
|
-
),
|
|
204
|
-
)
|
|
205
|
-
.get();
|
|
206
|
-
}
|
|
207
|
-
if (!anchorMessage) {
|
|
208
|
-
anchorMessage = db
|
|
209
|
-
.select()
|
|
210
|
-
.from(messages)
|
|
211
|
-
.where(
|
|
212
|
-
and(
|
|
213
|
-
eq(messages.conversationId, conversationId),
|
|
214
|
-
eq(messages.role, "assistant"),
|
|
215
|
-
),
|
|
216
|
-
)
|
|
217
|
-
.orderBy(desc(messages.createdAt))
|
|
218
|
-
.limit(1)
|
|
219
|
-
.get();
|
|
220
|
-
}
|
|
221
|
-
if (!anchorMessage) {
|
|
222
|
-
anchorMessage = db
|
|
223
|
-
.select()
|
|
224
|
-
.from(messages)
|
|
225
|
-
.where(eq(messages.conversationId, conversationId))
|
|
226
|
-
.orderBy(desc(messages.createdAt))
|
|
227
|
-
.limit(1)
|
|
228
|
-
.get();
|
|
229
|
-
anchorIsFallback = true;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 2. Compute the export time range.
|
|
233
|
-
// When an anchor message exists, scope from the earliest message in the
|
|
234
|
-
// conversation through the anchor so the full conversation context is
|
|
235
|
-
// captured. When no messages exist at all (empty conversation or race
|
|
236
|
-
// condition), use the current timestamp so the export still captures any
|
|
237
|
-
// in-flight usage/tool data.
|
|
238
|
-
const now = Date.now();
|
|
239
|
-
let rangeEnd: number;
|
|
240
|
-
let rangeStart: number;
|
|
241
|
-
let usageRangeEnd: number;
|
|
242
|
-
|
|
243
|
-
if (anchorMessage) {
|
|
244
|
-
const earliestMessage = db
|
|
245
|
-
.select()
|
|
246
|
-
.from(messages)
|
|
247
|
-
.where(eq(messages.conversationId, conversationId))
|
|
248
|
-
.orderBy(asc(messages.createdAt))
|
|
249
|
-
.limit(1)
|
|
250
|
-
.get();
|
|
251
|
-
|
|
252
|
-
rangeStart = earliestMessage?.createdAt ?? anchorMessage.createdAt - 2000;
|
|
253
|
-
|
|
254
|
-
// When the anchor was selected via the fallback "any message" path
|
|
255
|
-
// (because the assistant reply hasn't been persisted yet), extend the
|
|
256
|
-
// range to the current time so in-flight tool invocations and usage
|
|
257
|
-
// recorded after the user message are captured. An explicit anchor to a
|
|
258
|
-
// non-assistant message uses the message's own timestamp.
|
|
259
|
-
rangeEnd = anchorIsFallback ? now : anchorMessage.createdAt;
|
|
260
|
-
usageRangeEnd = anchorIsFallback
|
|
261
|
-
? now + 5000
|
|
262
|
-
: anchorMessage.createdAt + 5000;
|
|
263
|
-
} else {
|
|
264
|
-
// No messages at all — use the current time so we capture any
|
|
265
|
-
// in-flight LLM usage or tool invocations.
|
|
266
|
-
rangeStart = now - 60_000;
|
|
267
|
-
rangeEnd = now;
|
|
268
|
-
usageRangeEnd = now + 5000;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// 3. Query all messages in the range
|
|
272
|
-
const rangeMessages = db
|
|
273
|
-
.select()
|
|
274
|
-
.from(messages)
|
|
275
|
-
.where(
|
|
276
|
-
and(
|
|
277
|
-
eq(messages.conversationId, conversationId),
|
|
278
|
-
gte(messages.createdAt, rangeStart),
|
|
279
|
-
lte(messages.createdAt, rangeEnd),
|
|
280
|
-
),
|
|
281
|
-
)
|
|
282
|
-
.orderBy(messages.createdAt)
|
|
283
|
-
.all();
|
|
284
|
-
|
|
285
|
-
// 4. Query tool invocations in the range
|
|
286
|
-
const rangeToolInvocations = db
|
|
287
|
-
.select()
|
|
288
|
-
.from(toolInvocations)
|
|
289
|
-
.where(
|
|
290
|
-
and(
|
|
291
|
-
eq(toolInvocations.conversationId, conversationId),
|
|
292
|
-
gte(toolInvocations.createdAt, rangeStart),
|
|
293
|
-
lte(toolInvocations.createdAt, rangeEnd),
|
|
294
|
-
),
|
|
295
|
-
)
|
|
296
|
-
.orderBy(toolInvocations.createdAt)
|
|
297
|
-
.all();
|
|
298
|
-
|
|
299
|
-
// 5. Query LLM usage events
|
|
300
|
-
const rangeUsageEvents = db
|
|
301
|
-
.select()
|
|
302
|
-
.from(llmUsageEvents)
|
|
303
|
-
.where(
|
|
304
|
-
and(
|
|
305
|
-
eq(llmUsageEvents.conversationId, conversationId),
|
|
306
|
-
gte(llmUsageEvents.createdAt, rangeStart),
|
|
307
|
-
lte(llmUsageEvents.createdAt, usageRangeEnd),
|
|
308
|
-
),
|
|
309
|
-
)
|
|
310
|
-
.orderBy(llmUsageEvents.createdAt)
|
|
311
|
-
.all();
|
|
312
|
-
|
|
313
|
-
// 5b. Query raw LLM request/response logs
|
|
314
|
-
const rangeRequestLogs = db
|
|
315
|
-
.select()
|
|
316
|
-
.from(llmRequestLogs)
|
|
317
|
-
.where(
|
|
318
|
-
and(
|
|
319
|
-
eq(llmRequestLogs.conversationId, conversationId),
|
|
320
|
-
gte(llmRequestLogs.createdAt, rangeStart),
|
|
321
|
-
lte(llmRequestLogs.createdAt, usageRangeEnd),
|
|
322
|
-
),
|
|
323
|
-
)
|
|
324
|
-
.orderBy(llmRequestLogs.createdAt)
|
|
325
|
-
.all();
|
|
326
|
-
|
|
327
|
-
// 6. Write export files to a temp directory
|
|
328
|
-
const exportId = `diagnostics-${new Date().toISOString().replace(/[:.]/g, "-")}-${randomBytes(4).toString("hex")}`;
|
|
329
|
-
const tempDir = join(tmpdir(), exportId);
|
|
330
|
-
mkdirSync(tempDir, { recursive: true });
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
const manifest = {
|
|
334
|
-
version: "1.1",
|
|
335
|
-
exportedAt: new Date().toISOString(),
|
|
336
|
-
conversationId,
|
|
337
|
-
messageId: anchorMessage?.id ?? null,
|
|
338
|
-
};
|
|
339
|
-
writeFileSync(
|
|
340
|
-
join(tempDir, "manifest.json"),
|
|
341
|
-
JSON.stringify(manifest, null, 2),
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
const messagesLines = rangeMessages.map((m) =>
|
|
345
|
-
JSON.stringify({
|
|
346
|
-
id: m.id,
|
|
347
|
-
conversationId: m.conversationId,
|
|
348
|
-
role: m.role,
|
|
349
|
-
content: truncateAndRedact(m.content),
|
|
350
|
-
createdAt: m.createdAt,
|
|
351
|
-
}),
|
|
352
|
-
);
|
|
353
|
-
writeFileSync(
|
|
354
|
-
join(tempDir, "messages.jsonl"),
|
|
355
|
-
messagesLines.join("\n") + (messagesLines.length > 0 ? "\n" : ""),
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const toolLines = rangeToolInvocations.map((t) =>
|
|
359
|
-
JSON.stringify({
|
|
360
|
-
id: t.id,
|
|
361
|
-
conversationId: t.conversationId,
|
|
362
|
-
toolName: t.toolName,
|
|
363
|
-
input: truncateAndRedact(t.input),
|
|
364
|
-
result: truncateAndRedact(t.result),
|
|
365
|
-
decision: t.decision,
|
|
366
|
-
riskLevel: t.riskLevel,
|
|
367
|
-
durationMs: t.durationMs,
|
|
368
|
-
createdAt: t.createdAt,
|
|
369
|
-
}),
|
|
370
|
-
);
|
|
371
|
-
writeFileSync(
|
|
372
|
-
join(tempDir, "tool_invocations.jsonl"),
|
|
373
|
-
toolLines.join("\n") + (toolLines.length > 0 ? "\n" : ""),
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
const usageLines = rangeUsageEvents.map((u) =>
|
|
377
|
-
JSON.stringify({
|
|
378
|
-
id: u.id,
|
|
379
|
-
conversationId: u.conversationId,
|
|
380
|
-
actor: u.actor,
|
|
381
|
-
provider: u.provider,
|
|
382
|
-
model: u.model,
|
|
383
|
-
inputTokens: u.inputTokens,
|
|
384
|
-
outputTokens: u.outputTokens,
|
|
385
|
-
cacheCreationInputTokens: u.cacheCreationInputTokens,
|
|
386
|
-
cacheReadInputTokens: u.cacheReadInputTokens,
|
|
387
|
-
estimatedCostUsd: u.estimatedCostUsd,
|
|
388
|
-
pricingStatus: u.pricingStatus,
|
|
389
|
-
createdAt: u.createdAt,
|
|
390
|
-
}),
|
|
391
|
-
);
|
|
392
|
-
writeFileSync(
|
|
393
|
-
join(tempDir, "usage.jsonl"),
|
|
394
|
-
usageLines.join("\n") + (usageLines.length > 0 ? "\n" : ""),
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
const requestLogLines = rangeRequestLogs.map((r) => {
|
|
398
|
-
let request: unknown;
|
|
399
|
-
let response: unknown;
|
|
400
|
-
try {
|
|
401
|
-
request = JSON.parse(r.requestPayload);
|
|
402
|
-
} catch {
|
|
403
|
-
request = r.requestPayload;
|
|
404
|
-
}
|
|
405
|
-
try {
|
|
406
|
-
response = JSON.parse(r.responsePayload);
|
|
407
|
-
} catch {
|
|
408
|
-
response = r.responsePayload;
|
|
409
|
-
}
|
|
410
|
-
return JSON.stringify({
|
|
411
|
-
id: r.id,
|
|
412
|
-
conversationId: r.conversationId,
|
|
413
|
-
provider: r.provider,
|
|
414
|
-
request: redactDeep(request),
|
|
415
|
-
response: redactDeep(response),
|
|
416
|
-
createdAt: r.createdAt,
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
writeFileSync(
|
|
420
|
-
join(tempDir, "llm_requests.jsonl"),
|
|
421
|
-
requestLogLines.join("\n") + (requestLogLines.length > 0 ? "\n" : ""),
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
// 7. Zip the temp directory
|
|
425
|
-
const downloadsDir = join(homedir(), "Downloads");
|
|
426
|
-
mkdirSync(downloadsDir, { recursive: true });
|
|
427
|
-
const zipFilename = `${exportId}.zip`;
|
|
428
|
-
const zipPath = join(downloadsDir, zipFilename);
|
|
429
|
-
|
|
430
|
-
await new Promise<void>((resolve, reject) => {
|
|
431
|
-
const output = createWriteStream(zipPath);
|
|
432
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
433
|
-
|
|
434
|
-
output.on("close", () => resolve());
|
|
435
|
-
output.on("error", (err: Error) => reject(err));
|
|
436
|
-
archive.on("error", (err: Error) => reject(err));
|
|
437
|
-
archive.on("warning", (err: Error) => {
|
|
438
|
-
log.warn({ err }, "Archiver warning during diagnostics export");
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
archive.pipe(output);
|
|
442
|
-
archive.directory(tempDir, false);
|
|
443
|
-
|
|
444
|
-
// Add recent crash report files under crash-reports/.
|
|
445
|
-
// Text-based crash files (.crash, .ips, .diag) are redacted using the
|
|
446
|
-
// same patterns as conversation data. Binary archives (.tar.gz) are
|
|
447
|
-
// added as-is since they can't be meaningfully text-redacted.
|
|
448
|
-
const crashReportFiles = findRecentCrashReports();
|
|
449
|
-
for (const filePath of crashReportFiles) {
|
|
450
|
-
try {
|
|
451
|
-
const fileName = basename(filePath);
|
|
452
|
-
if (fileName.toLowerCase().endsWith(CRASH_REPORT_TAR_GZ)) {
|
|
453
|
-
archive.file(filePath, { name: "crash-reports/" + fileName });
|
|
454
|
-
} else {
|
|
455
|
-
const content = readFileSync(filePath, "utf-8");
|
|
456
|
-
archive.append(redact(content), {
|
|
457
|
-
name: "crash-reports/" + fileName,
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
} catch {
|
|
461
|
-
// Skip files that can't be read
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
archive.finalize();
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
log.info(
|
|
469
|
-
{ conversationId, zipPath, messageCount: rangeMessages.length },
|
|
470
|
-
"Diagnostics export completed via HTTP",
|
|
471
|
-
);
|
|
472
|
-
|
|
473
|
-
return Response.json({ success: true, filePath: zipPath });
|
|
474
|
-
} finally {
|
|
475
|
-
try {
|
|
476
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
477
|
-
} catch {
|
|
478
|
-
// Best-effort cleanup
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
} catch (err) {
|
|
482
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
483
|
-
log.error({ err, conversationId }, "Failed to export diagnostics");
|
|
484
|
-
return httpError(
|
|
485
|
-
"INTERNAL_ERROR",
|
|
486
|
-
`Failed to export diagnostics: ${errorMessage}`,
|
|
487
|
-
500,
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
28
|
// ---------------------------------------------------------------------------
|
|
493
29
|
// Dictation
|
|
494
30
|
// ---------------------------------------------------------------------------
|
|
@@ -895,18 +431,6 @@ async function handleCommandMode(
|
|
|
895
431
|
|
|
896
432
|
export function diagnosticsRouteDefinitions(): RouteDefinition[] {
|
|
897
433
|
return [
|
|
898
|
-
{
|
|
899
|
-
endpoint: "diagnostics/export",
|
|
900
|
-
method: "POST",
|
|
901
|
-
policyKey: "diagnostics/export",
|
|
902
|
-
handler: async ({ req }) => {
|
|
903
|
-
const body = (await req.json()) as {
|
|
904
|
-
conversationId?: string;
|
|
905
|
-
anchorMessageId?: string;
|
|
906
|
-
};
|
|
907
|
-
return handleDiagnosticsExport(body);
|
|
908
|
-
},
|
|
909
|
-
},
|
|
910
434
|
{
|
|
911
435
|
endpoint: "dictation",
|
|
912
436
|
method: "POST",
|
|
@@ -9,7 +9,10 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
|
|
10
10
|
import { getBaseDataDir } from "../../config/env-registry.js";
|
|
11
11
|
import { parseIdentityFields } from "../../daemon/handlers/identity.js";
|
|
12
|
-
import {
|
|
12
|
+
import { getMaxMigrationVersion } from "../../memory/migrations/registry.js";
|
|
13
|
+
import { getWorkspacePromptPath } from "../../util/platform.js";
|
|
14
|
+
import { WORKSPACE_MIGRATIONS } from "../../workspace/migrations/registry.js";
|
|
15
|
+
import { getLastWorkspaceMigrationId } from "../../workspace/migrations/runner.js";
|
|
13
16
|
import { httpError } from "../http-errors.js";
|
|
14
17
|
import type { RouteDefinition } from "../http-router.js";
|
|
15
18
|
import { getCachedIntro } from "./identity-intro-cache.js";
|
|
@@ -133,6 +136,10 @@ function getPackageVersion(): string | undefined {
|
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
export function handleHealth(): Response {
|
|
139
|
+
return Response.json({ status: "ok" });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function handleDetailedHealth(): Response {
|
|
136
143
|
return Response.json({
|
|
137
144
|
status: "healthy",
|
|
138
145
|
timestamp: new Date().toISOString(),
|
|
@@ -140,9 +147,18 @@ export function handleHealth(): Response {
|
|
|
140
147
|
disk: getDiskSpaceInfo(),
|
|
141
148
|
memory: getMemoryInfo(),
|
|
142
149
|
cpu: getCpuInfo(),
|
|
150
|
+
migrations: {
|
|
151
|
+
dbVersion: getMaxMigrationVersion(),
|
|
152
|
+
lastWorkspaceMigrationId:
|
|
153
|
+
getLastWorkspaceMigrationId(WORKSPACE_MIGRATIONS),
|
|
154
|
+
},
|
|
143
155
|
});
|
|
144
156
|
}
|
|
145
157
|
|
|
158
|
+
export function handleReadyz(): Response {
|
|
159
|
+
return Response.json({ status: "ok" });
|
|
160
|
+
}
|
|
161
|
+
|
|
146
162
|
export function handleGetIdentity(): Response {
|
|
147
163
|
const identityPath = getWorkspacePromptPath("IDENTITY.md");
|
|
148
164
|
if (!existsSync(identityPath)) {
|
|
@@ -163,31 +179,6 @@ export function handleGetIdentity(): Response {
|
|
|
163
179
|
// ignore
|
|
164
180
|
}
|
|
165
181
|
|
|
166
|
-
// Read lockfile for assistantId, cloud, and originSystem
|
|
167
|
-
let assistantId: string | undefined;
|
|
168
|
-
let cloud: string | undefined;
|
|
169
|
-
let originSystem: string | undefined;
|
|
170
|
-
try {
|
|
171
|
-
const lockData = readLockfile();
|
|
172
|
-
const assistants = lockData?.assistants as
|
|
173
|
-
| Array<Record<string, unknown>>
|
|
174
|
-
| undefined;
|
|
175
|
-
if (assistants && assistants.length > 0) {
|
|
176
|
-
// Use the most recently hatched assistant
|
|
177
|
-
const sorted = [...assistants].sort((a, b) => {
|
|
178
|
-
const dateA = new Date((a.hatchedAt as string) || 0).getTime();
|
|
179
|
-
const dateB = new Date((b.hatchedAt as string) || 0).getTime();
|
|
180
|
-
return dateB - dateA;
|
|
181
|
-
});
|
|
182
|
-
const latest = sorted[0];
|
|
183
|
-
assistantId = latest.assistantId as string | undefined;
|
|
184
|
-
cloud = latest.cloud as string | undefined;
|
|
185
|
-
originSystem = cloud === "local" ? "local" : cloud;
|
|
186
|
-
}
|
|
187
|
-
} catch {
|
|
188
|
-
// ignore -- lockfile may not exist
|
|
189
|
-
}
|
|
190
|
-
|
|
191
182
|
return Response.json({
|
|
192
183
|
name: fields.name ?? "",
|
|
193
184
|
role: fields.role ?? "",
|
|
@@ -195,9 +186,7 @@ export function handleGetIdentity(): Response {
|
|
|
195
186
|
emoji: fields.emoji ?? "",
|
|
196
187
|
home: fields.home ?? "",
|
|
197
188
|
version,
|
|
198
|
-
assistantId,
|
|
199
189
|
createdAt,
|
|
200
|
-
originSystem,
|
|
201
190
|
});
|
|
202
191
|
}
|
|
203
192
|
|
|
@@ -255,7 +244,7 @@ export function identityRouteDefinitions(): RouteDefinition[] {
|
|
|
255
244
|
{
|
|
256
245
|
endpoint: "health",
|
|
257
246
|
method: "GET",
|
|
258
|
-
handler: () =>
|
|
247
|
+
handler: () => handleDetailedHealth(),
|
|
259
248
|
},
|
|
260
249
|
{
|
|
261
250
|
endpoint: "identity",
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Telegram messages.
|
|
5
|
-
*
|
|
6
|
-
* The payload is stored before the scan so dead-lettered events can be
|
|
7
|
-
* replayed. If the scan detects embedded secrets the stored payload is
|
|
8
|
-
* cleared before the IngressBlockedError propagates, ensuring
|
|
9
|
-
* secret-bearing content is never left on disk.
|
|
2
|
+
* Inbound payload persistence stage: persists the raw inbound payload and
|
|
3
|
+
* records a conversation-seen signal for Telegram messages.
|
|
10
4
|
*
|
|
11
5
|
* Extracted from inbound-message-handler.ts to keep the top-level handler
|
|
12
6
|
* focused on orchestration.
|
|
@@ -15,8 +9,6 @@ import type { ChannelId } from "../../../channels/types.js";
|
|
|
15
9
|
import type { TrustContext } from "../../../daemon/conversation-runtime-assembly.js";
|
|
16
10
|
import { recordConversationSeenSignal } from "../../../memory/conversation-attention-store.js";
|
|
17
11
|
import * as deliveryCrud from "../../../memory/delivery-crud.js";
|
|
18
|
-
import { checkIngressForSecrets } from "../../../security/secret-ingress.js";
|
|
19
|
-
import { IngressBlockedError } from "../../../util/errors.js";
|
|
20
12
|
import { getLogger } from "../../../util/logger.js";
|
|
21
13
|
|
|
22
14
|
const log = getLogger("runtime-http");
|
|
@@ -44,10 +36,7 @@ export interface SecretIngressCheckParams {
|
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
/**
|
|
47
|
-
* Persist the raw payload
|
|
48
|
-
* Telegram seen signal.
|
|
49
|
-
*
|
|
50
|
-
* Throws IngressBlockedError if the content contains secrets.
|
|
39
|
+
* Persist the raw payload and record a Telegram seen signal.
|
|
51
40
|
*/
|
|
52
41
|
export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
|
|
53
42
|
const {
|
|
@@ -68,9 +57,7 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
|
|
|
68
57
|
canonicalAssistantId,
|
|
69
58
|
} = params;
|
|
70
59
|
|
|
71
|
-
// Persist the raw payload
|
|
72
|
-
// replayed. If the ingress check later detects secrets we clear it
|
|
73
|
-
// before throwing, so secret-bearing content is never left on disk.
|
|
60
|
+
// Persist the raw payload so dead-lettered events can always be replayed.
|
|
74
61
|
deliveryCrud.storePayload(eventId, {
|
|
75
62
|
sourceChannel,
|
|
76
63
|
externalChatId: conversationExternalId,
|
|
@@ -86,22 +73,6 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
|
|
|
86
73
|
assistantId: canonicalAssistantId,
|
|
87
74
|
});
|
|
88
75
|
|
|
89
|
-
const contentToCheck = content ?? "";
|
|
90
|
-
let ingressCheck: ReturnType<typeof checkIngressForSecrets>;
|
|
91
|
-
try {
|
|
92
|
-
ingressCheck = checkIngressForSecrets(contentToCheck);
|
|
93
|
-
} catch (checkErr) {
|
|
94
|
-
deliveryCrud.clearPayload(eventId);
|
|
95
|
-
throw checkErr;
|
|
96
|
-
}
|
|
97
|
-
if (ingressCheck.blocked) {
|
|
98
|
-
deliveryCrud.clearPayload(eventId);
|
|
99
|
-
throw new IngressBlockedError(
|
|
100
|
-
ingressCheck.userNotice!,
|
|
101
|
-
ingressCheck.detectedTypes,
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
76
|
// Record inferred seen signal for non-duplicate Telegram inbound messages
|
|
106
77
|
if (sourceChannel === "telegram") {
|
|
107
78
|
try {
|
|
@@ -24,7 +24,7 @@ mock.module("../../../config/assistant-feature-flags.js", () => ({
|
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
26
|
mock.module("../../../config/loader.js", () => ({
|
|
27
|
-
getConfig: () => ({
|
|
27
|
+
getConfig: () => ({}),
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
30
|
mock.module("../../../memory/attachments-store.js", () => ({
|