@sonnechasser/ntrp 1.5.2 → 1.5.7
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/index.js +2077 -479
- package/dist/mcp/server.js +1678 -141
- package/package.json +2 -1
package/dist/mcp/server.js
CHANGED
|
@@ -74,6 +74,17 @@ function formatCurrency(value) {
|
|
|
74
74
|
if (value >= 1e3) return `$${(value / 1e3).toFixed(0)}K`;
|
|
75
75
|
return `$${value.toFixed(0)}`;
|
|
76
76
|
}
|
|
77
|
+
function formatConstraintLine(aggregate) {
|
|
78
|
+
if (!aggregate) return void 0;
|
|
79
|
+
const gating = aggregate.gating_vital_sign;
|
|
80
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
81
|
+
const vital = aggregate.vital_signs.find((v) => v.vital_sign === gating);
|
|
82
|
+
if (vital?.dollar_value != null && vital.dollar_value > 0) {
|
|
83
|
+
const dollars = `${formatCurrency(vital.dollar_value)} ${vital.dollar_label ?? ""}`.trim();
|
|
84
|
+
return `${label} (${dollars})`;
|
|
85
|
+
}
|
|
86
|
+
return label;
|
|
87
|
+
}
|
|
77
88
|
function extractPipelineMetrics(vitals) {
|
|
78
89
|
const flowRate = vitals.find((v) => v.vital_sign === "flow_rate");
|
|
79
90
|
if (!flowRate) return null;
|
|
@@ -122,6 +133,7 @@ __export(store_exports, {
|
|
|
122
133
|
getExportsDir: () => getExportsDir,
|
|
123
134
|
getKnowledgeDir: () => getKnowledgeDir,
|
|
124
135
|
getMemoryDir: () => getMemoryDir,
|
|
136
|
+
getRuminationsDir: () => getRuminationsDir,
|
|
125
137
|
getStrategiesDir: () => getStrategiesDir,
|
|
126
138
|
getWinsDir: () => getWinsDir,
|
|
127
139
|
loadConfig: () => loadConfig,
|
|
@@ -265,6 +277,22 @@ from work done outside this platform.
|
|
|
265
277
|
}
|
|
266
278
|
return dir;
|
|
267
279
|
}
|
|
280
|
+
function getRuminationsDir() {
|
|
281
|
+
const dir = join(NTRP_DIR, "ruminations");
|
|
282
|
+
if (!existsSync(dir)) {
|
|
283
|
+
mkdirSync(dir, { recursive: true });
|
|
284
|
+
writeFileSync(
|
|
285
|
+
join(dir, "README.md"),
|
|
286
|
+
`# Craft logs
|
|
287
|
+
|
|
288
|
+
This directory stores overnight strategy craft jobs. Each job has a machine file (\`<id>.json\`) and a short log (\`<id>.md\`).
|
|
289
|
+
|
|
290
|
+
Resume a job with \`ntrp strategy craft --resume <id>\`.
|
|
291
|
+
`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return dir;
|
|
295
|
+
}
|
|
268
296
|
function getWinsDir() {
|
|
269
297
|
const dir = join(NTRP_DIR, "wins");
|
|
270
298
|
if (!existsSync(dir)) {
|
|
@@ -568,6 +596,14 @@ var init_path_safety = __esm({
|
|
|
568
596
|
});
|
|
569
597
|
|
|
570
598
|
// src/services/export-kinds.ts
|
|
599
|
+
function archiveSubdirForKind(kind) {
|
|
600
|
+
if (kind.startsWith("prompt:")) return "handoffs";
|
|
601
|
+
if (kind === "report") return "reports";
|
|
602
|
+
if (kind === "notes") return "notes";
|
|
603
|
+
if (kind === "csv") return "csv";
|
|
604
|
+
if (kind === "publish") return "publish";
|
|
605
|
+
return "handoffs";
|
|
606
|
+
}
|
|
571
607
|
function latestBasenameForKind(kind) {
|
|
572
608
|
if (kind.startsWith("prompt:")) {
|
|
573
609
|
const target = kind.slice("prompt:".length);
|
|
@@ -967,6 +1003,9 @@ import {
|
|
|
967
1003
|
} from "fs";
|
|
968
1004
|
import { basename as basename2, dirname as dirname2, join as join3, resolve as resolve3, sep as sep2 } from "path";
|
|
969
1005
|
import { randomUUID } from "crypto";
|
|
1006
|
+
function exportStamp(d = /* @__PURE__ */ new Date()) {
|
|
1007
|
+
return d.toISOString().replace(/T/, "-").replace(/:/g, "").slice(0, 15);
|
|
1008
|
+
}
|
|
970
1009
|
function ensureExportsLayout(root = getExportsDir()) {
|
|
971
1010
|
mkdirSync4(root, { recursive: true });
|
|
972
1011
|
mkdirSync4(join3(root, "latest"), { recursive: true });
|
|
@@ -985,6 +1024,15 @@ function ensureExportsLayout(root = getExportsDir()) {
|
|
|
985
1024
|
}
|
|
986
1025
|
return root;
|
|
987
1026
|
}
|
|
1027
|
+
function getArchiveKindDir(kind) {
|
|
1028
|
+
const root = ensureExportsLayout();
|
|
1029
|
+
const dir = join3(root, archiveSubdirForKind(kind));
|
|
1030
|
+
mkdirSync4(dir, { recursive: true });
|
|
1031
|
+
return dir;
|
|
1032
|
+
}
|
|
1033
|
+
function resolveArchivePath(kind, filename) {
|
|
1034
|
+
return join3(getArchiveKindDir(kind), filename);
|
|
1035
|
+
}
|
|
988
1036
|
function getAiInboxDir() {
|
|
989
1037
|
return getConfiguredAiInboxDir();
|
|
990
1038
|
}
|
|
@@ -1043,6 +1091,16 @@ function listExports(opts = {}) {
|
|
|
1043
1091
|
}
|
|
1044
1092
|
return items.slice(0, limit);
|
|
1045
1093
|
}
|
|
1094
|
+
function updateArchiveLatest(kind, sourcePath, root) {
|
|
1095
|
+
const latestDir = join3(root, "latest");
|
|
1096
|
+
mkdirSync4(latestDir, { recursive: true });
|
|
1097
|
+
const name = latestBasenameForKind(kind);
|
|
1098
|
+
const dest = join3(latestDir, name);
|
|
1099
|
+
copyPath(sourcePath, dest);
|
|
1100
|
+
if (kind.startsWith("prompt:")) {
|
|
1101
|
+
copyPath(sourcePath, join3(latestDir, "handoff.md"));
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1046
1104
|
function copyPath(src, dest) {
|
|
1047
1105
|
mkdirSync4(dirname2(dest), { recursive: true });
|
|
1048
1106
|
if (existsSync3(dest)) {
|
|
@@ -1240,6 +1298,29 @@ function syncRecentToInbox(limit = 10) {
|
|
|
1240
1298
|
if (newest) persistHandoffSkillFiles(newest);
|
|
1241
1299
|
return n;
|
|
1242
1300
|
}
|
|
1301
|
+
function recordExportWrite(opts) {
|
|
1302
|
+
const root = ensureExportsLayout();
|
|
1303
|
+
const path = resolve3(opts.path);
|
|
1304
|
+
if (!existsSync3(path)) {
|
|
1305
|
+
throw new Error(`Export path does not exist: ${path}`);
|
|
1306
|
+
}
|
|
1307
|
+
updateArchiveLatest(opts.kind, path, root);
|
|
1308
|
+
const event = {
|
|
1309
|
+
id: randomUUID().slice(0, 8),
|
|
1310
|
+
op: "write",
|
|
1311
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1312
|
+
kind: opts.kind,
|
|
1313
|
+
path,
|
|
1314
|
+
session_id: opts.sessionId,
|
|
1315
|
+
title: opts.title
|
|
1316
|
+
};
|
|
1317
|
+
persistHandoffSkillFiles(event);
|
|
1318
|
+
const inboxPath = syncAiInbox({ kind: opts.kind, path });
|
|
1319
|
+
if (inboxPath) event.inbox_path = inboxPath;
|
|
1320
|
+
appendManifestEvent(event, root);
|
|
1321
|
+
regenerateIndex(root);
|
|
1322
|
+
return event;
|
|
1323
|
+
}
|
|
1243
1324
|
function archiveIndexPath() {
|
|
1244
1325
|
return join3(ensureExportsLayout(), "INDEX.md");
|
|
1245
1326
|
}
|
|
@@ -1380,6 +1461,8 @@ function buildSessionContextDoc(file, opts = {}) {
|
|
|
1380
1461
|
lines.push(`- Constraints: ${file.strategist.constraintsNote}`);
|
|
1381
1462
|
}
|
|
1382
1463
|
if (file.strategist.origin) lines.push(`- Origin: ${file.strategist.origin}`);
|
|
1464
|
+
if (file.strategist.mode) lines.push(`- Mode: ${file.strategist.mode}`);
|
|
1465
|
+
if (file.strategist.ruminationId) lines.push(`- Craft job: ${file.strategist.ruminationId}`);
|
|
1383
1466
|
lines.push("");
|
|
1384
1467
|
}
|
|
1385
1468
|
if (file.think) {
|
|
@@ -5106,6 +5189,7 @@ var init_surfaces = __esm({
|
|
|
5106
5189
|
strategy: { defaultTier: "medium", allowUserTier: false },
|
|
5107
5190
|
strategist: { defaultTier: "high", allowUserTier: true },
|
|
5108
5191
|
strategist_stress: { defaultTier: "high", allowUserTier: false },
|
|
5192
|
+
strategist_critic: { defaultTier: "high", allowUserTier: false },
|
|
5109
5193
|
option_eval: { defaultTier: "low", allowUserTier: false }
|
|
5110
5194
|
};
|
|
5111
5195
|
}
|
|
@@ -7267,12 +7351,51 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
7267
7351
|
function strategyLibraryPath(slug) {
|
|
7268
7352
|
return join16(getStrategiesDir(), `${slug}.md`);
|
|
7269
7353
|
}
|
|
7270
|
-
function writeStrategyMarkdown(strategy) {
|
|
7354
|
+
function writeStrategyMarkdown(strategy, extras) {
|
|
7271
7355
|
const path = strategyLibraryPath(strategy.slug);
|
|
7272
|
-
writeFileSync11(path, renderStrategyMarkdown(strategy), "utf-8");
|
|
7356
|
+
writeFileSync11(path, renderStrategyMarkdown(strategy, extras), "utf-8");
|
|
7273
7357
|
return path;
|
|
7274
7358
|
}
|
|
7275
|
-
function
|
|
7359
|
+
function formatEffortSum(workstreams) {
|
|
7360
|
+
const sum = workstreams.reduce(
|
|
7361
|
+
(acc, ws) => acc + (Number.isFinite(ws.effort_hours) ? ws.effort_hours : 0),
|
|
7362
|
+
0
|
|
7363
|
+
);
|
|
7364
|
+
const count = workstreams.length;
|
|
7365
|
+
return `~${Math.round(sum)} team-hours across ${count} workstream${count === 1 ? "" : "s"}`;
|
|
7366
|
+
}
|
|
7367
|
+
function renderConstraintHeading(constraintLine) {
|
|
7368
|
+
return `## Constraint
|
|
7369
|
+
${constraintLine?.trim() || "none stated"}
|
|
7370
|
+
`;
|
|
7371
|
+
}
|
|
7372
|
+
function renderScopeHeading(constraints, outOfScope) {
|
|
7373
|
+
const inLines = constraints.length > 0 ? formatList(constraints) : "- none stated";
|
|
7374
|
+
const outItems = (outOfScope ?? []).map((s) => s.trim()).filter(Boolean);
|
|
7375
|
+
const outLines = outItems.length > 0 ? formatList(outItems) : "- none stated";
|
|
7376
|
+
return `## In scope / out of scope
|
|
7377
|
+
In scope:
|
|
7378
|
+
${inLines}
|
|
7379
|
+
|
|
7380
|
+
Out of scope:
|
|
7381
|
+
${outLines}
|
|
7382
|
+
`;
|
|
7383
|
+
}
|
|
7384
|
+
function renderKilledAlternativeLine(killedAlternative) {
|
|
7385
|
+
return `Killed alternative: ${killedAlternative?.trim() || "none stated"}`;
|
|
7386
|
+
}
|
|
7387
|
+
function renderEffortHeading(workstreams) {
|
|
7388
|
+
return `## Effort
|
|
7389
|
+
${formatEffortSum(workstreams)}
|
|
7390
|
+
`;
|
|
7391
|
+
}
|
|
7392
|
+
function renderReviewHeading(opts) {
|
|
7393
|
+
const cmd = opts.slug?.trim() ? `/strategy review ${opts.slug.trim()}` : "/strategy review";
|
|
7394
|
+
return `## Review
|
|
7395
|
+
Cadence: ${opts.cadence}. Check progress with ${cmd}.
|
|
7396
|
+
`;
|
|
7397
|
+
}
|
|
7398
|
+
function renderStrategyMarkdown(strategy, extras) {
|
|
7276
7399
|
const frontmatter = stringifyYaml({
|
|
7277
7400
|
id: strategy.id,
|
|
7278
7401
|
slug: strategy.slug,
|
|
@@ -7287,6 +7410,9 @@ function renderStrategyMarkdown(strategy) {
|
|
|
7287
7410
|
...strategy.baseline_batch_id ? { baseline_batch_id: strategy.baseline_batch_id } : {},
|
|
7288
7411
|
updated_at: strategy.updated_at
|
|
7289
7412
|
}).trim();
|
|
7413
|
+
if (strategy.origin === "strategist") {
|
|
7414
|
+
return renderStrategistLibraryMarkdown(strategy, extras, frontmatter);
|
|
7415
|
+
}
|
|
7290
7416
|
const objectiveSection = strategy.objective ? `
|
|
7291
7417
|
## Objective
|
|
7292
7418
|
${strategy.objective}
|
|
@@ -7333,6 +7459,67 @@ ${formatList(strategy.risks)}
|
|
|
7333
7459
|
## Experiment Design
|
|
7334
7460
|
${strategy.experiment_design}
|
|
7335
7461
|
|
|
7462
|
+
## Source Excerpt
|
|
7463
|
+
${strategy.raw_excerpt || "_No excerpt captured._"}
|
|
7464
|
+
`;
|
|
7465
|
+
}
|
|
7466
|
+
function renderStrategistLibraryMarkdown(strategy, extras, frontmatter) {
|
|
7467
|
+
const callSection = strategy.raw_excerpt ? `
|
|
7468
|
+
## The Call
|
|
7469
|
+
${strategy.raw_excerpt}
|
|
7470
|
+
` : "";
|
|
7471
|
+
const objectiveSection = strategy.objective ? `
|
|
7472
|
+
## Locked objective
|
|
7473
|
+
${strategy.objective}
|
|
7474
|
+
` : "";
|
|
7475
|
+
const workstreamSection = strategy.workstreams.length > 0 ? `
|
|
7476
|
+
## Workstreams
|
|
7477
|
+
${strategy.workstreams.map(formatWorkstream).join("\n")}
|
|
7478
|
+
` : "";
|
|
7479
|
+
const assumptionsSection = strategy.assumptions.length > 0 ? `
|
|
7480
|
+
## Assumptions (unverified)
|
|
7481
|
+
${formatList(strategy.assumptions)}
|
|
7482
|
+
` : "";
|
|
7483
|
+
const craftLogSection = extras?.craftLogPath ? `
|
|
7484
|
+
## Craft log
|
|
7485
|
+
See \`${extras.craftLogPath}\`.
|
|
7486
|
+
` : "";
|
|
7487
|
+
return `---
|
|
7488
|
+
${frontmatter}
|
|
7489
|
+
---
|
|
7490
|
+
|
|
7491
|
+
# ${strategy.title}
|
|
7492
|
+
${callSection}${objectiveSection}
|
|
7493
|
+
${renderConstraintHeading(extras?.constraintLine)}
|
|
7494
|
+
## Goal
|
|
7495
|
+
${strategy.goal}
|
|
7496
|
+
|
|
7497
|
+
${renderScopeHeading(strategy.constraints, extras?.outOfScope)}
|
|
7498
|
+
## Hypothesis
|
|
7499
|
+
${strategy.hypothesis}
|
|
7500
|
+
|
|
7501
|
+
${renderKilledAlternativeLine(extras?.killedAlternative)}
|
|
7502
|
+
|
|
7503
|
+
## Target Segment
|
|
7504
|
+
${strategy.target_segment}
|
|
7505
|
+
|
|
7506
|
+
${renderEffortHeading(strategy.workstreams)}${workstreamSection}
|
|
7507
|
+
## Success Metrics
|
|
7508
|
+
${formatMetrics(strategy.success_metrics)}
|
|
7509
|
+
|
|
7510
|
+
## Leading Indicators
|
|
7511
|
+
${formatMetrics(strategy.leading_indicators)}
|
|
7512
|
+
|
|
7513
|
+
## Recommended Actions
|
|
7514
|
+
${formatList(strategy.recommended_actions)}
|
|
7515
|
+
${assumptionsSection}
|
|
7516
|
+
## Risks
|
|
7517
|
+
${formatList(strategy.risks)}
|
|
7518
|
+
|
|
7519
|
+
## Experiment Design
|
|
7520
|
+
${strategy.experiment_design}
|
|
7521
|
+
|
|
7522
|
+
${renderReviewHeading({ cadence: strategy.review_cadence, slug: strategy.slug })}${craftLogSection}
|
|
7336
7523
|
## Source Excerpt
|
|
7337
7524
|
${strategy.raw_excerpt || "_No excerpt captured._"}
|
|
7338
7525
|
`;
|
|
@@ -8203,6 +8390,7 @@ function buildSessionFileSnapshot(ctx) {
|
|
|
8203
8390
|
if (ctx.strategistState) file.strategist = ctx.strategistState;
|
|
8204
8391
|
if (ctx.thinkState) file.think = ctx.thinkState;
|
|
8205
8392
|
if (ctx.pendingAsk) file.pending_ask = ctx.pendingAsk;
|
|
8393
|
+
if (ctx.lastCraftJobId) file.last_craft_job_id = ctx.lastCraftJobId;
|
|
8206
8394
|
return file;
|
|
8207
8395
|
}
|
|
8208
8396
|
function defaultSessionAnalysis(primary = "gtm_health") {
|
|
@@ -8557,6 +8745,9 @@ async function finalizeSession(ctx, stage) {
|
|
|
8557
8745
|
if (ctx.pendingAsk) {
|
|
8558
8746
|
file.pending_ask = ctx.pendingAsk;
|
|
8559
8747
|
}
|
|
8748
|
+
if (ctx.lastCraftJobId) {
|
|
8749
|
+
file.last_craft_job_id = ctx.lastCraftJobId;
|
|
8750
|
+
}
|
|
8560
8751
|
try {
|
|
8561
8752
|
writeFileSync13(ctx.sessionFile, JSON.stringify(file, null, 2) + "\n");
|
|
8562
8753
|
} catch {
|
|
@@ -8628,6 +8819,7 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
8628
8819
|
ctx.strategistState = opts.strategistState;
|
|
8629
8820
|
ctx.thinkState = opts.thinkState;
|
|
8630
8821
|
ctx.pendingAsk = opts.pendingAsk;
|
|
8822
|
+
ctx.lastCraftJobId = opts.lastCraftJobId;
|
|
8631
8823
|
ctx.gapAudit = void 0;
|
|
8632
8824
|
ctx.deliverIntent = false;
|
|
8633
8825
|
ctx.computeInProgress = false;
|
|
@@ -10025,7 +10217,9 @@ section: Settings
|
|
|
10025
10217
|
handler: ../commands/update.ts
|
|
10026
10218
|
---
|
|
10027
10219
|
|
|
10028
|
-
Install the latest global NTRP package via npm
|
|
10220
|
+
Install the latest global NTRP package via npm.
|
|
10221
|
+
Interactive confirms with \u23CE yes, then re-execs onto home.
|
|
10222
|
+
One-shot installs immediately and prints a restart hint.`
|
|
10029
10223
|
},
|
|
10030
10224
|
{
|
|
10031
10225
|
name: "resume",
|
|
@@ -10252,7 +10446,7 @@ This command is hidden. Type \`/ingest --demo\` instead. That command calls this
|
|
|
10252
10446
|
name: strategy
|
|
10253
10447
|
description: Make a measured strategy from your data
|
|
10254
10448
|
section: More
|
|
10255
|
-
args: [objective] | [list|show|review|ingest|add|sync|sources] [args]
|
|
10449
|
+
args: [objective] | [list|show|review|ingest|add|sync|sources|craft] [args]
|
|
10256
10450
|
handler: ../commands/strategy.ts
|
|
10257
10451
|
---
|
|
10258
10452
|
|
|
@@ -10261,6 +10455,8 @@ NTRP uses live data. It works back from the objective.
|
|
|
10261
10455
|
The result is sequenced workstreams with dated milestones, deliverables, outcome ranges, and a contingency per workstream.
|
|
10262
10456
|
Saved plans go to the strategy library. Later answers use them.
|
|
10263
10457
|
Type \`/strategy review [slug]\` to check the plan against live data when new batches arrive.
|
|
10458
|
+
Type \`keep going\` at the confirm card to keep working until the plan is ready.
|
|
10459
|
+
One-shot: \`ntrp strategy craft <objective>\`. Resume with \`ntrp strategy craft --resume <id>\`.
|
|
10264
10460
|
|
|
10265
10461
|
Library commands: \`/strategy list\`, \`/strategy show <slug>\`.
|
|
10266
10462
|
Type \`/strategy ingest <file>\` for markdown, YAML, PDF, text, or \`-\` for stdin.
|
|
@@ -14156,14 +14352,211 @@ var init_activation = __esm({
|
|
|
14156
14352
|
}
|
|
14157
14353
|
});
|
|
14158
14354
|
|
|
14355
|
+
// src/config/update-check.ts
|
|
14356
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync19, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
|
|
14357
|
+
import { join as join22 } from "path";
|
|
14358
|
+
var init_update_check = __esm({
|
|
14359
|
+
"src/config/update-check.ts"() {
|
|
14360
|
+
"use strict";
|
|
14361
|
+
init_store();
|
|
14362
|
+
}
|
|
14363
|
+
});
|
|
14364
|
+
|
|
14365
|
+
// src/version.ts
|
|
14366
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20 } from "fs";
|
|
14367
|
+
import { dirname as dirname4, join as join23 } from "path";
|
|
14368
|
+
import { fileURLToPath } from "url";
|
|
14369
|
+
function readVersionFromPackageJson(packageJsonPath) {
|
|
14370
|
+
if (!existsSync23(packageJsonPath)) return null;
|
|
14371
|
+
try {
|
|
14372
|
+
const pkg = JSON.parse(readFileSync20(packageJsonPath, "utf-8"));
|
|
14373
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
14374
|
+
} catch {
|
|
14375
|
+
}
|
|
14376
|
+
return null;
|
|
14377
|
+
}
|
|
14378
|
+
function readVersionNearEntry(entryPath) {
|
|
14379
|
+
const start = dirname4(entryPath);
|
|
14380
|
+
for (const rel of [join23(start, "..", "package.json"), join23(start, "../..", "package.json")]) {
|
|
14381
|
+
const version = readVersionFromPackageJson(rel);
|
|
14382
|
+
if (version) return version;
|
|
14383
|
+
}
|
|
14384
|
+
return null;
|
|
14385
|
+
}
|
|
14386
|
+
function readInstalledVersionFromDisk() {
|
|
14387
|
+
return readVersionNearEntry(fileURLToPath(import.meta.url));
|
|
14388
|
+
}
|
|
14389
|
+
function getInstalledVersion() {
|
|
14390
|
+
if (cachedVersion) return cachedVersion;
|
|
14391
|
+
cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
|
|
14392
|
+
return cachedVersion;
|
|
14393
|
+
}
|
|
14394
|
+
var cachedVersion;
|
|
14395
|
+
var init_version = __esm({
|
|
14396
|
+
"src/version.ts"() {
|
|
14397
|
+
"use strict";
|
|
14398
|
+
}
|
|
14399
|
+
});
|
|
14400
|
+
|
|
14401
|
+
// src/update/registry.ts
|
|
14402
|
+
function parseVersionParts(version) {
|
|
14403
|
+
const cleaned = version.trim().replace(/^v/i, "");
|
|
14404
|
+
const core = cleaned.split("-")[0] ?? cleaned;
|
|
14405
|
+
const parts = core.split(".").map((p) => parseInt(p, 10));
|
|
14406
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
14407
|
+
}
|
|
14408
|
+
function isNewerVersion(latest, current) {
|
|
14409
|
+
const [lMaj, lMin, lPatch] = parseVersionParts(latest);
|
|
14410
|
+
const [cMaj, cMin, cPatch] = parseVersionParts(current);
|
|
14411
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
14412
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
14413
|
+
return lPatch > cPatch;
|
|
14414
|
+
}
|
|
14415
|
+
function hasAvailableUpdate(update) {
|
|
14416
|
+
return Boolean(update && isNewerVersion(update.latest, update.current));
|
|
14417
|
+
}
|
|
14418
|
+
var init_registry2 = __esm({
|
|
14419
|
+
"src/update/registry.ts"() {
|
|
14420
|
+
"use strict";
|
|
14421
|
+
init_update_check();
|
|
14422
|
+
init_version();
|
|
14423
|
+
}
|
|
14424
|
+
});
|
|
14425
|
+
|
|
14426
|
+
// src/ruminations/store.ts
|
|
14427
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "fs";
|
|
14428
|
+
import { join as join24 } from "path";
|
|
14429
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
14430
|
+
function makeRuminationId(now2 = /* @__PURE__ */ new Date()) {
|
|
14431
|
+
const day = now2.toISOString().slice(0, 10).replace(/-/g, "");
|
|
14432
|
+
return `${day}-${randomUUID8().slice(0, 4)}`;
|
|
14433
|
+
}
|
|
14434
|
+
function ruminationJsonPath(id) {
|
|
14435
|
+
return join24(getRuminationsDir(), `${id}.json`);
|
|
14436
|
+
}
|
|
14437
|
+
function ruminationLogPath(id) {
|
|
14438
|
+
return join24(getRuminationsDir(), `${id}.md`);
|
|
14439
|
+
}
|
|
14440
|
+
function unfinishedCraftJob(id) {
|
|
14441
|
+
if (!id?.trim()) return null;
|
|
14442
|
+
const job = loadRuminationJob(id);
|
|
14443
|
+
if (!job || job.status === "ready") return null;
|
|
14444
|
+
return job;
|
|
14445
|
+
}
|
|
14446
|
+
function loadRuminationJob(id) {
|
|
14447
|
+
const path = ruminationJsonPath(id);
|
|
14448
|
+
if (!existsSync24(path)) return null;
|
|
14449
|
+
try {
|
|
14450
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
14451
|
+
if (!parsed || typeof parsed !== "object" || parsed.id !== id) return null;
|
|
14452
|
+
if (parsed.best_plan === void 0) parsed.best_plan = parsed.plan ?? null;
|
|
14453
|
+
if (parsed.best_score === void 0) parsed.best_score = parsed.last_critic?.score ?? null;
|
|
14454
|
+
return parsed;
|
|
14455
|
+
} catch {
|
|
14456
|
+
return null;
|
|
14457
|
+
}
|
|
14458
|
+
}
|
|
14459
|
+
function createRuminationJob(opts) {
|
|
14460
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
14461
|
+
return {
|
|
14462
|
+
id: opts.id ?? makeRuminationId(),
|
|
14463
|
+
objective: opts.objective,
|
|
14464
|
+
constraints_note: opts.constraintsNote,
|
|
14465
|
+
baseline_batch_id: opts.baselineBatchId ?? null,
|
|
14466
|
+
snapshot_hash: opts.snapshotHash,
|
|
14467
|
+
plan: null,
|
|
14468
|
+
best_plan: null,
|
|
14469
|
+
best_score: null,
|
|
14470
|
+
last_critic: null,
|
|
14471
|
+
rubric_gaps: [],
|
|
14472
|
+
iterations: [],
|
|
14473
|
+
cumulative_input_tokens: 0,
|
|
14474
|
+
cumulative_output_tokens: 0,
|
|
14475
|
+
status: "running",
|
|
14476
|
+
from_fallback: false,
|
|
14477
|
+
revised_once: false,
|
|
14478
|
+
extra_ground_used: false,
|
|
14479
|
+
min_score: opts.minScore,
|
|
14480
|
+
max_rounds: opts.maxRounds,
|
|
14481
|
+
max_tokens: opts.maxTokens,
|
|
14482
|
+
created_at: now2,
|
|
14483
|
+
updated_at: now2,
|
|
14484
|
+
no_improve_streak: 0,
|
|
14485
|
+
last_critic_score: null
|
|
14486
|
+
};
|
|
14487
|
+
}
|
|
14488
|
+
function renderRuminationLog(job) {
|
|
14489
|
+
const lines = [];
|
|
14490
|
+
lines.push(`# Craft log \u2014 ${job.id}`);
|
|
14491
|
+
lines.push("");
|
|
14492
|
+
lines.push(`Objective: ${job.objective}`);
|
|
14493
|
+
lines.push(`Status: ${job.status}${job.stop_reason ? ` (${job.stop_reason})` : ""}`);
|
|
14494
|
+
lines.push(
|
|
14495
|
+
`Tokens: ${job.cumulative_input_tokens + job.cumulative_output_tokens} total (${job.cumulative_input_tokens} in / ${job.cumulative_output_tokens} out)`
|
|
14496
|
+
);
|
|
14497
|
+
if (job.library_path) lines.push(`Strategy: ${job.library_path}`);
|
|
14498
|
+
if (job.handoff_path) lines.push(`Handoff: ${job.handoff_path}`);
|
|
14499
|
+
lines.push("");
|
|
14500
|
+
lines.push("## Rounds");
|
|
14501
|
+
lines.push("");
|
|
14502
|
+
if (job.iterations.length === 0) {
|
|
14503
|
+
lines.push("_No rounds yet._");
|
|
14504
|
+
} else {
|
|
14505
|
+
for (const it of job.iterations) {
|
|
14506
|
+
const codes = it.blocking_codes.length > 0 ? it.blocking_codes.join(", ") : "none";
|
|
14507
|
+
lines.push(
|
|
14508
|
+
`- Round ${it.round}: score ${it.critic_score ?? "\u2014"} \xB7 ${it.decision} \xB7 blocking ${codes} \xB7 ${it.tokens_in + it.tokens_out} tok`
|
|
14509
|
+
);
|
|
14510
|
+
}
|
|
14511
|
+
}
|
|
14512
|
+
if (job.last_critic?.partner_pushback) {
|
|
14513
|
+
lines.push("");
|
|
14514
|
+
lines.push("## Partner pushback");
|
|
14515
|
+
lines.push("");
|
|
14516
|
+
lines.push(job.last_critic.partner_pushback);
|
|
14517
|
+
}
|
|
14518
|
+
if (job.last_critic?.killed_alternative) {
|
|
14519
|
+
lines.push("");
|
|
14520
|
+
lines.push("## Killed alternative");
|
|
14521
|
+
lines.push("");
|
|
14522
|
+
lines.push(job.last_critic.killed_alternative);
|
|
14523
|
+
}
|
|
14524
|
+
lines.push("");
|
|
14525
|
+
return lines.join("\n");
|
|
14526
|
+
}
|
|
14527
|
+
function saveRuminationJob(job) {
|
|
14528
|
+
job.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
14529
|
+
getRuminationsDir();
|
|
14530
|
+
writeFileSync15(ruminationJsonPath(job.id), JSON.stringify(job, null, 2) + "\n", "utf-8");
|
|
14531
|
+
writeFileSync15(ruminationLogPath(job.id), renderRuminationLog(job), "utf-8");
|
|
14532
|
+
}
|
|
14533
|
+
function addRuminationIteration(job, iteration) {
|
|
14534
|
+
job.iterations.push({ ...iteration, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14535
|
+
job.cumulative_input_tokens += iteration.tokens_in;
|
|
14536
|
+
job.cumulative_output_tokens += iteration.tokens_out;
|
|
14537
|
+
}
|
|
14538
|
+
var init_store3 = __esm({
|
|
14539
|
+
"src/ruminations/store.ts"() {
|
|
14540
|
+
"use strict";
|
|
14541
|
+
init_store();
|
|
14542
|
+
}
|
|
14543
|
+
});
|
|
14544
|
+
|
|
14159
14545
|
// src/conversation/recommended-action.ts
|
|
14160
14546
|
function resolveRecommendedAction(ctx) {
|
|
14161
14547
|
if (!hasValidLicense()) return { submit: "/activate", hint: "/activate" };
|
|
14162
14548
|
const phase = resolveConversationPhase(ctx);
|
|
14163
14549
|
switch (phase) {
|
|
14164
14550
|
case "explore":
|
|
14551
|
+
if (!canUseReplAi(ctx)) {
|
|
14552
|
+
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
14553
|
+
return { submit: "/connect", hint: "/connect" };
|
|
14554
|
+
}
|
|
14555
|
+
if (unfinishedCraftJob(ctx.lastCraftJobId)) {
|
|
14556
|
+
return { submit: "keep going", hint: "keep going" };
|
|
14557
|
+
}
|
|
14165
14558
|
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
14166
|
-
return
|
|
14559
|
+
return null;
|
|
14167
14560
|
case "awaiting_data":
|
|
14168
14561
|
if (ctx.gapAudit?.can_compute) return { submit: "go ahead", hint: "go ahead" };
|
|
14169
14562
|
if (!sessionHasData(ctx)) return { submit: "use demo data", hint: "use demo data" };
|
|
@@ -14174,6 +14567,8 @@ function resolveRecommendedAction(ctx) {
|
|
|
14174
14567
|
return ctx.strategistState?.step === "objective_confirm" ? { submit: "yes", hint: "yes" } : null;
|
|
14175
14568
|
case "think":
|
|
14176
14569
|
return null;
|
|
14570
|
+
case "orient":
|
|
14571
|
+
return hasAvailableUpdate(ctx.updateAvailable) ? { submit: "/update", hint: "/update" } : null;
|
|
14177
14572
|
default:
|
|
14178
14573
|
return null;
|
|
14179
14574
|
}
|
|
@@ -14183,7 +14578,9 @@ var init_recommended_action = __esm({
|
|
|
14183
14578
|
"use strict";
|
|
14184
14579
|
init_repl_api();
|
|
14185
14580
|
init_activation();
|
|
14581
|
+
init_registry2();
|
|
14186
14582
|
init_phase();
|
|
14583
|
+
init_store3();
|
|
14187
14584
|
}
|
|
14188
14585
|
});
|
|
14189
14586
|
|
|
@@ -15890,11 +16287,11 @@ function jsonSafe(value) {
|
|
|
15890
16287
|
}
|
|
15891
16288
|
return value;
|
|
15892
16289
|
}
|
|
15893
|
-
function envelope(command, data, warnings) {
|
|
16290
|
+
function envelope(command, data, warnings, status = "ok") {
|
|
15894
16291
|
return {
|
|
15895
16292
|
schema_version: HEADLESS_SCHEMA_VERSION,
|
|
15896
16293
|
command,
|
|
15897
|
-
status
|
|
16294
|
+
status,
|
|
15898
16295
|
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15899
16296
|
data,
|
|
15900
16297
|
...warnings && warnings.length > 0 ? { warnings } : {}
|
|
@@ -15910,8 +16307,8 @@ function errorEnvelope(command, err) {
|
|
|
15910
16307
|
error: ntrpError.toHeadlessError()
|
|
15911
16308
|
};
|
|
15912
16309
|
}
|
|
15913
|
-
function emitResult(command, data, warnings) {
|
|
15914
|
-
console.log(JSON.stringify(jsonSafe(envelope(command, data, warnings)), null, 2));
|
|
16310
|
+
function emitResult(command, data, warnings, status = "ok") {
|
|
16311
|
+
console.log(JSON.stringify(jsonSafe(envelope(command, data, warnings, status)), null, 2));
|
|
15915
16312
|
}
|
|
15916
16313
|
function emitError(command, err) {
|
|
15917
16314
|
const ntrpError = err instanceof NtrpError ? err : toNtrpError(err);
|
|
@@ -17511,7 +17908,13 @@ async function persistStrategistPlan(plan, opts = {}) {
|
|
|
17511
17908
|
if (!strategy) {
|
|
17512
17909
|
throw new NtrpError("strategy_persist_failed", "Strategy was not found after saving.", 1 /* RuntimeError */);
|
|
17513
17910
|
}
|
|
17514
|
-
const
|
|
17911
|
+
const extras = {
|
|
17912
|
+
craftLogPath: opts.craftLogPath,
|
|
17913
|
+
constraintLine: opts.constraintLine,
|
|
17914
|
+
killedAlternative: opts.killedAlternative,
|
|
17915
|
+
outOfScope: opts.outOfScope
|
|
17916
|
+
};
|
|
17917
|
+
const writtenPath = writeStrategyMarkdown(strategy, extras);
|
|
17515
17918
|
await insertStrategySource({
|
|
17516
17919
|
strategy_id: strategy.id,
|
|
17517
17920
|
source_type: "agent",
|
|
@@ -17539,7 +17942,6 @@ var init_strategist = __esm({
|
|
|
17539
17942
|
init_errors2();
|
|
17540
17943
|
init_types2();
|
|
17541
17944
|
init_formatters();
|
|
17542
|
-
init_formatters();
|
|
17543
17945
|
}
|
|
17544
17946
|
});
|
|
17545
17947
|
|
|
@@ -17825,6 +18227,52 @@ ${AAR_BLOCK}
|
|
|
17825
18227
|
|
|
17826
18228
|
Then respond with the FINAL revised plan as strict JSON in the same schema \u2014 no markdown fences, no prose. Fold what you learned into constraints, assumptions, risks, and confidence. This version is the one that gets saved and reviewed against, so make every number one you are willing to be checked on.`;
|
|
17827
18229
|
}
|
|
18230
|
+
function buildCriticMessage(input) {
|
|
18231
|
+
return `CRAFT CRITIC. You are a skeptical partner reviewing a GTM plan before it ships to a client. The objective is locked and must be echoed verbatim.
|
|
18232
|
+
|
|
18233
|
+
ARMED OBJECTIVE (byte-identical; do not rewrite):
|
|
18234
|
+
"${input.objective}"
|
|
18235
|
+
|
|
18236
|
+
CODE RUBRIC GAPS (already blocking \u2014 you cannot ship until these are gone):
|
|
18237
|
+
${input.rubricGaps || "(none)"}
|
|
18238
|
+
|
|
18239
|
+
LIVE SNAPSHOT (cite these numbers only):
|
|
18240
|
+
${input.healthSnapshot}
|
|
18241
|
+
|
|
18242
|
+
CURRENT PLAN JSON:
|
|
18243
|
+
${input.planJson}
|
|
18244
|
+
|
|
18245
|
+
${HEILMEIER_GATE}
|
|
18246
|
+
|
|
18247
|
+
${AAR_BLOCK}
|
|
18248
|
+
|
|
18249
|
+
Decide whether a partner would ship this. Respond with STRICT JSON only:
|
|
18250
|
+
{
|
|
18251
|
+
"ship": false,
|
|
18252
|
+
"score": 0,
|
|
18253
|
+
"objective_echo": ${JSON.stringify(input.objective)},
|
|
18254
|
+
"gaps": [{ "code": "bottleneck", "severity": "blocking", "note": "...", "fix": "..." }],
|
|
18255
|
+
"killed_alternative": "one approach that failed newness/stake/exams",
|
|
18256
|
+
"partner_pushback": "one paragraph a partner would actually say"
|
|
18257
|
+
}
|
|
18258
|
+
|
|
18259
|
+
ship may be true only when objective_echo matches, blocking gaps are empty, and you would defend this plan in the room. Advisory gaps do not block. Score 0-100.`;
|
|
18260
|
+
}
|
|
18261
|
+
function buildReviseMessage(input) {
|
|
18262
|
+
return `CRAFT REVISE. Keep the objective verbatim. Patch only the listed gap codes. Copy baselines from the current plan / snapshot exactly \u2014 do not invent numbers.
|
|
18263
|
+
|
|
18264
|
+
ARMED OBJECTIVE (must appear unchanged as "objective"):
|
|
18265
|
+
"${input.objective}"
|
|
18266
|
+
|
|
18267
|
+
GAPS TO CLOSE:
|
|
18268
|
+
${input.gapsJson}
|
|
18269
|
+
|
|
18270
|
+
CURRENT PLAN:
|
|
18271
|
+
${input.planJson}
|
|
18272
|
+
|
|
18273
|
+
Respond with ONLY the full plan JSON object in this schema \u2014 no prose, no fences:
|
|
18274
|
+
${STRATEGIST_PLAN_SCHEMA_BLOCK}`;
|
|
18275
|
+
}
|
|
17828
18276
|
var STRATEGIST_PLAN_SCHEMA_BLOCK;
|
|
17829
18277
|
var init_strategist_prompt = __esm({
|
|
17830
18278
|
"src/ai/strategist-prompt.ts"() {
|
|
@@ -18352,53 +18800,344 @@ var init_strategist_validate = __esm({
|
|
|
18352
18800
|
}
|
|
18353
18801
|
});
|
|
18354
18802
|
|
|
18355
|
-
// src/ai/strategist.ts
|
|
18356
|
-
function
|
|
18357
|
-
|
|
18358
|
-
return
|
|
18359
|
-
|
|
18360
|
-
aggregate: {
|
|
18361
|
-
overall_score: aggregate.overall_score,
|
|
18362
|
-
overall_status: aggregate.overall_status,
|
|
18363
|
-
gating_vital_sign: aggregate.gating_vital_sign,
|
|
18364
|
-
total_value_at_risk: aggregate.total_value_at_risk,
|
|
18365
|
-
vital_signs: Object.fromEntries(
|
|
18366
|
-
aggregate.vital_signs.map((v) => [
|
|
18367
|
-
v.vital_sign,
|
|
18368
|
-
{ score: v.score, status: v.status, dollar_value: v.dollar_value, dollar_label: v.dollar_label }
|
|
18369
|
-
])
|
|
18370
|
-
)
|
|
18371
|
-
},
|
|
18372
|
-
segment_names: segments.map((s) => s.segment.name),
|
|
18373
|
-
top_divergences: divergences.slice(0, 5).map((d) => ({
|
|
18374
|
-
segment: d.segmentName,
|
|
18375
|
-
vital_sign: d.vitalSign,
|
|
18376
|
-
segment_score: d.segmentScore,
|
|
18377
|
-
aggregate_score: d.aggregateScore,
|
|
18378
|
-
delta: d.delta
|
|
18379
|
-
}))
|
|
18380
|
-
},
|
|
18381
|
-
null,
|
|
18382
|
-
2
|
|
18383
|
-
);
|
|
18803
|
+
// src/ai/strategist-rubric.ts
|
|
18804
|
+
function numbersMatch2(a, b) {
|
|
18805
|
+
if (a === b) return true;
|
|
18806
|
+
if (a === 0 || b === 0) return Math.abs(a - b) < 0.5;
|
|
18807
|
+
return Math.abs(a - b) / Math.max(Math.abs(a), Math.abs(b)) <= 0.02;
|
|
18384
18808
|
}
|
|
18385
|
-
|
|
18386
|
-
|
|
18387
|
-
|
|
18388
|
-
const
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
const
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18401
|
-
|
|
18809
|
+
function parseIsoDate2(value) {
|
|
18810
|
+
const match = value.trim().match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
18811
|
+
if (!match) return null;
|
|
18812
|
+
const date = /* @__PURE__ */ new Date(`${match[1]}-${match[2]}-${match[3]}T00:00:00Z`);
|
|
18813
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
18814
|
+
}
|
|
18815
|
+
function addDays2(date, days) {
|
|
18816
|
+
const out = new Date(date);
|
|
18817
|
+
out.setUTCDate(out.getUTCDate() + days);
|
|
18818
|
+
return out;
|
|
18819
|
+
}
|
|
18820
|
+
function workstreamText(ws) {
|
|
18821
|
+
return [ws.title, ws.problem, ws.rationale, ws.actions.join(" "), ws.play_ids.join(" ")].join(" ");
|
|
18822
|
+
}
|
|
18823
|
+
function hasOwnerShape(ws) {
|
|
18824
|
+
const blob = workstreamText(ws).toLowerCase();
|
|
18825
|
+
return OWNER_TOKENS.some((token) => blob.includes(token));
|
|
18826
|
+
}
|
|
18827
|
+
function firstWorkstreamMovesConstraint(ws, gating) {
|
|
18828
|
+
const plays = getPlaybook();
|
|
18829
|
+
if (ws.play_ids.some((id) => plays.find((p) => p.id === id)?.trigger_vital_sign === gating)) {
|
|
18830
|
+
return true;
|
|
18831
|
+
}
|
|
18832
|
+
const blob = workstreamText(ws).toLowerCase();
|
|
18833
|
+
return VITAL_ALIASES[gating].some((alias) => blob.includes(alias));
|
|
18834
|
+
}
|
|
18835
|
+
function citesStake(plan, snapshot) {
|
|
18836
|
+
const dollars = [];
|
|
18837
|
+
if (snapshot.aggregate.total_value_at_risk != null && snapshot.aggregate.total_value_at_risk > 0) {
|
|
18838
|
+
dollars.push(snapshot.aggregate.total_value_at_risk);
|
|
18839
|
+
}
|
|
18840
|
+
for (const vs of snapshot.aggregate.vital_signs) {
|
|
18841
|
+
if (vs.dollar_value != null && vs.dollar_value > 0) dollars.push(vs.dollar_value);
|
|
18842
|
+
}
|
|
18843
|
+
if (dollars.length === 0) return true;
|
|
18844
|
+
const blob = `${plan.summary_30k} ${plan.hypothesis} ${plan.workstreams[0]?.problem ?? ""}`;
|
|
18845
|
+
const found = extractNumbers(blob);
|
|
18846
|
+
if (found.some((n) => dollars.some((d) => numbersMatch2(n, d)))) return true;
|
|
18847
|
+
const formatted = dollars.map((d) => formatCurrency(d).toLowerCase());
|
|
18848
|
+
const lower = blob.toLowerCase();
|
|
18849
|
+
return formatted.some((f) => lower.includes(f.toLowerCase()));
|
|
18850
|
+
}
|
|
18851
|
+
function hasStartWithin48h(plan, todayIso) {
|
|
18852
|
+
const today = parseIsoDate2(todayIso) ?? /* @__PURE__ */ new Date(`${todayIso}T00:00:00Z`);
|
|
18853
|
+
const limit = addDays2(today, 2);
|
|
18854
|
+
const first = plan.workstreams[0];
|
|
18855
|
+
if (!first) return false;
|
|
18856
|
+
if (first.actions.some((action) => START_SOON_RE.test(action))) return true;
|
|
18857
|
+
const dated = [
|
|
18858
|
+
...first.milestones.map((m) => m.due),
|
|
18859
|
+
...first.deliverables.map((d) => d.due)
|
|
18860
|
+
];
|
|
18861
|
+
for (const iso of dated) {
|
|
18862
|
+
const parsed = parseIsoDate2(iso);
|
|
18863
|
+
if (parsed && parsed.getTime() <= limit.getTime()) return true;
|
|
18864
|
+
}
|
|
18865
|
+
return plan.workstreams.some(
|
|
18866
|
+
(ws) => ws.actions.some((action) => START_SOON_RE.test(action))
|
|
18867
|
+
);
|
|
18868
|
+
}
|
|
18869
|
+
function examsHold(ws) {
|
|
18870
|
+
const o = ws.expected_outcome;
|
|
18871
|
+
return extractNumbers(o.baseline).length > 0 && extractNumbers(o.target_range).length > 0 && isKnownInstrument(o.measured_by);
|
|
18872
|
+
}
|
|
18873
|
+
function inventedNumbers(plan, evidenceText) {
|
|
18874
|
+
const evidenceNums = extractNumbers(evidenceText);
|
|
18875
|
+
if (evidenceNums.length === 0) return false;
|
|
18876
|
+
for (const ws of plan.workstreams) {
|
|
18877
|
+
const baselines = extractNumbers(ws.expected_outcome.baseline);
|
|
18878
|
+
if (baselines.length === 0) continue;
|
|
18879
|
+
if (!baselines.some((b) => evidenceNums.some((e) => numbersMatch2(b, e)))) return true;
|
|
18880
|
+
}
|
|
18881
|
+
return false;
|
|
18882
|
+
}
|
|
18883
|
+
function scoreConsultantPlan(plan, opts) {
|
|
18884
|
+
const gaps = [];
|
|
18885
|
+
const gating = opts.snapshot.aggregate.gating_vital_sign;
|
|
18886
|
+
const first = plan.workstreams[0];
|
|
18887
|
+
const evidence = opts.evidenceText ?? JSON.stringify({
|
|
18888
|
+
aggregate: opts.snapshot.aggregate
|
|
18889
|
+
});
|
|
18890
|
+
if (opts.armedObjective && plan.objective !== opts.armedObjective) {
|
|
18891
|
+
gaps.push({
|
|
18892
|
+
code: "objective_drift",
|
|
18893
|
+
severity: "blocking",
|
|
18894
|
+
note: "Plan objective does not match the armed objective.",
|
|
18895
|
+
fix: "Copy the armed objective into plan.objective with no edits."
|
|
18896
|
+
});
|
|
18897
|
+
}
|
|
18898
|
+
if (!first || !firstWorkstreamMovesConstraint(first, gating)) {
|
|
18899
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
18900
|
+
gaps.push({
|
|
18901
|
+
code: "bottleneck",
|
|
18902
|
+
severity: "blocking",
|
|
18903
|
+
note: `First workstream does not move the gating vital (${label}).`,
|
|
18904
|
+
fix: `Lead with a play or problem that moves ${gating}. Non-constraint work waits.`
|
|
18905
|
+
});
|
|
18906
|
+
}
|
|
18907
|
+
if (!citesStake(plan, opts.snapshot)) {
|
|
18908
|
+
gaps.push({
|
|
18909
|
+
code: "stake",
|
|
18910
|
+
severity: "blocking",
|
|
18911
|
+
note: "30k or hypothesis does not cite snapshot dollars / cost of inaction.",
|
|
18912
|
+
fix: "Open with the gating dollar figure from the live snapshot."
|
|
18913
|
+
});
|
|
18914
|
+
}
|
|
18915
|
+
for (const ws of plan.workstreams) {
|
|
18916
|
+
if (!hasOwnerShape(ws)) {
|
|
18917
|
+
gaps.push({
|
|
18918
|
+
code: "owner",
|
|
18919
|
+
severity: "blocking",
|
|
18920
|
+
note: `Workstream "${ws.title}" has no function-shaped owner.`,
|
|
18921
|
+
fix: "Name RevOps, sales manager, CS lead, marketing ops, or AE lead in the actions."
|
|
18922
|
+
});
|
|
18923
|
+
break;
|
|
18924
|
+
}
|
|
18925
|
+
}
|
|
18926
|
+
if (!hasStartWithin48h(plan, opts.todayIso)) {
|
|
18927
|
+
gaps.push({
|
|
18928
|
+
code: "start_48h",
|
|
18929
|
+
severity: "blocking",
|
|
18930
|
+
note: "No action or milestone is startable within 48 hours.",
|
|
18931
|
+
fix: "Put a dated first action or milestone within two days of today."
|
|
18932
|
+
});
|
|
18933
|
+
}
|
|
18934
|
+
const missingEffort = plan.workstreams.filter(
|
|
18935
|
+
(ws) => !Number.isFinite(ws.effort_hours) || ws.effort_hours <= 0
|
|
18936
|
+
);
|
|
18937
|
+
if (missingEffort.length > 0 || plan.workstreams.length === 0) {
|
|
18938
|
+
gaps.push({
|
|
18939
|
+
code: "effort",
|
|
18940
|
+
severity: "blocking",
|
|
18941
|
+
note: "Each workstream needs a finite effort_hours greater than zero.",
|
|
18942
|
+
fix: "Set effort_hours to a real team-hour estimate per workstream."
|
|
18943
|
+
});
|
|
18944
|
+
}
|
|
18945
|
+
if (opts.constraintsNote?.trim() && plan.constraints.length === 0) {
|
|
18946
|
+
gaps.push({
|
|
18947
|
+
code: "scope",
|
|
18948
|
+
severity: "blocking",
|
|
18949
|
+
note: "Operator stated constraints, but the plan constraints list is empty.",
|
|
18950
|
+
fix: "Copy operator constraints into plan.constraints. Name what is out of scope."
|
|
18951
|
+
});
|
|
18952
|
+
}
|
|
18953
|
+
if (plan.workstreams.some((ws) => !examsHold(ws))) {
|
|
18954
|
+
gaps.push({
|
|
18955
|
+
code: "exams",
|
|
18956
|
+
severity: "blocking",
|
|
18957
|
+
note: "A workstream expected outcome is missing a numeric baseline, range, or known instrument.",
|
|
18958
|
+
fix: "Give every expected_outcome a baseline, target range, and measured_by instrument from live data."
|
|
18959
|
+
});
|
|
18960
|
+
}
|
|
18961
|
+
if (inventedNumbers(plan, evidence)) {
|
|
18962
|
+
gaps.push({
|
|
18963
|
+
code: "invented_numbers",
|
|
18964
|
+
severity: "blocking",
|
|
18965
|
+
note: "An outcome baseline is not in the grounded snapshot.",
|
|
18966
|
+
fix: "Copy baselines from the health snapshot exactly."
|
|
18967
|
+
});
|
|
18968
|
+
}
|
|
18969
|
+
const blocking = gaps.filter((g) => g.severity === "blocking");
|
|
18970
|
+
return { pass: blocking.length === 0, gaps, blocking };
|
|
18971
|
+
}
|
|
18972
|
+
function asGapCode(value) {
|
|
18973
|
+
return typeof value === "string" && GAP_CODES.includes(value) ? value : null;
|
|
18974
|
+
}
|
|
18975
|
+
function parseCriticResponse(raw, opts) {
|
|
18976
|
+
if (!raw) {
|
|
18977
|
+
return {
|
|
18978
|
+
ship: false,
|
|
18979
|
+
score: 0,
|
|
18980
|
+
objective_echo: "",
|
|
18981
|
+
gaps: [
|
|
18982
|
+
{
|
|
18983
|
+
code: "thin_evidence",
|
|
18984
|
+
severity: "blocking",
|
|
18985
|
+
note: "Critic reply was not valid JSON.",
|
|
18986
|
+
fix: "Respond with the critic JSON object only."
|
|
18987
|
+
}
|
|
18988
|
+
],
|
|
18989
|
+
killed_alternative: "",
|
|
18990
|
+
partner_pushback: ""
|
|
18991
|
+
};
|
|
18992
|
+
}
|
|
18993
|
+
const objectiveEcho = typeof raw.objective_echo === "string" ? raw.objective_echo : "";
|
|
18994
|
+
const killed = typeof raw.killed_alternative === "string" ? raw.killed_alternative.trim() : "";
|
|
18995
|
+
const pushback = typeof raw.partner_pushback === "string" ? raw.partner_pushback.trim() : "";
|
|
18996
|
+
const scoreRaw = typeof raw.score === "number" ? raw.score : Number(raw.score);
|
|
18997
|
+
const score = Number.isFinite(scoreRaw) ? Math.max(0, Math.min(100, scoreRaw)) : 0;
|
|
18998
|
+
const gaps = [];
|
|
18999
|
+
if (Array.isArray(raw.gaps)) {
|
|
19000
|
+
for (const item of raw.gaps) {
|
|
19001
|
+
if (!item || typeof item !== "object") continue;
|
|
19002
|
+
const rec = item;
|
|
19003
|
+
const code = asGapCode(rec.code);
|
|
19004
|
+
if (!code) continue;
|
|
19005
|
+
gaps.push({
|
|
19006
|
+
code,
|
|
19007
|
+
severity: rec.severity === "advisory" ? "advisory" : "blocking",
|
|
19008
|
+
note: typeof rec.note === "string" ? rec.note : "",
|
|
19009
|
+
fix: typeof rec.fix === "string" ? rec.fix : ""
|
|
19010
|
+
});
|
|
19011
|
+
}
|
|
19012
|
+
}
|
|
19013
|
+
if (objectiveEcho !== opts.armedObjective) {
|
|
19014
|
+
gaps.push({
|
|
19015
|
+
code: "objective_drift",
|
|
19016
|
+
severity: "blocking",
|
|
19017
|
+
note: "Critic objective_echo does not match the armed objective.",
|
|
19018
|
+
fix: "Echo the armed objective exactly."
|
|
19019
|
+
});
|
|
19020
|
+
}
|
|
19021
|
+
if (!killed) {
|
|
19022
|
+
gaps.push({
|
|
19023
|
+
code: "alternatives_killed",
|
|
19024
|
+
severity: "blocking",
|
|
19025
|
+
note: "Critic did not name an approach that failed newness, stake, or exams.",
|
|
19026
|
+
fix: "Name one killed alternative so the plan is not a restatement of the status quo."
|
|
19027
|
+
});
|
|
19028
|
+
}
|
|
19029
|
+
for (const gap of opts.rubricBlocking) {
|
|
19030
|
+
if (!gaps.some((g) => g.code === gap.code)) gaps.push(gap);
|
|
19031
|
+
}
|
|
19032
|
+
const blocking = gaps.filter((g) => g.severity === "blocking");
|
|
19033
|
+
const modelShip = raw.ship === true;
|
|
19034
|
+
const ship = modelShip && blocking.length === 0 && score >= opts.minScore && objectiveEcho === opts.armedObjective && killed.length > 0;
|
|
19035
|
+
return {
|
|
19036
|
+
ship,
|
|
19037
|
+
score,
|
|
19038
|
+
objective_echo: objectiveEcho,
|
|
19039
|
+
gaps,
|
|
19040
|
+
killed_alternative: killed,
|
|
19041
|
+
partner_pushback: pushback
|
|
19042
|
+
};
|
|
19043
|
+
}
|
|
19044
|
+
function lockPlanObjective(plan, armedObjective) {
|
|
19045
|
+
return { ...plan, objective: armedObjective };
|
|
19046
|
+
}
|
|
19047
|
+
var OWNER_TOKENS, VITAL_ALIASES, START_SOON_RE, GAP_CODES;
|
|
19048
|
+
var init_strategist_rubric = __esm({
|
|
19049
|
+
"src/ai/strategist-rubric.ts"() {
|
|
19050
|
+
"use strict";
|
|
19051
|
+
init_playbook();
|
|
19052
|
+
init_strategist_validate();
|
|
19053
|
+
init_formatters();
|
|
19054
|
+
OWNER_TOKENS = [
|
|
19055
|
+
"revops",
|
|
19056
|
+
"rev ops",
|
|
19057
|
+
"revenue ops",
|
|
19058
|
+
"sales manager",
|
|
19059
|
+
"sales ops",
|
|
19060
|
+
"cs lead",
|
|
19061
|
+
"customer success",
|
|
19062
|
+
"marketing ops",
|
|
19063
|
+
"ae lead",
|
|
19064
|
+
"account executive lead",
|
|
19065
|
+
"sdr lead",
|
|
19066
|
+
"bdr lead",
|
|
19067
|
+
"demand gen",
|
|
19068
|
+
"enablement"
|
|
19069
|
+
];
|
|
19070
|
+
VITAL_ALIASES = {
|
|
19071
|
+
freshness: ["freshness", "stale", "zombie", "dead pipeline"],
|
|
19072
|
+
flow_rate: ["flow_rate", "flow rate", "stuck", "velocity", "stage"],
|
|
19073
|
+
drop_rate: ["drop_rate", "drop rate", "handoff", "leak"],
|
|
19074
|
+
signal_to_noise: ["signal_to_noise", "signal-to-noise", "signal to noise", "noise", "misdirected"],
|
|
19075
|
+
thread_depth: ["thread_depth", "thread depth", "single-thread", "single threaded", "multi-thread"]
|
|
19076
|
+
};
|
|
19077
|
+
START_SOON_RE = /\b(48\s*-?hours?|48h|tomorrow|today|monday|this week|within two days|within 2 days)\b/i;
|
|
19078
|
+
GAP_CODES = [
|
|
19079
|
+
"bottleneck",
|
|
19080
|
+
"stake",
|
|
19081
|
+
"owner",
|
|
19082
|
+
"start_48h",
|
|
19083
|
+
"effort",
|
|
19084
|
+
"scope",
|
|
19085
|
+
"exams",
|
|
19086
|
+
"alternatives_killed",
|
|
19087
|
+
"thin_evidence",
|
|
19088
|
+
"invented_numbers",
|
|
19089
|
+
"objective_drift"
|
|
19090
|
+
];
|
|
19091
|
+
}
|
|
19092
|
+
});
|
|
19093
|
+
|
|
19094
|
+
// src/ai/strategist.ts
|
|
19095
|
+
function buildHealthSnapshot(computeResult, divergences) {
|
|
19096
|
+
const { aggregate, segments } = computeResult;
|
|
19097
|
+
return JSON.stringify(
|
|
19098
|
+
{
|
|
19099
|
+
aggregate: {
|
|
19100
|
+
overall_score: aggregate.overall_score,
|
|
19101
|
+
overall_status: aggregate.overall_status,
|
|
19102
|
+
gating_vital_sign: aggregate.gating_vital_sign,
|
|
19103
|
+
total_value_at_risk: aggregate.total_value_at_risk,
|
|
19104
|
+
vital_signs: Object.fromEntries(
|
|
19105
|
+
aggregate.vital_signs.map((v) => [
|
|
19106
|
+
v.vital_sign,
|
|
19107
|
+
{ score: v.score, status: v.status, dollar_value: v.dollar_value, dollar_label: v.dollar_label }
|
|
19108
|
+
])
|
|
19109
|
+
)
|
|
19110
|
+
},
|
|
19111
|
+
segment_names: segments.map((s) => s.segment.name),
|
|
19112
|
+
top_divergences: divergences.slice(0, 5).map((d) => ({
|
|
19113
|
+
segment: d.segmentName,
|
|
19114
|
+
vital_sign: d.vitalSign,
|
|
19115
|
+
segment_score: d.segmentScore,
|
|
19116
|
+
aggregate_score: d.aggregateScore,
|
|
19117
|
+
delta: d.delta
|
|
19118
|
+
}))
|
|
19119
|
+
},
|
|
19120
|
+
null,
|
|
19121
|
+
2
|
|
19122
|
+
);
|
|
19123
|
+
}
|
|
19124
|
+
async function* strategistPlanSession(options) {
|
|
19125
|
+
assertReplAi(options.ctx);
|
|
19126
|
+
const llmCfg = loadLlmConfig();
|
|
19127
|
+
const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19128
|
+
const systemPrompt = buildStrategistSystemPrompt(todayIso);
|
|
19129
|
+
const tools2 = [...AGENTIC_TOOLS];
|
|
19130
|
+
if (isWebRetrievalEnabled()) tools2.push(WEB_SEARCH_TOOL);
|
|
19131
|
+
const toolCtx = {
|
|
19132
|
+
computeResult: options.computeResult,
|
|
19133
|
+
divergences: options.divergences
|
|
19134
|
+
};
|
|
19135
|
+
if (options.includeMetrics) {
|
|
19136
|
+
try {
|
|
19137
|
+
const { computeFullMetrics: computeFullMetrics2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
|
|
19138
|
+
toolCtx.metrics = (await computeFullMetrics2()).aggregate.metrics;
|
|
19139
|
+
} catch {
|
|
19140
|
+
}
|
|
18402
19141
|
}
|
|
18403
19142
|
const healthSnapshot = buildHealthSnapshot(options.computeResult, options.divergences);
|
|
18404
19143
|
const messages = [
|
|
@@ -18574,6 +19313,15 @@ Respond with ONLY the corrected plan JSON object in the required schema (title,
|
|
|
18574
19313
|
for (const issue of validated.issues) {
|
|
18575
19314
|
yield { type: "notice", text: issue };
|
|
18576
19315
|
}
|
|
19316
|
+
const rubric = scoreConsultantPlan(validated.plan, {
|
|
19317
|
+
snapshot: options.computeResult,
|
|
19318
|
+
constraintsNote: options.constraintsNote,
|
|
19319
|
+
todayIso,
|
|
19320
|
+
evidenceText
|
|
19321
|
+
});
|
|
19322
|
+
for (const gap of rubric.blocking) {
|
|
19323
|
+
yield { type: "notice", text: `Plan gap (${gap.code}): ${gap.note}` };
|
|
19324
|
+
}
|
|
18577
19325
|
yield {
|
|
18578
19326
|
type: "plan",
|
|
18579
19327
|
plan: validated.plan,
|
|
@@ -18625,6 +19373,7 @@ var init_strategist2 = __esm({
|
|
|
18625
19373
|
init_thread();
|
|
18626
19374
|
init_strategist_prompt();
|
|
18627
19375
|
init_strategist_validate();
|
|
19376
|
+
init_strategist_rubric();
|
|
18628
19377
|
init_strategist_prompt();
|
|
18629
19378
|
GROUND_MAX_ROUNDS = 6;
|
|
18630
19379
|
BACKCAST_MAX_ROUNDS = 4;
|
|
@@ -18639,6 +19388,654 @@ var init_strategist2 = __esm({
|
|
|
18639
19388
|
}
|
|
18640
19389
|
});
|
|
18641
19390
|
|
|
19391
|
+
// src/ai/strategist-craft.ts
|
|
19392
|
+
import { createHash as createHash3 } from "crypto";
|
|
19393
|
+
function snapshotHash(snapshotJson) {
|
|
19394
|
+
return createHash3("sha256").update(snapshotJson).digest("hex").slice(0, 16);
|
|
19395
|
+
}
|
|
19396
|
+
function totalTokens(job) {
|
|
19397
|
+
return job.cumulative_input_tokens + job.cumulative_output_tokens;
|
|
19398
|
+
}
|
|
19399
|
+
function fallbackNotice(text) {
|
|
19400
|
+
return /grounded (playbook )?fallback/i.test(text);
|
|
19401
|
+
}
|
|
19402
|
+
function canShip(job, critic, rubricBlocking) {
|
|
19403
|
+
if (!critic.ship) return false;
|
|
19404
|
+
if (rubricBlocking.length > 0) return false;
|
|
19405
|
+
if (critic.score < job.min_score) return false;
|
|
19406
|
+
if (job.from_fallback && !job.revised_once) return false;
|
|
19407
|
+
return true;
|
|
19408
|
+
}
|
|
19409
|
+
function recordScoreStreak(job, score) {
|
|
19410
|
+
if (job.last_critic_score != null && score <= job.last_critic_score) {
|
|
19411
|
+
job.no_improve_streak += 1;
|
|
19412
|
+
} else {
|
|
19413
|
+
job.no_improve_streak = 0;
|
|
19414
|
+
}
|
|
19415
|
+
job.last_critic_score = score;
|
|
19416
|
+
}
|
|
19417
|
+
async function* strategistCraftSession(options) {
|
|
19418
|
+
assertReplAi(options.ctx);
|
|
19419
|
+
const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19420
|
+
const healthSnapshot = buildHealthSnapshot(options.computeResult, options.divergences);
|
|
19421
|
+
const maxRounds = options.maxRounds ?? DEFAULT_CRAFT_MAX_ROUNDS;
|
|
19422
|
+
const minScore = options.minScore ?? DEFAULT_CRAFT_MIN_SCORE;
|
|
19423
|
+
const complete = options.complete ?? completeWithFailover;
|
|
19424
|
+
const runIteration0 = options.runIteration0 ?? strategistPlanSession;
|
|
19425
|
+
const job = options.job ?? createRuminationJob({
|
|
19426
|
+
objective: options.objective,
|
|
19427
|
+
constraintsNote: options.constraintsNote,
|
|
19428
|
+
baselineBatchId: options.baselineBatchId,
|
|
19429
|
+
snapshotHash: snapshotHash(healthSnapshot),
|
|
19430
|
+
minScore,
|
|
19431
|
+
maxRounds,
|
|
19432
|
+
maxTokens: options.maxTokens
|
|
19433
|
+
});
|
|
19434
|
+
if (!options.job) job.objective = options.objective;
|
|
19435
|
+
job.min_score = minScore;
|
|
19436
|
+
job.max_rounds = maxRounds;
|
|
19437
|
+
if (options.maxTokens != null) job.max_tokens = options.maxTokens;
|
|
19438
|
+
saveRuminationJob(job);
|
|
19439
|
+
options.ctx.lastCraftJobId = job.status === "ready" ? void 0 : job.id;
|
|
19440
|
+
saveSessionState(options.ctx);
|
|
19441
|
+
const evidenceText = healthSnapshot;
|
|
19442
|
+
const armedObjective = job.objective;
|
|
19443
|
+
let lastMeta = { provider_used: "unknown", model_used: "unknown" };
|
|
19444
|
+
let measurable = { measurable_targets: 0, total_targets: 0 };
|
|
19445
|
+
const scorePlan = (plan) => scoreConsultantPlan(plan, {
|
|
19446
|
+
snapshot: options.computeResult,
|
|
19447
|
+
constraintsNote: options.constraintsNote ?? job.constraints_note,
|
|
19448
|
+
armedObjective,
|
|
19449
|
+
todayIso,
|
|
19450
|
+
evidenceText
|
|
19451
|
+
});
|
|
19452
|
+
const rememberBest = (plan, score) => {
|
|
19453
|
+
if (score == null) {
|
|
19454
|
+
if (!job.best_plan) job.best_plan = plan;
|
|
19455
|
+
return;
|
|
19456
|
+
}
|
|
19457
|
+
if (job.best_score == null || score > job.best_score) {
|
|
19458
|
+
job.best_score = score;
|
|
19459
|
+
job.best_plan = plan;
|
|
19460
|
+
}
|
|
19461
|
+
};
|
|
19462
|
+
try {
|
|
19463
|
+
if (job.status === "ready" && job.plan) {
|
|
19464
|
+
const locked = lockPlanObjective(job.plan, armedObjective);
|
|
19465
|
+
job.plan = locked;
|
|
19466
|
+
saveRuminationJob(job);
|
|
19467
|
+
yield {
|
|
19468
|
+
type: "plan",
|
|
19469
|
+
plan: locked,
|
|
19470
|
+
issues: [],
|
|
19471
|
+
measurable_targets: measurable.measurable_targets,
|
|
19472
|
+
total_targets: measurable.total_targets,
|
|
19473
|
+
baseline_batch_id: options.baselineBatchId ?? null
|
|
19474
|
+
};
|
|
19475
|
+
yield {
|
|
19476
|
+
type: "done",
|
|
19477
|
+
model_used: lastMeta.model_used,
|
|
19478
|
+
provider_used: lastMeta.provider_used,
|
|
19479
|
+
usage: lastMeta
|
|
19480
|
+
};
|
|
19481
|
+
return { job, plan: locked, status: job.status, stop_reason: job.stop_reason };
|
|
19482
|
+
}
|
|
19483
|
+
if (!job.plan) {
|
|
19484
|
+
yield { type: "stage", stage: "ground", label: "Craft \u2014 grounding, sequencing, stress-testing" };
|
|
19485
|
+
for await (const event of runIteration0({ ...options, objective: armedObjective })) {
|
|
19486
|
+
if (options.interrupted?.()) {
|
|
19487
|
+
job.status = "unfinished";
|
|
19488
|
+
job.stop_reason = "interrupt";
|
|
19489
|
+
saveRuminationJob(job);
|
|
19490
|
+
break;
|
|
19491
|
+
}
|
|
19492
|
+
if (event.type === "notice" && fallbackNotice(event.text)) {
|
|
19493
|
+
job.from_fallback = true;
|
|
19494
|
+
}
|
|
19495
|
+
if (event.type === "plan") {
|
|
19496
|
+
const locked = lockPlanObjective(event.plan, armedObjective);
|
|
19497
|
+
job.plan = locked;
|
|
19498
|
+
rememberBest(locked, null);
|
|
19499
|
+
measurable = {
|
|
19500
|
+
measurable_targets: event.measurable_targets,
|
|
19501
|
+
total_targets: event.total_targets
|
|
19502
|
+
};
|
|
19503
|
+
const rubric = scorePlan(locked);
|
|
19504
|
+
job.rubric_gaps = rubric.gaps;
|
|
19505
|
+
yield { ...event, plan: locked, issues: [...event.issues, ...rubric.blocking.map((g) => `Plan gap (${g.code}): ${g.note}`)] };
|
|
19506
|
+
} else if (event.type === "done") {
|
|
19507
|
+
lastMeta = event.usage ?? lastMeta;
|
|
19508
|
+
job.cumulative_input_tokens += event.usage?.input_tokens ?? 0;
|
|
19509
|
+
job.cumulative_output_tokens += event.usage?.output_tokens ?? 0;
|
|
19510
|
+
yield event;
|
|
19511
|
+
} else {
|
|
19512
|
+
yield event;
|
|
19513
|
+
}
|
|
19514
|
+
}
|
|
19515
|
+
saveRuminationJob(job);
|
|
19516
|
+
}
|
|
19517
|
+
if (!job.plan) {
|
|
19518
|
+
job.status = "unfinished";
|
|
19519
|
+
job.stop_reason = job.stop_reason ?? "error";
|
|
19520
|
+
saveRuminationJob(job);
|
|
19521
|
+
yield {
|
|
19522
|
+
type: "done",
|
|
19523
|
+
model_used: lastMeta.model_used,
|
|
19524
|
+
provider_used: lastMeta.provider_used,
|
|
19525
|
+
usage: lastMeta
|
|
19526
|
+
};
|
|
19527
|
+
return { job, plan: null, status: job.status, stop_reason: job.stop_reason };
|
|
19528
|
+
}
|
|
19529
|
+
rememberBest(job.plan, job.best_score);
|
|
19530
|
+
const systemPrompt = buildStrategistSystemPrompt(todayIso);
|
|
19531
|
+
const callText = async (surface, userContent, maxTokens) => {
|
|
19532
|
+
const result = await complete(
|
|
19533
|
+
{
|
|
19534
|
+
surface,
|
|
19535
|
+
messages: [{ role: "user", content: userContent }],
|
|
19536
|
+
system: systemPrompt,
|
|
19537
|
+
max_tokens: maxTokens
|
|
19538
|
+
},
|
|
19539
|
+
{ ctx: options.ctx }
|
|
19540
|
+
);
|
|
19541
|
+
lastMeta = result.meta;
|
|
19542
|
+
return {
|
|
19543
|
+
text: result.response.text,
|
|
19544
|
+
input: result.response.token_usage?.input_tokens ?? result.meta.input_tokens ?? 0,
|
|
19545
|
+
output: result.response.token_usage?.output_tokens ?? result.meta.output_tokens ?? 0
|
|
19546
|
+
};
|
|
19547
|
+
};
|
|
19548
|
+
let stop;
|
|
19549
|
+
const startRound = (job.iterations.at(-1)?.round ?? 0) + 1;
|
|
19550
|
+
for (let round = startRound; round <= maxRounds; round++) {
|
|
19551
|
+
if (options.interrupted?.()) {
|
|
19552
|
+
stop = "interrupt";
|
|
19553
|
+
break;
|
|
19554
|
+
}
|
|
19555
|
+
if (job.max_tokens != null && totalTokens(job) >= job.max_tokens) {
|
|
19556
|
+
stop = "budget_tokens";
|
|
19557
|
+
break;
|
|
19558
|
+
}
|
|
19559
|
+
const plan = job.plan;
|
|
19560
|
+
if (!plan) {
|
|
19561
|
+
stop = "error";
|
|
19562
|
+
break;
|
|
19563
|
+
}
|
|
19564
|
+
const rubric = scorePlan(plan);
|
|
19565
|
+
job.rubric_gaps = rubric.gaps;
|
|
19566
|
+
const criticRaw = await callText(
|
|
19567
|
+
"strategist_critic",
|
|
19568
|
+
buildCriticMessage({
|
|
19569
|
+
objective: armedObjective,
|
|
19570
|
+
planJson: JSON.stringify(plan),
|
|
19571
|
+
rubricGaps: rubric.blocking.map((g) => `${g.code}: ${g.note}`).join("\n") || "(none)",
|
|
19572
|
+
healthSnapshot
|
|
19573
|
+
}),
|
|
19574
|
+
CRITIC_MAX_TOKENS
|
|
19575
|
+
);
|
|
19576
|
+
const critic = parseCriticResponse(parseJsonObjectFromText(criticRaw.text), {
|
|
19577
|
+
armedObjective,
|
|
19578
|
+
minScore,
|
|
19579
|
+
rubricBlocking: rubric.blocking
|
|
19580
|
+
});
|
|
19581
|
+
job.last_critic = critic;
|
|
19582
|
+
recordScoreStreak(job, critic.score);
|
|
19583
|
+
rememberBest(plan, critic.score);
|
|
19584
|
+
const tokensThisRound = criticRaw.input + criticRaw.output;
|
|
19585
|
+
let decision = "revise";
|
|
19586
|
+
if (canShip(job, critic, rubric.blocking)) {
|
|
19587
|
+
decision = "ship";
|
|
19588
|
+
} else if (job.no_improve_streak >= 2) {
|
|
19589
|
+
decision = "no-delta";
|
|
19590
|
+
}
|
|
19591
|
+
addRuminationIteration(job, {
|
|
19592
|
+
round,
|
|
19593
|
+
critic_score: critic.score,
|
|
19594
|
+
ship: critic.ship,
|
|
19595
|
+
blocking_codes: critic.gaps.filter((g) => g.severity === "blocking").map((g) => g.code),
|
|
19596
|
+
tokens_in: criticRaw.input,
|
|
19597
|
+
tokens_out: criticRaw.output,
|
|
19598
|
+
decision
|
|
19599
|
+
});
|
|
19600
|
+
yield {
|
|
19601
|
+
type: "craft_round",
|
|
19602
|
+
round,
|
|
19603
|
+
max: maxRounds,
|
|
19604
|
+
score: critic.score,
|
|
19605
|
+
tokensThisRound,
|
|
19606
|
+
tokensTotal: totalTokens(job),
|
|
19607
|
+
decision
|
|
19608
|
+
};
|
|
19609
|
+
yield {
|
|
19610
|
+
type: "notice",
|
|
19611
|
+
text: `craft ${round}/${maxRounds} \xB7 score ${critic.score} \xB7 ${tokensThisRound} tok this round \xB7 ${totalTokens(job)} total`
|
|
19612
|
+
};
|
|
19613
|
+
saveRuminationJob(job);
|
|
19614
|
+
if (decision === "ship") {
|
|
19615
|
+
stop = "ship";
|
|
19616
|
+
break;
|
|
19617
|
+
}
|
|
19618
|
+
if (decision === "no-delta") {
|
|
19619
|
+
stop = "no_delta";
|
|
19620
|
+
break;
|
|
19621
|
+
}
|
|
19622
|
+
const needsGround = critic.gaps.some((g) => g.code === "thin_evidence") && !job.extra_ground_used;
|
|
19623
|
+
if (needsGround) {
|
|
19624
|
+
job.extra_ground_used = true;
|
|
19625
|
+
yield { type: "notice", text: "Critic asked for thinner evidence \u2014 one extra ground pass." };
|
|
19626
|
+
for await (const event of runIteration0({ ...options, objective: armedObjective })) {
|
|
19627
|
+
if (event.type === "notice" && fallbackNotice(event.text)) {
|
|
19628
|
+
job.from_fallback = true;
|
|
19629
|
+
} else if (event.type === "plan") {
|
|
19630
|
+
job.plan = lockPlanObjective(event.plan, armedObjective);
|
|
19631
|
+
rememberBest(job.plan, null);
|
|
19632
|
+
measurable = {
|
|
19633
|
+
measurable_targets: event.measurable_targets,
|
|
19634
|
+
total_targets: event.total_targets
|
|
19635
|
+
};
|
|
19636
|
+
} else if (event.type !== "done") {
|
|
19637
|
+
yield event;
|
|
19638
|
+
}
|
|
19639
|
+
}
|
|
19640
|
+
saveRuminationJob(job);
|
|
19641
|
+
continue;
|
|
19642
|
+
}
|
|
19643
|
+
if (options.interrupted?.()) {
|
|
19644
|
+
stop = "interrupt";
|
|
19645
|
+
break;
|
|
19646
|
+
}
|
|
19647
|
+
if (job.max_tokens != null && totalTokens(job) >= job.max_tokens) {
|
|
19648
|
+
stop = "budget_tokens";
|
|
19649
|
+
break;
|
|
19650
|
+
}
|
|
19651
|
+
const reviseRaw = await callText(
|
|
19652
|
+
"strategist",
|
|
19653
|
+
buildReviseMessage({
|
|
19654
|
+
objective: armedObjective,
|
|
19655
|
+
planJson: JSON.stringify(plan),
|
|
19656
|
+
gapsJson: JSON.stringify(critic.gaps)
|
|
19657
|
+
}),
|
|
19658
|
+
REVISE_MAX_TOKENS
|
|
19659
|
+
);
|
|
19660
|
+
job.cumulative_input_tokens += reviseRaw.input;
|
|
19661
|
+
job.cumulative_output_tokens += reviseRaw.output;
|
|
19662
|
+
const parsed = parseJsonObjectFromText(reviseRaw.text);
|
|
19663
|
+
const validated = parsed ? validateStrategistPlan(parsed, { evidenceText, todayIso }) : null;
|
|
19664
|
+
if (validated) {
|
|
19665
|
+
job.plan = lockPlanObjective(validated.plan, armedObjective);
|
|
19666
|
+
job.revised_once = true;
|
|
19667
|
+
job.from_fallback = false;
|
|
19668
|
+
measurable = {
|
|
19669
|
+
measurable_targets: validated.measurableTargets,
|
|
19670
|
+
total_targets: validated.totalTargets
|
|
19671
|
+
};
|
|
19672
|
+
const nextRubric = scorePlan(job.plan);
|
|
19673
|
+
job.rubric_gaps = nextRubric.gaps;
|
|
19674
|
+
} else {
|
|
19675
|
+
yield { type: "notice", text: "Revise JSON invalid \u2014 keeping prior plan." };
|
|
19676
|
+
}
|
|
19677
|
+
saveRuminationJob(job);
|
|
19678
|
+
}
|
|
19679
|
+
if (!stop) stop = "budget_rounds";
|
|
19680
|
+
if (stop !== "ship" && job.best_plan) {
|
|
19681
|
+
job.plan = lockPlanObjective(job.best_plan, armedObjective);
|
|
19682
|
+
}
|
|
19683
|
+
job.stop_reason = stop;
|
|
19684
|
+
job.status = stop === "ship" ? "ready" : "unfinished";
|
|
19685
|
+
saveRuminationJob(job);
|
|
19686
|
+
if (job.plan) {
|
|
19687
|
+
const rubric = scorePlan(job.plan);
|
|
19688
|
+
yield {
|
|
19689
|
+
type: "plan",
|
|
19690
|
+
plan: job.plan,
|
|
19691
|
+
issues: rubric.blocking.map((g) => `Plan gap (${g.code}): ${g.note}`),
|
|
19692
|
+
measurable_targets: measurable.measurable_targets,
|
|
19693
|
+
total_targets: measurable.total_targets,
|
|
19694
|
+
baseline_batch_id: options.baselineBatchId ?? null
|
|
19695
|
+
};
|
|
19696
|
+
}
|
|
19697
|
+
yield {
|
|
19698
|
+
type: "done",
|
|
19699
|
+
model_used: lastMeta.model_used,
|
|
19700
|
+
provider_used: lastMeta.provider_used,
|
|
19701
|
+
failover: lastMeta.failover,
|
|
19702
|
+
usage: {
|
|
19703
|
+
...lastMeta,
|
|
19704
|
+
input_tokens: job.cumulative_input_tokens,
|
|
19705
|
+
output_tokens: job.cumulative_output_tokens
|
|
19706
|
+
}
|
|
19707
|
+
};
|
|
19708
|
+
return { job, plan: job.plan, status: job.status, stop_reason: job.stop_reason };
|
|
19709
|
+
} catch (err) {
|
|
19710
|
+
job.status = "unfinished";
|
|
19711
|
+
job.stop_reason = "error";
|
|
19712
|
+
if (job.best_plan) job.plan = lockPlanObjective(job.best_plan, armedObjective);
|
|
19713
|
+
saveRuminationJob(job);
|
|
19714
|
+
throw err;
|
|
19715
|
+
}
|
|
19716
|
+
}
|
|
19717
|
+
function formatCraftRoundLine(event) {
|
|
19718
|
+
const score = event.score == null ? "\u2014" : String(event.score);
|
|
19719
|
+
const tok = event.tokensThisRound >= 1e3 ? `${Math.round(event.tokensThisRound / 1e3)}k` : String(event.tokensThisRound);
|
|
19720
|
+
const total = event.tokensTotal >= 1e3 ? `${Math.round(event.tokensTotal / 1e3)}k` : String(event.tokensTotal);
|
|
19721
|
+
return `craft ${event.round}/${event.max} \xB7 score ${score} \xB7 ${tok} tok this round \xB7 ${total} total`;
|
|
19722
|
+
}
|
|
19723
|
+
var CRITIC_MAX_TOKENS, REVISE_MAX_TOKENS, DEFAULT_CRAFT_MAX_ROUNDS, DEFAULT_CRAFT_MIN_SCORE;
|
|
19724
|
+
var init_strategist_craft = __esm({
|
|
19725
|
+
"src/ai/strategist-craft.ts"() {
|
|
19726
|
+
"use strict";
|
|
19727
|
+
init_failover();
|
|
19728
|
+
init_repl_api();
|
|
19729
|
+
init_context2();
|
|
19730
|
+
init_strategist2();
|
|
19731
|
+
init_strategist_validate();
|
|
19732
|
+
init_strategist_rubric();
|
|
19733
|
+
init_strategist_prompt();
|
|
19734
|
+
init_store3();
|
|
19735
|
+
CRITIC_MAX_TOKENS = 2048;
|
|
19736
|
+
REVISE_MAX_TOKENS = 8192;
|
|
19737
|
+
DEFAULT_CRAFT_MAX_ROUNDS = 5;
|
|
19738
|
+
DEFAULT_CRAFT_MIN_SCORE = 85;
|
|
19739
|
+
}
|
|
19740
|
+
});
|
|
19741
|
+
|
|
19742
|
+
// src/ruminations/handoff.ts
|
|
19743
|
+
function renderCraftHandoffMarkdown(opts) {
|
|
19744
|
+
const { plan } = opts;
|
|
19745
|
+
const lines = [];
|
|
19746
|
+
lines.push("# Action plan \u2014 NTRP craft");
|
|
19747
|
+
lines.push("");
|
|
19748
|
+
lines.push("Use this file as a prompt for another agent or as a morning brief.");
|
|
19749
|
+
lines.push("");
|
|
19750
|
+
lines.push("## The Call");
|
|
19751
|
+
lines.push("");
|
|
19752
|
+
lines.push(plan.summary_30k);
|
|
19753
|
+
lines.push("");
|
|
19754
|
+
lines.push("## Locked objective");
|
|
19755
|
+
lines.push("");
|
|
19756
|
+
lines.push(plan.objective);
|
|
19757
|
+
lines.push("");
|
|
19758
|
+
lines.push(renderConstraintHeading(opts.constraintLine).trimEnd());
|
|
19759
|
+
lines.push("");
|
|
19760
|
+
lines.push(renderScopeHeading(plan.constraints, opts.outOfScope).trimEnd());
|
|
19761
|
+
lines.push("");
|
|
19762
|
+
lines.push("## Hypothesis");
|
|
19763
|
+
lines.push("");
|
|
19764
|
+
lines.push(plan.hypothesis);
|
|
19765
|
+
lines.push("");
|
|
19766
|
+
lines.push(renderKilledAlternativeLine(opts.killedAlternative));
|
|
19767
|
+
lines.push("");
|
|
19768
|
+
lines.push(renderEffortHeading(plan.workstreams).trimEnd());
|
|
19769
|
+
lines.push("");
|
|
19770
|
+
lines.push("## Workstreams");
|
|
19771
|
+
lines.push("");
|
|
19772
|
+
for (const ws of plan.workstreams) {
|
|
19773
|
+
lines.push(`### ${ws.order}. ${ws.title}`);
|
|
19774
|
+
lines.push("");
|
|
19775
|
+
lines.push(`- Problem: ${ws.problem}`);
|
|
19776
|
+
lines.push(`- Why this order: ${ws.rationale}`);
|
|
19777
|
+
if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
|
|
19778
|
+
lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
|
|
19779
|
+
lines.push(
|
|
19780
|
+
`- Exam: ${ws.expected_outcome.metric} ${ws.expected_outcome.baseline} -> ${ws.expected_outcome.target_range} by ${ws.expected_outcome.check_date} (${ws.expected_outcome.measured_by})`
|
|
19781
|
+
);
|
|
19782
|
+
lines.push(
|
|
19783
|
+
`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) -> ${ws.contingency.fallback}`
|
|
19784
|
+
);
|
|
19785
|
+
lines.push("");
|
|
19786
|
+
}
|
|
19787
|
+
if (plan.risks.length > 0) {
|
|
19788
|
+
lines.push("## Risks");
|
|
19789
|
+
lines.push("");
|
|
19790
|
+
for (const r of plan.risks) lines.push(`- ${r}`);
|
|
19791
|
+
lines.push("");
|
|
19792
|
+
}
|
|
19793
|
+
if (plan.assumptions.length > 0) {
|
|
19794
|
+
lines.push("## Assumptions");
|
|
19795
|
+
lines.push("");
|
|
19796
|
+
for (const a of plan.assumptions) lines.push(`- ${a}`);
|
|
19797
|
+
lines.push("");
|
|
19798
|
+
}
|
|
19799
|
+
lines.push(renderReviewHeading({ cadence: plan.review_cadence, slug: opts.slug }).trimEnd());
|
|
19800
|
+
lines.push("");
|
|
19801
|
+
if (opts.journalPath) {
|
|
19802
|
+
lines.push("## Craft log");
|
|
19803
|
+
lines.push("");
|
|
19804
|
+
lines.push(`Status: ${opts.status}`);
|
|
19805
|
+
lines.push(`Log: ${opts.journalPath}`);
|
|
19806
|
+
if (opts.libraryPath) lines.push(`Strategy library: ${opts.libraryPath}`);
|
|
19807
|
+
lines.push("");
|
|
19808
|
+
} else if (opts.libraryPath) {
|
|
19809
|
+
lines.push("## Strategy library");
|
|
19810
|
+
lines.push("");
|
|
19811
|
+
lines.push(opts.libraryPath);
|
|
19812
|
+
lines.push("");
|
|
19813
|
+
}
|
|
19814
|
+
return lines.join("\n");
|
|
19815
|
+
}
|
|
19816
|
+
function writeCraftPlanHandoff(opts) {
|
|
19817
|
+
const journalPath = opts.jobId ? ruminationLogPath(opts.jobId) : void 0;
|
|
19818
|
+
const markdown = renderCraftHandoffMarkdown({
|
|
19819
|
+
plan: opts.plan,
|
|
19820
|
+
journalPath,
|
|
19821
|
+
libraryPath: opts.libraryPath,
|
|
19822
|
+
status: opts.status,
|
|
19823
|
+
constraintLine: opts.constraintLine,
|
|
19824
|
+
killedAlternative: opts.killedAlternative,
|
|
19825
|
+
outOfScope: opts.outOfScope,
|
|
19826
|
+
slug: opts.slug
|
|
19827
|
+
});
|
|
19828
|
+
const path = resolveArchivePath("prompt:plan", `handoff-plan-${exportStamp()}.md`);
|
|
19829
|
+
writeRedactedText(path, markdown);
|
|
19830
|
+
const event = recordExportWrite({
|
|
19831
|
+
kind: "prompt:plan",
|
|
19832
|
+
path,
|
|
19833
|
+
sessionId: opts.sessionId,
|
|
19834
|
+
title: `${opts.plan.title} craft plan`
|
|
19835
|
+
});
|
|
19836
|
+
return { path, inboxPath: event.inbox_path };
|
|
19837
|
+
}
|
|
19838
|
+
var init_handoff = __esm({
|
|
19839
|
+
"src/ruminations/handoff.ts"() {
|
|
19840
|
+
"use strict";
|
|
19841
|
+
init_exports_registry();
|
|
19842
|
+
init_redact_write();
|
|
19843
|
+
init_library();
|
|
19844
|
+
init_store3();
|
|
19845
|
+
}
|
|
19846
|
+
});
|
|
19847
|
+
|
|
19848
|
+
// src/services/strategist-run.ts
|
|
19849
|
+
async function assertComputableData() {
|
|
19850
|
+
await initSchema();
|
|
19851
|
+
let counts = {};
|
|
19852
|
+
try {
|
|
19853
|
+
counts = await getEntityCounts();
|
|
19854
|
+
} catch {
|
|
19855
|
+
counts = {};
|
|
19856
|
+
}
|
|
19857
|
+
if (!Object.values(counts).some((n) => (n ?? 0) > 0)) {
|
|
19858
|
+
throw new NtrpError(
|
|
19859
|
+
"strategy_no_data",
|
|
19860
|
+
"No pipeline data loaded. Ingest a CSV, run demo data, then craft a plan.",
|
|
19861
|
+
4 /* NoData */
|
|
19862
|
+
);
|
|
19863
|
+
}
|
|
19864
|
+
}
|
|
19865
|
+
function rememberCraftJob(ctx, job) {
|
|
19866
|
+
if (!job) return;
|
|
19867
|
+
ctx.lastCraftJobId = job.status === "ready" ? void 0 : job.id;
|
|
19868
|
+
saveSessionState(ctx);
|
|
19869
|
+
}
|
|
19870
|
+
async function executeStrategistJob(req) {
|
|
19871
|
+
if (!canUseReplAi(req.ctx)) {
|
|
19872
|
+
throw new NtrpError(
|
|
19873
|
+
"strategy_no_key",
|
|
19874
|
+
"No LLM API key configured. Run /connect and paste a key, then retry craft.",
|
|
19875
|
+
3 /* Auth */
|
|
19876
|
+
);
|
|
19877
|
+
}
|
|
19878
|
+
const resumeJob = req.resumeId ? loadRuminationJob(req.resumeId) : null;
|
|
19879
|
+
if (req.resumeId && !resumeJob) {
|
|
19880
|
+
throw new NtrpError(
|
|
19881
|
+
"strategy_resume_missing",
|
|
19882
|
+
`No craft job "${req.resumeId}". Check ~/.ntrp/ruminations/.`,
|
|
19883
|
+
2 /* Usage */
|
|
19884
|
+
);
|
|
19885
|
+
}
|
|
19886
|
+
if (resumeJob) rememberCraftJob(req.ctx, resumeJob);
|
|
19887
|
+
const objective = resumeJob?.objective || req.objective;
|
|
19888
|
+
if (!objective.trim()) {
|
|
19889
|
+
throw new NtrpError(
|
|
19890
|
+
"strategy_objective_required",
|
|
19891
|
+
'strategy craft requires an objective. Example: ntrp strategy craft "cut stale pipeline before Q4"',
|
|
19892
|
+
2 /* Usage */
|
|
19893
|
+
);
|
|
19894
|
+
}
|
|
19895
|
+
await assertComputableData();
|
|
19896
|
+
const inputs = await prepareStrategistInputs(req.ctx, objective);
|
|
19897
|
+
const notices = [];
|
|
19898
|
+
let plan = resumeJob?.plan ?? null;
|
|
19899
|
+
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
19900
|
+
let meta = {};
|
|
19901
|
+
let job = resumeJob ?? void 0;
|
|
19902
|
+
const sessionOpts = {
|
|
19903
|
+
objective,
|
|
19904
|
+
computeResult: inputs.snapshot,
|
|
19905
|
+
divergences: inputs.divergences,
|
|
19906
|
+
includeMetrics: inputs.includeMetrics,
|
|
19907
|
+
memoryBlock: inputs.memoryBlock,
|
|
19908
|
+
gapAuditBlock: inputs.gapAuditBlock,
|
|
19909
|
+
constraintsNote: req.constraintsNote ?? resumeJob?.constraints_note,
|
|
19910
|
+
baselineBatchId: inputs.baselineBatchId,
|
|
19911
|
+
ctx: req.ctx
|
|
19912
|
+
};
|
|
19913
|
+
const useCraft = req.mode === "craft" && req.untilReady !== false;
|
|
19914
|
+
const handleEvent = (event) => {
|
|
19915
|
+
req.onEvent?.(event);
|
|
19916
|
+
if (event.type === "notice") notices.push(event.text);
|
|
19917
|
+
if (event.type === "plan") {
|
|
19918
|
+
plan = event.plan;
|
|
19919
|
+
stats = {
|
|
19920
|
+
measurable_targets: event.measurable_targets,
|
|
19921
|
+
total_targets: event.total_targets
|
|
19922
|
+
};
|
|
19923
|
+
}
|
|
19924
|
+
if (event.type === "done") {
|
|
19925
|
+
meta = event.usage ?? { model_used: event.model_used, provider_used: event.provider_used };
|
|
19926
|
+
}
|
|
19927
|
+
if (event.type === "craft_round") {
|
|
19928
|
+
notices.push(formatCraftRoundLine(event));
|
|
19929
|
+
}
|
|
19930
|
+
};
|
|
19931
|
+
if (useCraft) {
|
|
19932
|
+
const gen = strategistCraftSession({
|
|
19933
|
+
...sessionOpts,
|
|
19934
|
+
maxRounds: req.maxRounds ?? resumeJob?.max_rounds ?? DEFAULT_CRAFT_MAX_ROUNDS,
|
|
19935
|
+
maxTokens: req.maxTokens ?? resumeJob?.max_tokens,
|
|
19936
|
+
minScore: req.minScore ?? resumeJob?.min_score ?? DEFAULT_CRAFT_MIN_SCORE,
|
|
19937
|
+
job: resumeJob ?? void 0,
|
|
19938
|
+
interrupted: req.interrupted
|
|
19939
|
+
});
|
|
19940
|
+
let next = await gen.next();
|
|
19941
|
+
while (!next.done) {
|
|
19942
|
+
handleEvent(next.value);
|
|
19943
|
+
next = await gen.next();
|
|
19944
|
+
}
|
|
19945
|
+
job = next.value.job;
|
|
19946
|
+
plan = next.value.plan;
|
|
19947
|
+
} else {
|
|
19948
|
+
for await (const event of strategistPlanSession(sessionOpts)) {
|
|
19949
|
+
handleEvent(event);
|
|
19950
|
+
}
|
|
19951
|
+
}
|
|
19952
|
+
const blocking = job?.rubric_gaps.filter((g) => g.severity === "blocking").map((g) => g.code) ?? [];
|
|
19953
|
+
const status = job?.status === "unfinished" || !plan ? "unfinished" : "ready";
|
|
19954
|
+
const result = {
|
|
19955
|
+
plan,
|
|
19956
|
+
status,
|
|
19957
|
+
stop_reason: job?.stop_reason,
|
|
19958
|
+
objective,
|
|
19959
|
+
critic_score: job?.last_critic?.score ?? null,
|
|
19960
|
+
rounds: job?.iterations.length ?? 0,
|
|
19961
|
+
usage: meta,
|
|
19962
|
+
journal_path: job ? ruminationLogPath(job.id) : void 0,
|
|
19963
|
+
blocking_gaps: blocking,
|
|
19964
|
+
measurable_targets: stats.measurable_targets,
|
|
19965
|
+
total_targets: stats.total_targets,
|
|
19966
|
+
notices,
|
|
19967
|
+
job
|
|
19968
|
+
};
|
|
19969
|
+
const constraintLine = formatConstraintLine(inputs.snapshot.aggregate);
|
|
19970
|
+
const killedAlternative = job?.last_critic?.killed_alternative;
|
|
19971
|
+
if (plan && req.save) {
|
|
19972
|
+
const persisted = await persistStrategistPlan(plan, {
|
|
19973
|
+
baselineBatchId: inputs.baselineBatchId,
|
|
19974
|
+
craftLogPath: job ? ruminationLogPath(job.id) : void 0,
|
|
19975
|
+
constraintLine,
|
|
19976
|
+
killedAlternative
|
|
19977
|
+
});
|
|
19978
|
+
result.slug = persisted.strategy.slug;
|
|
19979
|
+
result.library_path = persisted.library_path;
|
|
19980
|
+
req.ctx.deliverables.push({
|
|
19981
|
+
kind: "strategy",
|
|
19982
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19983
|
+
path: persisted.library_path,
|
|
19984
|
+
note: plan.title
|
|
19985
|
+
});
|
|
19986
|
+
if (job) {
|
|
19987
|
+
job.library_path = persisted.library_path;
|
|
19988
|
+
}
|
|
19989
|
+
}
|
|
19990
|
+
if (plan && req.handoff) {
|
|
19991
|
+
const written = writeCraftPlanHandoff({
|
|
19992
|
+
plan,
|
|
19993
|
+
jobId: job?.id,
|
|
19994
|
+
status: result.status,
|
|
19995
|
+
libraryPath: result.library_path,
|
|
19996
|
+
sessionId: req.ctx.sessionId,
|
|
19997
|
+
constraintLine,
|
|
19998
|
+
killedAlternative,
|
|
19999
|
+
slug: result.slug
|
|
20000
|
+
});
|
|
20001
|
+
result.handoff_path = written.path;
|
|
20002
|
+
result.inbox_path = written.inboxPath;
|
|
20003
|
+
if (job) {
|
|
20004
|
+
job.handoff_path = written.path;
|
|
20005
|
+
job.inbox_path = written.inboxPath;
|
|
20006
|
+
}
|
|
20007
|
+
req.ctx.deliverables.push({
|
|
20008
|
+
kind: "prompt:plan",
|
|
20009
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20010
|
+
path: written.path,
|
|
20011
|
+
note: plan.title
|
|
20012
|
+
});
|
|
20013
|
+
}
|
|
20014
|
+
if (job) {
|
|
20015
|
+
if (req.ctx.strategistState) req.ctx.strategistState.ruminationId = job.id;
|
|
20016
|
+
saveRuminationJob(job);
|
|
20017
|
+
rememberCraftJob(req.ctx, job);
|
|
20018
|
+
}
|
|
20019
|
+
return result;
|
|
20020
|
+
}
|
|
20021
|
+
var init_strategist_run = __esm({
|
|
20022
|
+
"src/services/strategist-run.ts"() {
|
|
20023
|
+
"use strict";
|
|
20024
|
+
init_context2();
|
|
20025
|
+
init_errors2();
|
|
20026
|
+
init_types2();
|
|
20027
|
+
init_repl_api();
|
|
20028
|
+
init_strategist2();
|
|
20029
|
+
init_strategist_craft();
|
|
20030
|
+
init_formatters();
|
|
20031
|
+
init_strategist();
|
|
20032
|
+
init_queries();
|
|
20033
|
+
init_schema();
|
|
20034
|
+
init_store3();
|
|
20035
|
+
init_handoff();
|
|
20036
|
+
}
|
|
20037
|
+
});
|
|
20038
|
+
|
|
18642
20039
|
// src/output/strategy-brief.ts
|
|
18643
20040
|
import chalk16 from "chalk";
|
|
18644
20041
|
function printWrapped(text, width, prefix = INDENT, style) {
|
|
@@ -18730,6 +20127,40 @@ function printStrategyBrief(plan, stats) {
|
|
|
18730
20127
|
console.log(`${INDENT}${coverageStyled}${chalk16.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
|
|
18731
20128
|
console.log();
|
|
18732
20129
|
}
|
|
20130
|
+
function printCraftWrapUp(opts) {
|
|
20131
|
+
console.log();
|
|
20132
|
+
if (opts.status === "ready") {
|
|
20133
|
+
console.log(INDENT + paint("accent", "Plan ready"));
|
|
20134
|
+
if (opts.library_path) console.log(INDENT + chalk16.dim(opts.library_path));
|
|
20135
|
+
if (opts.slug) {
|
|
20136
|
+
console.log(
|
|
20137
|
+
INDENT + chalk16.dim("Type ") + paint("accent", `/strategy review ${opts.slug}`) + chalk16.dim(" to check progress.")
|
|
20138
|
+
);
|
|
20139
|
+
}
|
|
20140
|
+
const handoff = opts.inbox_path ?? opts.handoff_path;
|
|
20141
|
+
if (handoff) console.log(INDENT + chalk16.dim(handoff));
|
|
20142
|
+
} else {
|
|
20143
|
+
console.log(INDENT + paint("accent", "Best so far."));
|
|
20144
|
+
const path = opts.library_path ?? opts.handoff_path ?? opts.inbox_path;
|
|
20145
|
+
if (path) console.log(INDENT + chalk16.dim(path));
|
|
20146
|
+
if (opts.oneShot && opts.jobId) {
|
|
20147
|
+
console.log(
|
|
20148
|
+
INDENT + chalk16.dim("Resume: ") + paint("accent", `ntrp strategy craft --resume ${opts.jobId}`)
|
|
20149
|
+
);
|
|
20150
|
+
} else {
|
|
20151
|
+
console.log(
|
|
20152
|
+
INDENT + chalk16.dim("Type ") + chalk16.cyan("keep going") + chalk16.dim(" to continue.")
|
|
20153
|
+
);
|
|
20154
|
+
}
|
|
20155
|
+
}
|
|
20156
|
+
if (opts.oneShot && opts.journal_path) {
|
|
20157
|
+
console.log(INDENT + chalk16.dim(`Craft log: ${opts.journal_path}`));
|
|
20158
|
+
}
|
|
20159
|
+
console.log();
|
|
20160
|
+
}
|
|
20161
|
+
function isCraftRoundNotice(text) {
|
|
20162
|
+
return /^craft \d+\/\d+/.test(text);
|
|
20163
|
+
}
|
|
18733
20164
|
var INDENT;
|
|
18734
20165
|
var init_strategy_brief = __esm({
|
|
18735
20166
|
"src/output/strategy-brief.ts"() {
|
|
@@ -18744,7 +20175,9 @@ var init_strategy_brief = __esm({
|
|
|
18744
20175
|
var strategist_flow_exports = {};
|
|
18745
20176
|
__export(strategist_flow_exports, {
|
|
18746
20177
|
extractObjectiveSeed: () => extractObjectiveSeed,
|
|
20178
|
+
handleKeepGoingLine: () => handleKeepGoingLine,
|
|
18747
20179
|
handleStrategizeFlow: () => handleStrategizeFlow,
|
|
20180
|
+
isKeepGoingIntent: () => isKeepGoingIntent,
|
|
18748
20181
|
isStrategistIntent: () => isStrategistIntent,
|
|
18749
20182
|
promptQueuedAiStrategist: () => promptQueuedAiStrategist,
|
|
18750
20183
|
queueStrategistForAnalysis: () => queueStrategistForAnalysis,
|
|
@@ -18767,7 +20200,8 @@ function queueStrategistForAnalysis(ctx, opts) {
|
|
|
18767
20200
|
ctx.strategistState = {
|
|
18768
20201
|
step: "awaiting_analysis",
|
|
18769
20202
|
objective: opts.seed,
|
|
18770
|
-
origin: opts.origin
|
|
20203
|
+
origin: opts.origin,
|
|
20204
|
+
mode: opts.mode
|
|
18771
20205
|
};
|
|
18772
20206
|
saveSessionState(ctx);
|
|
18773
20207
|
console.log();
|
|
@@ -18792,7 +20226,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
18792
20226
|
objective = (snapshot ? proposeObjectiveFromSnapshot(snapshot) : null) ?? "";
|
|
18793
20227
|
}
|
|
18794
20228
|
if (!objective) {
|
|
18795
|
-
ctx.strategistState = { step: "objective_input", origin: opts.origin };
|
|
20229
|
+
ctx.strategistState = { step: "objective_input", origin: opts.origin, mode: opts.mode };
|
|
18796
20230
|
saveSessionState(ctx);
|
|
18797
20231
|
console.log();
|
|
18798
20232
|
console.log(" " + chalk17.dim('What is the objective? State a finish line. Example: "cut stale pipeline in half before Q4".'));
|
|
@@ -18800,7 +20234,7 @@ async function startStrategistFlow(ctx, opts) {
|
|
|
18800
20234
|
recordMessage(ctx, "agent", "Strategist: asked for objective");
|
|
18801
20235
|
return "Awaiting objective";
|
|
18802
20236
|
}
|
|
18803
|
-
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin };
|
|
20237
|
+
ctx.strategistState = { step: "objective_confirm", objective, origin: opts.origin, mode: opts.mode };
|
|
18804
20238
|
saveSessionState(ctx);
|
|
18805
20239
|
printObjectiveCard(ctx, objective, !opts.seed);
|
|
18806
20240
|
recordMessage(ctx, "agent", `Strategist objective proposed: ${objective}`);
|
|
@@ -18813,7 +20247,8 @@ async function resumeStrategistAfterCompute(ctx) {
|
|
|
18813
20247
|
console.log(" " + paint("accent", "Analysis is ready. The strategy session continues."));
|
|
18814
20248
|
await startStrategistFlow(ctx, {
|
|
18815
20249
|
seed: state2.objective,
|
|
18816
|
-
origin: state2.origin ?? "nl"
|
|
20250
|
+
origin: state2.origin ?? "nl",
|
|
20251
|
+
mode: state2.mode
|
|
18817
20252
|
});
|
|
18818
20253
|
}
|
|
18819
20254
|
function promptQueuedAiStrategist(ctx) {
|
|
@@ -18823,6 +20258,64 @@ function promptQueuedAiStrategist(ctx) {
|
|
|
18823
20258
|
}
|
|
18824
20259
|
printObjectiveCard(ctx, state2.objective, true);
|
|
18825
20260
|
}
|
|
20261
|
+
function isKeepGoingIntent(input) {
|
|
20262
|
+
return CRAFT_RE.test(input.trim());
|
|
20263
|
+
}
|
|
20264
|
+
async function handleKeepGoingLine(ctx, input) {
|
|
20265
|
+
const line = input.trim();
|
|
20266
|
+
recordMessage(ctx, "user", line);
|
|
20267
|
+
const job = unfinishedCraftJob(ctx.lastCraftJobId);
|
|
20268
|
+
if (!job) {
|
|
20269
|
+
console.log();
|
|
20270
|
+
console.log(
|
|
20271
|
+
" " + chalk17.dim("No plan in progress. Type ") + chalk17.cyan("how should we fix this?") + chalk17.dim(" to start one.")
|
|
20272
|
+
);
|
|
20273
|
+
console.log();
|
|
20274
|
+
recordMessage(ctx, "agent", "No plan in progress");
|
|
20275
|
+
return "No plan in progress";
|
|
20276
|
+
}
|
|
20277
|
+
if (!canUseReplAi(ctx)) {
|
|
20278
|
+
console.log();
|
|
20279
|
+
console.log(
|
|
20280
|
+
" " + chalk17.dim("Type ") + chalk17.cyan("/connect") + chalk17.dim(" to connect a key. Then type keep going.")
|
|
20281
|
+
);
|
|
20282
|
+
console.log();
|
|
20283
|
+
recordMessage(ctx, "agent", "Keep going needs a key");
|
|
20284
|
+
return "Keep going needs a key";
|
|
20285
|
+
}
|
|
20286
|
+
const spinner = makeSpinner("Working the plan\u2026");
|
|
20287
|
+
try {
|
|
20288
|
+
const result = await executeStrategistJob({
|
|
20289
|
+
ctx,
|
|
20290
|
+
objective: job.objective,
|
|
20291
|
+
constraintsNote: job.constraints_note,
|
|
20292
|
+
mode: "craft",
|
|
20293
|
+
untilReady: true,
|
|
20294
|
+
save: true,
|
|
20295
|
+
handoff: true,
|
|
20296
|
+
resumeId: job.id,
|
|
20297
|
+
onEvent: (event) => {
|
|
20298
|
+
if (event.type === "thinking") {
|
|
20299
|
+
spinner.stop();
|
|
20300
|
+
console.log(" " + chalk17.dim.italic(event.text));
|
|
20301
|
+
spinner.start();
|
|
20302
|
+
spinner.text = "Working the plan\u2026";
|
|
20303
|
+
}
|
|
20304
|
+
}
|
|
20305
|
+
});
|
|
20306
|
+
spinner.stop();
|
|
20307
|
+
printCraftReplResult(ctx, result, job.objective);
|
|
20308
|
+
ctx.strategistState = void 0;
|
|
20309
|
+
saveSessionState(ctx);
|
|
20310
|
+
return result.status === "ready" ? `Strategy crafted: ${result.plan?.title}` : "Strategy craft unfinished";
|
|
20311
|
+
} catch (err) {
|
|
20312
|
+
spinner.fail("Strategy craft failed");
|
|
20313
|
+
console.error(" " + chalk17.red(String(err.message ?? err)));
|
|
20314
|
+
ctx.strategistState = void 0;
|
|
20315
|
+
saveSessionState(ctx);
|
|
20316
|
+
return;
|
|
20317
|
+
}
|
|
20318
|
+
}
|
|
18826
20319
|
async function handleStrategizeFlow(input, ctx) {
|
|
18827
20320
|
const state2 = ctx.strategistState;
|
|
18828
20321
|
if (!state2) return;
|
|
@@ -18849,6 +20342,11 @@ async function handleStrategizeFlow(input, ctx) {
|
|
|
18849
20342
|
printObjectiveCard(ctx, state2.objective, false);
|
|
18850
20343
|
return "Objective proposed";
|
|
18851
20344
|
}
|
|
20345
|
+
if (CRAFT_RE.test(line)) {
|
|
20346
|
+
state2.mode = "craft";
|
|
20347
|
+
saveSessionState(ctx);
|
|
20348
|
+
return runStrategistSession(ctx);
|
|
20349
|
+
}
|
|
18852
20350
|
if (CONFIRM_RE.test(line)) {
|
|
18853
20351
|
return runStrategistSession(ctx);
|
|
18854
20352
|
}
|
|
@@ -18899,7 +20397,7 @@ async function runStrategistSession(ctx) {
|
|
|
18899
20397
|
saveSessionState(ctx);
|
|
18900
20398
|
return "Skeleton plan (awaiting connect)";
|
|
18901
20399
|
}
|
|
18902
|
-
if (ctx.rl && !state2.constraintsNote) {
|
|
20400
|
+
if (ctx.rl && !state2.constraintsNote && state2.mode !== "craft") {
|
|
18903
20401
|
const prompts = createPromptSession(ctx.rl, ctx);
|
|
18904
20402
|
try {
|
|
18905
20403
|
const note = await prompts.ask(
|
|
@@ -18913,7 +20411,39 @@ async function runStrategistSession(ctx) {
|
|
|
18913
20411
|
}
|
|
18914
20412
|
}
|
|
18915
20413
|
console.log();
|
|
18916
|
-
const spinner = makeSpinner("Reading live data\u2026");
|
|
20414
|
+
const spinner = makeSpinner(state2.mode === "craft" ? "Working the plan\u2026" : "Reading live data\u2026");
|
|
20415
|
+
if (state2.mode === "craft") {
|
|
20416
|
+
try {
|
|
20417
|
+
const result = await executeStrategistJob({
|
|
20418
|
+
ctx,
|
|
20419
|
+
objective,
|
|
20420
|
+
constraintsNote: state2.constraintsNote,
|
|
20421
|
+
mode: "craft",
|
|
20422
|
+
untilReady: true,
|
|
20423
|
+
save: true,
|
|
20424
|
+
handoff: true,
|
|
20425
|
+
onEvent: (event) => {
|
|
20426
|
+
if (event.type === "thinking") {
|
|
20427
|
+
spinner.stop();
|
|
20428
|
+
console.log(" " + chalk17.dim.italic(event.text));
|
|
20429
|
+
spinner.start();
|
|
20430
|
+
spinner.text = "Working the plan\u2026";
|
|
20431
|
+
}
|
|
20432
|
+
}
|
|
20433
|
+
});
|
|
20434
|
+
spinner.stop();
|
|
20435
|
+
printCraftReplResult(ctx, result, objective);
|
|
20436
|
+
ctx.strategistState = void 0;
|
|
20437
|
+
saveSessionState(ctx);
|
|
20438
|
+
return result.status === "ready" ? `Strategy crafted: ${result.plan?.title}` : "Strategy craft unfinished";
|
|
20439
|
+
} catch (err) {
|
|
20440
|
+
spinner.fail("Strategy craft failed");
|
|
20441
|
+
console.error(" " + chalk17.red(String(err.message ?? err)));
|
|
20442
|
+
ctx.strategistState = void 0;
|
|
20443
|
+
saveSessionState(ctx);
|
|
20444
|
+
return;
|
|
20445
|
+
}
|
|
20446
|
+
}
|
|
18917
20447
|
let plan = null;
|
|
18918
20448
|
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
18919
20449
|
let baselineBatchId = null;
|
|
@@ -18997,7 +20527,10 @@ async function runStrategistSession(ctx) {
|
|
|
18997
20527
|
}
|
|
18998
20528
|
if (saved) {
|
|
18999
20529
|
try {
|
|
19000
|
-
const persisted = await persistStrategistPlan(plan, {
|
|
20530
|
+
const persisted = await persistStrategistPlan(plan, {
|
|
20531
|
+
baselineBatchId,
|
|
20532
|
+
constraintLine: formatConstraintLine(ctx.snapshot.computeResult?.aggregate)
|
|
20533
|
+
});
|
|
19001
20534
|
ctx.deliverables.push({
|
|
19002
20535
|
kind: "strategy",
|
|
19003
20536
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -19045,19 +20578,54 @@ async function ensureSnapshot(ctx) {
|
|
|
19045
20578
|
return null;
|
|
19046
20579
|
}
|
|
19047
20580
|
}
|
|
20581
|
+
function printCraftReplResult(ctx, result, objective) {
|
|
20582
|
+
if (result.plan) {
|
|
20583
|
+
printStrategyBrief(result.plan, {
|
|
20584
|
+
measurable_targets: result.measurable_targets,
|
|
20585
|
+
total_targets: result.total_targets
|
|
20586
|
+
});
|
|
20587
|
+
} else {
|
|
20588
|
+
console.log(" " + chalk17.dim("(No plan produced)"));
|
|
20589
|
+
}
|
|
20590
|
+
for (const notice of result.notices.slice(0, 8)) {
|
|
20591
|
+
if (!isCraftRoundNotice(notice)) console.log(" " + chalk17.dim(notice));
|
|
20592
|
+
}
|
|
20593
|
+
printLlmAttribution(result.usage);
|
|
20594
|
+
if (result.library_path) creditStrategySession(ctx);
|
|
20595
|
+
printCraftWrapUp({
|
|
20596
|
+
status: result.status,
|
|
20597
|
+
library_path: result.library_path,
|
|
20598
|
+
inbox_path: result.inbox_path,
|
|
20599
|
+
handoff_path: result.handoff_path,
|
|
20600
|
+
slug: result.slug,
|
|
20601
|
+
jobId: result.job?.id,
|
|
20602
|
+
oneShot: false
|
|
20603
|
+
});
|
|
20604
|
+
if (result.library_path || result.handoff_path) {
|
|
20605
|
+
recordMessage(ctx, "agent", `Strategy crafted: ${result.plan?.title ?? objective} (${result.status})`);
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
19048
20608
|
function printObjectiveCard(ctx, objective, proposed) {
|
|
20609
|
+
const craftMode = ctx.strategistState?.mode === "craft";
|
|
19049
20610
|
console.log();
|
|
19050
20611
|
console.log(" " + chalk17.bold("Strategy session"));
|
|
19051
20612
|
console.log(
|
|
19052
20613
|
" " + chalk17.dim(proposed ? "Proposed objective: " : "Objective: ") + paint("accent", objective)
|
|
19053
20614
|
);
|
|
19054
20615
|
console.log(
|
|
19055
|
-
" " + chalk17.dim(
|
|
20616
|
+
" " + chalk17.dim(
|
|
20617
|
+
craftMode ? "NTRP uses your live data. It keeps working until the plan is ready, then saves it and writes a handoff." : "NTRP uses your live data. It sequences the work and sets measured milestones. It then stress-tests the plan."
|
|
20618
|
+
)
|
|
19056
20619
|
);
|
|
19057
20620
|
console.log();
|
|
19058
20621
|
console.log(
|
|
19059
20622
|
" " + chalk17.dim("Confirm? ") + chalk17.cyan("\u23CE yes") + chalk17.dim(" \xB7 ") + chalk17.cyan("b back") + chalk17.dim(" \xB7 ") + chalk17.cyan("adjust") + chalk17.dim(" \xB7 ") + chalk17.cyan("cancel")
|
|
19060
20623
|
);
|
|
20624
|
+
if (!craftMode) {
|
|
20625
|
+
console.log(
|
|
20626
|
+
" " + chalk17.dim("or type ") + chalk17.cyan("keep going") + chalk17.dim(" to keep working until the plan is ready")
|
|
20627
|
+
);
|
|
20628
|
+
}
|
|
19061
20629
|
console.log();
|
|
19062
20630
|
}
|
|
19063
20631
|
async function printKeylessSkeletonPlan(ctx, objective) {
|
|
@@ -19119,7 +20687,7 @@ async function resumeStrategistAfterConnect(ctx) {
|
|
|
19119
20687
|
printObjectiveCard(ctx, state2.objective, true);
|
|
19120
20688
|
return true;
|
|
19121
20689
|
}
|
|
19122
|
-
var STRATEGIST_INTENT_RE, CANCEL_RE, CONFIRM_RE, ADJUST_RE, QUESTION_RE;
|
|
20690
|
+
var STRATEGIST_INTENT_RE, CANCEL_RE, CONFIRM_RE, ADJUST_RE, QUESTION_RE, CRAFT_RE;
|
|
19123
20691
|
var init_strategist_flow = __esm({
|
|
19124
20692
|
"src/conversation/strategist-flow.ts"() {
|
|
19125
20693
|
"use strict";
|
|
@@ -19133,15 +20701,18 @@ var init_strategist_flow = __esm({
|
|
|
19133
20701
|
init_divergence();
|
|
19134
20702
|
init_strategist();
|
|
19135
20703
|
init_strategist2();
|
|
20704
|
+
init_strategist_run();
|
|
19136
20705
|
init_strategy_brief();
|
|
19137
20706
|
init_llm_attribution();
|
|
19138
20707
|
init_time_bank();
|
|
19139
20708
|
init_formatters();
|
|
20709
|
+
init_store3();
|
|
19140
20710
|
STRATEGIST_INTENT_RE = /\b(strateg(y|ize|ic)|game\s?plan|battle\s?plan|roadmap|(build|draft|make|create|put together)\s+(me\s+)?(a\s+|the\s+)?plan\b|plan\s+(to|for)\s+(fix|improv|reduc|recover|hit|reach|get|grow|turn)|how\s+(should|do|can)\s+we\s+(fix|approach|tackle|attack|prioritize|sequence|turn\s+(this|it)\s+around)|what\s+should\s+we\s+(do|fix|tackle|prioritize|focus\s+on)\s+(first|next)|what\s+order\s+should|where\s+(do|should)\s+we\s+start)\b/i;
|
|
19141
20711
|
CANCEL_RE = /^(cancel|stop|quit|abort|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
|
|
19142
20712
|
CONFIRM_RE = /^(y|yes|yep|yeah|confirm|go|go ahead|do it|proceed|sounds good|looks good|lgtm|ok|okay)\b/i;
|
|
19143
20713
|
ADJUST_RE = /^(n|no|adjust|change|edit|different|not quite|refine)\b/i;
|
|
19144
20714
|
QUESTION_RE = /(\?\s*$)|^(what|why|how|when|which|where|who)\b/i;
|
|
20715
|
+
CRAFT_RE = /^(craft|keep going|until tight|keep working)\b/i;
|
|
19145
20716
|
}
|
|
19146
20717
|
});
|
|
19147
20718
|
|
|
@@ -19252,7 +20823,9 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
|
|
|
19252
20823
|
);
|
|
19253
20824
|
} else if (phase === "orient") {
|
|
19254
20825
|
lines.push("Already done: nothing locked yet");
|
|
19255
|
-
lines.push(
|
|
20826
|
+
lines.push(
|
|
20827
|
+
"Available now: help the user name a focus; CLI will propose scope from their words. If \u23CE /update is armed, the CLI owns the install confirm \u2014 do not re-ask about updating"
|
|
20828
|
+
);
|
|
19256
20829
|
} else if (phase === "think") {
|
|
19257
20830
|
lines.push("Already done: analysis complete; think channel open");
|
|
19258
20831
|
lines.push("Available now: socratic exploration; draft_strategy / draft_handoff when ready to graduate");
|
|
@@ -20169,8 +21742,8 @@ var init_bundle = __esm({
|
|
|
20169
21742
|
});
|
|
20170
21743
|
|
|
20171
21744
|
// src/repositories/markdown.ts
|
|
20172
|
-
import { mkdirSync as
|
|
20173
|
-
import { basename as basename5, dirname as
|
|
21745
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
21746
|
+
import { basename as basename5, dirname as dirname5, join as join25, resolve as resolve8 } from "path";
|
|
20174
21747
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
20175
21748
|
function renderMarkdownFiles(pkg) {
|
|
20176
21749
|
const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
|
|
@@ -20390,12 +21963,12 @@ var init_markdown2 = __esm({
|
|
|
20390
21963
|
write(pkg) {
|
|
20391
21964
|
const root = getRootPath(pkg.target);
|
|
20392
21965
|
const files = renderMarkdownFiles(pkg);
|
|
20393
|
-
|
|
21966
|
+
mkdirSync13(root, { recursive: true });
|
|
20394
21967
|
const written = [];
|
|
20395
21968
|
for (const file of files) {
|
|
20396
|
-
const absolutePath =
|
|
20397
|
-
|
|
20398
|
-
|
|
21969
|
+
const absolutePath = join25(root, file.relativePath);
|
|
21970
|
+
mkdirSync13(dirname5(absolutePath), { recursive: true });
|
|
21971
|
+
writeFileSync16(absolutePath, file.contents, "utf-8");
|
|
20399
21972
|
written.push(absolutePath);
|
|
20400
21973
|
}
|
|
20401
21974
|
return {
|
|
@@ -20498,7 +22071,7 @@ var init_publish = __esm({
|
|
|
20498
22071
|
});
|
|
20499
22072
|
|
|
20500
22073
|
// src/services/smoke-protocol.ts
|
|
20501
|
-
import { join as
|
|
22074
|
+
import { join as join26 } from "path";
|
|
20502
22075
|
function isSmokeProtocolTrigger(input) {
|
|
20503
22076
|
return normalize(input).includes(SMOKE_TRIGGER_PHRASE);
|
|
20504
22077
|
}
|
|
@@ -20534,7 +22107,7 @@ async function runSmokeProtocol(_input, ctx) {
|
|
|
20534
22107
|
});
|
|
20535
22108
|
const proposalResult = await proposeRepositoryExport({
|
|
20536
22109
|
target: "markdown",
|
|
20537
|
-
directory:
|
|
22110
|
+
directory: join26(getExportsDir(), "repository-smoke"),
|
|
20538
22111
|
source: "smoke_protocol",
|
|
20539
22112
|
modelOrFixture: "smoke-protocol-v1"
|
|
20540
22113
|
});
|
|
@@ -21274,7 +22847,7 @@ var init_scenario_fit = __esm({
|
|
|
21274
22847
|
});
|
|
21275
22848
|
|
|
21276
22849
|
// src/conversation/onboard-tiers.ts
|
|
21277
|
-
import { existsSync as
|
|
22850
|
+
import { existsSync as existsSync25, statSync as statSync4 } from "fs";
|
|
21278
22851
|
function flagSet(tier) {
|
|
21279
22852
|
return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
|
|
21280
22853
|
}
|
|
@@ -23897,18 +25470,18 @@ var init_generator = __esm({
|
|
|
23897
25470
|
});
|
|
23898
25471
|
|
|
23899
25472
|
// src/demo/taxonomy-cache.ts
|
|
23900
|
-
import { readFileSync as
|
|
25473
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync26, mkdirSync as mkdirSync14, unlinkSync as unlinkSync4 } from "fs";
|
|
23901
25474
|
import { homedir as homedir7 } from "os";
|
|
23902
|
-
import { join as
|
|
25475
|
+
import { join as join27 } from "path";
|
|
23903
25476
|
function ensureDir6() {
|
|
23904
|
-
if (!
|
|
23905
|
-
|
|
25477
|
+
if (!existsSync26(NTRP_DIR4)) {
|
|
25478
|
+
mkdirSync14(NTRP_DIR4, { recursive: true });
|
|
23906
25479
|
}
|
|
23907
25480
|
}
|
|
23908
25481
|
function loadCachedTaxonomy(profile) {
|
|
23909
|
-
if (!
|
|
25482
|
+
if (!existsSync26(TAXONOMY_PATH)) return null;
|
|
23910
25483
|
try {
|
|
23911
|
-
const parsed = JSON.parse(
|
|
25484
|
+
const parsed = JSON.parse(readFileSync22(TAXONOMY_PATH, "utf-8"));
|
|
23912
25485
|
if (!parsed || typeof parsed !== "object") return null;
|
|
23913
25486
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
23914
25487
|
return parsed;
|
|
@@ -23918,14 +25491,14 @@ function loadCachedTaxonomy(profile) {
|
|
|
23918
25491
|
}
|
|
23919
25492
|
function saveCachedTaxonomy(taxonomy) {
|
|
23920
25493
|
ensureDir6();
|
|
23921
|
-
|
|
25494
|
+
writeFileSync17(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
23922
25495
|
}
|
|
23923
25496
|
var NTRP_DIR4, TAXONOMY_PATH;
|
|
23924
25497
|
var init_taxonomy_cache = __esm({
|
|
23925
25498
|
"src/demo/taxonomy-cache.ts"() {
|
|
23926
25499
|
"use strict";
|
|
23927
|
-
NTRP_DIR4 =
|
|
23928
|
-
TAXONOMY_PATH =
|
|
25500
|
+
NTRP_DIR4 = join27(homedir7(), ".ntrp");
|
|
25501
|
+
TAXONOMY_PATH = join27(NTRP_DIR4, "demo-taxonomy.json");
|
|
23929
25502
|
}
|
|
23930
25503
|
});
|
|
23931
25504
|
|
|
@@ -24383,7 +25956,7 @@ __export(inbox_setup_exports, {
|
|
|
24383
25956
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
24384
25957
|
});
|
|
24385
25958
|
import chalk27 from "chalk";
|
|
24386
|
-
import { existsSync as
|
|
25959
|
+
import { existsSync as existsSync27 } from "fs";
|
|
24387
25960
|
function markDemoOffered() {
|
|
24388
25961
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
24389
25962
|
}
|
|
@@ -24415,7 +25988,7 @@ function printSkipHint(beat) {
|
|
|
24415
25988
|
async function reuseInboxFolderIfPresent(session, beat, folderPath) {
|
|
24416
25989
|
if (getAiInboxDir()) return false;
|
|
24417
25990
|
const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
|
|
24418
|
-
const existing = candidates.find((p) =>
|
|
25991
|
+
const existing = candidates.find((p) => existsSync27(p));
|
|
24419
25992
|
if (!existing) return false;
|
|
24420
25993
|
console.log(" " + chalk27.dim("Pickup folder still on disk: ") + existing);
|
|
24421
25994
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
@@ -24521,7 +26094,7 @@ __export(ingest_exports, {
|
|
|
24521
26094
|
handler: () => handler3
|
|
24522
26095
|
});
|
|
24523
26096
|
import chalk28 from "chalk";
|
|
24524
|
-
import { readFileSync as
|
|
26097
|
+
import { readFileSync as readFileSync23, existsSync as existsSync28 } from "fs";
|
|
24525
26098
|
import { basename as basename6 } from "path";
|
|
24526
26099
|
async function handler3(args, ctx) {
|
|
24527
26100
|
const { positional, flags } = parseArgs(args, [
|
|
@@ -24545,7 +26118,7 @@ async function handler3(args, ctx) {
|
|
|
24545
26118
|
console.error(chalk28.dim(" /ingest --demo [--scenario <name>]"));
|
|
24546
26119
|
process.exit(1);
|
|
24547
26120
|
}
|
|
24548
|
-
if (!
|
|
26121
|
+
if (!existsSync28(file)) {
|
|
24549
26122
|
console.error(chalk28.red(` File not found: ${file}`));
|
|
24550
26123
|
process.exit(1);
|
|
24551
26124
|
}
|
|
@@ -24563,7 +26136,7 @@ async function handler3(args, ctx) {
|
|
|
24563
26136
|
try {
|
|
24564
26137
|
await initSchema();
|
|
24565
26138
|
spinner.text = "Parsing CSV\u2026";
|
|
24566
|
-
const content =
|
|
26139
|
+
const content = readFileSync23(file, "utf-8");
|
|
24567
26140
|
const { rows, headers } = parseCSV(content);
|
|
24568
26141
|
if (rows.length === 0) {
|
|
24569
26142
|
spinner.fail("CSV is empty");
|
|
@@ -24933,8 +26506,8 @@ __export(ingest_chat_exports, {
|
|
|
24933
26506
|
loadDemoFromChat: () => loadDemoFromChat,
|
|
24934
26507
|
looksLikeFilePath: () => looksLikeFilePath
|
|
24935
26508
|
});
|
|
24936
|
-
import { existsSync as
|
|
24937
|
-
import { basename as basename7, join as
|
|
26509
|
+
import { existsSync as existsSync29, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
|
|
26510
|
+
import { basename as basename7, join as join28, resolve as resolve9 } from "path";
|
|
24938
26511
|
import { homedir as homedir8 } from "os";
|
|
24939
26512
|
import chalk30 from "chalk";
|
|
24940
26513
|
function extractFilePath(input) {
|
|
@@ -24956,7 +26529,7 @@ function extractFilePath(input) {
|
|
|
24956
26529
|
if (!candidate) continue;
|
|
24957
26530
|
if (!looksLikePathToken(candidate)) continue;
|
|
24958
26531
|
const p = expandPath(candidate);
|
|
24959
|
-
if (
|
|
26532
|
+
if (existsSync29(p)) {
|
|
24960
26533
|
try {
|
|
24961
26534
|
const st = statSync5(p);
|
|
24962
26535
|
if (st.isFile() || st.isDirectory()) return p;
|
|
@@ -24985,7 +26558,7 @@ function looksLikeFilePath(input) {
|
|
|
24985
26558
|
function listCsvsInFolder(dir) {
|
|
24986
26559
|
try {
|
|
24987
26560
|
if (!statSync5(dir).isDirectory()) return [];
|
|
24988
|
-
return readdirSync6(dir).filter((name) => name.toLowerCase().endsWith(".csv")).map((name) =>
|
|
26561
|
+
return readdirSync6(dir).filter((name) => name.toLowerCase().endsWith(".csv")).map((name) => join28(dir, name)).sort();
|
|
24989
26562
|
} catch {
|
|
24990
26563
|
return [];
|
|
24991
26564
|
}
|
|
@@ -25067,12 +26640,12 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
25067
26640
|
}
|
|
25068
26641
|
const { handler: ingest } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
|
|
25069
26642
|
const { detectEntityType: detectEntityType2 } = await Promise.resolve().then(() => (init_csv_detect(), csv_detect_exports));
|
|
25070
|
-
const { readFileSync:
|
|
26643
|
+
const { readFileSync: readFileSync25 } = await import("fs");
|
|
25071
26644
|
const { parseCSV: parseCSV2 } = await Promise.resolve().then(() => (init_csv_parse(), csv_parse_exports));
|
|
25072
26645
|
const { getStoredApiKey } = await Promise.resolve().then(() => (init_repl_api(), repl_api_exports));
|
|
25073
26646
|
let headerCheckFailed = false;
|
|
25074
26647
|
try {
|
|
25075
|
-
const raw =
|
|
26648
|
+
const raw = readFileSync25(filePath, "utf-8");
|
|
25076
26649
|
const { headers } = parseCSV2(raw);
|
|
25077
26650
|
const detected = detectEntityType2(headers, "unknown");
|
|
25078
26651
|
if (!detected) headerCheckFailed = true;
|
|
@@ -25985,9 +27558,9 @@ async function handleGetSessionBrief(input) {
|
|
|
25985
27558
|
if (!target) {
|
|
25986
27559
|
return { error: `No session matching "${raw}".` };
|
|
25987
27560
|
}
|
|
25988
|
-
const { existsSync:
|
|
27561
|
+
const { existsSync: existsSync31, readFileSync: readFileSync25 } = await import("fs");
|
|
25989
27562
|
const briefPath = contextDocPathForSession2(target.id);
|
|
25990
|
-
if (!
|
|
27563
|
+
if (!existsSync31(briefPath)) {
|
|
25991
27564
|
return {
|
|
25992
27565
|
session_id: target.id,
|
|
25993
27566
|
error: "No context brief on disk for this session (created before brief storage existed).",
|
|
@@ -25998,7 +27571,7 @@ async function handleGetSessionBrief(input) {
|
|
|
25998
27571
|
return {
|
|
25999
27572
|
session_id: target.id,
|
|
26000
27573
|
security_notice: UNTRUSTED_CONTENT_NOTICE,
|
|
26001
|
-
brief: wrapUntrustedContent(
|
|
27574
|
+
brief: wrapUntrustedContent(readFileSync25(briefPath, "utf-8"))
|
|
26002
27575
|
};
|
|
26003
27576
|
}
|
|
26004
27577
|
function auditDenied(name, input, resultJson, start) {
|
|
@@ -27002,15 +28575,15 @@ var init_ask = __esm({
|
|
|
27002
28575
|
});
|
|
27003
28576
|
|
|
27004
28577
|
// src/services/setup.ts
|
|
27005
|
-
import { existsSync as
|
|
27006
|
-
import { join as
|
|
28578
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "fs";
|
|
28579
|
+
import { join as join29 } from "path";
|
|
27007
28580
|
function setupCheck() {
|
|
27008
28581
|
const home = ntrpHome();
|
|
27009
28582
|
let writable = false;
|
|
27010
28583
|
try {
|
|
27011
|
-
|
|
27012
|
-
const probe =
|
|
27013
|
-
|
|
28584
|
+
mkdirSync15(home, { recursive: true });
|
|
28585
|
+
const probe = join29(home, ".write-check");
|
|
28586
|
+
writeFileSync18(probe, "ok\n");
|
|
27014
28587
|
writable = true;
|
|
27015
28588
|
} catch {
|
|
27016
28589
|
writable = false;
|
|
@@ -27060,42 +28633,6 @@ var init_setup = __esm({
|
|
|
27060
28633
|
}
|
|
27061
28634
|
});
|
|
27062
28635
|
|
|
27063
|
-
// src/version.ts
|
|
27064
|
-
import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
|
|
27065
|
-
import { dirname as dirname5, join as join27 } from "path";
|
|
27066
|
-
import { fileURLToPath } from "url";
|
|
27067
|
-
function readVersionFromPackageJson(packageJsonPath) {
|
|
27068
|
-
if (!existsSync28(packageJsonPath)) return null;
|
|
27069
|
-
try {
|
|
27070
|
-
const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
|
|
27071
|
-
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
27072
|
-
} catch {
|
|
27073
|
-
}
|
|
27074
|
-
return null;
|
|
27075
|
-
}
|
|
27076
|
-
function readVersionNearEntry(entryPath) {
|
|
27077
|
-
const start = dirname5(entryPath);
|
|
27078
|
-
for (const rel of [join27(start, "..", "package.json"), join27(start, "../..", "package.json")]) {
|
|
27079
|
-
const version = readVersionFromPackageJson(rel);
|
|
27080
|
-
if (version) return version;
|
|
27081
|
-
}
|
|
27082
|
-
return null;
|
|
27083
|
-
}
|
|
27084
|
-
function readInstalledVersionFromDisk() {
|
|
27085
|
-
return readVersionNearEntry(fileURLToPath(import.meta.url));
|
|
27086
|
-
}
|
|
27087
|
-
function getInstalledVersion() {
|
|
27088
|
-
if (cachedVersion) return cachedVersion;
|
|
27089
|
-
cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
|
|
27090
|
-
return cachedVersion;
|
|
27091
|
-
}
|
|
27092
|
-
var cachedVersion;
|
|
27093
|
-
var init_version = __esm({
|
|
27094
|
-
"src/version.ts"() {
|
|
27095
|
-
"use strict";
|
|
27096
|
-
}
|
|
27097
|
-
});
|
|
27098
|
-
|
|
27099
28636
|
// src/mcp/server.ts
|
|
27100
28637
|
init_context2();
|
|
27101
28638
|
init_diagnosis();
|