agent-nuvira 3.1.3 → 3.3.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 (164) hide show
  1. package/dist/agents/agents/reasoner.d.ts +8 -0
  2. package/dist/agents/agents/reasoner.d.ts.map +1 -1
  3. package/dist/agents/agents/reasoner.js +53 -2
  4. package/dist/agents/agents/reasoner.js.map +1 -1
  5. package/dist/agents/agents/writer.d.ts +24 -0
  6. package/dist/agents/agents/writer.d.ts.map +1 -1
  7. package/dist/agents/agents/writer.js +200 -0
  8. package/dist/agents/agents/writer.js.map +1 -1
  9. package/dist/agents/composite-plan.d.ts +143 -0
  10. package/dist/agents/composite-plan.d.ts.map +1 -0
  11. package/dist/agents/composite-plan.js +399 -0
  12. package/dist/agents/composite-plan.js.map +1 -0
  13. package/dist/agents/long-form-plan.d.ts +156 -0
  14. package/dist/agents/long-form-plan.d.ts.map +1 -0
  15. package/dist/agents/long-form-plan.js +274 -0
  16. package/dist/agents/long-form-plan.js.map +1 -0
  17. package/dist/agents/orchestrator.d.ts +107 -0
  18. package/dist/agents/orchestrator.d.ts.map +1 -1
  19. package/dist/agents/orchestrator.js +501 -35
  20. package/dist/agents/orchestrator.js.map +1 -1
  21. package/dist/agents/prompt-assembly.d.ts +8 -0
  22. package/dist/agents/prompt-assembly.d.ts.map +1 -1
  23. package/dist/agents/prompt-assembly.js +17 -0
  24. package/dist/agents/prompt-assembly.js.map +1 -1
  25. package/dist/cli/chat.d.ts +33 -0
  26. package/dist/cli/chat.d.ts.map +1 -1
  27. package/dist/cli/chat.js +161 -7
  28. package/dist/cli/chat.js.map +1 -1
  29. package/dist/cli/execute.d.ts +12 -0
  30. package/dist/cli/execute.d.ts.map +1 -1
  31. package/dist/cli/execute.js +150 -2
  32. package/dist/cli/execute.js.map +1 -1
  33. package/dist/cli/loop-executor.d.ts +17 -0
  34. package/dist/cli/loop-executor.d.ts.map +1 -1
  35. package/dist/cli/loop-executor.js +160 -1
  36. package/dist/cli/loop-executor.js.map +1 -1
  37. package/dist/cli/models.d.ts.map +1 -1
  38. package/dist/cli/models.js +10 -0
  39. package/dist/cli/models.js.map +1 -1
  40. package/dist/cli/trace.d.ts +19 -0
  41. package/dist/cli/trace.d.ts.map +1 -1
  42. package/dist/cli/trace.js +109 -2
  43. package/dist/cli/trace.js.map +1 -1
  44. package/dist/gateway/registry.d.ts +31 -0
  45. package/dist/gateway/registry.d.ts.map +1 -1
  46. package/dist/gateway/registry.js +162 -13
  47. package/dist/gateway/registry.js.map +1 -1
  48. package/dist/inference/model-entitlement.d.ts +46 -0
  49. package/dist/inference/model-entitlement.d.ts.map +1 -0
  50. package/dist/inference/model-entitlement.js +98 -0
  51. package/dist/inference/model-entitlement.js.map +1 -0
  52. package/dist/inference/tool-call-utils.d.ts +10 -0
  53. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  54. package/dist/inference/tool-call-utils.js +90 -0
  55. package/dist/inference/tool-call-utils.js.map +1 -1
  56. package/dist/learning/autonomy-policy.d.ts +370 -0
  57. package/dist/learning/autonomy-policy.d.ts.map +1 -0
  58. package/dist/learning/autonomy-policy.js +544 -0
  59. package/dist/learning/autonomy-policy.js.map +1 -0
  60. package/dist/learning/cost-tracker.d.ts +14 -0
  61. package/dist/learning/cost-tracker.d.ts.map +1 -1
  62. package/dist/learning/cost-tracker.js +22 -0
  63. package/dist/learning/cost-tracker.js.map +1 -1
  64. package/dist/learning/credential-fingerprint.d.ts +58 -0
  65. package/dist/learning/credential-fingerprint.d.ts.map +1 -0
  66. package/dist/learning/credential-fingerprint.js +126 -0
  67. package/dist/learning/credential-fingerprint.js.map +1 -0
  68. package/dist/learning/deliverable-class.d.ts +178 -0
  69. package/dist/learning/deliverable-class.d.ts.map +1 -0
  70. package/dist/learning/deliverable-class.js +504 -0
  71. package/dist/learning/deliverable-class.js.map +1 -0
  72. package/dist/learning/engine-router.d.ts +12 -1
  73. package/dist/learning/engine-router.d.ts.map +1 -1
  74. package/dist/learning/engine-router.js +28 -0
  75. package/dist/learning/engine-router.js.map +1 -1
  76. package/dist/learning/long-form.d.ts +252 -0
  77. package/dist/learning/long-form.d.ts.map +1 -0
  78. package/dist/learning/long-form.js +521 -0
  79. package/dist/learning/long-form.js.map +1 -0
  80. package/dist/learning/model-first-router.d.ts +17 -0
  81. package/dist/learning/model-first-router.d.ts.map +1 -1
  82. package/dist/learning/model-first-router.js +27 -0
  83. package/dist/learning/model-first-router.js.map +1 -1
  84. package/dist/learning/model-registry.d.ts +66 -4
  85. package/dist/learning/model-registry.d.ts.map +1 -1
  86. package/dist/learning/model-registry.js +67 -6
  87. package/dist/learning/model-registry.js.map +1 -1
  88. package/dist/learning/model-warmup.d.ts +97 -2
  89. package/dist/learning/model-warmup.d.ts.map +1 -1
  90. package/dist/learning/model-warmup.js +165 -60
  91. package/dist/learning/model-warmup.js.map +1 -1
  92. package/dist/learning/prompt-layers.d.ts +61 -0
  93. package/dist/learning/prompt-layers.d.ts.map +1 -0
  94. package/dist/learning/prompt-layers.js +140 -0
  95. package/dist/learning/prompt-layers.js.map +1 -0
  96. package/dist/learning/provider-limits.d.ts +66 -0
  97. package/dist/learning/provider-limits.d.ts.map +1 -0
  98. package/dist/learning/provider-limits.js +184 -0
  99. package/dist/learning/provider-limits.js.map +1 -0
  100. package/dist/learning/reasoning-trace.d.ts +125 -1
  101. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  102. package/dist/learning/reasoning-trace.js +85 -2
  103. package/dist/learning/reasoning-trace.js.map +1 -1
  104. package/dist/learning/resilient-call.d.ts +36 -1
  105. package/dist/learning/resilient-call.d.ts.map +1 -1
  106. package/dist/learning/resilient-call.js +80 -5
  107. package/dist/learning/resilient-call.js.map +1 -1
  108. package/dist/learning/unattended-job.d.ts +350 -0
  109. package/dist/learning/unattended-job.d.ts.map +1 -0
  110. package/dist/learning/unattended-job.js +636 -0
  111. package/dist/learning/unattended-job.js.map +1 -0
  112. package/dist/learning/unattended-progress.d.ts +95 -0
  113. package/dist/learning/unattended-progress.d.ts.map +1 -0
  114. package/dist/learning/unattended-progress.js +147 -0
  115. package/dist/learning/unattended-progress.js.map +1 -0
  116. package/dist/learning/working-state.d.ts +109 -0
  117. package/dist/learning/working-state.d.ts.map +1 -0
  118. package/dist/learning/working-state.js +244 -0
  119. package/dist/learning/working-state.js.map +1 -0
  120. package/dist/nlu/conversation-gate.d.ts +33 -0
  121. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  122. package/dist/nlu/conversation-gate.js +68 -3
  123. package/dist/nlu/conversation-gate.js.map +1 -1
  124. package/dist/tools/coding-tools.d.ts.map +1 -1
  125. package/dist/tools/coding-tools.js +95 -19
  126. package/dist/tools/coding-tools.js.map +1 -1
  127. package/dist/tools/edit-verification.d.ts +142 -0
  128. package/dist/tools/edit-verification.d.ts.map +1 -0
  129. package/dist/tools/edit-verification.js +262 -0
  130. package/dist/tools/edit-verification.js.map +1 -0
  131. package/dist/tools/git-tool.d.ts.map +1 -1
  132. package/dist/tools/git-tool.js +37 -4
  133. package/dist/tools/git-tool.js.map +1 -1
  134. package/dist/tools/registry.d.ts +30 -2
  135. package/dist/tools/registry.d.ts.map +1 -1
  136. package/dist/tools/registry.js +40 -11
  137. package/dist/tools/registry.js.map +1 -1
  138. package/dist/tools/run-cli.d.ts.map +1 -1
  139. package/dist/tools/run-cli.js +40 -13
  140. package/dist/tools/run-cli.js.map +1 -1
  141. package/dist/tools/run-terminal.d.ts +6 -1
  142. package/dist/tools/run-terminal.d.ts.map +1 -1
  143. package/dist/tools/run-terminal.js +158 -18
  144. package/dist/tools/run-terminal.js.map +1 -1
  145. package/dist/tools/tool-loop.d.ts +109 -0
  146. package/dist/tools/tool-loop.d.ts.map +1 -1
  147. package/dist/tools/tool-loop.js +362 -2
  148. package/dist/tools/tool-loop.js.map +1 -1
  149. package/dist/web-dashboard/chat-console.d.ts +8 -0
  150. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  151. package/dist/web-dashboard/chat-console.js +9 -0
  152. package/dist/web-dashboard/chat-console.js.map +1 -1
  153. package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
  154. package/dist/web-dashboard/chat-retry.js +10 -2
  155. package/dist/web-dashboard/chat-retry.js.map +1 -1
  156. package/dist/web-dashboard/server.d.ts.map +1 -1
  157. package/dist/web-dashboard/server.js +80 -8
  158. package/dist/web-dashboard/server.js.map +1 -1
  159. package/dist/web-dashboard/src/types.d.ts +81 -4
  160. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  161. package/package.json +1 -1
  162. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
  163. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
  164. package/src/web-dashboard/public/index.html +1 -1
