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,147 @@
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 { existsSync } from 'node:fs';
19
+ import { isAbsolute, resolve } from 'node:path';
20
+ import { findLatestJob, formatProgress, jobProgress } from './long-form.js';
21
+ import { startUnattendedJob } from './unattended-job.js';
22
+ /**
23
+ * Weight of the content phase in a composite deliverable's percentage.
24
+ *
25
+ * The content IS the deliverable, so it dominates; the surrounding artifacts
26
+ * are the remaining third. Weighting them any lower would let a beautiful
27
+ * empty shell sit at 99% after one chapter.
28
+ */
29
+ export const CONTENT_WEIGHT = 0.7;
30
+ /**
31
+ * How many expected artifacts exist on disk (relative paths resolved against
32
+ * the project root).
33
+ *
34
+ * Split out from the job-shaped wrapper because the orchestrator asks the same
35
+ * question at planning time — "has this deliverable already been produced?" —
36
+ * before any job record exists.
37
+ */
38
+ export function artifactsPresence(projectPath, expected) {
39
+ const paths = expected ?? [];
40
+ if (paths.length === 0)
41
+ return { present: 0, total: 0, missing: [] };
42
+ const missing = [];
43
+ let present = 0;
44
+ for (const rel of paths) {
45
+ const abs = isAbsolute(rel) ? rel : resolve(projectPath, rel);
46
+ if (existsSync(abs))
47
+ present++;
48
+ else
49
+ missing.push(rel);
50
+ }
51
+ return { present, total: paths.length, missing };
52
+ }
53
+ /** How many of a job's expected artifacts exist on disk. */
54
+ export function countPresentArtifacts(job) {
55
+ return artifactsPresence(job.projectPath, job.expectedArtifacts);
56
+ }
57
+ /**
58
+ * Measure the job from disk.
59
+ *
60
+ * Never throws: a measurement failure must degrade to "no progress recorded",
61
+ * which the runner correctly treats as a stall, rather than crashing the drain
62
+ * that is holding the user's promised work.
63
+ */
64
+ export function measureUnattendedProgress(job) {
65
+ try {
66
+ const ledger = findLatestJob(job.projectPath);
67
+ const artifacts = countPresentArtifacts(job);
68
+ if (!ledger) {
69
+ // No content ledger for this project: either the job is a phased build
70
+ // with no prose, or the first batch has not recorded anything yet. The
71
+ // artifacts are then the only honest signal.
72
+ if (artifacts.total > 0) {
73
+ const pct = Math.round((artifacts.present / artifacts.total) * 100);
74
+ return {
75
+ progress: pct,
76
+ progressLine: `${artifacts.present}/${artifacts.total} deliverable files present`,
77
+ finished: artifacts.present === artifacts.total,
78
+ };
79
+ }
80
+ return { progress: 0, progressLine: 'no progress recorded yet' };
81
+ }
82
+ const contentProgress = jobProgress(ledger);
83
+ const contentPercent = contentProgress.percent;
84
+ if (artifacts.total === 0) {
85
+ return {
86
+ progress: contentPercent,
87
+ progressLine: formatProgress(ledger),
88
+ finished: contentProgress.complete,
89
+ };
90
+ }
91
+ const artifactPercent = Math.round((artifacts.present / artifacts.total) * 100);
92
+ const combined = Math.round(contentPercent * CONTENT_WEIGHT + artifactPercent * (1 - CONTENT_WEIGHT));
93
+ const finished = contentProgress.complete && artifacts.present === artifacts.total;
94
+ return {
95
+ progress: finished ? 100 : combined,
96
+ progressLine: `${formatProgress(ledger)} · ${artifacts.present}/${artifacts.total} deliverable files` +
97
+ (artifacts.missing.length > 0 && artifacts.missing.length <= 3
98
+ ? ` (missing ${artifacts.missing.join(', ')})`
99
+ : ''),
100
+ finished,
101
+ };
102
+ }
103
+ catch {
104
+ return { progress: 0, progressLine: 'progress measurement unavailable' };
105
+ }
106
+ }
107
+ /**
108
+ * Is the deliverable complete right now?
109
+ *
110
+ * Used by surfaces to decide whether to keep scheduling a job at all — a
111
+ * resumed "continue" turn that finds the work already done must not start a
112
+ * second book (the ledger's oldest failure mode).
113
+ */
114
+ export function isDeliverableComplete(job) {
115
+ const measured = measureUnattendedProgress(job);
116
+ return measured.finished === true;
117
+ }
118
+ /**
119
+ * Schedule the continuation of a run that ended with unfinished work — the
120
+ * replacement for asking the user to type "continue".
121
+ *
122
+ * Returns the scheduled job, or `null` when there is nothing left to do. That
123
+ * second case matters as much as the first: a run that finished its deliverable
124
+ * must not enqueue a continuation that would immediately re-run and rebuild it.
125
+ *
126
+ * @param pending the orchestrator's `result.pendingWork` (may be undefined)
127
+ * @param surface who to report to and which drain owns the work
128
+ */
129
+ export function scheduleFromPendingWork(pending, surface, options = {}) {
130
+ if (!pending)
131
+ return null;
132
+ const presence = artifactsPresence(pending.projectPath, pending.expectedArtifacts);
133
+ const filesComplete = presence.total === 0 || presence.present === presence.total;
134
+ if (pending.percent >= 100 && filesComplete)
135
+ return null;
136
+ return startUnattendedJob({
137
+ kind: pending.kind,
138
+ goal: pending.goal,
139
+ projectPath: pending.projectPath,
140
+ surface,
141
+ continuationPrompt: pending.continuationPrompt,
142
+ ...(pending.expectedArtifacts ? { expectedArtifacts: pending.expectedArtifacts } : {}),
143
+ ...(options.deadlineMs !== undefined ? { deadlineMs: options.deadlineMs } : {}),
144
+ ...(options.notify !== undefined ? { notify: options.notify } : {}),
145
+ }).job;
146
+ }
147
+ //# sourceMappingURL=unattended-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unattended-progress.js","sourceRoot":"","sources":["../../src/learning/unattended-progress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Working state — the per-project ledger that gives the agent a memory of
3
+ * what it changed, whether the change was verified, and what the user has
4
+ * already reported broken (enterprise-grade hardening, G3 + G4).
5
+ *
6
+ * WHY THIS EXISTS (the trace audit):
7
+ * The calculator session ran 15 turns over 77 minutes. Each turn RE-DERIVED
8
+ * the same root cause from scratch (input tokens grew 2,172 → 7,317), the
9
+ * model flip-flopped on the diagnosis (found the `id`-mapping bug in turn 23,
10
+ * declared the CSS "perfectly valid" in turn 25), and its own fixes kept
11
+ * undoing earlier ones (turn 17 made the converter vanish; turn 21 broke the
12
+ * dropdowns). Nothing carried the STATE of the work across turns — only the
13
+ * raw transcript, which is the worst possible carrier.
14
+ *
15
+ * This ledger is deliberately DETERMINISTIC and LLM-free: it records facts
16
+ * (files touched, verified-or-not, user-reported regressions) and formats a
17
+ * compact block that is injected at the start of the next turn. No summarizer,
18
+ * no latency, no drift — the same philosophy as `trimThreadBudget`.
19
+ *
20
+ * Storage: `~/.nuvira/memory/working-state.json`, keyed by project path.
21
+ * Honours `NUVIRA_MEMORY_DIR` / `BUFF_MEMORY_DIR` like the reasoning traces.
22
+ * Every write is best-effort — a ledger write must NEVER break a turn.
23
+ */
24
+ /** One project's working state — everything worth remembering between turns. */
25
+ export interface ProjectWorkingState {
26
+ /** Absolute project path (the ledger key). */
27
+ projectPath: string;
28
+ /** Files the agent changed, most-recent last (deduped, capped). */
29
+ filesTouched: string[];
30
+ /** Distinct tools used across the session (for a quick capability read). */
31
+ toolsUsed: string[];
32
+ /** Turns recorded against this project. */
33
+ turns: number;
34
+ /**
35
+ * Consecutive unverified edit turns still outstanding. Reset to 0 the
36
+ * moment a turn verifies, because a verified state supersedes the backlog.
37
+ */
38
+ unverifiedEdits: number;
39
+ /** Epoch ms of the last turn that verified (ran tests/typecheck/browser). */
40
+ lastVerifiedAt?: number;
41
+ /**
42
+ * What the USER reported broken, newest last (deduped, capped). This is the
43
+ * regression memory: the exact thing the agent must not re-break or
44
+ * re-derive next turn.
45
+ */
46
+ openIssues: string[];
47
+ /** How many turns carried a regression signal ("still", "same issue"...). */
48
+ corrections: number;
49
+ /** Epoch ms of the last write. */
50
+ updatedAt: number;
51
+ }
52
+ /** A single finished turn, as recorded by the caller. */
53
+ export interface TurnRecord {
54
+ /** Files edited this turn (any order; deduped + capped on write). */
55
+ filesTouched?: string[];
56
+ /** Tools that ran successfully this turn. */
57
+ toolsUsed?: readonly string[];
58
+ /** A verification tool ran successfully this turn. */
59
+ verified?: boolean;
60
+ /** The turn mutated the workspace and verified nothing. */
61
+ unverifiedEdit?: boolean;
62
+ /** The user's message for this turn (scanned for a regression signal). */
63
+ userMessage?: string;
64
+ }
65
+ /** Cap on remembered files per project (oldest drop first). */
66
+ export declare const MAX_FILES = 40;
67
+ /** Cap on remembered user-reported issues per project. */
68
+ export declare const MAX_ISSUES = 8;
69
+ /** Cap on remembered tools per project. */
70
+ export declare const MAX_TOOLS = 24;
71
+ /** Normalize a project path so the same project always maps to one entry. */
72
+ export declare function normalizeProjectPath(projectPath: string): string;
73
+ /**
74
+ * Does the user's message report that something is STILL broken?
75
+ *
76
+ * This is the correction signal the audit was built on: the user said
77
+ * "still same issue", "i don't see any change", "converter is not having any
78
+ * conversion logic" six turns in a row. Detecting it lets the ledger keep the
79
+ * issue in front of the agent instead of letting it re-diagnose from zero.
80
+ *
81
+ * Deliberately conservative: a bare question ("does this work?") is not a
82
+ * regression, and neither is a fresh feature request.
83
+ */
84
+ export declare function detectRegressionSignal(userMessage: string): boolean;
85
+ /** Read one project's state (null when nothing has been recorded). */
86
+ export declare function getWorkingState(projectPath: string): ProjectWorkingState | null;
87
+ /**
88
+ * Record a finished turn and return the updated state. Best-effort: on any
89
+ * failure the caller still gets a usable (possibly unchanged) state.
90
+ *
91
+ * The rules encode the audit's lessons:
92
+ * - Touched files accumulate and are capped oldest-first.
93
+ * - A VERIFIED turn clears the unverified backlog AND the open issues (the
94
+ * fix is proven, so the previous report is answered).
95
+ * - An UNVERIFIED edit turn increments the backlog — the debt the agent owes.
96
+ * - A regression signal keeps the user's own words as an open issue.
97
+ */
98
+ export declare function recordWorkingState(projectPath: string, turn: TurnRecord): ProjectWorkingState;
99
+ /** Forget one project (or the whole ledger when no path is given). */
100
+ export declare function clearWorkingState(projectPath?: string): void;
101
+ /**
102
+ * Format the state as a compact, model-readable block ('' when there is
103
+ * nothing worth saying — a pristine project must not add noise to the prompt).
104
+ *
105
+ * Kept short on purpose: this rides in EVERY turn, so a verbose block would
106
+ * recreate the very drift it exists to prevent.
107
+ */
108
+ export declare function formatWorkingState(state: ProjectWorkingState | null, now?: number): string;
109
+ //# sourceMappingURL=working-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-state.d.ts","sourceRoot":"","sources":["../../src/learning/working-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAUD,+DAA+D;AAC/D,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,0DAA0D;AAC1D,eAAO,MAAM,UAAU,IAAI,CAAC;AAC5B,2CAA2C;AAC3C,eAAO,MAAM,SAAS,KAAK,CAAC;AAkC5B,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMhE;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAkBnE;AAID,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAG/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,mBAAmB,CAoE7F;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ5D;AAaD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAyBtG"}
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Working state — the per-project ledger that gives the agent a memory of
3
+ * what it changed, whether the change was verified, and what the user has
4
+ * already reported broken (enterprise-grade hardening, G3 + G4).
5
+ *
6
+ * WHY THIS EXISTS (the trace audit):
7
+ * The calculator session ran 15 turns over 77 minutes. Each turn RE-DERIVED
8
+ * the same root cause from scratch (input tokens grew 2,172 → 7,317), the
9
+ * model flip-flopped on the diagnosis (found the `id`-mapping bug in turn 23,
10
+ * declared the CSS "perfectly valid" in turn 25), and its own fixes kept
11
+ * undoing earlier ones (turn 17 made the converter vanish; turn 21 broke the
12
+ * dropdowns). Nothing carried the STATE of the work across turns — only the
13
+ * raw transcript, which is the worst possible carrier.
14
+ *
15
+ * This ledger is deliberately DETERMINISTIC and LLM-free: it records facts
16
+ * (files touched, verified-or-not, user-reported regressions) and formats a
17
+ * compact block that is injected at the start of the next turn. No summarizer,
18
+ * no latency, no drift — the same philosophy as `trimThreadBudget`.
19
+ *
20
+ * Storage: `~/.nuvira/memory/working-state.json`, keyed by project path.
21
+ * Honours `NUVIRA_MEMORY_DIR` / `BUFF_MEMORY_DIR` like the reasoning traces.
22
+ * Every write is best-effort — a ledger write must NEVER break a turn.
23
+ */
24
+ import { envBuff, resolveNuviraHome } from '../config/paths.js';
25
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
26
+ import { join, resolve } from 'node:path';
27
+ // ─── Constants ──────────────────────────────────────────────────────────────
28
+ const CURRENT_VERSION = 1;
29
+ /** Cap on remembered files per project (oldest drop first). */
30
+ export const MAX_FILES = 40;
31
+ /** Cap on remembered user-reported issues per project. */
32
+ export const MAX_ISSUES = 8;
33
+ /** Cap on remembered tools per project. */
34
+ export const MAX_TOOLS = 24;
35
+ // ─── Storage ────────────────────────────────────────────────────────────────
36
+ function memoryDir() {
37
+ return envBuff('MEMORY_DIR') || join(resolveNuviraHome(), 'memory');
38
+ }
39
+ function statePath() {
40
+ return join(memoryDir(), 'working-state.json');
41
+ }
42
+ function readFileSafe() {
43
+ try {
44
+ if (!existsSync(statePath()))
45
+ return { version: CURRENT_VERSION, projects: {} };
46
+ const data = JSON.parse(readFileSync(statePath(), 'utf-8'));
47
+ if (!data || typeof data !== 'object' || !data.projects || typeof data.projects !== 'object') {
48
+ return { version: CURRENT_VERSION, projects: {} };
49
+ }
50
+ return data;
51
+ }
52
+ catch {
53
+ return { version: CURRENT_VERSION, projects: {} };
54
+ }
55
+ }
56
+ function writeFileSafe(data) {
57
+ try {
58
+ if (!existsSync(memoryDir()))
59
+ mkdirSync(memoryDir(), { recursive: true });
60
+ writeFileSync(statePath(), JSON.stringify(data, null, 2), 'utf-8');
61
+ }
62
+ catch {
63
+ // Best-effort — the ledger must never break a turn.
64
+ }
65
+ }
66
+ /** Normalize a project path so the same project always maps to one entry. */
67
+ export function normalizeProjectPath(projectPath) {
68
+ try {
69
+ return resolve(projectPath);
70
+ }
71
+ catch {
72
+ return projectPath;
73
+ }
74
+ }
75
+ // ─── Regression signals ─────────────────────────────────────────────────────
76
+ /**
77
+ * Does the user's message report that something is STILL broken?
78
+ *
79
+ * This is the correction signal the audit was built on: the user said
80
+ * "still same issue", "i don't see any change", "converter is not having any
81
+ * conversion logic" six turns in a row. Detecting it lets the ledger keep the
82
+ * issue in front of the agent instead of letting it re-diagnose from zero.
83
+ *
84
+ * Deliberately conservative: a bare question ("does this work?") is not a
85
+ * regression, and neither is a fresh feature request.
86
+ */
87
+ export function detectRegressionSignal(userMessage) {
88
+ const text = (userMessage || '').trim();
89
+ if (!text)
90
+ return false;
91
+ if (text.length > 600)
92
+ return false; // a long brief is a new task, not a correction
93
+ const patterns = [
94
+ /\bstill\b/i,
95
+ /\bsame (?:issue|problem|bug|thing|error|behaviou?r)\b/i,
96
+ /\bnot (?:working|fixed|visible|showing|appearing|there)\b/i,
97
+ /\bdoesn'?t work\b/i,
98
+ /\bdidn'?t work\b/i,
99
+ /\bno (?:change|changes|luck|effect)\b/i,
100
+ /\bi (?:still )?(?:can'?t|cannot|don'?t) (?:see|find)\b/i,
101
+ /\byou (?:broke|broke?n)\b/i,
102
+ /\bregress(?:ion|ed)?\b/i,
103
+ /\bit'?s (?:blank|empty|gone|missing)\b/i,
104
+ /\bagain\b/i,
105
+ ];
106
+ return patterns.some((re) => re.test(text));
107
+ }
108
+ // ─── API ────────────────────────────────────────────────────────────────────
109
+ /** Read one project's state (null when nothing has been recorded). */
110
+ export function getWorkingState(projectPath) {
111
+ const data = readFileSafe();
112
+ return data.projects[normalizeProjectPath(projectPath)] ?? null;
113
+ }
114
+ /**
115
+ * Record a finished turn and return the updated state. Best-effort: on any
116
+ * failure the caller still gets a usable (possibly unchanged) state.
117
+ *
118
+ * The rules encode the audit's lessons:
119
+ * - Touched files accumulate and are capped oldest-first.
120
+ * - A VERIFIED turn clears the unverified backlog AND the open issues (the
121
+ * fix is proven, so the previous report is answered).
122
+ * - An UNVERIFIED edit turn increments the backlog — the debt the agent owes.
123
+ * - A regression signal keeps the user's own words as an open issue.
124
+ */
125
+ export function recordWorkingState(projectPath, turn) {
126
+ const key = normalizeProjectPath(projectPath);
127
+ const data = readFileSafe();
128
+ const now = Date.now();
129
+ const prev = data.projects[key] ??
130
+ {
131
+ projectPath: key,
132
+ filesTouched: [],
133
+ toolsUsed: [],
134
+ turns: 0,
135
+ unverifiedEdits: 0,
136
+ openIssues: [],
137
+ corrections: 0,
138
+ updatedAt: now,
139
+ };
140
+ const next = { ...prev, filesTouched: [...prev.filesTouched], toolsUsed: [...prev.toolsUsed], openIssues: [...prev.openIssues] };
141
+ next.turns = prev.turns + 1;
142
+ next.updatedAt = now;
143
+ // Files — dedupe (move-to-end so recency is meaningful) then cap.
144
+ for (const f of turn.filesTouched ?? []) {
145
+ if (!f)
146
+ continue;
147
+ const idx = next.filesTouched.indexOf(f);
148
+ if (idx !== -1)
149
+ next.filesTouched.splice(idx, 1);
150
+ next.filesTouched.push(f);
151
+ }
152
+ if (next.filesTouched.length > MAX_FILES) {
153
+ next.filesTouched = next.filesTouched.slice(-MAX_FILES);
154
+ }
155
+ // Tools — distinct, capped.
156
+ for (const t of turn.toolsUsed ?? []) {
157
+ if (t && !next.toolsUsed.includes(t))
158
+ next.toolsUsed.push(t);
159
+ }
160
+ if (next.toolsUsed.length > MAX_TOOLS) {
161
+ next.toolsUsed = next.toolsUsed.slice(-MAX_TOOLS);
162
+ }
163
+ // Verification debt.
164
+ if (turn.verified) {
165
+ next.unverifiedEdits = 0;
166
+ next.lastVerifiedAt = now;
167
+ // A proven fix answers the open reports.
168
+ next.openIssues = [];
169
+ }
170
+ else if (turn.unverifiedEdit) {
171
+ next.unverifiedEdits = prev.unverifiedEdits + 1;
172
+ }
173
+ // Regression memory — keep the user's own words.
174
+ const msg = (turn.userMessage || '').trim();
175
+ if (msg && detectRegressionSignal(msg)) {
176
+ next.corrections = prev.corrections + 1;
177
+ const short = msg.replace(/\s+/g, ' ').slice(0, 200);
178
+ if (!next.openIssues.includes(short))
179
+ next.openIssues.push(short);
180
+ if (next.openIssues.length > MAX_ISSUES)
181
+ next.openIssues = next.openIssues.slice(-MAX_ISSUES);
182
+ }
183
+ data.projects[key] = next;
184
+ // Bound the file: keep the most recently updated projects only.
185
+ const entries = Object.entries(data.projects);
186
+ if (entries.length > 50) {
187
+ entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
188
+ data.projects = Object.fromEntries(entries.slice(0, 50));
189
+ }
190
+ writeFileSafe(data);
191
+ return next;
192
+ }
193
+ /** Forget one project (or the whole ledger when no path is given). */
194
+ export function clearWorkingState(projectPath) {
195
+ if (!projectPath) {
196
+ writeFileSafe({ version: CURRENT_VERSION, projects: {} });
197
+ return;
198
+ }
199
+ const data = readFileSafe();
200
+ delete data.projects[normalizeProjectPath(projectPath)];
201
+ writeFileSafe(data);
202
+ }
203
+ // ─── Formatting (the injected block) ────────────────────────────────────────
204
+ function relativeAge(ts, now) {
205
+ const mins = Math.max(0, Math.round((now - ts) / 60_000));
206
+ if (mins < 1)
207
+ return 'just now';
208
+ if (mins < 60)
209
+ return `${mins}m ago`;
210
+ const hours = Math.round(mins / 60);
211
+ if (hours < 24)
212
+ return `${hours}h ago`;
213
+ return `${Math.round(hours / 24)}d ago`;
214
+ }
215
+ /**
216
+ * Format the state as a compact, model-readable block ('' when there is
217
+ * nothing worth saying — a pristine project must not add noise to the prompt).
218
+ *
219
+ * Kept short on purpose: this rides in EVERY turn, so a verbose block would
220
+ * recreate the very drift it exists to prevent.
221
+ */
222
+ export function formatWorkingState(state, now = Date.now()) {
223
+ if (!state)
224
+ return '';
225
+ const lines = [];
226
+ if (state.filesTouched.length > 0) {
227
+ const files = state.filesTouched.slice(-8);
228
+ lines.push(`• Files changed this session (${state.filesTouched.length}): ${files.join(', ')}${state.filesTouched.length > files.length ? ', …' : ''}`);
229
+ }
230
+ if (state.unverifiedEdits > 0) {
231
+ lines.push(`• ⚠️ ${state.unverifiedEdits} edit turn(s) were NEVER verified — do not assume they work; re-check before building on them.`);
232
+ }
233
+ if (state.lastVerifiedAt !== undefined) {
234
+ lines.push(`• Last verified: ${relativeAge(state.lastVerifiedAt, now)} (a test/typecheck/browser run passed).`);
235
+ }
236
+ if (state.openIssues.length > 0) {
237
+ const latest = state.openIssues[state.openIssues.length - 1];
238
+ lines.push(`• User has reported ${state.corrections} regression(s). Most recent report: "${latest}" — fix THIS, do not re-diagnose from scratch.`);
239
+ }
240
+ if (lines.length === 0)
241
+ return '';
242
+ return `[Working state — carried from previous turns in THIS project]\n${lines.join('\n')}`;
243
+ }
244
+ //# sourceMappingURL=working-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-state.js","sourceRoot":"","sources":["../../src/learning/working-state.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,30 @@ import { type ParsedRequest } from './parser.js';
34
34
  import { type NluLearning } from './learnings.js';
