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
@@ -33,6 +33,11 @@
33
33
  import { isTrivialPrompt } from '../memory/provider.js';
34
34
  import { parseRequestSync } from './parser.js';
35
35
  import { isContentArtifactAsk, stripArtifactReferences } from './intent.js';
36
+ import { isAuthoredGoal } from '../learning/deliverable-class.js';
37
+ import { parseLongFormTarget } from '../learning/long-form.js';
38
+ // The learned corrections. A plain store (mtime-cached, no routing graph), so
39
+ // consulting it on every ask costs a comparison, not a read.
40
+ import { applyLearning, noteLearningApplied } from './learnings.js';
36
41
  /**
37
42
  * Verbs that are unambiguous coding TASKS when used in command position.
38
43
  * Kept to verbs that are rarely used as nouns (unlike "update"/"remove"/
@@ -79,6 +84,42 @@ export function hasCodingAction(text) {
79
84
  .replace(/^(?:(?:please|now|next|then|ok|okay|so|also|and)\s*,?\s*)+/i, '');
80
85
  return TASK_VERB_AT_START.test(action) || TASK_VERB_AFTER_PREFIX.test(action);
81
86
  }
87
+ /**
88
+ * Is this a LONG-FORM authored deliverable — content the pipeline must build in
89
+ * bounded units rather than answer in one reply?
90
+ *
91
+ * WHY THIS EXISTS (live evidence): "write a 12 page story called Kharig Nights
92
+ * about a village boy who finds a lamp in a banyan root" was read as a
93
+ * CONVERSATIONAL question — the NLU maps `write` to a chat action — and answered
94
+ * in a single reply, bypassing the unit ledger entirely. For 12 pages that
95
+ * looked acceptable; for "write a 200 page book" it is fatal: the ask never
96
+ * reaches the machinery that decomposes it, so nothing measures progress,
97
+ * nothing resumes, and nothing continues unattended. A long-form deliverable is
98
+ * not a question, whatever verb introduces it.
99
+ *
100
+ * BOTH conditions are required:
101
+ * 1. an EXPLICIT magnitude (pages/chapters/words/sections) splitting into more
102
+ * than one unit — "write a story" keeps answering directly, and the
103
+ * DEFAULT chapter count invented for a bare "book" does not count as one;
104
+ * 2. the deliverable classifier reads the ask as authored content. Without
105
+ * this, "write a 20 page plan for my child" would be pushed into a pipeline
106
+ * whose authored path does not recognise it — reintroducing exactly the
107
+ * category error (plan a program for prose) this workstream exists to
108
+ * remove.
109
+ */
110
+ export function isLongFormDeliverable(text) {
111
+ const t = String(text ?? '').trim();
112
+ if (!t)
113
+ return false;
114
+ const target = parseLongFormTarget(t);
115
+ if (!target || target.unitCount <= 1)
116
+ return false;
117
+ // `source` is the matched phrase ("100 pages") or a sentence describing a
118
+ // default — a default magnitude is not something the user asked for.
119
+ if (/^default\b/.test(target.source))
120
+ return false;
121
+ return isAuthoredGoal(t);
122
+ }
82
123
  /**
83
124
  * The P0.5 gate: return true when the ask is a conversational question that
84
125
  * must be ANSWERED DIRECTLY and must NEVER run the multi-agent pipeline
@@ -95,6 +136,10 @@ export function isConversationalQuestion(text) {
95
136
  return false;
96
137
  if (isTrivialPrompt(t))
97
138
  return true;
139
+ // A long-form deliverable outranks the NLU's chat mapping for `write`: the
140
+ // machinery that can actually finish it must be the one that receives it.
141
+ if (isLongFormDeliverable(t))
142
+ return false;
98
143
  if (hasCodingAction(t))
99
144
  return false;
100
145
  const parsed = parseRequestSync(t);
@@ -122,15 +167,37 @@ export function isConversationalQuestion(text) {
122
167
  * gateway does), so this costs nothing extra on the hot path.
123
168
  */
