crewly 1.20.149 → 1.20.150
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/dist/backend/backend/src/constants.d.ts +76 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +77 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +22 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +2 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js +42 -1
- package/dist/backend/backend/src/controllers/messaging/messenger.routes.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +8 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +62 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.js +3 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.relay-adapter.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +94 -2
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +183 -2
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/owner-inbound.utils.d.ts +70 -0
- package/dist/backend/backend/src/services/chat-v2/owner-inbound.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/owner-inbound.utils.js +78 -0
- package/dist/backend/backend/src/services/chat-v2/owner-inbound.utils.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js +9 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/cloud-outbox.store.d.ts +136 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/cloud-outbox.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/cloud-outbox.store.js +207 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/cloud-outbox.store.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +62 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +111 -4
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/sqlite/unified-log.d.ts +206 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/unified-log.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/unified-log.js +442 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/unified-log.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/types.d.ts +29 -0
- package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/conversation-cloud-sync.service.d.ts +209 -0
- package/dist/backend/backend/src/services/cloud/conversation-cloud-sync.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/conversation-cloud-sync.service.js +607 -0
- package/dist/backend/backend/src/services/cloud/conversation-cloud-sync.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/conversation-ingest.contract.d.ts +167 -0
- package/dist/backend/backend/src/services/cloud/conversation-ingest.contract.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/conversation-ingest.contract.js +127 -0
- package/dist/backend/backend/src/services/cloud/conversation-ingest.contract.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +4 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/google-chat-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/google-chat-initializer.js +11 -1
- package/dist/backend/backend/src/services/messaging/google-chat-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js +13 -0
- package/dist/backend/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +13 -0
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +14 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +34 -2
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +1 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.d.ts +10 -0
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.js +27 -2
- package/dist/backend/backend/src/services/telegram/telegram-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts +10 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +27 -4
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/types/slack.types.d.ts +12 -0
- package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/slack.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +76 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +77 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts +94 -2
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js +183 -2
- package/dist/cli/backend/src/services/chat-v2/chat-v2.service.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.js +9 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/chat-db.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/cloud-outbox.store.d.ts +136 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/cloud-outbox.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/cloud-outbox.store.js +207 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/cloud-outbox.store.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts +62 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js +111 -4
- package/dist/cli/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/sqlite/unified-log.d.ts +206 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/unified-log.d.ts.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/unified-log.js +442 -0
- package/dist/cli/backend/src/services/chat-v2/sqlite/unified-log.js.map +1 -0
- package/dist/cli/backend/src/services/chat-v2/types.d.ts +29 -0
- package/dist/cli/backend/src/services/chat-v2/types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/chat-v2/types.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js +13 -0
- package/dist/cli/backend/src/services/slack/slack-agent-dm.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +14 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +34 -2
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +1 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/types/slack.types.d.ts +12 -0
- package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/slack.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified conversation log — the columns, outbox and migration that make
|
|
3
|
+
* `chat.db` the machine's superset of every conversation surface
|
|
4
|
+
* (specs/unified-conversations-cloud-store.md §A.4).
|
|
5
|
+
*
|
|
6
|
+
* Every message row carries first-class copies of what used to be hidden in
|
|
7
|
+
* `metadata` JSON:
|
|
8
|
+
*
|
|
9
|
+
* - `source` — which surface it belongs to (slack, crewly-chat, cloud-talk, …)
|
|
10
|
+
* - `direction` — `in` (to an agent), `out` (from an agent), `internal`
|
|
11
|
+
* - `sender_kind` — `owner`, `agent`, `human` (someone else), `system`
|
|
12
|
+
* - `agent_session` — the agent the message is to/from (denormalised)
|
|
13
|
+
* - `ext_ref` — JSON of external ids (Slack workspace/channel/ts/thread, …)
|
|
14
|
+
* - `cloud_sync` — 1 when the row may leave the machine, 0 when not (owner
|
|
15
|
+
* decision O3: other people's chatter in shared Slack
|
|
16
|
+
* channels stays local)
|
|
17
|
+
*
|
|
18
|
+
* A trigger on `chat_messages` fills `cloud_outbox` for every syncable insert,
|
|
19
|
+
* so any writer — present or future, through `recordTurn` or not — produces
|
|
20
|
+
* an upload row.
|
|
21
|
+
*
|
|
22
|
+
* @module services/chat-v2/sqlite/unified-log
|
|
23
|
+
*/
|
|
24
|
+
import { CONVERSATION_LOG_CONSTANTS } from '../../../constants.js';
|
|
25
|
+
import type { ChatChannelType, ChatSenderType } from '../types.js';
|
|
26
|
+
import type { ChatDatabase } from './chat-db.js';
|
|
27
|
+
/** Which surface a message belongs to. */
|
|
28
|
+
export type ConversationSource = (typeof CONVERSATION_LOG_CONSTANTS.SOURCES)[number];
|
|
29
|
+
/** Whether a message went to an agent, came from one, or neither. */
|
|
30
|
+
export type ConversationDirection = (typeof CONVERSATION_LOG_CONSTANTS.DIRECTIONS)[number];
|
|
31
|
+
/** Who wrote a message. */
|
|
32
|
+
export type ConversationSenderKind = (typeof CONVERSATION_LOG_CONSTANTS.SENDER_KINDS)[number];
|
|
33
|
+
/** External ids kept per message (only the fields that apply are present). */
|
|
34
|
+
export interface ConversationExtRef {
|
|
35
|
+
slackTeamId?: string;
|
|
36
|
+
slackChannelId?: string;
|
|
37
|
+
/** Slack message ts */
|
|
38
|
+
ts?: string;
|
|
39
|
+
/** Slack thread root ts */
|
|
40
|
+
threadTs?: string;
|
|
41
|
+
slackUserId?: string;
|
|
42
|
+
telegramChatId?: string;
|
|
43
|
+
telegramMessageId?: string;
|
|
44
|
+
gchatSpace?: string;
|
|
45
|
+
gchatThread?: string;
|
|
46
|
+
gchatMessage?: string;
|
|
47
|
+
whatsappChatId?: string;
|
|
48
|
+
}
|
|
49
|
+
/** The derived columns written with every insert. */
|
|
50
|
+
export interface UnifiedColumns {
|
|
51
|
+
source: ConversationSource;
|
|
52
|
+
direction: ConversationDirection;
|
|
53
|
+
senderKind: ConversationSenderKind;
|
|
54
|
+
agentSession: string | null;
|
|
55
|
+
extRef: ConversationExtRef | null;
|
|
56
|
+
/** 1 = may be uploaded to Cloud, 0 = stays on this machine. */
|
|
57
|
+
cloudSync: 0 | 1;
|
|
58
|
+
}
|
|
59
|
+
/** Who the owner is on the surfaces that carry other people too. */
|
|
60
|
+
export interface OwnerIdentity {
|
|
61
|
+
/** The owner's Slack user id (the person who installed the app), when known. */
|
|
62
|
+
slackUserId?: string | null;
|
|
63
|
+
/** The Slack workspace this machine serves, used when a row carries no team id. */
|
|
64
|
+
slackTeamId?: string | null;
|
|
65
|
+
}
|
|
66
|
+
/** Inputs of {@link deriveUnifiedColumns}. */
|
|
67
|
+
export interface DeriveUnifiedColumnsInput {
|
|
68
|
+
senderType: ChatSenderType;
|
|
69
|
+
senderId: string;
|
|
70
|
+
/** Parsed metadata (may be empty). */
|
|
71
|
+
metadata: Record<string, unknown>;
|
|
72
|
+
/** Mentioned agent sessions / member ids. */
|
|
73
|
+
mentions: readonly string[];
|
|
74
|
+
channel: {
|
|
75
|
+
id: string;
|
|
76
|
+
type: ChatChannelType;
|
|
77
|
+
agentSession: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* `source` column of the channel's most recent `user` row. An agent's reply
|
|
81
|
+
* belongs to the surface it answers (spec §A.4: reply-tool takes the
|
|
82
|
+
* inbound channel's source).
|
|
83
|
+
*/
|
|
84
|
+
latestInboundSource?: string | null;
|
|
85
|
+
owner?: OwnerIdentity | null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Whether a value is one of the {@link ConversationSource} values.
|
|
89
|
+
*
|
|
90
|
+
* @param value - Candidate
|
|
91
|
+
* @returns True for a known source
|
|
92
|
+
*/
|
|
93
|
+
export declare function isConversationSource(value: unknown): value is ConversationSource;
|
|
94
|
+
/**
|
|
95
|
+
* The surface a channel id names, for legacy rows whose tag is missing or
|
|
96
|
+
* wrong (WhatsApp rows were tagged `slack` before G2).
|
|
97
|
+
*
|
|
98
|
+
* @param channelId - chat-v2 channel id
|
|
99
|
+
* @returns The surface, or null when the id says nothing
|
|
100
|
+
*/
|
|
101
|
+
export declare function sourceFromChannelId(channelId: string): ConversationSource | null;
|
|
102
|
+
/**
|
|
103
|
+
* Derive the unified-log columns for one message.
|
|
104
|
+
*
|
|
105
|
+
* Pure: every input is passed in, so the same function serves live inserts
|
|
106
|
+
* and the legacy-row backfill.
|
|
107
|
+
*
|
|
108
|
+
* O3 (owner decision 2026-09-28): in a shared Slack channel (a `huddle` or
|
|
109
|
+
* `channel` room), only messages involving this account's agents may leave
|
|
110
|
+
* the machine — the owner's own messages, any local agent's message, and a
|
|
111
|
+
* message that @-mentions one of our agents. Anyone else's chatter is
|
|
112
|
+
* `cloud_sync = 0`.
|
|
113
|
+
*
|
|
114
|
+
* @param input - Row and channel facts
|
|
115
|
+
* @returns The derived columns
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* deriveUnifiedColumns({
|
|
120
|
+
* senderType: 'user', senderId: 'Maya', mentions: [],
|
|
121
|
+
* metadata: { source: 'slack', slackUserId: 'U2' },
|
|
122
|
+
* channel: { id: 'huddle-1', type: 'huddle', agentSession: '' },
|
|
123
|
+
* owner: { slackUserId: 'U1' },
|
|
124
|
+
* }).cloudSync; // 0 — someone else's chatter
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function deriveUnifiedColumns(input: DeriveUnifiedColumnsInput): UnifiedColumns;
|
|
128
|
+
/**
|
|
129
|
+
* Parse a stored metadata / mentions JSON column without throwing.
|
|
130
|
+
*
|
|
131
|
+
* @param raw - Column value
|
|
132
|
+
* @returns Parsed object (metadata) — `{}` when missing or malformed
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseMetadataColumn(raw: string | null | undefined): Record<string, unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* Parse a stored mentions JSON column without throwing.
|
|
137
|
+
*
|
|
138
|
+
* @param raw - Column value
|
|
139
|
+
* @returns String entries, `[]` when missing or malformed
|
|
140
|
+
*/
|
|
141
|
+
export declare function parseMentionsColumn(raw: string | null | undefined): string[];
|
|
142
|
+
/** Columns added to `chat_messages` (name → ADD COLUMN clause). */
|
|
143
|
+
export declare const UNIFIED_LOG_MESSAGE_COLUMNS: ReadonlyArray<{
|
|
144
|
+
name: string;
|
|
145
|
+
addClause: string;
|
|
146
|
+
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Outbox, sync state, per-agent index and outbox triggers. Idempotent.
|
|
149
|
+
*
|
|
150
|
+
* The triggers skip rows with `cloud_sync = 0` (O3). An update enqueues only
|
|
151
|
+
* when user-visible content changes or a row becomes syncable (owner
|
|
152
|
+
* reclassification); metadata-only bookkeeping does not.
|
|
153
|
+
*/
|
|
154
|
+
export declare const UNIFIED_LOG_SQL = "\nCREATE INDEX IF NOT EXISTS ix_messages_agent_created\n ON chat_messages(agent_session, created_at DESC);\n\n-- Backfill pages history in created_at order (spec \u00A7B.4).\nCREATE INDEX IF NOT EXISTS ix_messages_created\n ON chat_messages(created_at);\n\nCREATE TABLE IF NOT EXISTS cloud_outbox (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n message_id TEXT NOT NULL,\n op TEXT NOT NULL CHECK(op IN ('upsert','delete')),\n enqueued_at INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS cloud_sync_state (\n k TEXT PRIMARY KEY,\n v TEXT\n);\n\nCREATE TRIGGER IF NOT EXISTS trg_outbox_ins AFTER INSERT ON chat_messages\n WHEN COALESCE(NEW.cloud_sync, 1) = 1\n BEGIN\n INSERT INTO cloud_outbox(message_id, op, enqueued_at) VALUES (NEW.id, 'upsert', NEW.created_at);\n END;\n\nCREATE TRIGGER IF NOT EXISTS trg_outbox_upd AFTER UPDATE OF content, cloud_sync ON chat_messages\n WHEN COALESCE(NEW.cloud_sync, 1) = 1\n AND (NEW.content IS NOT OLD.content OR COALESCE(OLD.cloud_sync, 1) <> 1)\n BEGIN\n INSERT INTO cloud_outbox(message_id, op, enqueued_at)\n VALUES (NEW.id, 'upsert', CAST(strftime('%s','now') AS INTEGER) * 1000);\n END;\n\nCREATE TRIGGER IF NOT EXISTS trg_outbox_del AFTER DELETE ON chat_messages\n WHEN COALESCE(OLD.cloud_sync, 1) = 1\n BEGIN\n INSERT INTO cloud_outbox(message_id, op, enqueued_at)\n VALUES (OLD.id, 'delete', CAST(strftime('%s','now') AS INTEGER) * 1000);\n END;\n";
|
|
155
|
+
/** Report of {@link applyUnifiedLogUpgrades}. */
|
|
156
|
+
export interface UnifiedLogUpgradeReport {
|
|
157
|
+
/** Columns added to `chat_messages` on this run. */
|
|
158
|
+
columnsAdded: string[];
|
|
159
|
+
/** Legacy rows given unified-log columns on this run. */
|
|
160
|
+
rowsBackfilled: number;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Bring `chat.db` up to the unified-log schema: add the columns, create the
|
|
164
|
+
* outbox / state tables, index and triggers, then backfill rows that have no
|
|
165
|
+
* `source` yet (every row written before this migration).
|
|
166
|
+
*
|
|
167
|
+
* Backfilled rows are NOT put in the outbox — the first-sign-in backfill
|
|
168
|
+
* (spec §B.4) uploads history within the plan's window.
|
|
169
|
+
*
|
|
170
|
+
* Safe on every boot: each step is a no-op once applied.
|
|
171
|
+
*
|
|
172
|
+
* @param db - The chat database handle
|
|
173
|
+
* @returns What changed
|
|
174
|
+
*/
|
|
175
|
+
export declare function applyUnifiedLogUpgrades(db: ChatDatabase): UnifiedLogUpgradeReport;
|
|
176
|
+
/**
|
|
177
|
+
* Give legacy rows (`source IS NULL`) their unified-log columns, best effort.
|
|
178
|
+
*
|
|
179
|
+
* Runs in pages of {@link CONVERSATION_LOG_CONSTANTS.BACKFILL_BATCH_SIZE}
|
|
180
|
+
* rows ordered by channel and seq, carrying each channel's latest inbound
|
|
181
|
+
* source forward so agent replies inherit the surface they answered. The
|
|
182
|
+
* owner's Slack id is unknown at boot, so shared-channel Slack rows land as
|
|
183
|
+
* `human`; {@link reclassifyOwnerSlackRows} upgrades them once it is known.
|
|
184
|
+
*
|
|
185
|
+
* Nothing is enqueued: the update trigger fires only when `content` changes
|
|
186
|
+
* or `cloud_sync` rises from a non-1 value, and legacy rows have
|
|
187
|
+
* `cloud_sync IS NULL`, which the trigger already treats as 1.
|
|
188
|
+
*
|
|
189
|
+
* @param db - The chat database handle
|
|
190
|
+
* @returns Rows updated
|
|
191
|
+
*/
|
|
192
|
+
export declare function backfillUnifiedColumns(db: ChatDatabase): number;
|
|
193
|
+
/**
|
|
194
|
+
* Once the owner's Slack user id is known, mark their Slack rows as the
|
|
195
|
+
* owner's and — in shared channels — syncable. Rows recorded before the id
|
|
196
|
+
* was available (legacy backfill, early boot) were conservatively `human`.
|
|
197
|
+
*
|
|
198
|
+
* Idempotent; the `cloud_sync` 0 → 1 flip enqueues those rows for upload
|
|
199
|
+
* through the update trigger.
|
|
200
|
+
*
|
|
201
|
+
* @param db - The chat database handle
|
|
202
|
+
* @param ownerSlackUserId - The owner's Slack user id
|
|
203
|
+
* @returns Rows reclassified
|
|
204
|
+
*/
|
|
205
|
+
export declare function reclassifyOwnerSlackRows(db: ChatDatabase, ownerSlackUserId: string): number;
|
|
206
|
+
//# sourceMappingURL=unified-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-log.d.ts","sourceRoot":"","sources":["../../../../../../../backend/src/services/chat-v2/sqlite/unified-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified conversation log — the columns, outbox and migration that make
|
|
3
|
+
* `chat.db` the machine's superset of every conversation surface
|
|
4
|
+
* (specs/unified-conversations-cloud-store.md §A.4).
|
|
5
|
+
*
|
|
6
|
+
* Every message row carries first-class copies of what used to be hidden in
|
|
7
|
+
* `metadata` JSON:
|
|
8
|
+
*
|
|
9
|
+
* - `source` — which surface it belongs to (slack, crewly-chat, cloud-talk, …)
|
|
10
|
+
* - `direction` — `in` (to an agent), `out` (from an agent), `internal`
|
|
11
|
+
* - `sender_kind` — `owner`, `agent`, `human` (someone else), `system`
|
|
12
|
+
* - `agent_session` — the agent the message is to/from (denormalised)
|
|
13
|
+
* - `ext_ref` — JSON of external ids (Slack workspace/channel/ts/thread, …)
|
|
14
|
+
* - `cloud_sync` — 1 when the row may leave the machine, 0 when not (owner
|
|
15
|
+
* decision O3: other people's chatter in shared Slack
|
|
16
|
+
* channels stays local)
|
|
17
|
+
*
|
|
18
|
+
* A trigger on `chat_messages` fills `cloud_outbox` for every syncable insert,
|
|
19
|
+
* so any writer — present or future, through `recordTurn` or not — produces
|
|
20
|
+
* an upload row.
|
|
21
|
+
*
|
|
22
|
+
* @module services/chat-v2/sqlite/unified-log
|
|
23
|
+
*/
|
|
24
|
+
import { CONVERSATION_LOG_CONSTANTS, OWNER_EVIDENCE_METADATA } from '../../../constants.js';
|
|
25
|
+
/** `metadata.source` values that tag an agent's reply rather than a surface. */
|
|
26
|
+
const REPLY_METADATA_SOURCES = new Set(['reply-tool', 'pty-runtime', 'in-process-runtime']);
|
|
27
|
+
/** Runtime capture tags — shown as `runtime` when nothing better is known. */
|
|
28
|
+
const RUNTIME_METADATA_SOURCES = new Set(['pty-runtime', 'in-process-runtime']);
|
|
29
|
+
/** Metadata sources that are already surface names. */
|
|
30
|
+
const DIRECT_METADATA_SOURCES = {
|
|
31
|
+
web: 'crewly-chat',
|
|
32
|
+
'crewly-chat': 'crewly-chat',
|
|
33
|
+
slack: 'slack',
|
|
34
|
+
telegram: 'telegram',
|
|
35
|
+
'google-chat': 'google-chat',
|
|
36
|
+
whatsapp: 'whatsapp',
|
|
37
|
+
'cloud-talk': 'cloud-talk',
|
|
38
|
+
system: 'system',
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Whether a value is one of the {@link ConversationSource} values.
|
|
42
|
+
*
|
|
43
|
+
* @param value - Candidate
|
|
44
|
+
* @returns True for a known source
|
|
45
|
+
*/
|
|
46
|
+
export function isConversationSource(value) {
|
|
47
|
+
return typeof value === 'string' && CONVERSATION_LOG_CONSTANTS.SOURCES.includes(value);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The surface a channel id names, for legacy rows whose tag is missing or
|
|
51
|
+
* wrong (WhatsApp rows were tagged `slack` before G2).
|
|
52
|
+
*
|
|
53
|
+
* @param channelId - chat-v2 channel id
|
|
54
|
+
* @returns The surface, or null when the id says nothing
|
|
55
|
+
*/
|
|
56
|
+
export function sourceFromChannelId(channelId) {
|
|
57
|
+
const id = channelId.toLowerCase();
|
|
58
|
+
const p = CONVERSATION_LOG_CONSTANTS.CHANNEL_PREFIXES;
|
|
59
|
+
if (id.startsWith(p.WHATSAPP))
|
|
60
|
+
return 'whatsapp';
|
|
61
|
+
if (id.startsWith(p.TELEGRAM))
|
|
62
|
+
return 'telegram';
|
|
63
|
+
if (id.startsWith(p.GOOGLE_CHAT))
|
|
64
|
+
return 'google-chat';
|
|
65
|
+
if (id.startsWith(p.SLACK))
|
|
66
|
+
return 'slack';
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read a non-empty string field.
|
|
71
|
+
*
|
|
72
|
+
* @param obj - Source object
|
|
73
|
+
* @param key - Field
|
|
74
|
+
* @returns The trimmed string, or undefined
|
|
75
|
+
*/
|
|
76
|
+
function str(obj, key) {
|
|
77
|
+
const v = obj[key];
|
|
78
|
+
if (typeof v === 'string' && v.trim().length > 0)
|
|
79
|
+
return v;
|
|
80
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
81
|
+
return String(v);
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the `source` column.
|
|
86
|
+
*
|
|
87
|
+
* @param input - Derivation input
|
|
88
|
+
* @returns The surface
|
|
89
|
+
*/
|
|
90
|
+
function deriveSource(input) {
|
|
91
|
+
const meta = str(input.metadata, 'source');
|
|
92
|
+
const byChannel = sourceFromChannelId(input.channel.id);
|
|
93
|
+
// Messenger channels are named after their surface; trust the name over a
|
|
94
|
+
// tag (a WhatsApp row tagged `slack` is WhatsApp).
|
|
95
|
+
if (byChannel && byChannel !== 'slack' && meta !== 'system')
|
|
96
|
+
return byChannel;
|
|
97
|
+
if (meta && DIRECT_METADATA_SOURCES[meta])
|
|
98
|
+
return DIRECT_METADATA_SOURCES[meta];
|
|
99
|
+
if (input.senderType === 'system')
|
|
100
|
+
return 'system';
|
|
101
|
+
if (input.senderType === 'agent' || (meta && REPLY_METADATA_SOURCES.has(meta))) {
|
|
102
|
+
if (isConversationSource(input.latestInboundSource))
|
|
103
|
+
return input.latestInboundSource;
|
|
104
|
+
if (byChannel)
|
|
105
|
+
return byChannel;
|
|
106
|
+
return meta && RUNTIME_METADATA_SOURCES.has(meta) ? 'runtime' : 'crewly-chat';
|
|
107
|
+
}
|
|
108
|
+
return byChannel ?? 'crewly-chat';
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Resolve the `sender_kind` column.
|
|
112
|
+
*
|
|
113
|
+
* `owner` for the owner's own surfaces (Crewly Chat, Cloud Talk, messenger
|
|
114
|
+
* bridges — the same rows the approval gate reads as the owner). On Slack,
|
|
115
|
+
* where other people post too, `owner` only when the Slack user is the
|
|
116
|
+
* owner; when the owner's id is unknown a DM still counts as the owner and a
|
|
117
|
+
* shared channel does not.
|
|
118
|
+
*
|
|
119
|
+
* @param input - Derivation input
|
|
120
|
+
* @param source - Resolved source
|
|
121
|
+
* @returns The sender kind
|
|
122
|
+
*/
|
|
123
|
+
function deriveSenderKind(input, source) {
|
|
124
|
+
if (input.senderType === 'agent')
|
|
125
|
+
return 'agent';
|
|
126
|
+
if (input.senderType === 'system')
|
|
127
|
+
return 'system';
|
|
128
|
+
const md = input.metadata;
|
|
129
|
+
if (str(md, OWNER_EVIDENCE_METADATA.AUTHOR_AGENT_SESSION) || str(md, OWNER_EVIDENCE_METADATA.REMOTE_AGENT_SESSION)) {
|
|
130
|
+
return 'agent';
|
|
131
|
+
}
|
|
132
|
+
const explicit = str(md, 'senderKind');
|
|
133
|
+
if (explicit && CONVERSATION_LOG_CONSTANTS.SENDER_KINDS.includes(explicit)) {
|
|
134
|
+
return explicit;
|
|
135
|
+
}
|
|
136
|
+
if (source !== 'slack')
|
|
137
|
+
return 'owner';
|
|
138
|
+
const slackUserId = str(md, 'slackUserId');
|
|
139
|
+
const ownerId = input.owner?.slackUserId ?? null;
|
|
140
|
+
if (slackUserId && ownerId)
|
|
141
|
+
return slackUserId === ownerId ? 'owner' : 'human';
|
|
142
|
+
return input.channel.type === 'dm' ? 'owner' : 'human';
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Pick the external ids that apply to this row.
|
|
146
|
+
*
|
|
147
|
+
* @param md - Metadata
|
|
148
|
+
* @param source - Resolved source
|
|
149
|
+
* @param owner - Owner identity (supplies the Slack workspace when the row has none)
|
|
150
|
+
* @returns The ext ref, or null when there is nothing to keep
|
|
151
|
+
*/
|
|
152
|
+
function deriveExtRef(md, source, owner) {
|
|
153
|
+
const ext = {};
|
|
154
|
+
const slackChannelId = str(md, 'slackChannelId');
|
|
155
|
+
if (slackChannelId || source === 'slack') {
|
|
156
|
+
const team = str(md, 'slackTeamId') ?? (slackChannelId ? owner?.slackTeamId ?? undefined : undefined);
|
|
157
|
+
if (team)
|
|
158
|
+
ext.slackTeamId = team;
|
|
159
|
+
if (slackChannelId)
|
|
160
|
+
ext.slackChannelId = slackChannelId;
|
|
161
|
+
const ts = str(md, 'slackTs');
|
|
162
|
+
if (ts)
|
|
163
|
+
ext.ts = ts;
|
|
164
|
+
const threadTs = str(md, 'slackThreadTs');
|
|
165
|
+
if (threadTs)
|
|
166
|
+
ext.threadTs = threadTs;
|
|
167
|
+
const user = str(md, 'slackUserId');
|
|
168
|
+
if (user)
|
|
169
|
+
ext.slackUserId = user;
|
|
170
|
+
}
|
|
171
|
+
const tgChat = str(md, 'telegramChatId');
|
|
172
|
+
if (tgChat)
|
|
173
|
+
ext.telegramChatId = tgChat;
|
|
174
|
+
const tgMsg = str(md, 'telegramMessageId');
|
|
175
|
+
if (tgMsg)
|
|
176
|
+
ext.telegramMessageId = tgMsg;
|
|
177
|
+
for (const key of ['gchatSpace', 'gchatThread', 'gchatMessage']) {
|
|
178
|
+
const v = str(md, key);
|
|
179
|
+
if (v)
|
|
180
|
+
ext[key] = v;
|
|
181
|
+
}
|
|
182
|
+
if (source === 'whatsapp') {
|
|
183
|
+
const chat = str(md, 'whatsappChatId') ?? str(md, 'chatId');
|
|
184
|
+
if (chat)
|
|
185
|
+
ext.whatsappChatId = chat;
|
|
186
|
+
}
|
|
187
|
+
return Object.keys(ext).length > 0 ? ext : null;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Derive the unified-log columns for one message.
|
|
191
|
+
*
|
|
192
|
+
* Pure: every input is passed in, so the same function serves live inserts
|
|
193
|
+
* and the legacy-row backfill.
|
|
194
|
+
*
|
|
195
|
+
* O3 (owner decision 2026-09-28): in a shared Slack channel (a `huddle` or
|
|
196
|
+
* `channel` room), only messages involving this account's agents may leave
|
|
197
|
+
* the machine — the owner's own messages, any local agent's message, and a
|
|
198
|
+
* message that @-mentions one of our agents. Anyone else's chatter is
|
|
199
|
+
* `cloud_sync = 0`.
|
|
200
|
+
*
|
|
201
|
+
* @param input - Row and channel facts
|
|
202
|
+
* @returns The derived columns
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* deriveUnifiedColumns({
|
|
207
|
+
* senderType: 'user', senderId: 'Maya', mentions: [],
|
|
208
|
+
* metadata: { source: 'slack', slackUserId: 'U2' },
|
|
209
|
+
* channel: { id: 'huddle-1', type: 'huddle', agentSession: '' },
|
|
210
|
+
* owner: { slackUserId: 'U1' },
|
|
211
|
+
* }).cloudSync; // 0 — someone else's chatter
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export function deriveUnifiedColumns(input) {
|
|
215
|
+
const source = deriveSource(input);
|
|
216
|
+
const senderKind = deriveSenderKind(input, source);
|
|
217
|
+
const direction = input.senderType === 'agent' ? 'out' : input.senderType === 'system' ? 'internal' : 'in';
|
|
218
|
+
let agentSession = input.channel.agentSession || null;
|
|
219
|
+
if (!agentSession) {
|
|
220
|
+
if (input.senderType === 'agent')
|
|
221
|
+
agentSession = input.senderId || null;
|
|
222
|
+
else
|
|
223
|
+
agentSession = input.mentions.find((m) => m.length > 0) ?? null;
|
|
224
|
+
}
|
|
225
|
+
const sharedRoom = input.channel.type === 'huddle' || input.channel.type === 'channel';
|
|
226
|
+
const othersChatter = sharedRoom && source === 'slack' && input.senderType === 'user' && senderKind !== 'owner' && input.mentions.length === 0;
|
|
227
|
+
return {
|
|
228
|
+
source,
|
|
229
|
+
// Other people's chatter is not addressed to an agent: `internal`, which
|
|
230
|
+
// is also how Cloud's O3 filter reads it (`in` = delivered to an agent).
|
|
231
|
+
direction: othersChatter ? 'internal' : direction,
|
|
232
|
+
senderKind,
|
|
233
|
+
agentSession,
|
|
234
|
+
extRef: deriveExtRef(input.metadata, source, input.owner),
|
|
235
|
+
cloudSync: othersChatter ? 0 : 1,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Parse a stored metadata / mentions JSON column without throwing.
|
|
240
|
+
*
|
|
241
|
+
* @param raw - Column value
|
|
242
|
+
* @returns Parsed object (metadata) — `{}` when missing or malformed
|
|
243
|
+
*/
|
|
244
|
+
export function parseMetadataColumn(raw) {
|
|
245
|
+
if (!raw)
|
|
246
|
+
return {};
|
|
247
|
+
try {
|
|
248
|
+
const parsed = JSON.parse(raw);
|
|
249
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return {};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Parse a stored mentions JSON column without throwing.
|
|
257
|
+
*
|
|
258
|
+
* @param raw - Column value
|
|
259
|
+
* @returns String entries, `[]` when missing or malformed
|
|
260
|
+
*/
|
|
261
|
+
export function parseMentionsColumn(raw) {
|
|
262
|
+
if (!raw)
|
|
263
|
+
return [];
|
|
264
|
+
try {
|
|
265
|
+
const parsed = JSON.parse(raw);
|
|
266
|
+
return Array.isArray(parsed) ? parsed.filter((m) => typeof m === 'string') : [];
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// Migration
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
/** Columns added to `chat_messages` (name → ADD COLUMN clause). */
|
|
276
|
+
export const UNIFIED_LOG_MESSAGE_COLUMNS = [
|
|
277
|
+
{ name: 'source', addClause: 'source TEXT' },
|
|
278
|
+
{ name: 'direction', addClause: 'direction TEXT' },
|
|
279
|
+
{ name: 'sender_kind', addClause: 'sender_kind TEXT' },
|
|
280
|
+
{ name: 'agent_session', addClause: 'agent_session TEXT' },
|
|
281
|
+
{ name: 'ext_ref', addClause: 'ext_ref TEXT' },
|
|
282
|
+
{ name: 'cloud_sync', addClause: 'cloud_sync INTEGER' },
|
|
283
|
+
];
|
|
284
|
+
/**
|
|
285
|
+
* Outbox, sync state, per-agent index and outbox triggers. Idempotent.
|
|
286
|
+
*
|
|
287
|
+
* The triggers skip rows with `cloud_sync = 0` (O3). An update enqueues only
|
|
288
|
+
* when user-visible content changes or a row becomes syncable (owner
|
|
289
|
+
* reclassification); metadata-only bookkeeping does not.
|
|
290
|
+
*/
|
|
291
|
+
export const UNIFIED_LOG_SQL = `
|
|
292
|
+
CREATE INDEX IF NOT EXISTS ix_messages_agent_created
|
|
293
|
+
ON chat_messages(agent_session, created_at DESC);
|
|
294
|
+
|
|
295
|
+
-- Backfill pages history in created_at order (spec §B.4).
|
|
296
|
+
CREATE INDEX IF NOT EXISTS ix_messages_created
|
|
297
|
+
ON chat_messages(created_at);
|
|
298
|
+
|
|
299
|
+
CREATE TABLE IF NOT EXISTS cloud_outbox (
|
|
300
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
301
|
+
message_id TEXT NOT NULL,
|
|
302
|
+
op TEXT NOT NULL CHECK(op IN ('upsert','delete')),
|
|
303
|
+
enqueued_at INTEGER NOT NULL
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
CREATE TABLE IF NOT EXISTS cloud_sync_state (
|
|
307
|
+
k TEXT PRIMARY KEY,
|
|
308
|
+
v TEXT
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
CREATE TRIGGER IF NOT EXISTS trg_outbox_ins AFTER INSERT ON chat_messages
|
|
312
|
+
WHEN COALESCE(NEW.cloud_sync, 1) = 1
|
|
313
|
+
BEGIN
|
|
314
|
+
INSERT INTO cloud_outbox(message_id, op, enqueued_at) VALUES (NEW.id, 'upsert', NEW.created_at);
|
|
315
|
+
END;
|
|
316
|
+
|
|
317
|
+
CREATE TRIGGER IF NOT EXISTS trg_outbox_upd AFTER UPDATE OF content, cloud_sync ON chat_messages
|
|
318
|
+
WHEN COALESCE(NEW.cloud_sync, 1) = 1
|
|
319
|
+
AND (NEW.content IS NOT OLD.content OR COALESCE(OLD.cloud_sync, 1) <> 1)
|
|
320
|
+
BEGIN
|
|
321
|
+
INSERT INTO cloud_outbox(message_id, op, enqueued_at)
|
|
322
|
+
VALUES (NEW.id, 'upsert', CAST(strftime('%s','now') AS INTEGER) * 1000);
|
|
323
|
+
END;
|
|
324
|
+
|
|
325
|
+
CREATE TRIGGER IF NOT EXISTS trg_outbox_del AFTER DELETE ON chat_messages
|
|
326
|
+
WHEN COALESCE(OLD.cloud_sync, 1) = 1
|
|
327
|
+
BEGIN
|
|
328
|
+
INSERT INTO cloud_outbox(message_id, op, enqueued_at)
|
|
329
|
+
VALUES (OLD.id, 'delete', CAST(strftime('%s','now') AS INTEGER) * 1000);
|
|
330
|
+
END;
|
|
331
|
+
`;
|
|
332
|
+
/**
|
|
333
|
+
* Bring `chat.db` up to the unified-log schema: add the columns, create the
|
|
334
|
+
* outbox / state tables, index and triggers, then backfill rows that have no
|
|
335
|
+
* `source` yet (every row written before this migration).
|
|
336
|
+
*
|
|
337
|
+
* Backfilled rows are NOT put in the outbox — the first-sign-in backfill
|
|
338
|
+
* (spec §B.4) uploads history within the plan's window.
|
|
339
|
+
*
|
|
340
|
+
* Safe on every boot: each step is a no-op once applied.
|
|
341
|
+
*
|
|
342
|
+
* @param db - The chat database handle
|
|
343
|
+
* @returns What changed
|
|
344
|
+
*/
|
|
345
|
+
export function applyUnifiedLogUpgrades(db) {
|
|
346
|
+
const existing = new Set(db.pragma('table_info(chat_messages)').map((c) => c.name));
|
|
347
|
+
const columnsAdded = [];
|
|
348
|
+
for (const col of UNIFIED_LOG_MESSAGE_COLUMNS) {
|
|
349
|
+
if (!existing.has(col.name)) {
|
|
350
|
+
db.exec(`ALTER TABLE chat_messages ADD COLUMN ${col.addClause}`);
|
|
351
|
+
columnsAdded.push(col.name);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
db.exec(UNIFIED_LOG_SQL);
|
|
355
|
+
const rowsBackfilled = backfillUnifiedColumns(db);
|
|
356
|
+
return { columnsAdded, rowsBackfilled };
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Give legacy rows (`source IS NULL`) their unified-log columns, best effort.
|
|
360
|
+
*
|
|
361
|
+
* Runs in pages of {@link CONVERSATION_LOG_CONSTANTS.BACKFILL_BATCH_SIZE}
|
|
362
|
+
* rows ordered by channel and seq, carrying each channel's latest inbound
|
|
363
|
+
* source forward so agent replies inherit the surface they answered. The
|
|
364
|
+
* owner's Slack id is unknown at boot, so shared-channel Slack rows land as
|
|
365
|
+
* `human`; {@link reclassifyOwnerSlackRows} upgrades them once it is known.
|
|
366
|
+
*
|
|
367
|
+
* Nothing is enqueued: the update trigger fires only when `content` changes
|
|
368
|
+
* or `cloud_sync` rises from a non-1 value, and legacy rows have
|
|
369
|
+
* `cloud_sync IS NULL`, which the trigger already treats as 1.
|
|
370
|
+
*
|
|
371
|
+
* @param db - The chat database handle
|
|
372
|
+
* @returns Rows updated
|
|
373
|
+
*/
|
|
374
|
+
export function backfillUnifiedColumns(db) {
|
|
375
|
+
const pending = db.prepare('SELECT 1 FROM chat_messages WHERE source IS NULL LIMIT 1').get();
|
|
376
|
+
if (!pending)
|
|
377
|
+
return 0;
|
|
378
|
+
const select = db.prepare(`SELECT m.rowid AS rowid, m.id, m.channel_id, m.sender_type, m.sender_id, m.metadata, m.mentions,
|
|
379
|
+
c.type AS ch_type, c.agent_session AS ch_agent
|
|
380
|
+
FROM chat_messages m LEFT JOIN chat_channels c ON c.id = m.channel_id
|
|
381
|
+
WHERE m.source IS NULL
|
|
382
|
+
ORDER BY m.channel_id, m.seq
|
|
383
|
+
LIMIT ?`);
|
|
384
|
+
const update = db.prepare(`UPDATE chat_messages
|
|
385
|
+
SET source = ?, direction = ?, sender_kind = ?, agent_session = ?, ext_ref = ?, cloud_sync = ?
|
|
386
|
+
WHERE rowid = ?`);
|
|
387
|
+
const lastInbound = new Map();
|
|
388
|
+
let total = 0;
|
|
389
|
+
const batch = CONVERSATION_LOG_CONSTANTS.BACKFILL_BATCH_SIZE;
|
|
390
|
+
for (;;) {
|
|
391
|
+
const rows = select.all(batch);
|
|
392
|
+
if (rows.length === 0)
|
|
393
|
+
break;
|
|
394
|
+
db.transaction(() => {
|
|
395
|
+
for (const r of rows) {
|
|
396
|
+
const cols = deriveUnifiedColumns({
|
|
397
|
+
senderType: r.sender_type,
|
|
398
|
+
senderId: r.sender_id,
|
|
399
|
+
metadata: parseMetadataColumn(r.metadata),
|
|
400
|
+
mentions: parseMentionsColumn(r.mentions),
|
|
401
|
+
channel: { id: r.channel_id, type: r.ch_type ?? 'dm', agentSession: r.ch_agent ?? '' },
|
|
402
|
+
latestInboundSource: lastInbound.get(r.channel_id) ?? null,
|
|
403
|
+
owner: null,
|
|
404
|
+
});
|
|
405
|
+
if (r.sender_type === 'user')
|
|
406
|
+
lastInbound.set(r.channel_id, cols.source);
|
|
407
|
+
update.run(cols.source, cols.direction, cols.senderKind, cols.agentSession, cols.extRef ? JSON.stringify(cols.extRef) : null, cols.cloudSync, r.rowid);
|
|
408
|
+
}
|
|
409
|
+
})();
|
|
410
|
+
total += rows.length;
|
|
411
|
+
if (rows.length < batch)
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
return total;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Once the owner's Slack user id is known, mark their Slack rows as the
|
|
418
|
+
* owner's and — in shared channels — syncable. Rows recorded before the id
|
|
419
|
+
* was available (legacy backfill, early boot) were conservatively `human`.
|
|
420
|
+
*
|
|
421
|
+
* Idempotent; the `cloud_sync` 0 → 1 flip enqueues those rows for upload
|
|
422
|
+
* through the update trigger.
|
|
423
|
+
*
|
|
424
|
+
* @param db - The chat database handle
|
|
425
|
+
* @param ownerSlackUserId - The owner's Slack user id
|
|
426
|
+
* @returns Rows reclassified
|
|
427
|
+
*/
|
|
428
|
+
export function reclassifyOwnerSlackRows(db, ownerSlackUserId) {
|
|
429
|
+
if (!ownerSlackUserId)
|
|
430
|
+
return 0;
|
|
431
|
+
const result = db
|
|
432
|
+
.prepare(`UPDATE chat_messages
|
|
433
|
+
SET sender_kind = 'owner', cloud_sync = 1, direction = 'in'
|
|
434
|
+
WHERE source = 'slack' AND sender_type = 'user' AND sender_kind = 'human'
|
|
435
|
+
AND json_valid(metadata)
|
|
436
|
+
AND json_extract(metadata, '$.slackUserId') = ?
|
|
437
|
+
AND json_extract(metadata, '$.${OWNER_EVIDENCE_METADATA.AUTHOR_AGENT_SESSION}') IS NULL
|
|
438
|
+
AND json_extract(metadata, '$.${OWNER_EVIDENCE_METADATA.REMOTE_AGENT_SESSION}') IS NULL`)
|
|
439
|
+
.run(ownerSlackUserId);
|
|
440
|
+
return result.changes;
|
|
441
|
+
}
|
|
442
|
+
//# sourceMappingURL=unified-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified-log.js","sourceRoot":"","sources":["../../../../../../../backend/src/services/chat-v2/sqlite/unified-log.ts"],"names":[],"mappings":"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"}
|