35
35
  /** True when the ask carries an unambiguous coding action (task, not question). */
36
36
  export declare function hasCodingAction(text: string): boolean;
37
+ /**
38
+ * Is this a LONG-FORM authored deliverable — content the pipeline must build in
39
+ * bounded units rather than answer in one reply?
40
+ *
41
+ * WHY THIS EXISTS (live evidence): "write a 12 page story called Kharig Nights
42
+ * about a village boy who finds a lamp in a banyan root" was read as a
43
+ * CONVERSATIONAL question — the NLU maps `write` to a chat action — and answered
44
+ * in a single reply, bypassing the unit ledger entirely. For 12 pages that
45
+ * looked acceptable; for "write a 200 page book" it is fatal: the ask never
46
+ * reaches the machinery that decomposes it, so nothing measures progress,
47
+ * nothing resumes, and nothing continues unattended. A long-form deliverable is
48
+ * not a question, whatever verb introduces it.
49
+ *
50
+ * BOTH conditions are required:
51
+ * 1. an EXPLICIT magnitude (pages/chapters/words/sections) splitting into more
52
+ * than one unit — "write a story" keeps answering directly, and the
53
+ * DEFAULT chapter count invented for a bare "book" does not count as one;
54
+ * 2. the deliverable classifier reads the ask as authored content. Without
55
+ * this, "write a 20 page plan for my child" would be pushed into a pipeline
56
+ * whose authored path does not recognise it — reintroducing exactly the
57
+ * category error (plan a program for prose) this workstream exists to
58
+ * remove.
59
+ */
60
+ export declare function isLongFormDeliverable(text: string | null | undefined): boolean;
37
61
  /**
38
62
  * The P0.5 gate: return true when the ask is a conversational question that
39
63
  * must be ANSWERED DIRECTLY and must NEVER run the multi-agent pipeline
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-gate.d.ts","sourceRoot":"","sources":["../../src/nlu/conversation-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA0BtF,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAyBrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAOjF;AAED,uEAAuE;AACvE,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAET;AAED,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,0DAA0D;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,CAAC,EAAE,aAAa,GACrB,kBAAkB,CAepB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM7E"}
1
+ {"version":3,"file":"conversation-gate.d.ts","sourceRoot":"","sources":["../../src/nlu/conversation-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAMnE,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA0BtF,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAyBrD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAS9E;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAUjF;AAED,uEAAuE;AACvE,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAET;AAED,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,0DAA0D;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,MAAM,CAAC,EAAE,aAAa,GACrB,kBAAkB,CAiBpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAM7E"}
@@ -33,6 +33,8 @@
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';
36
38
  // The learned corrections. A plain store (mtime-cached, no routing graph), so
37
39
  // consulting it on every ask costs a comparison, not a read.
38
40
  import { applyLearning, noteLearningApplied } from './learnings.js';
@@ -82,6 +84,42 @@ export function hasCodingAction(text) {
82
84
  .replace(/^(?:(?:please|now|next|then|ok|okay|so|also|and)\s*,?\s*)+/i, '');
83
85
  return TASK_VERB_AT_START.test(action) || TASK_VERB_AFTER_PREFIX.test(action);
84
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
+ }
85
123
  /**
86
124
  * The P0.5 gate: return true when the ask is a conversational question that
87
125
  * must be ANSWERED DIRECTLY and must NEVER run the multi-agent pipeline
@@ -98,6 +136,10 @@ export function isConversationalQuestion(text) {
98
136
  return false;
99
137
  if (isTrivialPrompt(t))
100
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;
101
143
  if (hasCodingAction(t))
102
144
  return false;
103
145
  const parsed = parseRequestSync(t);
@@ -144,11 +186,13 @@ export function explainAskKind(text, parsed) {
144
186
  return { kind: 'chat', base: 'chat' };
145
187
  if (isTrivialPrompt(t))
146
188
  return { kind: 'chat', base: 'chat' };
147
- const base = hasCodingAction(t)
189
+ const base = isLongFormDeliverable(t)
148
190
  ? 'pipeline'
149
- : (parsed ?? parseRequestSync(t)).action.run === 'pipeline'
191
+ : hasCodingAction(t)
150
192
  ? 'pipeline'
151
- : 'chat';
193
+ : (parsed ?? parseRequestSync(t)).action.run === 'pipeline'
194
+ ? 'pipeline'
195
+ : 'chat';
152
196
  const learned = applyLearning(t, base);
153
197
  if (!learned)
154
198
  return { kind: base, base };