@vellumai/assistant 0.5.7 → 0.5.8
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/Dockerfile +2 -1
- package/docker-entrypoint.sh +9 -0
- package/docs/architecture/memory.md +13 -11
- package/node_modules/@vellumai/ces-contracts/src/error.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/grants.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -1
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +1 -1
- package/package.json +1 -1
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/ces-rpc-credential-backend.test.ts +3 -3
- package/src/__tests__/ces-startup-timeout.test.ts +40 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-schema.test.ts +2 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +2 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -1
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/conversation-queue.test.ts +0 -1
- package/src/__tests__/conversation-slash-queue.test.ts +0 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/credential-execution-client.test.ts +5 -2
- package/src/__tests__/credential-execution-feature-gates.test.ts +31 -16
- package/src/__tests__/credential-execution-managed-contract.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +1 -1
- package/src/__tests__/credential-security-invariants.test.ts +2 -5
- package/src/__tests__/credentials-cli.test.ts +4 -3
- package/src/__tests__/daemon-credential-client.test.ts +123 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -0
- package/src/__tests__/gateway-client-managed-outbound.test.ts +79 -1
- package/src/__tests__/journal-context.test.ts +335 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -3
- package/src/__tests__/memory-lifecycle-e2e.test.ts +70 -25
- package/src/__tests__/memory-recall-quality.test.ts +48 -17
- package/src/__tests__/memory-regressions.test.ts +408 -363
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -3
- package/src/__tests__/non-member-access-request.test.ts +2 -2
- package/src/__tests__/notification-decision-strategy.test.ts +71 -0
- package/src/__tests__/oauth-cli.test.ts +5 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -37
- package/src/__tests__/provider-error-scenarios.test.ts +0 -267
- package/src/__tests__/provider-streaming.benchmark.test.ts +2 -81
- package/src/__tests__/relay-server.test.ts +1 -2
- package/src/__tests__/script-proxy-injection-runtime.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -1
- package/src/__tests__/secure-keys.test.ts +18 -15
- package/src/__tests__/skill-memory.test.ts +17 -3
- package/src/__tests__/stale-approval-dedup.test.ts +171 -0
- package/src/__tests__/stt-hints.test.ts +437 -0
- package/src/__tests__/task-memory-cleanup.test.ts +14 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +139 -1
- package/src/__tests__/voice-quality.test.ts +58 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/__tests__/workspace-migration-016-migrate-credentials-from-keychain.test.ts +5 -3
- package/src/acp/agent-process.ts +9 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +164 -38
- package/src/calls/__tests__/tts-text-sanitizer.test.ts +254 -0
- package/src/calls/call-controller.ts +9 -5
- package/src/calls/fish-audio-client.ts +26 -14
- package/src/calls/stt-hints.ts +189 -0
- package/src/calls/tts-text-sanitizer.ts +61 -0
- package/src/calls/twilio-routes.ts +32 -4
- package/src/calls/voice-quality.ts +15 -3
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/avatar.ts +2 -2
- package/src/cli/commands/credentials.ts +110 -94
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/keys.ts +7 -7
- package/src/cli/commands/memory.ts +1 -1
- package/src/cli/commands/oauth/connections.ts +11 -29
- package/src/cli/commands/oauth/platform.ts +389 -43
- package/src/cli/lib/daemon-credential-client.ts +284 -0
- package/src/cli.ts +1 -1
- package/src/config/bundled-skills/AGENTS.md +34 -0
- package/src/config/bundled-skills/acp/SKILL.md +10 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -4
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +1 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +15 -2
- package/src/config/bundled-skills/settings/TOOLS.json +46 -1
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +59 -0
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +80 -0
- package/src/config/bundled-skills/slack/SKILL.md +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/defaults.ts +0 -2
- package/src/config/env-registry.ts +4 -4
- package/src/config/env.ts +14 -1
- package/src/config/feature-flag-registry.json +1 -1
- package/src/config/loader.ts +8 -11
- package/src/config/schema.ts +5 -16
- package/src/config/schemas/calls.ts +17 -0
- package/src/config/schemas/inference.ts +2 -2
- package/src/config/schemas/journal.ts +16 -0
- package/src/config/schemas/memory-processing.ts +2 -2
- package/src/config/types.ts +1 -0
- package/src/contacts/contact-store.ts +2 -2
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/startup-timeout.ts +36 -0
- package/src/daemon/approval-generators.ts +3 -9
- package/src/daemon/conversation-error.ts +13 -1
- package/src/daemon/conversation-memory.ts +1 -2
- package/src/daemon/conversation-process.ts +18 -1
- package/src/daemon/conversation-surfaces.ts +30 -1
- package/src/daemon/conversation.ts +20 -9
- package/src/daemon/guardian-action-generators.ts +3 -9
- package/src/daemon/lifecycle.ts +18 -11
- package/src/daemon/message-types/conversations.ts +1 -0
- package/src/daemon/server.ts +2 -3
- package/src/memory/app-store.ts +31 -0
- package/src/memory/db-init.ts +4 -0
- package/src/memory/indexer.ts +19 -10
- package/src/memory/items-extractor.ts +315 -322
- package/src/memory/job-handlers/summarization.ts +26 -16
- package/src/memory/jobs-store.ts +33 -1
- package/src/memory/journal-memory.ts +214 -0
- package/src/memory/migrations/193-add-source-type-columns.ts +81 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/retriever.test.ts +37 -25
- package/src/memory/retriever.ts +24 -49
- package/src/memory/schema/memory-core.ts +2 -0
- package/src/memory/search/formatting.ts +7 -44
- package/src/memory/search/staleness.ts +4 -0
- package/src/memory/search/tier-classifier.ts +10 -2
- package/src/memory/search/types.ts +2 -5
- package/src/memory/task-memory-cleanup.ts +4 -3
- package/src/notifications/adapters/slack.ts +168 -6
- package/src/notifications/broadcaster.ts +1 -0
- package/src/notifications/copy-composer.ts +59 -2
- package/src/notifications/signal.ts +2 -0
- package/src/notifications/types.ts +2 -0
- package/src/prompts/journal-context.ts +133 -0
- package/src/prompts/persona-resolver.ts +80 -24
- package/src/prompts/system-prompt.ts +8 -0
- package/src/prompts/templates/SOUL.md +10 -0
- package/src/providers/provider-send-message.ts +3 -32
- package/src/providers/registry.ts +2 -139
- package/src/providers/types.ts +1 -1
- package/src/runtime/access-request-helper.ts +4 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +9 -50
- package/src/runtime/auth/route-policy.ts +2 -0
- package/src/runtime/gateway-client.ts +47 -4
- package/src/runtime/guardian-decision-types.ts +45 -4
- package/src/runtime/http-server.ts +5 -2
- package/src/runtime/routes/access-request-decision.ts +2 -2
- package/src/runtime/routes/app-management-routes.ts +2 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +219 -30
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +37 -14
- package/src/runtime/routes/channel-readiness-routes.ts +9 -4
- package/src/runtime/routes/debug-routes.ts +12 -9
- package/src/runtime/routes/guardian-approval-interception.ts +168 -11
- package/src/runtime/routes/guardian-approval-prompt.ts +6 -1
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +103 -21
- package/src/runtime/routes/identity-routes.ts +1 -1
- package/src/runtime/routes/inbound-message-handler.ts +31 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +64 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +52 -40
- package/src/runtime/routes/integrations/twilio.ts +52 -10
- package/src/runtime/routes/memory-item-routes.test.ts +3 -3
- package/src/runtime/routes/memory-item-routes.ts +25 -11
- package/src/runtime/routes/secret-routes.ts +141 -10
- package/src/runtime/routes/tts-routes.ts +11 -1
- package/src/security/ces-credential-client.ts +18 -9
- package/src/security/ces-rpc-credential-backend.ts +4 -3
- package/src/security/credential-backend.ts +10 -4
- package/src/security/secure-keys.ts +21 -4
- package/src/skills/catalog-install.ts +4 -36
- package/src/skills/skill-memory.ts +1 -0
- package/src/subagent/manager.ts +2 -5
- package/src/tools/acp/spawn.ts +78 -1
- package/src/tools/credentials/vault.ts +5 -3
- package/src/tools/memory/definitions.ts +3 -2
- package/src/tools/memory/handlers.ts +10 -7
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/util/browser.ts +15 -0
- package/src/util/platform.ts +1 -1
- package/src/workspace/migrations/016-migrate-credentials-from-keychain.ts +4 -4
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -1
- package/src/workspace/migrations/018-rekey-compound-credential-keys.ts +184 -0
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +103 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -4
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/provider-commit-message-generator.ts +12 -21
- package/src/__tests__/provider-fail-open-selection.test.ts +0 -271
- package/src/__tests__/provider-failover-actual-provider.test.ts +0 -66
- package/src/memory/search/lexical.ts +0 -48
- package/src/providers/failover.ts +0 -186
|
@@ -214,10 +214,11 @@ describe("secure-keys", () => {
|
|
|
214
214
|
encryptedStore.setKey("enc-key-1", "val1");
|
|
215
215
|
encryptedStore.setKey("enc-key-2", "val2");
|
|
216
216
|
|
|
217
|
-
const
|
|
218
|
-
expect(
|
|
219
|
-
expect(
|
|
220
|
-
expect(
|
|
217
|
+
const result = await listSecureKeysAsync();
|
|
218
|
+
expect(result.unreachable).toBe(false);
|
|
219
|
+
expect(result.accounts).toContain("enc-key-1");
|
|
220
|
+
expect(result.accounts).toContain("enc-key-2");
|
|
221
|
+
expect(result.accounts.length).toBe(2);
|
|
221
222
|
});
|
|
222
223
|
|
|
223
224
|
test("returns encrypted store keys with VELLUM_DEV=1", async () => {
|
|
@@ -227,10 +228,11 @@ describe("secure-keys", () => {
|
|
|
227
228
|
encryptedStore.setKey("dev-key-1", "val2");
|
|
228
229
|
encryptedStore.setKey("dev-key-2", "val3");
|
|
229
230
|
|
|
230
|
-
const
|
|
231
|
-
expect(
|
|
232
|
-
expect(
|
|
233
|
-
expect(
|
|
231
|
+
const result = await listSecureKeysAsync();
|
|
232
|
+
expect(result.unreachable).toBe(false);
|
|
233
|
+
expect(result.accounts).toContain("dev-key-1");
|
|
234
|
+
expect(result.accounts).toContain("dev-key-2");
|
|
235
|
+
expect(result.accounts.length).toBe(2);
|
|
234
236
|
});
|
|
235
237
|
|
|
236
238
|
test("returns encrypted store keys with VELLUM_DESKTOP_APP=1", async () => {
|
|
@@ -240,15 +242,16 @@ describe("secure-keys", () => {
|
|
|
240
242
|
encryptedStore.setKey("desktop-key-1", "val1");
|
|
241
243
|
encryptedStore.setKey("desktop-key-2", "val2");
|
|
242
244
|
|
|
243
|
-
const
|
|
244
|
-
expect(
|
|
245
|
-
expect(
|
|
246
|
-
expect(
|
|
245
|
+
const result = await listSecureKeysAsync();
|
|
246
|
+
expect(result.unreachable).toBe(false);
|
|
247
|
+
expect(result.accounts).toContain("desktop-key-1");
|
|
248
|
+
expect(result.accounts).toContain("desktop-key-2");
|
|
249
|
+
expect(result.accounts.length).toBe(2);
|
|
247
250
|
});
|
|
248
251
|
|
|
249
|
-
test("returns empty
|
|
250
|
-
const
|
|
251
|
-
expect(
|
|
252
|
+
test("returns empty accounts when store is empty", async () => {
|
|
253
|
+
const result = await listSecureKeysAsync();
|
|
254
|
+
expect(result).toEqual({ accounts: [], unreachable: false });
|
|
252
255
|
});
|
|
253
256
|
});
|
|
254
257
|
|
|
@@ -276,8 +276,14 @@ describe("upsertSkillCapabilityMemory", () => {
|
|
|
276
276
|
upsertSkillCapabilityMemory("test-skill", makeSkill());
|
|
277
277
|
}).not.toThrow();
|
|
278
278
|
|
|
279
|
-
// Restore DB state for subsequent tests
|
|
279
|
+
// Restore DB state for subsequent tests.
|
|
280
|
+
// Delete the entire DB so initializeDb recreates it from scratch — just
|
|
281
|
+
// resetting the connection leaves stale migration checkpoints that skip
|
|
282
|
+
// checkpoint-guarded ALTER TABLE migrations (e.g. source_type column).
|
|
280
283
|
resetDb();
|
|
284
|
+
for (const ext of ["", "-wal", "-shm"]) {
|
|
285
|
+
rmSync(join(testDir, `test.db${ext}`), { force: true });
|
|
286
|
+
}
|
|
281
287
|
initializeDb();
|
|
282
288
|
});
|
|
283
289
|
});
|
|
@@ -324,8 +330,12 @@ describe("deleteSkillCapabilityMemory", () => {
|
|
|
324
330
|
deleteSkillCapabilityMemory("test-skill");
|
|
325
331
|
}).not.toThrow();
|
|
326
332
|
|
|
327
|
-
// Restore DB state for subsequent tests
|
|
333
|
+
// Restore DB state for subsequent tests (see upsert "does not throw" test
|
|
334
|
+
// for rationale on why we delete the DB file).
|
|
328
335
|
resetDb();
|
|
336
|
+
for (const ext of ["", "-wal", "-shm"]) {
|
|
337
|
+
rmSync(join(testDir, `test.db${ext}`), { force: true });
|
|
338
|
+
}
|
|
329
339
|
initializeDb();
|
|
330
340
|
});
|
|
331
341
|
});
|
|
@@ -542,8 +552,12 @@ describe("seedCatalogSkillMemories", () => {
|
|
|
542
552
|
|
|
543
553
|
await expect(seedCatalogSkillMemories()).resolves.toBeUndefined();
|
|
544
554
|
|
|
545
|
-
// Restore DB state for subsequent tests
|
|
555
|
+
// Restore DB state for subsequent tests (see upsert "does not throw" test
|
|
556
|
+
// for rationale on why we delete the DB file).
|
|
546
557
|
resetDb();
|
|
558
|
+
for (const ext of ["", "-wal", "-shm"]) {
|
|
559
|
+
rmSync(join(testDir, `test.db${ext}`), { force: true });
|
|
560
|
+
}
|
|
547
561
|
initializeDb();
|
|
548
562
|
});
|
|
549
563
|
});
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Mocks — must be declared before importing the module under test
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
mock.module("../util/logger.js", () => ({
|
|
8
|
+
getLogger: () =>
|
|
9
|
+
new Proxy({} as Record<string, unknown>, {
|
|
10
|
+
get: () => () => {},
|
|
11
|
+
}),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const deliveredMessages: Array<{
|
|
15
|
+
url: string;
|
|
16
|
+
body: Record<string, unknown>;
|
|
17
|
+
}> = [];
|
|
18
|
+
|
|
19
|
+
let deliveryShouldFail = false;
|
|
20
|
+
|
|
21
|
+
mock.module("../runtime/gateway-client.js", () => ({
|
|
22
|
+
deliverChannelReply: async (url: string, body: Record<string, unknown>) => {
|
|
23
|
+
if (deliveryShouldFail) {
|
|
24
|
+
throw new Error("simulated delivery failure");
|
|
25
|
+
}
|
|
26
|
+
deliveredMessages.push({ url, body });
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
mock.module("../runtime/approval-message-composer.js", () => ({
|
|
31
|
+
composeApprovalMessageGenerative: async () => "Already resolved.",
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Import the module under test after mocks are set up
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
import type pino from "pino";
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
clearStaleNotificationCache,
|
|
42
|
+
deliverStaleApprovalReply,
|
|
43
|
+
} from "../runtime/routes/guardian-approval-reply-helpers.js";
|
|
44
|
+
|
|
45
|
+
const noopLogger = new Proxy({} as pino.Logger, {
|
|
46
|
+
get: () => () => {},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Tests
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
describe("deliverStaleApprovalReply deduplication", () => {
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
deliveredMessages.length = 0;
|
|
56
|
+
deliveryShouldFail = false;
|
|
57
|
+
clearStaleNotificationCache();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
clearStaleNotificationCache();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("sends the first 'approval_already_resolved' notification", async () => {
|
|
65
|
+
await deliverStaleApprovalReply({
|
|
66
|
+
scenario: "approval_already_resolved",
|
|
67
|
+
sourceChannel: "slack",
|
|
68
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
69
|
+
chatId: "chat-1",
|
|
70
|
+
assistantId: "asst-1",
|
|
71
|
+
logger: noopLogger,
|
|
72
|
+
errorLogMessage: "test",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(deliveredMessages).toHaveLength(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("suppresses duplicate 'approval_already_resolved' for the same chat", async () => {
|
|
79
|
+
const params = {
|
|
80
|
+
scenario: "approval_already_resolved" as const,
|
|
81
|
+
sourceChannel: "slack" as const,
|
|
82
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
83
|
+
chatId: "chat-1",
|
|
84
|
+
assistantId: "asst-1",
|
|
85
|
+
logger: noopLogger,
|
|
86
|
+
errorLogMessage: "test",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
await deliverStaleApprovalReply(params);
|
|
90
|
+
await deliverStaleApprovalReply(params);
|
|
91
|
+
await deliverStaleApprovalReply(params);
|
|
92
|
+
|
|
93
|
+
expect(deliveredMessages).toHaveLength(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("allows 'approval_already_resolved' for different chats", async () => {
|
|
97
|
+
const base = {
|
|
98
|
+
scenario: "approval_already_resolved" as const,
|
|
99
|
+
sourceChannel: "slack" as const,
|
|
100
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
101
|
+
assistantId: "asst-1",
|
|
102
|
+
logger: noopLogger,
|
|
103
|
+
errorLogMessage: "test",
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
await deliverStaleApprovalReply({ ...base, chatId: "chat-1" });
|
|
107
|
+
await deliverStaleApprovalReply({ ...base, chatId: "chat-2" });
|
|
108
|
+
|
|
109
|
+
expect(deliveredMessages).toHaveLength(2);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("does not deduplicate non-'approval_already_resolved' scenarios", async () => {
|
|
113
|
+
const params = {
|
|
114
|
+
scenario: "reminder_prompt" as const,
|
|
115
|
+
sourceChannel: "slack" as const,
|
|
116
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
117
|
+
chatId: "chat-1",
|
|
118
|
+
assistantId: "asst-1",
|
|
119
|
+
logger: noopLogger,
|
|
120
|
+
errorLogMessage: "test",
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
await deliverStaleApprovalReply(params);
|
|
124
|
+
await deliverStaleApprovalReply(params);
|
|
125
|
+
|
|
126
|
+
expect(deliveredMessages).toHaveLength(2);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("allows re-send after cache is cleared (simulates TTL expiry)", async () => {
|
|
130
|
+
const params = {
|
|
131
|
+
scenario: "approval_already_resolved" as const,
|
|
132
|
+
sourceChannel: "slack" as const,
|
|
133
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
134
|
+
chatId: "chat-1",
|
|
135
|
+
assistantId: "asst-1",
|
|
136
|
+
logger: noopLogger,
|
|
137
|
+
errorLogMessage: "test",
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
await deliverStaleApprovalReply(params);
|
|
141
|
+
expect(deliveredMessages).toHaveLength(1);
|
|
142
|
+
|
|
143
|
+
// Simulate TTL expiry
|
|
144
|
+
clearStaleNotificationCache();
|
|
145
|
+
|
|
146
|
+
await deliverStaleApprovalReply(params);
|
|
147
|
+
expect(deliveredMessages).toHaveLength(2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("does not cache dedup key when delivery fails, allowing retries", async () => {
|
|
151
|
+
const params = {
|
|
152
|
+
scenario: "approval_already_resolved" as const,
|
|
153
|
+
sourceChannel: "slack" as const,
|
|
154
|
+
replyCallbackUrl: "https://example.com/reply",
|
|
155
|
+
chatId: "chat-1",
|
|
156
|
+
assistantId: "asst-1",
|
|
157
|
+
logger: noopLogger,
|
|
158
|
+
errorLogMessage: "test",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// First attempt fails — should not cache the dedup key
|
|
162
|
+
deliveryShouldFail = true;
|
|
163
|
+
await deliverStaleApprovalReply(params);
|
|
164
|
+
expect(deliveredMessages).toHaveLength(0);
|
|
165
|
+
|
|
166
|
+
// Second attempt succeeds — should not be suppressed by dedup
|
|
167
|
+
deliveryShouldFail = false;
|
|
168
|
+
await deliverStaleApprovalReply(params);
|
|
169
|
+
expect(deliveredMessages).toHaveLength(1);
|
|
170
|
+
});
|
|
171
|
+
});
|