@animalabs/connectome-host 0.7.2 → 0.7.4

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +203 -10
  2. package/HEADLESS-FLEET-PLAN.md +22 -0
  3. package/README.md +22 -11
  4. package/docs/AGENT-ONBOARDING.md +20 -1
  5. package/docs/debug-context-api.md +2 -2
  6. package/docs/retrieval-traces.md +173 -0
  7. package/docs/webui-deployment.md +2 -1
  8. package/package.json +3 -3
  9. package/scripts/audit-module-optins.ts +288 -0
  10. package/scripts/warmup-session.ts +17 -3
  11. package/src/codex-subscription-adapter.ts +13 -1
  12. package/src/framework-agent-config.ts +59 -4
  13. package/src/framework-strategy.ts +33 -3
  14. package/src/headless.ts +14 -0
  15. package/src/index.ts +95 -35
  16. package/src/logging-adapter.ts +13 -2
  17. package/src/mcpl-config.ts +8 -0
  18. package/src/modules/fleet-module.ts +60 -1
  19. package/src/modules/fleet-types.ts +30 -1
  20. package/src/modules/identity-module.ts +274 -0
  21. package/src/modules/mcpl-admin-module.ts +78 -5
  22. package/src/modules/observers-module.ts +12 -0
  23. package/src/modules/retrieval-module.ts +254 -52
  24. package/src/modules/retrieval-trace-page.ts +254 -0
  25. package/src/modules/retrieval-trace.ts +904 -0
  26. package/src/modules/settings-module.ts +28 -2
  27. package/src/modules/subscription-gc-module.ts +54 -1
  28. package/src/modules/tts-relay-module.ts +33 -18
  29. package/src/modules/web-ui-module.ts +445 -894
  30. package/src/recipe.ts +137 -12
  31. package/src/retrieval-config.ts +39 -0
  32. package/src/strategies/frontdesk-strategy.ts +34 -125
  33. package/src/tui.ts +325 -54
  34. package/src/web/panel-data.ts +1187 -0
  35. package/src/web/protocol.ts +75 -10
  36. package/test/audit-module-optins.test.ts +167 -0
  37. package/test/bedrock-prompt-caching.test.ts +170 -0
  38. package/test/fleet-panel-request.test.ts +90 -0
  39. package/test/framework-strategy-defaults.test.ts +110 -0
  40. package/test/frontdesk-strategy.test.ts +25 -37
  41. package/test/headless-panel-request.test.ts +201 -0
  42. package/test/identity-and-surfaces.test.ts +157 -0
  43. package/test/mcpl-admin-module.test.ts +23 -0
  44. package/test/mock-headless-child.ts +14 -0
  45. package/test/retrieval-auth-loopback.test.ts +49 -0
  46. package/test/retrieval-config.test.ts +74 -0
  47. package/test/retrieval-module.test.ts +821 -0
  48. package/test/subscription-gc-module.test.ts +152 -0
  49. package/test/tui-format.test.ts +106 -0
  50. package/test/web-ui-context-coverage.test.ts +1 -1
  51. package/test/web-ui-module.test.ts +189 -3
  52. package/test/web-ui-observers.test.ts +8 -5
  53. package/test/web-ui-protocol.test.ts +0 -0
  54. package/web/bun.lock +345 -0
  55. package/web/src/App.tsx +159 -44
  56. package/web/src/Context.tsx +35 -8
  57. package/web/src/ContextDocument.tsx +20 -5
  58. package/web/src/Files.tsx +2 -8
  59. package/web/src/Lessons.tsx +2 -38
  60. package/web/src/Mcpl.tsx +80 -14
  61. package/web/src/Pins.tsx +5 -0
  62. package/web/src/Settings.tsx +5 -0
  63. package/web/vite.config.ts +8 -2
