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.
- package/dist/agents/agents/reasoner.d.ts +8 -0
- package/dist/agents/agents/reasoner.d.ts.map +1 -1
- package/dist/agents/agents/reasoner.js +53 -2
- package/dist/agents/agents/reasoner.js.map +1 -1
- package/dist/agents/agents/writer.d.ts +24 -0
- package/dist/agents/agents/writer.d.ts.map +1 -1
- package/dist/agents/agents/writer.js +194 -0
- package/dist/agents/agents/writer.js.map +1 -1
- package/dist/agents/composite-plan.d.ts +143 -0
- package/dist/agents/composite-plan.d.ts.map +1 -0
- package/dist/agents/composite-plan.js +399 -0
- package/dist/agents/composite-plan.js.map +1 -0
- package/dist/agents/long-form-plan.d.ts +156 -0
- package/dist/agents/long-form-plan.d.ts.map +1 -0
- package/dist/agents/long-form-plan.js +274 -0
- package/dist/agents/long-form-plan.js.map +1 -0
- package/dist/agents/orchestrator.d.ts +107 -0
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +501 -35
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/agents/prompt-assembly.d.ts +8 -0
- package/dist/agents/prompt-assembly.d.ts.map +1 -1
- package/dist/agents/prompt-assembly.js +17 -0
- package/dist/agents/prompt-assembly.js.map +1 -1
- package/dist/cli/chat.d.ts +27 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +116 -6
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/execute.d.ts +12 -0
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +105 -2
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/models.d.ts.map +1 -1
- package/dist/cli/models.js +10 -0
- package/dist/cli/models.js.map +1 -1
- package/dist/cli/trace.d.ts.map +1 -1
- package/dist/cli/trace.js +27 -0
- package/dist/cli/trace.js.map +1 -1
- package/dist/gateway/registry.d.ts +31 -0
- package/dist/gateway/registry.d.ts.map +1 -1
- package/dist/gateway/registry.js +143 -13
- package/dist/gateway/registry.js.map +1 -1
- package/dist/inference/model-entitlement.d.ts +46 -0
- package/dist/inference/model-entitlement.d.ts.map +1 -0
- package/dist/inference/model-entitlement.js +98 -0
- package/dist/inference/model-entitlement.js.map +1 -0
- package/dist/learning/autonomy-policy.d.ts +334 -0
- package/dist/learning/autonomy-policy.d.ts.map +1 -0
- package/dist/learning/autonomy-policy.js +500 -0
- package/dist/learning/autonomy-policy.js.map +1 -0
- package/dist/learning/credential-fingerprint.d.ts +58 -0
- package/dist/learning/credential-fingerprint.d.ts.map +1 -0
- package/dist/learning/credential-fingerprint.js +126 -0
- package/dist/learning/credential-fingerprint.js.map +1 -0
- package/dist/learning/deliverable-class.d.ts +130 -0
- package/dist/learning/deliverable-class.d.ts.map +1 -0
- package/dist/learning/deliverable-class.js +432 -0
- package/dist/learning/deliverable-class.js.map +1 -0
- package/dist/learning/long-form.d.ts +252 -0
- package/dist/learning/long-form.d.ts.map +1 -0
- package/dist/learning/long-form.js +510 -0
- package/dist/learning/long-form.js.map +1 -0
- package/dist/learning/model-first-router.d.ts +17 -0
- package/dist/learning/model-first-router.d.ts.map +1 -1
- package/dist/learning/model-first-router.js +27 -0
- package/dist/learning/model-first-router.js.map +1 -1
- package/dist/learning/model-registry.d.ts +66 -4
- package/dist/learning/model-registry.d.ts.map +1 -1
- package/dist/learning/model-registry.js +67 -6
- package/dist/learning/model-registry.js.map +1 -1
- package/dist/learning/model-warmup.d.ts +97 -2
- package/dist/learning/model-warmup.d.ts.map +1 -1
- package/dist/learning/model-warmup.js +165 -60
- package/dist/learning/model-warmup.js.map +1 -1
- package/dist/learning/prompt-layers.d.ts +61 -0
- package/dist/learning/prompt-layers.d.ts.map +1 -0
- package/dist/learning/prompt-layers.js +140 -0
- package/dist/learning/prompt-layers.js.map +1 -0
- package/dist/learning/provider-limits.d.ts +66 -0
- package/dist/learning/provider-limits.d.ts.map +1 -0
- package/dist/learning/provider-limits.js +184 -0
- package/dist/learning/provider-limits.js.map +1 -0
- package/dist/learning/reasoning-trace.d.ts +36 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +36 -2
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/resilient-call.d.ts +36 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +80 -5
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/unattended-job.d.ts +293 -0
- package/dist/learning/unattended-job.d.ts.map +1 -0
- package/dist/learning/unattended-job.js +544 -0
- package/dist/learning/unattended-job.js.map +1 -0
- package/dist/learning/unattended-progress.d.ts +95 -0
- package/dist/learning/unattended-progress.d.ts.map +1 -0
- package/dist/learning/unattended-progress.js +147 -0
- package/dist/learning/unattended-progress.js.map +1 -0
- package/dist/learning/working-state.d.ts +109 -0
- package/dist/learning/working-state.d.ts.map +1 -0
- package/dist/learning/working-state.js +244 -0
- package/dist/learning/working-state.js.map +1 -0
- package/dist/nlu/conversation-gate.d.ts +24 -0
- package/dist/nlu/conversation-gate.d.ts.map +1 -1
- package/dist/nlu/conversation-gate.js +47 -3
- package/dist/nlu/conversation-gate.js.map +1 -1
- package/dist/tools/coding-tools.d.ts.map +1 -1
- package/dist/tools/coding-tools.js +95 -19
- package/dist/tools/coding-tools.js.map +1 -1
- package/dist/tools/edit-verification.d.ts +125 -0
- package/dist/tools/edit-verification.d.ts.map +1 -0
- package/dist/tools/edit-verification.js +237 -0
- package/dist/tools/edit-verification.js.map +1 -0
- package/dist/tools/git-tool.d.ts.map +1 -1
- package/dist/tools/git-tool.js +37 -4
- package/dist/tools/git-tool.js.map +1 -1
- package/dist/tools/registry.d.ts +30 -2
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +40 -11
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/run-cli.d.ts.map +1 -1
- package/dist/tools/run-cli.js +40 -13
- package/dist/tools/run-cli.js.map +1 -1
- package/dist/tools/run-terminal.d.ts +6 -1
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +158 -18
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts +35 -0
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +141 -1
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/web-dashboard/chat-console.d.ts +6 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/chat-retry.d.ts.map +1 -1
- package/dist/web-dashboard/chat-retry.js +10 -2
- package/dist/web-dashboard/chat-retry.js.map +1 -1
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +80 -8
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +81 -4
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js → index-kCUkORm7.js} +2 -2
- package/src/web-dashboard/public/assets/{index-Co7Hk2FT.js.map → index-kCUkORm7.js.map} +1 -1
- 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;
|
|
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 =
|
|
189
|
+
const base = isLongFormDeliverable(t)
|
|
148
190
|
? 'pipeline'
|
|
149
|
-
: (
|
|
191
|
+
: hasCodingAction(t)
|
|
150
192
|
? 'pipeline'
|
|
151
|
-
: '
|
|
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 };
|