@addai/node 0.4.0

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 (133) hide show
  1. package/README.md +96 -0
  2. package/dist/agent-auth.d.ts +9 -0
  3. package/dist/agent-auth.js +96 -0
  4. package/dist/attachments.d.ts +20 -0
  5. package/dist/attachments.js +164 -0
  6. package/dist/capabilities.d.ts +64 -0
  7. package/dist/capabilities.js +406 -0
  8. package/dist/claude-binary.d.ts +16 -0
  9. package/dist/claude-binary.js +145 -0
  10. package/dist/claude-config.d.ts +11 -0
  11. package/dist/claude-config.js +144 -0
  12. package/dist/claude-print.d.ts +35 -0
  13. package/dist/claude-print.js +312 -0
  14. package/dist/claude-spawn.d.ts +40 -0
  15. package/dist/claude-spawn.js +220 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +137 -0
  18. package/dist/codex-binary.d.ts +4 -0
  19. package/dist/codex-binary.js +70 -0
  20. package/dist/codex-spawn.d.ts +47 -0
  21. package/dist/codex-spawn.js +320 -0
  22. package/dist/command-runner.d.ts +9 -0
  23. package/dist/command-runner.js +592 -0
  24. package/dist/config.d.ts +7 -0
  25. package/dist/config.js +38 -0
  26. package/dist/control-server.d.ts +5 -0
  27. package/dist/control-server.js +292 -0
  28. package/dist/diskguard.d.ts +96 -0
  29. package/dist/diskguard.js +435 -0
  30. package/dist/error-classify.d.ts +20 -0
  31. package/dist/error-classify.js +159 -0
  32. package/dist/events.d.ts +26 -0
  33. package/dist/events.js +97 -0
  34. package/dist/gemini-binary.d.ts +4 -0
  35. package/dist/gemini-binary.js +64 -0
  36. package/dist/gemini-spawn.d.ts +52 -0
  37. package/dist/gemini-spawn.js +369 -0
  38. package/dist/git-identity.d.ts +18 -0
  39. package/dist/git-identity.js +104 -0
  40. package/dist/grok-binary.d.ts +4 -0
  41. package/dist/grok-binary.js +70 -0
  42. package/dist/grok-spawn.d.ts +41 -0
  43. package/dist/grok-spawn.js +495 -0
  44. package/dist/guardrails.d.ts +32 -0
  45. package/dist/guardrails.js +125 -0
  46. package/dist/hang-watchdog.d.ts +17 -0
  47. package/dist/hang-watchdog.js +48 -0
  48. package/dist/harness-registry.d.ts +55 -0
  49. package/dist/harness-registry.js +180 -0
  50. package/dist/heartbeat.d.ts +13 -0
  51. package/dist/heartbeat.js +95 -0
  52. package/dist/index.d.ts +12 -0
  53. package/dist/index.js +440 -0
  54. package/dist/install.d.ts +63 -0
  55. package/dist/install.js +467 -0
  56. package/dist/jsonl-tail.d.ts +20 -0
  57. package/dist/jsonl-tail.js +192 -0
  58. package/dist/kimi-binary.d.ts +4 -0
  59. package/dist/kimi-binary.js +68 -0
  60. package/dist/kimi-spawn.d.ts +47 -0
  61. package/dist/kimi-spawn.js +319 -0
  62. package/dist/lockfile.d.ts +30 -0
  63. package/dist/lockfile.js +169 -0
  64. package/dist/mcp-config.d.ts +23 -0
  65. package/dist/mcp-config.js +133 -0
  66. package/dist/memory-capture.d.ts +20 -0
  67. package/dist/memory-capture.js +45 -0
  68. package/dist/memory-pack.d.ts +34 -0
  69. package/dist/memory-pack.js +171 -0
  70. package/dist/model-filter.d.ts +4 -0
  71. package/dist/model-filter.js +34 -0
  72. package/dist/node-health.d.ts +18 -0
  73. package/dist/node-health.js +74 -0
  74. package/dist/pairing.d.ts +20 -0
  75. package/dist/pairing.js +129 -0
  76. package/dist/paths.d.ts +9 -0
  77. package/dist/paths.js +48 -0
  78. package/dist/precompact-hook.d.ts +27 -0
  79. package/dist/precompact-hook.js +136 -0
  80. package/dist/projects.d.ts +25 -0
  81. package/dist/projects.js +255 -0
  82. package/dist/prompt-keystrokes.d.ts +3 -0
  83. package/dist/prompt-keystrokes.js +56 -0
  84. package/dist/pty-helper.d.ts +26 -0
  85. package/dist/pty-helper.js +156 -0
  86. package/dist/request-pump.d.ts +12 -0
  87. package/dist/request-pump.js +249 -0
  88. package/dist/run-retry.d.ts +28 -0
  89. package/dist/run-retry.js +81 -0
  90. package/dist/self-update.d.ts +75 -0
  91. package/dist/self-update.js +192 -0
  92. package/dist/session-runner.d.ts +130 -0
  93. package/dist/session-runner.js +2271 -0
  94. package/dist/sleep-detector.d.ts +11 -0
  95. package/dist/sleep-detector.js +32 -0
  96. package/dist/state-migrate.d.ts +8 -0
  97. package/dist/state-migrate.js +91 -0
  98. package/dist/store.d.ts +5 -0
  99. package/dist/store.js +82 -0
  100. package/dist/supabase-client.d.ts +15 -0
  101. package/dist/supabase-client.js +119 -0
  102. package/dist/time-context.d.ts +10 -0
  103. package/dist/time-context.js +71 -0
  104. package/dist/tokens.d.ts +4 -0
  105. package/dist/tokens.js +33 -0
  106. package/dist/tui/app.d.ts +35 -0
  107. package/dist/tui/app.js +82 -0
  108. package/dist/tui/console-capture.d.ts +13 -0
  109. package/dist/tui/console-capture.js +62 -0
  110. package/dist/tui/dashboard.d.ts +42 -0
  111. package/dist/tui/dashboard.js +205 -0
  112. package/dist/tui/data.d.ts +57 -0
  113. package/dist/tui/data.js +62 -0
  114. package/dist/tui/harnesses.d.ts +16 -0
  115. package/dist/tui/harnesses.js +354 -0
  116. package/dist/tui/render.d.ts +21 -0
  117. package/dist/tui/render.js +116 -0
  118. package/dist/tui/requests.d.ts +18 -0
  119. package/dist/tui/requests.js +127 -0
  120. package/dist/tui/run.d.ts +13 -0
  121. package/dist/tui/run.js +178 -0
  122. package/dist/tui/transcript.d.ts +39 -0
  123. package/dist/tui/transcript.js +234 -0
  124. package/dist/tui.d.ts +1 -0
  125. package/dist/tui.js +314 -0
  126. package/dist/types.d.ts +65 -0
  127. package/dist/types.js +3 -0
  128. package/dist/win.d.ts +67 -0
  129. package/dist/win.js +317 -0
  130. package/package.json +60 -0
  131. package/scripts/fix-pty-helper.js +28 -0
  132. package/scripts/precompact-capture.js +292 -0
  133. package/scripts/smoke-test.sh +74 -0
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ // Minimal MCP catalog + per-session filtering. Lifted from the desktop's
3
+ // mcp-skills.ts and trimmed: the runtime only needs (a) "is this MCP name
4
+ // installed in the relevant scope?" so we can decide whether to filter,
5
+ // and (b) the ability to write a temp `.mcp.json` whitelisting just the
6
+ // caller-requested servers.
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.CLAUDE_HOME = void 0;
42
+ exports.listMcps = listMcps;
43
+ exports.writeSessionMcpConfig = writeSessionMcpConfig;
44
+ exports.cleanupSessionMcpConfig = cleanupSessionMcpConfig;
45
+ exports.cleanupAllSessionMcpConfigs = cleanupAllSessionMcpConfigs;
46
+ const fs = __importStar(require("fs"));
47
+ const os = __importStar(require("os"));
48
+ const path = __importStar(require("path"));
49
+ const paths_1 = require("./paths");
50
+ Object.defineProperty(exports, "CLAUDE_HOME", { enumerable: true, get: function () { return paths_1.CLAUDE_HOME; } });
51
+ const CLAUDE_CONFIG = path.join(os.homedir(), '.claude.json');
52
+ function readJson(filePath) {
53
+ try {
54
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ function extract(obj, scope) {
61
+ if (!obj)
62
+ return [];
63
+ const servers = (obj.mcpServers ?? {});
64
+ return Object.entries(servers)
65
+ .filter(([name]) => name !== 'codeflows-mcp-server')
66
+ .map(([name, s]) => ({ name, scope, command: s.command, args: s.args ?? [], env: s.env ?? {} }));
67
+ }
68
+ /** Best-effort MCP catalog. Used to decide what to whitelist when the
69
+ * caller asks for a subset. */
70
+ function listMcps(workingDirectory) {
71
+ const global = extract(readJson(CLAUDE_CONFIG), 'global');
72
+ const projectDotMcp = extract(readJson(path.join(workingDirectory, '.mcp.json')), 'project');
73
+ // Project-scope wins on name collision (matches desktop behavior).
74
+ const byName = new Map();
75
+ for (const s of global)
76
+ byName.set(s.name, s);
77
+ for (const s of projectDotMcp)
78
+ byName.set(s.name, s);
79
+ return Array.from(byName.values());
80
+ }
81
+ /**
82
+ * Write a temp `.mcp.json` containing ONLY the caller-allowed servers,
83
+ * suitable for `claude --mcp-config <path> --strict-mcp-config`. Returns
84
+ * null when no filtering is needed (caller wants all visible MCPs).
85
+ */
86
+ function writeSessionMcpConfig(ptyId, workingDirectory, enabled) {
87
+ if (enabled === null)
88
+ return null;
89
+ const all = listMcps(workingDirectory);
90
+ const allow = new Set(enabled);
91
+ // Whitelist matches everything visible → no filter needed.
92
+ if (allow.size === all.length && all.every(s => allow.has(s.name)))
93
+ return null;
94
+ const out = {};
95
+ for (const s of all) {
96
+ if (!allow.has(s.name))
97
+ continue;
98
+ const entry = { command: s.command, args: s.args ?? [] };
99
+ if (s.env && Object.keys(s.env).length > 0)
100
+ entry.env = s.env;
101
+ out[s.name] = entry;
102
+ }
103
+ if (!fs.existsSync(paths_1.RUNTIME_SESSIONS_DIR)) {
104
+ fs.mkdirSync(paths_1.RUNTIME_SESSIONS_DIR, { recursive: true });
105
+ }
106
+ const target = path.join(paths_1.RUNTIME_SESSIONS_DIR, `${ptyId}.mcp.json`);
107
+ fs.writeFileSync(target, JSON.stringify({ mcpServers: out }, null, 2));
108
+ return target;
109
+ }
110
+ function cleanupSessionMcpConfig(filePath) {
111
+ if (!filePath)
112
+ return;
113
+ try {
114
+ fs.unlinkSync(filePath);
115
+ }
116
+ catch { /* already gone */ }
117
+ }
118
+ // Used at startup to remove stale per-session configs from a previous run.
119
+ function cleanupAllSessionMcpConfigs() {
120
+ try {
121
+ if (!fs.existsSync(paths_1.RUNTIME_SESSIONS_DIR))
122
+ return;
123
+ for (const name of fs.readdirSync(paths_1.RUNTIME_SESSIONS_DIR)) {
124
+ if (name.endsWith('.mcp.json')) {
125
+ try {
126
+ fs.unlinkSync(path.join(paths_1.RUNTIME_SESSIONS_DIR, name));
127
+ }
128
+ catch { }
129
+ }
130
+ }
131
+ }
132
+ catch { }
133
+ }
@@ -0,0 +1,20 @@
1
+ export interface CapturePayload {
2
+ entity_id: string;
3
+ request_id: string;
4
+ surface: string;
5
+ turn: {
6
+ user_text: string;
7
+ assistant_text: string;
8
+ };
9
+ transcript_ref: {
10
+ kind: string;
11
+ path?: string;
12
+ host?: string;
13
+ } | null;
14
+ }
15
+ export declare function buildCapturePayload(req: {
16
+ entityId: string;
17
+ requestId: string;
18
+ surface: string;
19
+ }, userText: string, assistantText: string, transcriptRef: CapturePayload['transcript_ref']): CapturePayload;
20
+ export declare function postCapture(baseUrl: string, serviceToken: string, payload: CapturePayload, timeoutMs?: number): Promise<void>;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ // +Ai Memory (Task 5) — turn-capture payload builder and poster.
3
+ //
4
+ // Called from session-runner.ts's setStatus() 'completed' branch (same
5
+ // hook point as notifySelfImproveIfEnabled) so the DB's
6
+ // entity_studio_aggregate_assistant_text trigger has already aggregated
7
+ // assistant_text for this request before we read it back. Best-effort:
8
+ // postCapture must NEVER throw or slow down request processing — a failed
9
+ // or slow capture must not affect the run in any way.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.buildCapturePayload = buildCapturePayload;
12
+ exports.postCapture = postCapture;
13
+ function buildCapturePayload(req, userText, assistantText, transcriptRef) {
14
+ return {
15
+ entity_id: req.entityId,
16
+ request_id: req.requestId,
17
+ surface: req.surface,
18
+ turn: { user_text: userText.slice(0, 8000), assistant_text: assistantText.slice(0, 12000) },
19
+ transcript_ref: transcriptRef,
20
+ };
21
+ }
22
+ async function postCapture(baseUrl, serviceToken, payload, timeoutMs = 5000) {
23
+ try {
24
+ const res = await fetch(`${baseUrl}/functions/v1/entity-memory-capture`, {
25
+ method: 'POST',
26
+ headers: {
27
+ 'content-type': 'application/json',
28
+ apikey: serviceToken,
29
+ authorization: `Bearer ${serviceToken}`,
30
+ },
31
+ body: JSON.stringify(payload),
32
+ signal: AbortSignal.timeout(timeoutMs),
33
+ });
34
+ if (!res.ok) {
35
+ // fire-and-forget: no retries (capture is lower-stakes than
36
+ // self-improve), but a non-2xx must never be silently discarded.
37
+ const text = await res.text().catch(() => '');
38
+ console.error('[memory-capture] http', res.status, text.slice(0, 200));
39
+ }
40
+ }
41
+ catch (err) {
42
+ // fire-and-forget: log, never throw (capture must not break runs)
43
+ console.error('[memory-capture] failed:', err?.message);
44
+ }
45
+ }
@@ -0,0 +1,34 @@
1
+ export declare const MEMORY_FALLBACK_MARKER = "\n\n[memory pack unavailable \u2014 memory fallback active]\n";
2
+ export declare function replaceMemoryBlock(systemPrompt: string, packBlock: string): string;
3
+ export declare function buildPackBody(args: {
4
+ entityId: string;
5
+ prompt: string;
6
+ budgetTokens?: number;
7
+ memberProfileId?: string | null;
8
+ }): Record<string, unknown>;
9
+ export declare function fetchMemoryPack(baseUrl: string, serviceToken: string, args: {
10
+ entityId: string;
11
+ prompt: string;
12
+ budgetTokens?: number;
13
+ memberProfileId?: string | null;
14
+ }, timeoutMs?: number): Promise<{
15
+ block: string;
16
+ degraded: boolean;
17
+ flagged: boolean;
18
+ }>;
19
+ export declare function resolvePackEntityId(req: {
20
+ entity_studio_entity_id?: string | null;
21
+ entity_id?: string | null;
22
+ }): string | null;
23
+ export declare function applyMemoryPack(req: {
24
+ entity_studio_entity_id?: string | null;
25
+ entity_id?: string | null;
26
+ prompt?: string | null;
27
+ member_profile_id?: string | null;
28
+ }, systemPrompt: string | null, baseUrl: string, serviceToken: string, deps: {
29
+ /** Overridable for tests; defaults to the real fetchMemoryPack above. */
30
+ fetchPack?: typeof fetchMemoryPack;
31
+ buildLegacyManifest: () => Promise<string | null>;
32
+ onFallback?: () => void;
33
+ onFlagged?: (flagged: boolean) => void;
34
+ }): Promise<string | null>;
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MEMORY_FALLBACK_MARKER = void 0;
4
+ exports.replaceMemoryBlock = replaceMemoryBlock;
5
+ exports.buildPackBody = buildPackBody;
6
+ exports.fetchMemoryPack = fetchMemoryPack;
7
+ exports.resolvePackEntityId = resolvePackEntityId;
8
+ exports.applyMemoryPack = applyMemoryPack;
9
+ // +Ai Memory (Task 7) — semantic memory pack fetcher.
10
+ //
11
+ // Called from session-runner.ts's runRequest in place of the legacy flat
12
+ // DocFlows manifest (buildMemoryManifest). Hits the entity-memory-pack edge
13
+ // function (T6, deployed) which authenticates like entity-self-improve /
14
+ // entity-memory-capture: gateway JWT (anon key as Bearer).
15
+ //
16
+ // Contract with the edge function:
17
+ // - block === '' → entity has no memory_v2 pack (unflagged, or
18
+ // genuinely nothing to say) → caller falls back to
19
+ // the legacy buildMemoryManifest().
20
+ // - block non-empty → use it INSTEAD of the legacy manifest.
21
+ // - any failure (network error, timeout, non-2xx, bad body) → fail LOUD:
22
+ // return the fallback marker with degraded=true so the caller can both
23
+ // (a) still inject a marker block into the system prompt so the model
24
+ // knows its memory is degraded, and (b) emit a `memory_fallback`
25
+ // runtime event so the failure is visible/observable, never silent.
26
+ exports.MEMORY_FALLBACK_MARKER = '\n\n[memory pack unavailable — memory fallback active]\n';
27
+ // +Ai Memory (post-final-review Finding 2) — dedupe the '## Memory' block.
28
+ //
29
+ // The DB dispatch trigger (entity_memory_recall_inject,
30
+ // supabase/migrations/20260716110000_memory_v2_pack_trigger.sql, in the
31
+ // addai-entity-studio repo) already appends its own budgeted keyword pack
32
+ // to req.system_prompt at INSERT time for every memory_v2-flagged entity —
33
+ // that block always starts with the literal sequence '\n\n## Memory\n' and
34
+ // runs to the end of the string (the trigger appends it last). When this
35
+ // daemon then fetches the richer edge-fn pack (fetchMemoryPack above) and
36
+ // it comes back non-degraded and non-empty, appending it naively gives the
37
+ // model two '## Memory' sections. This helper strips the trigger's
38
+ // trailing block (found via the LAST occurrence of the marker, since the
39
+ // trigger always appends last) before appending the edge-fn pack in its
40
+ // place. Only call this when replacing with a real, non-degraded,
41
+ // non-empty pack — degraded/empty paths must leave system_prompt untouched
42
+ // by this function (empty packBlock is a no-op below).
43
+ function replaceMemoryBlock(systemPrompt, packBlock) {
44
+ if (!packBlock)
45
+ return systemPrompt;
46
+ const base = systemPrompt ?? '';
47
+ const idx = base.lastIndexOf('\n\n## Memory\n');
48
+ const stripped = idx !== -1 ? base.slice(0, idx) : base;
49
+ return stripped && stripped.trim().length > 0 ? `${stripped}\n\n${packBlock}` : packBlock;
50
+ }
51
+ // +Ai Memory (Phase 3 Task 2 — member delivery).
52
+ //
53
+ // Builds the entity-memory-pack POST body. Extracted from fetchMemoryPack
54
+ // so it's independently unit-testable: the null-member path must produce a
55
+ // body with EXACTLY the same keys as pre-phase-3 (byte-identical contract),
56
+ // which is easiest to assert directly on the plain object rather than by
57
+ // scraping a captured HTTP request. `requester` is omitted entirely (not
58
+ // sent as null) when there is no member, so a memberless dispatch's POST
59
+ // body is indistinguishable from the pre-phase-3 shape.
60
+ function buildPackBody(args) {
61
+ return {
62
+ entity_id: args.entityId,
63
+ prompt: args.prompt,
64
+ budget_tokens: args.budgetTokens,
65
+ ...(args.memberProfileId ? { requester: { profile_id: args.memberProfileId } } : {}),
66
+ };
67
+ }
68
+ async function fetchMemoryPack(baseUrl, serviceToken, args,
69
+ // edge fn steady-state ~600ms, cold start ~950ms; 3s bounds worst-case
70
+ // spawn latency
71
+ timeoutMs = 3000) {
72
+ try {
73
+ const r = await fetch(`${baseUrl}/functions/v1/entity-memory-pack`, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'content-type': 'application/json',
77
+ apikey: serviceToken,
78
+ authorization: `Bearer ${serviceToken}`,
79
+ },
80
+ body: JSON.stringify(buildPackBody(args)),
81
+ signal: AbortSignal.timeout(timeoutMs),
82
+ });
83
+ if (!r.ok)
84
+ throw new Error(`pack ${r.status}`);
85
+ const j = (await r.json());
86
+ // Phase 4 T7: entity-memory-pack returns `skipped:"flag"` distinctly
87
+ // from a genuinely-empty pack (block === '' either way) — this is the
88
+ // one reliable, already-happening-every-turn signal for "is this
89
+ // entity memory_v2-flagged", reused (not re-asked) to gate the
90
+ // PreCompact hook write (see precompact-hook.ts / session-runner.ts).
91
+ return { block: j.block ?? '', degraded: false, flagged: j.skipped !== 'flag' };
92
+ }
93
+ catch (err) {
94
+ console.error('[memory-pack] failed:', err?.message);
95
+ // Unknown on failure — conservatively NOT flagged, so a pack-fetch
96
+ // outage never causes the PreCompact hook to be written when we
97
+ // couldn't actually confirm the entity is on the pack.
98
+ return { block: exports.MEMORY_FALLBACK_MARKER, degraded: true, flagged: false };
99
+ }
100
+ }
101
+ // +Ai Memory — runtime "entity_id alias" fix (final phase-3 review finding).
102
+ //
103
+ // runtime_pick_next_request (addai-entity-studio/supabase/migrations/
104
+ // 20260718101000_memory_v3_pick_member.sql) `returns entity_runtime_requests`
105
+ // — the BARE table rowtype, via `returning * into v_req; return v_req;`. No
106
+ // jsonb_build_object, no column aliasing. PostgREST therefore serializes the
107
+ // owning-entity column under its real name, `entity_studio_entity_id` — an
108
+ // `entity_id` key never appears on a row from that RPC. The daemon's pack-
109
+ // fetch gate previously read `req.entity_id` only, which meant the richer
110
+ // edge-fn pack fetch below NEVER fired in production; memory only reached
111
+ // entities via the DB dispatch trigger's already-injected '## Memory' block
112
+ // (entity_memory_recall_inject). `entity_id` is kept as a secondary fallback
113
+ // here — never populated by the current pick RPC, but cheap insurance
114
+ // against any future/legacy shape (e.g. runtime_request_get_meta, which
115
+ // DOES alias the column to `entity_id` via jsonb_build_object) that hands
116
+ // this same row shape through a different code path.
117
+ function resolvePackEntityId(req) {
118
+ return req.entity_studio_entity_id ?? req.entity_id ?? null;
119
+ }
120
+ // Extracted from session-runner.ts's runRequest so the gate → fetch →
121
+ // three-way-switch → replaceMemoryBlock interaction is unit-testable without
122
+ // exercising the rest of the daemon (spawn, install, cwd resolution, …).
123
+ // Mirrors the inline logic byte-for-byte; behavior must not change when this
124
+ // is swapped in at the call site — only WHICH entity id field is read
125
+ // changes (see resolvePackEntityId above).
126
+ //
127
+ // 1. degraded → fail LOUD: fold the fallback marker AND the legacy
128
+ // manifest into system_prompt (never silently drop
129
+ // what was already there), and let the caller know
130
+ // via onFallback() so it can emit the runtime event.
131
+ // 2. non-empty block → the richer edge-fn pack REPLACES the DB dispatch
132
+ // trigger's already-injected '## Memory' block
133
+ // (replaceMemoryBlock, phase-1 B1 dedupe fix) —
134
+ // never doubles the section.
135
+ // 3. block === '' → entity has no pack (unflagged / nothing to
136
+ // recall) → legacy manifest, same as before.
137
+ // no entity id at all → pack fetch never fires; behaves exactly like case
138
+ // 3 (legacy manifest, or no-op if that's empty/null
139
+ // too) — preserves pre-fix behavior for non-entity
140
+ // runs byte-for-byte.
141
+ async function applyMemoryPack(req, systemPrompt, baseUrl, serviceToken, deps) {
142
+ const entityId = resolvePackEntityId(req);
143
+ const fetchPack = deps.fetchPack ?? fetchMemoryPack;
144
+ const pack = entityId
145
+ ? await fetchPack(baseUrl, serviceToken, {
146
+ entityId,
147
+ prompt: req.prompt ?? '',
148
+ memberProfileId: req.member_profile_id ?? null,
149
+ })
150
+ : { block: '', degraded: false, flagged: false };
151
+ deps.onFlagged?.(pack.flagged);
152
+ if (pack.degraded) {
153
+ deps.onFallback?.();
154
+ const legacy = await deps.buildLegacyManifest();
155
+ const degradedManifest = legacy ? `${pack.block}\n\n${legacy}` : pack.block;
156
+ if (!degradedManifest)
157
+ return systemPrompt;
158
+ return systemPrompt && systemPrompt.trim().length > 0
159
+ ? `${systemPrompt}\n\n${degradedManifest}`
160
+ : degradedManifest;
161
+ }
162
+ if (pack.block !== '') {
163
+ return replaceMemoryBlock(systemPrompt ?? '', pack.block);
164
+ }
165
+ const legacyManifest = await deps.buildLegacyManifest();
166
+ if (!legacyManifest)
167
+ return systemPrompt;
168
+ return systemPrompt && systemPrompt.trim().length > 0
169
+ ? `${systemPrompt}\n\n${legacyManifest}`
170
+ : legacyManifest;
171
+ }
@@ -0,0 +1,4 @@
1
+ export type ModelAgent = 'claude' | 'codex' | 'kimi' | 'gemini' | 'grok';
2
+ /** Returns the model if it plausibly belongs to `agent`, else undefined
3
+ * (= use the agent's default). Blank/null → undefined. */
4
+ export declare function modelForAgent(agent: ModelAgent, model: string | null | undefined): string | undefined;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ // Guard against stale cross-agent model ids.
3
+ //
4
+ // Chats store a `model` override. When the user switches a chat's AGENT
5
+ // (claude → grok etc.), the stored model tags along and the daemon would
6
+ // pass e.g. `grok --model claude-sonnet-4-5`, which exits 1 ("unknown
7
+ // model id") and the whole turn fails with an opaque error.
8
+ //
9
+ // Every provider's model ids carry a recognisable family prefix, so we
10
+ // drop any override that clearly belongs to a different agent and let the
11
+ // CLI use its own default — a working reply on the agent's default model
12
+ // beats a dead chat. Unknown/future ids that DO match the family pass
13
+ // through untouched; the CLI remains the source of truth for validity.
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.modelForAgent = modelForAgent;
16
+ const FAMILY = {
17
+ claude: /^(claude|sonnet|opus|haiku|fable)/i,
18
+ codex: /^(gpt|o\d|codex)/i,
19
+ kimi: /^(kimi|moonshot)/i,
20
+ gemini: /^gemini/i,
21
+ grok: /^grok/i,
22
+ };
23
+ /** Returns the model if it plausibly belongs to `agent`, else undefined
24
+ * (= use the agent's default). Blank/null → undefined. */
25
+ function modelForAgent(agent, model) {
26
+ const m = (model ?? '').trim();
27
+ if (!m)
28
+ return undefined;
29
+ if (!FAMILY[agent].test(m)) {
30
+ console.log(`[model-filter] dropping model '${m}' — not a ${agent} model (stale from an agent switch); using ${agent}'s default`);
31
+ return undefined;
32
+ }
33
+ return m;
34
+ }
@@ -0,0 +1,18 @@
1
+ export interface HealthSignals {
2
+ /** Percentage of the scratch volume used (0-100), or null if unreadable. */
3
+ diskUsedPct: number | null;
4
+ }
5
+ export interface HealthVerdict {
6
+ healthy: boolean;
7
+ blockers: Array<{
8
+ code: string;
9
+ message: string;
10
+ }>;
11
+ }
12
+ export interface HealthOpts {
13
+ /** Refuse pickup at/above this used-% on the scratch volume. */
14
+ diskCriticalPct?: number;
15
+ }
16
+ export declare function evaluateNodeHealth(signals: HealthSignals, opts?: HealthOpts): HealthVerdict;
17
+ /** Read used-% of the volume holding `root` via statfs. null if unreadable. */
18
+ export declare function readDiskUsedPct(root: string): number | null;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ // Pre-flight node health used to gate request pickup. The point is NOT to
3
+ // diagnose everything — it's to stop a node that physically cannot finish a
4
+ // run from ACCEPTING it. When the disk is full, every codex/claude spawn on
5
+ // this node dies with ENOSPC ("No space left on device") — 57+ failures in
6
+ // prod. If we refuse the pickup instead, the request stays `pending` and the
7
+ // server-side failover hands it to a healthy backup, turning a guaranteed
8
+ // failure into a clean failover.
9
+ //
10
+ // Fail-open is the rule: we only block on a signal we can actually read and
11
+ // that is unambiguously fatal. Missing/unreadable info never blocks.
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.evaluateNodeHealth = evaluateNodeHealth;
47
+ exports.readDiskUsedPct = readDiskUsedPct;
48
+ const fs = __importStar(require("fs"));
49
+ const DEFAULT_DISK_CRITICAL_PCT = 97;
50
+ function evaluateNodeHealth(signals, opts = {}) {
51
+ const blockers = [];
52
+ const critical = opts.diskCriticalPct ?? DEFAULT_DISK_CRITICAL_PCT;
53
+ if (signals.diskUsedPct != null && signals.diskUsedPct >= critical) {
54
+ blockers.push({
55
+ code: 'disk_critical',
56
+ message: `scratch disk ${signals.diskUsedPct.toFixed(0)}% full (>= ${critical}%) — refusing new runs so they fail over to a healthy node`,
57
+ });
58
+ }
59
+ return { healthy: blockers.length === 0, blockers };
60
+ }
61
+ /** Read used-% of the volume holding `root` via statfs. null if unreadable. */
62
+ function readDiskUsedPct(root) {
63
+ try {
64
+ const st = fs.statfsSync(root);
65
+ const blocks = Number(st.blocks);
66
+ const bavail = Number(st.bavail);
67
+ if (!Number.isFinite(blocks) || blocks <= 0)
68
+ return null;
69
+ return (1 - bavail / blocks) * 100;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
@@ -0,0 +1,20 @@
1
+ interface FinalizeResult {
2
+ runtime_id: string;
3
+ daemon_token: string;
4
+ }
5
+ /**
6
+ * Request a fresh 5-letter pairing code.
7
+ * Returns the code; the caller is responsible for displaying it.
8
+ */
9
+ export declare function requestPairCode(): Promise<string>;
10
+ /**
11
+ * Block until the user has claimed the code at vault.add.ai or until the
12
+ * code expires. Returns the runtime_id + raw daemon_token on success.
13
+ * Saves the pairing to ~/.ainode/state.json.
14
+ */
15
+ export declare function awaitPairing(code: string): Promise<FinalizeResult>;
16
+ /**
17
+ * Print the human-facing instructions for a fresh pairing.
18
+ */
19
+ export declare function printPairingInstructions(code: string): void;
20
+ export {};