agent-nuvira 3.1.3 → 3.2.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 (146) 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 +194 -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 +27 -0
  26. package/dist/cli/chat.d.ts.map +1 -1
  27. package/dist/cli/chat.js +116 -6
  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 +105 -2
  32. package/dist/cli/execute.js.map +1 -1
  33. package/dist/cli/models.d.ts.map +1 -1
  34. package/dist/cli/models.js +10 -0
  35. package/dist/cli/models.js.map +1 -1
  36. package/dist/cli/trace.d.ts.map +1 -1
  37. package/dist/cli/trace.js +27 -0
  38. package/dist/cli/trace.js.map +1 -1
  39. package/dist/gateway/registry.d.ts +31 -0
  40. package/dist/gateway/registry.d.ts.map +1 -1
  41. package/dist/gateway/registry.js +143 -13
  42. package/dist/gateway/registry.js.map +1 -1
  43. package/dist/inference/model-entitlement.d.ts +46 -0
  44. package/dist/inference/model-entitlement.d.ts.map +1 -0
  45. package/dist/inference/model-entitlement.js +98 -0
  46. package/dist/inference/model-entitlement.js.map +1 -0
  47. package/dist/learning/autonomy-policy.d.ts +334 -0
  48. package/dist/learning/autonomy-policy.d.ts.map +1 -0
  49. package/dist/learning/autonomy-policy.js +500 -0
  50. package/dist/learning/autonomy-policy.js.map +1 -0
  51. package/dist/learning/credential-fingerprint.d.ts +58 -0
  52. package/dist/learning/credential-fingerprint.d.ts.map +1 -0
  53. package/dist/learning/credential-fingerprint.js +126 -0
  54. package/dist/learning/credential-fingerprint.js.map +1 -0
  55. package/dist/learning/deliverable-class.d.ts +130 -0
  56. package/dist/learning/deliverable-class.d.ts.map +1 -0
  57. package/dist/learning/deliverable-class.js +432 -0
  58. package/dist/learning/deliverable-class.js.map +1 -0
  59. package/dist/learning/long-form.d.ts +252 -0
  60. package/dist/learning/long-form.d.ts.map +1 -0
  61. package/dist/learning/long-form.js +510 -0
  62. package/dist/learning/long-form.js.map +1 -0
  63. package/dist/learning/model-first-router.d.ts +17 -0
  64. package/dist/learning/model-first-router.d.ts.map +1 -1
  65. package/dist/learning/model-first-router.js +27 -0
  66. package/dist/learning/model-first-router.js.map +1 -1
  67. package/dist/learning/model-registry.d.ts +66 -4
  68. package/dist/learning/model-registry.d.ts.map +1 -1
  69. package/dist/learning/model-registry.js +67 -6
  70. package/dist/learning/model-registry.js.map +1 -1
  71. package/dist/learning/model-warmup.d.ts +97 -2
  72. package/dist/learning/model-warmup.d.ts.map +1 -1
  73. package/dist/learning/model-warmup.js +165 -60
  74. package/dist/learning/model-warmup.js.map +1 -1
  75. package/dist/learning/prompt-layers.d.ts +61 -0
  76. package/dist/learning/prompt-layers.d.ts.map +1 -0
  77. package/dist/learning/prompt-layers.js +140 -0
  78. package/dist/learning/prompt-layers.js.map +1 -0
  79. package/dist/learning/provider-limits.d.ts +66 -0
  80. package/dist/learning/provider-limits.d.ts.map +1 -0
  81. package/dist/learning/provider-limits.js +184 -0
  82. package/dist/learning/provider-limits.js.map +1 -0
  83. package/dist/learning/reasoning-trace.d.ts +36 -1
  84. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  85. package/dist/learning/reasoning-trace.js +36 -2
  86. package/dist/learning/reasoning-trace.js.map +1 -1
  87. package/dist/learning/resilient-call.d.ts +36 -1
  88. package/dist/learning/resilient-call.d.ts.map +1 -1
  89. package/dist/learning/resilient-call.js +80 -5
  90. package/dist/learning/resilient-call.js.map +1 -1
  91. package/dist/learning/unattended-job.d.ts +293 -0
  92. package/dist/learning/unattended-job.d.ts.map +1 -0
  93. package/dist/learning/unattended-job.js +544 -0
  94. package/dist/learning/unattended-job.js.map +1 -0
  95. package/dist/learning/unattended-progress.d.ts +95 -0
  96. package/dist/learning/unattended-progress.d.ts.map +1 -0
  97. package/dist/learning/unattended-progress.js +147 -0
  98. package/dist/learning/unattended-progress.js.map +1 -0
  99. package/dist/learning/working-state.d.ts +109 -0
  100. package/dist/learning/working-state.d.ts.map +1 -0
  101. package/dist/learning/working-state.js +244 -0
  102. package/dist/learning/working-state.js.map +1 -0
  103. package/dist/nlu/conversation-gate.d.ts +24 -0
  104. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  105. package/dist/nlu/conversation-gate.js +47 -3
  106. package/dist/nlu/conversation-gate.js.map +1 -1
  107. package/dist/tools/coding-tools.d.ts.map +1 -1
  108. package/dist/tools/coding-tools.js +95 -19
  109. package/dist/tools/coding-tools.js.map +1 -1
  110. package/dist/tools/edit-verification.d.ts +125 -0
  111. package/dist/tools/edit-verification.d.ts.map +1 -0
  112. package/dist/tools/edit-verification.js +237 -0
  113. package/dist/tools/edit-verification.js.map +1 -0
  114. package/dist/tools/git-tool.d.ts.map +1 -1
  115. package/dist/tools/git-tool.js +37 -4
  116. package/dist/tools/git-tool.js.map +1 -1
  117. package/dist/tools/registry.d.ts +30 -2
  118. package/dist/tools/registry.d.ts.map +1 -1
  119. package/dist/tools/registry.js +40 -11
  120. package/dist/tools/registry.js.map +1 -1
  121. package/dist/tools/run-cli.d.ts.map +1 -1
  122. package/dist/tools/run-cli.js +40 -13
  123. package/dist/tools/run-cli.js.map +1 -1
  124. package/dist/tools/run-terminal.d.ts +6 -1
  125. package/dist/tools/run-terminal.d.ts.map +1 -1
  126. package/dist/tools/run-terminal.js +158 -18
  127. package/dist/tools/run-terminal.js.map +1 -1
  128. package/dist/tools/tool-loop.d.ts +35 -0
  129. package/dist/tools/tool-loop.d.ts.map +1 -1
  130. package/dist/tools/tool-loop.js +141 -1
  131. package/dist/tools/tool-loop.js.map +1 -1
  132. package/dist/web-dashboard/chat-console.d.ts +6 -0
  133. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  134. package/dist/web-dashboard/chat-console.js.map +1 -1
  135. package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
  136. package/dist/web-dashboard/chat-retry.js +10 -2
  137. package/dist/web-dashboard/chat-retry.js.map +1 -1
  138. package/dist/web-dashboard/server.d.ts.map +1 -1
  139. package/dist/web-dashboard/server.js +80 -8
  140. package/dist/web-dashboard/server.js.map +1 -1
  141. package/dist/web-dashboard/src/types.d.ts +81 -4
  142. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  143. package/package.json +1 -1
  144. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
  145. package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
  146. package/src/web-dashboard/public/index.html +1 -1