124
169
  export function resolveAskKind(text, parsed) {
170
+ return explainAskKind(text, parsed).kind;
171
+ }
172
+ /**
173
+ * Resolve the route AND explain it — the same function `resolveAskKind` uses,
174
+ * split out so the CLI can say WHY a route differs from the rules. An override
175
+ * nobody can see is its own bug.
176
+ *
177
+ * A LEARNED correction outranks the rules: it exists because this exact ask was
178
+ * read wrong before and the model confirmed what it really is (see
179
+ * `intent-confirm.ts`), so the rules have already been shown to fail here. It is
180
+ * checked against the HEURISTIC verdict — a learning that agrees with the rules
181
+ * is not an override, and must not be reported as one.
182
+ */
183
+ export function explainAskKind(text, parsed) {
125
184
  const t = String(text ?? '').trim();
126
185
  if (!t)
127
- return 'chat';
186
+ return { kind: 'chat', base: 'chat' };
128
187
  if (isTrivialPrompt(t))
129
- return 'chat';
130
- if (hasCodingAction(t))
131
- return 'pipeline';
132
- const p = parsed ?? parseRequestSync(t);
133
- return p.action.run === 'pipeline' ? 'pipeline' : 'chat';
188
+ return { kind: 'chat', base: 'chat' };
189
+ const base = isLongFormDeliverable(t)
190
+ ? 'pipeline'
191
+ : hasCodingAction(t)
192
+ ? 'pipeline'
193
+ : (parsed ?? parseRequestSync(t)).action.run === 'pipeline'
194
+ ? 'pipeline'
195
+ : 'chat';
196
+ const learned = applyLearning(t, base);
197
+ if (!learned)
198
+ return { kind: base, base };
199
+ noteLearningApplied(learned.learning.id);
200
+ return { kind: learned.kind, base, learning: learned.learning };
134
201
  }
