@meetquinn/animactl 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/dist/server/{runtime/activity-payload.d.ts → activities/format.d.ts} +4 -1
- package/dist/server/activities/format.d.ts.map +1 -0
- package/dist/server/{runtime/activity-payload.js → activities/format.js} +16 -1
- package/dist/server/agents/agent-skills.d.ts +7 -3
- package/dist/server/agents/agent-skills.d.ts.map +1 -1
- package/dist/server/agents/agent-skills.js +243 -38
- package/dist/server/agents/agent-slack.service.d.ts +15 -0
- package/dist/server/agents/agent-slack.service.d.ts.map +1 -1
- package/dist/server/agents/agent-slack.service.js +43 -6
- package/dist/server/agents/default-skills.d.ts +9 -0
- package/dist/server/agents/default-skills.d.ts.map +1 -0
- package/dist/server/agents/default-skills.js +52 -0
- package/dist/server/cli/runtime-cli.d.ts +7 -1
- package/dist/server/cli/runtime-cli.d.ts.map +1 -1
- package/dist/server/cli/runtime-cli.js +107 -3
- package/dist/server/cli/services-cli.d.ts.map +1 -1
- package/dist/server/cli/services-cli.js +39 -0
- package/dist/server/inbox/slack-subscriber.d.ts +2 -0
- package/dist/server/inbox/slack-subscriber.d.ts.map +1 -1
- package/dist/server/inbox/slack-subscriber.js +28 -2
- package/dist/server/inbox/slack-subscription.service.d.ts.map +1 -1
- package/dist/server/inbox/slack-subscription.service.js +13 -1
- package/dist/server/inbox/wake-queue.service.d.ts +3 -1
- package/dist/server/inbox/wake-queue.service.d.ts.map +1 -1
- package/dist/server/inbox/wake-queue.service.js +5 -3
- package/dist/server/provider-usage/http.d.ts +1 -0
- package/dist/server/provider-usage/http.d.ts.map +1 -1
- package/dist/server/provider-usage/http.js +23 -1
- package/dist/server/provider-usage/provider-usage.service.d.ts +1 -4
- package/dist/server/provider-usage/provider-usage.service.d.ts.map +1 -1
- package/dist/server/provider-usage/provider-usage.service.js +1 -18
- package/dist/server/provider-usage/providers/claude.d.ts +1 -1
- package/dist/server/provider-usage/providers/claude.d.ts.map +1 -1
- package/dist/server/provider-usage/providers/claude.js +1 -1
- package/dist/server/provider-usage/providers/codex.d.ts +1 -1
- package/dist/server/provider-usage/providers/codex.d.ts.map +1 -1
- package/dist/server/provider-usage/providers/codex.js +1 -1
- package/dist/server/provider-usage/providers/kimi.d.ts +1 -1
- package/dist/server/provider-usage/providers/kimi.d.ts.map +1 -1
- package/dist/server/provider-usage/providers/kimi.js +4 -4
- package/dist/server/provider-usage/result.d.ts +5 -0
- package/dist/server/provider-usage/result.d.ts.map +1 -0
- package/dist/server/provider-usage/result.js +18 -0
- package/dist/server/providers/active-runtime.d.ts +1 -1
- package/dist/server/providers/active-runtime.d.ts.map +1 -1
- package/dist/server/providers/claude-events.d.ts +1 -1
- package/dist/server/providers/claude-events.d.ts.map +1 -1
- package/dist/server/providers/claude-events.js +2 -2
- package/dist/server/providers/claude.d.ts +1 -1
- package/dist/server/providers/claude.d.ts.map +1 -1
- package/dist/server/providers/claude.js +2 -2
- package/dist/server/providers/codex-app-server.d.ts +1 -1
- package/dist/server/providers/codex-app-server.d.ts.map +1 -1
- package/dist/server/providers/codex-app-server.js +1 -1
- package/dist/server/providers/codex-events.js +1 -1
- package/dist/server/providers/codex.d.ts +1 -1
- package/dist/server/providers/codex.d.ts.map +1 -1
- package/dist/server/providers/codex.js +2 -2
- package/dist/server/{runtime/provider-contract.d.ts → providers/contract.d.ts} +1 -1
- package/dist/server/providers/contract.d.ts.map +1 -0
- package/dist/server/providers/factory.d.ts +1 -1
- package/dist/server/providers/factory.d.ts.map +1 -1
- package/dist/server/providers/kimi-events.d.ts +1 -1
- package/dist/server/providers/kimi-events.d.ts.map +1 -1
- package/dist/server/providers/kimi-events.js +1 -1
- package/dist/server/providers/kimi.d.ts +1 -1
- package/dist/server/providers/kimi.d.ts.map +1 -1
- package/dist/server/providers/kimi.js +3 -2
- package/dist/server/providers/reasoning-events.js +1 -1
- package/dist/server/runtime/active-run-control.d.ts +19 -0
- package/dist/server/runtime/active-run-control.d.ts.map +1 -0
- package/dist/server/runtime/active-run-control.js +89 -0
- package/dist/server/runtime/activity.js +1 -1
- package/dist/server/runtime/agent-runner.d.ts +1 -1
- package/dist/server/runtime/agent-runner.d.ts.map +1 -1
- package/dist/server/runtime/delivery-prompt.d.ts +6 -0
- package/dist/server/runtime/delivery-prompt.d.ts.map +1 -1
- package/dist/server/runtime/delivery-prompt.js +53 -34
- package/dist/server/runtime/followup-appender.d.ts +20 -0
- package/dist/server/runtime/followup-appender.d.ts.map +1 -0
- package/dist/server/runtime/followup-appender.js +128 -0
- package/dist/server/runtime/host.d.ts +2 -0
- package/dist/server/runtime/host.d.ts.map +1 -1
- package/dist/server/runtime/host.js +8 -0
- package/dist/server/runtime/provider-runner.d.ts +16 -0
- package/dist/server/runtime/provider-runner.d.ts.map +1 -0
- package/dist/server/runtime/provider-runner.js +86 -0
- package/dist/server/runtime/runtime-bridge.d.ts +1 -1
- package/dist/server/runtime/runtime-bridge.d.ts.map +1 -1
- package/dist/server/runtime/runtime-session.service.d.ts +1 -1
- package/dist/server/runtime/runtime-session.service.d.ts.map +1 -1
- package/dist/server/runtime/runtime-session.service.js +1 -1
- package/dist/server/runtime/runtime-worker.d.ts +2 -10
- package/dist/server/runtime/runtime-worker.d.ts.map +1 -1
- package/dist/server/runtime/runtime-worker.js +60 -286
- package/dist/server/runtime-management/managed-runtime.d.ts.map +1 -0
- package/dist/server/runtime-management/runtime-release.d.ts.map +1 -0
- package/dist/server/{runtime → runtime-management}/runtime-upgrade.d.ts +1 -0
- package/dist/server/runtime-management/runtime-upgrade.d.ts.map +1 -0
- package/dist/server/{runtime → runtime-management}/runtime-upgrade.js +14 -0
- package/dist/server/services/system.service.d.ts +41 -0
- package/dist/server/services/system.service.d.ts.map +1 -0
- package/dist/server/services/system.service.js +154 -0
- package/dist/server/settings/settings.service.d.ts +2 -1
- package/dist/server/settings/settings.service.d.ts.map +1 -1
- package/dist/server/settings/settings.service.js +9 -1
- package/dist/server/slack/shortcuts.d.ts +0 -3
- package/dist/server/slack/shortcuts.d.ts.map +1 -1
- package/dist/server/slack/shortcuts.js +0 -3
- package/dist/server/slack-interactions/shortcut-ids.d.ts +4 -0
- package/dist/server/slack-interactions/shortcut-ids.d.ts.map +1 -0
- package/dist/server/slack-interactions/shortcut-ids.js +3 -0
- package/dist/server/slack-interactions/shortcut-views.d.ts +65 -0
- package/dist/server/slack-interactions/shortcut-views.d.ts.map +1 -0
- package/dist/server/slack-interactions/shortcut-views.js +211 -0
- package/dist/server/{slack → slack-interactions}/shortcut.service.d.ts +2 -47
- package/dist/server/slack-interactions/shortcut.service.d.ts.map +1 -0
- package/dist/server/slack-interactions/shortcut.service.js +227 -0
- package/dist/server/storage/jsonl-log.d.ts +9 -0
- package/dist/server/storage/jsonl-log.d.ts.map +1 -1
- package/dist/server/storage/jsonl-log.js +36 -1
- package/dist/server/storage/schema/server.store.d.ts +5 -0
- package/dist/server/storage/schema/server.store.d.ts.map +1 -1
- package/dist/server/storage/schema/server.store.js +2 -1
- package/dist/server/storage/schema/wake-queue.store.d.ts +15 -1
- package/dist/server/storage/schema/wake-queue.store.d.ts.map +1 -1
- package/dist/server/storage/schema/wake-queue.store.js +5 -3
- package/dist/server/tests/agent-config.test.d.ts +2 -0
- package/dist/server/tests/agent-config.test.d.ts.map +1 -0
- package/dist/server/tests/agent-config.test.js +229 -0
- package/dist/server/tests/agent-runtime.test.d.ts +2 -0
- package/dist/server/tests/agent-runtime.test.d.ts.map +1 -0
- package/dist/server/tests/agent-runtime.test.js +1110 -0
- package/dist/server/tests/agent-seed-memory.test.d.ts +2 -0
- package/dist/server/tests/agent-seed-memory.test.d.ts.map +1 -0
- package/dist/server/tests/agent-seed-memory.test.js +16 -0
- package/dist/server/tests/agent-skills.test.d.ts +2 -0
- package/dist/server/tests/agent-skills.test.d.ts.map +1 -0
- package/dist/server/tests/agent-skills.test.js +140 -0
- package/dist/server/tests/anima-home.d.ts +2 -0
- package/dist/server/tests/anima-home.d.ts.map +1 -0
- package/dist/server/tests/anima-home.js +4 -0
- package/dist/server/tests/cli-file.test.d.ts +2 -0
- package/dist/server/tests/cli-file.test.d.ts.map +1 -0
- package/dist/server/tests/cli-file.test.js +596 -0
- package/dist/server/tests/cli-message.test.d.ts +2 -0
- package/dist/server/tests/cli-message.test.d.ts.map +1 -0
- package/dist/server/tests/cli-message.test.js +1196 -0
- package/dist/server/tests/client-error-routes.test.d.ts +2 -0
- package/dist/server/tests/client-error-routes.test.d.ts.map +1 -0
- package/dist/server/tests/client-error-routes.test.js +163 -0
- package/dist/server/tests/config.test.d.ts +2 -0
- package/dist/server/tests/config.test.d.ts.map +1 -0
- package/dist/server/tests/config.test.js +497 -0
- package/dist/server/tests/default-skills.test.d.ts +2 -0
- package/dist/server/tests/default-skills.test.d.ts.map +1 -0
- package/dist/server/tests/default-skills.test.js +57 -0
- package/dist/server/tests/helpers/inbox.d.ts +10 -0
- package/dist/server/tests/helpers/inbox.d.ts.map +1 -0
- package/dist/server/tests/helpers/inbox.js +21 -0
- package/dist/server/tests/helpers/slack.d.ts +11 -0
- package/dist/server/tests/helpers/slack.d.ts.map +1 -0
- package/dist/server/tests/helpers/slack.js +17 -0
- package/dist/server/tests/helpers/state.d.ts +19 -0
- package/dist/server/tests/helpers/state.d.ts.map +1 -0
- package/dist/server/tests/helpers/state.js +69 -0
- package/dist/server/tests/inbox-slack-events.test.d.ts +2 -0
- package/dist/server/tests/inbox-slack-events.test.d.ts.map +1 -0
- package/dist/server/tests/inbox-slack-events.test.js +271 -0
- package/dist/server/tests/inbox.test.d.ts +2 -0
- package/dist/server/tests/inbox.test.d.ts.map +1 -0
- package/dist/server/tests/inbox.test.js +144 -0
- package/dist/server/tests/interactive-ask.test.d.ts +2 -0
- package/dist/server/tests/interactive-ask.test.d.ts.map +1 -0
- package/dist/server/tests/interactive-ask.test.js +387 -0
- package/dist/server/tests/kb.test.d.ts +2 -0
- package/dist/server/tests/kb.test.d.ts.map +1 -0
- package/dist/server/tests/kb.test.js +371 -0
- package/dist/server/tests/message.service.test.d.ts +2 -0
- package/dist/server/tests/message.service.test.d.ts.map +1 -0
- package/dist/server/tests/message.service.test.js +151 -0
- package/dist/server/tests/messages.test.d.ts +2 -0
- package/dist/server/tests/messages.test.d.ts.map +1 -0
- package/dist/server/tests/messages.test.js +12 -0
- package/dist/server/tests/prompt-attachments.test.d.ts +2 -0
- package/dist/server/tests/prompt-attachments.test.d.ts.map +1 -0
- package/dist/server/tests/prompt-attachments.test.js +274 -0
- package/dist/server/tests/provider-usage.test.d.ts +2 -0
- package/dist/server/tests/provider-usage.test.d.ts.map +1 -0
- package/dist/server/tests/provider-usage.test.js +97 -0
- package/dist/server/tests/reminders.test.d.ts +2 -0
- package/dist/server/tests/reminders.test.d.ts.map +1 -0
- package/dist/server/tests/reminders.test.js +383 -0
- package/dist/server/tests/runtime-cli.test.d.ts +2 -0
- package/dist/server/tests/runtime-cli.test.d.ts.map +1 -0
- package/dist/server/tests/runtime-cli.test.js +21 -0
- package/dist/server/tests/runtime-upgrade.test.d.ts +2 -0
- package/dist/server/tests/runtime-upgrade.test.d.ts.map +1 -0
- package/dist/server/tests/runtime-upgrade.test.js +307 -0
- package/dist/server/tests/runtime-worker.test.d.ts +2 -0
- package/dist/server/tests/runtime-worker.test.d.ts.map +1 -0
- package/dist/server/tests/runtime-worker.test.js +1021 -0
- package/dist/server/tests/runtime.test.d.ts +2 -0
- package/dist/server/tests/runtime.test.d.ts.map +1 -0
- package/dist/server/tests/runtime.test.js +354 -0
- package/dist/server/tests/services.test.d.ts +2 -0
- package/dist/server/tests/services.test.d.ts.map +1 -0
- package/dist/server/tests/services.test.js +636 -0
- package/dist/server/tests/slack-files.test.d.ts +2 -0
- package/dist/server/tests/slack-files.test.d.ts.map +1 -0
- package/dist/server/tests/slack-files.test.js +118 -0
- package/dist/server/tests/slack-shortcuts.test.d.ts +2 -0
- package/dist/server/tests/slack-shortcuts.test.d.ts.map +1 -0
- package/dist/server/tests/slack-shortcuts.test.js +310 -0
- package/dist/server/tests/slack.test.d.ts +2 -0
- package/dist/server/tests/slack.test.d.ts.map +1 -0
- package/dist/server/tests/slack.test.js +213 -0
- package/dist/server/tests/state-cache.test.d.ts +2 -0
- package/dist/server/tests/state-cache.test.d.ts.map +1 -0
- package/dist/server/tests/state-cache.test.js +184 -0
- package/dist/server/tests/subscriptions.test.d.ts +2 -0
- package/dist/server/tests/subscriptions.test.d.ts.map +1 -0
- package/dist/server/tests/subscriptions.test.js +292 -0
- package/dist/server/tests/system-service.test.d.ts +2 -0
- package/dist/server/tests/system-service.test.d.ts.map +1 -0
- package/dist/server/tests/system-service.test.js +20 -0
- package/dist/server/tests/url-routes.test.d.ts +2 -0
- package/dist/server/tests/url-routes.test.d.ts.map +1 -0
- package/dist/server/tests/url-routes.test.js +217 -0
- package/dist/server/tests/web-api.test.d.ts +2 -0
- package/dist/server/tests/web-api.test.d.ts.map +1 -0
- package/dist/server/tests/web-api.test.js +1759 -0
- package/dist/server/web/app.d.ts.map +1 -1
- package/dist/server/web/app.js +2 -0
- package/dist/server/web/client-error-routes.d.ts +3 -0
- package/dist/server/web/client-error-routes.d.ts.map +1 -0
- package/dist/server/web/client-error-routes.js +101 -0
- package/dist/server/web/system-routes.d.ts.map +1 -1
- package/dist/server/web/system-routes.js +21 -131
- package/dist/shared/agent-config.d.ts +8 -18
- package/dist/shared/agent-config.d.ts.map +1 -1
- package/dist/shared/agent-config.js +3 -4
- package/dist/shared/inbox.d.ts +27 -0
- package/dist/shared/inbox.d.ts.map +1 -1
- package/dist/shared/inbox.js +1 -0
- package/dist/shared/server-info.d.ts +6 -5
- package/dist/shared/server-info.d.ts.map +1 -1
- package/dist/shared/server-info.js +3 -1
- package/dist/shared/server-settings.d.ts +6 -0
- package/dist/shared/server-settings.d.ts.map +1 -1
- package/dist/shared/server-settings.js +1 -0
- package/dist/shared/skills.d.ts +24 -3
- package/dist/shared/skills.d.ts.map +1 -1
- package/dist/web/assets/activity-DHjip8tY.js +7 -0
- package/dist/web/assets/clipboard-D2NSC52v.js +1 -0
- package/dist/web/assets/{external-link-Dt0qTpmV.js → external-link-Bhr1WdRS.js} +1 -1
- package/dist/web/assets/{file-text-Dd9ys6py.js → file-text-CYMA_SMA.js} +1 -1
- package/dist/web/assets/{index-CzAQnbuO.js → index-CVpew409.js} +5 -5
- package/dist/web/assets/index-mh0uJTkY.css +2 -0
- package/dist/web/assets/kb-DDVpYkT9.js +35 -0
- package/dist/web/assets/profile-CBy6GNzp.js +1 -0
- package/dist/web/assets/reminders-Bbwb9QNT.js +1 -0
- package/dist/web/favicon.svg +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/templates/runtime-standing-prompt.md +16 -2
- package/templates/skills/find-skills/SKILL.md +133 -0
- package/dist/server/runtime/activity-payload.d.ts.map +0 -1
- package/dist/server/runtime/activity-text.d.ts +0 -4
- package/dist/server/runtime/activity-text.d.ts.map +0 -1
- package/dist/server/runtime/activity-text.js +0 -16
- package/dist/server/runtime/managed-runtime.d.ts.map +0 -1
- package/dist/server/runtime/provider-contract.d.ts.map +0 -1
- package/dist/server/runtime/runtime-release.d.ts.map +0 -1
- package/dist/server/runtime/runtime-upgrade.d.ts.map +0 -1
- package/dist/server/slack/shortcut.service.d.ts.map +0 -1
- package/dist/server/slack/shortcut.service.js +0 -493
- package/dist/web/assets/activity-BP0C81IV.js +0 -7
- package/dist/web/assets/globe-DmDZSgDP.js +0 -1
- package/dist/web/assets/index-DKecrvN0.css +0 -2
- package/dist/web/assets/kb-3wFeP2Jt.js +0 -32
- package/dist/web/assets/profile-D7wPZXW_.js +0 -1
- package/dist/web/assets/reminders-DOhroVwo.js +0 -1
- /package/dist/server/{runtime/provider-contract.js → providers/contract.js} +0 -0
- /package/dist/server/{runtime → runtime-management}/managed-runtime.d.ts +0 -0
- /package/dist/server/{runtime → runtime-management}/managed-runtime.js +0 -0
- /package/dist/server/{runtime → runtime-management}/runtime-release.d.ts +0 -0
- /package/dist/server/{runtime → runtime-management}/runtime-release.js +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdir, mkdtemp, readFile, readlink, rm, symlink, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { ensureDefaultSkills } from '../agents/default-skills.js';
|
|
7
|
+
test('ensureDefaultSkills installs find-skills into common skills and links Claude Code', async () => {
|
|
8
|
+
const homeDir = await mkdtemp(join(tmpdir(), 'anima-default-skills-'));
|
|
9
|
+
try {
|
|
10
|
+
const result = await ensureDefaultSkills({ homeDir });
|
|
11
|
+
assert.deepEqual(result, {
|
|
12
|
+
claudeLinkInstalled: true,
|
|
13
|
+
commonSkillInstalled: true,
|
|
14
|
+
});
|
|
15
|
+
const skillPath = join(homeDir, '.agents', 'skills', 'find-skills', 'SKILL.md');
|
|
16
|
+
assert.match(await readFile(skillPath, 'utf8'), /name: find-skills/);
|
|
17
|
+
assert.equal(await readlink(join(homeDir, '.claude', 'skills', 'find-skills')), '../../.agents/skills/find-skills');
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await rm(homeDir, { force: true, recursive: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
test('ensureDefaultSkills preserves existing skill content and provider paths', async () => {
|
|
24
|
+
const homeDir = await mkdtemp(join(tmpdir(), 'anima-default-skills-'));
|
|
25
|
+
try {
|
|
26
|
+
const commonSkillDir = join(homeDir, '.agents', 'skills', 'find-skills');
|
|
27
|
+
const claudeSkillDir = join(homeDir, '.claude', 'skills', 'find-skills');
|
|
28
|
+
await mkdir(commonSkillDir, { recursive: true });
|
|
29
|
+
await mkdir(claudeSkillDir, { recursive: true });
|
|
30
|
+
await writeFile(join(commonSkillDir, 'SKILL.md'), 'custom common skill\n', 'utf8');
|
|
31
|
+
await writeFile(join(claudeSkillDir, 'SKILL.md'), 'custom claude skill\n', 'utf8');
|
|
32
|
+
const result = await ensureDefaultSkills({ homeDir });
|
|
33
|
+
assert.deepEqual(result, {
|
|
34
|
+
claudeLinkInstalled: false,
|
|
35
|
+
commonSkillInstalled: false,
|
|
36
|
+
});
|
|
37
|
+
assert.equal(await readFile(join(commonSkillDir, 'SKILL.md'), 'utf8'), 'custom common skill\n');
|
|
38
|
+
assert.equal(await readFile(join(claudeSkillDir, 'SKILL.md'), 'utf8'), 'custom claude skill\n');
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await rm(homeDir, { force: true, recursive: true });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
test('ensureDefaultSkills repairs a broken Claude Code find-skills symlink', async () => {
|
|
45
|
+
const homeDir = await mkdtemp(join(tmpdir(), 'anima-default-skills-'));
|
|
46
|
+
try {
|
|
47
|
+
const claudeSkillsDir = join(homeDir, '.claude', 'skills');
|
|
48
|
+
await mkdir(claudeSkillsDir, { recursive: true });
|
|
49
|
+
await symlink('../missing/find-skills', join(claudeSkillsDir, 'find-skills'), 'dir');
|
|
50
|
+
const result = await ensureDefaultSkills({ homeDir });
|
|
51
|
+
assert.equal(result.claudeLinkInstalled, true);
|
|
52
|
+
assert.equal(await readlink(join(claudeSkillsDir, 'find-skills')), '../../.agents/skills/find-skills');
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
await rm(homeDir, { force: true, recursive: true });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type InboxItem } from '../../inbox/wake-queue.service.js';
|
|
2
|
+
import type { RuntimeWorkerConfig, RuntimeItemContext } from '../../runtime/types.js';
|
|
3
|
+
import type { ReminderInboxItem } from '../../../shared/inbox.js';
|
|
4
|
+
export declare function ingestEvent(event: InboxItem, config: RuntimeWorkerConfig): Promise<RuntimeItemContext>;
|
|
5
|
+
export declare function makeReminderInboxItem(opts: {
|
|
6
|
+
eventId?: string;
|
|
7
|
+
reminderId: string;
|
|
8
|
+
timestamp?: string;
|
|
9
|
+
}): ReminderInboxItem;
|
|
10
|
+
//# sourceMappingURL=inbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../../../../server/tests/helpers/inbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAErF,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,wBAAsB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAG5G;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,iBAAiB,CAcpB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { WakeQueueService } from '../../inbox/wake-queue.service.js';
|
|
2
|
+
import { runtimeContextForItemId } from '../../runtime/context.js';
|
|
3
|
+
export async function ingestEvent(event, config) {
|
|
4
|
+
const result = await new WakeQueueService(config.agentId).enqueue(event);
|
|
5
|
+
return runtimeContextForItemId(result.item.id, config);
|
|
6
|
+
}
|
|
7
|
+
export function makeReminderInboxItem(opts) {
|
|
8
|
+
const receivedAt = opts.timestamp ?? new Date().toISOString();
|
|
9
|
+
return {
|
|
10
|
+
id: opts.eventId ?? `evt_reminder_${opts.reminderId}`,
|
|
11
|
+
kind: 'reminder',
|
|
12
|
+
receivedAt,
|
|
13
|
+
handling: {
|
|
14
|
+
createdAt: receivedAt,
|
|
15
|
+
queuedAt: receivedAt,
|
|
16
|
+
status: 'queued',
|
|
17
|
+
updatedAt: receivedAt,
|
|
18
|
+
},
|
|
19
|
+
reminderId: opts.reminderId,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SlackEvent } from '../../inbox/slack-events.js';
|
|
2
|
+
export declare function makeSlackEvent(input: {
|
|
3
|
+
channelId: string;
|
|
4
|
+
teamId: string;
|
|
5
|
+
text: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
eventId?: string;
|
|
8
|
+
ts?: string;
|
|
9
|
+
timestamp?: string;
|
|
10
|
+
} & Partial<SlackEvent>): SlackEvent;
|
|
11
|
+
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../../server/tests/helpers/slack.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,wBAAgB,cAAc,CAC5B,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,UAAU,CAAC,GACtB,UAAU,CAmBZ"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { makeId } from '../../ids.js';
|
|
2
|
+
export function makeSlackEvent(input) {
|
|
3
|
+
const ts = input.ts ?? `${Date.now() / 1000}`;
|
|
4
|
+
const now = input.timestamp ?? new Date().toISOString();
|
|
5
|
+
return {
|
|
6
|
+
id: input.eventId ?? input.id ?? makeId('evt'),
|
|
7
|
+
kind: 'slack',
|
|
8
|
+
receivedAt: input.receivedAt ?? now,
|
|
9
|
+
handling: input.handling ?? { createdAt: now, queuedAt: now, status: 'queued', updatedAt: now },
|
|
10
|
+
teamId: input.teamId,
|
|
11
|
+
channelId: input.channelId,
|
|
12
|
+
messageTs: ts,
|
|
13
|
+
actor: { userId: input.userId, ...(input.actor ?? {}) },
|
|
14
|
+
text: input.text,
|
|
15
|
+
...Object.fromEntries(Object.entries(input).filter(([key]) => !['id', 'eventId', 'kind', 'receivedAt', 'handling', 'teamId', 'channelId', 'messageTs', 'actor', 'text', 'userId', 'ts', 'timestamp'].includes(key))),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type InboxItem } from '../../inbox/wake-queue.service.js';
|
|
2
|
+
import type { Reminder } from '../../../shared/reminder.js';
|
|
3
|
+
import { type Session } from '../../storage/schema/session.store.js';
|
|
4
|
+
import { type SubscriptionRecord } from '../../storage/schema/subscription.store.js';
|
|
5
|
+
import type { Activity } from '../../../shared/activity.js';
|
|
6
|
+
export interface TestState {
|
|
7
|
+
activities: Record<string, Activity>;
|
|
8
|
+
events: Record<string, InboxItem>;
|
|
9
|
+
subscriptions: Record<string, SubscriptionRecord>;
|
|
10
|
+
reminders: Record<string, Reminder>;
|
|
11
|
+
items: Record<string, InboxItem>;
|
|
12
|
+
sessions: Record<string, Session>;
|
|
13
|
+
}
|
|
14
|
+
type ActivityState = Pick<TestState, 'activities'>;
|
|
15
|
+
export declare function loadState(): Promise<TestState>;
|
|
16
|
+
export declare function loadAgentState(agentId: string): Promise<TestState>;
|
|
17
|
+
export declare function allActivities(state: ActivityState): Activity[];
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../server/tests/helpers/state.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAGrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AACxG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,KAAK,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAEnD,wBAAsB,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,CAKpD;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAIxE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,EAAE,CAE9D"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { WakeQueueService } from '../../inbox/wake-queue.service.js';
|
|
3
|
+
import { reminderServiceForAgent } from '../../reminders/reminder.service.js';
|
|
4
|
+
import { isMissingFile } from '../../storage/json-file.js';
|
|
5
|
+
import { activityServiceForAgent } from '../../activities/activity.service.js';
|
|
6
|
+
import { agentsDir } from '../../storage/schema/agent.store.js';
|
|
7
|
+
import { SessionStore } from '../../storage/schema/session.store.js';
|
|
8
|
+
import { SubscriptionStore } from '../../storage/schema/subscription.store.js';
|
|
9
|
+
export async function loadState() {
|
|
10
|
+
const state = emptyState();
|
|
11
|
+
const agentIds = await listAgentIds();
|
|
12
|
+
await Promise.all(agentIds.map((agentId) => hydrateAgentState(agentId, state)));
|
|
13
|
+
return state;
|
|
14
|
+
}
|
|
15
|
+
export async function loadAgentState(agentId) {
|
|
16
|
+
const state = emptyState();
|
|
17
|
+
await hydrateAgentState(agentId, state);
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
export function allActivities(state) {
|
|
21
|
+
return Object.values(state.activities).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
22
|
+
}
|
|
23
|
+
async function hydrateAgentState(agentId, state) {
|
|
24
|
+
const queue = new WakeQueueService(agentId);
|
|
25
|
+
const [session, events, subscriptions, reminders, items, activities] = await Promise.all([
|
|
26
|
+
readSession(agentId),
|
|
27
|
+
queue.list(),
|
|
28
|
+
new SubscriptionStore(agentId).list(),
|
|
29
|
+
reminderServiceForAgent(agentId).listAllReminders(),
|
|
30
|
+
queue.list(),
|
|
31
|
+
activityServiceForAgent(agentId).readAll(),
|
|
32
|
+
]);
|
|
33
|
+
if (session)
|
|
34
|
+
state.sessions[agentId] = session;
|
|
35
|
+
for (const event of events)
|
|
36
|
+
state.events[event.id] = event;
|
|
37
|
+
for (const subscription of subscriptions)
|
|
38
|
+
state.subscriptions[subscription.subscriptionId] = subscription;
|
|
39
|
+
for (const reminder of reminders)
|
|
40
|
+
state.reminders[reminder.reminderId] = reminder;
|
|
41
|
+
for (const item of items)
|
|
42
|
+
state.items[item.id] = item;
|
|
43
|
+
for (const activity of activities)
|
|
44
|
+
state.activities[activity.activityId] = activity;
|
|
45
|
+
}
|
|
46
|
+
async function readSession(agentId) {
|
|
47
|
+
return new SessionStore(agentId).read();
|
|
48
|
+
}
|
|
49
|
+
async function listAgentIds() {
|
|
50
|
+
try {
|
|
51
|
+
const entries = await readdir(agentsDir(), { withFileTypes: true });
|
|
52
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (isMissingFile(error))
|
|
56
|
+
return [];
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function emptyState() {
|
|
61
|
+
return {
|
|
62
|
+
activities: {},
|
|
63
|
+
events: {},
|
|
64
|
+
subscriptions: {},
|
|
65
|
+
reminders: {},
|
|
66
|
+
items: {},
|
|
67
|
+
sessions: {},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox-slack-events.test.d.ts","sourceRoot":"","sources":["../../../server/tests/inbox-slack-events.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { isRoutableSlackMessage, isUserAuthoredSlackMessage, normalizeSlackMessage } from '../inbox/slack-events.js';
|
|
4
|
+
import { slackSurfaceForEvent } from '../inbox/slack-events.js';
|
|
5
|
+
test('normalizes Slack DM messages into private primary-session events', () => {
|
|
6
|
+
const event = normalizeSlackMessage({
|
|
7
|
+
envelope: { event_id: 'Ev123', team_id: 'T123' },
|
|
8
|
+
event: {
|
|
9
|
+
channel: 'D123',
|
|
10
|
+
channel_type: 'im',
|
|
11
|
+
text: 'Can you remember this privately?',
|
|
12
|
+
ts: '1770000010.000001',
|
|
13
|
+
type: 'message',
|
|
14
|
+
user: 'U123',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const surface = slackSurfaceForEvent(event);
|
|
18
|
+
assert.equal(event.id, 'slack:T123:D123:1770000010.000001');
|
|
19
|
+
assert.equal(surface.id, 'slack:T123:D123');
|
|
20
|
+
assert.equal(surface.teamId, 'T123');
|
|
21
|
+
assert.equal(surface.channelId, 'D123');
|
|
22
|
+
assert.equal(surface.kind, 'dm');
|
|
23
|
+
assert.equal(surface.visibility, 'private');
|
|
24
|
+
assert.equal(event.messageTs, '1770000010.000001');
|
|
25
|
+
});
|
|
26
|
+
test('normalizes Slack thread messages with reply routing metadata', () => {
|
|
27
|
+
const event = normalizeSlackMessage({
|
|
28
|
+
envelope: { event_id: 'Ev456', team_id: 'T123' },
|
|
29
|
+
channelName: 'product',
|
|
30
|
+
event: {
|
|
31
|
+
channel: 'C123',
|
|
32
|
+
channel_type: 'channel',
|
|
33
|
+
text: 'Can you summarize here?',
|
|
34
|
+
thread_ts: '1770000000.000001',
|
|
35
|
+
ts: '1770000020.000001',
|
|
36
|
+
type: 'message',
|
|
37
|
+
user: 'U123',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const surface = slackSurfaceForEvent(event);
|
|
41
|
+
assert.equal(surface.kind, 'thread');
|
|
42
|
+
assert.equal(surface.channelName, 'product');
|
|
43
|
+
assert.equal(surface.threadTs, '1770000000.000001');
|
|
44
|
+
assert.equal(surface.visibility, 'public');
|
|
45
|
+
});
|
|
46
|
+
test('normalizes Slack app mention events as addressed channel messages', () => {
|
|
47
|
+
const event = normalizeSlackMessage({
|
|
48
|
+
envelope: { event_id: 'Ev789', team_id: 'T123' },
|
|
49
|
+
event: {
|
|
50
|
+
channel: 'C123',
|
|
51
|
+
text: '<@U999> can you help here?',
|
|
52
|
+
ts: '1770000030.000001',
|
|
53
|
+
type: 'app_mention',
|
|
54
|
+
user: 'U123',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const surface = slackSurfaceForEvent(event);
|
|
58
|
+
assert.equal(event.id, 'slack:T123:C123:1770000030.000001');
|
|
59
|
+
assert.equal(surface.kind, 'channel');
|
|
60
|
+
assert.equal(surface.visibility, 'public');
|
|
61
|
+
assert.equal(surface.id, 'slack:T123:C123');
|
|
62
|
+
});
|
|
63
|
+
test('normalizes resolved Slack mention text', () => {
|
|
64
|
+
const event = normalizeSlackMessage({
|
|
65
|
+
envelope: { event_id: 'EvMentionText', team_id: 'T123' },
|
|
66
|
+
event: {
|
|
67
|
+
channel: 'C123',
|
|
68
|
+
text: '<@U999> please ask <@U123> in <#C456|product>',
|
|
69
|
+
ts: '1770000031.000001',
|
|
70
|
+
type: 'app_mention',
|
|
71
|
+
user: 'U123',
|
|
72
|
+
},
|
|
73
|
+
text: '@anima please ask @alice in #product',
|
|
74
|
+
});
|
|
75
|
+
assert.equal(event.text, '@anima please ask @alice in #product');
|
|
76
|
+
});
|
|
77
|
+
test('uses Slack message ts as the stable event id across message and app_mention deliveries', () => {
|
|
78
|
+
const message = normalizeSlackMessage({
|
|
79
|
+
envelope: { event_id: 'EvMessage', team_id: 'T123' },
|
|
80
|
+
event: {
|
|
81
|
+
channel: 'C123',
|
|
82
|
+
channel_type: 'channel',
|
|
83
|
+
text: '<@U999> can you help here?',
|
|
84
|
+
ts: '1770000030.000001',
|
|
85
|
+
type: 'message',
|
|
86
|
+
user: 'U123',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const mention = normalizeSlackMessage({
|
|
90
|
+
envelope: { event_id: 'EvMention', team_id: 'T123' },
|
|
91
|
+
event: {
|
|
92
|
+
channel: 'C123',
|
|
93
|
+
text: '<@U999> can you help here?',
|
|
94
|
+
ts: '1770000030.000001',
|
|
95
|
+
type: 'app_mention',
|
|
96
|
+
user: 'U123',
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
assert.equal(message.id, mention.id);
|
|
100
|
+
assert.equal(message.id, 'slack:T123:C123:1770000030.000001');
|
|
101
|
+
});
|
|
102
|
+
test('normalizes optional Slack user profile metadata into actor context', () => {
|
|
103
|
+
const event = normalizeSlackMessage({
|
|
104
|
+
envelope: { event_id: 'EvProfile', team_id: 'T123' },
|
|
105
|
+
event: {
|
|
106
|
+
channel: 'D123',
|
|
107
|
+
channel_type: 'im',
|
|
108
|
+
text: 'Do you know my name?',
|
|
109
|
+
ts: '1770000040.000001',
|
|
110
|
+
type: 'message',
|
|
111
|
+
user: 'U123',
|
|
112
|
+
},
|
|
113
|
+
userProfile: {
|
|
114
|
+
displayName: 'Alice',
|
|
115
|
+
handle: 'alice',
|
|
116
|
+
realName: 'Alice Lee',
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
assert.equal(event.actor?.userId, 'U123');
|
|
120
|
+
assert.equal(event.actor?.displayName, 'Alice');
|
|
121
|
+
assert.equal(event.actor?.handle, 'alice');
|
|
122
|
+
assert.equal(event.actor?.realName, 'Alice Lee');
|
|
123
|
+
});
|
|
124
|
+
test('routes non-self bot-authored channel messages for subscription-window checks', () => {
|
|
125
|
+
assert.equal(isUserAuthoredSlackMessage({
|
|
126
|
+
bot_id: 'B123',
|
|
127
|
+
channel: 'C123',
|
|
128
|
+
channel_type: 'channel',
|
|
129
|
+
text: 'bot text',
|
|
130
|
+
ts: '1770000020.000001',
|
|
131
|
+
type: 'message',
|
|
132
|
+
user: 'U123',
|
|
133
|
+
}), false);
|
|
134
|
+
assert.equal(isRoutableSlackMessage({
|
|
135
|
+
bot_id: 'B123',
|
|
136
|
+
channel: 'C123',
|
|
137
|
+
channel_type: 'channel',
|
|
138
|
+
text: 'bot text',
|
|
139
|
+
ts: '1770000020.000001',
|
|
140
|
+
type: 'message',
|
|
141
|
+
user: 'U123',
|
|
142
|
+
}), true);
|
|
143
|
+
assert.equal(isRoutableSlackMessage({
|
|
144
|
+
bot_id: 'B999',
|
|
145
|
+
channel: 'C123',
|
|
146
|
+
channel_type: 'channel',
|
|
147
|
+
text: 'self echo',
|
|
148
|
+
ts: '1770000021.000001',
|
|
149
|
+
type: 'message',
|
|
150
|
+
user: 'U999',
|
|
151
|
+
}), true);
|
|
152
|
+
});
|
|
153
|
+
test('filters unsupported subtype messages before runtime ingestion', () => {
|
|
154
|
+
assert.equal(isUserAuthoredSlackMessage({
|
|
155
|
+
channel: 'C123',
|
|
156
|
+
channel_type: 'channel',
|
|
157
|
+
subtype: 'message_changed',
|
|
158
|
+
text: 'edited text',
|
|
159
|
+
ts: '1770000020.000001',
|
|
160
|
+
type: 'message',
|
|
161
|
+
user: 'U123',
|
|
162
|
+
}), false);
|
|
163
|
+
assert.equal(isRoutableSlackMessage({
|
|
164
|
+
channel: 'C123',
|
|
165
|
+
channel_type: 'channel',
|
|
166
|
+
subtype: 'message_changed',
|
|
167
|
+
text: 'edited text',
|
|
168
|
+
ts: '1770000020.000001',
|
|
169
|
+
type: 'message',
|
|
170
|
+
user: 'U123',
|
|
171
|
+
}), false);
|
|
172
|
+
});
|
|
173
|
+
test('routes bot-authored messages that explicitly mention this bot', () => {
|
|
174
|
+
assert.equal(isRoutableSlackMessage({
|
|
175
|
+
bot_id: 'B123',
|
|
176
|
+
channel: 'C123',
|
|
177
|
+
channel_type: 'channel',
|
|
178
|
+
text: '<@U999> can you review this?',
|
|
179
|
+
thread_ts: '1770000010.000001',
|
|
180
|
+
ts: '1770000020.000001',
|
|
181
|
+
type: 'message',
|
|
182
|
+
user: 'U123',
|
|
183
|
+
}), true);
|
|
184
|
+
assert.equal(isRoutableSlackMessage({
|
|
185
|
+
bot_id: 'B999',
|
|
186
|
+
channel: 'C123',
|
|
187
|
+
channel_type: 'channel',
|
|
188
|
+
text: '<@U999> self echo',
|
|
189
|
+
thread_ts: '1770000010.000001',
|
|
190
|
+
ts: '1770000021.000001',
|
|
191
|
+
type: 'message',
|
|
192
|
+
user: 'U999',
|
|
193
|
+
}), true);
|
|
194
|
+
});
|
|
195
|
+
test('routes file-only messages (empty text, files present) and normalizes file metadata', () => {
|
|
196
|
+
const rawEvent = {
|
|
197
|
+
channel: 'D123',
|
|
198
|
+
channel_type: 'im',
|
|
199
|
+
files: [
|
|
200
|
+
{
|
|
201
|
+
id: 'F-screenshot',
|
|
202
|
+
mimetype: 'image/png',
|
|
203
|
+
name: 'screenshot.png',
|
|
204
|
+
size: 2048,
|
|
205
|
+
url_private: 'https://files.slack.com/private',
|
|
206
|
+
url_private_download: 'https://files.slack.com/download',
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
subtype: 'file_share',
|
|
210
|
+
text: '',
|
|
211
|
+
ts: '1770000050.000001',
|
|
212
|
+
type: 'message',
|
|
213
|
+
user: 'U123',
|
|
214
|
+
};
|
|
215
|
+
assert.equal(isUserAuthoredSlackMessage(rawEvent), true);
|
|
216
|
+
assert.equal(isRoutableSlackMessage(rawEvent), true);
|
|
217
|
+
const event = normalizeSlackMessage({
|
|
218
|
+
envelope: { event_id: 'EvFile', team_id: 'T123' },
|
|
219
|
+
event: rawEvent,
|
|
220
|
+
});
|
|
221
|
+
assert.equal(slackSurfaceForEvent(event).kind, 'dm');
|
|
222
|
+
assert.equal(event.files?.length, 1);
|
|
223
|
+
assert.equal(event.files?.[0]?.id, 'F-screenshot');
|
|
224
|
+
assert.equal(event.files?.[0]?.mimetype, 'image/png');
|
|
225
|
+
assert.equal(event.files?.[0]?.sizeBytes, 2048);
|
|
226
|
+
});
|
|
227
|
+
test('normalizeSlackMessage prefers explicitly enriched files over raw event files', () => {
|
|
228
|
+
const event = normalizeSlackMessage({
|
|
229
|
+
envelope: { event_id: 'EvFileEnriched', team_id: 'T123' },
|
|
230
|
+
event: {
|
|
231
|
+
channel: 'C123',
|
|
232
|
+
channel_type: 'channel',
|
|
233
|
+
files: [{ id: 'F-raw', mimetype: 'image/png', name: 'raw.png', size: 1 }],
|
|
234
|
+
text: 'see attached',
|
|
235
|
+
ts: '1770000060.000001',
|
|
236
|
+
type: 'message',
|
|
237
|
+
user: 'U123',
|
|
238
|
+
},
|
|
239
|
+
files: [{
|
|
240
|
+
id: 'F-enriched',
|
|
241
|
+
mimetype: 'image/png',
|
|
242
|
+
name: 'enriched.png',
|
|
243
|
+
sizeBytes: 4096,
|
|
244
|
+
}],
|
|
245
|
+
});
|
|
246
|
+
assert.equal(event.files?.length, 1);
|
|
247
|
+
assert.equal(event.files?.[0]?.id, 'F-enriched');
|
|
248
|
+
assert.equal(Object.hasOwn(event.files?.[0] ?? {}, 'localPath'), false);
|
|
249
|
+
});
|
|
250
|
+
test('routes non-self bot-authored thread messages for subscription-window checks', () => {
|
|
251
|
+
assert.equal(isRoutableSlackMessage({
|
|
252
|
+
bot_id: 'B123',
|
|
253
|
+
channel: 'C123',
|
|
254
|
+
channel_type: 'channel',
|
|
255
|
+
text: 'continuing in the active thread',
|
|
256
|
+
thread_ts: '1770000010.000001',
|
|
257
|
+
ts: '1770000020.000001',
|
|
258
|
+
type: 'message',
|
|
259
|
+
user: 'U123',
|
|
260
|
+
}), true);
|
|
261
|
+
assert.equal(isRoutableSlackMessage({
|
|
262
|
+
bot_id: 'B999',
|
|
263
|
+
channel: 'C123',
|
|
264
|
+
channel_type: 'channel',
|
|
265
|
+
text: 'self echo in thread',
|
|
266
|
+
thread_ts: '1770000010.000001',
|
|
267
|
+
ts: '1770000021.000001',
|
|
268
|
+
type: 'message',
|
|
269
|
+
user: 'U999',
|
|
270
|
+
}), true);
|
|
271
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.test.d.ts","sourceRoot":"","sources":["../../../server/tests/inbox.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { WakeQueueService } from '../inbox/wake-queue.service.js';
|
|
7
|
+
import { WakeQueueStore } from '../storage/schema/wake-queue.store.js';
|
|
8
|
+
import { makeSlackEvent } from './helpers/slack.js';
|
|
9
|
+
import { withAnimaHome } from './anima-home.js';
|
|
10
|
+
import { loadState } from './helpers/state.js';
|
|
11
|
+
function memoryWakeQueueStore(initial = {}) {
|
|
12
|
+
let state = { ...initial };
|
|
13
|
+
return {
|
|
14
|
+
async read() {
|
|
15
|
+
return state;
|
|
16
|
+
},
|
|
17
|
+
async write(value) {
|
|
18
|
+
state = value;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
test('wake queue store ignores duplicate Slack message deliveries', async () => {
|
|
23
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'anima-slack-queued-dedupe-test-'));
|
|
24
|
+
try {
|
|
25
|
+
await withAnimaHome(stateDir, async () => {
|
|
26
|
+
const queue = new WakeQueueService('scout');
|
|
27
|
+
const first = await queue.enqueue(makeSlackEvent({
|
|
28
|
+
channelId: 'C-product',
|
|
29
|
+
eventId: 'slack:T-demo:C-product:1770000010.000001',
|
|
30
|
+
teamId: 'T-demo',
|
|
31
|
+
text: '<@U999> remember bagels',
|
|
32
|
+
ts: '1770000010.000001',
|
|
33
|
+
userId: 'U1',
|
|
34
|
+
}));
|
|
35
|
+
const second = await queue.enqueue(makeSlackEvent({
|
|
36
|
+
channelId: 'C-product',
|
|
37
|
+
eventId: 'slack:T-demo:C-product:1770000010.000001',
|
|
38
|
+
teamId: 'T-demo',
|
|
39
|
+
text: '<@U999> remember bagels',
|
|
40
|
+
ts: '1770000010.000001',
|
|
41
|
+
userId: 'U1',
|
|
42
|
+
}));
|
|
43
|
+
assert.equal(first.queued, true);
|
|
44
|
+
assert.equal(second.duplicate, true);
|
|
45
|
+
assert.equal(second.item.id, first.item.id);
|
|
46
|
+
assert.deepEqual((await queue.listRunnable()).map((item) => item.id), [first.item.id]);
|
|
47
|
+
const state = await loadState();
|
|
48
|
+
assert.equal(Object.keys(state.events).length, 1);
|
|
49
|
+
assert.equal(Object.keys(state.items).length, 1);
|
|
50
|
+
assert.equal(state.items[first.item.id]?.id, 'slack:T-demo:C-product:1770000010.000001');
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
await rm(stateDir, { force: true, recursive: true });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
test('wake queue store can use an injected file persistence', async () => {
|
|
58
|
+
const queue = new WakeQueueService('scout', new WakeQueueStore('scout', memoryWakeQueueStore()), { recordInboxItem: async () => undefined });
|
|
59
|
+
const event = makeSlackEvent({
|
|
60
|
+
channelId: 'D-user',
|
|
61
|
+
eventId: 'evt-injected-store',
|
|
62
|
+
teamId: 'T-demo',
|
|
63
|
+
text: 'queued without filesystem store',
|
|
64
|
+
ts: '1770000010.000001',
|
|
65
|
+
userId: 'U1',
|
|
66
|
+
});
|
|
67
|
+
const decision = await queue.enqueue(event);
|
|
68
|
+
assert.equal(decision.queued, true);
|
|
69
|
+
const claimed = await queue.claimNext('worker-1');
|
|
70
|
+
assert.equal(claimed?.id, event.id);
|
|
71
|
+
assert.equal(claimed?.handling.status, 'running');
|
|
72
|
+
await queue.complete(event.id);
|
|
73
|
+
const completed = await queue.find(event.id);
|
|
74
|
+
assert.equal(completed?.handling.status, 'completed');
|
|
75
|
+
assert.deepEqual((await queue.list()).map((item) => item.id), [event.id]);
|
|
76
|
+
});
|
|
77
|
+
test('wake queue enqueue does not fail when message ledger write fails', async () => {
|
|
78
|
+
const warnings = [];
|
|
79
|
+
const queue = new WakeQueueService('scout', new WakeQueueStore('scout', memoryWakeQueueStore()), { recordInboxItem: async () => { throw new Error('ledger down'); } }, { warn: (message) => warnings.push(message) });
|
|
80
|
+
const event = makeSlackEvent({
|
|
81
|
+
channelId: 'D-user',
|
|
82
|
+
eventId: 'evt-ledger-failure',
|
|
83
|
+
teamId: 'T-demo',
|
|
84
|
+
text: 'queued even when ledger is down',
|
|
85
|
+
ts: '1770000011.000001',
|
|
86
|
+
userId: 'U1',
|
|
87
|
+
});
|
|
88
|
+
const decision = await queue.enqueue(event);
|
|
89
|
+
assert.equal(decision.queued, true);
|
|
90
|
+
assert.deepEqual((await queue.list()).map((item) => item.id), [event.id]);
|
|
91
|
+
assert.match(warnings[0] ?? '', /ledger down/);
|
|
92
|
+
});
|
|
93
|
+
test('wake queue retention waits for legacy message backfill before pruning settled items', async () => {
|
|
94
|
+
let legacyBackfilled = false;
|
|
95
|
+
const oldSettled = makeSlackEvent({
|
|
96
|
+
channelId: 'D-user',
|
|
97
|
+
eventId: 'evt-old-settled',
|
|
98
|
+
handling: {
|
|
99
|
+
completedAt: '2000-01-01T00:00:00.000Z',
|
|
100
|
+
createdAt: '2000-01-01T00:00:00.000Z',
|
|
101
|
+
queuedAt: '2000-01-01T00:00:00.000Z',
|
|
102
|
+
status: 'completed',
|
|
103
|
+
updatedAt: '2000-01-01T00:00:00.000Z',
|
|
104
|
+
},
|
|
105
|
+
teamId: 'T-demo',
|
|
106
|
+
text: 'old completed message',
|
|
107
|
+
ts: '946684800.000001',
|
|
108
|
+
userId: 'U1',
|
|
109
|
+
});
|
|
110
|
+
const oldQueued = makeSlackEvent({
|
|
111
|
+
channelId: 'D-user',
|
|
112
|
+
eventId: 'evt-old-queued',
|
|
113
|
+
handling: {
|
|
114
|
+
createdAt: '2000-01-01T00:00:00.000Z',
|
|
115
|
+
queuedAt: '2000-01-01T00:00:00.000Z',
|
|
116
|
+
status: 'queued',
|
|
117
|
+
updatedAt: '2000-01-01T00:00:00.000Z',
|
|
118
|
+
},
|
|
119
|
+
teamId: 'T-demo',
|
|
120
|
+
text: 'old queued message',
|
|
121
|
+
ts: '946684801.000001',
|
|
122
|
+
userId: 'U1',
|
|
123
|
+
});
|
|
124
|
+
const queue = new WakeQueueService('scout', new WakeQueueStore('scout', memoryWakeQueueStore({
|
|
125
|
+
[oldSettled.id]: oldSettled,
|
|
126
|
+
[oldQueued.id]: oldQueued,
|
|
127
|
+
})), {
|
|
128
|
+
legacyBackfilled: async () => legacyBackfilled,
|
|
129
|
+
recordInboxItem: async () => undefined,
|
|
130
|
+
});
|
|
131
|
+
const event = makeSlackEvent({
|
|
132
|
+
channelId: 'D-user',
|
|
133
|
+
eventId: 'evt-new',
|
|
134
|
+
teamId: 'T-demo',
|
|
135
|
+
text: 'new message',
|
|
136
|
+
ts: '1770000012.000001',
|
|
137
|
+
userId: 'U1',
|
|
138
|
+
});
|
|
139
|
+
await queue.enqueue(event);
|
|
140
|
+
assert.deepEqual((await queue.list()).map((item) => item.id), [oldSettled.id, oldQueued.id, event.id]);
|
|
141
|
+
legacyBackfilled = true;
|
|
142
|
+
await queue.complete(event.id);
|
|
143
|
+
assert.deepEqual((await queue.list()).map((item) => item.id), [oldQueued.id, event.id]);
|
|
144
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-ask.test.d.ts","sourceRoot":"","sources":["../../../server/tests/interactive-ask.test.ts"],"names":[],"mappings":""}
|