agent-nuvira 3.3.8 → 3.3.9

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 (257) hide show
  1. package/README.md +57 -50
  2. package/dist/agents/agents/planner.d.ts.map +1 -1
  3. package/dist/agents/agents/planner.js +41 -0
  4. package/dist/agents/agents/planner.js.map +1 -1
  5. package/dist/agents/agents/reasoner.d.ts.map +1 -1
  6. package/dist/agents/agents/reasoner.js +6 -0
  7. package/dist/agents/agents/reasoner.js.map +1 -1
  8. package/dist/agents/agents/runner.d.ts +6 -0
  9. package/dist/agents/agents/runner.d.ts.map +1 -1
  10. package/dist/agents/agents/runner.js +82 -5
  11. package/dist/agents/agents/runner.js.map +1 -1
  12. package/dist/agents/agents/writer-tool-calling.d.ts.map +1 -1
  13. package/dist/agents/agents/writer-tool-calling.js +5 -2
  14. package/dist/agents/agents/writer-tool-calling.js.map +1 -1
  15. package/dist/agents/agents/writer.d.ts.map +1 -1
  16. package/dist/agents/agents/writer.js +8 -4
  17. package/dist/agents/agents/writer.js.map +1 -1
  18. package/dist/agents/fanout-scheduler.d.ts +72 -0
  19. package/dist/agents/fanout-scheduler.d.ts.map +1 -0
  20. package/dist/agents/fanout-scheduler.js +100 -0
  21. package/dist/agents/fanout-scheduler.js.map +1 -0
  22. package/dist/agents/orchestrator.d.ts.map +1 -1
  23. package/dist/agents/orchestrator.js +30 -1
  24. package/dist/agents/orchestrator.js.map +1 -1
  25. package/dist/agents/plan-quality.d.ts +58 -0
  26. package/dist/agents/plan-quality.d.ts.map +1 -0
  27. package/dist/agents/plan-quality.js +152 -0
  28. package/dist/agents/plan-quality.js.map +1 -0
  29. package/dist/agents/prompt-assembly.d.ts +8 -0
  30. package/dist/agents/prompt-assembly.d.ts.map +1 -1
  31. package/dist/agents/prompt-assembly.js +18 -0
  32. package/dist/agents/prompt-assembly.js.map +1 -1
  33. package/dist/agents/step-handoff.d.ts +12 -2
  34. package/dist/agents/step-handoff.d.ts.map +1 -1
  35. package/dist/agents/step-handoff.js +65 -8
  36. package/dist/agents/step-handoff.js.map +1 -1
  37. package/dist/agents/tool-calling-agent.d.ts.map +1 -1
  38. package/dist/agents/tool-calling-agent.js +18 -0
  39. package/dist/agents/tool-calling-agent.js.map +1 -1
  40. package/dist/cli/cache.d.ts.map +1 -1
  41. package/dist/cli/cache.js +34 -4
  42. package/dist/cli/cache.js.map +1 -1
  43. package/dist/cli/chat.d.ts +6 -0
  44. package/dist/cli/chat.d.ts.map +1 -1
  45. package/dist/cli/chat.js +79 -18
  46. package/dist/cli/chat.js.map +1 -1
  47. package/dist/cli/cli-program.d.ts.map +1 -1
  48. package/dist/cli/cli-program.js +6 -0
  49. package/dist/cli/cli-program.js.map +1 -1
  50. package/dist/cli/config.d.ts +10 -0
  51. package/dist/cli/config.d.ts.map +1 -1
  52. package/dist/cli/config.js +179 -1
  53. package/dist/cli/config.js.map +1 -1
  54. package/dist/cli/continuity.d.ts +61 -0
  55. package/dist/cli/continuity.d.ts.map +1 -0
  56. package/dist/cli/continuity.js +112 -0
  57. package/dist/cli/continuity.js.map +1 -0
  58. package/dist/cli/dashboard.d.ts +3 -1
  59. package/dist/cli/dashboard.d.ts.map +1 -1
  60. package/dist/cli/dashboard.js +5 -16
  61. package/dist/cli/dashboard.js.map +1 -1
  62. package/dist/cli/doctor.d.ts.map +1 -1
  63. package/dist/cli/doctor.js +48 -1
  64. package/dist/cli/doctor.js.map +1 -1
  65. package/dist/cli/execute.d.ts +8 -0
  66. package/dist/cli/execute.d.ts.map +1 -1
  67. package/dist/cli/execute.js +34 -1
  68. package/dist/cli/execute.js.map +1 -1
  69. package/dist/cli/loop-executor.d.ts +24 -0
  70. package/dist/cli/loop-executor.d.ts.map +1 -1
  71. package/dist/cli/loop-executor.js +134 -13
  72. package/dist/cli/loop-executor.js.map +1 -1
  73. package/dist/cli/website.d.ts +25 -0
  74. package/dist/cli/website.d.ts.map +1 -0
  75. package/dist/cli/website.js +58 -0
  76. package/dist/cli/website.js.map +1 -0
  77. package/dist/config/manager.d.ts.map +1 -1
  78. package/dist/config/manager.js +35 -0
  79. package/dist/config/manager.js.map +1 -1
  80. package/dist/config/process-env.d.ts.map +1 -1
  81. package/dist/config/process-env.js +20 -0
  82. package/dist/config/process-env.js.map +1 -1
  83. package/dist/config/provider-env.d.ts +2 -1
  84. package/dist/config/provider-env.d.ts.map +1 -1
  85. package/dist/config/provider-env.js +19 -2
  86. package/dist/config/provider-env.js.map +1 -1
  87. package/dist/config/service-catalog.d.ts +81 -0
  88. package/dist/config/service-catalog.d.ts.map +1 -0
  89. package/dist/config/service-catalog.js +286 -0
  90. package/dist/config/service-catalog.js.map +1 -0
  91. package/dist/config/service-probe.d.ts +27 -0
  92. package/dist/config/service-probe.d.ts.map +1 -0
  93. package/dist/config/service-probe.js +104 -0
  94. package/dist/config/service-probe.js.map +1 -0
  95. package/dist/config/types.d.ts +63 -0
  96. package/dist/config/types.d.ts.map +1 -1
  97. package/dist/context/cache.d.ts +46 -2
  98. package/dist/context/cache.d.ts.map +1 -1
  99. package/dist/context/cache.js +85 -7
  100. package/dist/context/cache.js.map +1 -1
  101. package/dist/findings/verdicts.d.ts +59 -0
  102. package/dist/findings/verdicts.d.ts.map +1 -1
  103. package/dist/findings/verdicts.js +117 -0
  104. package/dist/findings/verdicts.js.map +1 -1
  105. package/dist/forwarded.d.ts +2 -0
  106. package/dist/forwarded.d.ts.map +1 -0
  107. package/dist/forwarded.js +2 -0
  108. package/dist/forwarded.js.map +1 -0
  109. package/dist/gateway/registry.d.ts.map +1 -1
  110. package/dist/gateway/registry.js +9 -0
  111. package/dist/gateway/registry.js.map +1 -1
  112. package/dist/learning/auto-router.d.ts +30 -0
  113. package/dist/learning/auto-router.d.ts.map +1 -1
  114. package/dist/learning/auto-router.js +69 -12
  115. package/dist/learning/auto-router.js.map +1 -1
  116. package/dist/learning/context-assembly.d.ts +37 -0
  117. package/dist/learning/context-assembly.d.ts.map +1 -0
  118. package/dist/learning/context-assembly.js +63 -0
  119. package/dist/learning/context-assembly.js.map +1 -0
  120. package/dist/learning/reasoning-trace.d.ts +26 -5
  121. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  122. package/dist/learning/reasoning-trace.js +21 -1
  123. package/dist/learning/reasoning-trace.js.map +1 -1
  124. package/dist/learning/session-digest.d.ts +78 -0
  125. package/dist/learning/session-digest.d.ts.map +1 -0
  126. package/dist/learning/session-digest.js +185 -0
  127. package/dist/learning/session-digest.js.map +1 -0
  128. package/dist/learning/session-recall.d.ts +135 -0
  129. package/dist/learning/session-recall.d.ts.map +1 -0
  130. package/dist/learning/session-recall.js +305 -0
  131. package/dist/learning/session-recall.js.map +1 -0
  132. package/dist/learning/session-store.d.ts +172 -0
  133. package/dist/learning/session-store.d.ts.map +1 -0
  134. package/dist/learning/session-store.js +424 -0
  135. package/dist/learning/session-store.js.map +1 -0
  136. package/dist/learning/step-checkpoint.d.ts +28 -0
  137. package/dist/learning/step-checkpoint.d.ts.map +1 -1
  138. package/dist/learning/step-checkpoint.js +90 -6
  139. package/dist/learning/step-checkpoint.js.map +1 -1
  140. package/dist/learning/working-state.d.ts +19 -0
  141. package/dist/learning/working-state.d.ts.map +1 -1
  142. package/dist/learning/working-state.js +34 -1
  143. package/dist/learning/working-state.js.map +1 -1
  144. package/dist/parity/drivers.d.ts.map +1 -1
  145. package/dist/parity/drivers.js +29 -0
  146. package/dist/parity/drivers.js.map +1 -1
  147. package/dist/parity/wire-fixtures.d.ts +88 -0
  148. package/dist/parity/wire-fixtures.d.ts.map +1 -0
  149. package/dist/parity/wire-fixtures.js +405 -0
  150. package/dist/parity/wire-fixtures.js.map +1 -0
  151. package/dist/tools/artifact-append.d.ts +23 -0
  152. package/dist/tools/artifact-append.d.ts.map +1 -1
  153. package/dist/tools/artifact-append.js +28 -0
  154. package/dist/tools/artifact-append.js.map +1 -1
  155. package/dist/tools/ask-user.d.ts +5 -0
  156. package/dist/tools/ask-user.d.ts.map +1 -1
  157. package/dist/tools/ask-user.js +51 -5
  158. package/dist/tools/ask-user.js.map +1 -1
  159. package/dist/tools/child-agent-entry.js +1 -0
  160. package/dist/tools/child-agent-entry.js.map +1 -1
  161. package/dist/tools/child-agent-runtime.d.ts +7 -0
  162. package/dist/tools/child-agent-runtime.d.ts.map +1 -1
  163. package/dist/tools/child-agent-runtime.js +116 -6
  164. package/dist/tools/child-agent-runtime.js.map +1 -1
  165. package/dist/tools/child-agent-worker.d.ts +15 -0
  166. package/dist/tools/child-agent-worker.d.ts.map +1 -0
  167. package/dist/tools/child-agent-worker.js.map +1 -0
  168. package/dist/tools/coding-tools.d.ts.map +1 -1
  169. package/dist/tools/coding-tools.js +11 -0
  170. package/dist/tools/coding-tools.js.map +1 -1
  171. package/dist/tools/finding-tool.d.ts +6 -1
  172. package/dist/tools/finding-tool.d.ts.map +1 -1
  173. package/dist/tools/finding-tool.js +15 -2
  174. package/dist/tools/finding-tool.js.map +1 -1
  175. package/dist/tools/followup-utils.d.ts +35 -0
  176. package/dist/tools/followup-utils.d.ts.map +1 -1
  177. package/dist/tools/followup-utils.js +36 -0
  178. package/dist/tools/followup-utils.js.map +1 -1
  179. package/dist/tools/loop-project-context.d.ts +4 -1
  180. package/dist/tools/loop-project-context.d.ts.map +1 -1
  181. package/dist/tools/loop-project-context.js +48 -1
  182. package/dist/tools/loop-project-context.js.map +1 -1
  183. package/dist/tools/loop-skill-hint.d.ts.map +1 -1
  184. package/dist/tools/loop-skill-hint.js +7 -0
  185. package/dist/tools/loop-skill-hint.js.map +1 -1
  186. package/dist/tools/modality/image-gen.d.ts +25 -6
  187. package/dist/tools/modality/image-gen.d.ts.map +1 -1
  188. package/dist/tools/modality/image-gen.js +46 -27
  189. package/dist/tools/modality/image-gen.js.map +1 -1
  190. package/dist/tools/modality/image-providers.d.ts +71 -0
  191. package/dist/tools/modality/image-providers.d.ts.map +1 -0
  192. package/dist/tools/modality/image-providers.js +277 -0
  193. package/dist/tools/modality/image-providers.js.map +1 -0
  194. package/dist/tools/registry.d.ts +10 -1
  195. package/dist/tools/registry.d.ts.map +1 -1
  196. package/dist/tools/registry.js +35 -8
  197. package/dist/tools/registry.js.map +1 -1
  198. package/dist/tools/run-cli.d.ts.map +1 -1
  199. package/dist/tools/run-cli.js +39 -1
  200. package/dist/tools/run-cli.js.map +1 -1
  201. package/dist/tools/run-terminal.d.ts.map +1 -1
  202. package/dist/tools/run-terminal.js +50 -6
  203. package/dist/tools/run-terminal.js.map +1 -1
  204. package/dist/tools/subagent-spawner.d.ts +8 -0
  205. package/dist/tools/subagent-spawner.d.ts.map +1 -1
  206. package/dist/tools/subagent-spawner.js +3 -0
  207. package/dist/tools/subagent-spawner.js.map +1 -1
  208. package/dist/tools/tool-loop.d.ts +55 -0
  209. package/dist/tools/tool-loop.d.ts.map +1 -1
  210. package/dist/tools/tool-loop.js +136 -0
  211. package/dist/tools/tool-loop.js.map +1 -1
  212. package/dist/tools/untrusted-content.d.ts +29 -0
  213. package/dist/tools/untrusted-content.d.ts.map +1 -0
  214. package/dist/tools/untrusted-content.js +53 -0
  215. package/dist/tools/untrusted-content.js.map +1 -0
  216. package/dist/tools/web-research.d.ts +76 -14
  217. package/dist/tools/web-research.d.ts.map +1 -1
  218. package/dist/tools/web-research.js +280 -35
  219. package/dist/tools/web-research.js.map +1 -1
  220. package/dist/utils/effect-verification.d.ts +115 -0
  221. package/dist/utils/effect-verification.d.ts.map +1 -0
  222. package/dist/utils/effect-verification.js +337 -0
  223. package/dist/utils/effect-verification.js.map +1 -0
  224. package/dist/utils/open-url.d.ts +16 -0
  225. package/dist/utils/open-url.d.ts.map +1 -0
  226. package/dist/utils/open-url.js +32 -0
  227. package/dist/utils/open-url.js.map +1 -0
  228. package/dist/utils/project-env.d.ts +146 -0
  229. package/dist/utils/project-env.d.ts.map +1 -0
  230. package/dist/utils/project-env.js +584 -0
  231. package/dist/utils/project-env.js.map +1 -0
  232. package/dist/utils/shell.d.ts +7 -0
  233. package/dist/utils/shell.d.ts.map +1 -1
  234. package/dist/utils/shell.js +2 -0
  235. package/dist/utils/shell.js.map +1 -1
  236. package/dist/web-dashboard/chat-console.d.ts +2 -0
  237. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  238. package/dist/web-dashboard/chat-console.js +7 -0
  239. package/dist/web-dashboard/chat-console.js.map +1 -1
  240. package/dist/web-dashboard/server.d.ts +3 -1
  241. package/dist/web-dashboard/server.d.ts.map +1 -1
  242. package/dist/web-dashboard/server.js +534 -8
  243. package/dist/web-dashboard/server.js.map +1 -1
  244. package/dist/web-dashboard/src/types.d.ts +150 -1
  245. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  246. package/dist/web-dashboard/workspace-guard.d.ts +40 -0
  247. package/dist/web-dashboard/workspace-guard.d.ts.map +1 -0
  248. package/dist/web-dashboard/workspace-guard.js +92 -0
  249. package/dist/web-dashboard/workspace-guard.js.map +1 -0
  250. package/package.json +3 -1
  251. package/src/web-dashboard/public/assets/index-Beportyl.js +207 -0
  252. package/src/web-dashboard/public/assets/index-Beportyl.js.map +1 -0
  253. package/src/web-dashboard/public/assets/index-gtQyg9nm.css +1 -0
  254. package/src/web-dashboard/public/index.html +2 -2
  255. package/src/web-dashboard/public/assets/index-J3sGSCLr.js +0 -207
  256. package/src/web-dashboard/public/assets/index-J3sGSCLr.js.map +0 -1
  257. package/src/web-dashboard/public/assets/index-u7mu-fxK.css +0 -1
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Session recall (Phase 4 follow-on) — SEMANTIC recall across past asks.
3
+ *
4
+ * WHY THIS EXISTS, AND WHY IT IS OPT-IN.
5
+ * The deterministic session digest (`session-digest.ts`) shows the LAST few asks
6
+ * in a project. That is good for continuity, but it is recency-based: an ask from
7
+ * three weeks ago about the SAME subject is invisible if ten unrelated asks
8
+ * happened since. This module adds *semantic* recall — embed each finished ask,
9
+ * and at the start of a new turn retrieve the past asks that mean the same thing,
10
+ * above an explicit cosine THRESHOLD, so relevance is by meaning rather than by
11
+ * a literal token overlap (`goalsLookSame`) or by recency.
12
+ *
13
+ * It is deliberately OFF unless asked for (`NUVIRA_SESSION_RECALL=1`). Semantics
14
+ * introduce an embedding dependency whose tiers can be slow to fail over; a
15
+ * feature that makes an ordinary turn wait on a model download would be a worse
16
+ * bug than the recall it buys. So, exactly like `--resume`, an ordinary run pays
17
+ * nothing: no read, no embed, no query.
18
+ *
19
+ * WHY IT CANNOT RE-OPEN FALSE SUCCESS. A recalled ask is HISTORY, presented as
20
+ * however it ENDED (`acted` / `incomplete` / `failed` / `cancelled`) — never as a
21
+ * completion the current work inherits. The block says so outright, and nothing
22
+ * here is read to decide whether a step is done: completion still derives from
23
+ * artifacts on disk. A recalled "the build works" is a sentence the past model
24
+ * wrote, quoted, not a fact the framework adopts.
25
+ *
26
+ * Bounded + best-effort throughout: a fixed entry cap, a fixed top-k, a fixed
27
+ * block size, and every read/write/embed wrapped so a corrupt index or a missing
28
+ * embedding tier degrades to "no recall", never a broken turn.
29
+ */
30
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
31
+ import { join, resolve } from 'node:path';
32
+ import { envBuff, resolveNuviraHome } from '../config/paths.js';
33
+ import { embed } from '../memory/embedder.js';
34
+ import { cosineSimilarity } from '../memory/vector-store.js';
35
+ /** The environment key that turns semantic recall on (`1`/`true`/`yes`). */
36
+ export const SESSION_RECALL_ENV = 'NUVIRA_SESSION_RECALL';
37
+ /** Cosine floor a past ask must clear to be recalled (0-1). */
38
+ export const DEFAULT_RECALL_THRESHOLD = 0.5;
39
+ /** How many past asks to surface at most. */
40
+ export const DEFAULT_RECALL_TOP_K = 3;
41
+ /** Hard cap on the index (oldest dropped) — recall must never grow unbounded. */
42
+ export const MAX_INDEX_ENTRIES = 500;
43
+ /** Hard cap on the rendered block — it rides in prompts. */
44
+ export const MAX_RECALL_BLOCK_CHARS = 1_200;
45
+ const CURRENT_VERSION = 1;
46
+ // ─── Enablement ─────────────────────────────────────────────────────────────
47
+ /** Words a reader treats as OFF (same vocabulary the process-env page uses). */
48
+ const OFF_WORDS = new Set(['0', 'false', 'off', 'no']);
49
+ /**
50
+ * Is semantic recall on? DEFAULT ON — like checkpointing, an operator can turn
51
+ * it off, but a fresh install gets the feature rather than a switch that does
52
+ * nothing until someone reads the docs. `0`/`false`/`off`/`no` turn it off;
53
+ * unset (or any other value) leaves it ON.
54
+ */
55
+ export function sessionRecallEnabled(env = process.env) {
56
+ const raw = env[SESSION_RECALL_ENV];
57
+ if (typeof raw !== 'string')
58
+ return true;
59
+ return !OFF_WORDS.has(raw.trim().toLowerCase());
60
+ }
61
+ // Keep the repo's env indirection working too (`NUVIRA_` then `BUFF_`).
62
+ function recallEnabledViaHelper() {
63
+ const raw = envBuff('SESSION_RECALL');
64
+ if (typeof raw !== 'string')
65
+ return undefined;
66
+ return !OFF_WORDS.has(raw.trim().toLowerCase());
67
+ }
68
+ /**
69
+ * Resolve whether semantic recall is on, with the repo's precedence:
70
+ * an explicit FLAG wins, then the environment, then config, then the default
71
+ * (ON). This is the single place the decision is made, so the CLI, the loop and
72
+ * the dashboard cannot disagree.
73
+ */
74
+ export function resolveSessionRecall(input = {}) {
75
+ if (input.flag !== undefined)
76
+ return input.flag;
77
+ const viaEnv = recallEnabledViaHelper();
78
+ if (viaEnv !== undefined)
79
+ return viaEnv;
80
+ const cfg = input.configManager?.getAll().memory?.sessionRecall;
81
+ return cfg !== false;
82
+ }
83
+ function enabled() {
84
+ return resolveSessionRecall();
85
+ }
86
+ // ─── Storage ────────────────────────────────────────────────────────────────
87
+ function memoryDir() {
88
+ return envBuff('MEMORY_DIR') || join(resolveNuviraHome(), 'memory');
89
+ }
90
+ function indexPath() {
91
+ return join(memoryDir(), 'session-recall-index.json');
92
+ }
93
+ function normalizeProjectPath(projectPath) {
94
+ try {
95
+ return resolve(projectPath);
96
+ }
97
+ catch {
98
+ return projectPath;
99
+ }
100
+ }
101
+ function readIndex() {
102
+ try {
103
+ if (!existsSync(indexPath()))
104
+ return { version: CURRENT_VERSION, entries: [] };
105
+ const data = JSON.parse(readFileSync(indexPath(), 'utf-8'));
106
+ if (!data || typeof data !== 'object' || !Array.isArray(data.entries)) {
107
+ return { version: CURRENT_VERSION, entries: [] };
108
+ }
109
+ const entries = data.entries.filter(isRecallEntry);
110
+ return { version: CURRENT_VERSION, entries };
111
+ }
112
+ catch {
113
+ return { version: CURRENT_VERSION, entries: [] };
114
+ }
115
+ }
116
+ function isRecallEntry(value) {
117
+ if (!value || typeof value !== 'object')
118
+ return false;
119
+ const e = value;
120
+ return (typeof e.id === 'string' &&
121
+ typeof e.projectPath === 'string' &&
122
+ typeof e.goal === 'string' &&
123
+ Array.isArray(e.vector) &&
124
+ e.vector.every((n) => typeof n === 'number' && Number.isFinite(n)));
125
+ }
126
+ function writeIndex(index) {
127
+ try {
128
+ mkdirSync(memoryDir(), { recursive: true });
129
+ writeFileSync(indexPath(), JSON.stringify(index, null, 2), 'utf-8');
130
+ return true;
131
+ }
132
+ catch {
133
+ return false;
134
+ }
135
+ }
136
+ function isZeroVector(vector) {
137
+ return vector.length === 0 || vector.every((n) => n === 0);
138
+ }
139
+ // ─── Indexing ───────────────────────────────────────────────────────────────
140
+ /** A stable id for one ask in a project, so re-indexing a turn overwrites it. */
141
+ function entryId(projectPath, goal, savedAt) {
142
+ return `${normalizeProjectPath(projectPath)}#${savedAt}#${goal.slice(0, 48)}`;
143
+ }
144
+ /**
145
+ * Index one finished ask for later semantic recall. Returns whether an entry was
146
+ * written. Best-effort and NO-OP when recall is off or embeddings are
147
+ * unavailable (a zero vector is never stored — it would only ever be a
148
+ * meaningless match).
149
+ */
150
+ export async function indexSessionTurn(input, opts = {}) {
151
+ const on = opts.enabled ?? enabled();
152
+ if ((!opts.force && !on) || !input.goal.trim())
153
+ return false;
154
+ const embedFn = opts.embedFn ?? ((t) => embed(t));
155
+ let vector;
156
+ try {
157
+ vector = await embedFn(input.goal);
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ if (isZeroVector(vector))
163
+ return false;
164
+ const savedAt = input.savedAt ?? Date.now();
165
+ const entry = {
166
+ id: entryId(input.projectPath, input.goal, savedAt),
167
+ projectPath: normalizeProjectPath(input.projectPath),
168
+ goal: input.goal.slice(0, 240),
169
+ outcome: (input.outcome ?? 'unknown').slice(0, 24),
170
+ savedAt,
171
+ vector,
172
+ };
173
+ const index = readIndex();
174
+ const entries = [...index.entries.filter((e) => e.id !== entry.id), entry];
175
+ // Oldest-first eviction past the cap.
176
+ if (entries.length > MAX_INDEX_ENTRIES)
177
+ entries.splice(0, entries.length - MAX_INDEX_ENTRIES);
178
+ return writeIndex({ version: CURRENT_VERSION, entries });
179
+ }
180
+ // ─── Recall ─────────────────────────────────────────────────────────────────
181
+ /**
182
+ * The past asks semantically closest to `query`, above the threshold.
183
+ *
184
+ * `scope: 'project'` (default) limits recall to the current project directory —
185
+ * the same scoping the rest of the continuity stack uses, so a new task is not
186
+ * handed another project's history. `scope: 'all'` searches every project and
187
+ * names each hit's project in the block.
188
+ */
189
+ export async function recallPastSessions(query, opts = {}) {
190
+ if (!query.trim())
191
+ return [];
192
+ const on = opts.enabled ?? enabled();
193
+ if (!opts.force && !on)
194
+ return [];
195
+ const index = readIndex();
196
+ if (index.entries.length === 0)
197
+ return [];
198
+ const scope = opts.scope ?? 'project';
199
+ const wanted = opts.projectPath ? normalizeProjectPath(opts.projectPath) : '';
200
+ const candidates = scope === 'all' || !wanted ? index.entries : index.entries.filter((e) => e.projectPath === wanted);
201
+ if (candidates.length === 0)
202
+ return [];
203
+ const embedFn = opts.embedFn ?? ((t) => embed(t));
204
+ let queryVector;
205
+ try {
206
+ queryVector = await embedFn(query);
207
+ }
208
+ catch {
209
+ return [];
210
+ }
211
+ if (isZeroVector(queryVector))
212
+ return [];
213
+ const threshold = opts.threshold ?? DEFAULT_RECALL_THRESHOLD;
214
+ const topK = Math.max(1, opts.topK ?? DEFAULT_RECALL_TOP_K);
215
+ const hits = [];
216
+ for (const entry of candidates) {
217
+ let similarity;
218
+ try {
219
+ similarity = cosineSimilarity(queryVector, entry.vector);
220
+ }
221
+ catch {
222
+ continue;
223
+ }
224
+ if (!Number.isFinite(similarity) || similarity < threshold)
225
+ continue;
226
+ hits.push({ entry, similarity });
227
+ }
228
+ hits.sort((a, b) => b.similarity - a.similarity);
229
+ return hits.slice(0, topK);
230
+ }
231
+ /**
232
+ * Render recall hits as a bounded, model-readable block ('' when there are none).
233
+ *
234
+ * The header is load-bearing: it states that these are PAST asks — history for
235
+ * context only, NOT a status — so a recalled line cannot be read as proof the
236
+ * current work is done. Each hit carries how it ENDED and its project when the
237
+ * recall crossed projects.
238
+ */
239
+ export function formatSessionRecall(hits, opts = {}) {
240
+ if (hits.length === 0)
241
+ return '';
242
+ const now = opts.now ?? Date.now();
243
+ const projectScoped = opts.projectPath !== undefined;
244
+ const lines = [
245
+ '[Semantically similar PAST asks — history for context only. This is NOT a status of the current work; verify artifacts on disk before assuming anything is done.]',
246
+ ];
247
+ for (const { entry, similarity } of hits) {
248
+ const age = relativeAge(entry.savedAt, now);
249
+ const where = projectScoped ? '' : ` [${shortenPath(entry.projectPath)}]`;
250
+ lines.push(`• (${Math.round(similarity * 100)}% match)${where} ${age} — ${entry.outcome}: "${entry.goal}"`);
251
+ }
252
+ let block = lines.join('\n');
253
+ if (block.length > MAX_RECALL_BLOCK_CHARS) {
254
+ block = `${block.slice(0, MAX_RECALL_BLOCK_CHARS)}\n[recall truncated to fit the context budget]`;
255
+ }
256
+ return block;
257
+ }
258
+ function relativeAge(ts, now) {
259
+ const mins = Math.max(0, Math.round((now - ts) / 60_000));
260
+ if (mins < 1)
261
+ return 'just now';
262
+ if (mins < 60)
263
+ return `${mins}m ago`;
264
+ const hours = Math.round(mins / 60);
265
+ if (hours < 24)
266
+ return `${hours}h ago`;
267
+ return `${Math.round(hours / 24)}d ago`;
268
+ }
269
+ function shortenPath(p) {
270
+ const parts = p.split(/[\\/]/).filter(Boolean);
271
+ return parts.slice(-2).join('/') || p;
272
+ }
273
+ /**
274
+ * List every indexed past ask, newest first. Best-effort. The vectors are part of
275
+ * each entry; a caller that only needs a summary maps them away.
276
+ */
277
+ export function listRecallEntries() {
278
+ return readIndex().entries.slice().sort((a, b) => b.savedAt - a.savedAt);
279
+ }
280
+ /** Remove one indexed past ask by id. Returns whether it was present. */
281
+ export function removeRecallEntry(id) {
282
+ try {
283
+ const index = readIndex();
284
+ const next = index.entries.filter((e) => e.id !== id);
285
+ if (next.length === index.entries.length)
286
+ return false;
287
+ return writeIndex({ version: CURRENT_VERSION, entries: next });
288
+ }
289
+ catch {
290
+ return false;
291
+ }
292
+ }
293
+ /** Remove the recall index (used by `nuvira retrieval clear`-style resets / tests). */
294
+ export function clearSessionRecallIndex() {
295
+ try {
296
+ if (!existsSync(indexPath()))
297
+ return false;
298
+ rmSync(indexPath(), { force: true });
299
+ return true;
300
+ }
301
+ catch {
302
+ return false;
303
+ }
304
+ }
305
+ //# sourceMappingURL=session-recall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-recall.js","sourceRoot":"","sources":["../../src/learning/session-recall.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * WS5 (#27) — Phase 4b/4c: the PERSISTENT SESSION STORE (the L2 layer).
3
+ *
4
+ * WHY THIS EXISTS. The resume ledger (`step-checkpoint.ts`) makes a re-run cheap
5
+ * by replaying unchanged MODEL CALLS, and the task checkpoints
6
+ * (`agents/checkpoint-store.ts`) skip COMPLETED pipeline tasks. Both are keyed by
7
+ * what a run FINISHED. Neither survives the one failure a long-running agent
8
+ * actually hits: the PROCESS dies mid-turn (a closed laptop, a killed terminal,
9
+ * a quota-killed provider). At that instant the conversation lived only in
10
+ * memory, and the next process starts cold — it re-derives the whole context and
11
+ * re-pays for the steps that were already done.
12
+ *
13
+ * WHAT IT STORES, AND WHAT IT REFUSES TO STORE. At every step boundary the loop
14
+ * hands over its live `ToolMessage[]` thread plus a small accumulator (steps,
15
+ * successful tools, mutated paths). The store keeps a bounded, REDACTED copy on
16
+ * disk. It is a transcript — nothing more. It does NOT record a completion
17
+ * state, an outcome, or any verdict: completion is still derived from the
18
+ * filesystem and the run's own facts (see the Part 1/Part 2 lesson). Re-importing
19
+ * the transcript gives the next process the CONVERSATION, not a claim that the
20
+ * work is done.
21
+ *
22
+ * WHY THE HEAD IS REPLACED ON REHYDRATION. The first messages of a loop thread
23
+ * are the system prompt (tool contract) and the `[Project context]` block. Those
24
+ * are reconstructible, and they DRIFT between runs (the file tree, the git
25
+ * digest). Persisting them would both age and bloat the record. So the store
26
+ * counts the leading head messages at save time (`headLength`) and the caller
27
+ * re-injects a FRESH head over the stored conversation tail on resume: the
28
+ * contract is current, the history is intact.
29
+ *
30
+ * WHY A COMPLETED SESSION IS NOT RESUMABLE. If a turn ran to a clean end, its
31
+ * transcript is history — re-feeding a finished conversation would have the model
32
+ * continue a conversation that is over, and risk narrating a stale "done". So
33
+ * `finish()` marks a snapshot closed, and the resumable lookup skips closed
34
+ * snapshots. The transcript is still kept (bounded) for a human to read; it is
35
+ * simply not replayed.
36
+ *
37
+ * WHY NOTHING HAPPENS WITHOUT A REQUEST. Opening the store is the ONLY thing that
38
+ * reads or writes the session directory. An ordinary run never touches it, pays
39
+ * no filesystem cost, and cannot inherit another run's conversation.
40
+ *
41
+ * Everything here is best-effort: a corrupt record is a MISS, never a crash, and
42
+ * a failed write reports (or stays silent) without breaking the turn that asked.
43
+ */
44
+ import type { ToolMessage } from '../inference/interface.js';
45
+ /** The environment key a deployment uses to turn session persistence OFF. */
46
+ export declare const SESSION_ENABLE_ENV = "NUVIRA_SESSION_STORE";
47
+ /**
48
+ * Is session persistence on? DEFAULT ON — like checkpointing, a fresh install
49
+ * gets continuity and an operator can turn it off. `0`/`false`/`off`/`no` turn
50
+ * it off; unset (or any other value) leaves it ON.
51
+ */
52
+ export declare function sessionStoreEnabled(env?: NodeJS.ProcessEnv): boolean;
53
+ /** The config/env shape a resolver reads (structural, so no import cycle). */
54
+ export interface StoreConfigSource {
55
+ getAll(): {
56
+ memory?: {
57
+ sessionStore?: boolean;
58
+ };
59
+ };
60
+ }
61
+ /**
62
+ * Resolve whether session persistence is on, with the repo's precedence: an
63
+ * explicit FLAG wins, then the environment, then config, then the default (ON).
64
+ * One place decides, so the CLI, the loop and the dashboard cannot disagree.
65
+ */
66
+ export declare function resolveSessionStore(input?: {
67
+ flag?: boolean;
68
+ configManager?: StoreConfigSource | null;
69
+ }): boolean;
70
+ /** Bound on stored conversation messages — the most recent N are kept. */
71
+ export declare const MAX_SESSION_MESSAGES = 48;
72
+ /** Bound on a single stored message's content (the rest is clipped). */
73
+ export declare const MAX_MESSAGE_CHARS = 8000;
74
+ /** The accumulators the loop reports at a step boundary. */
75
+ export interface SessionAccumulators {
76
+ /** Steps completed so far this turn. */
77
+ steps: number;
78
+ /** Tool names that ran successfully (in order). */
79
+ successfulTools: string[];
80
+ /** Paths mutated successfully (in order). */
81
+ mutatedPaths: string[];
82
+ /** Tool names that errored, when the caller tracks them. */
83
+ erroredTools?: string[];
84
+ }
85
+ /** A persisted session snapshot: the conversation tail plus the accumulators. */
86
+ export interface SessionSnapshot {
87
+ id: string;
88
+ goal: string;
89
+ cwd: string;
90
+ savedAt: number;
91
+ /** Monotonic save counter, so a reader can tell updates apart. */
92
+ revision: number;
93
+ /** False once the turn ran to a clean end (and is therefore not resumable). */
94
+ open: boolean;
95
+ /** Leading thread messages that are reconstructible and must be replaced. */
96
+ headLength: number;
97
+ messages: ToolMessage[];
98
+ accumulators: SessionAccumulators;
99
+ }
100
+ /** Read a snapshot, best-effort. A corrupt record is a miss, never a crash. */
101
+ export declare function loadSessionSnapshot(id: string): SessionSnapshot | null;
102
+ /**
103
+ * The writer a loop threads through its step boundaries.
104
+ *
105
+ * `save` is called once per completed step; `finish` once, when the turn ends
106
+ * cleanly. Both are best-effort — a read-only home directory degrades to "no
107
+ * continuity", never to a failed turn.
108
+ */
109
+ export interface SessionStore {
110
+ readonly id: string;
111
+ /** Snapshot the current thread + accumulators at a step boundary. */
112
+ save(thread: readonly ToolMessage[], accumulators: SessionAccumulators): void;
113
+ /** Mark the turn finished so the transcript is history, not a resume point. */
114
+ finish(thread?: readonly ToolMessage[], accumulators?: SessionAccumulators): void;
115
+ /** Whether the most recent write reached the disk. */
116
+ lastSaved(): boolean;
117
+ }
118
+ export declare function openSession(input: {
119
+ goal: string;
120
+ cwd: string;
121
+ id?: string;
122
+ }): SessionStore;
123
+ /**
124
+ * The session id for this ask, in this directory, possibly WORDED DIFFERENTLY.
125
+ *
126
+ * Same contract as `resolveRecordIdFor` in the resume ledger: the exact id if a
127
+ * snapshot exists, else the newest OPEN snapshot in this directory whose goal
128
+ * `goalsLookSame`. A snapshot for a different ask is never returned — the caller
129
+ * would otherwise rehydrate a conversation it never had.
130
+ */
131
+ export declare function resolveSessionIdFor(goal: string, cwd: string, options?: {
132
+ onlyOpen?: boolean;
133
+ }): string;
134
+ /**
135
+ * The resumable snapshot for this ask, or null when there is nothing to resume.
136
+ *
137
+ * Only OPEN snapshots qualify (see the module docstring): a transcript from a
138
+ * turn that ended cleanly is history, and re-feeding it would have the model
139
+ * continue a finished conversation.
140
+ */
141
+ export declare function findResumableSessionFor(goal: string, cwd: string): SessionSnapshot | null;
142
+ /**
143
+ * Rebuild a loop thread from a stored snapshot and a FRESH head.
144
+ *
145
+ * The fresh head (system prompt, current project context) replaces the stored
146
+ * head; the stored conversation tail — the goal and everything done after it —
147
+ * is preserved. The result is what the next process loops over: it already knows
148
+ * what the dead process did, so those steps are neither re-run nor re-paid.
149
+ */
150
+ export declare function rehydrateThread(head: readonly ToolMessage[], snapshot: SessionSnapshot): ToolMessage[];
151
+ /** A bounded, human-facing line describing a resumable snapshot. */
152
+ export declare function formatSessionResume(snapshot: SessionSnapshot): string;
153
+ /**
154
+ * List every stored session snapshot, newest first. Best-effort: a corrupt or
155
+ * unreadable record is skipped, never thrown.
156
+ */
157
+ export declare function listSessionSnapshots(): SessionSnapshot[];
158
+ /** Remove one snapshot. Returns whether a file was removed. */
159
+ export declare function clearSession(id: string): boolean;
160
+ /**
161
+ * Bound the session directory: drop snapshots older than `maxAgeMs` and keep at
162
+ * most `maxCount` (newest first). Returns how many files were removed.
163
+ *
164
+ * Best-effort and never throws — a prune that fails leaves the records (a disk
165
+ * full of old transcripts is a smaller problem than a broken turn).
166
+ */
167
+ export declare function pruneSessions(options?: {
168
+ maxAgeMs?: number;
169
+ maxCount?: number;
170
+ now?: number;
171
+ }): number;
172
+ //# sourceMappingURL=session-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/learning/session-store.ts"],"names":[],"mappings":"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"}