135
202
  /**
136
203
  * Is this ask a LOCAL CLI COMMAND aimed at the agent itself ("run nuvira
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-gate.js","sourceRoot":"","sources":["../../src/nlu/conversation-gate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation-gate.js","sourceRoot":"","sources":["../../src/nlu/conversation-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Intent confirmation — the self-correction half of routing.
3
+ *
4
+ * THE PROBLEM. When a turn fails, the agent has exactly one reliable way to
5
+ * find out whether the failure was the WORLD's fault (no model, no quota) or its
6
+ * OWN (it read the request wrong and worked on the wrong thing): ask the model
7
+ * what the request actually is. Without that, a misreading is permanent — the
8
+ * same ask runs down the same wrong path on every future message, burning a
9
+ * pipeline run each time, and the user's only recourse is to rephrase.
10
+ *
11
+ * The rules it corrects are deterministic and cheap, which is why they are worth
12
+ * keeping, and blind to the object of a verb, which is why they need this. So the
13
+ * design is: keep the rules on the hot path, and let a CONFIRMED correction
14
+ * override them from then on (`learnings.ts`).
15
+ *
16
+ * THREE DELIBERATE RESTRAINTS
17
+ * 1. It runs only on a REPEATED failure. A single failure mostly says something
18
+ * about the world; probing on every one would spend a quota-limited model to
19
+ * audit a routing decision that was probably fine.
20
+ * 2. It is asked to answer with JSON and nothing else, but the reply is parsed
21
+ * defensively — a model that ignores the format yields NO correction rather
22
+ * than a guessed one.
23
+ * 3. Any error (no model, unparseable answer) means "no correction". Teaching
24
+ * the NLU from a failed probe would encode an outage as a rule, which is the
25
+ * worst possible outcome: the router would get worse exactly when the pool
26
+ * is down.
27
+ */
28
+ import { type NluLearning } from './learnings.js';
29
+ import type { AskKind } from './conversation-gate.js';
30
+ /** What one confirmation established. */
31
+ export interface IntentConfirmResult {
32
+ /** The kind to act on now — the corrected one, or the routed one unchanged. */
33
+ kind: AskKind;
34
+ /** True when the model agreed with the original reading (or gave no verdict). */
35
+ agreed: boolean;
36
+ /** The model's one-line reason, when it gave one. */
37
+ reason?: string;
38
+ /** The learning written, when the reading was corrected. */
39
+ learning?: NluLearning;
40
+ /** True when the probe could not run (no model / unreadable answer). */
41
+ failed?: boolean;
42
+ }
43
+ /**
44
+ * The audit prompt. Kept to a JSON contract with the decision space spelled out,
45
+ * because the model is being asked to judge a routing decision — not to answer
46
+ * the request. Answering it here would leak a second, contradictory answer into
47
+ * the conversation.
48
+ */
49
+ export declare function buildIntentConfirmPrompt(ask: string, routed: AskKind): string;
50
+ /**
51
+ * Read a verdict out of the model's reply.
52
+ *
53
+ * Order matters: an explicit JSON `intent` field is authoritative, then a bare
54
+ * quoted token, then keyword scanning. Anything ambiguous returns no verdict
55
+ * (`understood: false`) so the caller keeps the route it had.
56
+ */
57
+ export declare function parseIntentConfirmReply(raw: string): {
58
+ kind?: AskKind;
59
+ reason?: string;
60
+ understood: boolean;
61
+ };
62
+ /**
63
+ * Ask the model what this ask really needs, and record the correction when it
64
+ * disagrees with the route that just failed.
65
+ *
66
+ * @param callLLM A one-shot model call. Injected so the caller owns routing and
67
+ * tests stay deterministic — and so a probe never piggy-backs on the failover
68
+ * state of the turn that just died.
69
+ */
70
+ export declare function confirmRoutedIntent(input: {
71
+ ask: string;
72
+ routed: AskKind;
73
+ callLLM: (prompt: string) => Promise<string>;
74
+ /** Record the correction as a learning (default: true). */
75
+ record?: boolean;
76
+ now?: number;
77
+ }): Promise<IntentConfirmResult>;
78
+ /** The line a sender sees when the audit CONFIRMS the reading. */
79
+ export declare function intentConfirmedNote(routed: AskKind): string;
80
+ /** The line a sender sees when the audit CORRECTED the reading. */
81
+ export declare function intentCorrectedNote(to: AskKind, reason?: string): string;
82
+ //# sourceMappingURL=intent-confirm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-confirm.d.ts","sourceRoot":"","sources":["../../src/nlu/intent-confirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,yCAAyC;AACzC,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,IAAI,EAAE,OAAO,CAAC;IACd,iFAAiF;IACjF,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAc7E;AAOD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CA0B7G;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,2DAA2D;IAC3D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAmC/B;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAI3D;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAKxE"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Intent confirmation — the self-correction half of routing.
3
+ *
4
+ * THE PROBLEM. When a turn fails, the agent has exactly one reliable way to
5
+ * find out whether the failure was the WORLD's fault (no model, no quota) or its
6
+ * OWN (it read the request wrong and worked on the wrong thing): ask the model
7
+ * what the request actually is. Without that, a misreading is permanent — the
8
+ * same ask runs down the same wrong path on every future message, burning a
9
+ * pipeline run each time, and the user's only recourse is to rephrase.
10
+ *
11
+ * The rules it corrects are deterministic and cheap, which is why they are worth
12
+ * keeping, and blind to the object of a verb, which is why they need this. So the
13
+ * design is: keep the rules on the hot path, and let a CONFIRMED correction
14
+ * override them from then on (`learnings.ts`).
15
+ *
16
+ * THREE DELIBERATE RESTRAINTS
17
+ * 1. It runs only on a REPEATED failure. A single failure mostly says something
18
+ * about the world; probing on every one would spend a quota-limited model to
19
+ * audit a routing decision that was probably fine.
20
+ * 2. It is asked to answer with JSON and nothing else, but the reply is parsed
21
+ * defensively — a model that ignores the format yields NO correction rather
22
+ * than a guessed one.
23
+ * 3. Any error (no model, unparseable answer) means "no correction". Teaching
24
+ * the NLU from a failed probe would encode an outage as a rule, which is the
25
+ * worst possible outcome: the router would get worse exactly when the pool
26
+ * is down.
27
+ */
28
+ import { recordLearning } from './learnings.js';
29
+ /**
30
+ * The audit prompt. Kept to a JSON contract with the decision space spelled out,
31
+ * because the model is being asked to judge a routing decision — not to answer
32
+ * the request. Answering it here would leak a second, contradictory answer into
33
+ * the conversation.
34
+ */
35
+ export function buildIntentConfirmPrompt(ask, routed) {
36
+ return [
37
+ 'You are auditing ONE routing decision. Do not answer the request, do not explain the topic, do not add prose.',
38
+ '',
39
+ `The request was: """${ask.slice(0, 800)}"""`,
40
+ `It was routed as: ${routed === 'pipeline' ? '"coding-task" (the developer pipeline builds software)' : '"chat" (a direct written answer is produced)'}`,
41
+ '',
42
+ 'Decide which one genuinely serves the sender:',
43
+ '- "chat": the sender wants knowledge, advice, content or an explanation delivered as text — a plan, routine, schedule, diet, story, lesson, letter, explanation, comparison, or ANY question. Producing a software project for this would be wrong.',
44
+ '- "coding-task": the sender wants software built or changed in a codebase — an app, script, API, test, fix, refactor, deploy.',
45
+ '',
46
+ 'Reply with JSON only, exactly this shape:',
47
+ '{"intent":"chat"|"coding-task","reason":"<max 12 words>"}',
48
+ ].join('\n');
49
+ }
50
+ /** Phrases that mean "the developer pipeline". */
51
+ const PIPELINE_TOKENS = ['coding-task', 'coding task', 'coding_task', 'pipeline', 'developer', 'software', 'code-task'];
52
+ /** Phrases that mean "answer it directly". */
53
+ const CHAT_TOKENS = ['"chat"', 'chat', 'answer', 'direct', 'prose', 'content'];
54
+ /**
55
+ * Read a verdict out of the model's reply.
56
+ *
57
+ * Order matters: an explicit JSON `intent` field is authoritative, then a bare
58
+ * quoted token, then keyword scanning. Anything ambiguous returns no verdict
59
+ * (`understood: false`) so the caller keeps the route it had.
60
+ */
61
+ export function parseIntentConfirmReply(raw) {
62
+ const text = String(raw ?? '');
63
+ if (!text.trim())
64
+ return { understood: false };
65
+ // 1. An explicit JSON intent field.
66
+ const jsonMatch = /"intent"\s*:\s*"([^"]+)"/i.exec(text);
67
+ const reasonMatch = /"reason"\s*:\s*"([^"]*)"/i.exec(text);
68
+ const reason = reasonMatch?.[1]?.trim().slice(0, 200) || undefined;
69
+ if (jsonMatch) {
70
+ const value = jsonMatch[1].toLowerCase();
71
+ if (PIPELINE_TOKENS.includes(value) || /coding|code|pipeline|developer/.test(value)) {
72
+ return { kind: 'pipeline', reason, understood: true };
73
+ }
74
+ if (CHAT_TOKENS.includes(value) || /chat|answer/.test(value)) {
75
+ return { kind: 'chat', reason, understood: true };
76
+ }
77
+ return { understood: false, ...(reason ? { reason } : {}) };
78
+ }
79
+ // 2. A bare token, which is what a truncated or prose answer usually leaves.
80
+ const lower = text.toLowerCase();
81
+ const hasPipeline = /\bcoding[- _]?task\b|\bpipeline\b|\bdeveloper\b/.test(lower);
82
+ const hasChat = /(^|\W)chat(\W|$)|direct answer|answer directly|prose/.test(lower);
83
+ if (hasPipeline && !hasChat)
84
+ return { kind: 'pipeline', reason, understood: true };
85
+ if (hasChat && !hasPipeline)
86
+ return { kind: 'chat', reason, understood: true };
87
+ return { understood: false, ...(reason ? { reason } : {}) };
88
+ }
89
+ /**
90
+ * Ask the model what this ask really needs, and record the correction when it
91
+ * disagrees with the route that just failed.
92
+ *
93
+ * @param callLLM A one-shot model call. Injected so the caller owns routing and
94
+ * tests stay deterministic — and so a probe never piggy-backs on the failover
95
+ * state of the turn that just died.
96
+ */
97
+ export async function confirmRoutedIntent(input) {
98
+ let raw = '';
99
+ try {
100
+ raw = await input.callLLM(buildIntentConfirmPrompt(input.ask, input.routed));
101
+ }
102
+ catch {
103
+ // The probe is best-effort by contract: a failure means "no verdict".
104
+ return { kind: input.routed, agreed: true, failed: true };
105
+ }
106
+ const parsed = parseIntentConfirmReply(raw);
107
+ if (!parsed.understood || !parsed.kind || parsed.kind === input.routed) {
108
+ return {
109
+ kind: input.routed,
110
+ agreed: true,
111
+ ...(parsed.reason ? { reason: parsed.reason } : {}),
112
+ ...(parsed.understood ? {} : { failed: true }),
113
+ };
114
+ }
115
+ const result = {
116
+ kind: parsed.kind,
117
+ agreed: false,
118
+ ...(parsed.reason ? { reason: parsed.reason } : {}),
119
+ };
120
+ if (input.record !== false) {
121
+ const learning = recordLearning({
122
+ text: input.ask,
123
+ from: input.routed,
124
+ to: parsed.kind,
125
+ ...(parsed.reason ? { reason: parsed.reason } : {}),
126
+ ...(input.now !== undefined ? { now: input.now } : {}),
127
+ });
128
+ if (learning)
129
+ result.learning = learning;
130
+ }
131
+ return result;
132
+ }
133
+ /** The line a sender sees when the audit CONFIRMS the reading. */
134
+ export function intentConfirmedNote(routed) {
135
+ return routed === 'pipeline'
136
+ ? '🧭 I double-checked what you asked for: building this is the right read, so the only problem is model availability.'
137
+ : '🧭 I double-checked what you asked for: this is a question to answer, not code to write, so the only problem is model availability.';
138
+ }
139
+ /** The line a sender sees when the audit CORRECTED the reading. */
140
+ export function intentCorrectedNote(to, reason) {
141
+ const why = reason ? ` (${reason})` : '';
142
+ return to === 'pipeline'
143
+ ? `🧭 I re-read your request${why} — that one needs the build pipeline, not a written answer. Running it now.`
144
+ : `🧭 I re-read your request${why} — that's a question to answer, not code to write. Answering it directly now.`;
145
+ }
146
+ //# sourceMappingURL=intent-confirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intent-confirm.js","sourceRoot":"","sources":["../../src/nlu/intent-confirm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * NLU learnings — the record of what the router READ WRONG, applied to the next
3
+ * ask that means the same thing.
4
+ *
5
+ * WHY THIS EXISTS. Every routing mistake in this codebase had the same shape: a
6
+ * rule keyed off a surface word, the ask was quietly misread, and the mistake
7
+ * was indistinguishable from a fresh ask on the next message. An agent that
8
+ * cannot learn from a confirmed misreading is a frozen ruleset, not something
9
+ * that improves — which is precisely the complaint this module answers.
10
+ *
11
+ * A learning is written ONLY when a misreading has been CONFIRMED by the model
12
+ * (see `intent-confirm.ts`), never from a guess and never from a failure alone:
13
+ * a turn can fail for reasons that say nothing about its intent (quota, network,
14
+ * an oversized prompt), and teaching the router from those would make it worse
15
+ * with every outage. The correction is stored with the ask that earned it, so
16
+ * the store stays inspectable — a human can read exactly which asks were
17
+ * misread and how.
18
+ *
19
+ * MATCHING IS DELIBERATELY NARROW. A learning applies to the same ask, not to a
20
+ * similar-looking one: word order and repeated words are ignored (token set), but
21
+ * nothing else is. A loose match would be worse than no learning at all — the
22
+ * observed object-blindness ("create a *project plan*" vs "create a *plan* for
23
+ * my kid") is exactly the distinction a fuzzy matcher would erase.
24
+ */
25
+ import type { AskKind } from './conversation-gate.js';
26
+ /** One confirmed correction of a routing reading. */
27
+ export interface NluLearning {
28
+ id: string;
29
+ /** Token-set signature of the ask this was learned from (see `signatureOf`). */
30
+ signature: string;
31
+ /** The ask, verbatim — so a stored learning is readable, not a token soup. */
32
+ example: string;
33
+ /** The kind the ask was read AS (the mistake). */
34
+ from: AskKind;
35
+ /** The kind it should have been (the correction). */
36
+ to: AskKind;
37
+ /** How the correction was established. */
38
+ source: 'intent-confirm';
39
+ recordedAt: number;
40
+ /** How many times this learning changed a routing decision. */
41
+ hits: number;
42
+ lastAppliedAt?: number;
43
+ /** The model's one-line reason, kept for the store's audit value. */
44
+ reason?: string;
45
+ }
46
+ /**
47
+ * A token-set signature: lowercase, punctuation removed, stopwords dropped,
48
+ * deduped, sorted. Word order and repetition are intentionally invisible — the
49
+ * SAME ask phrased as "for class 4, create a plan" is the same request — while
50
+ * any word that carries meaning still distinguishes it.
51
+ */
52
+ export declare function signatureOf(text: string | null | undefined): string;
53
+ /**
54
+ * Read the store, cached on mtime. This runs on the routing hot path (every
55
+ * inbound message), so a real file read per ask is not acceptable — and a
56
+ * stale cache is not either, since a learning is written by a DIFFERENT process
57
+ * in the dashboard/gateway case.
58
+ */
59
+ export declare function loadLearnings(): NluLearning[];
60
+ /**
61
+ * Record a confirmed misreading.
62
+ *
63
+ * One entry per (signature, target): re-confirming the same correction refreshes
64
+ * the existing learning instead of accumulating duplicates, and a LATER
65
+ * correction that contradicts an earlier one replaces it — the newest confirmed
66
+ * verdict is the truth, and keeping both would leave the store self-contradictory.
67
+ */
68
+ export declare function recordLearning(input: {
69
+ text: string;
70
+ from: AskKind;
71
+ to: AskKind;
72
+ reason?: string;
73
+ now?: number;
74
+ }): NluLearning | undefined;
75
+ /**
76
+ * The learning that applies to this ask, if any.
77
+ *
78
+ * An exact signature match wins. Otherwise a near-identical ask is accepted —
79
+ * same object, one word added or removed — under all three guards above.
80
+ * Nothing looser: a wrong correction silently misroutes a request, which is the
81
+ * class of bug this store exists to fix.
82
+ */
83
+ export declare function matchLearning(text: string | null | undefined): NluLearning | undefined;
84
+ /**
85
+ * Apply a learning to a verdict. Returns the corrected kind plus the learning
86
+ * that changed it, so the caller can report WHY the route differs from the
87
+ * rules (an unexplained override is its own bug).
88
+ */
89
+ export declare function applyLearning(text: string | null | undefined, verdict: AskKind): {
90
+ kind: AskKind;
91
+ learning: NluLearning;
92
+ } | undefined;
93
+ /** Record one applied hit (best-effort; telemetry must not break routing). */
94
+ export declare function noteLearningApplied(id: string, now?: number): void;
95
+ /** Every stored learning, newest first (CLI / dashboard inspection). */
96
+ export declare function listLearnings(): NluLearning[];
97
+ /** Drop one learning (a correction that turned out wrong must be removable). */
98
+ export declare function removeLearning(id: string): boolean;
99
+ /** Test/ops escape hatch: forget everything. */
100
+ export declare function clearLearnings(): void;
101
+ //# sourceMappingURL=learnings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnings.d.ts","sourceRoot":"","sources":["../../src/nlu/learnings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,IAAI,EAAE,OAAO,CAAC;IACd,qDAAqD;IACrD,EAAE,EAAE,OAAO,CAAC;IACZ,0CAA0C;IAC1C,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAyBD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOnE;AAeD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,WAAW,EAAE,CAkB7C;AA6BD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,WAAW,GAAG,SAAS,CA6B1B;AAwCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAsBtF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,OAAO,EAAE,OAAO,GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,SAAS,CAKtD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI,CAW9E;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,WAAW,EAAE,CAE7C;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAMlD;AAED,gDAAgD;AAChD,wBAAgB,cAAc,IAAI,IAAI,CAErC"}