@@ -0,0 +1,49 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
2
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import type { ModuleContext } from '@animalabs/agent-framework';
6
+ import {
7
+ WebUiModule,
8
+ __getSharedServerPortForTests,
9
+ __resetSharedServerForTests,
10
+ } from '../src/modules/web-ui-module.js';
11
+
12
+ describe('retrieval operator authentication on credential-free loopback', () => {
13
+ let webUiModule: WebUiModule | undefined;
14
+ let root: string;
15
+ let baseUrl: string;
16
+
17
+ beforeAll(async () => {
18
+ root = mkdtempSync(join(tmpdir(), 'retrieval-auth-loopback-'));
19
+ const staticRoot = join(root, 'web');
20
+ mkdirSync(staticRoot);
21
+ writeFileSync(join(staticRoot, 'index.html'), '<!doctype html><title>loopback</title>');
22
+ webUiModule = new WebUiModule({
23
+ port: 0,
24
+ host: '127.0.0.1',
25
+ staticDir: staticRoot,
26
+ });
27
+ await webUiModule.start({} as ModuleContext);
28
+ const port = __getSharedServerPortForTests();
29
+ if (!port) throw new Error('webui server not bound; did start() succeed?');
30
+ baseUrl = `http://127.0.0.1:${port}`;
31
+ });
32
+
33
+ afterAll(async () => {
34
+ await webUiModule?.stop();
35
+ await __resetSharedServerForTests();
36
+ if (root) rmSync(root, { recursive: true, force: true });
37
+ });
38
+
39
+ test('denies both retrieval routes without changing ordinary loopback routes', async () => {
40
+ expect((await fetch(`${baseUrl}/`)).status).toBe(200);
41
+ expect((await fetch(`${baseUrl}/debug/context`)).status).toBe(503);
42
+
43
+ for (const path of ['/debug/retrieval', '/debug/retrieval/view']) {
44
+ const response = await fetch(`${baseUrl}${path}`);
45
+ expect(response.status).toBe(401);
46
+ expect(response.headers.get('cache-control')).toBe('no-store');
47
+ }
48
+ });
49
+ });
@@ -0,0 +1,74 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import type { Membrane } from '@animalabs/membrane';
3
+ import { validateRecipe } from '../src/recipe.js';
4
+ import { buildRetrievalModuleConfig } from '../src/retrieval-config.js';
5
+
6
+ const membrane = {} as Membrane;
7
+
8
+ function recipe(retrieval: unknown, provider: string = 'openai-codex') {
9
+ return {
10
+ name: 'retrieval-config-test',
11
+ agent: { systemPrompt: 'sys', provider },
12
+ modules: { retrieval },
13
+ };
14
+ }
15
+
16
+ describe('retrieval recipe config', () => {
17
+ test('accepts and maps provider reasoning settings', () => {
18
+ const parsed = validateRecipe(recipe({
19
+ model: 'test-model',
20
+ maxInjected: 7,
21
+ reasoningEffort: 'minimal',
22
+ }));
23
+
24
+ expect(parsed.modules?.retrieval).toEqual({
25
+ model: 'test-model',
26
+ maxInjected: 7,
27
+ reasoningEffort: 'minimal',
28
+ });
29
+ expect(buildRetrievalModuleConfig(membrane, parsed.modules!.retrieval!, 'openai-codex')).toEqual({
30
+ membrane,
31
+ retrievalModel: 'test-model',
32
+ maxInjectedLessons: 7,
33
+ retrievalReasoning: { effort: 'minimal' },
34
+ });
35
+ });
36
+
37
+ test('preserves boolean shorthand and omits unconfigured reasoning', () => {
38
+ expect(validateRecipe(recipe(true)).modules?.retrieval).toBe(true);
39
+ expect(validateRecipe(recipe(false)).modules?.retrieval).toBe(false);
40
+ expect(buildRetrievalModuleConfig(membrane, { model: 'test-model' }, 'anthropic')).toEqual({
41
+ membrane,
42
+ retrievalModel: 'test-model',
43
+ });
44
+ });
45
+
46
+ test('rejects malformed retrieval reasoning settings', () => {
47
+ expect(() => validateRecipe(recipe(null))).toThrow(/modules\.retrieval must be a boolean or object/);
48
+ expect(() => validateRecipe(recipe([]))).toThrow(/modules\.retrieval must be a boolean or object/);
49
+ expect(() => validateRecipe(recipe({ reasoningEffort: 'ultra' }))).toThrow(/reasoningEffort/);
50
+ expect(() => validateRecipe(recipe({ reasoningEffort: ['high'] }))).toThrow(/reasoningEffort/);
51
+ expect(() => validateRecipe(recipe({ reasoningContext: 'current_turn' }))).toThrow(
52
+ /independent one-shot requests/,
53
+ );
54
+ expect(() => validateRecipe(recipe({ reasoningEffort: 'high' }, 'anthropic'))).toThrow(
55
+ /requires agent\.provider/,
56
+ );
57
+ expect(() => buildRetrievalModuleConfig(
58
+ membrane,
59
+ { reasoningEffort: 'high' },
60
+ 'anthropic',
61
+ )).toThrow(/requires agent\.provider/);
62
+ expect(() => validateRecipe(recipe({ reasoningEffort: 'high' }))).toThrow(
63
+ /model must be a non-empty string/,
64
+ );
65
+ expect(() => validateRecipe(recipe({ model: ' ', reasoningEffort: 'high' }))).toThrow(
66
+ /model must be a non-empty string/,
67
+ );
68
+ expect(() => buildRetrievalModuleConfig(
69
+ membrane,
70
+ { reasoningEffort: 'high' },
71
+ 'openai-codex',
72
+ )).toThrow(/model must be a non-empty string/);
73
+ });
74
+ });