agent-nuvira 3.1.1 → 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 (172) 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 +133 -8
  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/nlu.d.ts +8 -0
  37. package/dist/cli/nlu.d.ts.map +1 -1
  38. package/dist/cli/nlu.js +57 -0
  39. package/dist/cli/nlu.js.map +1 -1
  40. package/dist/cli/trace.d.ts.map +1 -1
  41. package/dist/cli/trace.js +27 -0
  42. package/dist/cli/trace.js.map +1 -1
  43. package/dist/gateway/gateway-log.d.ts +1 -1
  44. package/dist/gateway/gateway-log.d.ts.map +1 -1
  45. package/dist/gateway/gateway-log.js.map +1 -1
  46. package/dist/gateway/registry.d.ts +116 -1
  47. package/dist/gateway/registry.d.ts.map +1 -1
  48. package/dist/gateway/registry.js +575 -52
  49. package/dist/gateway/registry.js.map +1 -1
  50. package/dist/inference/model-entitlement.d.ts +46 -0
  51. package/dist/inference/model-entitlement.d.ts.map +1 -0
  52. package/dist/inference/model-entitlement.js +98 -0
  53. package/dist/inference/model-entitlement.js.map +1 -0
  54. package/dist/inference/tool-call-utils.d.ts.map +1 -1
  55. package/dist/inference/tool-call-utils.js +70 -1
  56. package/dist/inference/tool-call-utils.js.map +1 -1
  57. package/dist/learning/autonomy-policy.d.ts +334 -0
  58. package/dist/learning/autonomy-policy.d.ts.map +1 -0
  59. package/dist/learning/autonomy-policy.js +500 -0
  60. package/dist/learning/autonomy-policy.js.map +1 -0
  61. package/dist/learning/credential-fingerprint.d.ts +58 -0
  62. package/dist/learning/credential-fingerprint.d.ts.map +1 -0
  63. package/dist/learning/credential-fingerprint.js +126 -0
  64. package/dist/learning/credential-fingerprint.js.map +1 -0
  65. package/dist/learning/deferred-task.d.ts +159 -0
  66. package/dist/learning/deferred-task.d.ts.map +1 -0
  67. package/dist/learning/deferred-task.js +451 -0
  68. package/dist/learning/deferred-task.js.map +1 -0
  69. package/dist/learning/deliverable-class.d.ts +130 -0
  70. package/dist/learning/deliverable-class.d.ts.map +1 -0
  71. package/dist/learning/deliverable-class.js +432 -0
  72. package/dist/learning/deliverable-class.js.map +1 -0
  73. package/dist/learning/long-form.d.ts +252 -0
  74. package/dist/learning/long-form.d.ts.map +1 -0
  75. package/dist/learning/long-form.js +510 -0
  76. package/dist/learning/long-form.js.map +1 -0
  77. package/dist/learning/model-first-router.d.ts +17 -0
  78. package/dist/learning/model-first-router.d.ts.map +1 -1
  79. package/dist/learning/model-first-router.js +35 -0
  80. package/dist/learning/model-first-router.js.map +1 -1
  81. package/dist/learning/model-registry.d.ts +96 -4
  82. package/dist/learning/model-registry.d.ts.map +1 -1
  83. package/dist/learning/model-registry.js +128 -6
  84. package/dist/learning/model-registry.js.map +1 -1
  85. package/dist/learning/model-warmup.d.ts +97 -2
  86. package/dist/learning/model-warmup.d.ts.map +1 -1
  87. package/dist/learning/model-warmup.js +165 -60
  88. package/dist/learning/model-warmup.js.map +1 -1
  89. package/dist/learning/prompt-layers.d.ts +61 -0
  90. package/dist/learning/prompt-layers.d.ts.map +1 -0
  91. package/dist/learning/prompt-layers.js +140 -0
  92. package/dist/learning/prompt-layers.js.map +1 -0
  93. package/dist/learning/provider-limits.d.ts +66 -0
  94. package/dist/learning/provider-limits.d.ts.map +1 -0
  95. package/dist/learning/provider-limits.js +184 -0
  96. package/dist/learning/provider-limits.js.map +1 -0
  97. package/dist/learning/reasoning-trace.d.ts +36 -1
  98. package/dist/learning/reasoning-trace.d.ts.map +1 -1
  99. package/dist/learning/reasoning-trace.js +36 -2
  100. package/dist/learning/reasoning-trace.js.map +1 -1
  101. package/dist/learning/resilient-call.d.ts +36 -1
  102. package/dist/learning/resilient-call.d.ts.map +1 -1
  103. package/dist/learning/resilient-call.js +118 -8
  104. package/dist/learning/resilient-call.js.map +1 -1
  105. package/dist/learning/unattended-job.d.ts +293 -0
  106. package/dist/learning/unattended-job.d.ts.map +1 -0
  107. package/dist/learning/unattended-job.js +544 -0
  108. package/dist/learning/unattended-job.js.map +1 -0
  109. package/dist/learning/unattended-progress.d.ts +95 -0
  110. package/dist/learning/unattended-progress.d.ts.map +1 -0
  111. package/dist/learning/unattended-progress.js +147 -0
  112. package/dist/learning/unattended-progress.js.map +1 -0
  113. package/dist/learning/working-state.d.ts +109 -0
  114. package/dist/learning/working-state.d.ts.map +1 -0
  115. package/dist/learning/working-state.js +244 -0
  116. package/dist/learning/working-state.js.map +1 -0
  117. package/dist/nlu/conversation-gate.d.ts +46 -0
  118. package/dist/nlu/conversation-gate.d.ts.map +1 -1
  119. package/dist/nlu/conversation-gate.js +73 -6
  120. package/dist/nlu/conversation-gate.js.map +1 -1
  121. package/dist/nlu/intent-confirm.d.ts +82 -0
  122. package/dist/nlu/intent-confirm.d.ts.map +1 -0
  123. package/dist/nlu/intent-confirm.js +146 -0
  124. package/dist/nlu/intent-confirm.js.map +1 -0
  125. package/dist/nlu/learnings.d.ts +101 -0
  126. package/dist/nlu/learnings.d.ts.map +1 -0
  127. package/dist/nlu/learnings.js +283 -0
  128. package/dist/nlu/learnings.js.map +1 -0
  129. package/dist/tools/coding-tools.d.ts.map +1 -1
  130. package/dist/tools/coding-tools.js +95 -19
  131. package/dist/tools/coding-tools.js.map +1 -1
  132. package/dist/tools/edit-verification.d.ts +125 -0
  133. package/dist/tools/edit-verification.d.ts.map +1 -0
  134. package/dist/tools/edit-verification.js +237 -0
  135. package/dist/tools/edit-verification.js.map +1 -0
  136. package/dist/tools/git-tool.d.ts.map +1 -1
  137. package/dist/tools/git-tool.js +37 -4
  138. package/dist/tools/git-tool.js.map +1 -1
  139. package/dist/tools/registry.d.ts +30 -2
  140. package/dist/tools/registry.d.ts.map +1 -1
  141. package/dist/tools/registry.js +40 -11
  142. package/dist/tools/registry.js.map +1 -1
  143. package/dist/tools/run-cli.d.ts.map +1 -1
  144. package/dist/tools/run-cli.js +40 -13
  145. package/dist/tools/run-cli.js.map +1 -1
  146. package/dist/tools/run-terminal.d.ts +6 -1
  147. package/dist/tools/run-terminal.d.ts.map +1 -1
  148. package/dist/tools/run-terminal.js +158 -18
  149. package/dist/tools/run-terminal.js.map +1 -1
  150. package/dist/tools/tool-loop.d.ts +35 -0
  151. package/dist/tools/tool-loop.d.ts.map +1 -1
  152. package/dist/tools/tool-loop.js +157 -3
  153. package/dist/tools/tool-loop.js.map +1 -1
  154. package/dist/web-dashboard/chat-console.d.ts +35 -0
  155. package/dist/web-dashboard/chat-console.d.ts.map +1 -1
  156. package/dist/web-dashboard/chat-console.js +97 -18
  157. package/dist/web-dashboard/chat-console.js.map +1 -1
  158. package/dist/web-dashboard/chat-retry.d.ts +143 -0
  159. package/dist/web-dashboard/chat-retry.d.ts.map +1 -0
  160. package/dist/web-dashboard/chat-retry.js +227 -0
  161. package/dist/web-dashboard/chat-retry.js.map +1 -0
  162. package/dist/web-dashboard/server.d.ts +17 -0
  163. package/dist/web-dashboard/server.d.ts.map +1 -1
  164. package/dist/web-dashboard/server.js +200 -9
  165. package/dist/web-dashboard/server.js.map +1 -1
  166. package/dist/web-dashboard/src/types.d.ts +81 -4
  167. package/dist/web-dashboard/src/types.d.ts.map +1 -1
  168. package/package.json +1 -1
  169. package/src/web-dashboard/public/assets/{index-CQW0qv1r.js → index-kCUkORm7.js} +27 -27
  170. package/src/web-dashboard/public/assets/index-kCUkORm7.js.map +1 -0
  171. package/src/web-dashboard/public/index.html +1 -1
  172. package/src/web-dashboard/public/assets/index-CQW0qv1r.js.map +0 -1
