vellum 0.2.1 → 0.2.2
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/README.md +15 -2
- package/bun.lock +5 -2
- package/package.json +4 -2
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +299 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +62 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +246 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +163 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +28 -4
- package/src/daemon/ipc-contract.ts +133 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/http-server.ts +108 -20
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +5 -10
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +220 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +77 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
* data. Provides upload, delete, and message-linkage operations.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { eq
|
|
8
|
+
import { eq } from 'drizzle-orm';
|
|
9
9
|
import { v4 as uuid } from 'uuid';
|
|
10
10
|
import { getDb } from './db.js';
|
|
11
11
|
import { attachments, messageAttachments } from './schema.js';
|
|
12
12
|
|
|
13
13
|
export interface StoredAttachment {
|
|
14
14
|
id: string;
|
|
15
|
-
assistantId: string;
|
|
16
15
|
originalFilename: string;
|
|
17
16
|
mimeType: string;
|
|
18
17
|
sizeBytes: number;
|
|
19
18
|
kind: string;
|
|
19
|
+
thumbnailBase64: string | null;
|
|
20
20
|
createdAt: number;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -152,7 +152,6 @@ function computeContentHash(dataBase64: string): string {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
export function uploadAttachment(
|
|
155
|
-
assistantId: string,
|
|
156
155
|
filename: string,
|
|
157
156
|
mimeType: string,
|
|
158
157
|
dataBase64: string,
|
|
@@ -173,25 +172,20 @@ export function uploadAttachment(
|
|
|
173
172
|
const db = getDb();
|
|
174
173
|
const contentHash = computeContentHash(dataBase64);
|
|
175
174
|
|
|
176
|
-
// Dedup: if an attachment with the same content already exists
|
|
177
|
-
//
|
|
175
|
+
// Dedup: if an attachment with the same content already exists, return it
|
|
176
|
+
// instead of storing a duplicate.
|
|
178
177
|
const existing = db
|
|
179
178
|
.select({
|
|
180
179
|
id: attachments.id,
|
|
181
|
-
assistantId: attachments.assistantId,
|
|
182
180
|
originalFilename: attachments.originalFilename,
|
|
183
181
|
mimeType: attachments.mimeType,
|
|
184
182
|
sizeBytes: attachments.sizeBytes,
|
|
185
183
|
kind: attachments.kind,
|
|
184
|
+
thumbnailBase64: attachments.thumbnailBase64,
|
|
186
185
|
createdAt: attachments.createdAt,
|
|
187
186
|
})
|
|
188
187
|
.from(attachments)
|
|
189
|
-
.where(
|
|
190
|
-
and(
|
|
191
|
-
eq(attachments.assistantId, assistantId),
|
|
192
|
-
eq(attachments.contentHash, contentHash),
|
|
193
|
-
),
|
|
194
|
-
)
|
|
188
|
+
.where(eq(attachments.contentHash, contentHash))
|
|
195
189
|
.get();
|
|
196
190
|
|
|
197
191
|
if (existing) {
|
|
@@ -203,7 +197,7 @@ export function uploadAttachment(
|
|
|
203
197
|
|
|
204
198
|
const record = {
|
|
205
199
|
id: uuid(),
|
|
206
|
-
assistantId,
|
|
200
|
+
assistantId: 'self',
|
|
207
201
|
originalFilename: filename,
|
|
208
202
|
mimeType,
|
|
209
203
|
sizeBytes,
|
|
@@ -217,28 +211,34 @@ export function uploadAttachment(
|
|
|
217
211
|
|
|
218
212
|
return {
|
|
219
213
|
id: record.id,
|
|
220
|
-
assistantId,
|
|
221
214
|
originalFilename: filename,
|
|
222
215
|
mimeType,
|
|
223
216
|
sizeBytes,
|
|
224
217
|
kind,
|
|
218
|
+
thumbnailBase64: null,
|
|
225
219
|
createdAt: now,
|
|
226
220
|
};
|
|
227
221
|
}
|
|
228
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Update the thumbnail for an existing attachment.
|
|
225
|
+
*/
|
|
226
|
+
export function setAttachmentThumbnail(attachmentId: string, thumbnailBase64: string): void {
|
|
227
|
+
const db = getDb();
|
|
228
|
+
db.update(attachments)
|
|
229
|
+
.set({ thumbnailBase64 })
|
|
230
|
+
.where(eq(attachments.id, attachmentId))
|
|
231
|
+
.run();
|
|
232
|
+
}
|
|
233
|
+
|
|
229
234
|
export type DeleteAttachmentResult = 'deleted' | 'not_found' | 'still_referenced';
|
|
230
235
|
|
|
231
|
-
export function deleteAttachment(
|
|
236
|
+
export function deleteAttachment(attachmentId: string): DeleteAttachmentResult {
|
|
232
237
|
const db = getDb();
|
|
233
238
|
const existing = db
|
|
234
239
|
.select({ id: attachments.id })
|
|
235
240
|
.from(attachments)
|
|
236
|
-
.where(
|
|
237
|
-
and(
|
|
238
|
-
eq(attachments.id, attachmentId),
|
|
239
|
-
eq(attachments.assistantId, assistantId),
|
|
240
|
-
),
|
|
241
|
-
)
|
|
241
|
+
.where(eq(attachments.id, attachmentId))
|
|
242
242
|
.get();
|
|
243
243
|
|
|
244
244
|
if (!existing) return 'not_found';
|
|
@@ -263,7 +263,6 @@ export function deleteAttachment(assistantId: string, attachmentId: string): Del
|
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
export function getAttachmentsByIds(
|
|
266
|
-
assistantId: string,
|
|
267
266
|
ids: string[],
|
|
268
267
|
): Array<StoredAttachment & { dataBase64: string }> {
|
|
269
268
|
if (ids.length === 0) return [];
|
|
@@ -273,21 +272,16 @@ export function getAttachmentsByIds(
|
|
|
273
272
|
const row = db
|
|
274
273
|
.select()
|
|
275
274
|
.from(attachments)
|
|
276
|
-
.where(
|
|
277
|
-
and(
|
|
278
|
-
eq(attachments.id, id),
|
|
279
|
-
eq(attachments.assistantId, assistantId),
|
|
280
|
-
),
|
|
281
|
-
)
|
|
275
|
+
.where(eq(attachments.id, id))
|
|
282
276
|
.get();
|
|
283
277
|
if (row) {
|
|
284
278
|
results.push({
|
|
285
279
|
id: row.id,
|
|
286
|
-
assistantId: row.assistantId,
|
|
287
280
|
originalFilename: row.originalFilename,
|
|
288
281
|
mimeType: row.mimeType,
|
|
289
282
|
sizeBytes: row.sizeBytes,
|
|
290
283
|
kind: row.kind,
|
|
284
|
+
thumbnailBase64: row.thumbnailBase64,
|
|
291
285
|
dataBase64: row.dataBase64,
|
|
292
286
|
createdAt: row.createdAt,
|
|
293
287
|
});
|
|
@@ -318,7 +312,6 @@ export function linkAttachmentToMessage(
|
|
|
318
312
|
*/
|
|
319
313
|
export function getAttachmentsForMessage(
|
|
320
314
|
messageId: string,
|
|
321
|
-
assistantId: string,
|
|
322
315
|
): Array<StoredAttachment & { dataBase64: string }> {
|
|
323
316
|
const db = getDb();
|
|
324
317
|
const links = db
|
|
@@ -331,7 +324,7 @@ export function getAttachmentsForMessage(
|
|
|
331
324
|
if (links.length === 0) return [];
|
|
332
325
|
|
|
333
326
|
const ids = links.map((l) => l.attachmentId).filter((id): id is string => id !== null);
|
|
334
|
-
return getAttachmentsByIds(
|
|
327
|
+
return getAttachmentsByIds(ids);
|
|
335
328
|
}
|
|
336
329
|
|
|
337
330
|
/**
|
|
@@ -342,7 +335,6 @@ export function getAttachmentsForMessage(
|
|
|
342
335
|
*/
|
|
343
336
|
export function getAttachmentMetadataForMessage(
|
|
344
337
|
messageId: string,
|
|
345
|
-
assistantId: string,
|
|
346
338
|
): StoredAttachment[] {
|
|
347
339
|
const db = getDb();
|
|
348
340
|
const links = db
|
|
@@ -360,75 +352,28 @@ export function getAttachmentMetadataForMessage(
|
|
|
360
352
|
const row = db
|
|
361
353
|
.select({
|
|
362
354
|
id: attachments.id,
|
|
363
|
-
assistantId: attachments.assistantId,
|
|
364
355
|
originalFilename: attachments.originalFilename,
|
|
365
356
|
mimeType: attachments.mimeType,
|
|
366
357
|
sizeBytes: attachments.sizeBytes,
|
|
367
358
|
kind: attachments.kind,
|
|
359
|
+
thumbnailBase64: attachments.thumbnailBase64,
|
|
368
360
|
createdAt: attachments.createdAt,
|
|
369
361
|
})
|
|
370
362
|
.from(attachments)
|
|
371
|
-
.where(
|
|
372
|
-
and(
|
|
373
|
-
eq(attachments.id, link.attachmentId),
|
|
374
|
-
eq(attachments.assistantId, assistantId),
|
|
375
|
-
),
|
|
376
|
-
)
|
|
377
|
-
.get();
|
|
378
|
-
if (row) results.push(row);
|
|
379
|
-
}
|
|
380
|
-
return results;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Return all attachments linked to a message without assistant scoping.
|
|
385
|
-
* Used by the desktop IPC history handler where tenant isolation is not needed.
|
|
386
|
-
*/
|
|
387
|
-
export function getAttachmentsForMessageUnscoped(
|
|
388
|
-
messageId: string,
|
|
389
|
-
): Array<StoredAttachment & { dataBase64: string }> {
|
|
390
|
-
const db = getDb();
|
|
391
|
-
const links = db
|
|
392
|
-
.select({ attachmentId: messageAttachments.attachmentId, position: messageAttachments.position })
|
|
393
|
-
.from(messageAttachments)
|
|
394
|
-
.where(eq(messageAttachments.messageId, messageId))
|
|
395
|
-
.orderBy(messageAttachments.position)
|
|
396
|
-
.all();
|
|
397
|
-
|
|
398
|
-
if (links.length === 0) return [];
|
|
399
|
-
|
|
400
|
-
const results: Array<StoredAttachment & { dataBase64: string }> = [];
|
|
401
|
-
for (const link of links) {
|
|
402
|
-
if (!link.attachmentId) continue;
|
|
403
|
-
const row = db
|
|
404
|
-
.select()
|
|
405
|
-
.from(attachments)
|
|
406
363
|
.where(eq(attachments.id, link.attachmentId))
|
|
407
364
|
.get();
|
|
408
|
-
if (row)
|
|
409
|
-
results.push({
|
|
410
|
-
id: row.id,
|
|
411
|
-
assistantId: row.assistantId,
|
|
412
|
-
originalFilename: row.originalFilename,
|
|
413
|
-
mimeType: row.mimeType,
|
|
414
|
-
sizeBytes: row.sizeBytes,
|
|
415
|
-
kind: row.kind,
|
|
416
|
-
dataBase64: row.dataBase64,
|
|
417
|
-
createdAt: row.createdAt,
|
|
418
|
-
});
|
|
419
|
-
}
|
|
365
|
+
if (row) results.push(row);
|
|
420
366
|
}
|
|
421
367
|
return results;
|
|
422
368
|
}
|
|
423
369
|
|
|
424
370
|
/**
|
|
425
|
-
* Retrieve a single attachment by ID
|
|
371
|
+
* Retrieve a single attachment by ID.
|
|
426
372
|
*/
|
|
427
373
|
export function getAttachmentById(
|
|
428
|
-
assistantId: string,
|
|
429
374
|
attachmentId: string,
|
|
430
375
|
): (StoredAttachment & { dataBase64: string }) | null {
|
|
431
|
-
const results = getAttachmentsByIds(
|
|
376
|
+
const results = getAttachmentsByIds([attachmentId]);
|
|
432
377
|
return results[0] ?? null;
|
|
433
378
|
}
|
|
434
379
|
|
|
@@ -35,10 +35,9 @@ export interface RecordInboundOptions {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Record an inbound channel event. Returns `duplicate: true` if this
|
|
38
|
-
* exact (
|
|
38
|
+
* exact (channel, chat, message) combination was already seen.
|
|
39
39
|
*/
|
|
40
40
|
export function recordInbound(
|
|
41
|
-
assistantId: string,
|
|
42
41
|
sourceChannel: string,
|
|
43
42
|
externalChatId: string,
|
|
44
43
|
externalMessageId: string,
|
|
@@ -54,7 +53,6 @@ export function recordInbound(
|
|
|
54
53
|
.from(channelInboundEvents)
|
|
55
54
|
.where(
|
|
56
55
|
and(
|
|
57
|
-
eq(channelInboundEvents.assistantId, assistantId),
|
|
58
56
|
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
59
57
|
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
60
58
|
eq(channelInboundEvents.externalMessageId, externalMessageId),
|
|
@@ -72,7 +70,7 @@ export function recordInbound(
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
const conversationKey = `${sourceChannel}:${externalChatId}`;
|
|
75
|
-
const mapping = getOrCreateConversation(
|
|
73
|
+
const mapping = getOrCreateConversation(conversationKey);
|
|
76
74
|
const now = Date.now();
|
|
77
75
|
const eventId = uuid();
|
|
78
76
|
|
|
@@ -84,7 +82,7 @@ export function recordInbound(
|
|
|
84
82
|
tx.insert(channelInboundEvents)
|
|
85
83
|
.values({
|
|
86
84
|
id: eventId,
|
|
87
|
-
assistantId,
|
|
85
|
+
assistantId: 'self',
|
|
88
86
|
sourceChannel,
|
|
89
87
|
externalChatId,
|
|
90
88
|
externalMessageId,
|
|
@@ -122,7 +120,6 @@ export function linkMessage(eventId: string, messageId: string): void {
|
|
|
122
120
|
* platform-level message identifier (e.g. Telegram message_id).
|
|
123
121
|
*/
|
|
124
122
|
export function findMessageBySourceId(
|
|
125
|
-
assistantId: string,
|
|
126
123
|
sourceChannel: string,
|
|
127
124
|
externalChatId: string,
|
|
128
125
|
sourceMessageId: string,
|
|
@@ -136,7 +133,6 @@ export function findMessageBySourceId(
|
|
|
136
133
|
.from(channelInboundEvents)
|
|
137
134
|
.where(
|
|
138
135
|
and(
|
|
139
|
-
eq(channelInboundEvents.assistantId, assistantId),
|
|
140
136
|
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
141
137
|
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
142
138
|
eq(channelInboundEvents.sourceMessageId, sourceMessageId),
|
|
@@ -153,7 +149,6 @@ export function findMessageBySourceId(
|
|
|
153
149
|
* Acknowledge delivery of an outbound message for a channel event.
|
|
154
150
|
*/
|
|
155
151
|
export function acknowledgeDelivery(
|
|
156
|
-
assistantId: string,
|
|
157
152
|
sourceChannel: string,
|
|
158
153
|
externalChatId: string,
|
|
159
154
|
externalMessageId: string,
|
|
@@ -166,7 +161,6 @@ export function acknowledgeDelivery(
|
|
|
166
161
|
.from(channelInboundEvents)
|
|
167
162
|
.where(
|
|
168
163
|
and(
|
|
169
|
-
eq(channelInboundEvents.assistantId, assistantId),
|
|
170
164
|
eq(channelInboundEvents.sourceChannel, sourceChannel),
|
|
171
165
|
eq(channelInboundEvents.externalChatId, externalChatId),
|
|
172
166
|
eq(channelInboundEvents.externalMessageId, externalMessageId),
|
|
@@ -271,7 +265,6 @@ export function recordProcessingFailure(eventId: string, err: unknown): void {
|
|
|
271
265
|
/** Fetch events eligible for automatic retry (failed + past their backoff). */
|
|
272
266
|
export function getRetryableEvents(limit = 20): Array<{
|
|
273
267
|
id: string;
|
|
274
|
-
assistantId: string;
|
|
275
268
|
conversationId: string;
|
|
276
269
|
processingAttempts: number;
|
|
277
270
|
rawPayload: string | null;
|
|
@@ -281,7 +274,6 @@ export function getRetryableEvents(limit = 20): Array<{
|
|
|
281
274
|
return db
|
|
282
275
|
.select({
|
|
283
276
|
id: channelInboundEvents.id,
|
|
284
|
-
assistantId: channelInboundEvents.assistantId,
|
|
285
277
|
conversationId: channelInboundEvents.conversationId,
|
|
286
278
|
processingAttempts: channelInboundEvents.processingAttempts,
|
|
287
279
|
rawPayload: channelInboundEvents.rawPayload,
|
|
@@ -297,8 +289,8 @@ export function getRetryableEvents(limit = 20): Array<{
|
|
|
297
289
|
.all();
|
|
298
290
|
}
|
|
299
291
|
|
|
300
|
-
/** Fetch dead-lettered events
|
|
301
|
-
export function getDeadLetterEvents(
|
|
292
|
+
/** Fetch dead-lettered events. */
|
|
293
|
+
export function getDeadLetterEvents(): Array<{
|
|
302
294
|
id: string;
|
|
303
295
|
sourceChannel: string;
|
|
304
296
|
externalChatId: string;
|
|
@@ -321,12 +313,7 @@ export function getDeadLetterEvents(assistantId: string): Array<{
|
|
|
321
313
|
createdAt: channelInboundEvents.createdAt,
|
|
322
314
|
})
|
|
323
315
|
.from(channelInboundEvents)
|
|
324
|
-
.where(
|
|
325
|
-
and(
|
|
326
|
-
eq(channelInboundEvents.assistantId, assistantId),
|
|
327
|
-
eq(channelInboundEvents.processingStatus, 'dead_letter'),
|
|
328
|
-
),
|
|
329
|
-
)
|
|
316
|
+
.where(eq(channelInboundEvents.processingStatus, 'dead_letter'))
|
|
330
317
|
.all();
|
|
331
318
|
}
|
|
332
319
|
|
|
@@ -334,7 +321,7 @@ export function getDeadLetterEvents(assistantId: string): Array<{
|
|
|
334
321
|
* Reset dead-lettered events back to 'failed' so the sweep can retry
|
|
335
322
|
* them. Resets attempt counter and sets an immediate retry_after.
|
|
336
323
|
*/
|
|
337
|
-
export function replayDeadLetters(
|
|
324
|
+
export function replayDeadLetters(eventIds: string[]): number {
|
|
338
325
|
const db = getDb();
|
|
339
326
|
const now = Date.now();
|
|
340
327
|
let count = 0;
|
|
@@ -345,7 +332,6 @@ export function replayDeadLetters(assistantId: string, eventIds: string[]): numb
|
|
|
345
332
|
.where(
|
|
346
333
|
and(
|
|
347
334
|
eq(channelInboundEvents.id, id),
|
|
348
|
-
eq(channelInboundEvents.assistantId, assistantId),
|
|
349
335
|
eq(channelInboundEvents.processingStatus, 'dead_letter'),
|
|
350
336
|
),
|
|
351
337
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import { getConfig } from '../config/loader.js';
|
|
3
|
+
import { truncate } from '../util/truncate.js';
|
|
3
4
|
|
|
4
5
|
const DEFAULT_RESOLVER_MODEL = 'claude-haiku-4-5-20251001';
|
|
5
6
|
const DEFAULT_RESOLVER_TIMEOUT_MS = 12_000;
|
|
@@ -85,7 +86,7 @@ export async function resolveConflictClarification(
|
|
|
85
86
|
resolution: 'still_unclear',
|
|
86
87
|
strategy: 'llm_error',
|
|
87
88
|
resolvedStatement: null,
|
|
88
|
-
explanation: `Clarification resolver failed: ${message
|
|
89
|
+
explanation: `Clarification resolver failed: ${truncate(message, 300, '')}`,
|
|
89
90
|
};
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -243,7 +244,7 @@ async function resolveWithLlm(
|
|
|
243
244
|
resolution: parsed.resolution,
|
|
244
245
|
strategy: 'llm',
|
|
245
246
|
resolvedStatement,
|
|
246
|
-
explanation: normalize(parsed.explanation ?? 'Resolved via LLM fallback.')
|
|
247
|
+
explanation: truncate(normalize(parsed.explanation ?? 'Resolved via LLM fallback.'), 500, ''),
|
|
247
248
|
};
|
|
248
249
|
} catch (err) {
|
|
249
250
|
clearTimeout(timer);
|
|
@@ -291,7 +292,7 @@ function buildMergedStatement(input: ClarificationResolverInput): string {
|
|
|
291
292
|
if (normalizedUserMessage.length >= 8 && normalizedUserMessage.length <= 320) {
|
|
292
293
|
return normalizedUserMessage;
|
|
293
294
|
}
|
|
294
|
-
const existing = normalize(input.existingStatement)
|
|
295
|
-
const candidate = normalize(input.candidateStatement)
|
|
296
|
-
return `Merged clarification: ${existing}; ${candidate}
|
|
295
|
+
const existing = truncate(normalize(input.existingStatement), 140, '');
|
|
296
|
+
const candidate = truncate(normalize(input.candidateStatement), 140, '');
|
|
297
|
+
return truncate(`Merged clarification: ${existing}; ${candidate}`, 320, '');
|
|
297
298
|
}
|
|
@@ -2,6 +2,7 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
2
2
|
import { eq } from 'drizzle-orm';
|
|
3
3
|
import { getConfig } from '../config/loader.js';
|
|
4
4
|
import { getLogger } from '../util/logger.js';
|
|
5
|
+
import { truncate } from '../util/truncate.js';
|
|
5
6
|
import { createOrUpdatePendingConflict } from './conflict-store.js';
|
|
6
7
|
import { getDb } from './db.js';
|
|
7
8
|
import { enqueueMemoryJob } from './jobs-store.js';
|
|
@@ -234,7 +235,7 @@ async function classifyRelationship(
|
|
|
234
235
|
|
|
235
236
|
return {
|
|
236
237
|
relationship,
|
|
237
|
-
explanation: String(input.explanation ?? '')
|
|
238
|
+
explanation: truncate(String(input.explanation ?? ''), 500, ''),
|
|
238
239
|
};
|
|
239
240
|
}
|
|
240
241
|
|
|
@@ -324,6 +325,6 @@ function escapeSqlLike(s: string): string {
|
|
|
324
325
|
|
|
325
326
|
function buildClarificationQuestion(existingStatement: string, candidateStatement: string): string {
|
|
326
327
|
const normalize = (input: string): string =>
|
|
327
|
-
input.replace(/\s+/g, ' ').trim()
|
|
328
|
+
truncate(input.replace(/\s+/g, ' ').trim(), 180, '');
|
|
328
329
|
return `I have conflicting notes: "${normalize(existingStatement)}" vs "${normalize(candidateStatement)}". Which one is correct?`;
|
|
329
330
|
}
|
|
@@ -1,49 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Maps
|
|
2
|
+
* Maps conversation keys to internal conversation IDs.
|
|
3
3
|
*
|
|
4
4
|
* The web UI identifies conversations by an opaque `conversationKey` (e.g.
|
|
5
5
|
* a user ID, a channel thread ID). This store resolves those keys to the
|
|
6
|
-
*
|
|
6
|
+
* daemon's internal conversation IDs, creating new conversations on
|
|
7
7
|
* first contact.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { eq
|
|
10
|
+
import { eq } from 'drizzle-orm';
|
|
11
11
|
import { v4 as uuid } from 'uuid';
|
|
12
12
|
import { getDb } from './db.js';
|
|
13
13
|
import { conversations, conversationKeys } from './schema.js';
|
|
14
14
|
|
|
15
15
|
export interface ConversationKeyMapping {
|
|
16
16
|
id: string;
|
|
17
|
-
assistantId: string;
|
|
18
17
|
conversationKey: string;
|
|
19
18
|
conversationId: string;
|
|
20
19
|
createdAt: number;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
|
-
* Look up the conversation ID for a given
|
|
23
|
+
* Look up the conversation ID for a given conversationKey.
|
|
25
24
|
* Returns `null` if no mapping exists yet.
|
|
26
25
|
*/
|
|
27
26
|
export function getConversationByKey(
|
|
28
|
-
assistantId: string,
|
|
29
27
|
conversationKey: string,
|
|
30
28
|
): ConversationKeyMapping | null {
|
|
31
29
|
const db = getDb();
|
|
32
30
|
const result = db
|
|
33
31
|
.select()
|
|
34
32
|
.from(conversationKeys)
|
|
35
|
-
.where(
|
|
36
|
-
and(
|
|
37
|
-
eq(conversationKeys.assistantId, assistantId),
|
|
38
|
-
eq(conversationKeys.conversationKey, conversationKey),
|
|
39
|
-
),
|
|
40
|
-
)
|
|
33
|
+
.where(eq(conversationKeys.conversationKey, conversationKey))
|
|
41
34
|
.get();
|
|
42
35
|
return result ?? null;
|
|
43
36
|
}
|
|
44
37
|
|
|
45
38
|
/**
|
|
46
|
-
* Delete the conversation-key mapping for a given
|
|
39
|
+
* Delete the conversation-key mapping for a given conversationKey.
|
|
47
40
|
*
|
|
48
41
|
* This is a soft reset: the old conversation data remains in the database,
|
|
49
42
|
* but it is no longer reachable via this key. The next message with the
|
|
@@ -51,29 +44,22 @@ export function getConversationByKey(
|
|
|
51
44
|
*
|
|
52
45
|
*/
|
|
53
46
|
export function deleteConversationKey(
|
|
54
|
-
assistantId: string,
|
|
55
47
|
conversationKey: string,
|
|
56
48
|
): void {
|
|
57
49
|
const db = getDb();
|
|
58
50
|
db.delete(conversationKeys)
|
|
59
|
-
.where(
|
|
60
|
-
and(
|
|
61
|
-
eq(conversationKeys.assistantId, assistantId),
|
|
62
|
-
eq(conversationKeys.conversationKey, conversationKey),
|
|
63
|
-
),
|
|
64
|
-
)
|
|
51
|
+
.where(eq(conversationKeys.conversationKey, conversationKey))
|
|
65
52
|
.run();
|
|
66
53
|
}
|
|
67
54
|
|
|
68
55
|
/**
|
|
69
|
-
* Get or create a conversation for the given
|
|
56
|
+
* Get or create a conversation for the given conversationKey.
|
|
70
57
|
*
|
|
71
58
|
* If a mapping already exists, returns the existing conversation ID.
|
|
72
59
|
* Otherwise, creates a new conversation and mapping atomically within a
|
|
73
60
|
* single transaction to prevent race conditions and orphaned rows.
|
|
74
61
|
*/
|
|
75
62
|
export function getOrCreateConversation(
|
|
76
|
-
assistantId: string,
|
|
77
63
|
conversationKey: string,
|
|
78
64
|
): { conversationId: string; created: boolean } {
|
|
79
65
|
const db = getDb();
|
|
@@ -82,12 +68,7 @@ export function getOrCreateConversation(
|
|
|
82
68
|
const existing = tx
|
|
83
69
|
.select()
|
|
84
70
|
.from(conversationKeys)
|
|
85
|
-
.where(
|
|
86
|
-
and(
|
|
87
|
-
eq(conversationKeys.assistantId, assistantId),
|
|
88
|
-
eq(conversationKeys.conversationKey, conversationKey),
|
|
89
|
-
),
|
|
90
|
-
)
|
|
71
|
+
.where(eq(conversationKeys.conversationKey, conversationKey))
|
|
91
72
|
.get();
|
|
92
73
|
|
|
93
74
|
if (existing) {
|
|
@@ -115,7 +96,7 @@ export function getOrCreateConversation(
|
|
|
115
96
|
tx.insert(conversationKeys)
|
|
116
97
|
.values({
|
|
117
98
|
id: uuid(),
|
|
118
|
-
assistantId,
|
|
99
|
+
assistantId: 'self',
|
|
119
100
|
conversationKey,
|
|
120
101
|
conversationId,
|
|
121
102
|
createdAt: now,
|
|
@@ -108,7 +108,7 @@ export function getLatestConversation() {
|
|
|
108
108
|
return result ?? null;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
export function addMessage(conversationId: string, role: string, content: string) {
|
|
111
|
+
export function addMessage(conversationId: string, role: string, content: string, metadata?: Record<string, unknown>) {
|
|
112
112
|
const db = getDb();
|
|
113
113
|
const now = monotonicNow();
|
|
114
114
|
const message = {
|
|
@@ -117,6 +117,7 @@ export function addMessage(conversationId: string, role: string, content: string
|
|
|
117
117
|
role,
|
|
118
118
|
content,
|
|
119
119
|
createdAt: now,
|
|
120
|
+
...(metadata ? { metadata: JSON.stringify(metadata) } : {}),
|
|
120
121
|
};
|
|
121
122
|
// Wrap insert + updatedAt bump in a transaction so they're atomic.
|
|
122
123
|
db.transaction((tx) => {
|