@vellumai/assistant 0.3.16 → 0.3.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +74 -13
- package/README.md +6 -0
- package/docs/architecture/http-token-refresh.md +23 -1
- package/docs/architecture/security.md +80 -0
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -0
- package/src/__tests__/access-request-decision.test.ts +4 -7
- package/src/__tests__/call-controller.test.ts +170 -0
- package/src/__tests__/channel-guardian.test.ts +3 -1
- package/src/__tests__/checker.test.ts +139 -48
- package/src/__tests__/config-watcher.test.ts +11 -13
- package/src/__tests__/conversation-pairing.test.ts +103 -3
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -1
- package/src/__tests__/guardian-action-followup-executor.test.ts +1 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +511 -0
- package/src/__tests__/guardian-action-late-reply.test.ts +131 -0
- package/src/__tests__/guardian-action-store.test.ts +182 -0
- package/src/__tests__/guardian-dispatch.test.ts +180 -0
- package/src/__tests__/guardian-grant-minting.test.ts +543 -0
- package/src/__tests__/ipc-snapshot.test.ts +22 -0
- package/src/__tests__/non-member-access-request.test.ts +1 -2
- package/src/__tests__/notification-broadcaster.test.ts +115 -4
- package/src/__tests__/notification-decision-strategy.test.ts +2 -1
- package/src/__tests__/notification-deep-link.test.ts +44 -1
- package/src/__tests__/notification-guardian-path.test.ts +157 -0
- package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
- package/src/__tests__/remote-skill-policy.test.ts +215 -0
- package/src/__tests__/scoped-approval-grants.test.ts +521 -0
- package/src/__tests__/scoped-grant-security-matrix.test.ts +443 -0
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/trust-store.test.ts +23 -21
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +5 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +9 -9
- package/src/__tests__/update-bulletin-state.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +66 -3
- package/src/__tests__/update-template-contract.test.ts +6 -11
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +571 -0
- package/src/__tests__/voice-session-bridge.test.ts +109 -9
- package/src/calls/call-controller.ts +150 -8
- package/src/calls/call-domain.ts +12 -0
- package/src/calls/guardian-action-sweep.ts +1 -1
- package/src/calls/guardian-dispatch.ts +16 -0
- package/src/calls/relay-server.ts +13 -0
- package/src/calls/voice-session-bridge.ts +46 -5
- package/src/cli/core-commands.ts +41 -1
- package/src/config/bundled-skills/notifications/SKILL.md +18 -0
- package/src/config/schema.ts +6 -0
- package/src/config/skills-schema.ts +27 -0
- package/src/config/templates/UPDATES.md +5 -6
- package/src/config/update-bulletin-format.ts +2 -0
- package/src/config/update-bulletin-state.ts +1 -1
- package/src/config/update-bulletin-template-path.ts +6 -0
- package/src/config/update-bulletin.ts +21 -6
- package/src/daemon/config-watcher.ts +3 -2
- package/src/daemon/daemon-control.ts +64 -10
- package/src/daemon/handlers/config-channels.ts +18 -0
- package/src/daemon/handlers/config-slack-channel.ts +1 -1
- package/src/daemon/handlers/identity.ts +45 -25
- package/src/daemon/handlers/sessions.ts +1 -1
- package/src/daemon/handlers/skills.ts +45 -2
- package/src/daemon/ipc-contract/sessions.ts +1 -1
- package/src/daemon/ipc-contract/skills.ts +1 -0
- package/src/daemon/ipc-contract/workspace.ts +12 -1
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +8 -0
- package/src/daemon/server.ts +25 -3
- package/src/daemon/session-process.ts +450 -184
- package/src/daemon/tls-certs.ts +17 -12
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/memory/channel-delivery-store.ts +18 -20
- package/src/memory/channel-guardian-store.ts +39 -42
- package/src/memory/conversation-crud.ts +2 -2
- package/src/memory/conversation-queries.ts +2 -2
- package/src/memory/conversation-store.ts +24 -25
- package/src/memory/db-init.ts +17 -1
- package/src/memory/embedding-local.ts +16 -7
- package/src/memory/fts-reconciler.ts +41 -26
- package/src/memory/guardian-action-store.ts +65 -7
- package/src/memory/guardian-verification.ts +1 -0
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
- package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
- package/src/memory/migrations/033-scoped-approval-grants.ts +51 -0
- package/src/memory/migrations/034-guardian-action-tool-metadata.ts +12 -0
- package/src/memory/migrations/index.ts +6 -2
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/schema.ts +36 -1
- package/src/memory/scoped-approval-grants.ts +509 -0
- package/src/memory/search/semantic.ts +3 -3
- package/src/notifications/README.md +158 -17
- package/src/notifications/broadcaster.ts +68 -50
- package/src/notifications/conversation-pairing.ts +96 -18
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/deliveries-store.ts +12 -0
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/thread-candidates.ts +60 -25
- package/src/notifications/types.ts +2 -1
- package/src/permissions/checker.ts +28 -16
- package/src/permissions/defaults.ts +14 -4
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-grant-minter.ts +97 -0
- package/src/runtime/http-server.ts +11 -11
- package/src/runtime/routes/access-request-decision.ts +1 -1
- package/src/runtime/routes/debug-routes.ts +4 -4
- package/src/runtime/routes/guardian-approval-interception.ts +120 -4
- package/src/runtime/routes/inbound-message-handler.ts +100 -33
- package/src/runtime/routes/integration-routes.ts +2 -2
- package/src/security/tool-approval-digest.ts +67 -0
- package/src/skills/remote-skill-policy.ts +131 -0
- package/src/tools/permission-checker.ts +1 -2
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/system/voice-config.ts +1 -1
- package/src/version.ts +29 -2
package/src/daemon/tls-certs.ts
CHANGED
|
@@ -152,20 +152,25 @@ export async function ensureTlsCert(): Promise<{ cert: string; key: string; fing
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
if (status === 'approaching_expiry') {
|
|
155
|
-
// Buffer existing cert/key/fingerprint before attempting renewal.
|
|
156
|
-
// generateNewCert() overwrites key.pem in-place, so if it fails mid-flight
|
|
157
|
-
// (e.g., key written but cert generation fails), reading from disk in the
|
|
158
|
-
// catch block would return a mismatched key/cert pair.
|
|
159
|
-
const [existingCert, existingKey, existingFp] = await Promise.all([
|
|
160
|
-
readFile(certPath, 'utf-8'),
|
|
161
|
-
readFile(keyPath, 'utf-8'),
|
|
162
|
-
readFile(fpPath, 'utf-8'),
|
|
163
|
-
]);
|
|
164
155
|
try {
|
|
165
|
-
|
|
156
|
+
// Buffer existing cert/key/fingerprint before attempting renewal.
|
|
157
|
+
// generateNewCert() overwrites key.pem in-place, so if it fails mid-flight
|
|
158
|
+
// (e.g., key written but cert generation fails), reading from disk in the
|
|
159
|
+
// catch block would return a mismatched key/cert pair.
|
|
160
|
+
const [existingCert, existingKey, existingFp] = await Promise.all([
|
|
161
|
+
readFile(certPath, 'utf-8'),
|
|
162
|
+
readFile(keyPath, 'utf-8'),
|
|
163
|
+
readFile(fpPath, 'utf-8'),
|
|
164
|
+
]);
|
|
165
|
+
try {
|
|
166
|
+
return await generateNewCert(tlsDir, certPath, keyPath, fpPath);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
log.warn({ err }, 'Proactive TLS renewal failed, continuing with existing certificate');
|
|
169
|
+
return { cert: existingCert, key: existingKey, fingerprint: existingFp.trim() };
|
|
170
|
+
}
|
|
166
171
|
} catch (err) {
|
|
167
|
-
log.warn({ err }, '
|
|
168
|
-
return
|
|
172
|
+
log.warn({ err }, 'Failed to read existing TLS cert for buffering, attempting regeneration');
|
|
173
|
+
return await generateNewCert(tlsDir, certPath, keyPath, fpPath);
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* registry entry instead of another if/else branch.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { normalizeActivationKey } from './handlers/config-voice.js';
|
|
11
10
|
import { updatePublishedAppDeployment } from '../services/published-app-updater.js';
|
|
12
11
|
import { openAppViaSurface } from '../tools/apps/open-proxy.js';
|
|
13
12
|
import type { ToolExecutionResult } from '../tools/types.js';
|
|
14
13
|
import { isDoordashCommand, updateDoordashProgress } from './doordash-steps.js';
|
|
14
|
+
import { normalizeActivationKey } from './handlers/config-voice.js';
|
|
15
15
|
import type { ServerMessage } from './ipc-protocol.js';
|
|
16
16
|
import {
|
|
17
17
|
refreshSurfacesForApp,
|
|
@@ -8,33 +8,31 @@
|
|
|
8
8
|
* - delivery-channels.ts — verification replies, segment progress, delivery guards
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
export type { PendingVerificationReply } from './delivery-channels.js';
|
|
12
|
+
export {
|
|
13
|
+
claimRunDelivery,
|
|
14
|
+
clearPendingVerificationReply,
|
|
15
|
+
getDeliveredSegmentCount,
|
|
16
|
+
getPendingVerificationReply,
|
|
17
|
+
resetAllRunDeliveryClaims,
|
|
18
|
+
resetRunDeliveryClaim,
|
|
19
|
+
storePendingVerificationReply,
|
|
20
|
+
updateDeliveredSegmentCount,
|
|
21
|
+
} from './delivery-channels.js';
|
|
22
|
+
export type { InboundResult, RecordInboundOptions } from './delivery-crud.js';
|
|
11
23
|
export {
|
|
12
|
-
recordInbound,
|
|
13
|
-
linkMessage,
|
|
14
|
-
findMessageBySourceId,
|
|
15
|
-
storePayload,
|
|
16
24
|
clearPayload,
|
|
25
|
+
findMessageBySourceId,
|
|
17
26
|
getLatestStoredPayload,
|
|
27
|
+
linkMessage,
|
|
28
|
+
recordInbound,
|
|
29
|
+
storePayload,
|
|
18
30
|
} from './delivery-crud.js';
|
|
19
|
-
export type { InboundResult, RecordInboundOptions } from './delivery-crud.js';
|
|
20
|
-
|
|
21
31
|
export {
|
|
22
32
|
acknowledgeDelivery,
|
|
33
|
+
getDeadLetterEvents,
|
|
34
|
+
getRetryableEvents,
|
|
23
35
|
markProcessed,
|
|
24
36
|
recordProcessingFailure,
|
|
25
|
-
getRetryableEvents,
|
|
26
|
-
getDeadLetterEvents,
|
|
27
37
|
replayDeadLetters,
|
|
28
38
|
} from './delivery-status.js';
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
storePendingVerificationReply,
|
|
32
|
-
getPendingVerificationReply,
|
|
33
|
-
clearPendingVerificationReply,
|
|
34
|
-
getDeliveredSegmentCount,
|
|
35
|
-
updateDeliveredSegmentCount,
|
|
36
|
-
claimRunDelivery,
|
|
37
|
-
resetRunDeliveryClaim,
|
|
38
|
-
resetAllRunDeliveryClaims,
|
|
39
|
-
} from './delivery-channels.js';
|
|
40
|
-
export type { PendingVerificationReply } from './delivery-channels.js';
|
|
@@ -10,60 +10,57 @@
|
|
|
10
10
|
* This file re-exports everything for backward compatibility.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
export {
|
|
14
|
+
type ApprovalRequestStatus,
|
|
15
|
+
countPendingByConversation,
|
|
16
|
+
createApprovalRequest,
|
|
17
|
+
findPendingAccessRequestForRequester,
|
|
18
|
+
getAllPendingApprovalsByGuardianChat,
|
|
19
|
+
getApprovalRequestById,
|
|
20
|
+
getApprovalRequestByRunId,
|
|
21
|
+
getExpiredPendingApprovals,
|
|
22
|
+
getPendingApprovalByGuardianChat,
|
|
23
|
+
getPendingApprovalByRequestAndGuardianChat,
|
|
24
|
+
getPendingApprovalByRunAndGuardianChat,
|
|
25
|
+
getPendingApprovalForRequest,
|
|
26
|
+
getPendingApprovalForRun,
|
|
27
|
+
getUnresolvedApprovalForRequest,
|
|
28
|
+
getUnresolvedApprovalForRun,
|
|
29
|
+
type GuardianApprovalRequest,
|
|
30
|
+
listPendingApprovalRequests,
|
|
31
|
+
resolveApprovalRequest,
|
|
32
|
+
updateApprovalDecision,
|
|
33
|
+
} from './guardian-approvals.js';
|
|
13
34
|
export {
|
|
14
35
|
type BindingStatus,
|
|
15
|
-
type GuardianBinding,
|
|
16
36
|
createBinding,
|
|
17
37
|
getActiveBinding,
|
|
38
|
+
type GuardianBinding,
|
|
18
39
|
revokeBinding,
|
|
19
40
|
} from './guardian-bindings.js';
|
|
20
|
-
|
|
21
41
|
export {
|
|
42
|
+
getRateLimit,
|
|
43
|
+
recordInvalidAttempt,
|
|
44
|
+
resetRateLimit,
|
|
45
|
+
type VerificationRateLimit,
|
|
46
|
+
} from './guardian-rate-limits.js';
|
|
47
|
+
export {
|
|
48
|
+
bindSessionIdentity,
|
|
22
49
|
type ChallengeStatus,
|
|
23
|
-
type SessionStatus,
|
|
24
|
-
type IdentityBindingStatus,
|
|
25
|
-
type VerificationPurpose,
|
|
26
|
-
type VerificationChallenge,
|
|
27
|
-
createChallenge,
|
|
28
|
-
revokePendingChallenges,
|
|
29
|
-
findPendingChallengeByHash,
|
|
30
|
-
findPendingChallengeForChannel,
|
|
31
50
|
consumeChallenge,
|
|
51
|
+
countRecentSendsToDestination,
|
|
52
|
+
createChallenge,
|
|
32
53
|
createVerificationSession,
|
|
33
54
|
findActiveSession,
|
|
55
|
+
findPendingChallengeByHash,
|
|
56
|
+
findPendingChallengeForChannel,
|
|
34
57
|
findSessionByBootstrapTokenHash,
|
|
35
58
|
findSessionByIdentity,
|
|
36
|
-
|
|
59
|
+
type IdentityBindingStatus,
|
|
60
|
+
revokePendingChallenges,
|
|
61
|
+
type SessionStatus,
|
|
37
62
|
updateSessionDelivery,
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
updateSessionStatus,
|
|
64
|
+
type VerificationChallenge,
|
|
65
|
+
type VerificationPurpose,
|
|
40
66
|
} from './guardian-verification.js';
|
|
41
|
-
|
|
42
|
-
export {
|
|
43
|
-
type ApprovalRequestStatus,
|
|
44
|
-
type GuardianApprovalRequest,
|
|
45
|
-
createApprovalRequest,
|
|
46
|
-
getPendingApprovalForRun,
|
|
47
|
-
getPendingApprovalForRequest,
|
|
48
|
-
getUnresolvedApprovalForRun,
|
|
49
|
-
getUnresolvedApprovalForRequest,
|
|
50
|
-
getPendingApprovalByGuardianChat,
|
|
51
|
-
getPendingApprovalByRunAndGuardianChat,
|
|
52
|
-
getPendingApprovalByRequestAndGuardianChat,
|
|
53
|
-
getAllPendingApprovalsByGuardianChat,
|
|
54
|
-
getExpiredPendingApprovals,
|
|
55
|
-
updateApprovalDecision,
|
|
56
|
-
listPendingApprovalRequests,
|
|
57
|
-
getApprovalRequestById,
|
|
58
|
-
getApprovalRequestByRunId,
|
|
59
|
-
resolveApprovalRequest,
|
|
60
|
-
countPendingByConversation,
|
|
61
|
-
findPendingAccessRequestForRequester,
|
|
62
|
-
} from './guardian-approvals.js';
|
|
63
|
-
|
|
64
|
-
export {
|
|
65
|
-
type VerificationRateLimit,
|
|
66
|
-
getRateLimit,
|
|
67
|
-
recordInvalidAttempt,
|
|
68
|
-
resetRateLimit,
|
|
69
|
-
} from './guardian-rate-limits.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, count, eq, inArray, isNull, sql
|
|
1
|
+
import { and, asc,count, eq, inArray, isNull, sql } from 'drizzle-orm';
|
|
2
2
|
import { v4 as uuid } from 'uuid';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ import { getLogger } from '../util/logger.js';
|
|
|
11
11
|
import { createRowMapper } from '../util/row-mapper.js';
|
|
12
12
|
import { deleteOrphanAttachments } from './attachments-store.js';
|
|
13
13
|
import { projectAssistantMessage } from './conversation-attention-store.js';
|
|
14
|
-
import { getDb,
|
|
14
|
+
import { getDb, rawExec, rawGet } from './db.js';
|
|
15
15
|
import { indexMessageNow } from './indexer.js';
|
|
16
16
|
import { channelInboundEvents, conversations, llmRequestLogs, memoryEmbeddings, memoryItemEntities, memoryItems, memoryItemSources, memorySegments, messageAttachments, messages, toolInvocations } from './schema.js';
|
|
17
17
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { and, asc, count, desc, eq, gte, lt, ne, or, sql } from 'drizzle-orm';
|
|
2
2
|
|
|
3
3
|
import { getLogger } from '../util/logger.js';
|
|
4
|
-
import { getDb, rawAll } from './db.js';
|
|
5
|
-
import { parseConversation, parseMessage } from './conversation-crud.js';
|
|
6
4
|
import type { ConversationRow, MessageRow } from './conversation-crud.js';
|
|
5
|
+
import { parseConversation, parseMessage } from './conversation-crud.js';
|
|
6
|
+
import { getDb, rawAll } from './db.js';
|
|
7
7
|
import { conversations, messages } from './schema.js';
|
|
8
8
|
import { buildFtsMatchQuery } from './search/lexical.js';
|
|
9
9
|
|
|
@@ -2,44 +2,43 @@
|
|
|
2
2
|
// Existing imports from this file continue to work without changes.
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
provenanceFromGuardianContext,
|
|
5
|
+
addMessage,
|
|
6
|
+
clearAll,
|
|
8
7
|
type ConversationRow,
|
|
9
|
-
parseConversation,
|
|
10
|
-
type MessageRow,
|
|
11
|
-
parseMessage,
|
|
12
8
|
createConversation,
|
|
9
|
+
deleteConversation,
|
|
10
|
+
type DeletedMemoryIds,
|
|
11
|
+
deleteLastExchange,
|
|
12
|
+
deleteMessageById,
|
|
13
13
|
getConversation,
|
|
14
|
-
getConversationThreadType,
|
|
15
14
|
getConversationMemoryScopeId,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
getConversationOriginChannel,
|
|
16
|
+
getConversationOriginInterface,
|
|
17
|
+
getConversationThreadType,
|
|
19
18
|
getMessageById,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
getMessages,
|
|
20
|
+
type MessageMetadata,
|
|
21
|
+
messageMetadataSchema,
|
|
22
|
+
type MessageRow,
|
|
23
|
+
parseConversation,
|
|
24
|
+
parseMessage,
|
|
25
|
+
provenanceFromGuardianContext,
|
|
27
26
|
relinkAttachments,
|
|
28
|
-
deleteMessageById,
|
|
29
27
|
setConversationOriginChannelIfUnset,
|
|
30
|
-
getConversationOriginChannel,
|
|
31
28
|
setConversationOriginInterfaceIfUnset,
|
|
32
|
-
|
|
29
|
+
updateConversationContextWindow,
|
|
30
|
+
updateConversationTitle,
|
|
31
|
+
updateConversationUsage,
|
|
32
|
+
updateMessageContent,
|
|
33
33
|
} from './conversation-crud.js';
|
|
34
|
-
|
|
35
34
|
export {
|
|
36
|
-
|
|
35
|
+
type ConversationSearchResult,
|
|
37
36
|
countConversations,
|
|
38
37
|
getLatestConversation,
|
|
39
|
-
getNextMessage,
|
|
40
|
-
type PaginatedMessagesResult,
|
|
41
38
|
getMessagesPaginated,
|
|
39
|
+
getNextMessage,
|
|
42
40
|
isLastUserMessageToolResult,
|
|
43
|
-
|
|
41
|
+
listConversations,
|
|
42
|
+
type PaginatedMessagesResult,
|
|
44
43
|
searchConversations,
|
|
45
44
|
} from './conversation-queries.js';
|
package/src/memory/db-init.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
createConversationAttentionTables,
|
|
9
9
|
createCoreIndexes,
|
|
10
10
|
createCoreTables,
|
|
11
|
+
createScopedApprovalGrantsTable,
|
|
11
12
|
createExternalConversationBindingsTables,
|
|
12
13
|
createFollowupsTables,
|
|
13
14
|
createMediaAssetsTables,
|
|
@@ -17,14 +18,17 @@ import {
|
|
|
17
18
|
createTasksAndWorkItemsTables,
|
|
18
19
|
createWatchersAndLogsTables,
|
|
19
20
|
migrateCallSessionMode,
|
|
20
|
-
migrateFkCascadeRebuilds,
|
|
21
21
|
migrateChannelInboundDeliveredSegments,
|
|
22
22
|
migrateConversationsThreadTypeIndex,
|
|
23
|
+
migrateFkCascadeRebuilds,
|
|
23
24
|
migrateGuardianActionFollowup,
|
|
25
|
+
migrateGuardianActionToolMetadata,
|
|
24
26
|
migrateGuardianBootstrapToken,
|
|
27
|
+
migrateGuardianDeliveryConversationIndex,
|
|
25
28
|
migrateGuardianVerificationPurpose,
|
|
26
29
|
migrateGuardianVerificationSessions,
|
|
27
30
|
migrateMessagesFtsBackfill,
|
|
31
|
+
migrateNotificationDeliveryThreadDecision,
|
|
28
32
|
migrateReminderRoutingIntent,
|
|
29
33
|
migrateSchemaIndexesAndColumns,
|
|
30
34
|
recoverCrashedMigrations,
|
|
@@ -100,9 +104,15 @@ export function initializeDb(): void {
|
|
|
100
104
|
// 14c. Guardian action follow-up lifecycle columns (timeout reason, late answers)
|
|
101
105
|
migrateGuardianActionFollowup(database);
|
|
102
106
|
|
|
107
|
+
// 14c2. Guardian action tool-approval metadata columns (tool_name, input_digest)
|
|
108
|
+
migrateGuardianActionToolMetadata(database);
|
|
109
|
+
|
|
103
110
|
// 14d. Index on conversations.thread_type for frequent WHERE filters
|
|
104
111
|
migrateConversationsThreadTypeIndex(database);
|
|
105
112
|
|
|
113
|
+
// 14e. Index on guardian_action_deliveries.destination_conversation_id for conversation-based lookups
|
|
114
|
+
migrateGuardianDeliveryConversationIndex(database);
|
|
115
|
+
|
|
106
116
|
// 15. Notification system
|
|
107
117
|
createNotificationTables(database);
|
|
108
118
|
|
|
@@ -125,5 +135,11 @@ export function initializeDb(): void {
|
|
|
125
135
|
// 21. Rebuild tables to add ON DELETE CASCADE to FK constraints
|
|
126
136
|
migrateFkCascadeRebuilds(database);
|
|
127
137
|
|
|
138
|
+
// 22. Scoped approval grants (channel-agnostic one-time-use grants)
|
|
139
|
+
createScopedApprovalGrantsTable(database);
|
|
140
|
+
|
|
141
|
+
// 23. Thread decision audit columns on notification_deliveries
|
|
142
|
+
migrateNotificationDeliveryThreadDecision(database);
|
|
143
|
+
|
|
128
144
|
validateMigrationState(database);
|
|
129
145
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
|
+
|
|
1
3
|
import { getLogger } from '../util/logger.js';
|
|
2
4
|
import { PromiseGuard } from '../util/promise-guard.js';
|
|
3
5
|
import type { EmbeddingBackend, EmbeddingRequestOptions } from './embedding-backend.js';
|
|
@@ -59,13 +61,20 @@ export class LocalEmbeddingBackend implements EmbeddingBackend {
|
|
|
59
61
|
let transformers: typeof import('@huggingface/transformers');
|
|
60
62
|
try {
|
|
61
63
|
transformers = await import('@huggingface/transformers');
|
|
62
|
-
} catch
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
} catch {
|
|
65
|
+
// In compiled Bun binaries, bare specifier resolution starts from the
|
|
66
|
+
// virtual /$bunfs/root/ filesystem and can't find externalized packages.
|
|
67
|
+
// Fall back to resolving from the executable's real disk location where
|
|
68
|
+
// node_modules/ is co-located.
|
|
69
|
+
try {
|
|
70
|
+
const execDir = dirname(process.execPath);
|
|
71
|
+
const modulePath = join(execDir, 'node_modules', '@huggingface', 'transformers');
|
|
72
|
+
transformers = await import(modulePath);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Local embedding backend unavailable: failed to load @huggingface/transformers (${err instanceof Error ? err.message : String(err)})`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
this.extractor = await transformers.pipeline('feature-extraction', this.model, {
|
|
71
80
|
dtype: 'fp32',
|
|
@@ -84,37 +84,52 @@ function reconcileTable(opts: {
|
|
|
84
84
|
*/
|
|
85
85
|
export function reconcileFtsIndexes(): FtsReconciliationResult[] {
|
|
86
86
|
const results: FtsReconciliationResult[] = [];
|
|
87
|
+
const errors: unknown[] = [];
|
|
87
88
|
|
|
88
89
|
// memory_segment_fts tracks memory_segments
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
try {
|
|
91
|
+
const memResult = reconcileTable({
|
|
92
|
+
ftsTable: 'memory_segment_fts',
|
|
93
|
+
ftsIdColumn: 'segment_id',
|
|
94
|
+
ftsContentColumn: 'text',
|
|
95
|
+
baseTable: 'memory_segments',
|
|
96
|
+
baseIdColumn: 'id',
|
|
97
|
+
baseContentColumn: 'text',
|
|
98
|
+
});
|
|
99
|
+
results.push(memResult);
|
|
100
|
+
if (memResult.missingInserted > 0 || memResult.orphansRemoved > 0 || memResult.staleRefreshed > 0) {
|
|
101
|
+
log.info(memResult, 'Reconciled memory_segment_fts');
|
|
102
|
+
} else {
|
|
103
|
+
log.debug(memResult, 'memory_segment_fts is in sync');
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
log.error({ err }, 'Failed to reconcile memory_segment_fts');
|
|
107
|
+
errors.push(err);
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
// messages_fts tracks messages
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
try {
|
|
112
|
+
const msgResult = reconcileTable({
|
|
113
|
+
ftsTable: 'messages_fts',
|
|
114
|
+
ftsIdColumn: 'message_id',
|
|
115
|
+
ftsContentColumn: 'content',
|
|
116
|
+
baseTable: 'messages',
|
|
117
|
+
baseIdColumn: 'id',
|
|
118
|
+
baseContentColumn: 'content',
|
|
119
|
+
});
|
|
120
|
+
results.push(msgResult);
|
|
121
|
+
if (msgResult.missingInserted > 0 || msgResult.orphansRemoved > 0 || msgResult.staleRefreshed > 0) {
|
|
122
|
+
log.info(msgResult, 'Reconciled messages_fts');
|
|
123
|
+
} else {
|
|
124
|
+
log.debug(msgResult, 'messages_fts is in sync');
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
log.error({ err }, 'Failed to reconcile messages_fts');
|
|
128
|
+
errors.push(err);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (errors.length > 0) {
|
|
132
|
+
throw new AggregateError(errors, `FTS reconciliation failed for ${errors.length} table(s)`);
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
return results;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* answer resolves the request and all other deliveries are marked answered.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { and, desc, eq, inArray, lt } from 'drizzle-orm';
|
|
10
|
+
import { and, count, desc, eq, inArray, lt } from 'drizzle-orm';
|
|
11
11
|
import { v4 as uuid } from 'uuid';
|
|
12
12
|
|
|
13
13
|
import { getLogger } from '../util/logger.js';
|
|
@@ -51,6 +51,8 @@ export interface GuardianActionRequest {
|
|
|
51
51
|
lateAnsweredAt: number | null;
|
|
52
52
|
followupAction: FollowupAction | null;
|
|
53
53
|
followupCompletedAt: number | null;
|
|
54
|
+
toolName: string | null;
|
|
55
|
+
inputDigest: string | null;
|
|
54
56
|
createdAt: number;
|
|
55
57
|
updatedAt: number;
|
|
56
58
|
}
|
|
@@ -97,6 +99,8 @@ function rowToRequest(row: typeof guardianActionRequests.$inferSelect): Guardian
|
|
|
97
99
|
lateAnsweredAt: row.lateAnsweredAt ?? null,
|
|
98
100
|
followupAction: (row.followupAction as FollowupAction) ?? null,
|
|
99
101
|
followupCompletedAt: row.followupCompletedAt ?? null,
|
|
102
|
+
toolName: row.toolName ?? null,
|
|
103
|
+
inputDigest: row.inputDigest ?? null,
|
|
100
104
|
createdAt: row.createdAt,
|
|
101
105
|
updatedAt: row.updatedAt,
|
|
102
106
|
};
|
|
@@ -137,6 +141,8 @@ export function createGuardianActionRequest(params: {
|
|
|
137
141
|
pendingQuestionId: string;
|
|
138
142
|
questionText: string;
|
|
139
143
|
expiresAt: number;
|
|
144
|
+
toolName?: string;
|
|
145
|
+
inputDigest?: string;
|
|
140
146
|
}): GuardianActionRequest {
|
|
141
147
|
const db = getDb();
|
|
142
148
|
const now = Date.now();
|
|
@@ -164,6 +170,8 @@ export function createGuardianActionRequest(params: {
|
|
|
164
170
|
lateAnsweredAt: null,
|
|
165
171
|
followupAction: null,
|
|
166
172
|
followupCompletedAt: null,
|
|
173
|
+
toolName: params.toolName ?? null,
|
|
174
|
+
inputDigest: params.inputDigest ?? null,
|
|
167
175
|
createdAt: now,
|
|
168
176
|
updatedAt: now,
|
|
169
177
|
};
|
|
@@ -212,6 +220,26 @@ export function getPendingRequestByCallSessionId(callSessionId: string): Guardia
|
|
|
212
220
|
return row ? rowToRequest(row) : null;
|
|
213
221
|
}
|
|
214
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Count pending guardian action requests for a given call session.
|
|
225
|
+
* Used as a candidate-affinity hint so the decision engine knows how many
|
|
226
|
+
* active guardian requests already exist for the current call.
|
|
227
|
+
*/
|
|
228
|
+
export function countPendingRequestsByCallSessionId(callSessionId: string): number {
|
|
229
|
+
const db = getDb();
|
|
230
|
+
const row = db
|
|
231
|
+
.select({ count: count() })
|
|
232
|
+
.from(guardianActionRequests)
|
|
233
|
+
.where(
|
|
234
|
+
and(
|
|
235
|
+
eq(guardianActionRequests.callSessionId, callSessionId),
|
|
236
|
+
eq(guardianActionRequests.status, 'pending'),
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
.get();
|
|
240
|
+
return row?.count ?? 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
215
243
|
/**
|
|
216
244
|
* First-response-wins resolution. Checks that the request is still
|
|
217
245
|
* 'pending' before updating; returns the updated request on success
|
|
@@ -595,6 +623,16 @@ export function getPendingDeliveriesByDestination(
|
|
|
595
623
|
* Look up a pending delivery by destination conversation ID (for mac channel routing).
|
|
596
624
|
*/
|
|
597
625
|
export function getPendingDeliveryByConversation(conversationId: string): GuardianActionDelivery | null {
|
|
626
|
+
const all = getPendingDeliveriesByConversation(conversationId);
|
|
627
|
+
return all.length > 0 ? all[0] : null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Look up all pending deliveries by destination conversation ID.
|
|
632
|
+
* Used for disambiguation when a reused vellum thread has multiple active
|
|
633
|
+
* guardian requests.
|
|
634
|
+
*/
|
|
635
|
+
export function getPendingDeliveriesByConversation(conversationId: string): GuardianActionDelivery[] {
|
|
598
636
|
try {
|
|
599
637
|
const db = getDb();
|
|
600
638
|
const rows = db
|
|
@@ -612,11 +650,11 @@ export function getPendingDeliveryByConversation(conversationId: string): Guardi
|
|
|
612
650
|
),
|
|
613
651
|
)
|
|
614
652
|
.all();
|
|
615
|
-
return rows.
|
|
653
|
+
return rows.map((r) => rowToDelivery(r.delivery));
|
|
616
654
|
} catch (err) {
|
|
617
655
|
if (err instanceof Error && err.message.includes('no such table')) {
|
|
618
656
|
log.warn({ err }, 'guardian tables not yet created');
|
|
619
|
-
return
|
|
657
|
+
return [];
|
|
620
658
|
}
|
|
621
659
|
throw err;
|
|
622
660
|
}
|
|
@@ -669,6 +707,16 @@ export function getExpiredDeliveriesByDestination(
|
|
|
669
707
|
* Look up an expired delivery by destination conversation ID (for mac channel routing).
|
|
670
708
|
*/
|
|
671
709
|
export function getExpiredDeliveryByConversation(conversationId: string): GuardianActionDelivery | null {
|
|
710
|
+
const all = getExpiredDeliveriesByConversation(conversationId);
|
|
711
|
+
return all.length > 0 ? all[0] : null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Look up all expired deliveries by destination conversation ID.
|
|
716
|
+
* Used for disambiguation when a reused vellum thread has multiple expired
|
|
717
|
+
* guardian requests eligible for follow-up.
|
|
718
|
+
*/
|
|
719
|
+
export function getExpiredDeliveriesByConversation(conversationId: string): GuardianActionDelivery[] {
|
|
672
720
|
try {
|
|
673
721
|
const db = getDb();
|
|
674
722
|
const rows = db
|
|
@@ -687,11 +735,11 @@ export function getExpiredDeliveryByConversation(conversationId: string): Guardi
|
|
|
687
735
|
),
|
|
688
736
|
)
|
|
689
737
|
.all();
|
|
690
|
-
return rows.
|
|
738
|
+
return rows.map((r) => rowToDelivery(r.delivery));
|
|
691
739
|
} catch (err) {
|
|
692
740
|
if (err instanceof Error && err.message.includes('no such table')) {
|
|
693
741
|
log.warn({ err }, 'guardian tables not yet created');
|
|
694
|
-
return
|
|
742
|
+
return [];
|
|
695
743
|
}
|
|
696
744
|
throw err;
|
|
697
745
|
}
|
|
@@ -746,6 +794,16 @@ export function getFollowupDeliveriesByDestination(
|
|
|
746
794
|
* state by destination conversation ID (for mac channel routing).
|
|
747
795
|
*/
|
|
748
796
|
export function getFollowupDeliveryByConversation(conversationId: string): GuardianActionDelivery | null {
|
|
797
|
+
const all = getFollowupDeliveriesByConversation(conversationId);
|
|
798
|
+
return all.length > 0 ? all[0] : null;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Look up all deliveries for requests in `awaiting_guardian_choice` follow-up
|
|
803
|
+
* state by destination conversation ID. Used for disambiguation when a reused
|
|
804
|
+
* vellum thread has multiple follow-up guardian requests.
|
|
805
|
+
*/
|
|
806
|
+
export function getFollowupDeliveriesByConversation(conversationId: string): GuardianActionDelivery[] {
|
|
749
807
|
try {
|
|
750
808
|
const db = getDb();
|
|
751
809
|
const rows = db
|
|
@@ -764,11 +822,11 @@ export function getFollowupDeliveryByConversation(conversationId: string): Guard
|
|
|
764
822
|
),
|
|
765
823
|
)
|
|
766
824
|
.all();
|
|
767
|
-
return rows.
|
|
825
|
+
return rows.map((r) => rowToDelivery(r.delivery));
|
|
768
826
|
} catch (err) {
|
|
769
827
|
if (err instanceof Error && err.message.includes('no such table')) {
|
|
770
828
|
log.warn({ err }, 'guardian tables not yet created');
|
|
771
|
-
return
|
|
829
|
+
return [];
|
|
772
830
|
}
|
|
773
831
|
throw err;
|
|
774
832
|
}
|
|
@@ -2,6 +2,7 @@ import { getConfig } from '../config/loader.js';
|
|
|
2
2
|
import type { AssistantConfig } from '../config/types.js';
|
|
3
3
|
import { getLogger } from '../util/logger.js';
|
|
4
4
|
import { rawRun } from './db.js';
|
|
5
|
+
import { reconcileFtsIndexes } from './fts-reconciler.js';
|
|
5
6
|
import { backfillEntityRelationsJob,backfillJob } from './job-handlers/backfill.js';
|
|
6
7
|
import { checkContradictionsJob, cleanupStaleSupersededItemsJob, pruneOldConversationsJob } from './job-handlers/cleanup.js';
|
|
7
8
|
import { cleanupResolvedConflictsJob,resolvePendingConflictsForMessageJob } from './job-handlers/conflict.js';
|
|
@@ -9,7 +10,6 @@ import { cleanupResolvedConflictsJob,resolvePendingConflictsForMessageJob } from
|
|
|
9
10
|
import { embedItemJob, embedSegmentJob, embedSummaryJob } from './job-handlers/embedding.js';
|
|
10
11
|
import { extractEntitiesJob,extractItemsJob } from './job-handlers/extraction.js';
|
|
11
12
|
import { deleteQdrantVectorsJob,rebuildIndexJob } from './job-handlers/index-maintenance.js';
|
|
12
|
-
import { reconcileFtsIndexes } from './fts-reconciler.js';
|
|
13
13
|
import { mediaProcessingJob } from './job-handlers/media-processing.js';
|
|
14
14
|
import { buildConversationSummaryJob, buildGlobalSummaryJob } from './job-handlers/summarization.js';
|
|
15
15
|
import {
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
RETRY_MAX_ATTEMPTS,
|
|
19
19
|
retryDelayForAttempt,
|
|
20
20
|
} from './job-utils.js';
|
|
21
|
-
import { QdrantCircuitOpenError } from './qdrant-circuit-breaker.js';
|
|
22
21
|
import {
|
|
23
22
|
claimMemoryJobs,
|
|
24
23
|
completeMemoryJob,
|
|
@@ -32,6 +31,7 @@ import {
|
|
|
32
31
|
type MemoryJob,
|
|
33
32
|
resetRunningJobsToPending,
|
|
34
33
|
} from './jobs-store.js';
|
|
34
|
+
import { QdrantCircuitOpenError } from './qdrant-circuit-breaker.js';
|
|
35
35
|
import { bumpMemoryVersion } from './recall-cache.js';
|
|
36
36
|
|
|
37
37
|
// Re-export public utilities consumed by tests and other modules
|