@@ -0,0 +1,521 @@
1
+ /**
2
+ * Long-form execution — bounded units + a progress ledger for deliverables
3
+ * that are larger than one LLM generation (enterprise-grade hardening, G8).
4
+ *
5
+ * WHY THIS EXISTS (the WhatsApp story audit):
6
+ * The user asked for a 100-page story. Two facts made that ask IMPOSSIBLE for
7
+ * any of the 507 eligible models, independently of the model-shortage myth the
8
+ * failure message told:
9
+ *
10
+ * 1. The code writer generates with `maxTokens: 2048` — about 1,500 words,
11
+ * roughly 4–5 pages. A 100-page book is ~35,000 words, i.e. 25–40 times
12
+ * the largest artifact the pipeline could ever emit.
13
+ * 2. Nothing in the pipeline decomposed the ask. The plan was 4 steps, none
14
+ * of which produced prose, and no component counted words or pages, so
15
+ * there was no notion of "partially done" and nothing to resume.
16
+ *
17
+ * So the fix is structural, not a bigger prompt: split the work into bounded
18
+ * units that fit one generation, persist progress per unit, and report honest
19
+ * completion ("chapter 7 of 39"). This module owns the arithmetic and the
20
+ * ledger; the writer owns one unit at a time.
21
+ *
22
+ * WHY UNIT FILES, NOT APPENDS TO ONE FILE:
23
+ * The obvious design — append each chapter to `Mahagatha.md` — requires the
24
+ * writer to rewrite the whole file every unit (the FileChange contract is a
25
+ * full-content write). By chapter 35 that is ~35k words of input tokens per
26
+ * generation, which does not fit the context window it was going to be
27
+ * generated from. One file per unit keeps every generation bounded and makes
28
+ * the work genuinely resumable. The single document the user asked for is
29
+ * ASSEMBLED deterministically (pure string concatenation, no LLM, no cost)
30
+ * once the units are done.
31
+ *
32
+ * Storage: `~/.nuvira/memory/long-form.json`, keyed by project + document.
33
+ * Honours `NUVIRA_MEMORY_DIR` / `BUFF_MEMORY_DIR` like the other ledgers.
34
+ * Every write is best-effort — the ledger must NEVER break a turn.
35
+ */
36
+ import { envBuff, resolveNuviraHome } from '../config/paths.js';
37
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
38
+ import { dirname, join, relative, resolve } from 'node:path';
39
+ // ─── Constants ──────────────────────────────────────────────────────────────
40
+ /**
41
+ * Words per printed page. 350 is the standard estimate for a 12pt manuscript
42
+ * page (double-spaced manuscript convention is ~250; a typeset book page is
43
+ * ~350–400). Erring low keeps the plan honest: a "100-page" target that lands
44
+ * at 35,000 words is comfortably 100 pages, never short of it.
45
+ */
46
+ export const WORDS_PER_PAGE = 350;
47
+ /**
48
+ * Words per generation unit. Sized to land inside a raised output cap with
49
+ * headroom for the model to finish a sentence: 900 words ≈ 1,400 tokens,
50
+ * comfortably under a 2,048-token floor and well within a 4k–8k cap. Larger
51
+ * units risk truncation, which is exactly how the original writer failed
52
+ * ("no parseable output" on a truncated response).
53
+ */
54
+ export const WORDS_PER_GENERATION = 900;
55
+ /** Words per chapter when the user counts chapters instead of pages. */
56
+ export const WORDS_PER_CHAPTER = 2_500;
57
+ /**
58
+ * Units planned per turn. A 100-page book is ~39 units; planning all of them
59
+ * as orchestrator steps would build a plan the run could never finish, and a
60
+ * single-run attempt is precisely the "bigger ask" the agent kept losing.
61
+ * Bounded batches let each turn end with CONCRETE delivered text and a
62
+ * recorded progress line, and the next turn resumes where it stopped.
63
+ */
64
+ export const MAX_UNITS_PER_RUN = 4;
65
+ /** Largest section count we will ever plan (sanity bound on the ledger). */
66
+ const MAX_SECTIONS = 400;
67
+ const CURRENT_VERSION = 1;
68
+ const FILE_NAME = 'long-form.json';
69
+ // ─── Target parsing ─────────────────────────────────────────────────────────
70
+ /** Default chapter count when a book/novel is requested with no magnitude. */
71
+ const DEFAULT_CHAPTERS = 10;
72
+ /**
73
+ * Extract the long-form target from a goal, or `null` when the goal does not
74
+ * name a magnitude (in which case the ask fits one generation and this module
75
+ * stays out of the way).
76
+ *
77
+ * Understands the shapes users actually type — "a 100 page story", "100-page
78
+ * book", "in 12 chapters", "about 5000 words", "10 पेज", "एक उपन्यास जिसमें 8
79
+ * अध्याय हों" — and prefers the most specific unit named.
80
+ */
81
+ export function parseLongFormTarget(goal) {
82
+ const g = (goal || '').trim();
83
+ if (!g)
84
+ return null;
85
+ // A magnitude must be explicit. Never invent one from "a long story".
86
+ const pages = g.match(/(\d[\d,]*)\s*[-\s]?\s*(pages?|pg|पेज|पृष्ठ)/i);
87
+ const chapters = g.match(/(\d[\d,]*)\s*[-\s]?\s*(chapters?|अध्याय)/i);
88
+ const words = g.match(/(\d[\d,]*)\s*[-\s]?\s*(words?|शब्द)/i);
89
+ const sections = g.match(/(\d[\d,]*)\s*[-\s]?\s*(sections?|parts?)/i);
90
+ const num = (raw) => Number.parseInt(raw.replace(/[,\s]/g, ''), 10);
91
+ // Prefer the unit that most directly describes the deliverable: pages and
92
+ // chapters are how books are specified; words is a fallback; sections last.
93
+ if (pages) {
94
+ return makeTarget('pages', num(pages[1]), `"${pages[0].trim()}"`);
95
+ }
96
+ if (chapters) {
97
+ return makeTarget('chapters', num(chapters[1]), `"${chapters[0].trim()}"`);
98
+ }
99
+ if (words) {
100
+ return makeTarget('words', num(words[1]), `"${words[0].trim()}"`);
101
+ }
102
+ if (sections) {
103
+ return makeTarget('sections', num(sections[1]), `"${sections[0].trim()}"`);
104
+ }
105
+ // A book/novel with no magnitude still needs a plan: default to a sensible
106
+ // chapter count rather than pretending it fits one generation.
107
+ if (/\b(novel|novella|book|उपन्यास|पुस्तक)\b/i.test(g)) {
108
+ return makeTarget('chapters', DEFAULT_CHAPTERS, 'default for an unnumbered book/novel');
109
+ }
110
+ return null;
111
+ }
112
+ /** Convert a user-facing magnitude into a word target and unit count. */
113
+ function makeTarget(unit, amount, source) {
114
+ const safeAmount = Math.max(1, Math.min(amount, 10_000));
115
+ let wordsTarget;
116
+ let unitCount;
117
+ switch (unit) {
118
+ case 'pages':
119
+ // Pages are not a generation boundary — split into as many bounded units
120
+ // as the word count needs (100 pages ≈ 39 units of ~900 words).
121
+ wordsTarget = safeAmount * WORDS_PER_PAGE;
122
+ unitCount = Math.ceil(wordsTarget / WORDS_PER_GENERATION);
123
+ break;
124
+ case 'words':
125
+ wordsTarget = safeAmount;
126
+ unitCount = Math.ceil(wordsTarget / WORDS_PER_GENERATION);
127
+ break;
128
+ case 'chapters':
129
+ // A chapter IS the unit the user counted, so one unit per chapter.
130
+ // Splitting "12 chapters" into 34 fragments would deliver something the
131
+ // user did not ask for and mislabel every one of them.
132
+ wordsTarget = safeAmount * WORDS_PER_CHAPTER;
133
+ unitCount = safeAmount;
134
+ break;
135
+ default:
136
+ wordsTarget = safeAmount * WORDS_PER_GENERATION;
137
+ unitCount = safeAmount;
138
+ }
139
+ return { unit, amount: safeAmount, wordsTarget, unitCount: Math.max(1, Math.min(unitCount, MAX_SECTIONS)), source };
140
+ }
141
+ // ─── Section planning ───────────────────────────────────────────────────────
142
+ /** Filesystem-safe slug for a section title. */
143
+ function slug(text) {
144
+ return text
145
+ .toLowerCase()
146
+ .replace(/[^a-z0-9]+/g, '-')
147
+ .replace(/^-+|-+$/g, '')
148
+ .slice(0, 40) || 'unit';
149
+ }
150
+ /** Pad to two digits so chapter files sort lexicographically. */
151
+ function pad(n) {
152
+ return String(n).padStart(2, '0');
153
+ }
154
+ /** Default display title for a unit, by class. */
155
+ export function sectionTitle(cls, index) {
156
+ return cls === 'creative' ? `Chapter ${index}` : `Section ${index}`;
157
+ }
158
+ /**
159
+ * Build the unit list for a target. Word targets are distributed evenly with
160
+ * the remainder spread over the first units, so the SUM of unit targets always
161
+ * equals the overall word target — the plan cannot silently undershoot the ask.
162
+ */
163
+ export function planSections(target, cls, dir = 'chapters') {
164
+ const count = target.unitCount;
165
+ const base = Math.floor(target.wordsTarget / count);
166
+ const remainder = target.wordsTarget - base * count;
167
+ const sections = [];
168
+ for (let i = 1; i <= count; i++) {
169
+ const title = sectionTitle(cls, i);
170
+ sections.push({
171
+ index: i,
172
+ title,
173
+ path: `${dir}/${pad(i)}-${slug(title)}.md`,
174
+ targetWords: base + (i <= remainder ? 1 : 0),
175
+ status: 'pending',
176
+ words: 0,
177
+ attempts: 0,
178
+ });
179
+ }
180
+ return sections;
181
+ }
182
+ /** Pages implied by a word count (for progress reporting). */
183
+ export function estimatePages(words) {
184
+ return Math.max(0, Math.round((words / WORDS_PER_PAGE) * 10) / 10);
185
+ }
186
+ function envBuffSafe(name) {
187
+ try {
188
+ return envBuff(name);
189
+ }
190
+ catch {
191
+ return undefined;
192
+ }
193
+ }
194
+ /** Memory dir, honouring the test/dev override. */
195
+ function memoryDir() {
196
+ const override = envBuffSafe('MEMORY_DIR');
197
+ if (override)
198
+ return override;
199
+ try {
200
+ return join(resolveNuviraHome(), 'memory');
201
+ }
202
+ catch {
203
+ return join(process.cwd(), '.nuvira-memory');
204
+ }
205
+ }
206
+ function ledgerPath() {
207
+ return join(memoryDir(), FILE_NAME);
208
+ }
209
+ function readLedger() {
210
+ try {
211
+ const raw = readFileSync(ledgerPath(), 'utf-8');
212
+ const parsed = JSON.parse(raw);
213
+ if (parsed && typeof parsed === 'object' && parsed.jobs && typeof parsed.jobs === 'object') {
214
+ return { version: parsed.version ?? CURRENT_VERSION, jobs: parsed.jobs };
215
+ }
216
+ }
217
+ catch {
218
+ // Missing or corrupt — start clean. A ledger read must never throw.
219
+ }
220
+ return { version: CURRENT_VERSION, jobs: {} };
221
+ }
222
+ function writeLedger(data) {
223
+ try {
224
+ const dir = memoryDir();
225
+ if (!existsSync(dir))
226
+ mkdirSync(dir, { recursive: true });
227
+ // Bounded: keep the most recent 30 jobs so the file cannot grow forever.
228
+ const entries = Object.entries(data.jobs);
229
+ if (entries.length > 30) {
230
+ entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
231
+ data.jobs = Object.fromEntries(entries.slice(0, 30));
232
+ }
233
+ writeFileSync(ledgerPath(), JSON.stringify({ ...data, version: CURRENT_VERSION }, null, 2), 'utf-8');
234
+ }
235
+ catch {
236
+ // Best-effort — persistence must never break a turn.
237
+ }
238
+ }
239
+ /** Stable key for a (project, document) pair. */
240
+ export function longFormKey(projectPath, docPath) {
241
+ return `${resolve(projectPath)}::${resolve(docPath)}`;
242
+ }
243
+ /** Both `chapters/03-chapter-3.md` (relative) and absolute path forms. */
244
+ export function sectionAbsolutePath(projectPath, sectionPath) {
245
+ return resolve(projectPath, sectionPath);
246
+ }
247
+ // ─── Job lifecycle ──────────────────────────────────────────────────────────
248
+ /**
249
+ * Start a job, or RESUME an existing one for the same document.
250
+ *
251
+ * Resuming is the whole point: the failing session re-derived the same plan
252
+ * six times and lost every partial result. A resumed job keeps its existing
253
+ * units, their statuses and their word counts, so the caller can ask for the
254
+ * next pending batch and make real forward progress per turn.
255
+ */
256
+ export function startOrResumeJob(input) {
257
+ const key = longFormKey(input.projectPath, input.docPath);
258
+ const data = readLedger();
259
+ const existing = data.jobs[key];
260
+ const now = Date.now();
261
+ if (existing && existing.status === 'in_progress') {
262
+ // Reconcile with disk: a unit marked done whose file vanished is pending
263
+ // again (the user deleted it, or a write failed after the ledger write).
264
+ let repaired = false;
265
+ for (const s of existing.sections) {
266
+ if (s.status === 'done' && !existsSync(sectionAbsolutePath(existing.projectPath, s.path))) {
267
+ s.status = 'pending';
268
+ s.words = 0;
269
+ repaired = true;
270
+ }
271
+ }
272
+ if (repaired)
273
+ recalcWords(existing);
274
+ existing.goal = input.goal;
275
+ existing.updatedAt = now;
276
+ data.jobs[key] = existing;
277
+ writeLedger(data);
278
+ return { job: existing, resumed: true };
279
+ }
280
+ const sections = planSections(input.target, input.deliverableClass, input.dir);
281
+ const job = {
282
+ key,
283
+ goal: input.goal,
284
+ projectPath: resolve(input.projectPath),
285
+ docPath: resolve(input.docPath),
286
+ deliverableClass: input.deliverableClass,
287
+ target: input.target,
288
+ sections,
289
+ words: 0,
290
+ status: 'in_progress',
291
+ createdAt: now,
292
+ updatedAt: now,
293
+ };
294
+ data.jobs[key] = job;
295
+ writeLedger(data);
296
+ return { job, resumed: false };
297
+ }
298
+ /** Read a job without creating one. */
299
+ export function getJob(projectPath, docPath) {
300
+ const data = readLedger();
301
+ return data.jobs[longFormKey(projectPath, docPath)] ?? null;
302
+ }
303
+ /**
304
+ * The most recently updated job for a project REGARDLESS of status, or null.
305
+ *
306
+ * The unattended runner needs this: after the last batch completes the job it
307
+ * must be able to read the FINISHED record to confirm the document exists, and
308
+ * `findInProgressJob` (correctly) hides those.
309
+ */
310
+ export function findLatestJob(projectPath) {
311
+ const data = readLedger();
312
+ const key = resolve(projectPath);
313
+ const candidates = Object.values(data.jobs)
314
+ .filter((j) => j.projectPath === key)
315
+ .sort((a, b) => b.updatedAt - a.updatedAt);
316
+ return candidates[0] ?? null;
317
+ }
318
+ /**
319
+ * The most recently updated UNFINISHED job for a project, or null.
320
+ *
321
+ * WHY: a follow-up turn says "continue" — which names no document, no length,
322
+ * and no class. Re-deriving the job from that text would land on the default
323
+ * filename and start a SECOND book from scratch, which is the "agent goes in a
324
+ * loop and never delivers" behaviour this ledger exists to end. The in-flight
325
+ * job for this project is the correct target for a continuation.
326
+ */
327
+ export function findInProgressJob(projectPath) {
328
+ const data = readLedger();
329
+ const key = resolve(projectPath);
330
+ const candidates = Object.values(data.jobs)
331
+ .filter((j) => j.projectPath === key && j.status === 'in_progress')
332
+ .sort((a, b) => b.updatedAt - a.updatedAt);
333
+ return candidates[0] ?? null;
334
+ }
335
+ /** Number of words in a body of text (whitespace-delimited). */
336
+ export function countWords(text) {
337
+ const t = (text || '').trim();
338
+ if (!t)
339
+ return 0;
340
+ return t.split(/\s+/).filter(Boolean).length;
341
+ }
342
+ function recalcWords(job) {
343
+ job.words = job.sections.reduce((sum, s) => sum + (s.status === 'done' ? s.words : 0), 0);
344
+ }
345
+ /**
346
+ * Record the outcome of ONE unit.
347
+ *
348
+ * `ok` requires actual content: a unit whose file is missing or empty is a
349
+ * FAILURE even if the writer reported success. That is the G10 principle
350
+ * applied to long-form work — "the step ran" is not "the work exists".
351
+ */
352
+ export function recordSectionOutcome(job, index, outcome) {
353
+ const section = job.sections.find((s) => s.index === index);
354
+ if (!section)
355
+ return job;
356
+ section.attempts += 1;
357
+ if (outcome.ok) {
358
+ section.status = 'done';
359
+ section.words = outcome.words ?? section.words;
360
+ delete section.lastError;
361
+ }
362
+ else {
363
+ section.status = 'failed';
364
+ section.lastError = (outcome.error || 'unknown error').slice(0, 300);
365
+ }
366
+ recalcWords(job);
367
+ job.status = job.sections.every((s) => s.status === 'done') ? 'done' : 'in_progress';
368
+ job.updatedAt = Date.now();
369
+ const data = readLedger();
370
+ data.jobs[job.key] = job;
371
+ writeLedger(data);
372
+ return job;
373
+ }
374
+ /**
375
+ * Persist a job that the caller has already mutated (e.g. after writing unit
376
+ * files). Kept separate from `recordSectionOutcome` so batch callers write
377
+ * once instead of once per unit.
378
+ */
379
+ export function saveJob(job) {
380
+ job.updatedAt = Date.now();
381
+ recalcWords(job);
382
+ job.status = job.sections.every((s) => s.status === 'done') ? 'done' : 'in_progress';
383
+ const data = readLedger();
384
+ data.jobs[job.key] = job;
385
+ writeLedger(data);
386
+ }
387
+ /**
388
+ * The next pending units to execute — bounded per run so a turn always ends
389
+ * with delivered text rather than an unfinished marathon.
390
+ */
391
+ export function nextPendingSections(job, limit = MAX_UNITS_PER_RUN) {
392
+ return job.sections.filter((s) => s.status !== 'done').slice(0, Math.max(1, limit));
393
+ }
394
+ export function jobProgress(job) {
395
+ const total = job.sections.length;
396
+ const done = job.sections.filter((s) => s.status === 'done').length;
397
+ const words = job.sections.reduce((sum, s) => sum + (s.status === 'done' ? s.words : 0), 0);
398
+ const wordsPercent = job.target.wordsTarget > 0
399
+ ? Math.min(100, Math.round((words / job.target.wordsTarget) * 100))
400
+ : 0;
401
+ // The completion percentage is the MINIMUM of the two ways the deliverable can
402
+ // be incomplete, and that is deliberate. Writers routinely OVERSHOOT the per-
403
+ // chapter target — a live 100-page run hit 43,906 words against a 35,000 target
404
+ // by chapter 31 of 39 — so a word-only percentage saturated at 100 while eight
405
+ // units were still owed: the progress line read "31/39 complete … 100%", which
406
+ // is the same class of contradiction as a listing count presented as a
407
+ // capability. Units are the generation boundary and the work still outstanding,
408
+ // so the lower of the two is the honest number, and 100% means the deliverable
409
+ // EXISTS (all units done) rather than "the word count ran out of road".
410
+ const unitPercent = total > 0 ? Math.round((done / total) * 100) : 0;
411
+ const percent = Math.min(wordsPercent, unitPercent);
412
+ return {
413
+ done,
414
+ total,
415
+ words,
416
+ wordsTarget: job.target.wordsTarget,
417
+ pages: estimatePages(words),
418
+ pagesTarget: estimatePages(job.target.wordsTarget),
419
+ percent,
420
+ complete: total > 0 && done === total,
421
+ };
422
+ }
423
+ /**
424
+ * The honest progress line — reported to the user every turn.
425
+ *
426
+ * Deliberately quotes BOTH the unit count and the words, because the user
427
+ * asked in pages and a word count alone would look like a dodge. This is the
428
+ * sentence the original session could never produce: it had no idea how much
429
+ * of the book existed.
430
+ */
431
+ export function formatProgress(job) {
432
+ const p = jobProgress(job);
433
+ const noun = job.deliverableClass === 'creative' ? 'chapter' : 'section';
434
+ return `📖 ${noun} ${p.done}/${p.total} complete · ${p.words.toLocaleString()}/${p.wordsTarget.toLocaleString()} words (${p.pages} of ~${p.pagesTarget} pages, ${p.percent}%)`;
435
+ }
436
+ /** The next `limit` units as a plan the writer can execute one at a time. */
437
+ export function nextUnitBrief(job, limit = MAX_UNITS_PER_RUN) {
438
+ const briefs = [];
439
+ for (const s of nextPendingSections(job, limit)) {
440
+ briefs.push({
441
+ index: s.index,
442
+ title: s.title,
443
+ path: s.path,
444
+ targetWords: s.targetWords,
445
+ previousTail: readTail(sectionAbsolutePath(job.projectPath, prevSectionPath(job, s.index)), 1_200),
446
+ });
447
+ }
448
+ return briefs;
449
+ }
450
+ /** Path of the unit before `index`, or '' when there is none. */
451
+ function prevSectionPath(job, index) {
452
+ const prev = job.sections.find((s) => s.index === index - 1);
453
+ return prev ? prev.path : '';
454
+ }
455
+ /** Last `chars` of a file, or '' when it does not exist. */
456
+ export function readTail(absolutePath, chars) {
457
+ try {
458
+ if (!absolutePath || !existsSync(absolutePath))
459
+ return '';
460
+ const text = readFileSync(absolutePath, 'utf-8');
461
+ return text.length <= chars ? text : text.slice(text.length - chars);
462
+ }
463
+ catch {
464
+ return '';
465
+ }
466
+ }
467
+ /**
468
+ * Deterministically assemble the finished document from its unit files.
469
+ *
470
+ * Pure concatenation — no LLM, no cost, no drift. Runs only when every unit is
471
+ * done, so the assembled document is never a half-book presented as complete.
472
+ * Returns null when the job is not finished (the caller must not claim
473
+ * completion, and must keep reporting progress instead).
474
+ */
475
+ export function assembleDocument(job) {
476
+ const progress = jobProgress(job);
477
+ if (!progress.complete)
478
+ return null;
479
+ const parts = [];
480
+ for (const s of job.sections) {
481
+ const abs = sectionAbsolutePath(job.projectPath, s.path);
482
+ let body = '';
483
+ try {
484
+ if (existsSync(abs))
485
+ body = readFileSync(abs, 'utf-8').trim();
486
+ }
487
+ catch {
488
+ body = '';
489
+ }
490
+ if (body)
491
+ parts.push(`# ${s.title}\n\n${body}`);
492
+ }
493
+ if (parts.length === 0)
494
+ return null;
495
+ const text = `${parts.join('\n\n---\n\n')}\n`;
496
+ try {
497
+ const dir = dirname(job.docPath);
498
+ if (!existsSync(dir))
499
+ mkdirSync(dir, { recursive: true });
500
+ writeFileSync(job.docPath, text, 'utf-8');
501
+ }
502
+ catch {
503
+ return null;
504
+ }
505
+ const words = countWords(text);
506
+ return { path: job.docPath, files: parts.length, words, pages: estimatePages(words) };
507
+ }
508
+ /** Relative path display, for a concise log line. */
509
+ export function displayPath(projectPath, absolute) {
510
+ try {
511
+ return relative(projectPath, absolute) || absolute;
512
+ }
513
+ catch {
514
+ return absolute;
515
+ }
516
+ }
517
+ /** Test/reset hook: forget every job. */
518
+ export function clearLongFormJobs() {
519
+ writeLedger({ version: CURRENT_VERSION, jobs: {} });
520
+ }
521
+ //# sourceMappingURL=long-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"long-form.js","sourceRoot":"","sources":["../../src/learning/long-form.ts"],"names":[],"mappings":"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"}
@@ -74,6 +74,23 @@ export declare function buildModelCandidates(taskDescription: string, complexity
74
74
  * Returns the top candidate, or undefined if no models available.
75
75
  */
76
76
  export declare function pickBestModelCandidate(taskDescription: string, complexity: ComplexityLevel, configManager?: ConfigManager, allowedProviders?: string[]): ModelCandidate | undefined;
77
+ /**
78
+ * Count what the pool ACTUALLY offers right now.
79
+ *
80
+ * WHY THIS EXISTS (G9): the failure report used to decide its closing sentence
81
+ * from the PARK list alone — and a dead pair (a provider that answered "model
82
+ * not found") has no expiry, so a report whose only exclusions were dead pairs
83
+ * printed "No suitable model is available right now". Live, that sentence was
84
+ * shown while 507 models were eligible and the provider served a request 52
85
+ * seconds later. A report that cannot count the pool cannot claim it is empty,
86
+ * so the count is computed through the SAME filtering the router uses (dead
87
+ * pairs + non-chat models excluded) — the report can never disagree with what
88
+ * routing would actually consider.
89
+ */
90
+ export declare function countEligibleModels(allowedProviders?: string[]): {
91
+ models: number;
92
+ providers: number;
93
+ };
77
94
  /**
78
95
  * Get the top N model candidates for failover.
79
96
  */
@@ -1 +1 @@
1
- {"version":3,"file":"model-first-router.d.ts","sourceRoot":"","sources":["../../src/learning/model-first-router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model-first-router.d.ts","sourceRoot":"","sources":["../../src/learning/model-first-router.ts"],"names":[],"mappings":"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"}
@@ -308,6 +308,33 @@ export function pickBestModelCandidate(taskDescription, complexity, configManage
308
308
  const candidates = buildModelCandidates(taskDescription, complexity, configManager, allowedProviders);
309
309
  return candidates[0];
310
310
  }
311
+ /**
312
+ * Count what the pool ACTUALLY offers right now.
313
+ *
314
+ * WHY THIS EXISTS (G9): the failure report used to decide its closing sentence
315
+ * from the PARK list alone — and a dead pair (a provider that answered "model
316
+ * not found") has no expiry, so a report whose only exclusions were dead pairs
317
+ * printed "No suitable model is available right now". Live, that sentence was
318
+ * shown while 507 models were eligible and the provider served a request 52
319
+ * seconds later. A report that cannot count the pool cannot claim it is empty,
320
+ * so the count is computed through the SAME filtering the router uses (dead
321
+ * pairs + non-chat models excluded) — the report can never disagree with what
322
+ * routing would actually consider.
323
+ */
324
+ export function countEligibleModels(allowedProviders) {
325
+ try {
326
+ // A neutral, class-agnostic description: we want the SIZE of the pool, not
327
+ // a ranking. Complexity only shifts score weights, never eligibility.
328
+ const candidates = buildModelCandidates('general task', 'moderate', undefined, allowedProviders);
329
+ const providers = new Set();
330
+ for (const c of candidates)
331
+ providers.add(c.provider);
332
+ return { models: candidates.length, providers: providers.size };
333
+ }
334
+ catch {
335
+ return { models: 0, providers: 0 };
336
+ }
337
+ }
311
338
  /**
312
339
  * Get the top N model candidates for failover.
313
340
  */