@@ -0,0 +1,451 @@
1
+ /**
2
+ * Deferred retry queue — the MECHANISM behind "Reply *yes* and I will keep
3
+ * trying until it is done."
4
+ *
5
+ * THE BUG THIS CLOSES. `renderModelBreadthReport` ends every failed turn with
6
+ * that offer, and it went out live to WhatsApp
7
+ * ("A model frees up in about 44s … Reply *yes* …"). Nothing parsed the "yes",
8
+ * stored the task, or re-ran it when a model came back — so a sender who
9
+ * followed the instruction got silence, which is worse than never being asked.
10
+ * A promise the agent cannot keep is a defect, not a courtesy.
11
+ *
12
+ * WHY A STORE INSTEAD OF A TIMER IN THE FAILURE PATH: the model that frees up
13
+ * is often minutes away (a quota window), and a messaging transport's turn is
14
+ * long over by then — there is no in-process stack to sit on. The task is
15
+ * therefore persisted (surviving a gateway restart, which is the normal case
16
+ * for a quota wait), and a periodic drain claims whatever is due.
17
+ *
18
+ * Deliberately a LEAF module (node builtins + path resolution only): the
19
+ * gateway, the CLI and the dashboard all need it, and a shared queue that
20
+ * dragged the routing graph in would be unusable from the dashboard.
21
+ */
22
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
23
+ import { dirname } from 'node:path';
24
+ import { resolveNuviraDataPath } from '../config/paths.js';
25
+ /** Bounded: a queue is a convenience, not a backlog. */
26
+ const MAX_TASKS = 50;
27
+ /** Confirmed horizon — "yes, keep trying until it is done". */
28
+ export const DEFERRED_TASK_TTL_MS = 6 * 3_600_000;
29
+ /** Optimistic horizon — queued because the ask was real, but never confirmed. */
30
+ export const UNCONFIRMED_TASK_TTL_MS = 30 * 60_000;
31
+ /** Attempt cap, so a permanently broken ask cannot loop for six hours. */
32
+ const MAX_ATTEMPTS = 40;
33
+ /** Attempt cap while the sender has NOT confirmed the retry. */
34
+ const MAX_ATTEMPTS_UNCONFIRMED = 4;
35
+ /** How many attempts this task is allowed. */
36
+ export function attemptCap(task) {
37
+ return task.confirmed ? MAX_ATTEMPTS : MAX_ATTEMPTS_UNCONFIRMED;
38
+ }
39
+ /**
40
+ * Floor between attempts. A "free in 5s" hint is not a licence to hammer the
41
+ * pool — the failover walk already failed everything once, and a tight loop
42
+ * spends quota re-proving that.
43
+ */
44
+ const MIN_WAIT_MS = 20_000;
45
+ /** Ceiling: a known 10-hour reset is not worth holding a queue slot for. */
46
+ const MAX_WAIT_MS = 60 * 60_000;
47
+ /** Default wait when the report could not name a free-up time. */
48
+ const DEFAULT_WAIT_MS = 5 * 60_000;
49
+ const FILE_NAME = 'deferred-tasks.json';
50
+ /**
51
+ * How long a task may sit in `running` before it is treated as abandoned.
52
+ *
53
+ * A retry is claimed by setting `status: 'running'` BEFORE the turn. If the
54
+ * process dies mid-attempt — a dashboard/gateway restart, a reboot, a crash,
55
+ * all normal during a quota wait — nothing ever resets it, and a `running` task
56
+ * is invisible to both `dueTasks` and `expiredTasks`. The promise would be
57
+ * stranded silently, which is the exact failure this whole store exists to
58
+ * prevent. The ceiling is generous (a retry is a full turn, capped at 5 minutes
59
+ * by the chat HTTP ceiling), so a live attempt is never stolen.
60
+ */
61
+ const STALE_RUNNING_MS = 15 * 60_000;
62
+ function storePath() {
63
+ return resolveNuviraDataPath(FILE_NAME);
64
+ }
65
+ /** Read the queue. Best-effort: a corrupt file is an empty queue, never a throw. */
66
+ export function loadDeferredTasks() {
67
+ try {
68
+ const path = storePath();
69
+ if (!existsSync(path))
70
+ return [];
71
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
72
+ if (!parsed || !Array.isArray(parsed.tasks))
73
+ return [];
74
+ return recoverStaleRunning(parsed.tasks.filter(isTaskShape));
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ }
80
+ /**
81
+ * Self-heal tasks abandoned mid-attempt (see STALE_RUNNING_MS).
82
+ *
83
+ * Applied on READ, so no process has to remember to run a recovery pass on
84
+ * startup, and the repair is written back so the next reader sees it too.
85
+ */
86
+ function recoverStaleRunning(tasks, now = Date.now()) {
87
+ let repaired = false;
88
+ for (const task of tasks) {
89
+ if (task.status !== 'running')
90
+ continue;
91
+ const startedAt = task.lastAttemptAt || task.createdAt;
92
+ if (now - startedAt <= STALE_RUNNING_MS)
93
+ continue;
94
+ task.status = 'pending';
95
+ // Its wait elapsed long ago (it was due when the attempt was claimed), so
96
+ // being due again immediately is correct — nothing to wait for.
97
+ task.notBefore = Math.min(task.notBefore, now);
98
+ repaired = true;
99
+ }
100
+ if (repaired)
101
+ writeDeferredTasks(tasks);
102
+ return tasks;
103
+ }
104
+ function writeDeferredTasks(tasks) {
105
+ try {
106
+ const path = storePath();
107
+ const dir = dirname(path);
108
+ if (!existsSync(dir))
109
+ mkdirSync(dir, { recursive: true });
110
+ const payload = { version: 1, tasks: tasks.slice(0, MAX_TASKS) };
111
+ writeFileSync(path, JSON.stringify(payload, null, 2), 'utf-8');
112
+ }
113
+ catch {
114
+ // Best-effort — a store write must never break the turn that deferred it.
115
+ }
116
+ }
117
+ function isTaskShape(t) {
118
+ if (!t || typeof t !== 'object')
119
+ return false;
120
+ const o = t;
121
+ return (typeof o.id === 'string' &&
122
+ typeof o.platform === 'string' &&
123
+ typeof o.channelId === 'string' &&
124
+ typeof o.text === 'string' &&
125
+ (o.kind === 'chat' || o.kind === 'pipeline') &&
126
+ typeof o.createdAt === 'number' &&
127
+ typeof o.notBefore === 'number' &&
128
+ typeof o.deadline === 'number');
129
+ }
130
+ /** The conversation a task belongs to — one live retry per conversation. */
131
+ function conversationKey(platform, channelId) {
132
+ return `${platform}:${channelId}`;
133
+ }
134
+ function newId() {
135
+ try {
136
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
137
+ return `retry-${crypto.randomUUID().slice(0, 8)}`;
138
+ }
139
+ }
140
+ catch {
141
+ /* fall through */
142
+ }
143
+ return `retry-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
144
+ }
145
+ /** Clamp a free-up hint into a sane retry wait. */
146
+ export function retryWaitMs(nextFreeInMs) {
147
+ if (typeof nextFreeInMs !== 'number' || !Number.isFinite(nextFreeInMs) || nextFreeInMs <= 0) {
148
+ return DEFAULT_WAIT_MS;
149
+ }
150
+ return Math.min(MAX_WAIT_MS, Math.max(MIN_WAIT_MS, Math.round(nextFreeInMs)));
151
+ }
152
+ /**
153
+ * Record a failed ask for retry, or refresh the pending task for that
154
+ * conversation.
155
+ *
156
+ * The UPDATE path deliberately preserves `attempts` and `createdAt`: the retry
157
+ * that just failed re-enters this function (it goes through the same failure
158
+ * branch), and resetting the counters there would make the attempt cap and TTL
159
+ * meaningless — the task would retry forever.
160
+ *
161
+ * @returns the stored task and whether it was created (vs refreshed).
162
+ */
163
+ export function deferTask(input) {
164
+ const tasks = loadDeferredTasks();
165
+ const key = conversationKey(input.platform, input.channelId);
166
+ const now = Date.now();
167
+ const existing = tasks.find((t) => conversationKey(t.platform, t.channelId) === key && t.text === input.text);
168
+ const notBefore = now + retryWaitMs(input.nextFreeInMs);
169
+ if (existing) {
170
+ existing.notBefore = Math.max(existing.notBefore, notBefore);
171
+ existing.status = 'pending';
172
+ if (input.lastError)
173
+ existing.lastError = input.lastError;
174
+ if (input.kind)
175
+ existing.kind = input.kind;
176
+ writeDeferredTasks(tasks);
177
+ return { task: existing, created: false };
178
+ }
179
+ const task = {
180
+ id: newId(),
181
+ platform: input.platform,
182
+ channelId: input.channelId,
183
+ ...(input.from ? { from: input.from } : {}),
184
+ ...(input.senderId ? { senderId: input.senderId } : {}),
185
+ ...(input.isGroup !== undefined ? { isGroup: input.isGroup } : {}),
186
+ text: input.text,
187
+ kind: input.kind,
188
+ createdAt: now,
189
+ lastAttemptAt: 0,
190
+ attempts: 0,
191
+ status: 'pending',
192
+ notBefore,
193
+ deadline: now + UNCONFIRMED_TASK_TTL_MS,
194
+ ...(input.lastError ? { lastError: input.lastError } : {}),
195
+ };
196
+ // Newest first, bounded: the oldest pending task is the one a user is most
197
+ // likely to have forgotten, and dropping it silently is better than growing
198
+ // an unbounded file.
199
+ writeDeferredTasks([task, ...tasks]);
200
+ return { task, created: true };
201
+ }
202
+ /** The live task for a conversation (newest first), if any. */
203
+ export function getPendingTask(platform, channelId) {
204
+ const key = conversationKey(platform, channelId);
205
+ return loadDeferredTasks().find((t) => conversationKey(t.platform, t.channelId) === key);
206
+ }
207
+ /** Every task currently waiting (for status surfaces, newest first). */
208
+ export function listPendingTasks() {
209
+ return loadDeferredTasks();
210
+ }
211
+ /** Tasks whose next attempt is due and whose TTL has not elapsed. */
212
+ export function dueTasks(now = Date.now(), owns = () => true) {
213
+ return loadDeferredTasks().filter((t) => t.status === 'pending' &&
214
+ t.notBefore <= now &&
215
+ t.deadline > now &&
216
+ t.attempts < attemptCap(t) &&
217
+ owns(t));
218
+ }
219
+ /** Tasks that ran out of attempts or TTL — the sender must be told, not ghosted. */
220
+ export function expiredTasks(now = Date.now(), owns = () => true) {
221
+ return loadDeferredTasks().filter((t) => t.status === 'pending' && (t.deadline <= now || t.attempts >= attemptCap(t)) && owns(t));
222
+ }
223
+ /**
224
+ * The sender said yes: extend this task to the full horizon and keep going.
225
+ * Returns the confirmed task (or undefined when it had already expired).
226
+ */
227
+ export function confirmTask(id, now = Date.now()) {
228
+ const tasks = loadDeferredTasks();
229
+ const task = tasks.find((t) => t.id === id);
230
+ if (!task)
231
+ return undefined;
232
+ task.confirmed = true;
233
+ task.status = 'pending';
234
+ task.deadline = Math.max(task.deadline, now + DEFERRED_TASK_TTL_MS);
235
+ writeDeferredTasks(tasks);
236
+ return task;
237
+ }
238
+ /** Apply a partial patch to one task by id. Returns the patched task. */
239
+ export function updateDeferredTask(id, patch) {
240
+ const tasks = loadDeferredTasks();
241
+ const task = tasks.find((t) => t.id === id);
242
+ if (!task)
243
+ return undefined;
244
+ Object.assign(task, patch);
245
+ writeDeferredTasks(tasks);
246
+ return task;
247
+ }
248
+ /** Drop a task (on success, on a cancel reply, or when it expires). */
249
+ export function removeDeferredTask(id) {
250
+ writeDeferredTasks(loadDeferredTasks().filter((t) => t.id !== id));
251
+ }
252
+ /** Drop every task for a conversation (`stop`, `no`, `cancel`). */
253
+ export function cancelTasksFor(platform, channelId) {
254
+ const key = conversationKey(platform, channelId);
255
+ const tasks = loadDeferredTasks();
256
+ const kept = tasks.filter((t) => conversationKey(t.platform, t.channelId) !== key);
257
+ if (kept.length !== tasks.length)
258
+ writeDeferredTasks(kept);
259
+ return tasks.length - kept.length;
260
+ }
261
+ // ─── "yes" / "no" recognition ───────────────────────────────────────────────
262
+ /**
263
+ * Phrases that ACCEPT the retry offer. Whole-message matches only — the gates
264
+ * below make the matcher conservative because a wrong verdict here swallows a
265
+ * real request, which is exactly the class of bug this queue exists to fix.
266
+ */
267
+ const ACCEPT_PHRASES = new Set([
268
+ 'yes',
269
+ 'y',
270
+ 'yep',
271
+ 'yeah',
272
+ 'yup',
273
+ 'ya',
274
+ 'yaa',
275
+ 'ok',
276
+ 'okay',
277
+ 'k',
278
+ 'sure',
279
+ 'definitely',
280
+ 'please do',
281
+ 'do it',
282
+ 'go ahead',
283
+ 'go on',
284
+ 'go for it',
285
+ 'keep trying',
286
+ 'keep going',
287
+ 'keep trying please',
288
+ 'try again',
289
+ 'retry',
290
+ 'please retry',
291
+ 'haan',
292
+ 'ha',
293
+ 'han',
294
+ 'theek hai',
295
+ 'chalega',
296
+ 'yes please',
297
+ 'yes please do',
298
+ 'please keep trying',
299
+ 'sure please',
300
+ 'ok please',
301
+ 'continue',
302
+ 'continue please',
303
+ ]);
304
+ /** Phrases that DECLINE / cancel the offer. */
305
+ const DECLINE_PHRASES = new Set([
306
+ 'no',
307
+ 'n',
308
+ 'nope',
309
+ 'nah',
310
+ 'nahi',
311
+ 'stop',
312
+ 'stop it',
313
+ 'cancel',
314
+ 'cancel it',
315
+ 'forget it',
316
+ 'never mind',
317
+ 'nevermind',
318
+ 'leave it',
319
+ 'no thanks',
320
+ 'no thank you',
321
+ 'dont',
322
+ "don't",
323
+ 'do not',
324
+ 'nahi chahiye',
325
+ ]);
326
+ /** An acceptance token embedded in a short reply ("yes, please keep trying"). */
327
+ const ACCEPT_TOKEN_RE = /\b(?:yes|yep|yeah|yup|sure|ok|okay|haan|please do|go ahead|go on|keep trying|keep going|try again|retry|continue|theek hai)\b/;
328
+ /**
329
+ * Words allowed to accompany an accept token. This is what separates
330
+ * "yes, please keep trying" (an acceptance) from "yes I also want a website"
331
+ * (a NEW request that merely starts with yes) — the second contains words that
332
+ * are not filler, so it is handled normally instead of being swallowed.
333
+ * Losing a user's real request to a thumbs-up heuristic is exactly the class of
334
+ * bug this queue exists to fix, so the check is word-by-word strict.
335
+ */
336
+ const ACCEPT_FILLER = new Set([
337
+ 'yes', 'yep', 'yeah', 'yup', 'ya', 'sure', 'ok', 'okay', 'k', 'please', 'pls',
338
+ 'thanks', 'thank', 'you', 'keep', 'trying', 'going', 'go', 'ahead', 'on',
339
+ 'do', 'it', 'now', 'try', 'again', 'retry', 'continue', 'haan', 'han', 'yaa',
340
+ 'theek', 'hai', 'chalega', 'much', 'so', 'and', 'that', 'this', 'for', 'to',
341
+ ]);
342
+ const DECLINE_TOKEN_RE = /\b(?:no|nope|nah|nahi|stop|cancel|forget it|never ?mind|leave it|don'?t)\b/;
343
+ /** True when the message is ONLY punctuation/whitespace/emoji. */
344
+ const NON_WORD_ONLY_RE = /^[^\p{L}\p{N}]+$/u;
345
+ /**
346
+ * Emoji-only replies that unambiguously accept: 👍 🙌 👌 ✅ 🆗.
347
+ * A bare thumbs-up is how most messaging users say "yes", so it must not fall
348
+ * through to normal handling as punctuation — while an unrelated emoji (❤️, 😂)
349
+ * still does, since those are not answers.
350
+ */
351
+ const ACCEPT_EMOJI_RE = /[\u{1F44D}\u{1F44C}\u{1F64C}\u{2705}\u{1F197}]/u;
352
+ /**
353
+ * Normalize a reply for phrase matching: lowercase, emoji/punctuation stripped,
354
+ * whitespace collapsed. `"Yes, please! 🙏"` → `"yes please"`.
355
+ */
356
+ export function normalizeRetryAnswer(text) {
357
+ return String(text ?? '')
358
+ .toLowerCase()
359
+ .replace(/[^\p{L}\p{N}\s]/gu, ' ')
360
+ .replace(/\s+/g, ' ')
361
+ .trim();
362
+ }
363
+ /**
364
+ * Is this message the sender ACCEPTING the retry offer?
365
+ *
366
+ * Two gates, both required:
367
+ * 1. the whole message must be short (≤ 6 words) — anything longer is a new
368
+ * request that merely happens to start with "yes", and swallowing it would
369
+ * lose the user's actual ask;
370
+ * 2. it must be an exact accept phrase, or a short message carrying an accept
371
+ * token and NO decline token ("yes, keep trying" accepts; "no, do the
372
+ * other thing" does not).
373
+ *
374
+ * A message that fails either gate falls through to normal handling — the
375
+ * pending task simply stays queued, so the sender can accept later.
376
+ */
377
+ export function isRetryAcceptance(text) {
378
+ const raw = String(text ?? '').trim();
379
+ if (!raw)
380
+ return false;
381
+ if (NON_WORD_ONLY_RE.test(raw))
382
+ return ACCEPT_EMOJI_RE.test(raw);
383
+ const n = normalizeRetryAnswer(raw);
384
+ if (!n)
385
+ return false;
386
+ if (DECLINE_TOKEN_RE.test(n))
387
+ return false;
388
+ if (ACCEPT_PHRASES.has(n))
389
+ return true;
390
+ const words = n.split(' ').filter(Boolean);
391
+ if (words.length > 6)
392
+ return false;
393
+ if (!ACCEPT_TOKEN_RE.test(n))
394
+ return false;
395
+ // Every other word must be filler: see ACCEPT_FILLER.
396
+ return words.every((w) => ACCEPT_FILLER.has(w) || ACCEPT_TOKEN_RE.test(w));
397
+ }
398
+ /** Is this message the sender cancelling the retry offer? */
399
+ export function isRetryDecline(text) {
400
+ const raw = String(text ?? '').trim();
401
+ if (!raw || NON_WORD_ONLY_RE.test(raw))
402
+ return false;
403
+ const n = normalizeRetryAnswer(raw);
404
+ if (!n)
405
+ return false;
406
+ if (DECLINE_PHRASES.has(n))
407
+ return true;
408
+ const words = n.split(' ').filter(Boolean);
409
+ // TWO words, max. "no, don't send it to her" is a different request that
410
+ // happens to contain a decline token — treating it as "cancel" would drop the
411
+ // user's actual instruction as well as their task.
412
+ if (words.length > 2)
413
+ return false;
414
+ // A decline must not carry an accept token: "no, keep trying" is ambiguous,
415
+ // and guessing wrong CANCELS work the user asked to continue.
416
+ if (ACCEPT_TOKEN_RE.test(n))
417
+ return false;
418
+ return DECLINE_TOKEN_RE.test(n);
419
+ }
420
+ // ─── Sender-facing lines ────────────────────────────────────────────────────
421
+ /** Confirmation sent the moment an offer is accepted. */
422
+ export function acceptedLine(task, now = Date.now()) {
423
+ const wait = Math.max(0, task.notBefore - now);
424
+ const when = wait > 60_000 ? `in about ${Math.round(wait / 60_000)}m` : 'shortly';
425
+ return (`👍 Understood — I'll keep trying and message you here the moment it's done.\n` +
426
+ `Next attempt ${when}. Reply *stop* to cancel.`);
427
+ }
428
+ /** The line sent when a retry gives up (TTL or attempt cap reached). */
429
+ export function abandonedLine(task, now = Date.now()) {
430
+ const mins = Math.max(1, Math.round((now - task.createdAt) / 60_000));
431
+ const span = mins >= 90 ? `about ${Math.round(mins / 60)}h` : `about ${mins}m`;
432
+ const tries = `${task.attempts} attempt${task.attempts === 1 ? '' : 's'}`;
433
+ if (!task.confirmed) {
434
+ // Honest about WHY it stopped: the sender never confirmed, so this is a
435
+ // courtesy report, not a broken promise.
436
+ return (`😔 "${task.text.slice(0, 90)}" still couldn't run — I tried again ${tries} over ${span} and no model came back. ` +
437
+ `I've stopped. Reply *yes* if you want me to keep checking and run it the moment one is free.`);
438
+ }
439
+ return (`😔 I kept retrying "${task.text.slice(0, 90)}" for ${span} (${tries}) and no model became available. ` +
440
+ `I've stopped rather than hold your request forever. Say *retry* any time and I'll start again.`);
441
+ }
442
+ /** The line sent when a retry attempt starts, so an unexpected reply is explained. */
443
+ export function retryingLine(task) {
444
+ const cap = attemptCap(task);
445
+ return `🔁 Trying again now (attempt ${task.attempts} of at most ${cap}) — you asked for: "${task.text.slice(0, 80)}"`;
446
+ }
447
+ /** Count of live tasks (status surfaces / dashboard). */
448
+ export function pendingTaskCount() {
449
+ return loadDeferredTasks().length;
450
+ }
451
+ //# sourceMappingURL=deferred-task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred-task.js","sourceRoot":"","sources":["../../src/learning/deferred-task.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Deliverable classification — what KIND of thing did the user actually ask
3
+ * for? (enterprise-grade hardening, G7.)
4
+ *
5
+ * WHY THIS EXISTS (the WhatsApp story audit):
6
+ * A user asked, over WhatsApp, for a 100-page Harry-Potter-style story as a
7
+ * PDF. Six orchestrator runs over 32 minutes ALL failed, and the reason was
8
+ * not model availability (507 models were eligible). The reasoner — the
9
+ * technical-decision layer that runs BEFORE the planner — read "write a story"
10
+ * and emitted:
11
+ *
12
+ * {"language":"python","framework":"none","deliverable":"markdown_file",
13
+ * "reasoning":"…a Python script is the most efficient way to read existing
14
+ * chapters, process the outline, and update the target file…"}
15
+ *
16
+ * The planner then produced ZERO prose steps: step-02 was "Create a Python
17
+ * script to append the story continuation", step-03 "Run the Python script".
18
+ * The agent built a TOOL to write the story instead of writing the story, and
19
+ * because the tool-creation step itself failed, nothing was written at all —
20
+ * the target directory did not exist afterwards.
21
+ *
22
+ * The decision layer had no vocabulary for authored work: every branch of its
23
+ * taxonomy (language/framework/buildCommand/architecture/deliverable) assumes
24
+ * the deliverable is a PROGRAM. So a creative ask could only ever be expressed
25
+ * as a program that emits the creative artifact.
26
+ *
27
+ * This module is the missing vocabulary. It is deliberately DETERMINISTIC and
28
+ * LLM-free — the classifier is a safety net that the LLM's own decision is
29
+ * checked against, so a weak model cannot reintroduce the category error. It
30
+ * runs on the raw goal before planning, and for `document`/`creative` the
31
+ * orchestrator plans SECTIONS instead of a program.
32
+ */
33
+ /**
34
+ * The five kinds of work the planner knows how to plan.
35
+ *
36
+ * `code` is the historical default and keeps every software ask on its
37
+ * existing path — this module must never change how a code task is planned.
38
+ */
39
+ export type DeliverableClass = 'code' | 'document' | 'creative' | 'data' | 'research';
40
+ /**
41
+ * What a deliverable is physically MADE OF.
42
+ *
43
+ * Added for the hybrid asks: "develop a web based book which might require
44
+ * Python for voice assistance or interactiveness" is not one thing — it is
45
+ * prose + a web experience + (optionally) a Python narration service. A single
46
+ * `class` cannot express that, and forcing a choice is how the original audit
47
+ * produced a Python script instead of a story: the taxonomy only had room for
48
+ * one answer, and it picked the program.
49
+ */
50
+ export type Substrate = 'prose' | 'web' | 'python' | 'asset' | 'data';
51
+ /** The result of classifying a goal. */
52
+ export interface DeliverableVerdict {
53
+ /** The winning class. */
54
+ class: DeliverableClass;
55
+ /** 0–1. Below `AUTHORED_CONFIDENCE_FLOOR` the caller should not override the LLM. */
56
+ confidence: number;
57
+ /** Which words drove the decision (auditable, shown in logs/traces). */
58
+ signals: string[];
59
+ /**
60
+ * True when the deliverable IS authored content (prose), as opposed to a
61
+ * program/library that manipulates it. This is the flag that switches the
62
+ * pipeline from "plan code steps" to "plan sections".
63
+ */
64
+ authored: boolean;
65
+ /**
66
+ * Ordered materials, primary first. A plain story is `['prose']`; an
67
+ * interactive web book is `['web', 'prose']` (+ `'python'` when a narration
68
+ * service was asked for). Always at least one entry.
69
+ */
70
+ substrates: Substrate[];
71
+ /**
72
+ * True when the deliverable is MORE THAN ONE thing joined — a book AND the
73
+ * site that presents it. Composite asks need a PHASED plan (scaffold first,
74
+ * then the content, then the experience layer), not a single-mode plan.
75
+ */
76
+ composite: boolean;
77
+ /**
78
+ * True when the deliverable must be experienced, not just read: a site to
79
+ * navigate, narration to play, chapters to flip through. Drives the
80
+ * interactivity phase and the end-to-end verification step.
81
+ */
82
+ interactive: boolean;
83
+ }
84
+ /**
85
+ * Below this the verdict is a hint, not a ruling: the caller keeps the LLM's
86
+ * own decision. Set high enough that a passing mention ("write a report about
87
+ * the API I built") cannot hijack a genuine engineering task.
88
+ */
89
+ export declare const AUTHORED_CONFIDENCE_FLOOR = 0.55;
90
+ /**
91
+ * Classify a goal into a deliverable class.
92
+ *
93
+ * Scoring is additive with per-signal weights rather than first-match, so a
94
+ * goal that mixes signals ("write a story AND package it as a CLI") resolves
95
+ * on the balance of evidence. Confidence is derived from the winning margin,
96
+ * so a clear call scores high and a coin-flip stays low — which is exactly
97
+ * what the caller needs to decide whether to override the LLM.
98
+ *
99
+ * Defaults to `code` when nothing matches: that preserves today's behaviour
100
+ * for every goal this module does not understand.
101
+ */
102
+ export declare function classifyDeliverable(goal: string): DeliverableVerdict;
103
+ /**
104
+ * Convenience: is this goal asking for authored content (prose) rather than a
105
+ * program? The orchestrator uses this one predicate to switch planning modes.
106
+ */
107
+ export declare function isAuthoredGoal(goal: string): boolean;
108
+ /**
109
+ * Is this a HYBRID deliverable — content plus a way to experience it?
110
+ *
111
+ * The orchestrator uses this one predicate to switch from "plan the content" to
112
+ * "plan the phases" (scaffold → content → experience → assets → verify). Kept
113
+ * here rather than in the planner so the intent vocabulary lives in one place.
114
+ */
115
+ export declare function isCompositeGoal(goal: string): boolean;
116
+ /** Human-readable substrate list, for logs, traces and prompts. */
117
+ export declare function describeSubstrates(verdict: DeliverableVerdict): string;
118
+ /** Human-readable label for logs, traces and prompts. */
119
+ export declare function deliverableClassLabel(cls: DeliverableClass): string;
120
+ /**
121
+ * The instruction handed to the reasoner so its technical-decision document
122
+ * stops pretending an authored ask is a program.
123
+ *
124
+ * This is the fix for the exact reasoning string the audit captured
125
+ * ("a Python script is the most efficient way to…"): the reasoner is told, in
126
+ * its own JSON vocabulary, that `language` must NOT be a programming language
127
+ * for these classes, and that building a generator is the wrong deliverable.
128
+ */
129
+ export declare function authoredDeliverableGuidance(cls: DeliverableClass, substrates?: Substrate[]): string;
130
+ //# sourceMappingURL=deliverable-class.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliverable-class.d.ts","sourceRoot":"","sources":["../../src/learning/deliverable-class.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAEtF;;;;;;;;;GASG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtE,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,KAAK,EAAE,gBAAgB,CAAC;IACxB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAiI9C;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAqFpE;AA0FD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAEtE;AAED,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,MAAM,CAanE;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,gBAAgB,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,CA0BnG"}