@@ -0,0 +1,544 @@
1
+ /**
2
+ * Unattended jobs — the engine that finishes long work WITHOUT the user having
3
+ * to say "continue" (enterprise-grade hardening, G11).
4
+ *
5
+ * THE BUG THIS CLOSES. The long-form ledger (G8) made a 100-page book
6
+ * *possible*: it split the ask into bounded units and recorded progress. But it
7
+ * left the CADENCE manual. Every batch ended with
8
+ *
9
+ * "Reply \"continue\" and I will write the next batch"
10
+ *
11
+ * so a 39-unit book needed ~10 human turns. The user asked for a book and got a
12
+ * job they had to keep pressing. That is a shortcut, not a delivery: "write me
13
+ * a 100-page book" is itself the authorization to spend the time, and an
14
+ * assistant that stops after 4 of 39 units to ask whether to continue has
15
+ * substituted a protocol for the work.
16
+ *
17
+ * WHAT THIS MODULE IS. A persisted queue of IN-PROGRESS WORK, plus a runner
18
+ * that keeps executing it until it is done, blocked on a genuine question, or
19
+ * out of its own time budget. It is deliberately NOT the retry queue
20
+ * (`deferred-task.ts`):
21
+ *
22
+ * | | deferred-task | unattended-job |
23
+ * |---|---|---|
24
+ * | triggered by | a FAILURE | unfinished WORK |
25
+ * | authorization | the user replying "yes" | the original ask |
26
+ * | wait shape | backoff between attempts | run continuously |
27
+ * | budget | ~6h of retries | hours of actual work (default 10h) |
28
+ * | stops on | attempts/TTL | completion, a real question, or no progress |
29
+ *
30
+ * Merging the two would make "keep retrying the model" and "keep writing the
31
+ * book" the same switch, and the consent semantics of each are opposites.
32
+ *
33
+ * THREE GUARANTEES THE RUNNER ENFORCES
34
+ * 1. PROGRESS IS MEASURED, NOT CLAIMED. A batch that did not move the ledger
35
+ * is a stall even if every step reported success. Three stalls in a row
36
+ * stop the run and ask, because an agent looping without progress is the
37
+ * exact "goes in a loop and never delivers" behaviour this exists to end.
38
+ * 2. IT STOPS FOR QUESTIONS, NOT FOR PERMISSION. The only thing that pauses
39
+ * work is a decision the user genuinely owns (see `autonomy-policy.ts`).
40
+ * 3. IT NEVER RUNS UNBOUNDED. A deadline (default 10 hours), a stall cap, a
41
+ * failure cap and a per-drain batch cap.
42
+ *
43
+ * Storage: `<memory>/unattended-jobs.json`. Best-effort writes — bookkeeping
44
+ * must never break the turn that scheduled the work.
45
+ */
46
+ import { envBuff, resolveNuviraHome } from '../config/paths.js';
47
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
48
+ import { join } from 'node:path';
49
+ // ─── Constants ──────────────────────────────────────────────────────────────
50
+ /**
51
+ * How long a job may keep working before it gives up and reports.
52
+ *
53
+ * Ten hours because the ask this module exists for is "a 100/200-page book" or
54
+ * "a phased build", and the user's requirement is explicit: those must run to
55
+ * completion unattended rather than in 4-turn increments. It is a CEILING, not
56
+ * a target — a job that finishes in ten minutes leaves the queue immediately.
57
+ */
58
+ export const DEFAULT_DEADLINE_MS = 10 * 3_600_000;
59
+ /** Consecutive no-progress batches before the run stops and asks. */
60
+ export const MAX_STALLED_BATCHES = 3;
61
+ /** Consecutive hard failures (provider errors, verification failures). */
62
+ export const MAX_CONSECUTIVE_FAILURES = 6;
63
+ /** Batches one drain may run before yielding (keeps a tick responsive). */
64
+ export const MAX_BATCHES_PER_DRAIN = 6;
65
+ /**
66
+ * How long a job may sit in `running` before it is treated as abandoned.
67
+ *
68
+ * A batch is claimed before it runs; if the process dies mid-batch — a restart,
69
+ * a reboot, a crash, all normal over a ten-hour job — nothing resets it and a
70
+ * `running` job is invisible to every query. The work would be stranded
71
+ * silently, which is the failure mode the whole ledger exists to prevent. The
72
+ * ceiling is generous (well past a single batch) so a live batch is never
73
+ * stolen.
74
+ */
75
+ export const STALE_RUNNING_MS = 25 * 60_000;
76
+ /** Bounded store — a queue is a convenience, not a backlog. */
77
+ const MAX_JOBS = 25;
78
+ const FILE_NAME = 'unattended-jobs.json';
79
+ const CURRENT_VERSION = 1;
80
+ function envBuffSafe(name) {
81
+ try {
82
+ return envBuff(name);
83
+ }
84
+ catch {
85
+ return undefined;
86
+ }
87
+ }
88
+ /** Memory dir, honouring the same test/dev override the long-form ledger uses. */
89
+ function memoryDir() {
90
+ const override = envBuffSafe('MEMORY_DIR');
91
+ if (override)
92
+ return override;
93
+ try {
94
+ return join(resolveNuviraHome(), 'memory');
95
+ }
96
+ catch {
97
+ return join(process.cwd(), '.nuvira-memory');
98
+ }
99
+ }
100
+ function storePath() {
101
+ return join(memoryDir(), FILE_NAME);
102
+ }
103
+ function isJobShape(value) {
104
+ if (!value || typeof value !== 'object')
105
+ return false;
106
+ const j = value;
107
+ return (typeof j.id === 'string' &&
108
+ typeof j.goal === 'string' &&
109
+ typeof j.projectPath === 'string' &&
110
+ typeof j.createdAt === 'number' &&
111
+ typeof j.deadline === 'number' &&
112
+ typeof j.continuationPrompt === 'string' &&
113
+ !!j.surface &&
114
+ typeof j.surface.platform === 'string' &&
115
+ typeof j.surface.channelId === 'string');
116
+ }
117
+ /**
118
+ * Self-heal jobs abandoned mid-batch (see STALE_RUNNING_MS), applied on READ so
119
+ * no process has to remember a recovery pass, and written back so the next
120
+ * reader sees the repair too.
121
+ */
122
+ function recoverStaleRunning(jobs, now) {
123
+ let repaired = false;
124
+ for (const job of jobs) {
125
+ if (job.status !== 'running')
126
+ continue;
127
+ const startedAt = job.lastRunAt || job.createdAt;
128
+ if (now - startedAt <= STALE_RUNNING_MS)
129
+ continue;
130
+ job.status = 'pending';
131
+ job.stopReason = undefined;
132
+ repaired = true;
133
+ }
134
+ if (repaired)
135
+ writeJobs(jobs);
136
+ return jobs;
137
+ }
138
+ /** Read the queue. Best-effort: a corrupt file is an empty queue, never a throw. */
139
+ export function loadUnattendedJobs(now = Date.now()) {
140
+ try {
141
+ const path = storePath();
142
+ if (!existsSync(path))
143
+ return [];
144
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
145
+ if (!parsed || !Array.isArray(parsed.jobs))
146
+ return [];
147
+ return recoverStaleRunning(parsed.jobs.filter(isJobShape), now);
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ }
153
+ function writeJobs(jobs) {
154
+ try {
155
+ const dir = memoryDir();
156
+ if (!existsSync(dir))
157
+ mkdirSync(dir, { recursive: true });
158
+ // Newest first, bounded: the oldest job is the one most likely to have been
159
+ // forgotten, and dropping it silently beats an unbounded file.
160
+ const ordered = [...jobs].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, MAX_JOBS);
161
+ const payload = { version: CURRENT_VERSION, jobs: ordered };
162
+ writeFileSync(storePath(), JSON.stringify(payload, null, 2), 'utf-8');
163
+ }
164
+ catch {
165
+ // Best-effort — bookkeeping must never break the turn that scheduled work.
166
+ }
167
+ }
168
+ function newId() {
169
+ try {
170
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
171
+ return `job-${crypto.randomUUID().slice(0, 8)}`;
172
+ }
173
+ }
174
+ catch {
175
+ /* fall through */
176
+ }
177
+ return `job-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
178
+ }
179
+ // ─── Lifecycle ──────────────────────────────────────────────────────────────
180
+ /** Stable identity for "the ongoing work in this project, for this surface". */
181
+ function jobIdentity(kind, projectPath, surface) {
182
+ return `${kind}|${projectPath}|${surface.platform}:${surface.channelId}`;
183
+ }
184
+ /**
185
+ * Schedule ongoing work — or return the job already carrying it.
186
+ *
187
+ * Idempotent per (kind, project, surface) on purpose: every batch of a book
188
+ * re-enters this function, and treating each re-entry as a new job would spawn
189
+ * a duplicate worker per batch and have them race on the same chapters.
190
+ */
191
+ export function startUnattendedJob(input) {
192
+ const jobs = loadUnattendedJobs();
193
+ const now = Date.now();
194
+ const identity = jobIdentity(input.kind, input.projectPath, input.surface);
195
+ const existing = jobs.find((j) => jobIdentity(j.kind, j.projectPath, j.surface) === identity && j.status !== 'cancelled' && j.status !== 'done');
196
+ if (existing) {
197
+ // A live job keeps its own counters and deadline — re-entering must not
198
+ // reset the time budget, or a job that keeps re-planning could run forever.
199
+ existing.goal = input.goal;
200
+ existing.updatedAt = now;
201
+ if (input.continuationPrompt)
202
+ existing.continuationPrompt = input.continuationPrompt;
203
+ writeJobs(jobs);
204
+ return { job: existing, created: false };
205
+ }
206
+ const job = {
207
+ id: newId(),
208
+ kind: input.kind,
209
+ goal: input.goal,
210
+ projectPath: input.projectPath,
211
+ surface: input.surface,
212
+ continuationPrompt: input.continuationPrompt ?? 'continue',
213
+ createdAt: now,
214
+ updatedAt: now,
215
+ lastRunAt: 0,
216
+ deadline: now + Math.max(60_000, input.deadlineMs ?? DEFAULT_DEADLINE_MS),
217
+ batches: 0,
218
+ stalledBatches: 0,
219
+ failures: 0,
220
+ status: 'pending',
221
+ progress: 0,
222
+ notify: input.notify !== false,
223
+ ...(input.expectedArtifacts?.length ? { expectedArtifacts: input.expectedArtifacts } : {}),
224
+ };
225
+ writeJobs([job, ...jobs]);
226
+ return { job, created: true };
227
+ }
228
+ export function getUnattendedJob(id) {
229
+ return loadUnattendedJobs().find((j) => j.id === id);
230
+ }
231
+ /** The live job for a surface (newest first), if any. */
232
+ export function findUnattendedJob(surface) {
233
+ return loadUnattendedJobs().find((j) => j.surface.platform === surface.platform &&
234
+ j.surface.channelId === surface.channelId &&
235
+ (j.status === 'pending' || j.status === 'running' || j.status === 'blocked'));
236
+ }
237
+ /** Every job still waiting for a turn (for status surfaces, newest first). */
238
+ export function listUnattendedJobs() {
239
+ return loadUnattendedJobs();
240
+ }
241
+ /**
242
+ * Jobs ready to run now: pending, inside their deadline, and not out of road.
243
+ *
244
+ * `blocked` jobs are deliberately excluded — they are waiting on the user, not
245
+ * on the scheduler.
246
+ */
247
+ export function dueUnattendedJobs(now = Date.now(), owns = () => true) {
248
+ return loadUnattendedJobs(now).filter((j) => j.status === 'pending' &&
249
+ j.deadline > now &&
250
+ j.stalledBatches < MAX_STALLED_BATCHES &&
251
+ j.failures < MAX_CONSECUTIVE_FAILURES &&
252
+ owns(j));
253
+ }
254
+ /** Jobs whose time budget ran out — the user must be told, not ghosted. */
255
+ export function expiredUnattendedJobs(now = Date.now(), owns = () => true) {
256
+ return loadUnattendedJobs(now).filter((j) => j.status === 'pending' && j.deadline <= now && owns(j));
257
+ }
258
+ /**
259
+ * Stop a job with a reason, without touching its counters.
260
+ *
261
+ * Used by the runner to expire jobs whose time budget ran out — the reason
262
+ * must be RECORDED (the user is told why the promised work stopped), and doing
263
+ * it here keeps the runner free of ledger-write logic.
264
+ */
265
+ export function stopUnattendedJob(id, status, reason, now = Date.now()) {
266
+ const jobs = loadUnattendedJobs(now);
267
+ const job = jobs.find((j) => j.id === id);
268
+ if (!job)
269
+ return undefined;
270
+ job.status = status;
271
+ job.stopReason = reason;
272
+ job.updatedAt = now;
273
+ if (status === 'done')
274
+ job.progress = 100;
275
+ writeJobs(jobs);
276
+ return job;
277
+ }
278
+ /** Stop a job (the user said stop, or the work is being replaced). */
279
+ export function cancelUnattendedJob(id, reason = 'cancelled by the user') {
280
+ const jobs = loadUnattendedJobs();
281
+ const job = jobs.find((j) => j.id === id);
282
+ if (!job)
283
+ return;
284
+ job.status = 'cancelled';
285
+ job.stopReason = reason;
286
+ job.updatedAt = Date.now();
287
+ writeJobs(jobs);
288
+ }
289
+ /** Drop every job for a surface (`stop`, `cancel`, a fresh unrelated ask). */
290
+ export function cancelUnattendedJobsFor(surface, reason = 'cancelled by the user') {
291
+ const jobs = loadUnattendedJobs();
292
+ let changed = 0;
293
+ for (const job of jobs) {
294
+ if (job.surface.platform !== surface.platform || job.surface.channelId !== surface.channelId)
295
+ continue;
296
+ if (job.status === 'done' || job.status === 'cancelled' || job.status === 'failed')
297
+ continue;
298
+ job.status = 'cancelled';
299
+ job.stopReason = reason;
300
+ job.updatedAt = Date.now();
301
+ changed++;
302
+ }
303
+ if (changed > 0)
304
+ writeJobs(jobs);
305
+ return changed;
306
+ }
307
+ /** Claim a job for one batch. Returns the claimed job, or undefined. */
308
+ export function claimUnattendedJob(id, now = Date.now()) {
309
+ const jobs = loadUnattendedJobs(now);
310
+ const job = jobs.find((j) => j.id === id);
311
+ if (!job || job.status !== 'pending')
312
+ return undefined;
313
+ job.status = 'running';
314
+ job.lastRunAt = now;
315
+ job.updatedAt = now;
316
+ writeJobs(jobs);
317
+ return job;
318
+ }
319
+ /**
320
+ * Record what one batch actually did, and decide what happens next.
321
+ *
322
+ * This is where "no manual cadence" is enforced: the batch's own outcome
323
+ * determines whether the job keeps running, stops because it is DONE, or stops
324
+ * because it needs the user — nobody has to say "continue".
325
+ *
326
+ * @returns the updated job (undefined when the id is unknown).
327
+ */
328
+ export function recordBatchOutcome(id, outcome, now = Date.now()) {
329
+ const jobs = loadUnattendedJobs(now);
330
+ const job = jobs.find((j) => j.id === id);
331
+ if (!job)
332
+ return undefined;
333
+ job.batches += 1;
334
+ job.updatedAt = now;
335
+ job.stopReason = undefined;
336
+ job.pendingQuestion = undefined;
337
+ if (outcome.progressLine)
338
+ job.progressLine = outcome.progressLine;
339
+ // ── A hard failure is checked FIRST, before completion. ──────────────────
340
+ // A batch that failed verification can leave every file on disk and still
341
+ // not be a delivered deliverable (a site that does not present the chapters).
342
+ // Letting `finished` win would report success over exactly the failure the
343
+ // verify step exists to catch.
344
+ if (outcome.error) {
345
+ job.failures += 1;
346
+ if (job.failures >= MAX_CONSECUTIVE_FAILURES) {
347
+ job.status = 'failed';
348
+ job.stopReason = `stopped after ${job.failures} consecutive failed batches — last error: ${outcome.error.slice(0, 300)}`;
349
+ writeJobs(jobs);
350
+ return job;
351
+ }
352
+ job.status = 'pending';
353
+ writeJobs(jobs);
354
+ return job;
355
+ }
356
+ // ── Finished: the deliverable exists. ────────────────────────────────────
357
+ if (outcome.finished) {
358
+ job.status = 'done';
359
+ job.progress = 100;
360
+ job.stopReason = 'complete';
361
+ writeJobs(jobs);
362
+ return job;
363
+ }
364
+ // ── A genuine question: stop the RUN, not the work. ──────────────────────
365
+ // Marked blocked rather than failed so the answer resumes from the ledger.
366
+ if (outcome.question) {
367
+ job.status = 'blocked';
368
+ job.pendingQuestion = outcome.question;
369
+ job.stopReason = 'waiting on a decision only the user can make';
370
+ writeJobs(jobs);
371
+ return job;
372
+ }
373
+ job.failures = 0;
374
+ // ── Progress is MEASURED. A batch that did not move is a stall. ──────────
375
+ const measured = typeof outcome.progress === 'number' ? outcome.progress : job.progress;
376
+ const moved = measured > job.progress;
377
+ job.progress = Math.max(job.progress, measured);
378
+ job.stalledBatches = moved ? 0 : job.stalledBatches + 1;
379
+ if (job.stalledBatches >= MAX_STALLED_BATCHES) {
380
+ job.status = 'blocked';
381
+ job.stopReason = `no measurable progress in ${job.stalledBatches} batches`;
382
+ job.pendingQuestion =
383
+ `I've run ${job.stalledBatches} batches with NO measurable progress — the deliverable has not grown ` +
384
+ `(still ${job.progress}% — ${job.progressLine ?? 'no progress recorded'}). ` +
385
+ 'Something is genuinely wrong that I cannot resolve on my own; how would you like me to proceed?';
386
+ writeJobs(jobs);
387
+ return job;
388
+ }
389
+ if (now >= job.deadline) {
390
+ job.status = 'failed';
391
+ job.stopReason = 'time budget exhausted before the work finished';
392
+ writeJobs(jobs);
393
+ return job;
394
+ }
395
+ job.status = 'pending';
396
+ writeJobs(jobs);
397
+ return job;
398
+ }
399
+ /** Resume a blocked job after the user answered. */
400
+ export function resumeUnattendedJob(id, extraDeadlineMs = 0, now = Date.now()) {
401
+ const jobs = loadUnattendedJobs(now);
402
+ const job = jobs.find((j) => j.id === id);
403
+ if (!job)
404
+ return undefined;
405
+ job.status = 'pending';
406
+ job.pendingQuestion = undefined;
407
+ job.stopReason = undefined;
408
+ // An answer is fresh authorization: the stall counter and the clock both get
409
+ // a new run at it, or the job would stop again on the very next batch.
410
+ job.stalledBatches = 0;
411
+ job.failures = 0;
412
+ if (extraDeadlineMs > 0)
413
+ job.deadline = Math.max(job.deadline, now + extraDeadlineMs);
414
+ job.updatedAt = now;
415
+ writeJobs(jobs);
416
+ return job;
417
+ }
418
+ // ─── Reporting ──────────────────────────────────────────────────────────────
419
+ /** Percentage complete, clamped. */
420
+ export function jobPercent(job) {
421
+ return Math.max(0, Math.min(100, Math.round(job.progress)));
422
+ }
423
+ /**
424
+ * The honest status line for a job.
425
+ *
426
+ * Deliberately quotes MEASURED progress and says explicitly that no reply is
427
+ * needed — the user's complaint was having to reply at all, so a progress
428
+ * report that ends by asking for one would repeat the mistake in miniature.
429
+ */
430
+ export function unattendedStatusLine(job) {
431
+ const head = job.kind === 'long-form' ? '📖' : '🛠️';
432
+ const pct = jobPercent(job);
433
+ switch (job.status) {
434
+ case 'done':
435
+ return `${head} Job complete — ${job.progressLine ?? 'deliverable written'} (${job.batches} batch${job.batches === 1 ? '' : 'es'}).`;
436
+ case 'blocked':
437
+ return `${head} ${pct}% ${job.progressLine ?? ''}\n\n❓ ${job.pendingQuestion ?? job.stopReason ?? 'I need your input to continue.'}`.trim();
438
+ case 'failed':
439
+ return `${head} I could not finish this one — ${pct}% done${job.progressLine ? ` (${job.progressLine})` : ''}. ${job.stopReason ?? ''}`.trim();
440
+ case 'cancelled':
441
+ return `${head} Stopped — ${job.stopReason ?? 'cancelled'}.`;
442
+ default:
443
+ return (`${head} Still working — ${pct}%${job.progressLine ? ` · ${job.progressLine}` : ''} ` +
444
+ `(batch ${job.batches + 1}). No reply needed; I'll keep going and report when it's done.`);
445
+ }
446
+ }
447
+ /** How long the job may still run, in ms (0 when the budget is spent). */
448
+ export function remainingBudgetMs(job, now = Date.now()) {
449
+ return Math.max(0, job.deadline - now);
450
+ }
451
+ /** Test/reset hook. */
452
+ export function clearUnattendedJobs() {
453
+ writeJobs([]);
454
+ }
455
+ /**
456
+ * The unattended continuation loop.
457
+ *
458
+ * One instance per surface process. `drain()` is the periodic tick: it expires
459
+ * anything out of time, reports it, then runs whatever is due — looping so a
460
+ * single tick advances a long job as far as its budget allows instead of one
461
+ * chapter per tick. Drains are serialized through an internal chain, because
462
+ * two overlapping drains would run the same job twice and race on the same
463
+ * files (exactly how the first end-to-end run produced four copies of the last
464
+ * chapter).
465
+ */
466
+ export class UnattendedRunner {
467
+ deps;
468
+ chain = Promise.resolve();
469
+ active = false;
470
+ constructor(deps) {
471
+ this.deps = deps;
472
+ }
473
+ now() {
474
+ return this.deps.now ? this.deps.now() : Date.now();
475
+ }
476
+ /** Is a drain currently running? Status surfaces use this to show activity. */
477
+ get isRunning() {
478
+ return this.active;
479
+ }
480
+ /** The jobs this runner would pick up right now. */
481
+ due() {
482
+ return dueUnattendedJobs(this.now(), this.deps.owns ?? (() => true));
483
+ }
484
+ /**
485
+ * Advance every due job as far as one tick may.
486
+ *
487
+ * @returns how many batches were run.
488
+ */
489
+ async drain() {
490
+ this.chain = this.chain.then(() => this.runOnce()).catch(() => undefined);
491
+ await this.chain;
492
+ return this.lastBatches;
493
+ }
494
+ lastBatches = 0;
495
+ async runOnce() {
496
+ this.active = true;
497
+ this.lastBatches = 0;
498
+ try {
499
+ const now = this.now();
500
+ // Expiry FIRST: a user who was promised the work must hear the outcome
501
+ // even when the queue is busy with something else.
502
+ for (const job of expiredUnattendedJobs(now, this.deps.owns ?? (() => true))) {
503
+ const expired = stopUnattendedJob(job.id, 'failed', 'time budget exhausted before the work finished', now) ?? job;
504
+ await this.safeNotify(expired, unattendedStatusLine(expired));
505
+ }
506
+ const budget = Math.max(1, this.deps.maxBatchesPerDrain ?? MAX_BATCHES_PER_DRAIN);
507
+ for (let i = 0; i < budget; i++) {
508
+ const next = this.due().find((job) => !(this.deps.isBusy?.(job) ?? false));
509
+ if (!next)
510
+ break;
511
+ const claimed = claimUnattendedJob(next.id, this.now());
512
+ if (!claimed)
513
+ continue;
514
+ let outcome;
515
+ try {
516
+ outcome = await this.deps.runBatch(claimed);
517
+ }
518
+ catch (err) {
519
+ outcome = { error: err instanceof Error ? err.message : String(err) };
520
+ }
521
+ this.lastBatches += 1;
522
+ const updated = recordBatchOutcome(claimed.id, outcome, this.now());
523
+ if (updated && claimed.notify) {
524
+ await this.safeNotify(updated, unattendedStatusLine(updated));
525
+ }
526
+ }
527
+ }
528
+ finally {
529
+ this.active = false;
530
+ }
531
+ }
532
+ /** A notification must never break the run that produced it. */
533
+ async safeNotify(job, line) {
534
+ if (!this.deps.notify)
535
+ return;
536
+ try {
537
+ await this.deps.notify(job, line);
538
+ }
539
+ catch {
540
+ /* best-effort */
541
+ }
542
+ }
543
+ }
544
+ //# sourceMappingURL=unattended-job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unattended-job.js","sourceRoot":"","sources":["../../src/learning/unattended-job.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Measured progress for an unattended job (enterprise-grade hardening, G11).
3
+ *
4
+ * WHY THIS IS ITS OWN MODULE. The runner's central guarantee is that progress
5
+ * is MEASURED, not claimed: a batch that reports success but did not move the
6
+ * deliverable is a stall, and three stalls stop the run and ask. That rule is
7
+ * only as good as the measurement, so the measurement lives in one place,
8
+ * reading artifacts off disk — and every surface (CLI, dashboard, gateway)
9
+ * measures the same way instead of each inventing its own idea of "done".
10
+ *
11
+ * The two inputs:
12
+ * 1. the long-form ledger — how much of the CONTENT exists (words, units);
13
+ * 2. the job's `expectedArtifacts` — the files the deliverable promises
14
+ * (site, chapter index, optional tools). For a composite ask this is what
15
+ * stops "the prose phase finished" being reported as "the deliverable is
16
+ * done" while the site is still missing.
17
+ */
18
+ import { type BatchOutcome, type UnattendedJob, type UnattendedSurface } from './unattended-job.js';
19
+ /**
20
+ * Weight of the content phase in a composite deliverable's percentage.
21
+ *
22
+ * The content IS the deliverable, so it dominates; the surrounding artifacts
23
+ * are the remaining third. Weighting them any lower would let a beautiful
24
+ * empty shell sit at 99% after one chapter.
25
+ */
26
+ export declare const CONTENT_WEIGHT = 0.7;
27
+ /**
28
+ * How many expected artifacts exist on disk (relative paths resolved against
29
+ * the project root).
30
+ *
31
+ * Split out from the job-shaped wrapper because the orchestrator asks the same
32
+ * question at planning time — "has this deliverable already been produced?" —
33
+ * before any job record exists.
34
+ */
35
+ export declare function artifactsPresence(projectPath: string, expected: string[] | undefined): {
36
+ present: number;
37
+ total: number;
38
+ missing: string[];
39
+ };
40
+ /** How many of a job's expected artifacts exist on disk. */
41
+ export declare function countPresentArtifacts(job: UnattendedJob): {
42
+ present: number;
43
+ total: number;
44
+ missing: string[];
45
+ };
46
+ /**
47
+ * Measure the job from disk.
48
+ *
49
+ * Never throws: a measurement failure must degrade to "no progress recorded",
50
+ * which the runner correctly treats as a stall, rather than crashing the drain
51
+ * that is holding the user's promised work.
52
+ */
53
+ export declare function measureUnattendedProgress(job: UnattendedJob): BatchOutcome;
54
+ /**
55
+ * Is the deliverable complete right now?
56
+ *
57
+ * Used by surfaces to decide whether to keep scheduling a job at all — a
58
+ * resumed "continue" turn that finds the work already done must not start a
59
+ * second book (the ledger's oldest failure mode).
60
+ */
61
+ export declare function isDeliverableComplete(job: UnattendedJob): boolean;
62
+ /**
63
+ * The slice of an orchestrator result a surface needs to schedule the rest of
64
+ * the work.
65
+ *
66
+ * Declared STRUCTURALLY (not imported from the orchestrator) because the
67
+ * gateway, the CLI and the dashboard all consume it, and a shared consumer must
68
+ * not drag the whole agent graph into its import closure.
69
+ */
70
+ export interface PendingWorkLike {
71
+ kind: 'long-form' | 'phased';
72
+ goal: string;
73
+ projectPath: string;
74
+ continuationPrompt: string;
75
+ expectedArtifacts?: string[];
76
+ progressLine: string;
77
+ percent: number;
78
+ reason: string;
79
+ }
80
+ /**
81
+ * Schedule the continuation of a run that ended with unfinished work — the
82
+ * replacement for asking the user to type "continue".
83
+ *
84
+ * Returns the scheduled job, or `null` when there is nothing left to do. That
85
+ * second case matters as much as the first: a run that finished its deliverable
86
+ * must not enqueue a continuation that would immediately re-run and rebuild it.
87
+ *
88
+ * @param pending the orchestrator's `result.pendingWork` (may be undefined)
89
+ * @param surface who to report to and which drain owns the work
90
+ */
91
+ export declare function scheduleFromPendingWork(pending: PendingWorkLike | undefined, surface: UnattendedSurface, options?: {
92
+ deadlineMs?: number;
93
+ notify?: boolean;
94
+ }): UnattendedJob | null;
95
+ //# sourceMappingURL=unattended-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unattended-progress.d.ts","sourceRoot":"","sources":["../../src/learning/unattended-progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,EAAsB,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExH;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAC7B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAWvD;AAED,4DAA4D;AAC5D,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAE/G;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,aAAa,GAAG,YAAY,CA+C1E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAGjE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,eAAe,GAAG,SAAS,EACpC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,aAAa,GAAG,IAAI,CAiBtB"}