@vellumai/assistant 0.4.32 → 0.4.34
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/docs/architecture/memory.md +1 -1
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +85 -4
- package/src/__tests__/actor-token-service.test.ts +4 -12
- package/src/__tests__/approval-primitive.test.ts +0 -45
- package/src/__tests__/approval-routes-http.test.ts +0 -1
- package/src/__tests__/assistant-id-boundary-guard.test.ts +150 -0
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/callback-handoff-copy.test.ts +0 -1
- package/src/__tests__/channel-approval-routes.test.ts +5 -45
- package/src/__tests__/channel-guardian.test.ts +122 -346
- package/src/__tests__/channel-invite-transport.test.ts +52 -40
- package/src/__tests__/commit-message-enrichment-service.test.ts +4 -38
- package/src/__tests__/computer-use-session-working-dir.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -3
- package/src/__tests__/contacts-tools.test.ts +4 -5
- package/src/__tests__/conversation-attention-store.test.ts +2 -65
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -3
- package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -7
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -74
- package/src/__tests__/guardian-action-late-reply.test.ts +1 -8
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-grant-minting.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +0 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -3
- package/src/__tests__/handlers-telegram-config.test.ts +0 -1
- package/src/__tests__/inbound-invite-redemption.test.ts +1 -7
- package/src/__tests__/ingress-reconcile.test.ts +3 -36
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
- package/src/__tests__/migration-export-http.test.ts +0 -1
- package/src/__tests__/migration-import-commit-http.test.ts +0 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +0 -1
- package/src/__tests__/migration-validate-http.test.ts +0 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -8
- package/src/__tests__/notification-broadcaster.test.ts +1 -2
- package/src/__tests__/notification-decision-fallback.test.ts +0 -2
- package/src/__tests__/notification-decision-strategy.test.ts +0 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-telegram-adapter.test.ts +0 -4
- package/src/__tests__/relay-server.test.ts +151 -80
- package/src/__tests__/sandbox-host-parity.test.ts +5 -2
- package/src/__tests__/scoped-approval-grants.test.ts +9 -40
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -36
- package/src/__tests__/send-endpoint-busy.test.ts +0 -1
- package/src/__tests__/send-notification-tool.test.ts +0 -1
- package/src/__tests__/session-init.benchmark.test.ts +0 -2
- package/src/__tests__/slack-channel-config.test.ts +0 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -5
- package/src/__tests__/sms-messaging-provider.test.ts +0 -4
- package/src/__tests__/terminal-tools.test.ts +5 -2
- package/src/__tests__/thread-seed-composer.test.ts +0 -1
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -4
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +65 -77
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -18
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -14
- package/src/__tests__/trusted-contact-verification.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +2 -3
- package/src/__tests__/update-bulletin.test.ts +0 -2
- package/src/__tests__/user-reference.test.ts +47 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -38
- package/src/__tests__/workspace-git-service.test.ts +2 -2
- package/src/approvals/approval-primitive.ts +0 -15
- package/src/approvals/guardian-decision-primitive.ts +0 -3
- package/src/approvals/guardian-request-resolvers.ts +0 -5
- package/src/calls/call-domain.ts +0 -3
- package/src/calls/call-store.ts +0 -3
- package/src/calls/guardian-action-sweep.ts +2 -1
- package/src/calls/guardian-dispatch.ts +1 -2
- package/src/calls/relay-access-wait.ts +0 -4
- package/src/calls/relay-server.ts +8 -66
- package/src/calls/relay-setup-router.ts +1 -2
- package/src/calls/relay-verification.ts +0 -1
- package/src/calls/twilio-routes.ts +0 -3
- package/src/calls/types.ts +0 -1
- package/src/calls/voice-session-bridge.ts +0 -1
- package/src/channels/config.ts +41 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -1
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/slack-digest-setup/SKILL.md +164 -0
- package/src/config/env.ts +0 -4
- package/src/config/feature-flag-registry.json +4 -4
- package/src/config/user-reference.ts +47 -9
- package/src/contacts/contact-store.ts +13 -88
- package/src/contacts/contacts-write.ts +3 -11
- package/src/contacts/types.ts +0 -1
- package/src/daemon/handlers/config-channels.ts +19 -44
- package/src/daemon/handlers/config-inbox.ts +6 -6
- package/src/daemon/handlers/contacts.ts +8 -12
- package/src/daemon/handlers/index.ts +0 -2
- package/src/daemon/lifecycle.ts +18 -26
- package/src/daemon/session-process.ts +0 -4
- package/src/memory/channel-delivery-store.ts +1 -0
- package/src/memory/conversation-attention-store.ts +4 -19
- package/src/memory/conversation-crud.ts +0 -2
- package/src/memory/db-init.ts +8 -0
- package/src/memory/delivery-crud.ts +13 -0
- package/src/memory/guardian-action-store.ts +0 -12
- package/src/memory/guardian-approvals.ts +35 -80
- package/src/memory/guardian-rate-limits.ts +1 -14
- package/src/memory/guardian-verification.ts +6 -34
- package/src/memory/invite-store.ts +76 -15
- package/src/memory/migrations/040-invite-code-hash-column.ts +16 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +64 -45
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +263 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/migrations/registry.ts +14 -1
- package/src/memory/schema/calls.ts +0 -7
- package/src/memory/schema/contacts.ts +2 -8
- package/src/memory/schema/guardian.ts +0 -5
- package/src/memory/schema/infrastructure.ts +0 -2
- package/src/memory/schema/notifications.ts +3 -17
- package/src/memory/scoped-approval-grants.ts +2 -24
- package/src/notifications/adapters/sms.ts +2 -1
- package/src/notifications/broadcaster.ts +1 -6
- package/src/notifications/decision-engine.ts +3 -4
- package/src/notifications/deliveries-store.ts +0 -4
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/deterministic-checks.ts +1 -6
- package/src/notifications/emit-signal.ts +4 -11
- package/src/notifications/events-store.ts +7 -17
- package/src/notifications/preference-summary.ts +2 -2
- package/src/notifications/preferences-store.ts +2 -9
- package/src/notifications/signal.ts +0 -1
- package/src/notifications/thread-candidates.ts +1 -11
- package/src/notifications/types.ts +0 -3
- package/src/runtime/access-request-helper.ts +3 -10
- package/src/runtime/actor-refresh-token-store.ts +0 -6
- package/src/runtime/actor-token-store.ts +3 -16
- package/src/runtime/actor-trust-resolver.ts +1 -4
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -9
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -3
- package/src/runtime/auth/credential-service.ts +1 -15
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/auth/token-service.ts +50 -0
- package/src/runtime/channel-guardian-service.ts +16 -49
- package/src/runtime/channel-invite-transport.ts +129 -34
- package/src/runtime/channel-invite-transports/email.ts +54 -0
- package/src/runtime/channel-invite-transports/slack.ts +87 -0
- package/src/runtime/channel-invite-transports/sms.ts +74 -0
- package/src/runtime/channel-invite-transports/telegram.ts +35 -11
- package/src/runtime/channel-invite-transports/voice.ts +12 -12
- package/src/runtime/confirmation-request-guardian-bridge.ts +0 -1
- package/src/runtime/guardian-action-followup-executor.ts +3 -2
- package/src/runtime/guardian-action-grant-minter.ts +0 -1
- package/src/runtime/guardian-outbound-actions.ts +2 -12
- package/src/runtime/guardian-vellum-migration.ts +2 -3
- package/src/runtime/http-server.ts +0 -1
- package/src/runtime/invite-redemption-service.ts +191 -11
- package/src/runtime/invite-redemption-templates.ts +6 -6
- package/src/runtime/invite-service.ts +81 -11
- package/src/runtime/local-actor-identity.ts +2 -5
- package/src/runtime/routes/access-request-decision.ts +52 -7
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -9
- package/src/runtime/routes/channel-readiness-routes.ts +29 -18
- package/src/runtime/routes/contact-routes.ts +48 -46
- package/src/runtime/routes/conversation-attention-routes.ts +0 -2
- package/src/runtime/routes/global-search-routes.ts +0 -2
- package/src/runtime/routes/guardian-bootstrap-routes.ts +6 -12
- package/src/runtime/routes/guardian-expiry-sweep.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -6
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +296 -47
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +6 -42
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -6
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +10 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +0 -1
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +0 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -7
- package/src/runtime/routes/invite-routes.ts +1 -0
- package/src/runtime/routes/pairing-routes.ts +4 -4
- package/src/runtime/tool-grant-request-helper.ts +0 -1
- package/src/tools/browser/browser-manager.ts +22 -12
- package/src/tools/browser/runtime-check.ts +110 -3
- package/src/tools/calls/call-start.ts +1 -3
- package/src/tools/followups/followup_create.ts +1 -2
- package/src/tools/shared/shell-output.ts +7 -2
- package/src/tools/tool-approval-handler.ts +0 -2
- package/src/util/platform.ts +0 -4
- package/src/workspace/git-service.ts +10 -4
|
@@ -32,7 +32,6 @@ export type Confidence = "explicit" | "inferred";
|
|
|
32
32
|
export interface AttentionEvent {
|
|
33
33
|
id: string;
|
|
34
34
|
conversationId: string;
|
|
35
|
-
assistantId: string;
|
|
36
35
|
sourceChannel: string;
|
|
37
36
|
signalType: SignalType;
|
|
38
37
|
confidence: Confidence;
|
|
@@ -45,7 +44,6 @@ export interface AttentionEvent {
|
|
|
45
44
|
|
|
46
45
|
export interface AttentionState {
|
|
47
46
|
conversationId: string;
|
|
48
|
-
assistantId: string;
|
|
49
47
|
latestAssistantMessageId: string | null;
|
|
50
48
|
latestAssistantMessageAt: number | null;
|
|
51
49
|
lastSeenAssistantMessageId: string | null;
|
|
@@ -68,7 +66,6 @@ function rowToEvent(
|
|
|
68
66
|
return {
|
|
69
67
|
id: row.id,
|
|
70
68
|
conversationId: row.conversationId,
|
|
71
|
-
assistantId: row.assistantId,
|
|
72
69
|
sourceChannel: row.sourceChannel,
|
|
73
70
|
signalType: row.signalType as SignalType,
|
|
74
71
|
confidence: row.confidence as Confidence,
|
|
@@ -85,7 +82,6 @@ function rowToState(
|
|
|
85
82
|
): AttentionState {
|
|
86
83
|
return {
|
|
87
84
|
conversationId: row.conversationId,
|
|
88
|
-
assistantId: row.assistantId,
|
|
89
85
|
latestAssistantMessageId: row.latestAssistantMessageId,
|
|
90
86
|
latestAssistantMessageAt: row.latestAssistantMessageAt,
|
|
91
87
|
lastSeenAssistantMessageId: row.lastSeenAssistantMessageId,
|
|
@@ -109,11 +105,10 @@ function rowToState(
|
|
|
109
105
|
*/
|
|
110
106
|
export function projectAssistantMessage(params: {
|
|
111
107
|
conversationId: string;
|
|
112
|
-
assistantId: string;
|
|
113
108
|
messageId: string;
|
|
114
109
|
messageAt: number;
|
|
115
110
|
}): void {
|
|
116
|
-
const { conversationId,
|
|
111
|
+
const { conversationId, messageId, messageAt } = params;
|
|
117
112
|
const db = getDb();
|
|
118
113
|
const now = Date.now();
|
|
119
114
|
|
|
@@ -129,7 +124,6 @@ export function projectAssistantMessage(params: {
|
|
|
129
124
|
db.insert(conversationAssistantAttentionState)
|
|
130
125
|
.values({
|
|
131
126
|
conversationId,
|
|
132
|
-
assistantId,
|
|
133
127
|
latestAssistantMessageId: messageId,
|
|
134
128
|
latestAssistantMessageAt: messageAt,
|
|
135
129
|
lastSeenAssistantMessageId: null,
|
|
@@ -175,7 +169,6 @@ export function projectAssistantMessage(params: {
|
|
|
175
169
|
*/
|
|
176
170
|
export function recordConversationSeenSignal(params: {
|
|
177
171
|
conversationId: string;
|
|
178
|
-
assistantId: string;
|
|
179
172
|
sourceChannel: string;
|
|
180
173
|
signalType: SignalType;
|
|
181
174
|
confidence: Confidence;
|
|
@@ -186,7 +179,6 @@ export function recordConversationSeenSignal(params: {
|
|
|
186
179
|
}): AttentionEvent {
|
|
187
180
|
const {
|
|
188
181
|
conversationId,
|
|
189
|
-
assistantId,
|
|
190
182
|
sourceChannel,
|
|
191
183
|
signalType,
|
|
192
184
|
confidence,
|
|
@@ -205,7 +197,6 @@ export function recordConversationSeenSignal(params: {
|
|
|
205
197
|
const event: typeof conversationAttentionEvents.$inferInsert = {
|
|
206
198
|
id: eventId,
|
|
207
199
|
conversationId,
|
|
208
|
-
assistantId,
|
|
209
200
|
sourceChannel,
|
|
210
201
|
signalType,
|
|
211
202
|
confidence,
|
|
@@ -252,7 +243,6 @@ export function recordConversationSeenSignal(params: {
|
|
|
252
243
|
tx.insert(conversationAssistantAttentionState)
|
|
253
244
|
.values({
|
|
254
245
|
conversationId,
|
|
255
|
-
assistantId,
|
|
256
246
|
latestAssistantMessageId: latestMsgId,
|
|
257
247
|
latestAssistantMessageAt: latestMsgAt,
|
|
258
248
|
lastSeenAssistantMessageId: latestMsgId,
|
|
@@ -348,7 +338,6 @@ export function getAttentionStateByConversationIds(
|
|
|
348
338
|
export type AttentionFilterState = "seen" | "unseen" | "all";
|
|
349
339
|
|
|
350
340
|
export interface ListConversationAttentionParams {
|
|
351
|
-
assistantId: string;
|
|
352
341
|
state?: AttentionFilterState;
|
|
353
342
|
sourceChannel?: string;
|
|
354
343
|
source?: string;
|
|
@@ -364,7 +353,6 @@ export function listConversationAttention(
|
|
|
364
353
|
params: ListConversationAttentionParams,
|
|
365
354
|
): AttentionState[] {
|
|
366
355
|
const {
|
|
367
|
-
assistantId,
|
|
368
356
|
state: filterState = "all",
|
|
369
357
|
sourceChannel,
|
|
370
358
|
source,
|
|
@@ -373,9 +361,8 @@ export function listConversationAttention(
|
|
|
373
361
|
} = params;
|
|
374
362
|
|
|
375
363
|
const db = getDb();
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
];
|
|
364
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
365
|
+
const conditions: any[] = [];
|
|
379
366
|
|
|
380
367
|
if (sourceChannel) {
|
|
381
368
|
conditions.push(eq(conversations.originChannel, sourceChannel));
|
|
@@ -415,7 +402,6 @@ export function listConversationAttention(
|
|
|
415
402
|
let query = db
|
|
416
403
|
.select({
|
|
417
404
|
conversationId: conversationAssistantAttentionState.conversationId,
|
|
418
|
-
assistantId: conversationAssistantAttentionState.assistantId,
|
|
419
405
|
latestAssistantMessageId:
|
|
420
406
|
conversationAssistantAttentionState.latestAssistantMessageId,
|
|
421
407
|
latestAssistantMessageAt:
|
|
@@ -448,8 +434,7 @@ export function listConversationAttention(
|
|
|
448
434
|
);
|
|
449
435
|
}
|
|
450
436
|
|
|
451
|
-
const rows = query
|
|
452
|
-
.where(and(...conditions))
|
|
437
|
+
const rows = (conditions.length > 0 ? query.where(and(...conditions)) : query)
|
|
453
438
|
.orderBy(desc(conversationAssistantAttentionState.latestAssistantMessageAt))
|
|
454
439
|
.limit(limit)
|
|
455
440
|
.all();
|
|
@@ -7,7 +7,6 @@ import { parseChannelId, parseInterfaceId } from "../channels/types.js";
|
|
|
7
7
|
import { CHANNEL_IDS, INTERFACE_IDS, isChannelId } from "../channels/types.js";
|
|
8
8
|
import { getConfig } from "../config/loader.js";
|
|
9
9
|
import type { TrustContext } from "../daemon/session-runtime-assembly.js";
|
|
10
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
11
10
|
import { getLogger } from "../util/logger.js";
|
|
12
11
|
import { createRowMapper } from "../util/row-mapper.js";
|
|
13
12
|
import { deleteOrphanAttachments } from "./attachments-store.js";
|
|
@@ -394,7 +393,6 @@ export async function addMessage(
|
|
|
394
393
|
try {
|
|
395
394
|
projectAssistantMessage({
|
|
396
395
|
conversationId,
|
|
397
|
-
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
398
396
|
messageId: message.id,
|
|
399
397
|
messageAt: message.createdAt,
|
|
400
398
|
});
|
package/src/memory/db-init.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
migrateBackfillContactInteractionStats,
|
|
37
37
|
migrateBackfillGuardianPrincipalId,
|
|
38
38
|
migrateCallSessionMode,
|
|
39
|
+
migrateDropAssistantIdColumns,
|
|
39
40
|
migrateCanonicalGuardianDeliveriesDestinationIndex,
|
|
40
41
|
migrateCanonicalGuardianRequesterChatId,
|
|
41
42
|
migrateChannelInboundDeliveredSegments,
|
|
@@ -56,6 +57,7 @@ import {
|
|
|
56
57
|
migrateGuardianPrincipalIdNotNull,
|
|
57
58
|
migrateGuardianVerificationPurpose,
|
|
58
59
|
migrateGuardianVerificationSessions,
|
|
60
|
+
migrateInviteCodeHashColumn,
|
|
59
61
|
migrateMessagesFtsBackfill,
|
|
60
62
|
migrateNormalizePhoneIdentities,
|
|
61
63
|
migrateNotificationDeliveryThreadDecision,
|
|
@@ -246,6 +248,9 @@ export function initializeDb(): void {
|
|
|
246
248
|
// 27. Voice invite display metadata (friend_name, guardian_name) for personalized prompts
|
|
247
249
|
migrateVoiceInviteDisplayMetadata(database);
|
|
248
250
|
|
|
251
|
+
// 27b. 6-digit invite code hash column for non-voice channel invite redemption
|
|
252
|
+
migrateInviteCodeHashColumn(database);
|
|
253
|
+
|
|
249
254
|
// 28. Actor token records (hash-only actor token persistence)
|
|
250
255
|
createActorTokenRecordsTable(database);
|
|
251
256
|
|
|
@@ -285,6 +290,9 @@ export function initializeDb(): void {
|
|
|
285
290
|
// 39. Backfill contact interaction stats from channel lastSeenAt
|
|
286
291
|
migrateBackfillContactInteractionStats(database);
|
|
287
292
|
|
|
293
|
+
// 40. Drop assistant_id columns from all 16 daemon tables
|
|
294
|
+
migrateDropAssistantIdColumns(database);
|
|
295
|
+
|
|
288
296
|
validateMigrationState(database);
|
|
289
297
|
|
|
290
298
|
if (process.env.BUN_TEST === "1") {
|
|
@@ -125,6 +125,19 @@ export function recordInbound(
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Delete an inbound event record by its event ID. Used to roll back a
|
|
130
|
+
* dedup record when downstream processing (e.g. invite redemption) fails,
|
|
131
|
+
* so that webhook retries can re-attempt instead of short-circuiting as
|
|
132
|
+
* duplicates.
|
|
133
|
+
*/
|
|
134
|
+
export function deleteInbound(eventId: string): void {
|
|
135
|
+
const db = getDb();
|
|
136
|
+
db.delete(channelInboundEvents)
|
|
137
|
+
.where(eq(channelInboundEvents.id, eventId))
|
|
138
|
+
.run();
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
/**
|
|
129
142
|
* Link an inbound event to the user message it created, so edits can
|
|
130
143
|
* later find the correct message by source_message_id -> message_id.
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import { and, desc, eq, inArray, lt } from "drizzle-orm";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
12
12
|
|
|
13
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
14
13
|
import { getLogger } from "../util/logger.js";
|
|
15
14
|
import { getDb, rawChanges } from "./db.js";
|
|
16
15
|
import { guardianActionDeliveries, guardianActionRequests } from "./schema.js";
|
|
@@ -49,7 +48,6 @@ export type FollowupAction = "call_back" | "message_back" | "decline";
|
|
|
49
48
|
|
|
50
49
|
export interface GuardianActionRequest {
|
|
51
50
|
id: string;
|
|
52
|
-
assistantId: string;
|
|
53
51
|
kind: string;
|
|
54
52
|
sourceChannel: string;
|
|
55
53
|
sourceConversationId: string;
|
|
@@ -101,7 +99,6 @@ function rowToRequest(
|
|
|
101
99
|
): GuardianActionRequest {
|
|
102
100
|
return {
|
|
103
101
|
id: row.id,
|
|
104
|
-
assistantId: row.assistantId,
|
|
105
102
|
kind: row.kind,
|
|
106
103
|
sourceChannel: row.sourceChannel,
|
|
107
104
|
sourceConversationId: row.sourceConversationId,
|
|
@@ -165,7 +162,6 @@ function generateRequestCode(): string {
|
|
|
165
162
|
* legacy guardian action rows continue to compile.
|
|
166
163
|
*/
|
|
167
164
|
export function createGuardianActionRequest(params: {
|
|
168
|
-
assistantId?: string;
|
|
169
165
|
kind: string;
|
|
170
166
|
sourceChannel: string;
|
|
171
167
|
sourceConversationId: string;
|
|
@@ -182,7 +178,6 @@ export function createGuardianActionRequest(params: {
|
|
|
182
178
|
|
|
183
179
|
const row = {
|
|
184
180
|
id,
|
|
185
|
-
assistantId: params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
186
181
|
kind: params.kind,
|
|
187
182
|
sourceChannel: params.sourceChannel,
|
|
188
183
|
sourceConversationId: params.sourceConversationId,
|
|
@@ -693,14 +688,12 @@ export function createGuardianActionDelivery(params: {
|
|
|
693
688
|
* Used by inbound message routing to match incoming answers to deliveries.
|
|
694
689
|
*/
|
|
695
690
|
export function getPendingDeliveriesByDestination(
|
|
696
|
-
assistantId: string,
|
|
697
691
|
channel: string,
|
|
698
692
|
chatId: string,
|
|
699
693
|
): GuardianActionDelivery[] {
|
|
700
694
|
try {
|
|
701
695
|
const db = getDb();
|
|
702
696
|
|
|
703
|
-
// Join deliveries with requests to filter by assistantId
|
|
704
697
|
const rows = db
|
|
705
698
|
.select({
|
|
706
699
|
delivery: guardianActionDeliveries,
|
|
@@ -712,7 +705,6 @@ export function getPendingDeliveriesByDestination(
|
|
|
712
705
|
)
|
|
713
706
|
.where(
|
|
714
707
|
and(
|
|
715
|
-
eq(guardianActionRequests.assistantId, assistantId),
|
|
716
708
|
eq(guardianActionRequests.status, "pending"),
|
|
717
709
|
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
718
710
|
eq(guardianActionDeliveries.destinationChatId, chatId),
|
|
@@ -784,7 +776,6 @@ export function getPendingDeliveriesByConversation(
|
|
|
784
776
|
* Used by inbound message routing to match late guardian answers to expired requests.
|
|
785
777
|
*/
|
|
786
778
|
export function getExpiredDeliveriesByDestination(
|
|
787
|
-
assistantId: string,
|
|
788
779
|
channel: string,
|
|
789
780
|
chatId: string,
|
|
790
781
|
): GuardianActionDelivery[] {
|
|
@@ -802,7 +793,6 @@ export function getExpiredDeliveriesByDestination(
|
|
|
802
793
|
)
|
|
803
794
|
.where(
|
|
804
795
|
and(
|
|
805
|
-
eq(guardianActionRequests.assistantId, assistantId),
|
|
806
796
|
eq(guardianActionRequests.status, "expired"),
|
|
807
797
|
eq(guardianActionRequests.followupState, "none"),
|
|
808
798
|
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
@@ -877,7 +867,6 @@ export function getExpiredDeliveriesByConversation(
|
|
|
877
867
|
* on channel paths (Telegram, SMS).
|
|
878
868
|
*/
|
|
879
869
|
export function getFollowupDeliveriesByDestination(
|
|
880
|
-
assistantId: string,
|
|
881
870
|
channel: string,
|
|
882
871
|
chatId: string,
|
|
883
872
|
): GuardianActionDelivery[] {
|
|
@@ -895,7 +884,6 @@ export function getFollowupDeliveriesByDestination(
|
|
|
895
884
|
)
|
|
896
885
|
.where(
|
|
897
886
|
and(
|
|
898
|
-
eq(guardianActionRequests.assistantId, assistantId),
|
|
899
887
|
eq(guardianActionRequests.status, "expired"),
|
|
900
888
|
eq(guardianActionRequests.followupState, "awaiting_guardian_choice"),
|
|
901
889
|
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import { and, count, desc, eq, gt, lte } from "drizzle-orm";
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
|
|
12
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
13
12
|
import { getDb } from "./db.js";
|
|
14
13
|
import { channelGuardianApprovalRequests } from "./schema.js";
|
|
15
14
|
|
|
@@ -29,7 +28,6 @@ export interface GuardianApprovalRequest {
|
|
|
29
28
|
runId: string;
|
|
30
29
|
requestId: string;
|
|
31
30
|
conversationId: string;
|
|
32
|
-
assistantId: string;
|
|
33
31
|
channel: string;
|
|
34
32
|
requesterExternalUserId: string;
|
|
35
33
|
requesterChatId: string;
|
|
@@ -57,7 +55,6 @@ function rowToApprovalRequest(
|
|
|
57
55
|
runId: row.runId,
|
|
58
56
|
requestId: row.requestId ?? row.runId,
|
|
59
57
|
conversationId: row.conversationId,
|
|
60
|
-
assistantId: row.assistantId,
|
|
61
58
|
channel: row.channel,
|
|
62
59
|
requesterExternalUserId: row.requesterExternalUserId,
|
|
63
60
|
requesterChatId: row.requesterChatId,
|
|
@@ -88,7 +85,6 @@ export function createApprovalRequest(params: {
|
|
|
88
85
|
runId: string;
|
|
89
86
|
requestId?: string;
|
|
90
87
|
conversationId: string;
|
|
91
|
-
assistantId?: string;
|
|
92
88
|
channel: string;
|
|
93
89
|
requesterExternalUserId: string;
|
|
94
90
|
requesterChatId: string;
|
|
@@ -108,7 +104,6 @@ export function createApprovalRequest(params: {
|
|
|
108
104
|
runId: params.runId,
|
|
109
105
|
requestId: params.requestId ?? null,
|
|
110
106
|
conversationId: params.conversationId,
|
|
111
|
-
assistantId: params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
112
107
|
channel: params.channel,
|
|
113
108
|
requesterExternalUserId: params.requesterExternalUserId,
|
|
114
109
|
requesterChatId: params.requesterChatId,
|
|
@@ -172,34 +167,25 @@ export function getUnresolvedApprovalForRequest(
|
|
|
172
167
|
/**
|
|
173
168
|
* Find a pending guardian approval request by the guardian's chat ID.
|
|
174
169
|
* Used when the guardian sends a decision from their chat.
|
|
175
|
-
*
|
|
176
|
-
* When `assistantId` is provided, the lookup is scoped to that assistant,
|
|
177
|
-
* preventing cross-assistant approval consumption in shared guardian chats.
|
|
178
170
|
*/
|
|
179
171
|
export function getPendingApprovalByGuardianChat(
|
|
180
172
|
channel: string,
|
|
181
173
|
guardianChatId: string,
|
|
182
|
-
assistantId?: string,
|
|
183
174
|
): GuardianApprovalRequest | null {
|
|
184
175
|
const db = getDb();
|
|
185
176
|
const now = Date.now();
|
|
186
177
|
|
|
187
|
-
const conditions = [
|
|
188
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
189
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
190
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
191
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
192
|
-
];
|
|
193
|
-
if (assistantId) {
|
|
194
|
-
conditions.push(
|
|
195
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
178
|
const row = db
|
|
200
179
|
.select()
|
|
201
180
|
.from(channelGuardianApprovalRequests)
|
|
202
|
-
.where(
|
|
181
|
+
.where(
|
|
182
|
+
and(
|
|
183
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
184
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
185
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
186
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
187
|
+
),
|
|
188
|
+
)
|
|
203
189
|
.orderBy(desc(channelGuardianApprovalRequests.createdAt))
|
|
204
190
|
.get();
|
|
205
191
|
|
|
@@ -216,28 +202,22 @@ export function getPendingApprovalByRequestAndGuardianChat(
|
|
|
216
202
|
requestId: string,
|
|
217
203
|
channel: string,
|
|
218
204
|
guardianChatId: string,
|
|
219
|
-
assistantId?: string,
|
|
220
205
|
): GuardianApprovalRequest | null {
|
|
221
206
|
const db = getDb();
|
|
222
207
|
const now = Date.now();
|
|
223
208
|
|
|
224
|
-
const conditions = [
|
|
225
|
-
eq(channelGuardianApprovalRequests.requestId, requestId),
|
|
226
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
227
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
228
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
229
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
230
|
-
];
|
|
231
|
-
if (assistantId) {
|
|
232
|
-
conditions.push(
|
|
233
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
209
|
const row = db
|
|
238
210
|
.select()
|
|
239
211
|
.from(channelGuardianApprovalRequests)
|
|
240
|
-
.where(
|
|
212
|
+
.where(
|
|
213
|
+
and(
|
|
214
|
+
eq(channelGuardianApprovalRequests.requestId, requestId),
|
|
215
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
216
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
217
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
218
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
219
|
+
),
|
|
220
|
+
)
|
|
241
221
|
.get();
|
|
242
222
|
|
|
243
223
|
return row ? rowToApprovalRequest(row) : null;
|
|
@@ -247,34 +227,25 @@ export function getPendingApprovalByRequestAndGuardianChat(
|
|
|
247
227
|
* Return all pending (non-expired) guardian approval requests for a given
|
|
248
228
|
* guardian chat and channel. Used to detect ambiguity when a guardian sends
|
|
249
229
|
* a plain-text decision while multiple approvals are pending.
|
|
250
|
-
*
|
|
251
|
-
* When `assistantId` is provided, the results are scoped to that assistant
|
|
252
|
-
* to prevent cross-assistant approval consumption.
|
|
253
230
|
*/
|
|
254
231
|
export function getAllPendingApprovalsByGuardianChat(
|
|
255
232
|
channel: string,
|
|
256
233
|
guardianChatId: string,
|
|
257
|
-
assistantId?: string,
|
|
258
234
|
): GuardianApprovalRequest[] {
|
|
259
235
|
const db = getDb();
|
|
260
236
|
const now = Date.now();
|
|
261
237
|
|
|
262
|
-
const conditions = [
|
|
263
|
-
eq(channelGuardianApprovalRequests.channel, channel),
|
|
264
|
-
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
265
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
266
|
-
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
267
|
-
];
|
|
268
|
-
if (assistantId) {
|
|
269
|
-
conditions.push(
|
|
270
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
238
|
const rows = db
|
|
275
239
|
.select()
|
|
276
240
|
.from(channelGuardianApprovalRequests)
|
|
277
|
-
.where(
|
|
241
|
+
.where(
|
|
242
|
+
and(
|
|
243
|
+
eq(channelGuardianApprovalRequests.channel, channel),
|
|
244
|
+
eq(channelGuardianApprovalRequests.guardianChatId, guardianChatId),
|
|
245
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
246
|
+
gt(channelGuardianApprovalRequests.expiresAt, now),
|
|
247
|
+
),
|
|
248
|
+
)
|
|
278
249
|
.orderBy(desc(channelGuardianApprovalRequests.createdAt))
|
|
279
250
|
.all();
|
|
280
251
|
|
|
@@ -326,11 +297,10 @@ export function updateApprovalDecision(
|
|
|
326
297
|
// ---------------------------------------------------------------------------
|
|
327
298
|
|
|
328
299
|
/**
|
|
329
|
-
* List approval requests filtered by
|
|
330
|
-
*
|
|
300
|
+
* List approval requests optionally filtered by channel, conversation, and
|
|
301
|
+
* status. Returns a paginated list of escalations.
|
|
331
302
|
*/
|
|
332
303
|
export function listPendingApprovalRequests(params: {
|
|
333
|
-
assistantId?: string;
|
|
334
304
|
channel?: string;
|
|
335
305
|
conversationId?: string;
|
|
336
306
|
status?: ApprovalRequestStatus;
|
|
@@ -339,12 +309,7 @@ export function listPendingApprovalRequests(params: {
|
|
|
339
309
|
}): GuardianApprovalRequest[] {
|
|
340
310
|
const db = getDb();
|
|
341
311
|
|
|
342
|
-
const conditions = [
|
|
343
|
-
eq(
|
|
344
|
-
channelGuardianApprovalRequests.assistantId,
|
|
345
|
-
params.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
346
|
-
),
|
|
347
|
-
];
|
|
312
|
+
const conditions: ReturnType<typeof eq>[] = [];
|
|
348
313
|
if (params.channel) {
|
|
349
314
|
conditions.push(
|
|
350
315
|
eq(channelGuardianApprovalRequests.channel, params.channel),
|
|
@@ -447,26 +412,18 @@ export function resolveApprovalRequest(
|
|
|
447
412
|
* Count pending approval requests for a given conversation.
|
|
448
413
|
* Used by thread state projection to compute `pending_escalation_count`.
|
|
449
414
|
*/
|
|
450
|
-
export function countPendingByConversation(
|
|
451
|
-
conversationId: string,
|
|
452
|
-
assistantId?: string,
|
|
453
|
-
): number {
|
|
415
|
+
export function countPendingByConversation(conversationId: string): number {
|
|
454
416
|
const db = getDb();
|
|
455
417
|
|
|
456
|
-
const conditions = [
|
|
457
|
-
eq(channelGuardianApprovalRequests.conversationId, conversationId),
|
|
458
|
-
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
459
|
-
];
|
|
460
|
-
if (assistantId) {
|
|
461
|
-
conditions.push(
|
|
462
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
418
|
const result = db
|
|
467
419
|
.select({ count: count() })
|
|
468
420
|
.from(channelGuardianApprovalRequests)
|
|
469
|
-
.where(
|
|
421
|
+
.where(
|
|
422
|
+
and(
|
|
423
|
+
eq(channelGuardianApprovalRequests.conversationId, conversationId),
|
|
424
|
+
eq(channelGuardianApprovalRequests.status, "pending"),
|
|
425
|
+
),
|
|
426
|
+
)
|
|
470
427
|
.get();
|
|
471
428
|
|
|
472
429
|
return result?.count ?? 0;
|
|
@@ -478,7 +435,6 @@ export function countPendingByConversation(
|
|
|
478
435
|
* Retained for existing test fixtures that check legacy approval dedup.
|
|
479
436
|
*/
|
|
480
437
|
export function findPendingAccessRequestForRequester(
|
|
481
|
-
assistantId: string,
|
|
482
438
|
channel: string,
|
|
483
439
|
requesterExternalUserId: string,
|
|
484
440
|
toolName: string,
|
|
@@ -491,7 +447,6 @@ export function findPendingAccessRequestForRequester(
|
|
|
491
447
|
.from(channelGuardianApprovalRequests)
|
|
492
448
|
.where(
|
|
493
449
|
and(
|
|
494
|
-
eq(channelGuardianApprovalRequests.assistantId, assistantId),
|
|
495
450
|
eq(channelGuardianApprovalRequests.channel, channel),
|
|
496
451
|
eq(
|
|
497
452
|
channelGuardianApprovalRequests.requesterExternalUserId,
|
|
@@ -17,7 +17,6 @@ import { channelGuardianRateLimits } from "./schema.js";
|
|
|
17
17
|
|
|
18
18
|
export interface VerificationRateLimit {
|
|
19
19
|
id: string;
|
|
20
|
-
assistantId: string;
|
|
21
20
|
channel: string;
|
|
22
21
|
actorExternalUserId: string;
|
|
23
22
|
actorChatId: string;
|
|
@@ -49,7 +48,6 @@ function rowToRateLimit(
|
|
|
49
48
|
const timestamps = parseTimestamps(row.attemptTimestampsJson);
|
|
50
49
|
return {
|
|
51
50
|
id: row.id,
|
|
52
|
-
assistantId: row.assistantId,
|
|
53
51
|
channel: row.channel,
|
|
54
52
|
actorExternalUserId: row.actorExternalUserId,
|
|
55
53
|
actorChatId: row.actorChatId,
|
|
@@ -69,7 +67,6 @@ function rowToRateLimit(
|
|
|
69
67
|
* Get the rate-limit record for a given actor on a specific channel.
|
|
70
68
|
*/
|
|
71
69
|
export function getRateLimit(
|
|
72
|
-
assistantId: string,
|
|
73
70
|
channel: string,
|
|
74
71
|
actorExternalUserId: string,
|
|
75
72
|
actorChatId: string,
|
|
@@ -80,7 +77,6 @@ export function getRateLimit(
|
|
|
80
77
|
.from(channelGuardianRateLimits)
|
|
81
78
|
.where(
|
|
82
79
|
and(
|
|
83
|
-
eq(channelGuardianRateLimits.assistantId, assistantId),
|
|
84
80
|
eq(channelGuardianRateLimits.channel, channel),
|
|
85
81
|
eq(channelGuardianRateLimits.actorExternalUserId, actorExternalUserId),
|
|
86
82
|
eq(channelGuardianRateLimits.actorChatId, actorChatId),
|
|
@@ -101,7 +97,6 @@ export function getRateLimit(
|
|
|
101
97
|
* accumulate indefinitely.
|
|
102
98
|
*/
|
|
103
99
|
export function recordInvalidAttempt(
|
|
104
|
-
assistantId: string,
|
|
105
100
|
channel: string,
|
|
106
101
|
actorExternalUserId: string,
|
|
107
102
|
actorChatId: string,
|
|
@@ -113,12 +108,7 @@ export function recordInvalidAttempt(
|
|
|
113
108
|
const now = Date.now();
|
|
114
109
|
const cutoff = now - windowMs;
|
|
115
110
|
|
|
116
|
-
const existing = getRateLimit(
|
|
117
|
-
assistantId,
|
|
118
|
-
channel,
|
|
119
|
-
actorExternalUserId,
|
|
120
|
-
actorChatId,
|
|
121
|
-
);
|
|
111
|
+
const existing = getRateLimit(channel, actorExternalUserId, actorChatId);
|
|
122
112
|
|
|
123
113
|
if (existing) {
|
|
124
114
|
// Keep only timestamps within the sliding window, then add the new one
|
|
@@ -158,7 +148,6 @@ export function recordInvalidAttempt(
|
|
|
158
148
|
const lockedUntil = 1 >= maxAttempts ? now + lockoutMs : null;
|
|
159
149
|
const row = {
|
|
160
150
|
id,
|
|
161
|
-
assistantId,
|
|
162
151
|
channel,
|
|
163
152
|
actorExternalUserId,
|
|
164
153
|
actorChatId,
|
|
@@ -181,7 +170,6 @@ export function recordInvalidAttempt(
|
|
|
181
170
|
* successful verification).
|
|
182
171
|
*/
|
|
183
172
|
export function resetRateLimit(
|
|
184
|
-
assistantId: string,
|
|
185
173
|
channel: string,
|
|
186
174
|
actorExternalUserId: string,
|
|
187
175
|
actorChatId: string,
|
|
@@ -197,7 +185,6 @@ export function resetRateLimit(
|
|
|
197
185
|
})
|
|
198
186
|
.where(
|
|
199
187
|
and(
|
|
200
|
-
eq(channelGuardianRateLimits.assistantId, assistantId),
|
|
201
188
|
eq(channelGuardianRateLimits.channel, channel),
|
|
202
189
|
eq(channelGuardianRateLimits.actorExternalUserId, actorExternalUserId),
|
|
203
190
|
eq(channelGuardianRateLimits.actorChatId, actorChatId),
|