@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 @@
|
|
|
1
|
+
{"version":3,"file":"client-error-routes.test.d.ts","sourceRoot":"","sources":["../../../server/tests/client-error-routes.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { once } from 'node:events';
|
|
2
|
+
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { createWebServer } from '../web/app.js';
|
|
8
|
+
import { withAnimaHome } from './anima-home.js';
|
|
9
|
+
// Spin up the real web server under a scoped ANIMA_HOME, run `body` against it,
|
|
10
|
+
// then tear it down. Mirrors the pattern in web-api.test.ts.
|
|
11
|
+
async function withServer(body) {
|
|
12
|
+
const home = await mkdtemp(join(tmpdir(), 'anima-client-error-test-'));
|
|
13
|
+
try {
|
|
14
|
+
await withAnimaHome(home, async () => {
|
|
15
|
+
const server = await createWebServer();
|
|
16
|
+
try {
|
|
17
|
+
server.listen(0, '127.0.0.1');
|
|
18
|
+
await once(server, 'listening');
|
|
19
|
+
const address = server.address();
|
|
20
|
+
if (!address || typeof address === 'string') {
|
|
21
|
+
throw new Error('Expected web API to listen on a TCP address.');
|
|
22
|
+
}
|
|
23
|
+
await body(`http://127.0.0.1:${address.port}`, home);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
server.close();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await rm(home, { force: true, recursive: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function postClientError(base, body) {
|
|
35
|
+
return fetch(`${base}/api/client-errors`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function readLog(home) {
|
|
42
|
+
const logPath = join(home, 'logs', 'client-errors.jsonl');
|
|
43
|
+
let raw;
|
|
44
|
+
try {
|
|
45
|
+
raw = await readFile(logPath, 'utf8');
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
return raw
|
|
51
|
+
.trim()
|
|
52
|
+
.split('\n')
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.map((line) => JSON.parse(line));
|
|
55
|
+
}
|
|
56
|
+
test('client-errors: valid payload returns 204 and writes JSONL under the active ANIMA_HOME', async () => {
|
|
57
|
+
await withServer(async (base, home) => {
|
|
58
|
+
const res = await postClientError(base, JSON.stringify({
|
|
59
|
+
kind: 'error',
|
|
60
|
+
message: 'Cannot read properties of undefined',
|
|
61
|
+
stack: 'Error: x\n at foo (app.js:1:1)',
|
|
62
|
+
path: '/kb/team/skills.md',
|
|
63
|
+
userAgent: 'probe/1.0',
|
|
64
|
+
createdAt: '2026-06-01T00:00:00.000Z',
|
|
65
|
+
}));
|
|
66
|
+
assert.equal(res.status, 204);
|
|
67
|
+
assert.equal(await res.text(), '');
|
|
68
|
+
const records = await readLog(home);
|
|
69
|
+
assert.equal(records.length, 1);
|
|
70
|
+
const record = records[0];
|
|
71
|
+
assert.ok(record);
|
|
72
|
+
assert.equal(record.kind, 'error');
|
|
73
|
+
assert.equal(record.message, 'Cannot read properties of undefined');
|
|
74
|
+
assert.equal(record.path, '/kb/team/skills.md');
|
|
75
|
+
// The server stamps its own receipt time regardless of client input.
|
|
76
|
+
assert.equal(typeof record.receivedAt, 'string');
|
|
77
|
+
assert.ok(record.receivedAt.length > 0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
test('client-errors: invalid payload (bad kind / missing message) returns 400 and writes nothing', async () => {
|
|
81
|
+
await withServer(async (base, home) => {
|
|
82
|
+
const badKind = await postClientError(base, JSON.stringify({
|
|
83
|
+
kind: 'nope',
|
|
84
|
+
message: 'x',
|
|
85
|
+
path: '/',
|
|
86
|
+
userAgent: 'p',
|
|
87
|
+
createdAt: 'now',
|
|
88
|
+
}));
|
|
89
|
+
assert.equal(badKind.status, 400);
|
|
90
|
+
const missingMessage = await postClientError(base, JSON.stringify({ kind: 'error', path: '/', userAgent: 'p', createdAt: 'now' }));
|
|
91
|
+
assert.equal(missingMessage.status, 400);
|
|
92
|
+
assert.equal((await readLog(home)).length, 0);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
test('client-errors: oversized body is rejected with 413 before parsing', async () => {
|
|
96
|
+
await withServer(async (base, home) => {
|
|
97
|
+
const res = await postClientError(base, JSON.stringify({
|
|
98
|
+
kind: 'error',
|
|
99
|
+
message: 'x'.repeat(40_000),
|
|
100
|
+
path: '/',
|
|
101
|
+
userAgent: 'p',
|
|
102
|
+
createdAt: 'now',
|
|
103
|
+
}));
|
|
104
|
+
assert.equal(res.status, 413);
|
|
105
|
+
assert.equal((await readLog(home)).length, 0);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
test('client-errors: an over-long stack is truncated server-side', async () => {
|
|
109
|
+
await withServer(async (base, home) => {
|
|
110
|
+
const res = await postClientError(base, JSON.stringify({
|
|
111
|
+
kind: 'error',
|
|
112
|
+
message: 'long stack case',
|
|
113
|
+
stack: 'S'.repeat(20_000),
|
|
114
|
+
path: '/',
|
|
115
|
+
userAgent: 'p',
|
|
116
|
+
createdAt: '2026-06-01T00:00:00.000Z',
|
|
117
|
+
}));
|
|
118
|
+
assert.equal(res.status, 204);
|
|
119
|
+
const records = await readLog(home);
|
|
120
|
+
assert.equal(records.length, 1);
|
|
121
|
+
const record = records[0];
|
|
122
|
+
assert.ok(record);
|
|
123
|
+
assert.ok(record.stack);
|
|
124
|
+
// 8_000 cap + the "…[truncated]" marker; far below the original 20_000.
|
|
125
|
+
assert.ok(record.stack.length < 20_000);
|
|
126
|
+
assert.ok(record.stack.endsWith('[truncated]'));
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
test('client-errors: an absolute URL with userinfo/query/hash is reduced to its pathname only', async () => {
|
|
130
|
+
await withServer(async (base, home) => {
|
|
131
|
+
const res = await postClientError(base, JSON.stringify({
|
|
132
|
+
kind: 'error',
|
|
133
|
+
message: 'absolute url case',
|
|
134
|
+
path: 'https://token@example.com/kb/foo?token=SECRET#frag',
|
|
135
|
+
userAgent: 'p',
|
|
136
|
+
createdAt: '2026-06-01T00:00:00.000Z',
|
|
137
|
+
}));
|
|
138
|
+
assert.equal(res.status, 204);
|
|
139
|
+
const records = await readLog(home);
|
|
140
|
+
assert.equal(records.length, 1);
|
|
141
|
+
const record = records[0];
|
|
142
|
+
assert.ok(record);
|
|
143
|
+
// Origin, userinfo, query, and hash must all be dropped — only the path remains.
|
|
144
|
+
assert.equal(record.path, '/kb/foo');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
test('client-errors: a relative path keeps its pathname but drops query and hash', async () => {
|
|
148
|
+
await withServer(async (base, home) => {
|
|
149
|
+
const res = await postClientError(base, JSON.stringify({
|
|
150
|
+
kind: 'render',
|
|
151
|
+
message: 'relative path case',
|
|
152
|
+
path: '/agents/nora?token=SECRET123#frag',
|
|
153
|
+
userAgent: 'p',
|
|
154
|
+
createdAt: '2026-06-01T00:00:00.000Z',
|
|
155
|
+
}));
|
|
156
|
+
assert.equal(res.status, 204);
|
|
157
|
+
const records = await readLog(home);
|
|
158
|
+
assert.equal(records.length, 1);
|
|
159
|
+
const record = records[0];
|
|
160
|
+
assert.ok(record);
|
|
161
|
+
assert.equal(record.path, '/agents/nora');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../../server/tests/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir, tmpdir } from 'node:os';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { defaultAgentRegistryService } from '../agents/agent.service.js';
|
|
7
|
+
import { loadRuntimeAgents } from '../runtime/host.js';
|
|
8
|
+
import { SessionStore } from '../storage/schema/session.store.js';
|
|
9
|
+
import { defaultServerSettingsService } from '../settings/settings.service.js';
|
|
10
|
+
import { resolveAnimaHome } from '../anima-home.js';
|
|
11
|
+
import { installManagedRuntime, packageSpecifier, readManagedRuntimeStatus, resolveManagedAnimaHome, } from '../runtime-management/managed-runtime.js';
|
|
12
|
+
import { AgentCreateRequest, PROVIDER_IDLE_TIMEOUT_MS_DEFAULT } from '../../shared/agent-config.js';
|
|
13
|
+
import { providerCatalogEntry } from '../../shared/provider-catalog.js';
|
|
14
|
+
import { withAnimaHome } from './anima-home.js';
|
|
15
|
+
const agentService = (agentId) => defaultAgentRegistryService.serviceFor(agentId);
|
|
16
|
+
test('anima home resolution prefers ANIMA_HOME env, then local .anima, then ~/.anima', async () => {
|
|
17
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-home-resolve-test-'));
|
|
18
|
+
const explicitDir = join(rootDir, 'explicit');
|
|
19
|
+
const previousCwd = process.cwd();
|
|
20
|
+
const previousHome = process.env.ANIMA_HOME;
|
|
21
|
+
try {
|
|
22
|
+
delete process.env.ANIMA_HOME;
|
|
23
|
+
process.chdir(rootDir);
|
|
24
|
+
// No env var, no local .anima → default to ~/.anima
|
|
25
|
+
assert.match(resolveAnimaHome(), /\.anima$/);
|
|
26
|
+
// Local .anima directory present → that wins; config.json itself is optional.
|
|
27
|
+
await mkdir(join(rootDir, '.anima'));
|
|
28
|
+
assert.equal(resolveAnimaHome(), resolve('.anima'));
|
|
29
|
+
// ANIMA_HOME wins over local .anima
|
|
30
|
+
await writeConfig(explicitDir, [{ id: 'explicit' }]);
|
|
31
|
+
await withAnimaHome(explicitDir, async () => {
|
|
32
|
+
assert.equal(resolveAnimaHome(), resolve(explicitDir));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
process.chdir(previousCwd);
|
|
37
|
+
if (previousHome === undefined)
|
|
38
|
+
delete process.env.ANIMA_HOME;
|
|
39
|
+
else
|
|
40
|
+
process.env.ANIMA_HOME = previousHome;
|
|
41
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
test('managed runtime home defaults to ~/.anima instead of the cwd .anima', async () => {
|
|
45
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-managed-home-test-'));
|
|
46
|
+
const previousCwd = process.cwd();
|
|
47
|
+
const previousHome = process.env.ANIMA_HOME;
|
|
48
|
+
try {
|
|
49
|
+
delete process.env.ANIMA_HOME;
|
|
50
|
+
process.chdir(rootDir);
|
|
51
|
+
await mkdir(join(rootDir, '.anima'));
|
|
52
|
+
assert.equal(resolveAnimaHome(), resolve('.anima'));
|
|
53
|
+
assert.equal(resolveManagedAnimaHome(), join(homedir(), '.anima'));
|
|
54
|
+
process.env.ANIMA_HOME = join(rootDir, 'explicit-home');
|
|
55
|
+
assert.equal(resolveManagedAnimaHome(), resolve(rootDir, 'explicit-home'));
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
process.chdir(previousCwd);
|
|
59
|
+
if (previousHome === undefined)
|
|
60
|
+
delete process.env.ANIMA_HOME;
|
|
61
|
+
else
|
|
62
|
+
process.env.ANIMA_HOME = previousHome;
|
|
63
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
test('managed runtime install writes package metadata', async () => {
|
|
67
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-managed-runtime-test-'));
|
|
68
|
+
try {
|
|
69
|
+
const runtimeDir = join(rootDir, 'runtime', 'current');
|
|
70
|
+
assert.equal(packageSpecifier({ packageName: '@meetquinn/animactl', version: '0.1.0' }), '@meetquinn/animactl@0.1.0');
|
|
71
|
+
assert.equal(packageSpecifier({ packageName: '@meetquinn/animactl', channel: 'canary' }), '@meetquinn/animactl@canary');
|
|
72
|
+
assert.throws(() => packageSpecifier({ packageName: '@meetquinn/animactl', channel: 'canary', version: '0.1.0' }), /Choose either --version or --channel/);
|
|
73
|
+
const result = await installManagedRuntime({
|
|
74
|
+
packageName: '@meetquinn/animactl',
|
|
75
|
+
runtimeDir,
|
|
76
|
+
version: '0.1.0',
|
|
77
|
+
runner: async (command, args, options) => {
|
|
78
|
+
assert.equal(command, 'npm');
|
|
79
|
+
assert.deepEqual(args, [
|
|
80
|
+
'install',
|
|
81
|
+
'--prefix',
|
|
82
|
+
runtimeDir,
|
|
83
|
+
'--omit=dev',
|
|
84
|
+
'--no-audit',
|
|
85
|
+
'--fund=false',
|
|
86
|
+
'@meetquinn/animactl@0.1.0',
|
|
87
|
+
]);
|
|
88
|
+
assert.equal(options.cwd, runtimeDir);
|
|
89
|
+
const packageDir = join(runtimeDir, 'node_modules', '@meetquinn', 'animactl');
|
|
90
|
+
await mkdir(packageDir, { recursive: true });
|
|
91
|
+
await writeFile(join(packageDir, 'package.json'), `${JSON.stringify({ name: '@meetquinn/animactl', version: '0.1.0' }, null, 2)}\n`, 'utf8');
|
|
92
|
+
await mkdir(join(packageDir, 'dist', 'server', 'cli'), { recursive: true });
|
|
93
|
+
await writeFile(join(packageDir, 'dist', 'server', 'cli', 'animactl.js'), '', 'utf8');
|
|
94
|
+
return { stdout: 'installed', stderr: '' };
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
assert.equal(result.metadata.packageName, '@meetquinn/animactl');
|
|
98
|
+
assert.equal(result.metadata.version, '0.1.0');
|
|
99
|
+
assert.equal(result.metadata.specifier, '@meetquinn/animactl@0.1.0');
|
|
100
|
+
const status = await readManagedRuntimeStatus({ packageName: '@meetquinn/animactl', runtimeDir });
|
|
101
|
+
assert.equal(status.installed, true);
|
|
102
|
+
assert.equal(status.version, '0.1.0');
|
|
103
|
+
assert.equal(status.metadata?.specifier, '@meetquinn/animactl@0.1.0');
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
test('server config defaults to empty when config.json is missing', async () => {
|
|
110
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
111
|
+
const previousCwd = process.cwd();
|
|
112
|
+
try {
|
|
113
|
+
process.chdir(rootDir);
|
|
114
|
+
await withAnimaHome(rootDir, async () => {
|
|
115
|
+
assert.deepEqual(await defaultServerSettingsService.readConfig(), {});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
process.chdir(previousCwd);
|
|
120
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
test('config loader resolves homePath relative to the anima home', async () => {
|
|
124
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
125
|
+
try {
|
|
126
|
+
const configDir = join(rootDir, 'config-root');
|
|
127
|
+
await writeConfig(configDir, [
|
|
128
|
+
{
|
|
129
|
+
id: 'anima',
|
|
130
|
+
provider: {
|
|
131
|
+
kind: 'codex-cli',
|
|
132
|
+
model: 'gpt-5.2-codex',
|
|
133
|
+
reasoningEffort: 'high',
|
|
134
|
+
},
|
|
135
|
+
slack: {
|
|
136
|
+
appToken: 'xapp-config',
|
|
137
|
+
botToken: 'xoxb-config',
|
|
138
|
+
},
|
|
139
|
+
homePath: 'agents/anima',
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
await withAnimaHome(configDir, async () => {
|
|
143
|
+
const agent = await agentService('anima').getConfig();
|
|
144
|
+
assert.equal(agent.homePath, 'agents/anima');
|
|
145
|
+
assert.equal(agent.provider?.kind, 'codex-cli');
|
|
146
|
+
assert.equal(agent.provider.model, 'gpt-5.2-codex');
|
|
147
|
+
assert.equal(agent.provider.idleTimeoutMs, PROVIDER_IDLE_TIMEOUT_MS_DEFAULT);
|
|
148
|
+
assert.ok('reasoningEffort' in agent.provider);
|
|
149
|
+
assert.equal(agent.provider.reasoningEffort, 'high');
|
|
150
|
+
assert.equal(agent.slack?.botToken, 'xoxb-config');
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
test('config loader reads legacy runtime key as provider config', async () => {
|
|
158
|
+
const rootDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
159
|
+
try {
|
|
160
|
+
const configDir = join(rootDir, 'config-root');
|
|
161
|
+
await writeConfig(configDir, [
|
|
162
|
+
{
|
|
163
|
+
id: 'legacy',
|
|
164
|
+
runtime: {
|
|
165
|
+
env: { LEGACY_PROVIDER_SECRET: 'kept' },
|
|
166
|
+
kind: 'codex-cli',
|
|
167
|
+
model: 'gpt-5.2-codex',
|
|
168
|
+
reasoningEffort: 'high',
|
|
169
|
+
},
|
|
170
|
+
homePath: 'agents/legacy',
|
|
171
|
+
},
|
|
172
|
+
]);
|
|
173
|
+
await withAnimaHome(configDir, async () => {
|
|
174
|
+
const agent = await agentService('legacy').getConfig();
|
|
175
|
+
assert.equal(agent.provider.kind, 'codex-cli');
|
|
176
|
+
assert.equal(agent.provider.model, 'gpt-5.2-codex');
|
|
177
|
+
assert.equal(agent.provider.env?.['LEGACY_PROVIDER_SECRET'], 'kept');
|
|
178
|
+
assert.equal(agent.provider.idleTimeoutMs, PROVIDER_IDLE_TIMEOUT_MS_DEFAULT);
|
|
179
|
+
assert.ok('reasoningEffort' in agent.provider);
|
|
180
|
+
assert.equal(agent.provider.reasoningEffort, 'high');
|
|
181
|
+
assert.equal('runtime' in agent, false);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
await rm(rootDir, { force: true, recursive: true });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
test('kimi provider does not expose or retain reasoning effort', async () => {
|
|
189
|
+
assert.deepEqual(providerCatalogEntry('kimi-cli')?.reasoningEfforts, []);
|
|
190
|
+
assert.throws(() => AgentCreateRequest.parse({
|
|
191
|
+
name: 'Kimi',
|
|
192
|
+
homePath: 'agents/kimi',
|
|
193
|
+
role: 'general purpose',
|
|
194
|
+
provider: {
|
|
195
|
+
kind: 'kimi-cli',
|
|
196
|
+
model: 'kimi-code/kimi-for-coding',
|
|
197
|
+
reasoningEffort: 'high',
|
|
198
|
+
},
|
|
199
|
+
}), /unsupported reasoningEffort high/);
|
|
200
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
201
|
+
try {
|
|
202
|
+
await writeConfig(configDir, [
|
|
203
|
+
{
|
|
204
|
+
id: 'kimi',
|
|
205
|
+
provider: {
|
|
206
|
+
kind: 'kimi-cli',
|
|
207
|
+
model: 'kimi-code/kimi-for-coding',
|
|
208
|
+
reasoningEffort: 'high',
|
|
209
|
+
},
|
|
210
|
+
homePath: 'agents/kimi',
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
213
|
+
await withAnimaHome(configDir, async () => {
|
|
214
|
+
const agent = await agentService('kimi').getConfig();
|
|
215
|
+
assert.equal(agent.provider.kind, 'kimi-cli');
|
|
216
|
+
assert.equal(agent.provider.model, 'kimi-code/kimi-for-coding');
|
|
217
|
+
assert.equal('reasoningEffort' in agent.provider, false);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
await rm(configDir, { force: true, recursive: true });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
test('provider idle timeout defaults to 30 minutes for all providers', async () => {
|
|
225
|
+
for (const provider of [
|
|
226
|
+
{ kind: 'claude-code', model: 'opus' },
|
|
227
|
+
{ kind: 'codex-cli', model: 'gpt-5.5' },
|
|
228
|
+
{ kind: 'kimi-cli', model: 'kimi-code/kimi-for-coding' },
|
|
229
|
+
]) {
|
|
230
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-timeout-test-'));
|
|
231
|
+
try {
|
|
232
|
+
await writeConfig(configDir, [
|
|
233
|
+
{
|
|
234
|
+
id: provider.kind,
|
|
235
|
+
provider,
|
|
236
|
+
homePath: `agents/${provider.kind}`,
|
|
237
|
+
},
|
|
238
|
+
]);
|
|
239
|
+
await withAnimaHome(configDir, async () => {
|
|
240
|
+
const agent = await agentService(provider.kind).getConfig();
|
|
241
|
+
assert.equal(agent.provider.idleTimeoutMs, PROVIDER_IDLE_TIMEOUT_MS_DEFAULT);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await rm(configDir, { force: true, recursive: true });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
test('agent provider env patch preserves, updates, and deletes write-only keys', async () => {
|
|
250
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-env-patch-test-'));
|
|
251
|
+
try {
|
|
252
|
+
await writeConfig(configDir, [
|
|
253
|
+
{
|
|
254
|
+
id: 'anima',
|
|
255
|
+
provider: {
|
|
256
|
+
env: {
|
|
257
|
+
KEEP_ME: 'keep',
|
|
258
|
+
REMOVE_ME: 'remove',
|
|
259
|
+
},
|
|
260
|
+
kind: 'codex-cli',
|
|
261
|
+
model: 'gpt-5.5',
|
|
262
|
+
},
|
|
263
|
+
homePath: 'agents/anima',
|
|
264
|
+
},
|
|
265
|
+
]);
|
|
266
|
+
await withAnimaHome(configDir, async () => {
|
|
267
|
+
const agent = await agentService('anima').updateProvider({
|
|
268
|
+
env: {
|
|
269
|
+
ADD_ME: 'add',
|
|
270
|
+
REMOVE_ME: null,
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
assert.deepEqual(agent.provider.env, {
|
|
274
|
+
ADD_ME: 'add',
|
|
275
|
+
KEEP_ME: 'keep',
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
await rm(configDir, { force: true, recursive: true });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
test('agent provider env patch rejects Anima-managed launch keys', async () => {
|
|
284
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-env-patch-test-'));
|
|
285
|
+
try {
|
|
286
|
+
await writeConfig(configDir, [
|
|
287
|
+
{
|
|
288
|
+
id: 'anima',
|
|
289
|
+
provider: {
|
|
290
|
+
kind: 'codex-cli',
|
|
291
|
+
model: 'gpt-5.5',
|
|
292
|
+
},
|
|
293
|
+
homePath: 'agents/anima',
|
|
294
|
+
},
|
|
295
|
+
]);
|
|
296
|
+
await withAnimaHome(configDir, async () => {
|
|
297
|
+
await assert.rejects(agentService('anima').updateProvider({
|
|
298
|
+
env: {
|
|
299
|
+
SLACK_BOT_TOKEN: 'bad-token',
|
|
300
|
+
},
|
|
301
|
+
}), /SLACK_BOT_TOKEN is managed by Anima/);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
await rm(configDir, { force: true, recursive: true });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
test('agent provider kind patch resets provider defaults, preserves env, and archives old session', async () => {
|
|
309
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-provider-kind-test-'));
|
|
310
|
+
try {
|
|
311
|
+
await writeConfig(configDir, [
|
|
312
|
+
{
|
|
313
|
+
id: 'anima',
|
|
314
|
+
provider: {
|
|
315
|
+
env: {
|
|
316
|
+
CUSTOM_LAUNCH_FLAG: 'kept',
|
|
317
|
+
},
|
|
318
|
+
kind: 'codex-cli',
|
|
319
|
+
model: 'gpt-5.5',
|
|
320
|
+
reasoningEffort: 'high',
|
|
321
|
+
},
|
|
322
|
+
homePath: 'agents/anima',
|
|
323
|
+
},
|
|
324
|
+
]);
|
|
325
|
+
await withAnimaHome(configDir, async () => {
|
|
326
|
+
await new SessionStore('anima').write({
|
|
327
|
+
createdAt: '2026-05-26T00:00:00.000Z',
|
|
328
|
+
current: {
|
|
329
|
+
id: 'codex-thread-1',
|
|
330
|
+
kind: 'codex-cli',
|
|
331
|
+
updatedAt: '2026-05-26T00:01:00.000Z',
|
|
332
|
+
},
|
|
333
|
+
latestProviderStats: {
|
|
334
|
+
activityId: 'act_stats',
|
|
335
|
+
createdAt: '2026-05-26T00:01:00.000Z',
|
|
336
|
+
runtimeKind: 'codex-cli',
|
|
337
|
+
usedTokens: 42,
|
|
338
|
+
},
|
|
339
|
+
updatedAt: '2026-05-26T00:01:00.000Z',
|
|
340
|
+
});
|
|
341
|
+
const agent = await agentService('anima').updateProvider({
|
|
342
|
+
kind: 'claude-code',
|
|
343
|
+
});
|
|
344
|
+
assert.equal(agent.provider.kind, 'claude-code');
|
|
345
|
+
assert.equal(agent.provider.model, 'opus');
|
|
346
|
+
assert.ok('reasoningEffort' in agent.provider);
|
|
347
|
+
assert.equal(agent.provider.reasoningEffort, 'xhigh');
|
|
348
|
+
assert.deepEqual(agent.provider.env, { CUSTOM_LAUNCH_FLAG: 'kept' });
|
|
349
|
+
const session = await new SessionStore('anima').read();
|
|
350
|
+
assert.ok(session);
|
|
351
|
+
assert.equal(session.current, undefined);
|
|
352
|
+
assert.equal(session.latestProviderStats, undefined);
|
|
353
|
+
assert.equal(session.archived?.[0]?.kind, 'codex-cli');
|
|
354
|
+
assert.equal(session.archived?.[0]?.id, 'codex-thread-1');
|
|
355
|
+
assert.match(session.archived?.[0]?.note ?? '', /provider switched from codex-cli to claude-code/);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
finally {
|
|
359
|
+
await rm(configDir, { force: true, recursive: true });
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
test('agent provider model-only patch keeps current session', async () => {
|
|
363
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-provider-model-test-'));
|
|
364
|
+
try {
|
|
365
|
+
await writeConfig(configDir, [
|
|
366
|
+
{
|
|
367
|
+
id: 'anima',
|
|
368
|
+
provider: {
|
|
369
|
+
kind: 'codex-cli',
|
|
370
|
+
model: 'gpt-5.4',
|
|
371
|
+
reasoningEffort: 'low',
|
|
372
|
+
},
|
|
373
|
+
homePath: 'agents/anima',
|
|
374
|
+
},
|
|
375
|
+
]);
|
|
376
|
+
await withAnimaHome(configDir, async () => {
|
|
377
|
+
await new SessionStore('anima').write({
|
|
378
|
+
createdAt: '2026-05-26T00:00:00.000Z',
|
|
379
|
+
current: {
|
|
380
|
+
id: 'codex-thread-1',
|
|
381
|
+
kind: 'codex-cli',
|
|
382
|
+
updatedAt: '2026-05-26T00:01:00.000Z',
|
|
383
|
+
},
|
|
384
|
+
updatedAt: '2026-05-26T00:01:00.000Z',
|
|
385
|
+
});
|
|
386
|
+
const agent = await agentService('anima').updateProvider({
|
|
387
|
+
kind: 'codex-cli',
|
|
388
|
+
model: 'gpt-5.5',
|
|
389
|
+
reasoningEffort: 'high',
|
|
390
|
+
});
|
|
391
|
+
assert.equal(agent.provider.model, 'gpt-5.5');
|
|
392
|
+
assert.equal('reasoningEffort' in agent.provider ? agent.provider.reasoningEffort : undefined, 'high');
|
|
393
|
+
const session = await new SessionStore('anima').read();
|
|
394
|
+
assert.ok(session);
|
|
395
|
+
assert.equal(session.current?.id, 'codex-thread-1');
|
|
396
|
+
assert.equal(session.archived, undefined);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
finally {
|
|
400
|
+
await rm(configDir, { force: true, recursive: true });
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
test('agent provider patch rejects invalid kind/model/effort combinations', async () => {
|
|
404
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-provider-invalid-test-'));
|
|
405
|
+
try {
|
|
406
|
+
await writeConfig(configDir, [
|
|
407
|
+
{
|
|
408
|
+
id: 'anima',
|
|
409
|
+
provider: {
|
|
410
|
+
kind: 'codex-cli',
|
|
411
|
+
model: 'gpt-5.5',
|
|
412
|
+
reasoningEffort: 'high',
|
|
413
|
+
},
|
|
414
|
+
homePath: 'agents/anima',
|
|
415
|
+
},
|
|
416
|
+
]);
|
|
417
|
+
await withAnimaHome(configDir, async () => {
|
|
418
|
+
await assert.rejects(agentService('anima').updateProvider({
|
|
419
|
+
kind: 'claude-code',
|
|
420
|
+
model: 'gpt-5.5',
|
|
421
|
+
}), /unsupported model for claude-code: gpt-5.5/);
|
|
422
|
+
await assert.rejects(agentService('anima').updateProvider({
|
|
423
|
+
kind: 'kimi-cli',
|
|
424
|
+
reasoningEffort: 'high',
|
|
425
|
+
}), /unsupported reasoningEffort high/);
|
|
426
|
+
const agent = await agentService('anima').getConfig();
|
|
427
|
+
assert.equal(agent.provider.kind, 'codex-cli');
|
|
428
|
+
assert.equal(agent.provider.model, 'gpt-5.5');
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
finally {
|
|
432
|
+
await rm(configDir, { force: true, recursive: true });
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
test('config loader finds named agents and ignores home dirs without config', async () => {
|
|
436
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
437
|
+
try {
|
|
438
|
+
await writeConfig(configDir, [
|
|
439
|
+
{ id: 'anima', homePath: 'agents/anima' },
|
|
440
|
+
{ id: 'support', profile: { displayName: 'Support Anima' } },
|
|
441
|
+
]);
|
|
442
|
+
await mkdir(join(configDir, 'agents', 'scratch'), { recursive: true });
|
|
443
|
+
await withAnimaHome(configDir, async () => {
|
|
444
|
+
const support = await agentService('support').getConfig();
|
|
445
|
+
assert.equal(support.profile?.displayName, 'Support Anima');
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
finally {
|
|
449
|
+
await rm(configDir, { force: true, recursive: true });
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
test('agent config id must match its directory name', async () => {
|
|
453
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-config-test-'));
|
|
454
|
+
try {
|
|
455
|
+
await writeConfig(configDir, [{ id: 'anima' }]);
|
|
456
|
+
await mkdir(join(configDir, 'agents', 'milo'), { recursive: true });
|
|
457
|
+
await writeFile(join(configDir, 'agents', 'milo', 'config.json'), `${JSON.stringify({ id: 'nora' }, null, 2)}\n`, 'utf8');
|
|
458
|
+
await withAnimaHome(configDir, async () => {
|
|
459
|
+
await assert.rejects(defaultAgentRegistryService.listAgentConfigs(), /agent id must match directory name milo/);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
finally {
|
|
463
|
+
await rm(configDir, { force: true, recursive: true });
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
test('supervisor service commands ignore ambient runtime agent id', async () => {
|
|
467
|
+
const configDir = await mkdtemp(join(tmpdir(), 'anima-supervisor-agents-test-'));
|
|
468
|
+
const previousAgentId = process.env.ANIMA_AGENT_ID;
|
|
469
|
+
try {
|
|
470
|
+
await writeConfig(configDir, [{ id: 'anima' }, { id: 'milo' }, { id: 'nora' }]);
|
|
471
|
+
process.env.ANIMA_AGENT_ID = 'nora';
|
|
472
|
+
await withAnimaHome(configDir, async () => {
|
|
473
|
+
assert.deepEqual((await loadRuntimeAgents()).map((agent) => agent.id), ['anima', 'milo', 'nora']);
|
|
474
|
+
assert.deepEqual((await loadRuntimeAgents({ agent: 'milo' })).map((agent) => agent.id), ['milo']);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
if (previousAgentId === undefined)
|
|
479
|
+
delete process.env.ANIMA_AGENT_ID;
|
|
480
|
+
else
|
|
481
|
+
process.env.ANIMA_AGENT_ID = previousAgentId;
|
|
482
|
+
await rm(configDir, { force: true, recursive: true });
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
async function writeConfig(configDir, agents) {
|
|
486
|
+
await mkdir(configDir, { recursive: true });
|
|
487
|
+
await writeFile(join(configDir, 'config.json'), `${JSON.stringify({}, null, 2)}\n`, 'utf8');
|
|
488
|
+
const seenDirs = new Set();
|
|
489
|
+
for (const [index, agent] of agents.entries()) {
|
|
490
|
+
const id = typeof agent.id === 'string' ? agent.id : `agent-${index}`;
|
|
491
|
+
const dirName = seenDirs.has(id) ? `${id}-${index}` : id;
|
|
492
|
+
seenDirs.add(dirName);
|
|
493
|
+
const agentDir = join(configDir, 'agents', dirName);
|
|
494
|
+
await mkdir(agentDir, { recursive: true });
|
|
495
|
+
await writeFile(join(agentDir, 'config.json'), `${JSON.stringify(agent, null, 2)}\n`, 'utf8');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-skills.test.d.ts","sourceRoot":"","sources":["../../../server/tests/default-skills.test.ts"],"names":[],"mappings":""}
|