@sonnechasser/ntrp 1.8.1 → 2.1.9
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/README.md +63 -0
- package/dist/index.js +1238 -705
- package/dist/mcp/server.js +7387 -6799
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -288,6 +288,56 @@ var init_formatters = __esm({
|
|
|
288
288
|
}
|
|
289
289
|
});
|
|
290
290
|
|
|
291
|
+
// src/io/diagnostics.ts
|
|
292
|
+
import { renameSync } from "fs";
|
|
293
|
+
function isDiagnosticsEnabled() {
|
|
294
|
+
const raw = process.env.NTRP_DEBUG;
|
|
295
|
+
if (!raw) return false;
|
|
296
|
+
return raw !== "0" && raw.toLowerCase() !== "false";
|
|
297
|
+
}
|
|
298
|
+
function describeError(err) {
|
|
299
|
+
if (!(err instanceof Error)) return String(err);
|
|
300
|
+
const parts = [err.message || err.name];
|
|
301
|
+
let cause = err.cause;
|
|
302
|
+
const seen = /* @__PURE__ */ new Set([err]);
|
|
303
|
+
while (cause && !seen.has(cause)) {
|
|
304
|
+
seen.add(cause);
|
|
305
|
+
parts.push(cause instanceof Error ? cause.message || cause.name : String(cause));
|
|
306
|
+
cause = cause instanceof Error ? cause.cause : void 0;
|
|
307
|
+
}
|
|
308
|
+
return parts.join(": ");
|
|
309
|
+
}
|
|
310
|
+
function debugError(scope, err, note) {
|
|
311
|
+
if (!isDiagnosticsEnabled()) return;
|
|
312
|
+
const detail = describeError(err);
|
|
313
|
+
console.error(` [ntrp:debug] ${scope}${note ? ` (${note})` : ""}: ${detail}`);
|
|
314
|
+
if (err instanceof Error && err.stack) {
|
|
315
|
+
console.error(err.stack.split("\n").slice(1).join("\n"));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
function warnOnce(key, message) {
|
|
319
|
+
if (warnedKeys.has(key)) return;
|
|
320
|
+
warnedKeys.add(key);
|
|
321
|
+
console.error(` ${message}`);
|
|
322
|
+
}
|
|
323
|
+
function quarantineCorruptFile(path, scope) {
|
|
324
|
+
const target = `${path}.corrupt-${Date.now()}`;
|
|
325
|
+
try {
|
|
326
|
+
renameSync(path, target);
|
|
327
|
+
return target;
|
|
328
|
+
} catch (err) {
|
|
329
|
+
debugError(scope, err, `could not quarantine ${path}`);
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
var warnedKeys;
|
|
334
|
+
var init_diagnostics = __esm({
|
|
335
|
+
"src/io/diagnostics.ts"() {
|
|
336
|
+
"use strict";
|
|
337
|
+
warnedKeys = /* @__PURE__ */ new Set();
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
291
341
|
// src/config/store.ts
|
|
292
342
|
var store_exports = {};
|
|
293
343
|
__export(store_exports, {
|
|
@@ -318,7 +368,8 @@ function ntrpHome() {
|
|
|
318
368
|
function chmodQuiet(path, mode) {
|
|
319
369
|
try {
|
|
320
370
|
chmodSync(path, mode);
|
|
321
|
-
} catch {
|
|
371
|
+
} catch (err) {
|
|
372
|
+
debugError("config.chmod", err, path);
|
|
322
373
|
}
|
|
323
374
|
}
|
|
324
375
|
function ensureDir() {
|
|
@@ -345,7 +396,13 @@ function loadConfig() {
|
|
|
345
396
|
}
|
|
346
397
|
try {
|
|
347
398
|
cachedConfig = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
348
|
-
} catch {
|
|
399
|
+
} catch (err) {
|
|
400
|
+
debugError("config.load", err, CONFIG_PATH);
|
|
401
|
+
const quarantined = quarantineCorruptFile(CONFIG_PATH, "config.load");
|
|
402
|
+
warnOnce(
|
|
403
|
+
"config-unreadable",
|
|
404
|
+
`Could not read ${CONFIG_PATH} (${describeError(err)}).` + (quarantined ? ` Kept a copy at ${quarantined}; starting from an empty config.` : " Starting from an empty config \u2014 the file will be overwritten on the next save.")
|
|
405
|
+
);
|
|
349
406
|
cachedConfig = {};
|
|
350
407
|
}
|
|
351
408
|
return cachedConfig;
|
|
@@ -490,6 +547,7 @@ var NTRP_DIR, CONFIG_PATH, cachedConfig;
|
|
|
490
547
|
var init_store = __esm({
|
|
491
548
|
"src/config/store.ts"() {
|
|
492
549
|
"use strict";
|
|
550
|
+
init_diagnostics();
|
|
493
551
|
NTRP_DIR = process.env.NTRP_HOME ? resolve(process.env.NTRP_HOME) : join(homedir(), ".ntrp");
|
|
494
552
|
CONFIG_PATH = join(NTRP_DIR, "config.json");
|
|
495
553
|
cachedConfig = null;
|
|
@@ -500,14 +558,23 @@ var init_store = __esm({
|
|
|
500
558
|
function stripAnsi(value) {
|
|
501
559
|
return value.replace(ANSI_ANY, "").replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "");
|
|
502
560
|
}
|
|
561
|
+
function mask(value) {
|
|
562
|
+
return `${value.slice(0, 6)}\u2026[redacted]`;
|
|
563
|
+
}
|
|
564
|
+
function looksLikeSecretValue(value) {
|
|
565
|
+
return /\d/.test(value) || value.length >= 20;
|
|
566
|
+
}
|
|
503
567
|
function redactSecrets(line) {
|
|
504
|
-
let out = line
|
|
568
|
+
let out = line.replace(
|
|
569
|
+
SECRET_ARGUMENT_RE,
|
|
570
|
+
(m, prefix, value) => looksLikeSecretValue(value) ? `${prefix}${mask(value)}` : m
|
|
571
|
+
);
|
|
505
572
|
for (const pattern of SECRET_PATTERNS) {
|
|
506
|
-
out = out.replace(pattern,
|
|
573
|
+
out = out.replace(pattern, mask);
|
|
507
574
|
}
|
|
508
575
|
return out;
|
|
509
576
|
}
|
|
510
|
-
var MAX_LINES_DEFAULT, DROP_CHUNK, ANSI_ANY, SECRET_PATTERNS, SCREEN_CLEAR_MARKER, TerminalCapture;
|
|
577
|
+
var MAX_LINES_DEFAULT, DROP_CHUNK, ANSI_ANY, SECRET_PATTERNS, SECRET_ARGUMENT_RE, SCREEN_CLEAR_MARKER, TerminalCapture;
|
|
511
578
|
var init_terminal_capture = __esm({
|
|
512
579
|
"src/services/terminal-capture.ts"() {
|
|
513
580
|
"use strict";
|
|
@@ -532,9 +599,24 @@ var init_terminal_capture = __esm({
|
|
|
532
599
|
// Fireworks
|
|
533
600
|
/\bAIza[A-Za-z0-9_-]{10,}/g,
|
|
534
601
|
// Google
|
|
602
|
+
/\btvly-[A-Za-z0-9_-]{8,}/g,
|
|
603
|
+
// Tavily web retrieval
|
|
604
|
+
/\bBSA[A-Za-z0-9_-]{20,}/g,
|
|
605
|
+
// Brave Search subscription token
|
|
606
|
+
/\bhf_[A-Za-z0-9]{16,}/g,
|
|
607
|
+
// Hugging Face
|
|
608
|
+
/\bnvapi-[A-Za-z0-9_-]{8,}/g,
|
|
609
|
+
// NVIDIA NIM
|
|
610
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/g,
|
|
611
|
+
// GitHub fine-grained token
|
|
612
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}/g,
|
|
613
|
+
// GitHub classic tokens
|
|
614
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
615
|
+
// AWS access key id
|
|
535
616
|
/\bNTRP-[A-Z0-9][A-Z0-9-]{8,}/g
|
|
536
|
-
// license keys
|
|
617
|
+
// dev/CI license keys
|
|
537
618
|
];
|
|
619
|
+
SECRET_ARGUMENT_RE = /((?:--(?:key|llm-key|license-key|api-key|token)|\bactivate|\bapi[\s_-]?key|\blicense[\s_-]?key|\bBearer|\bNTRP_SIGNING_SECRET)(?:\s*[=:]\s*|\s+))(\S{8,})/gi;
|
|
538
620
|
SCREEN_CLEAR_MARKER = "\u2500\u2500 screen cleared \u2500\u2500";
|
|
539
621
|
TerminalCapture = class {
|
|
540
622
|
constructor(maxLines = MAX_LINES_DEFAULT) {
|
|
@@ -1228,7 +1310,7 @@ import {
|
|
|
1228
1310
|
mkdirSync as mkdirSync4,
|
|
1229
1311
|
readFileSync as readFileSync2,
|
|
1230
1312
|
readdirSync,
|
|
1231
|
-
renameSync,
|
|
1313
|
+
renameSync as renameSync2,
|
|
1232
1314
|
rmSync,
|
|
1233
1315
|
statSync,
|
|
1234
1316
|
writeFileSync as writeFileSync3
|
|
@@ -1302,7 +1384,8 @@ function readManifestEvents(root = getExportsDir()) {
|
|
|
1302
1384
|
if (!trimmed) continue;
|
|
1303
1385
|
try {
|
|
1304
1386
|
events.push(JSON.parse(trimmed));
|
|
1305
|
-
} catch {
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
debugError("exports.manifest.read", err, "skipped corrupt line");
|
|
1306
1389
|
}
|
|
1307
1390
|
}
|
|
1308
1391
|
return events;
|
|
@@ -1491,7 +1574,8 @@ function pruneInboxArchive(archiveDir, keep = INBOX_ARCHIVE_KEEP) {
|
|
|
1491
1574
|
const p = join3(archiveDir, name);
|
|
1492
1575
|
try {
|
|
1493
1576
|
return { name, path: p, mtime: statSync(p).mtimeMs };
|
|
1494
|
-
} catch {
|
|
1577
|
+
} catch (err) {
|
|
1578
|
+
debugError("exports.pruneArchive.stat", err, p);
|
|
1495
1579
|
return null;
|
|
1496
1580
|
}
|
|
1497
1581
|
}).filter((e) => e != null).sort((a, b) => b.mtime - a.mtime);
|
|
@@ -1582,7 +1666,7 @@ function moveExport(idOrPath, destDir) {
|
|
|
1582
1666
|
if (existsSync3(destPath)) {
|
|
1583
1667
|
destPath = join3(destRoot, `${exportStamp()}-${name}`);
|
|
1584
1668
|
}
|
|
1585
|
-
|
|
1669
|
+
renameSync2(item.path, destPath);
|
|
1586
1670
|
const previous = [...item.previous_paths ?? [], item.path];
|
|
1587
1671
|
updateArchiveLatest(item.kind, destPath, ensureExportsLayout());
|
|
1588
1672
|
const event = {
|
|
@@ -1636,6 +1720,7 @@ var init_exports_registry = __esm({
|
|
|
1636
1720
|
init_path_safety();
|
|
1637
1721
|
init_export_kinds();
|
|
1638
1722
|
init_handoff_skill();
|
|
1723
|
+
init_diagnostics();
|
|
1639
1724
|
init_export_kinds();
|
|
1640
1725
|
KIND_DIRS = ["handoffs", "reports", "notes", "csv", "publish"];
|
|
1641
1726
|
INBOX_ARCHIVE_KEEP = 20;
|
|
@@ -1810,7 +1895,8 @@ function buildSessionContextDoc(file, opts = {}) {
|
|
|
1810
1895
|
} else {
|
|
1811
1896
|
lines.push("- AI inbox: unset \u2014 `/inbox set <folder>` then paste `/inbox skill` into your agent");
|
|
1812
1897
|
}
|
|
1813
|
-
} catch {
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
debugError("contextDoc.exportCatalog", err);
|
|
1814
1900
|
lines.push("- Export catalog unavailable.");
|
|
1815
1901
|
}
|
|
1816
1902
|
lines.push("");
|
|
@@ -1857,13 +1943,15 @@ function writeSessionContextDoc(ctx) {
|
|
|
1857
1943
|
const file = buildSessionFileSnapshot(ctx);
|
|
1858
1944
|
const doc = buildSessionContextDoc(file, { snapshot: ctx.snapshot.computeResult });
|
|
1859
1945
|
writeFileSync4(contextDocPathForSession(ctx.sessionId), doc);
|
|
1860
|
-
} catch {
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
debugError("contextDoc.write", err, ctx.sessionId);
|
|
1861
1948
|
}
|
|
1862
1949
|
}
|
|
1863
1950
|
function writeContextDocForSessionFile(file, opts = {}) {
|
|
1864
1951
|
try {
|
|
1865
1952
|
writeFileSync4(contextDocPathForSession(file.id), buildSessionContextDoc(file, opts));
|
|
1866
|
-
} catch {
|
|
1953
|
+
} catch (err) {
|
|
1954
|
+
debugError("contextDoc.writeForFile", err, file.id);
|
|
1867
1955
|
}
|
|
1868
1956
|
}
|
|
1869
1957
|
var AGENT_EXCERPT_CHARS;
|
|
@@ -1875,6 +1963,7 @@ var init_context_doc = __esm({
|
|
|
1875
1963
|
init_store();
|
|
1876
1964
|
init_exports_registry();
|
|
1877
1965
|
init_terminal_capture();
|
|
1966
|
+
init_diagnostics();
|
|
1878
1967
|
AGENT_EXCERPT_CHARS = 400;
|
|
1879
1968
|
}
|
|
1880
1969
|
});
|
|
@@ -1923,7 +2012,8 @@ function discardSessionTranscript(sessionId) {
|
|
|
1923
2012
|
}
|
|
1924
2013
|
try {
|
|
1925
2014
|
rmSync2(transcriptPathForSession(sessionId), { force: true });
|
|
1926
|
-
} catch {
|
|
2015
|
+
} catch (err) {
|
|
2016
|
+
debugError("transcript.discard", err, sessionId);
|
|
1927
2017
|
}
|
|
1928
2018
|
}
|
|
1929
2019
|
function pauseTranscriptCapture() {
|
|
@@ -1955,7 +2045,8 @@ function installTees() {
|
|
|
1955
2045
|
state.capture.feed(text);
|
|
1956
2046
|
scheduleFlush();
|
|
1957
2047
|
}
|
|
1958
|
-
} catch {
|
|
2048
|
+
} catch (err) {
|
|
2049
|
+
debugError("transcript.capture", err);
|
|
1959
2050
|
}
|
|
1960
2051
|
return original(chunk, encoding, callback);
|
|
1961
2052
|
})
|
|
@@ -1979,7 +2070,8 @@ function createState(sessionId) {
|
|
|
1979
2070
|
if (existsSync4(filePath)) {
|
|
1980
2071
|
try {
|
|
1981
2072
|
base = readFileSync3(filePath, "utf-8").trimEnd() + "\n";
|
|
1982
|
-
} catch {
|
|
2073
|
+
} catch (err) {
|
|
2074
|
+
debugError("transcript.readExisting", err, filePath);
|
|
1983
2075
|
base = "";
|
|
1984
2076
|
}
|
|
1985
2077
|
}
|
|
@@ -2039,7 +2131,8 @@ function flushNow(closedNote) {
|
|
|
2039
2131
|
try {
|
|
2040
2132
|
getSessionsDir();
|
|
2041
2133
|
writeFileSync5(s.filePath, render(s, closedNote));
|
|
2042
|
-
} catch {
|
|
2134
|
+
} catch (err) {
|
|
2135
|
+
debugError("transcript.flush", err, s.filePath);
|
|
2043
2136
|
}
|
|
2044
2137
|
}
|
|
2045
2138
|
function scheduleFlush() {
|
|
@@ -2073,6 +2166,7 @@ var init_transcript = __esm({
|
|
|
2073
2166
|
"use strict";
|
|
2074
2167
|
init_context2();
|
|
2075
2168
|
init_terminal_capture();
|
|
2169
|
+
init_diagnostics();
|
|
2076
2170
|
FLUSH_THROTTLE_MS = 250;
|
|
2077
2171
|
FLUSH_MAX_STALENESS_MS = 900;
|
|
2078
2172
|
state = null;
|
|
@@ -2172,7 +2266,7 @@ async function discardConnection() {
|
|
|
2172
2266
|
db = null;
|
|
2173
2267
|
lastHealthCheckMs = 0;
|
|
2174
2268
|
if (currentConn) {
|
|
2175
|
-
await closeConnection(currentConn).catch(() =>
|
|
2269
|
+
await closeConnection(currentConn).catch((err) => debugError("db.discard.closeConnection", err));
|
|
2176
2270
|
}
|
|
2177
2271
|
if (currentDb) {
|
|
2178
2272
|
await new Promise((resolve10) => {
|
|
@@ -2248,6 +2342,7 @@ var init_connection = __esm({
|
|
|
2248
2342
|
"src/db/connection.ts"() {
|
|
2249
2343
|
"use strict";
|
|
2250
2344
|
init_store();
|
|
2345
|
+
init_diagnostics();
|
|
2251
2346
|
NTRP_DIR2 = process.env.NTRP_HOME ? resolve4(process.env.NTRP_HOME) : join5(process.env.HOME ?? "", ".ntrp");
|
|
2252
2347
|
DEFAULT_DB_PATH = process.env.NTRP_DB_PATH ? resolve4(process.env.NTRP_DB_PATH) : join5(NTRP_DIR2, "ntrp.duckdb");
|
|
2253
2348
|
DB_PATH_PINNED = !!process.env.NTRP_DB_PATH;
|
|
@@ -2344,7 +2439,7 @@ async function inTransaction(fn) {
|
|
|
2344
2439
|
await run("COMMIT");
|
|
2345
2440
|
return result;
|
|
2346
2441
|
} catch (err) {
|
|
2347
|
-
await run("ROLLBACK").catch(() =>
|
|
2442
|
+
await run("ROLLBACK").catch((rollbackErr) => debugError("db.rollback", rollbackErr));
|
|
2348
2443
|
throw err;
|
|
2349
2444
|
}
|
|
2350
2445
|
}
|
|
@@ -3053,6 +3148,7 @@ var init_queries = __esm({
|
|
|
3053
3148
|
"src/db/queries.ts"() {
|
|
3054
3149
|
"use strict";
|
|
3055
3150
|
init_connection();
|
|
3151
|
+
init_diagnostics();
|
|
3056
3152
|
init_formatters();
|
|
3057
3153
|
init_connection();
|
|
3058
3154
|
}
|
|
@@ -3424,7 +3520,7 @@ var init_install = __esm({
|
|
|
3424
3520
|
});
|
|
3425
3521
|
|
|
3426
3522
|
// src/config/progress-migrate.ts
|
|
3427
|
-
import { existsSync as existsSync7, readFileSync as readFileSync5, renameSync as
|
|
3523
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
3428
3524
|
import { join as join7 } from "path";
|
|
3429
3525
|
function legacyStatePath() {
|
|
3430
3526
|
return join7(ntrpHome(), "state.json");
|
|
@@ -3455,11 +3551,13 @@ function migrateLegacyStateIfNeeded(installId) {
|
|
|
3455
3551
|
};
|
|
3456
3552
|
writeFileSync7(progressPath(), JSON.stringify(progress, null, 2) + "\n");
|
|
3457
3553
|
try {
|
|
3458
|
-
|
|
3459
|
-
} catch {
|
|
3554
|
+
renameSync3(legacyPath, legacyStateBackupPath());
|
|
3555
|
+
} catch (err) {
|
|
3556
|
+
debugError("progress.migrate.backup", err, legacyPath);
|
|
3460
3557
|
}
|
|
3461
3558
|
return progress;
|
|
3462
|
-
} catch {
|
|
3559
|
+
} catch (err) {
|
|
3560
|
+
debugError("progress.migrate", err);
|
|
3463
3561
|
return null;
|
|
3464
3562
|
}
|
|
3465
3563
|
}
|
|
@@ -3467,6 +3565,7 @@ var init_progress_migrate = __esm({
|
|
|
3467
3565
|
"src/config/progress-migrate.ts"() {
|
|
3468
3566
|
"use strict";
|
|
3469
3567
|
init_store();
|
|
3568
|
+
init_diagnostics();
|
|
3470
3569
|
}
|
|
3471
3570
|
});
|
|
3472
3571
|
|
|
@@ -4382,7 +4481,13 @@ function loadCustomProviders() {
|
|
|
4382
4481
|
try {
|
|
4383
4482
|
const parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
4384
4483
|
cachedEntries = Array.isArray(parsed.providers) ? parsed.providers : [];
|
|
4385
|
-
} catch {
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
debugError("llm.customProviders.load", err, path);
|
|
4486
|
+
const quarantined = quarantineCorruptFile(path, "llm.customProviders.load");
|
|
4487
|
+
warnOnce(
|
|
4488
|
+
"providers-unreadable",
|
|
4489
|
+
`Could not read ${path} (${describeError(err)}).` + (quarantined ? ` Kept a copy at ${quarantined}; re-add custom endpoints with /connect --base-url.` : " Re-add custom endpoints with /connect --base-url.")
|
|
4490
|
+
);
|
|
4386
4491
|
cachedEntries = [];
|
|
4387
4492
|
}
|
|
4388
4493
|
return cachedEntries;
|
|
@@ -4456,6 +4561,7 @@ var init_providers = __esm({
|
|
|
4456
4561
|
"src/ai/llm/providers.ts"() {
|
|
4457
4562
|
"use strict";
|
|
4458
4563
|
init_store();
|
|
4564
|
+
init_diagnostics();
|
|
4459
4565
|
BUILTIN_SPECS = [
|
|
4460
4566
|
{
|
|
4461
4567
|
id: "anthropic",
|
|
@@ -5192,7 +5298,8 @@ function loadFile() {
|
|
|
5192
5298
|
try {
|
|
5193
5299
|
const parsed = JSON.parse(readFileSync8(path, "utf-8"));
|
|
5194
5300
|
cached = { version: 1, providers: parsed.providers ?? {} };
|
|
5195
|
-
} catch {
|
|
5301
|
+
} catch (err) {
|
|
5302
|
+
debugError("llm.modelsCache.load", err, path);
|
|
5196
5303
|
cached = { version: 1, providers: {} };
|
|
5197
5304
|
}
|
|
5198
5305
|
return cached;
|
|
@@ -5247,6 +5354,7 @@ var init_models_cache = __esm({
|
|
|
5247
5354
|
"src/ai/llm/models-cache.ts"() {
|
|
5248
5355
|
"use strict";
|
|
5249
5356
|
init_store();
|
|
5357
|
+
init_diagnostics();
|
|
5250
5358
|
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
5251
5359
|
cached = null;
|
|
5252
5360
|
}
|
|
@@ -5288,14 +5396,14 @@ var init_catalog = __esm({
|
|
|
5288
5396
|
init_models_cache();
|
|
5289
5397
|
ENTRIES = [
|
|
5290
5398
|
{
|
|
5291
|
-
id: "claude-
|
|
5399
|
+
id: "claude-fable-5",
|
|
5292
5400
|
provider: "anthropic",
|
|
5293
5401
|
tier: "high",
|
|
5294
5402
|
status: "active",
|
|
5295
5403
|
successor_id: null,
|
|
5296
5404
|
supports_tools: true,
|
|
5297
|
-
max_context_tokens:
|
|
5298
|
-
display_name: "Claude
|
|
5405
|
+
max_context_tokens: 1e6,
|
|
5406
|
+
display_name: "Claude Fable 5",
|
|
5299
5407
|
relative_cost: 3
|
|
5300
5408
|
},
|
|
5301
5409
|
{
|
|
@@ -5321,36 +5429,36 @@ var init_catalog = __esm({
|
|
|
5321
5429
|
relative_cost: 1
|
|
5322
5430
|
},
|
|
5323
5431
|
{
|
|
5324
|
-
id: "gpt-
|
|
5432
|
+
id: "gpt-5",
|
|
5325
5433
|
provider: "openai",
|
|
5326
5434
|
tier: "high",
|
|
5327
5435
|
status: "active",
|
|
5328
5436
|
successor_id: null,
|
|
5329
5437
|
supports_tools: true,
|
|
5330
5438
|
max_context_tokens: 1047576,
|
|
5331
|
-
display_name: "GPT-
|
|
5439
|
+
display_name: "GPT-5",
|
|
5332
5440
|
relative_cost: 3
|
|
5333
5441
|
},
|
|
5334
5442
|
{
|
|
5335
|
-
id: "gpt-
|
|
5443
|
+
id: "gpt-5-mini",
|
|
5336
5444
|
provider: "openai",
|
|
5337
5445
|
tier: "medium",
|
|
5338
5446
|
status: "active",
|
|
5339
5447
|
successor_id: null,
|
|
5340
5448
|
supports_tools: true,
|
|
5341
5449
|
max_context_tokens: 1047576,
|
|
5342
|
-
display_name: "GPT-
|
|
5450
|
+
display_name: "GPT-5 Mini",
|
|
5343
5451
|
relative_cost: 2
|
|
5344
5452
|
},
|
|
5345
5453
|
{
|
|
5346
|
-
id: "gpt-
|
|
5454
|
+
id: "gpt-5-nano",
|
|
5347
5455
|
provider: "openai",
|
|
5348
5456
|
tier: "low",
|
|
5349
5457
|
status: "active",
|
|
5350
5458
|
successor_id: null,
|
|
5351
5459
|
supports_tools: true,
|
|
5352
5460
|
max_context_tokens: 1047576,
|
|
5353
|
-
display_name: "GPT-
|
|
5461
|
+
display_name: "GPT-5 Nano",
|
|
5354
5462
|
relative_cost: 1
|
|
5355
5463
|
}
|
|
5356
5464
|
];
|
|
@@ -5370,9 +5478,11 @@ function fixtureResponse(url, headers) {
|
|
|
5370
5478
|
if (entry.auth_includes && !headerValues.includes(entry.auth_includes)) continue;
|
|
5371
5479
|
return { status: entry.status, ok: entry.status >= 200 && entry.status < 300, body: entry.body };
|
|
5372
5480
|
}
|
|
5373
|
-
} catch {
|
|
5481
|
+
} catch (err) {
|
|
5482
|
+
debugError("llm.http.fixture", err, process.env.NTRP_LLM_HTTP_FIXTURE);
|
|
5483
|
+
return { status: 0, ok: false, body: void 0, error: `fixture unreadable: ${describeError(err)}` };
|
|
5374
5484
|
}
|
|
5375
|
-
return { status: 0, ok: false, body: void 0 };
|
|
5485
|
+
return { status: 0, ok: false, body: void 0, error: `no fixture entry matched ${url}` };
|
|
5376
5486
|
}
|
|
5377
5487
|
async function llmHttpGetJson(url, headers, timeoutMs = 6e3) {
|
|
5378
5488
|
if (process.env.NTRP_LLM_HTTP_FIXTURE) {
|
|
@@ -5385,12 +5495,20 @@ async function llmHttpGetJson(url, headers, timeoutMs = 6e3) {
|
|
|
5385
5495
|
let body;
|
|
5386
5496
|
try {
|
|
5387
5497
|
body = await res.json();
|
|
5388
|
-
} catch {
|
|
5498
|
+
} catch (err) {
|
|
5499
|
+
debugError("llm.http.parse", err, url);
|
|
5389
5500
|
body = void 0;
|
|
5390
5501
|
}
|
|
5391
5502
|
return { status: res.status, ok: res.ok, body };
|
|
5392
|
-
} catch {
|
|
5393
|
-
|
|
5503
|
+
} catch (err) {
|
|
5504
|
+
debugError("llm.http.get", err, url);
|
|
5505
|
+
const aborted = err instanceof Error && err.name === "AbortError";
|
|
5506
|
+
return {
|
|
5507
|
+
status: 0,
|
|
5508
|
+
ok: false,
|
|
5509
|
+
body: void 0,
|
|
5510
|
+
error: aborted ? `timed out after ${timeoutMs}ms` : describeError(err)
|
|
5511
|
+
};
|
|
5394
5512
|
} finally {
|
|
5395
5513
|
clearTimeout(timer);
|
|
5396
5514
|
}
|
|
@@ -5398,6 +5516,7 @@ async function llmHttpGetJson(url, headers, timeoutMs = 6e3) {
|
|
|
5398
5516
|
var init_http = __esm({
|
|
5399
5517
|
"src/ai/llm/http.ts"() {
|
|
5400
5518
|
"use strict";
|
|
5519
|
+
init_diagnostics();
|
|
5401
5520
|
}
|
|
5402
5521
|
});
|
|
5403
5522
|
|
|
@@ -5416,86 +5535,157 @@ function extractVersion(id) {
|
|
|
5416
5535
|
const match = id.match(/(\d+(?:\.\d+)?)/);
|
|
5417
5536
|
return match ? Number(match[1]) : 0;
|
|
5418
5537
|
}
|
|
5419
|
-
function
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
return
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5538
|
+
function matchesAny(id, patterns) {
|
|
5539
|
+
return patterns.some((p) => p.test(id));
|
|
5540
|
+
}
|
|
5541
|
+
function toolsScore(model, noTools) {
|
|
5542
|
+
if (noTools.has(model.id)) return 0;
|
|
5543
|
+
if (model.supports_tools === true) return 2;
|
|
5544
|
+
if (model.supports_tools === false) return 0;
|
|
5545
|
+
return 1;
|
|
5546
|
+
}
|
|
5547
|
+
function compareCompatible(a, b, noTools = /* @__PURE__ */ new Set()) {
|
|
5548
|
+
const scoreA = toolsScore(a, noTools);
|
|
5549
|
+
const scoreB = toolsScore(b, noTools);
|
|
5550
|
+
if (scoreA !== scoreB) return scoreB - scoreA;
|
|
5551
|
+
return compareModels(a, b);
|
|
5552
|
+
}
|
|
5553
|
+
function bestOf(models, noTools) {
|
|
5554
|
+
if (models.length === 0) return void 0;
|
|
5555
|
+
return [...models].sort((a, b) => compareCompatible(a, b, noTools))[0];
|
|
5556
|
+
}
|
|
5557
|
+
function ladderFor(providerId) {
|
|
5558
|
+
return PROVIDER_LADDERS[providerId];
|
|
5559
|
+
}
|
|
5560
|
+
function classifyModel(providerId, model) {
|
|
5561
|
+
const id = model.id;
|
|
5562
|
+
const ladder = ladderFor(providerId);
|
|
5563
|
+
if (ladder) {
|
|
5564
|
+
if (matchesAny(id, ladder.high)) return "high";
|
|
5565
|
+
if (matchesAny(id, ladder.medium)) return "medium";
|
|
5566
|
+
if (matchesAny(id, ladder.low)) return "low";
|
|
5567
|
+
}
|
|
5568
|
+
if (GENERIC_LOW.test(id)) return "low";
|
|
5569
|
+
if (ladder && matchesAny(id, ladder.namespaces)) return "high";
|
|
5570
|
+
if (GENERIC_HIGH.test(id)) return "high";
|
|
5429
5571
|
return "medium";
|
|
5430
5572
|
}
|
|
5431
|
-
function
|
|
5432
|
-
const bucket = models.filter((m) => genericBucket(m) === tier);
|
|
5433
|
-
if (bucket.length > 0) return [...bucket].sort(compareModels)[0];
|
|
5434
|
-
return void 0;
|
|
5435
|
-
}
|
|
5436
|
-
function rankModels(providerId, models) {
|
|
5573
|
+
function rankModels(providerId, models, opts = {}) {
|
|
5437
5574
|
if (models.length === 0) return null;
|
|
5438
|
-
const
|
|
5439
|
-
const
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
const
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5575
|
+
const noTools = new Set(opts.noTools ?? []);
|
|
5576
|
+
const ceiling = [];
|
|
5577
|
+
const mid = [];
|
|
5578
|
+
const floor = [];
|
|
5579
|
+
for (const model of models) {
|
|
5580
|
+
const band = classifyModel(providerId, model);
|
|
5581
|
+
if (band === "high") ceiling.push(model);
|
|
5582
|
+
else if (band === "low") floor.push(model);
|
|
5583
|
+
else mid.push(model);
|
|
5584
|
+
}
|
|
5585
|
+
const highPick = bestOf(ceiling, noTools) ?? bestOf(mid, noTools) ?? bestOf(floor, noTools) ?? bestOf(models, noTools);
|
|
5586
|
+
const used = /* @__PURE__ */ new Set([highPick.id]);
|
|
5587
|
+
const mediumPick = bestOf(
|
|
5588
|
+
mid.filter((m) => !used.has(m.id)),
|
|
5589
|
+
noTools
|
|
5590
|
+
) ?? bestOf(
|
|
5591
|
+
ceiling.filter((m) => !used.has(m.id)),
|
|
5592
|
+
noTools
|
|
5593
|
+
) ?? bestOf(
|
|
5594
|
+
floor.filter((m) => !used.has(m.id)),
|
|
5595
|
+
noTools
|
|
5596
|
+
) ?? highPick;
|
|
5597
|
+
used.add(mediumPick.id);
|
|
5598
|
+
const lowPick = bestOf(
|
|
5599
|
+
floor.filter((m) => !used.has(m.id)),
|
|
5600
|
+
noTools
|
|
5601
|
+
) ?? bestOf(
|
|
5602
|
+
mid.filter((m) => !used.has(m.id)),
|
|
5603
|
+
noTools
|
|
5604
|
+
) ?? bestOf(
|
|
5605
|
+
ceiling.filter((m) => !used.has(m.id)),
|
|
5606
|
+
noTools
|
|
5607
|
+
) ?? mediumPick;
|
|
5608
|
+
return { high: highPick.id, medium: mediumPick.id, low: lowPick.id };
|
|
5609
|
+
}
|
|
5610
|
+
var PROVIDER_LADDERS, GENERIC_LOW, GENERIC_HIGH;
|
|
5452
5611
|
var init_ranking = __esm({
|
|
5453
5612
|
"src/ai/llm/ranking.ts"() {
|
|
5454
5613
|
"use strict";
|
|
5455
|
-
|
|
5614
|
+
PROVIDER_LADDERS = {
|
|
5456
5615
|
anthropic: {
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5616
|
+
namespaces: [/^claude-/i],
|
|
5617
|
+
high: [/^claude-mythos/i, /^claude-fable/i, /^claude-opus/i],
|
|
5618
|
+
medium: [/^claude-sonnet/i],
|
|
5619
|
+
low: [/^claude-haiku/i]
|
|
5460
5620
|
},
|
|
5461
5621
|
openai: {
|
|
5462
|
-
|
|
5463
|
-
|
|
5622
|
+
namespaces: [/^gpt-/i, /^o\d/i],
|
|
5623
|
+
high: [
|
|
5624
|
+
/^gpt-5(?!.*(mini|nano|chat))/i,
|
|
5625
|
+
/^gpt-4\.1(?!.*(mini|nano))/i,
|
|
5626
|
+
/^gpt-4o(?!.*mini)/i,
|
|
5627
|
+
/^o\d(?!.*mini)/i
|
|
5628
|
+
],
|
|
5629
|
+
medium: [/^gpt-5.*mini/i, /^gpt-4\.1-mini/i, /^gpt-4o-mini/i, /^o\d.*mini/i],
|
|
5464
5630
|
low: [/^gpt-5.*nano/i, /^gpt-4\.1-nano/i, /^gpt-4o-mini/i]
|
|
5465
5631
|
},
|
|
5466
5632
|
google: {
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5633
|
+
namespaces: [/^gemini-/i],
|
|
5634
|
+
high: [/^gemini-[\d.]+-pro/i, /^gemini-[\d.]+-ultra/i],
|
|
5635
|
+
medium: [/^gemini-[\d.]+-flash(?!-lite|-8b)/i],
|
|
5636
|
+
low: [/^gemini-[\d.]+-flash-lite/i, /flash-8b/i]
|
|
5637
|
+
},
|
|
5638
|
+
xai: {
|
|
5639
|
+
namespaces: [/^grok-/i, /^grok(?!-)/i],
|
|
5640
|
+
high: [/^grok-\d+(?!.*(mini|fast))/i, /^grok(?!.*(mini|fast))/i],
|
|
5641
|
+
medium: [/^grok.*mini(?!.*fast)/i, /^grok.*fast/i],
|
|
5642
|
+
low: [/^grok.*mini.*fast/i, /^grok.*mini/i]
|
|
5470
5643
|
},
|
|
5471
5644
|
groq: {
|
|
5472
|
-
|
|
5645
|
+
// Any hosted chat id is a peer — new OSS renames should still ceiling.
|
|
5646
|
+
namespaces: [/./],
|
|
5647
|
+
high: [/llama-3\.3-70b/i, /gpt-oss-120b/i, /70b/i, /405b/i, /deepseek-r1/i],
|
|
5473
5648
|
medium: [/llama-3\.1-8b-instant/i, /gpt-oss-20b/i, /llama.*8b/i],
|
|
5474
5649
|
low: [/8b-instant/i, /llama.*8b/i, /gemma/i]
|
|
5475
5650
|
},
|
|
5476
5651
|
deepseek: {
|
|
5477
|
-
|
|
5652
|
+
namespaces: [/^deepseek-/i],
|
|
5653
|
+
high: [/reasoner/i],
|
|
5478
5654
|
medium: [/chat/i],
|
|
5479
5655
|
low: [/chat/i]
|
|
5480
5656
|
},
|
|
5481
5657
|
mistral: {
|
|
5482
|
-
|
|
5483
|
-
|
|
5658
|
+
namespaces: [/^mistral-/i, /^ministral-/i, /^codestral-/i, /^pixtral-/i, /^devstral-/i],
|
|
5659
|
+
high: [/large/i],
|
|
5660
|
+
medium: [/medium/i, /^mistral-small/i, /^codestral/i, /^pixtral/i, /^devstral/i],
|
|
5484
5661
|
low: [/ministral/i, /small/i, /tiny/i]
|
|
5485
5662
|
},
|
|
5486
|
-
xai: {
|
|
5487
|
-
high: [/^grok-\d+(?!.*(mini|fast))/i, /^grok(?!.*(mini|fast))/i],
|
|
5488
|
-
medium: [/^grok.*mini(?!.*fast)/i, /^grok.*fast/i],
|
|
5489
|
-
low: [/^grok.*mini.*fast/i, /^grok.*mini/i]
|
|
5490
|
-
},
|
|
5491
5663
|
openrouter: {
|
|
5492
|
-
|
|
5664
|
+
namespaces: [
|
|
5665
|
+
/^anthropic\//i,
|
|
5666
|
+
/^openai\//i,
|
|
5667
|
+
/^google\//i,
|
|
5668
|
+
/^x-ai\//i,
|
|
5669
|
+
/^meta-llama\//i,
|
|
5670
|
+
/^mistralai\//i,
|
|
5671
|
+
/^deepseek\//i,
|
|
5672
|
+
/^openrouter\//i,
|
|
5673
|
+
/^[^/]+\/[^/]+/
|
|
5674
|
+
],
|
|
5675
|
+
high: [
|
|
5676
|
+
/^openrouter\/auto$/i,
|
|
5677
|
+
/claude.*(mythos|fable|opus)/i,
|
|
5678
|
+
/^openai\/gpt-5(?!.*(mini|nano))/i,
|
|
5679
|
+
/^openai\/o\d(?!.*mini)/i,
|
|
5680
|
+
/gemini.*pro/i,
|
|
5681
|
+
/gemini.*ultra/i
|
|
5682
|
+
],
|
|
5493
5683
|
medium: [/claude.*sonnet/i, /gpt-5.*mini/i, /gpt-4\.1-mini/i, /gemini.*flash(?!-lite)/i],
|
|
5494
5684
|
low: [/claude.*haiku/i, /nano/i, /flash-lite/i, /mini/i]
|
|
5495
5685
|
}
|
|
5496
5686
|
};
|
|
5497
5687
|
GENERIC_LOW = /(mini|nano|lite|tiny|micro|small|haiku|instant|flash|turbo|\b0?\.?5b\b|\b[1-8]b\b)/i;
|
|
5498
|
-
GENERIC_HIGH = /(opus|ultra|large|max\b|\bpro\b|405b|253b|235b|120b|72b|70b|reason|-r1\b|think|deep)/i;
|
|
5688
|
+
GENERIC_HIGH = /(opus|fable|mythos|ultra|large|max\b|\bpro\b|405b|253b|235b|120b|72b|70b|reasoner|reason|-r1\b|think|deep)/i;
|
|
5499
5689
|
}
|
|
5500
5690
|
});
|
|
5501
5691
|
|
|
@@ -5540,7 +5730,9 @@ async function fetchProviderModels(spec, apiKey, timeoutMs = 6e3) {
|
|
|
5540
5730
|
let url = modelsUrl(spec);
|
|
5541
5731
|
for (let page = 0; page < 5 && url; page++) {
|
|
5542
5732
|
const res2 = await llmHttpGetJson(url, headers, timeoutMs);
|
|
5543
|
-
if (!res2.ok)
|
|
5733
|
+
if (!res2.ok) {
|
|
5734
|
+
return models2.length > 0 ? { ok: true, models: models2 } : { ok: false, status: res2.status, ...res2.error ? { error: res2.error } : {} };
|
|
5735
|
+
}
|
|
5544
5736
|
const body2 = res2.body;
|
|
5545
5737
|
for (const item of body2?.data ?? []) {
|
|
5546
5738
|
const model = normalizeItem(spec, item);
|
|
@@ -5551,7 +5743,7 @@ async function fetchProviderModels(spec, apiKey, timeoutMs = 6e3) {
|
|
|
5551
5743
|
return { ok: true, models: models2 };
|
|
5552
5744
|
}
|
|
5553
5745
|
const res = await llmHttpGetJson(modelsUrl(spec), headers, timeoutMs);
|
|
5554
|
-
if (!res.ok) return { ok: false, status: res.status };
|
|
5746
|
+
if (!res.ok) return { ok: false, status: res.status, ...res.error ? { error: res.error } : {} };
|
|
5555
5747
|
const body = res.body;
|
|
5556
5748
|
const list = Array.isArray(body) ? body : body?.data ?? [];
|
|
5557
5749
|
const models = [];
|
|
@@ -5570,9 +5762,9 @@ function storeDiscoveredModels(providerId, rawModels) {
|
|
|
5570
5762
|
if (!spec || rawModels.length === 0) return null;
|
|
5571
5763
|
const chat = filterChatModels(spec, rawModels);
|
|
5572
5764
|
const usable = chat.length > 0 ? chat : rawModels;
|
|
5573
|
-
const stack = rankModels(providerId, usable);
|
|
5574
|
-
if (!stack) return null;
|
|
5575
5765
|
const prior = getProviderModels(providerId);
|
|
5766
|
+
const stack = rankModels(providerId, usable, { noTools: prior?.quirks?.no_tools });
|
|
5767
|
+
if (!stack) return null;
|
|
5576
5768
|
const entry = {
|
|
5577
5769
|
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5578
5770
|
models: usable,
|
|
@@ -5598,7 +5790,7 @@ function rerankExcluding(providerId, deadModelId) {
|
|
|
5598
5790
|
const prior = getProviderModels(providerId);
|
|
5599
5791
|
if (!prior) return null;
|
|
5600
5792
|
const survivors = prior.models.filter((m) => m.id !== deadModelId);
|
|
5601
|
-
const stack = rankModels(providerId, survivors);
|
|
5793
|
+
const stack = rankModels(providerId, survivors, { noTools: prior.quirks?.no_tools });
|
|
5602
5794
|
if (!stack) return null;
|
|
5603
5795
|
const entry = { ...prior, models: survivors, tier_stack: stack };
|
|
5604
5796
|
setProviderModels(providerId, entry);
|
|
@@ -6397,7 +6589,13 @@ function loadProfile() {
|
|
|
6397
6589
|
const parsed = JSON.parse(readFileSync11(PROFILE_PATH, "utf-8"));
|
|
6398
6590
|
if (!parsed || typeof parsed !== "object") return null;
|
|
6399
6591
|
return parsed;
|
|
6400
|
-
} catch {
|
|
6592
|
+
} catch (err) {
|
|
6593
|
+
debugError("profile.load", err, PROFILE_PATH);
|
|
6594
|
+
const quarantined = quarantineCorruptFile(PROFILE_PATH, "profile.load");
|
|
6595
|
+
warnOnce(
|
|
6596
|
+
"profile-unreadable",
|
|
6597
|
+
`Could not read ${PROFILE_PATH} (${describeError(err)}).` + (quarantined ? ` Kept a copy at ${quarantined}; run /onboard to rebuild it.` : " Run /onboard to rebuild it.")
|
|
6598
|
+
);
|
|
6401
6599
|
return null;
|
|
6402
6600
|
}
|
|
6403
6601
|
}
|
|
@@ -6435,6 +6633,7 @@ var init_profile = __esm({
|
|
|
6435
6633
|
"src/config/profile.ts"() {
|
|
6436
6634
|
"use strict";
|
|
6437
6635
|
init_store();
|
|
6636
|
+
init_diagnostics();
|
|
6438
6637
|
NTRP_DIR3 = ntrpHome();
|
|
6439
6638
|
PROFILE_PATH = join12(NTRP_DIR3, "profile.json");
|
|
6440
6639
|
}
|
|
@@ -6640,7 +6839,9 @@ async function resolveModelWithDiscovery(provider, cfg, opts) {
|
|
|
6640
6839
|
const override = overrideForProvider(opts.modelOverride, provider, cfg.activeProvider);
|
|
6641
6840
|
const direct = resolveModelSafe(provider, cfg.tier, override);
|
|
6642
6841
|
if (direct) return direct;
|
|
6643
|
-
await refreshProviderModels(provider, { apiKey: opts.apiKey, force: true }).catch(
|
|
6842
|
+
await refreshProviderModels(provider, { apiKey: opts.apiKey, force: true }).catch(
|
|
6843
|
+
(err) => debugError("llm.refreshProviderModels", err, provider)
|
|
6844
|
+
);
|
|
6644
6845
|
return resolveModelSafe(provider, cfg.tier, override);
|
|
6645
6846
|
}
|
|
6646
6847
|
function stripTools(req) {
|
|
@@ -6908,6 +7109,7 @@ var init_failover = __esm({
|
|
|
6908
7109
|
init_resolver();
|
|
6909
7110
|
init_pseudonymize();
|
|
6910
7111
|
init_lexicon_seed();
|
|
7112
|
+
init_diagnostics();
|
|
6911
7113
|
NO_PROVIDER_MESSAGE = "No LLM provider configured. Run /connect and paste any API key (Anthropic, OpenAI, Groq, Gemini, ...).";
|
|
6912
7114
|
}
|
|
6913
7115
|
});
|
|
@@ -7002,7 +7204,8 @@ async function callProvider(texts) {
|
|
|
7002
7204
|
const json = await res.json();
|
|
7003
7205
|
if (!json.data) return null;
|
|
7004
7206
|
return json.data.map((d) => d.embedding);
|
|
7005
|
-
} catch {
|
|
7207
|
+
} catch (err) {
|
|
7208
|
+
debugError("ai.embeddings.fetch", err);
|
|
7006
7209
|
return null;
|
|
7007
7210
|
}
|
|
7008
7211
|
}
|
|
@@ -7054,6 +7257,7 @@ var init_embeddings = __esm({
|
|
|
7054
7257
|
init_llm_config();
|
|
7055
7258
|
init_pseudonymize();
|
|
7056
7259
|
init_lexicon_seed();
|
|
7260
|
+
init_diagnostics();
|
|
7057
7261
|
VOYAGE_MODEL = "voyage-3";
|
|
7058
7262
|
OPENAI_MODEL = "text-embedding-3-small";
|
|
7059
7263
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -7111,7 +7315,8 @@ async function rankByRelevance(query, items, topK) {
|
|
|
7111
7315
|
if (scored.length > 0) return scored.slice(0, topK);
|
|
7112
7316
|
}
|
|
7113
7317
|
}
|
|
7114
|
-
} catch {
|
|
7318
|
+
} catch (err) {
|
|
7319
|
+
debugError("memory.rankByRelevance", err, "fell back to keyword ranking");
|
|
7115
7320
|
}
|
|
7116
7321
|
return keywordRank(query, items, topK);
|
|
7117
7322
|
}
|
|
@@ -7119,6 +7324,7 @@ var STOPWORDS;
|
|
|
7119
7324
|
var init_retrieval = __esm({
|
|
7120
7325
|
"src/memory/retrieval.ts"() {
|
|
7121
7326
|
"use strict";
|
|
7327
|
+
init_diagnostics();
|
|
7122
7328
|
STOPWORDS = /* @__PURE__ */ new Set([
|
|
7123
7329
|
"the",
|
|
7124
7330
|
"and",
|
|
@@ -7317,7 +7523,7 @@ async function readPdf(sourcePath, data) {
|
|
|
7317
7523
|
2 /* Usage */
|
|
7318
7524
|
);
|
|
7319
7525
|
} finally {
|
|
7320
|
-
await parser.destroy().catch(() =>
|
|
7526
|
+
await parser.destroy().catch((err) => debugError("strategies.reader.destroy", err));
|
|
7321
7527
|
}
|
|
7322
7528
|
}
|
|
7323
7529
|
function createDocument(sourceType, sourcePath, rawText, structuredHint, metadata = {}) {
|
|
@@ -7363,6 +7569,7 @@ var init_readers = __esm({
|
|
|
7363
7569
|
"use strict";
|
|
7364
7570
|
init_errors2();
|
|
7365
7571
|
init_types2();
|
|
7572
|
+
init_diagnostics();
|
|
7366
7573
|
MAX_STRATEGY_BYTES = 10 * 1024 * 1024;
|
|
7367
7574
|
MAX_PDF_PAGES = 50;
|
|
7368
7575
|
}
|
|
@@ -7384,7 +7591,8 @@ function loadKnowledgeChunks() {
|
|
|
7384
7591
|
if (!trimmed) continue;
|
|
7385
7592
|
try {
|
|
7386
7593
|
out.push(JSON.parse(trimmed));
|
|
7387
|
-
} catch {
|
|
7594
|
+
} catch (err) {
|
|
7595
|
+
debugError("knowledge.load", err, "skipped malformed chunk line");
|
|
7388
7596
|
}
|
|
7389
7597
|
}
|
|
7390
7598
|
return out;
|
|
@@ -7427,7 +7635,8 @@ async function addKnowledgeFile(pathOrDash, titleOverride) {
|
|
|
7427
7635
|
}));
|
|
7428
7636
|
try {
|
|
7429
7637
|
appendFileSync2(knowledgePath(), rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length ? "\n" : ""));
|
|
7430
|
-
} catch {
|
|
7638
|
+
} catch (err) {
|
|
7639
|
+
debugError("knowledge.append", err, knowledgePath());
|
|
7431
7640
|
}
|
|
7432
7641
|
return { doc_id: docId, title, source_path: doc.source_path, chunks: rows.length };
|
|
7433
7642
|
}
|
|
@@ -7453,7 +7662,8 @@ function listKnowledgeDocs() {
|
|
|
7453
7662
|
function listKnowledgeDirFiles(dir) {
|
|
7454
7663
|
try {
|
|
7455
7664
|
return readdirSync2(dir).filter((n) => !n.toLowerCase().endsWith("readme.md") && /\.(md|markdown|txt|pdf|yaml|yml)$/i.test(n));
|
|
7456
|
-
} catch {
|
|
7665
|
+
} catch (err) {
|
|
7666
|
+
debugError("knowledge.listDir", err, dir);
|
|
7457
7667
|
return [];
|
|
7458
7668
|
}
|
|
7459
7669
|
}
|
|
@@ -7463,6 +7673,7 @@ var init_knowledge = __esm({
|
|
|
7463
7673
|
"use strict";
|
|
7464
7674
|
init_store();
|
|
7465
7675
|
init_readers();
|
|
7676
|
+
init_diagnostics();
|
|
7466
7677
|
KNOWLEDGE_FILE = "knowledge.jsonl";
|
|
7467
7678
|
MAX_CHUNK_CHARS = 1200;
|
|
7468
7679
|
}
|
|
@@ -7470,7 +7681,6 @@ var init_knowledge = __esm({
|
|
|
7470
7681
|
|
|
7471
7682
|
// src/ai/privacy.ts
|
|
7472
7683
|
import { existsSync as existsSync16, mkdirSync as mkdirSync10, appendFileSync as appendFileSync3 } from "fs";
|
|
7473
|
-
import { homedir as homedir4 } from "os";
|
|
7474
7684
|
import { join as join14 } from "path";
|
|
7475
7685
|
function scrubSensitiveText(text) {
|
|
7476
7686
|
return redactSecrets(text).replace(EMAIL_RE2, "[email]");
|
|
@@ -7489,24 +7699,29 @@ function stripPII(obj) {
|
|
|
7489
7699
|
}
|
|
7490
7700
|
return out;
|
|
7491
7701
|
}
|
|
7492
|
-
function
|
|
7493
|
-
|
|
7494
|
-
|
|
7702
|
+
function auditDir() {
|
|
7703
|
+
const dir = join14(secureNtrpHome(), "audit");
|
|
7704
|
+
if (!existsSync16(dir)) {
|
|
7705
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
7495
7706
|
}
|
|
7707
|
+
chmodQuiet(dir, 448);
|
|
7708
|
+
return dir;
|
|
7496
7709
|
}
|
|
7497
7710
|
function logToolCall(entry) {
|
|
7498
|
-
ensureAuditDir();
|
|
7499
7711
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7500
|
-
const path = join14(
|
|
7712
|
+
const path = join14(auditDir(), `agentic-${date}.jsonl`);
|
|
7501
7713
|
appendFileSync3(
|
|
7502
7714
|
path,
|
|
7503
|
-
JSON.stringify({ ...entry, result_preview: scrubSensitiveText(entry.result_preview) }) + "\n"
|
|
7715
|
+
JSON.stringify({ ...entry, result_preview: scrubSensitiveText(entry.result_preview) }) + "\n",
|
|
7716
|
+
{ mode: 384 }
|
|
7504
7717
|
);
|
|
7718
|
+
chmodQuiet(path, 384);
|
|
7505
7719
|
}
|
|
7506
|
-
var PII_FIELDS, EMAIL_RE2
|
|
7720
|
+
var PII_FIELDS, EMAIL_RE2;
|
|
7507
7721
|
var init_privacy = __esm({
|
|
7508
7722
|
"src/ai/privacy.ts"() {
|
|
7509
7723
|
"use strict";
|
|
7724
|
+
init_store();
|
|
7510
7725
|
init_terminal_capture();
|
|
7511
7726
|
PII_FIELDS = /* @__PURE__ */ new Set([
|
|
7512
7727
|
"name",
|
|
@@ -7537,7 +7752,6 @@ var init_privacy = __esm({
|
|
|
7537
7752
|
"metadata"
|
|
7538
7753
|
]);
|
|
7539
7754
|
EMAIL_RE2 = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
|
|
7540
|
-
AUDIT_DIR = join14(homedir4(), ".ntrp", "audit");
|
|
7541
7755
|
}
|
|
7542
7756
|
});
|
|
7543
7757
|
|
|
@@ -7603,6 +7817,83 @@ var init_untrusted = __esm({
|
|
|
7603
7817
|
}
|
|
7604
7818
|
});
|
|
7605
7819
|
|
|
7820
|
+
// src/ai/json-response.ts
|
|
7821
|
+
function stripJsonFences(text) {
|
|
7822
|
+
const trimmed = text.trim();
|
|
7823
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
7824
|
+
if (fenced) return fenced[1].trim();
|
|
7825
|
+
return trimmed.replace(/```(?:json)?\s*/gi, "").replace(/```/g, "").trim();
|
|
7826
|
+
}
|
|
7827
|
+
function parseJsonObjectFromText(text) {
|
|
7828
|
+
const cleaned = stripJsonFences(text);
|
|
7829
|
+
const start = cleaned.indexOf("{");
|
|
7830
|
+
if (start === -1) return null;
|
|
7831
|
+
let depth = 0;
|
|
7832
|
+
let inString = false;
|
|
7833
|
+
let escaped = false;
|
|
7834
|
+
let end = -1;
|
|
7835
|
+
for (let i = start; i < cleaned.length; i++) {
|
|
7836
|
+
const ch = cleaned[i];
|
|
7837
|
+
if (escaped) {
|
|
7838
|
+
escaped = false;
|
|
7839
|
+
continue;
|
|
7840
|
+
}
|
|
7841
|
+
if (ch === "\\") {
|
|
7842
|
+
if (inString) escaped = true;
|
|
7843
|
+
continue;
|
|
7844
|
+
}
|
|
7845
|
+
if (ch === '"') {
|
|
7846
|
+
inString = !inString;
|
|
7847
|
+
continue;
|
|
7848
|
+
}
|
|
7849
|
+
if (inString) continue;
|
|
7850
|
+
if (ch === "{") depth++;
|
|
7851
|
+
else if (ch === "}") {
|
|
7852
|
+
depth--;
|
|
7853
|
+
if (depth === 0) {
|
|
7854
|
+
end = i;
|
|
7855
|
+
break;
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
if (end === -1) return null;
|
|
7860
|
+
try {
|
|
7861
|
+
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
7862
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
7863
|
+
} catch {
|
|
7864
|
+
return null;
|
|
7865
|
+
}
|
|
7866
|
+
}
|
|
7867
|
+
function parseJsonArrayFromText(text) {
|
|
7868
|
+
const cleaned = stripJsonFences(text);
|
|
7869
|
+
const start = cleaned.indexOf("[");
|
|
7870
|
+
if (start === -1) return null;
|
|
7871
|
+
let depth = 0;
|
|
7872
|
+
let end = -1;
|
|
7873
|
+
for (let i = start; i < cleaned.length; i++) {
|
|
7874
|
+
if (cleaned[i] === "[") depth++;
|
|
7875
|
+
else if (cleaned[i] === "]") {
|
|
7876
|
+
depth--;
|
|
7877
|
+
if (depth === 0) {
|
|
7878
|
+
end = i;
|
|
7879
|
+
break;
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
}
|
|
7883
|
+
if (end === -1) return null;
|
|
7884
|
+
try {
|
|
7885
|
+
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
7886
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
7887
|
+
} catch {
|
|
7888
|
+
return null;
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
var init_json_response = __esm({
|
|
7892
|
+
"src/ai/json-response.ts"() {
|
|
7893
|
+
"use strict";
|
|
7894
|
+
}
|
|
7895
|
+
});
|
|
7896
|
+
|
|
7606
7897
|
// src/data/gtm-counsel/play-routing.ts
|
|
7607
7898
|
function getPlayRouting(playId) {
|
|
7608
7899
|
return PLAY_ROUTING[playId];
|
|
@@ -7798,7 +8089,8 @@ function getCustomPlays() {
|
|
|
7798
8089
|
try {
|
|
7799
8090
|
const play = JSON.parse(trimmed);
|
|
7800
8091
|
out.push({ ...play, source: "learned" });
|
|
7801
|
-
} catch {
|
|
8092
|
+
} catch (err) {
|
|
8093
|
+
debugError("playbook.learned.read", err, "skipped malformed line");
|
|
7802
8094
|
}
|
|
7803
8095
|
}
|
|
7804
8096
|
return out;
|
|
@@ -7827,7 +8119,8 @@ function addCustomPlay(input) {
|
|
|
7827
8119
|
};
|
|
7828
8120
|
try {
|
|
7829
8121
|
appendFileSync4(playsPath(), JSON.stringify(play) + "\n");
|
|
7830
|
-
} catch {
|
|
8122
|
+
} catch (err) {
|
|
8123
|
+
debugError("playbook.learned.append", err, playsPath());
|
|
7831
8124
|
}
|
|
7832
8125
|
return play;
|
|
7833
8126
|
}
|
|
@@ -7910,6 +8203,7 @@ var init_playbook = __esm({
|
|
|
7910
8203
|
"use strict";
|
|
7911
8204
|
init_store();
|
|
7912
8205
|
init_play_routing();
|
|
8206
|
+
init_diagnostics();
|
|
7913
8207
|
PLAYBOOK = [
|
|
7914
8208
|
{
|
|
7915
8209
|
id: "multi-thread-deals",
|
|
@@ -8312,12 +8606,6 @@ var init_playbook = __esm({
|
|
|
8312
8606
|
});
|
|
8313
8607
|
|
|
8314
8608
|
// src/ai/strategy-normalize.ts
|
|
8315
|
-
function stripFences(text) {
|
|
8316
|
-
const trimmed = text.trim();
|
|
8317
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
8318
|
-
if (fenced) return fenced[1].trim();
|
|
8319
|
-
return trimmed;
|
|
8320
|
-
}
|
|
8321
8609
|
async function normalizeStrategy(input, ctx) {
|
|
8322
8610
|
assertReplAi(ctx);
|
|
8323
8611
|
const playbookBlock = getPlaybook().map((play) => `- ${play.id}: ${play.name} (${play.trigger_vital_sign}) \u2014 ${play.why}`).join("\n");
|
|
@@ -8341,7 +8629,7 @@ Normalize this strategy now as strict JSON.`,
|
|
|
8341
8629
|
);
|
|
8342
8630
|
let parsed;
|
|
8343
8631
|
try {
|
|
8344
|
-
parsed = JSON.parse(
|
|
8632
|
+
parsed = JSON.parse(stripJsonFences(text));
|
|
8345
8633
|
} catch {
|
|
8346
8634
|
throw new Error("AI response is not valid strategy JSON");
|
|
8347
8635
|
}
|
|
@@ -8417,6 +8705,7 @@ var init_strategy_normalize = __esm({
|
|
|
8417
8705
|
"use strict";
|
|
8418
8706
|
init_repl_api();
|
|
8419
8707
|
init_complete();
|
|
8708
|
+
init_json_response();
|
|
8420
8709
|
init_playbook();
|
|
8421
8710
|
SYSTEM_PROMPT = `You are a strategy-ingestion engine for NTRP, a GTM pipeline-health and orchestration tool.
|
|
8422
8711
|
|
|
@@ -8459,6 +8748,29 @@ JSON SHAPE:
|
|
|
8459
8748
|
}
|
|
8460
8749
|
});
|
|
8461
8750
|
|
|
8751
|
+
// src/output/strategy-signal.ts
|
|
8752
|
+
function isProcessMetaLine(text) {
|
|
8753
|
+
return PROCESS_META_RE.test(text.trim());
|
|
8754
|
+
}
|
|
8755
|
+
function filterOperatorLines(lines) {
|
|
8756
|
+
return lines.map((l) => l.trim()).filter((l) => l.length > 0 && !isProcessMetaLine(l));
|
|
8757
|
+
}
|
|
8758
|
+
function isGenericWorkstreamRationale(rationale) {
|
|
8759
|
+
return GENERIC_RATIONALE_RE.test(rationale.trim());
|
|
8760
|
+
}
|
|
8761
|
+
function usedGroundedFallback(notices) {
|
|
8762
|
+
return notices.some((n) => /grounded fallback/i.test(n));
|
|
8763
|
+
}
|
|
8764
|
+
var PROCESS_META_RE, GENERIC_RATIONALE_RE, STRATEGY_FALLBACK_BANNER;
|
|
8765
|
+
var init_strategy_signal = __esm({
|
|
8766
|
+
"src/output/strategy-signal.ts"() {
|
|
8767
|
+
"use strict";
|
|
8768
|
+
PROCESS_META_RE = /LLM\b|plan JSON|JSON invalid|grounded fallback|validated LLM|emitting grounded|Backcast JSON|Stress-test revision|Counsel reflection|Reality digest|Strategist JSON failed|Plan gap\s*\(/i;
|
|
8769
|
+
GENERIC_RATIONALE_RE = /Layer-order first|Next in dependency order after the prior workstream/i;
|
|
8770
|
+
STRATEGY_FALLBACK_BANNER = "Drafted from live vitals \u2014 refine with /strategy after the first review.";
|
|
8771
|
+
}
|
|
8772
|
+
});
|
|
8773
|
+
|
|
8462
8774
|
// src/strategies/library.ts
|
|
8463
8775
|
import { writeFileSync as writeFileSync11 } from "fs";
|
|
8464
8776
|
import { join as join16 } from "path";
|
|
@@ -8480,24 +8792,32 @@ function formatEffortSum(workstreams) {
|
|
|
8480
8792
|
return `~${Math.round(sum)} team-hours across ${count} workstream${count === 1 ? "" : "s"}`;
|
|
8481
8793
|
}
|
|
8482
8794
|
function renderConstraintHeading(constraintLine) {
|
|
8795
|
+
const line = constraintLine?.trim();
|
|
8796
|
+
if (!line) return "";
|
|
8483
8797
|
return `## Constraint
|
|
8484
|
-
${
|
|
8798
|
+
${line}
|
|
8485
8799
|
`;
|
|
8486
8800
|
}
|
|
8487
8801
|
function renderScopeHeading(constraints, outOfScope) {
|
|
8488
|
-
const
|
|
8802
|
+
const inItems = filterOperatorLines(constraints);
|
|
8489
8803
|
const outItems = (outOfScope ?? []).map((s) => s.trim()).filter(Boolean);
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8804
|
+
if (inItems.length === 0 && outItems.length === 0) return "";
|
|
8805
|
+
const parts = ["## In scope / out of scope"];
|
|
8806
|
+
if (inItems.length > 0) {
|
|
8807
|
+
parts.push("In scope:");
|
|
8808
|
+
parts.push(formatList(inItems));
|
|
8809
|
+
}
|
|
8810
|
+
if (outItems.length > 0) {
|
|
8811
|
+
if (inItems.length > 0) parts.push("");
|
|
8812
|
+
parts.push("Out of scope:");
|
|
8813
|
+
parts.push(formatList(outItems));
|
|
8814
|
+
}
|
|
8815
|
+
return parts.join("\n") + "\n";
|
|
8498
8816
|
}
|
|
8499
8817
|
function renderKilledAlternativeLine(killedAlternative) {
|
|
8500
|
-
|
|
8818
|
+
const line = killedAlternative?.trim();
|
|
8819
|
+
if (!line) return "";
|
|
8820
|
+
return `Killed alternative: ${line}`;
|
|
8501
8821
|
}
|
|
8502
8822
|
function renderEffortHeading(workstreams) {
|
|
8503
8823
|
return `## Effort
|
|
@@ -8536,14 +8856,15 @@ ${strategy.objective}
|
|
|
8536
8856
|
## Workstreams
|
|
8537
8857
|
${strategy.workstreams.map(formatWorkstream).join("\n")}
|
|
8538
8858
|
` : "";
|
|
8539
|
-
const constraintsSection = strategy.constraints.length > 0 ? `
|
|
8859
|
+
const constraintsSection = filterOperatorLines(strategy.constraints).length > 0 ? `
|
|
8540
8860
|
## Constraints
|
|
8541
|
-
${formatList(strategy.constraints)}
|
|
8861
|
+
${formatList(filterOperatorLines(strategy.constraints))}
|
|
8542
8862
|
` : "";
|
|
8543
|
-
const assumptionsSection = strategy.assumptions.length > 0 ? `
|
|
8863
|
+
const assumptionsSection = filterOperatorLines(strategy.assumptions).length > 0 ? `
|
|
8544
8864
|
## Assumptions (unverified)
|
|
8545
|
-
${formatList(strategy.assumptions)}
|
|
8865
|
+
${formatList(filterOperatorLines(strategy.assumptions))}
|
|
8546
8866
|
` : "";
|
|
8867
|
+
const risks = filterOperatorLines(strategy.risks);
|
|
8547
8868
|
return `---
|
|
8548
8869
|
${frontmatter}
|
|
8549
8870
|
---
|
|
@@ -8569,7 +8890,7 @@ ${formatMetrics(strategy.leading_indicators)}
|
|
|
8569
8890
|
${formatList(strategy.recommended_actions)}
|
|
8570
8891
|
${constraintsSection}${assumptionsSection}
|
|
8571
8892
|
## Risks
|
|
8572
|
-
${formatList(
|
|
8893
|
+
${formatList(risks)}
|
|
8573
8894
|
|
|
8574
8895
|
## Experiment Design
|
|
8575
8896
|
${strategy.experiment_design}
|
|
@@ -8591,76 +8912,75 @@ ${strategy.objective}
|
|
|
8591
8912
|
## Workstreams
|
|
8592
8913
|
${strategy.workstreams.map(formatWorkstream).join("\n")}
|
|
8593
8914
|
` : "";
|
|
8594
|
-
const
|
|
8915
|
+
const assumptions = filterOperatorLines(strategy.assumptions);
|
|
8916
|
+
const assumptionsSection = assumptions.length > 0 ? `
|
|
8595
8917
|
## Assumptions (unverified)
|
|
8596
|
-
${formatList(
|
|
8597
|
-
` : "";
|
|
8598
|
-
const craftLogSection = extras?.craftLogPath ? `
|
|
8599
|
-
## Craft log
|
|
8600
|
-
See \`${extras.craftLogPath}\`.
|
|
8918
|
+
${formatList(assumptions)}
|
|
8601
8919
|
` : "";
|
|
8602
|
-
|
|
8920
|
+
const risks = filterOperatorLines(strategy.risks);
|
|
8921
|
+
const constraintBlock = renderConstraintHeading(extras?.constraintLine);
|
|
8922
|
+
const scopeBlock = renderScopeHeading(strategy.constraints, extras?.outOfScope);
|
|
8923
|
+
const killedLine = renderKilledAlternativeLine(extras?.killedAlternative);
|
|
8924
|
+
const parts = [
|
|
8925
|
+
`---
|
|
8603
8926
|
${frontmatter}
|
|
8604
8927
|
---
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
## Hypothesis
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
##
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
${renderReviewHeading({ cadence: strategy.review_cadence, slug: strategy.slug })}${craftLogSection}
|
|
8638
|
-
## Source Excerpt
|
|
8639
|
-
${strategy.raw_excerpt || "_No excerpt captured._"}
|
|
8640
|
-
`;
|
|
8928
|
+
`,
|
|
8929
|
+
`# ${strategy.title}`,
|
|
8930
|
+
callSection.trimEnd(),
|
|
8931
|
+
objectiveSection.trimEnd(),
|
|
8932
|
+
constraintBlock.trimEnd(),
|
|
8933
|
+
"## Goal",
|
|
8934
|
+
strategy.goal,
|
|
8935
|
+
scopeBlock.trimEnd(),
|
|
8936
|
+
"## Hypothesis",
|
|
8937
|
+
strategy.hypothesis,
|
|
8938
|
+
killedLine,
|
|
8939
|
+
"## Target Segment",
|
|
8940
|
+
strategy.target_segment,
|
|
8941
|
+
renderEffortHeading(strategy.workstreams).trimEnd(),
|
|
8942
|
+
workstreamSection.trimEnd(),
|
|
8943
|
+
"## Success Metrics",
|
|
8944
|
+
formatMetrics(strategy.success_metrics),
|
|
8945
|
+
"## Leading Indicators",
|
|
8946
|
+
formatMetrics(strategy.leading_indicators),
|
|
8947
|
+
"## Recommended Actions",
|
|
8948
|
+
formatList(strategy.recommended_actions),
|
|
8949
|
+
assumptionsSection.trimEnd(),
|
|
8950
|
+
"## Risks",
|
|
8951
|
+
formatList(risks),
|
|
8952
|
+
"## Experiment Design",
|
|
8953
|
+
strategy.experiment_design,
|
|
8954
|
+
renderReviewHeading({ cadence: strategy.review_cadence, slug: strategy.slug }).trimEnd(),
|
|
8955
|
+
"## Source Excerpt",
|
|
8956
|
+
strategy.raw_excerpt || "_No excerpt captured._"
|
|
8957
|
+
].filter((p) => p != null && String(p).length > 0);
|
|
8958
|
+
return parts.join("\n\n") + "\n";
|
|
8641
8959
|
}
|
|
8642
8960
|
function formatWorkstream(ws) {
|
|
8643
8961
|
const lines = [];
|
|
8644
8962
|
lines.push(`### ${ws.order}. ${ws.title}`);
|
|
8645
8963
|
lines.push(`- Problem: ${ws.problem}`);
|
|
8646
|
-
|
|
8964
|
+
if (ws.rationale && !isGenericWorkstreamRationale(ws.rationale)) {
|
|
8965
|
+
lines.push(`- Why this order: ${ws.rationale}`);
|
|
8966
|
+
}
|
|
8647
8967
|
if (ws.play_ids.length > 0) lines.push(`- Plays: ${ws.play_ids.join(", ")}`);
|
|
8648
8968
|
lines.push(
|
|
8649
|
-
`- Expected outcome: ${ws.expected_outcome.metric} \u2014 ${ws.expected_outcome.baseline}
|
|
8969
|
+
`- Expected outcome: ${ws.expected_outcome.metric} \u2014 ${ws.expected_outcome.baseline} \u2192 ${ws.expected_outcome.target_range} by ${ws.expected_outcome.check_date} (measured by ${ws.expected_outcome.measured_by})`
|
|
8650
8970
|
);
|
|
8651
8971
|
for (const li of ws.leading_indicators) {
|
|
8652
|
-
lines.push(`- Leading indicator: ${li.metric} \u2014 ${li.baseline}
|
|
8972
|
+
lines.push(`- Leading indicator: ${li.metric} \u2014 ${li.baseline} \u2192 ${li.target_range} by ${li.check_date} (measured by ${li.measured_by})`);
|
|
8653
8973
|
}
|
|
8654
8974
|
if (ws.milestones.length > 0) {
|
|
8655
8975
|
lines.push(`- Milestones:`);
|
|
8656
8976
|
for (const m of ws.milestones) {
|
|
8657
|
-
lines.push(` - [ ] ${m.due} \u2014 ${m.label}
|
|
8977
|
+
lines.push(` - [ ] ${m.due} \u2014 ${m.label}`);
|
|
8658
8978
|
}
|
|
8659
8979
|
}
|
|
8660
8980
|
if (ws.deliverables.length > 0) {
|
|
8661
8981
|
lines.push(`- Deliverables:`);
|
|
8662
8982
|
for (const d of ws.deliverables) {
|
|
8663
|
-
lines.push(` - [ ] ${d.label} (
|
|
8983
|
+
lines.push(` - [ ] ${d.label} (due ${d.due})`);
|
|
8664
8984
|
}
|
|
8665
8985
|
}
|
|
8666
8986
|
if (ws.actions.length > 0) {
|
|
@@ -8669,7 +8989,7 @@ function formatWorkstream(ws) {
|
|
|
8669
8989
|
lines.push(` - ${action}`);
|
|
8670
8990
|
}
|
|
8671
8991
|
}
|
|
8672
|
-
lines.push(`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date})
|
|
8992
|
+
lines.push(`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) \u2192 ${ws.contingency.fallback}`);
|
|
8673
8993
|
lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
|
|
8674
8994
|
return lines.join("\n") + "\n";
|
|
8675
8995
|
}
|
|
@@ -8691,12 +9011,13 @@ var init_library = __esm({
|
|
|
8691
9011
|
"src/strategies/library.ts"() {
|
|
8692
9012
|
"use strict";
|
|
8693
9013
|
init_store();
|
|
9014
|
+
init_strategy_signal();
|
|
8694
9015
|
}
|
|
8695
9016
|
});
|
|
8696
9017
|
|
|
8697
9018
|
// src/strategies/connectors.ts
|
|
8698
9019
|
import { readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
8699
|
-
import { homedir as
|
|
9020
|
+
import { homedir as homedir4 } from "os";
|
|
8700
9021
|
import { basename as basename4, extname as extname2, join as join17, relative, resolve as resolve6, sep as sep3 } from "path";
|
|
8701
9022
|
function createLocalFolderConnector(options) {
|
|
8702
9023
|
const rootPath = resolveUserPath2(options.rootPath);
|
|
@@ -8743,15 +9064,15 @@ function walkLocalFolder(rootPath, currentPath, refs, opts) {
|
|
|
8743
9064
|
const absolutePath = join17(currentPath, entry.name);
|
|
8744
9065
|
const relativePath = normalizePath(relative(rootPath, absolutePath));
|
|
8745
9066
|
if (entry.isDirectory()) {
|
|
8746
|
-
if (shouldSkipDirectory(entry.name) ||
|
|
9067
|
+
if (shouldSkipDirectory(entry.name) || matchesAny2(relativePath, opts.excludePatterns)) continue;
|
|
8747
9068
|
walkLocalFolder(rootPath, absolutePath, refs, opts);
|
|
8748
9069
|
continue;
|
|
8749
9070
|
}
|
|
8750
9071
|
if (!entry.isFile()) continue;
|
|
8751
9072
|
const ext = extname2(entry.name).toLowerCase();
|
|
8752
9073
|
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
8753
|
-
if (opts.includePatterns.length > 0 && !
|
|
8754
|
-
if (
|
|
9074
|
+
if (opts.includePatterns.length > 0 && !matchesAny2(relativePath, opts.includePatterns)) continue;
|
|
9075
|
+
if (matchesAny2(relativePath, opts.excludePatterns)) continue;
|
|
8755
9076
|
const stat = safeStat(absolutePath);
|
|
8756
9077
|
if (!stat || stat.size > opts.maxBytes) continue;
|
|
8757
9078
|
refs.push({
|
|
@@ -8782,7 +9103,7 @@ function safeStat(path) {
|
|
|
8782
9103
|
function normalizePatterns(patterns) {
|
|
8783
9104
|
return (patterns ?? []).map((pattern) => pattern.trim()).filter(Boolean).map(normalizePath);
|
|
8784
9105
|
}
|
|
8785
|
-
function
|
|
9106
|
+
function matchesAny2(relativePath, patterns) {
|
|
8786
9107
|
return patterns.some((pattern) => matchesPattern(relativePath, pattern));
|
|
8787
9108
|
}
|
|
8788
9109
|
function matchesPattern(relativePath, pattern) {
|
|
@@ -8803,8 +9124,8 @@ function normalizePath(path) {
|
|
|
8803
9124
|
return path.split(sep3).join("/");
|
|
8804
9125
|
}
|
|
8805
9126
|
function resolveUserPath2(path) {
|
|
8806
|
-
if (path === "~") return
|
|
8807
|
-
if (path.startsWith("~/")) return join17(
|
|
9127
|
+
if (path === "~") return homedir4();
|
|
9128
|
+
if (path.startsWith("~/")) return join17(homedir4(), path.slice(2));
|
|
8808
9129
|
return resolve6(path);
|
|
8809
9130
|
}
|
|
8810
9131
|
var DEFAULT_MAX_FILES, DEFAULT_MAX_BYTES, SUPPORTED_EXTENSIONS, DEFAULT_EXCLUDED_DIRS;
|
|
@@ -9034,7 +9355,8 @@ function readJsonl(file) {
|
|
|
9034
9355
|
if (!trimmed) continue;
|
|
9035
9356
|
try {
|
|
9036
9357
|
out.push(JSON.parse(trimmed));
|
|
9037
|
-
} catch {
|
|
9358
|
+
} catch (err) {
|
|
9359
|
+
debugError("memory.readJsonl", err, `${file}: skipped malformed line`);
|
|
9038
9360
|
}
|
|
9039
9361
|
}
|
|
9040
9362
|
return out;
|
|
@@ -9042,13 +9364,15 @@ function readJsonl(file) {
|
|
|
9042
9364
|
function appendJsonl(file, obj) {
|
|
9043
9365
|
try {
|
|
9044
9366
|
appendFileSync5(memPath(file), JSON.stringify(obj) + "\n");
|
|
9045
|
-
} catch {
|
|
9367
|
+
} catch (err) {
|
|
9368
|
+
debugError("memory.appendJsonl", err, file);
|
|
9046
9369
|
}
|
|
9047
9370
|
}
|
|
9048
9371
|
function rewriteJsonl(file, rows) {
|
|
9049
9372
|
try {
|
|
9050
9373
|
writeFileSync12(memPath(file), rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length ? "\n" : ""));
|
|
9051
|
-
} catch {
|
|
9374
|
+
} catch (err) {
|
|
9375
|
+
debugError("memory.rewriteJsonl", err, file);
|
|
9052
9376
|
}
|
|
9053
9377
|
}
|
|
9054
9378
|
function scrubText(text) {
|
|
@@ -9152,7 +9476,8 @@ async function loadStrategySnippets() {
|
|
|
9152
9476
|
title: s.title,
|
|
9153
9477
|
text: `${s.title}. Goal: ${s.goal} Hypothesis: ${s.hypothesis} Target: ${s.target_segment}`
|
|
9154
9478
|
}));
|
|
9155
|
-
} catch {
|
|
9479
|
+
} catch (err) {
|
|
9480
|
+
debugError("memory.loadStrategySnippets", err);
|
|
9156
9481
|
return [];
|
|
9157
9482
|
}
|
|
9158
9483
|
}
|
|
@@ -9168,7 +9493,8 @@ function loadWinSnippets() {
|
|
|
9168
9493
|
out.push({ id: `win:${name}`, title, text: `${title}. ${body}` });
|
|
9169
9494
|
}
|
|
9170
9495
|
return out;
|
|
9171
|
-
} catch {
|
|
9496
|
+
} catch (err) {
|
|
9497
|
+
debugError("memory.loadWinSnippets", err);
|
|
9172
9498
|
return [];
|
|
9173
9499
|
}
|
|
9174
9500
|
}
|
|
@@ -9188,7 +9514,8 @@ async function buildMemoryBlock(query, opts = {}) {
|
|
|
9188
9514
|
let knowledge = [];
|
|
9189
9515
|
try {
|
|
9190
9516
|
knowledge = loadKnowledgeChunks();
|
|
9191
|
-
} catch {
|
|
9517
|
+
} catch (err) {
|
|
9518
|
+
debugError("memory.loadKnowledgeChunks", err);
|
|
9192
9519
|
knowledge = [];
|
|
9193
9520
|
}
|
|
9194
9521
|
const sections = [];
|
|
@@ -9270,6 +9597,7 @@ var init_store2 = __esm({
|
|
|
9270
9597
|
init_knowledge();
|
|
9271
9598
|
init_privacy();
|
|
9272
9599
|
init_untrusted();
|
|
9600
|
+
init_diagnostics();
|
|
9273
9601
|
FACTS_FILE = "facts.jsonl";
|
|
9274
9602
|
LEDGER_FILE = "ledger.jsonl";
|
|
9275
9603
|
FACTS_JSONL = FACTS_FILE;
|
|
@@ -9461,7 +9789,7 @@ __export(context_exports, {
|
|
|
9461
9789
|
});
|
|
9462
9790
|
import { basename as basename5, join as join19, resolve as resolve7, sep as sep4 } from "path";
|
|
9463
9791
|
import { existsSync as existsSync19, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13, readFileSync as readFileSync16, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync5 } from "fs";
|
|
9464
|
-
import { homedir as
|
|
9792
|
+
import { homedir as homedir5 } from "os";
|
|
9465
9793
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9466
9794
|
function isSessionStale(s) {
|
|
9467
9795
|
return Date.now() - s.mtime > STALE_SESSION_MS;
|
|
@@ -9475,7 +9803,7 @@ function isAnalysisReady(ctx) {
|
|
|
9475
9803
|
return Object.values(counts).some((n) => n > 0);
|
|
9476
9804
|
}
|
|
9477
9805
|
function ntrpHomeDir() {
|
|
9478
|
-
return process.env.NTRP_HOME ? resolve7(process.env.NTRP_HOME) : join19(
|
|
9806
|
+
return process.env.NTRP_HOME ? resolve7(process.env.NTRP_HOME) : join19(homedir5(), ".ntrp");
|
|
9479
9807
|
}
|
|
9480
9808
|
function getSessionsDir() {
|
|
9481
9809
|
const dir = join19(ntrpHomeDir(), "sessions");
|
|
@@ -13826,13 +14154,65 @@ __export(spinner_exports, {
|
|
|
13826
14154
|
withSpinner: () => withSpinner
|
|
13827
14155
|
});
|
|
13828
14156
|
import ora from "ora";
|
|
14157
|
+
function clearElapsedTimer(spin) {
|
|
14158
|
+
const state2 = timerStates.get(spin);
|
|
14159
|
+
if (state2?.timer) clearInterval(state2.timer);
|
|
14160
|
+
if (state2) state2.timer = null;
|
|
14161
|
+
spin.suffixText = "";
|
|
14162
|
+
}
|
|
14163
|
+
function startElapsedTimer(spin) {
|
|
14164
|
+
if (!spin.isSpinning || process.stderr.isTTY !== true) return;
|
|
14165
|
+
const state2 = timerStates.get(spin) ?? {
|
|
14166
|
+
startedAt: Date.now(),
|
|
14167
|
+
timer: null,
|
|
14168
|
+
reassured: false
|
|
14169
|
+
};
|
|
14170
|
+
if (state2.timer) return;
|
|
14171
|
+
state2.startedAt = Date.now();
|
|
14172
|
+
state2.reassured = false;
|
|
14173
|
+
spin.suffixText = "(0s)";
|
|
14174
|
+
state2.timer = setInterval(() => {
|
|
14175
|
+
const elapsedSeconds = Math.floor((Date.now() - state2.startedAt) / 1e3);
|
|
14176
|
+
spin.suffixText = `(${elapsedSeconds}s)`;
|
|
14177
|
+
if (!state2.reassured && elapsedSeconds >= STILL_WORKING_AFTER_SECONDS) {
|
|
14178
|
+
state2.reassured = true;
|
|
14179
|
+
spin.clear();
|
|
14180
|
+
process.stderr.write("Still working\u2026\n");
|
|
14181
|
+
spin.render();
|
|
14182
|
+
}
|
|
14183
|
+
}, ELAPSED_TICK_MS);
|
|
14184
|
+
timerStates.set(spin, state2);
|
|
14185
|
+
}
|
|
14186
|
+
function addElapsedTimer(spin) {
|
|
14187
|
+
const start = spin.start.bind(spin);
|
|
14188
|
+
const stop = spin.stop.bind(spin);
|
|
14189
|
+
const stopAndPersist = spin.stopAndPersist.bind(spin);
|
|
14190
|
+
spin.start = (text) => {
|
|
14191
|
+
start(text);
|
|
14192
|
+
startElapsedTimer(spin);
|
|
14193
|
+
return spin;
|
|
14194
|
+
};
|
|
14195
|
+
spin.stop = () => {
|
|
14196
|
+
clearElapsedTimer(spin);
|
|
14197
|
+
stop();
|
|
14198
|
+
return spin;
|
|
14199
|
+
};
|
|
14200
|
+
spin.stopAndPersist = (options) => {
|
|
14201
|
+
clearElapsedTimer(spin);
|
|
14202
|
+
stopAndPersist(options);
|
|
14203
|
+
return spin;
|
|
14204
|
+
};
|
|
14205
|
+
return spin;
|
|
14206
|
+
}
|
|
13829
14207
|
function makeSpinner(text, opts = {}) {
|
|
13830
|
-
return
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
14208
|
+
return addElapsedTimer(
|
|
14209
|
+
ora({
|
|
14210
|
+
text,
|
|
14211
|
+
color: "cyan",
|
|
14212
|
+
indent: opts.indent ?? 2,
|
|
14213
|
+
discardStdin: false
|
|
14214
|
+
})
|
|
14215
|
+
).start();
|
|
13836
14216
|
}
|
|
13837
14217
|
async function withSpinner(text, fn, opts = {}) {
|
|
13838
14218
|
const spin = makeSpinner(text, opts);
|
|
@@ -13848,11 +14228,17 @@ async function withSpinner(text, fn, opts = {}) {
|
|
|
13848
14228
|
if (opts.fail) spin.fail(opts.fail);
|
|
13849
14229
|
else spin.stop();
|
|
13850
14230
|
throw err;
|
|
14231
|
+
} finally {
|
|
14232
|
+
clearElapsedTimer(spin);
|
|
13851
14233
|
}
|
|
13852
14234
|
}
|
|
14235
|
+
var ELAPSED_TICK_MS, STILL_WORKING_AFTER_SECONDS, timerStates;
|
|
13853
14236
|
var init_spinner = __esm({
|
|
13854
14237
|
"src/ui/spinner.ts"() {
|
|
13855
14238
|
"use strict";
|
|
14239
|
+
ELAPSED_TICK_MS = 1e3;
|
|
14240
|
+
STILL_WORKING_AFTER_SECONDS = 15;
|
|
14241
|
+
timerStates = /* @__PURE__ */ new WeakMap();
|
|
13856
14242
|
}
|
|
13857
14243
|
});
|
|
13858
14244
|
|
|
@@ -16258,7 +16644,8 @@ function readVersionFromPackageJson(packageJsonPath) {
|
|
|
16258
16644
|
try {
|
|
16259
16645
|
const pkg = JSON.parse(readFileSync18(packageJsonPath, "utf-8"));
|
|
16260
16646
|
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
16261
|
-
} catch {
|
|
16647
|
+
} catch (err) {
|
|
16648
|
+
debugError("version.readPackageJson", err, packageJsonPath);
|
|
16262
16649
|
}
|
|
16263
16650
|
return null;
|
|
16264
16651
|
}
|
|
@@ -16282,6 +16669,7 @@ var cachedVersion;
|
|
|
16282
16669
|
var init_version = __esm({
|
|
16283
16670
|
"src/version.ts"() {
|
|
16284
16671
|
"use strict";
|
|
16672
|
+
init_diagnostics();
|
|
16285
16673
|
}
|
|
16286
16674
|
});
|
|
16287
16675
|
|
|
@@ -16338,7 +16726,7 @@ function applyUpdateCheckResult(ctx, result) {
|
|
|
16338
16726
|
function startBackgroundUpdateCheck(ctx) {
|
|
16339
16727
|
const pending = checkForUpdate({ force: true, timeoutMs: 5e3 });
|
|
16340
16728
|
ctx.pendingUpdateCheck = pending;
|
|
16341
|
-
void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch(() =>
|
|
16729
|
+
void pending.then((result) => applyUpdateCheckResult(ctx, result)).catch((err) => debugError("update.backgroundCheck", err));
|
|
16342
16730
|
}
|
|
16343
16731
|
async function fetchLatestVersion(timeoutMs = 5e3) {
|
|
16344
16732
|
try {
|
|
@@ -16346,7 +16734,8 @@ async function fetchLatestVersion(timeoutMs = 5e3) {
|
|
|
16346
16734
|
if (!res.ok) return null;
|
|
16347
16735
|
const data = await res.json();
|
|
16348
16736
|
return typeof data.version === "string" && data.version.length > 0 ? data.version : null;
|
|
16349
|
-
} catch {
|
|
16737
|
+
} catch (err) {
|
|
16738
|
+
debugError("update.fetchLatestVersion", err);
|
|
16350
16739
|
return null;
|
|
16351
16740
|
}
|
|
16352
16741
|
}
|
|
@@ -16381,6 +16770,7 @@ var init_registry = __esm({
|
|
|
16381
16770
|
"use strict";
|
|
16382
16771
|
init_update_check();
|
|
16383
16772
|
init_version();
|
|
16773
|
+
init_diagnostics();
|
|
16384
16774
|
NPM_PACKAGE = "@sonnechasser/ntrp";
|
|
16385
16775
|
}
|
|
16386
16776
|
});
|
|
@@ -17229,6 +17619,369 @@ var init_gap_card = __esm({
|
|
|
17229
17619
|
}
|
|
17230
17620
|
});
|
|
17231
17621
|
|
|
17622
|
+
// src/conversation/onboard-tiers.ts
|
|
17623
|
+
var onboard_tiers_exports = {};
|
|
17624
|
+
__export(onboard_tiers_exports, {
|
|
17625
|
+
ONBOARD_TIER_ORDER: () => ONBOARD_TIER_ORDER,
|
|
17626
|
+
canRunDomainTier: () => canRunDomainTier,
|
|
17627
|
+
clearOnboardTierFlags: () => clearOnboardTierFlags,
|
|
17628
|
+
describeOnboardTier: () => describeOnboardTier,
|
|
17629
|
+
getOnboardTierFlag: () => getOnboardTierFlag,
|
|
17630
|
+
getOnboardTierStatus: () => getOnboardTierStatus,
|
|
17631
|
+
hasProductionDataset: () => hasProductionDataset,
|
|
17632
|
+
listCompletedOnboardTier: () => listCompletedOnboardTier,
|
|
17633
|
+
markDemoDataSeen: () => markDemoDataSeen,
|
|
17634
|
+
markOnboardTierComplete: () => markOnboardTierComplete,
|
|
17635
|
+
markProductionDataSeen: () => markProductionDataSeen,
|
|
17636
|
+
pathLooksPresent: () => pathLooksPresent,
|
|
17637
|
+
resetOnboardTierProgress: () => resetOnboardTierProgress,
|
|
17638
|
+
resolveNextOnboardTier: () => resolveNextOnboardTier
|
|
17639
|
+
});
|
|
17640
|
+
import { existsSync as existsSync24, statSync as statSync4 } from "fs";
|
|
17641
|
+
function flagSet(tier) {
|
|
17642
|
+
return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
|
|
17643
|
+
}
|
|
17644
|
+
function getOnboardTierFlag(tier) {
|
|
17645
|
+
return getConfigValue(TIER_CONFIG_KEYS[tier]);
|
|
17646
|
+
}
|
|
17647
|
+
function markOnboardTierComplete(...tiers) {
|
|
17648
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
17649
|
+
for (const tier of tiers) {
|
|
17650
|
+
if (!flagSet(tier)) setConfigValue(TIER_CONFIG_KEYS[tier], at);
|
|
17651
|
+
}
|
|
17652
|
+
}
|
|
17653
|
+
function clearOnboardTierFlags(...tiers) {
|
|
17654
|
+
for (const tier of tiers) {
|
|
17655
|
+
deleteConfigValue(TIER_CONFIG_KEYS[tier]);
|
|
17656
|
+
}
|
|
17657
|
+
}
|
|
17658
|
+
function resetOnboardTierProgress() {
|
|
17659
|
+
clearOnboardTierFlags(...ONBOARD_TIER_ORDER);
|
|
17660
|
+
}
|
|
17661
|
+
function hasProductionDataset(ctx) {
|
|
17662
|
+
const source = ctx?.dataset?.source;
|
|
17663
|
+
if (source && !source.startsWith("demo:")) {
|
|
17664
|
+
if (source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source) || source.startsWith("~")) {
|
|
17665
|
+
return true;
|
|
17666
|
+
}
|
|
17667
|
+
if (!source.includes(":") && existsSync24(source)) return true;
|
|
17668
|
+
if (source.startsWith("csv:") || source.startsWith("file:") || source.startsWith("folder:")) {
|
|
17669
|
+
return true;
|
|
17670
|
+
}
|
|
17671
|
+
}
|
|
17672
|
+
if (ctx?.attachments && ctx.attachments.length > 0) return true;
|
|
17673
|
+
return flagSet("production");
|
|
17674
|
+
}
|
|
17675
|
+
function hasDemoExperience(ctx) {
|
|
17676
|
+
if (flagSet("demo")) return true;
|
|
17677
|
+
if (getPreferredDemoScenario()) return true;
|
|
17678
|
+
const source = ctx?.dataset?.source;
|
|
17679
|
+
if (source?.startsWith("demo:")) return true;
|
|
17680
|
+
return false;
|
|
17681
|
+
}
|
|
17682
|
+
function listCompletedOnboardTier(ctx) {
|
|
17683
|
+
const done = [];
|
|
17684
|
+
const profileOk = flagSet("profile") || isProfileConfigured(loadProfile());
|
|
17685
|
+
if (profileOk) done.push("profile");
|
|
17686
|
+
else return done;
|
|
17687
|
+
if (flagSet("domain")) done.push("domain");
|
|
17688
|
+
else return done;
|
|
17689
|
+
if (hasDemoExperience(ctx)) done.push("demo");
|
|
17690
|
+
else return done;
|
|
17691
|
+
if (hasProductionDataset(ctx) || flagSet("production")) done.push("production");
|
|
17692
|
+
return done;
|
|
17693
|
+
}
|
|
17694
|
+
function resolveNextOnboardTier(ctx) {
|
|
17695
|
+
const done = new Set(listCompletedOnboardTier(ctx));
|
|
17696
|
+
for (const tier of ONBOARD_TIER_ORDER) {
|
|
17697
|
+
if (!done.has(tier)) return tier;
|
|
17698
|
+
}
|
|
17699
|
+
return null;
|
|
17700
|
+
}
|
|
17701
|
+
function getOnboardTierStatus(ctx) {
|
|
17702
|
+
const completed = listCompletedOnboardTier(ctx);
|
|
17703
|
+
const next = resolveNextOnboardTier(ctx);
|
|
17704
|
+
const meta = next ? TIER_META[next] : null;
|
|
17705
|
+
return {
|
|
17706
|
+
completed,
|
|
17707
|
+
next,
|
|
17708
|
+
nextLabel: meta?.label ?? null,
|
|
17709
|
+
nextHint: meta?.hint ?? null
|
|
17710
|
+
};
|
|
17711
|
+
}
|
|
17712
|
+
function describeOnboardTier(tier) {
|
|
17713
|
+
return TIER_META[tier];
|
|
17714
|
+
}
|
|
17715
|
+
function canRunDomainTier() {
|
|
17716
|
+
return isProfileConfigured(loadProfile()) && hasAnyLlmProvider();
|
|
17717
|
+
}
|
|
17718
|
+
function markProductionDataSeen() {
|
|
17719
|
+
markOnboardTierComplete("production");
|
|
17720
|
+
}
|
|
17721
|
+
function markDemoDataSeen() {
|
|
17722
|
+
markOnboardTierComplete("demo");
|
|
17723
|
+
}
|
|
17724
|
+
function pathLooksPresent(raw) {
|
|
17725
|
+
try {
|
|
17726
|
+
return existsSync24(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
|
|
17727
|
+
} catch {
|
|
17728
|
+
return false;
|
|
17729
|
+
}
|
|
17730
|
+
}
|
|
17731
|
+
var ONBOARD_TIER_ORDER, TIER_CONFIG_KEYS, TIER_META;
|
|
17732
|
+
var init_onboard_tiers = __esm({
|
|
17733
|
+
"src/conversation/onboard-tiers.ts"() {
|
|
17734
|
+
"use strict";
|
|
17735
|
+
init_store();
|
|
17736
|
+
init_profile();
|
|
17737
|
+
init_repl_api();
|
|
17738
|
+
init_scenario_fit();
|
|
17739
|
+
ONBOARD_TIER_ORDER = [
|
|
17740
|
+
"profile",
|
|
17741
|
+
"domain",
|
|
17742
|
+
"demo",
|
|
17743
|
+
"production"
|
|
17744
|
+
];
|
|
17745
|
+
TIER_CONFIG_KEYS = {
|
|
17746
|
+
profile: "onboard-tier-profile",
|
|
17747
|
+
domain: "onboard-tier-domain",
|
|
17748
|
+
demo: "onboard-tier-demo",
|
|
17749
|
+
production: "onboard-tier-production"
|
|
17750
|
+
};
|
|
17751
|
+
TIER_META = {
|
|
17752
|
+
profile: {
|
|
17753
|
+
label: "Company profile",
|
|
17754
|
+
hint: "Name, industry, ICP \u2014 works without an API key"
|
|
17755
|
+
},
|
|
17756
|
+
domain: {
|
|
17757
|
+
label: "Domain research",
|
|
17758
|
+
hint: "Connect a key and let NTRP research your company"
|
|
17759
|
+
},
|
|
17760
|
+
demo: {
|
|
17761
|
+
label: "Sample data",
|
|
17762
|
+
hint: "Load a fitted demo book of business"
|
|
17763
|
+
},
|
|
17764
|
+
production: {
|
|
17765
|
+
label: "Your data",
|
|
17766
|
+
hint: "Drag-drop a CSV or folder path into the REPL"
|
|
17767
|
+
}
|
|
17768
|
+
};
|
|
17769
|
+
}
|
|
17770
|
+
});
|
|
17771
|
+
|
|
17772
|
+
// src/conversation/teaching-suggestions.ts
|
|
17773
|
+
function hasPriorExploreExchange() {
|
|
17774
|
+
return (getUsageStats().nl_exchanges ?? 0) > 0;
|
|
17775
|
+
}
|
|
17776
|
+
function shouldShowTeachingSuggestions(ctx) {
|
|
17777
|
+
if (hasPriorExploreExchange()) return false;
|
|
17778
|
+
if (hasProductionDataset(ctx)) return false;
|
|
17779
|
+
return true;
|
|
17780
|
+
}
|
|
17781
|
+
var init_teaching_suggestions = __esm({
|
|
17782
|
+
"src/conversation/teaching-suggestions.ts"() {
|
|
17783
|
+
"use strict";
|
|
17784
|
+
init_usage_stats();
|
|
17785
|
+
init_onboard_tiers();
|
|
17786
|
+
}
|
|
17787
|
+
});
|
|
17788
|
+
|
|
17789
|
+
// src/conversation/suggested-asks.ts
|
|
17790
|
+
function clip(s, maxLen) {
|
|
17791
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
17792
|
+
if (t.length <= maxLen) return t;
|
|
17793
|
+
return t.slice(0, Math.max(0, maxLen - 1)).trimEnd() + "\u2026";
|
|
17794
|
+
}
|
|
17795
|
+
function money(vs) {
|
|
17796
|
+
if (vs.dollar_value == null || vs.dollar_value <= 0) return null;
|
|
17797
|
+
return formatCurrency(vs.dollar_value);
|
|
17798
|
+
}
|
|
17799
|
+
function askForVital(vs) {
|
|
17800
|
+
const label = VITAL_SIGN_LABELS[vs.vital_sign] ?? vs.vital_sign;
|
|
17801
|
+
const $ = money(vs);
|
|
17802
|
+
switch (vs.vital_sign) {
|
|
17803
|
+
case "freshness":
|
|
17804
|
+
return $ ? `Which accounts hold ${$} in stale pipeline?` : `What's driving Freshness at ${Math.round(vs.score)}?`;
|
|
17805
|
+
case "flow_rate":
|
|
17806
|
+
return $ ? `Which deals make up ${$} stuck in pipeline?` : `Which deals are stuck the longest?`;
|
|
17807
|
+
case "drop_rate":
|
|
17808
|
+
return $ ? `Where do we lose an estimated ${$} at handoff?` : `Where are we losing leads in the handoff?`;
|
|
17809
|
+
case "signal_to_noise":
|
|
17810
|
+
return $ ? `Where is ${$} of misdirected effort going?` : `Where is effort going that isn't tied to pipeline?`;
|
|
17811
|
+
case "thread_depth":
|
|
17812
|
+
return $ ? `Which deals make up ${$} of single-threaded risk?` : `Which large deals are single-threaded?`;
|
|
17813
|
+
default:
|
|
17814
|
+
return `What's behind ${label} at ${Math.round(vs.score)}?`;
|
|
17815
|
+
}
|
|
17816
|
+
}
|
|
17817
|
+
function secondPressure(health, gating) {
|
|
17818
|
+
const ranked = [...health.vital_signs].filter((v) => v.vital_sign !== gating).sort((a, b) => {
|
|
17819
|
+
const aBad = a.status === "red" ? 0 : a.status === "yellow" ? 1 : 2;
|
|
17820
|
+
const bBad = b.status === "red" ? 0 : b.status === "yellow" ? 1 : 2;
|
|
17821
|
+
if (aBad !== bBad) return aBad - bBad;
|
|
17822
|
+
return a.score - b.score;
|
|
17823
|
+
});
|
|
17824
|
+
return ranked[0] ?? null;
|
|
17825
|
+
}
|
|
17826
|
+
function playAsk(gating) {
|
|
17827
|
+
const play = getPlaysForVitalSign(gating)[0];
|
|
17828
|
+
if (!play) return null;
|
|
17829
|
+
const label = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
17830
|
+
return `Would "${play.name}" help with ${label}?`;
|
|
17831
|
+
}
|
|
17832
|
+
function scopeAsk(summary, maxLen) {
|
|
17833
|
+
if (!summary) return null;
|
|
17834
|
+
const cleaned = summary.replace(/\s+/g, " ").trim();
|
|
17835
|
+
if (cleaned.length < 8) return null;
|
|
17836
|
+
if (/\?$/.test(cleaned)) return clip(cleaned, maxLen);
|
|
17837
|
+
return clip(`Given our focus \u2014 ${cleaned} \u2014 what matters most?`, maxLen);
|
|
17838
|
+
}
|
|
17839
|
+
function findingAsk(findings, maxLen) {
|
|
17840
|
+
const f = findings?.find((x) => x.finding?.trim());
|
|
17841
|
+
if (!f?.finding) return null;
|
|
17842
|
+
const head = f.finding.replace(/\s+/g, " ").trim();
|
|
17843
|
+
const short = head.length > 48 ? `${head.slice(0, 45).trimEnd()}\u2026` : head;
|
|
17844
|
+
return clip(`What's the so-what on "${short}"?`, maxLen);
|
|
17845
|
+
}
|
|
17846
|
+
function uniqueAsks(asks, limit, maxLen) {
|
|
17847
|
+
const out = [];
|
|
17848
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17849
|
+
for (const raw of asks) {
|
|
17850
|
+
const q = clip(raw, maxLen);
|
|
17851
|
+
if (!q) continue;
|
|
17852
|
+
const key = q.toLowerCase();
|
|
17853
|
+
if (seen.has(key)) continue;
|
|
17854
|
+
seen.add(key);
|
|
17855
|
+
out.push(q);
|
|
17856
|
+
if (out.length >= limit) break;
|
|
17857
|
+
}
|
|
17858
|
+
return out;
|
|
17859
|
+
}
|
|
17860
|
+
function buildGtmSuggestedAsks(health, opts = {}) {
|
|
17861
|
+
const maxLen = opts.maxLen ?? DEFAULT_MAX;
|
|
17862
|
+
const gating = health.gating_vital_sign;
|
|
17863
|
+
const gatingVs = health.vital_signs.find((v) => v.vital_sign === gating) ?? health.vital_signs[0];
|
|
17864
|
+
const candidates = [];
|
|
17865
|
+
if (gatingVs) candidates.push(askForVital(gatingVs));
|
|
17866
|
+
const second = secondPressure(health, gating);
|
|
17867
|
+
if (second) candidates.push(askForVital(second));
|
|
17868
|
+
const scoped = scopeAsk(opts.scopeSummary, maxLen);
|
|
17869
|
+
if (scoped) candidates.push(scoped);
|
|
17870
|
+
const fromFinding = findingAsk(opts.findings, maxLen);
|
|
17871
|
+
if (fromFinding) candidates.push(fromFinding);
|
|
17872
|
+
const play = playAsk(gating);
|
|
17873
|
+
if (play) candidates.push(play);
|
|
17874
|
+
const gateLabel = VITAL_SIGN_LABELS[gating] ?? gating;
|
|
17875
|
+
const gate$ = gatingVs ? money(gatingVs) : null;
|
|
17876
|
+
candidates.push(
|
|
17877
|
+
gate$ ? `What should I fix first given ${gateLabel} is gating (${gate$})?` : `What should I fix first given ${gateLabel} is gating?`
|
|
17878
|
+
);
|
|
17879
|
+
if (health.total_value_at_risk != null && health.total_value_at_risk > 0) {
|
|
17880
|
+
candidates.push(
|
|
17881
|
+
`Where does the ${formatCurrency(health.total_value_at_risk)} at risk concentrate?`
|
|
17882
|
+
);
|
|
17883
|
+
}
|
|
17884
|
+
return uniqueAsks(candidates, 3, maxLen);
|
|
17885
|
+
}
|
|
17886
|
+
function buildMetricsSuggestedAsks(insightAsks, headline, opts = {}) {
|
|
17887
|
+
const maxLen = opts.maxLen ?? DEFAULT_MAX;
|
|
17888
|
+
const candidates = [...insightAsks.filter(Boolean)];
|
|
17889
|
+
const byKey = (key) => headline?.find((h) => h.metric === key);
|
|
17890
|
+
const nrr = byKey("nrr");
|
|
17891
|
+
const arr = byKey("arr");
|
|
17892
|
+
const coverage = byKey("pipeline_coverage");
|
|
17893
|
+
const win = byKey("win_rate");
|
|
17894
|
+
const grr = byKey("grr");
|
|
17895
|
+
if (nrr?.formatted) candidates.push(`Why is NRR at ${nrr.formatted}?`);
|
|
17896
|
+
if (grr?.formatted && grr.formatted !== nrr?.formatted) {
|
|
17897
|
+
candidates.push(`Is GRR at ${grr.formatted} telling the real retention story?`);
|
|
17898
|
+
}
|
|
17899
|
+
if (arr?.formatted) candidates.push(`Which deals drove ARR to ${arr.formatted}?`);
|
|
17900
|
+
if (coverage?.formatted) {
|
|
17901
|
+
candidates.push(`Is pipeline coverage of ${coverage.formatted} realistic?`);
|
|
17902
|
+
}
|
|
17903
|
+
if (win?.formatted) candidates.push(`How reliable is a ${win.formatted} win rate here?`);
|
|
17904
|
+
const scoped = scopeAsk(opts.scopeSummary, maxLen);
|
|
17905
|
+
if (scoped) candidates.push(scoped);
|
|
17906
|
+
candidates.push("What should I fix first in the revenue picture?");
|
|
17907
|
+
return uniqueAsks(candidates, 3, maxLen);
|
|
17908
|
+
}
|
|
17909
|
+
function resolveSuggestedAsks(ctx, justCompleted, suggestedAsks, health, insightAsks) {
|
|
17910
|
+
if (suggestedAsks && suggestedAsks.length > 0) {
|
|
17911
|
+
return uniqueAsks(suggestedAsks, 3, DEFAULT_MAX);
|
|
17912
|
+
}
|
|
17913
|
+
const scopeSummary = ctx.scope?.intent_summary;
|
|
17914
|
+
if (justCompleted === "revenue_metrics") {
|
|
17915
|
+
return buildMetricsSuggestedAsks(insightAsks ?? [], ctx.analysis.headline, {
|
|
17916
|
+
scopeSummary
|
|
17917
|
+
});
|
|
17918
|
+
}
|
|
17919
|
+
const fromSnapshot = health ?? ctx.snapshot.computeResult?.aggregate ?? null;
|
|
17920
|
+
if (fromSnapshot) {
|
|
17921
|
+
return buildGtmSuggestedAsks(fromSnapshot, { scopeSummary });
|
|
17922
|
+
}
|
|
17923
|
+
return [...FALLBACK_GTM_ASKS];
|
|
17924
|
+
}
|
|
17925
|
+
function asksToGhostHints(asks) {
|
|
17926
|
+
return asks.map((q) => {
|
|
17927
|
+
const bare = q.replace(/^try\s+/i, "").replace(/^"|"$/g, "");
|
|
17928
|
+
return `try "${bare}"`;
|
|
17929
|
+
});
|
|
17930
|
+
}
|
|
17931
|
+
function dedupeGhosts(hints) {
|
|
17932
|
+
const out = [];
|
|
17933
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17934
|
+
for (const h of hints) {
|
|
17935
|
+
const key = h.toLowerCase();
|
|
17936
|
+
if (seen.has(key)) continue;
|
|
17937
|
+
seen.add(key);
|
|
17938
|
+
out.push(h);
|
|
17939
|
+
}
|
|
17940
|
+
return out;
|
|
17941
|
+
}
|
|
17942
|
+
function buildExploreTeachingGhosts(ctx, staticExplore) {
|
|
17943
|
+
const cached2 = ctx.teachingAskHints;
|
|
17944
|
+
if (cached2 && cached2.length > 0) {
|
|
17945
|
+
const ghosts = asksToGhostHints(cached2);
|
|
17946
|
+
const gating = ctx.snapshot.computeResult?.aggregate.gating_vital_sign;
|
|
17947
|
+
if (gating) ghosts.push(`try /deepdive ${gating}`);
|
|
17948
|
+
ghosts.push('try "how should we fix this?"');
|
|
17949
|
+
return dedupeGhosts(ghosts);
|
|
17950
|
+
}
|
|
17951
|
+
const health = ctx.snapshot.computeResult?.aggregate;
|
|
17952
|
+
if (health) {
|
|
17953
|
+
const asks = buildGtmSuggestedAsks(health, {
|
|
17954
|
+
scopeSummary: ctx.scope?.intent_summary
|
|
17955
|
+
});
|
|
17956
|
+
return dedupeGhosts([
|
|
17957
|
+
...asksToGhostHints(asks),
|
|
17958
|
+
`try /deepdive ${health.gating_vital_sign}`,
|
|
17959
|
+
'try "how should we fix this?"'
|
|
17960
|
+
]);
|
|
17961
|
+
}
|
|
17962
|
+
if (ctx.analysis.completed.includes("revenue_metrics") && ctx.analysis.headline?.length) {
|
|
17963
|
+
const asks = buildMetricsSuggestedAsks([], ctx.analysis.headline, {
|
|
17964
|
+
scopeSummary: ctx.scope?.intent_summary
|
|
17965
|
+
});
|
|
17966
|
+
return dedupeGhosts([...asksToGhostHints(asks), 'try "how should we fix this?"']);
|
|
17967
|
+
}
|
|
17968
|
+
return staticExplore;
|
|
17969
|
+
}
|
|
17970
|
+
var DEFAULT_MAX, FALLBACK_GTM_ASKS;
|
|
17971
|
+
var init_suggested_asks = __esm({
|
|
17972
|
+
"src/conversation/suggested-asks.ts"() {
|
|
17973
|
+
"use strict";
|
|
17974
|
+
init_formatters();
|
|
17975
|
+
init_playbook();
|
|
17976
|
+
DEFAULT_MAX = 72;
|
|
17977
|
+
FALLBACK_GTM_ASKS = [
|
|
17978
|
+
"Which deals are stuck the longest?",
|
|
17979
|
+
"Where are we losing leads in the handoff?",
|
|
17980
|
+
"What should I fix first?"
|
|
17981
|
+
];
|
|
17982
|
+
}
|
|
17983
|
+
});
|
|
17984
|
+
|
|
17232
17985
|
// src/metrics/companion.ts
|
|
17233
17986
|
import chalk13 from "chalk";
|
|
17234
17987
|
function getCompanionRecommendation(input) {
|
|
@@ -17271,23 +18024,26 @@ function printCompanionBanner(invoked, primary) {
|
|
|
17271
18024
|
}
|
|
17272
18025
|
}
|
|
17273
18026
|
function printCompanionFooter(ctx, companion, options = { justCompleted: "gtm_health" }) {
|
|
17274
|
-
const { justCompleted, suggestedAsks } = options;
|
|
18027
|
+
const { justCompleted, suggestedAsks, health, insightAsks } = options;
|
|
17275
18028
|
const completed = new Set(ctx.analysis.completed);
|
|
17276
18029
|
printAnalysisComplete(justCompleted);
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
|
|
17280
|
-
|
|
17281
|
-
|
|
17282
|
-
|
|
17283
|
-
|
|
17284
|
-
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
18030
|
+
const showTeaching = shouldShowTeachingSuggestions(ctx);
|
|
18031
|
+
if (showTeaching) {
|
|
18032
|
+
const asks = resolveSuggestedAsks(
|
|
18033
|
+
ctx,
|
|
18034
|
+
justCompleted,
|
|
18035
|
+
suggestedAsks,
|
|
18036
|
+
health,
|
|
18037
|
+
insightAsks
|
|
18038
|
+
);
|
|
18039
|
+
ctx.teachingAskHints = asks;
|
|
18040
|
+
console.log();
|
|
18041
|
+
console.log(" " + chalk13.bold("Try asking"));
|
|
18042
|
+
for (const q of asks) {
|
|
18043
|
+
console.log(chalk13.dim(` "${q}"`));
|
|
18044
|
+
}
|
|
18045
|
+
} else {
|
|
18046
|
+
ctx.teachingAskHints = void 0;
|
|
17291
18047
|
}
|
|
17292
18048
|
console.log();
|
|
17293
18049
|
const extras = [];
|
|
@@ -17322,6 +18078,8 @@ var init_companion = __esm({
|
|
|
17322
18078
|
"src/metrics/companion.ts"() {
|
|
17323
18079
|
"use strict";
|
|
17324
18080
|
init_theme();
|
|
18081
|
+
init_teaching_suggestions();
|
|
18082
|
+
init_suggested_asks();
|
|
17325
18083
|
}
|
|
17326
18084
|
});
|
|
17327
18085
|
|
|
@@ -18583,7 +19341,7 @@ __export(play_outcomes_exports, {
|
|
|
18583
19341
|
listPlayOutcomes: () => listPlayOutcomes,
|
|
18584
19342
|
recordPlayOutcomes: () => recordPlayOutcomes
|
|
18585
19343
|
});
|
|
18586
|
-
import { existsSync as
|
|
19344
|
+
import { existsSync as existsSync25, readFileSync as readFileSync20, appendFileSync as appendFileSync6 } from "fs";
|
|
18587
19345
|
import { join as join24 } from "path";
|
|
18588
19346
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
18589
19347
|
function outcomesPath() {
|
|
@@ -18591,14 +19349,15 @@ function outcomesPath() {
|
|
|
18591
19349
|
}
|
|
18592
19350
|
function listPlayOutcomes() {
|
|
18593
19351
|
const path = outcomesPath();
|
|
18594
|
-
if (!
|
|
19352
|
+
if (!existsSync25(path)) return [];
|
|
18595
19353
|
const out = [];
|
|
18596
19354
|
for (const line of readFileSync20(path, "utf-8").split("\n")) {
|
|
18597
19355
|
const trimmed = line.trim();
|
|
18598
19356
|
if (!trimmed) continue;
|
|
18599
19357
|
try {
|
|
18600
19358
|
out.push(JSON.parse(trimmed));
|
|
18601
|
-
} catch {
|
|
19359
|
+
} catch (err) {
|
|
19360
|
+
debugError("memory.playOutcomes.load", err, "skipped malformed line");
|
|
18602
19361
|
}
|
|
18603
19362
|
}
|
|
18604
19363
|
return out;
|
|
@@ -18638,7 +19397,8 @@ function recordPlayOutcomes(strategy, outcomes, batchId) {
|
|
|
18638
19397
|
try {
|
|
18639
19398
|
appendFileSync6(outcomesPath(), JSON.stringify(record) + "\n");
|
|
18640
19399
|
written++;
|
|
18641
|
-
} catch {
|
|
19400
|
+
} catch (err) {
|
|
19401
|
+
debugError("memory.playOutcomes.append", err);
|
|
18642
19402
|
}
|
|
18643
19403
|
}
|
|
18644
19404
|
}
|
|
@@ -18670,6 +19430,7 @@ var init_play_outcomes = __esm({
|
|
|
18670
19430
|
"src/memory/play-outcomes.ts"() {
|
|
18671
19431
|
"use strict";
|
|
18672
19432
|
init_store();
|
|
19433
|
+
init_diagnostics();
|
|
18673
19434
|
OUTCOMES_FILE = "play_outcomes.jsonl";
|
|
18674
19435
|
}
|
|
18675
19436
|
});
|
|
@@ -21863,7 +22624,9 @@ async function connectCustomEndpoint(opts) {
|
|
|
21863
22624
|
const result = await fetchProviderModels(spec, opts.key);
|
|
21864
22625
|
if (!result.ok) {
|
|
21865
22626
|
if (result.status === 0) {
|
|
21866
|
-
throw new ConnectError(
|
|
22627
|
+
throw new ConnectError(
|
|
22628
|
+
`Couldn't reach ${baseUrl}${result.error ? ` (${result.error})` : ""} \u2014 check the URL (expects an OpenAI-compatible /models endpoint).`
|
|
22629
|
+
);
|
|
21867
22630
|
}
|
|
21868
22631
|
if (result.status === 401 || result.status === 403) {
|
|
21869
22632
|
throw new ConnectError(
|
|
@@ -22404,6 +23167,33 @@ var init_divergence = __esm({
|
|
|
22404
23167
|
}
|
|
22405
23168
|
});
|
|
22406
23169
|
|
|
23170
|
+
// src/vitals/context-snapshot.ts
|
|
23171
|
+
function cacheHealthSnapshot(ctx, snapshot) {
|
|
23172
|
+
ctx.snapshot.computeResult = snapshot;
|
|
23173
|
+
ctx.snapshot.divergences = detectDivergences(
|
|
23174
|
+
snapshot.aggregate,
|
|
23175
|
+
snapshot.segments.map((segment) => ({
|
|
23176
|
+
segmentId: segment.segment.id,
|
|
23177
|
+
segmentName: segment.segment.name,
|
|
23178
|
+
result: segment.result
|
|
23179
|
+
}))
|
|
23180
|
+
).divergences;
|
|
23181
|
+
return snapshot;
|
|
23182
|
+
}
|
|
23183
|
+
async function computeAndCacheHealthSnapshot(ctx) {
|
|
23184
|
+
return cacheHealthSnapshot(ctx, await computeFullHealth());
|
|
23185
|
+
}
|
|
23186
|
+
async function ensureHealthSnapshot(ctx) {
|
|
23187
|
+
return ctx.snapshot.computeResult ?? computeAndCacheHealthSnapshot(ctx);
|
|
23188
|
+
}
|
|
23189
|
+
var init_context_snapshot = __esm({
|
|
23190
|
+
"src/vitals/context-snapshot.ts"() {
|
|
23191
|
+
"use strict";
|
|
23192
|
+
init_divergence();
|
|
23193
|
+
init_health_score();
|
|
23194
|
+
}
|
|
23195
|
+
});
|
|
23196
|
+
|
|
22407
23197
|
// src/services/strategist.ts
|
|
22408
23198
|
import { createHash as createHash2 } from "crypto";
|
|
22409
23199
|
function serializeGapAudit(audit) {
|
|
@@ -22420,20 +23210,16 @@ function serializeGapAudit(audit) {
|
|
|
22420
23210
|
return lines.join("\n");
|
|
22421
23211
|
}
|
|
22422
23212
|
async function prepareStrategistInputs(ctx, objective) {
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
segmentId: s.segment.id,
|
|
22429
|
-
segmentName: s.segment.name,
|
|
22430
|
-
result: s.result
|
|
22431
|
-
}));
|
|
22432
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
22433
|
-
}
|
|
22434
|
-
const audit = ctx.gapAudit ?? await refreshGapAudit(ctx).catch(() => null);
|
|
23213
|
+
const snapshot = await ensureHealthSnapshot(ctx);
|
|
23214
|
+
const audit = ctx.gapAudit ?? await refreshGapAudit(ctx).catch((err) => {
|
|
23215
|
+
debugError("strategist.refreshGapAudit", err);
|
|
23216
|
+
return null;
|
|
23217
|
+
});
|
|
22435
23218
|
const gapAuditBlock = audit ? serializeGapAudit(audit) : "";
|
|
22436
|
-
const memoryBlock = await Promise.resolve().then(() => (init_store2(), store_exports2)).then((m) => m.buildMemoryBlock(objective)).catch(() =>
|
|
23219
|
+
const memoryBlock = await Promise.resolve().then(() => (init_store2(), store_exports2)).then((m) => m.buildMemoryBlock(objective)).catch((err) => {
|
|
23220
|
+
debugError("strategist.buildMemoryBlock", err);
|
|
23221
|
+
return "";
|
|
23222
|
+
});
|
|
22437
23223
|
let baselineBatchId = null;
|
|
22438
23224
|
try {
|
|
22439
23225
|
const reading = await getLatestHealthReading();
|
|
@@ -22543,13 +23329,13 @@ var init_strategist = __esm({
|
|
|
22543
23329
|
"use strict";
|
|
22544
23330
|
init_schema();
|
|
22545
23331
|
init_queries();
|
|
22546
|
-
|
|
22547
|
-
init_divergence();
|
|
23332
|
+
init_context_snapshot();
|
|
22548
23333
|
init_gap_audit();
|
|
22549
23334
|
init_library();
|
|
22550
23335
|
init_errors2();
|
|
22551
23336
|
init_types2();
|
|
22552
23337
|
init_formatters();
|
|
23338
|
+
init_diagnostics();
|
|
22553
23339
|
}
|
|
22554
23340
|
});
|
|
22555
23341
|
|
|
@@ -24658,84 +25444,7 @@ var init_strategist_prompt = __esm({
|
|
|
24658
25444
|
}
|
|
24659
25445
|
});
|
|
24660
25446
|
|
|
24661
|
-
// src/ai/json-response.ts
|
|
24662
|
-
function stripJsonFences(text) {
|
|
24663
|
-
const trimmed = text.trim();
|
|
24664
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
24665
|
-
if (fenced) return fenced[1].trim();
|
|
24666
|
-
return trimmed.replace(/```(?:json)?\s*/gi, "").replace(/```/g, "").trim();
|
|
24667
|
-
}
|
|
24668
|
-
function parseJsonArrayFromText(text) {
|
|
24669
|
-
const cleaned = stripJsonFences(text);
|
|
24670
|
-
const start = cleaned.indexOf("[");
|
|
24671
|
-
if (start === -1) return null;
|
|
24672
|
-
let depth = 0;
|
|
24673
|
-
let end = -1;
|
|
24674
|
-
for (let i = start; i < cleaned.length; i++) {
|
|
24675
|
-
if (cleaned[i] === "[") depth++;
|
|
24676
|
-
else if (cleaned[i] === "]") {
|
|
24677
|
-
depth--;
|
|
24678
|
-
if (depth === 0) {
|
|
24679
|
-
end = i;
|
|
24680
|
-
break;
|
|
24681
|
-
}
|
|
24682
|
-
}
|
|
24683
|
-
}
|
|
24684
|
-
if (end === -1) return null;
|
|
24685
|
-
try {
|
|
24686
|
-
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
24687
|
-
return Array.isArray(parsed) ? parsed : null;
|
|
24688
|
-
} catch {
|
|
24689
|
-
return null;
|
|
24690
|
-
}
|
|
24691
|
-
}
|
|
24692
|
-
var init_json_response = __esm({
|
|
24693
|
-
"src/ai/json-response.ts"() {
|
|
24694
|
-
"use strict";
|
|
24695
|
-
}
|
|
24696
|
-
});
|
|
24697
|
-
|
|
24698
25447
|
// src/ai/strategist-validate.ts
|
|
24699
|
-
function parseJsonObjectFromText(text) {
|
|
24700
|
-
const cleaned = stripJsonFences(text);
|
|
24701
|
-
const start = cleaned.indexOf("{");
|
|
24702
|
-
if (start === -1) return null;
|
|
24703
|
-
let depth = 0;
|
|
24704
|
-
let inString = false;
|
|
24705
|
-
let escaped = false;
|
|
24706
|
-
let end = -1;
|
|
24707
|
-
for (let i = start; i < cleaned.length; i++) {
|
|
24708
|
-
const ch = cleaned[i];
|
|
24709
|
-
if (escaped) {
|
|
24710
|
-
escaped = false;
|
|
24711
|
-
continue;
|
|
24712
|
-
}
|
|
24713
|
-
if (ch === "\\") {
|
|
24714
|
-
if (inString) escaped = true;
|
|
24715
|
-
continue;
|
|
24716
|
-
}
|
|
24717
|
-
if (ch === '"') {
|
|
24718
|
-
inString = !inString;
|
|
24719
|
-
continue;
|
|
24720
|
-
}
|
|
24721
|
-
if (inString) continue;
|
|
24722
|
-
if (ch === "{") depth++;
|
|
24723
|
-
else if (ch === "}") {
|
|
24724
|
-
depth--;
|
|
24725
|
-
if (depth === 0) {
|
|
24726
|
-
end = i;
|
|
24727
|
-
break;
|
|
24728
|
-
}
|
|
24729
|
-
}
|
|
24730
|
-
}
|
|
24731
|
-
if (end === -1) return null;
|
|
24732
|
-
try {
|
|
24733
|
-
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
24734
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
24735
|
-
} catch {
|
|
24736
|
-
return null;
|
|
24737
|
-
}
|
|
24738
|
-
}
|
|
24739
25448
|
function extractNumbers(text) {
|
|
24740
25449
|
const out = [];
|
|
24741
25450
|
for (const match of text.matchAll(NUMBER_RE)) {
|
|
@@ -25019,31 +25728,34 @@ function buildGroundedFallbackPlan(input) {
|
|
|
25019
25728
|
});
|
|
25020
25729
|
const workstreams = sources.map(({ play, vital }, index) => {
|
|
25021
25730
|
const score = Math.round(vital.score);
|
|
25731
|
+
const label = VITAL_SIGN_LABELS[vital.vital_sign] ?? String(vital.vital_sign);
|
|
25022
25732
|
const dollar = vital.dollar_value != null && vital.dollar_value > 0 ? `$${Math.round(vital.dollar_value).toLocaleString("en-US")}` : null;
|
|
25023
25733
|
const baseline = dollar ?? String(score);
|
|
25024
25734
|
const targetLow = vital.dollar_value != null && vital.dollar_value > 0 ? `$${Math.round(vital.dollar_value * 0.4).toLocaleString("en-US")}` : String(Math.min(100, score + 20));
|
|
25025
25735
|
const targetHigh = vital.dollar_value != null && vital.dollar_value > 0 ? `$${Math.round(vital.dollar_value * 0.6).toLocaleString("en-US")}` : String(Math.min(100, score + 35));
|
|
25736
|
+
const targetRange = `${targetLow}\u2013${targetHigh}`;
|
|
25026
25737
|
const checkDate = toIso(addDays(today, 21 + index * 7));
|
|
25027
25738
|
const outcome = {
|
|
25028
25739
|
metric: vital.vital_sign,
|
|
25029
25740
|
baseline,
|
|
25030
|
-
target_range:
|
|
25741
|
+
target_range: targetRange,
|
|
25031
25742
|
check_date: checkDate,
|
|
25032
25743
|
measured_by: `${vital.vital_sign} vital sign`
|
|
25033
25744
|
};
|
|
25745
|
+
const problem = dollar ? `${label} is under pressure \u2014 ${dollar} ${vital.dollar_label ?? ""}`.trimEnd() : `${label} is under pressure (score ${score}, ${vital.status})`;
|
|
25034
25746
|
return {
|
|
25035
25747
|
order: index + 1,
|
|
25036
25748
|
title: play.name,
|
|
25037
|
-
problem
|
|
25749
|
+
problem,
|
|
25038
25750
|
rationale: index === 0 ? "Layer-order first: clean or unblock the gating vital before downstream work" : "Next in dependency order after the prior workstream",
|
|
25039
25751
|
play_ids: [play.id],
|
|
25040
25752
|
actions: play.steps.slice(0, 3),
|
|
25041
25753
|
effort_hours: 8 + index * 4,
|
|
25042
25754
|
milestones: [
|
|
25043
25755
|
{
|
|
25044
|
-
label: `Check ${
|
|
25756
|
+
label: `Check ${label} movement`,
|
|
25045
25757
|
due: checkDate,
|
|
25046
|
-
verification: `${
|
|
25758
|
+
verification: `${label} moves toward ${targetRange} (baseline ${baseline})`
|
|
25047
25759
|
}
|
|
25048
25760
|
],
|
|
25049
25761
|
deliverables: [
|
|
@@ -25056,26 +25768,29 @@ function buildGroundedFallbackPlan(input) {
|
|
|
25056
25768
|
expected_outcome: outcome,
|
|
25057
25769
|
leading_indicators: [],
|
|
25058
25770
|
contingency: {
|
|
25059
|
-
trigger: `${
|
|
25771
|
+
trigger: `${label} flat or worse at first check`,
|
|
25060
25772
|
trigger_check_date: checkDate,
|
|
25061
25773
|
fallback: "Descope to the single highest-dollar entity cohort and re-run /strategy review"
|
|
25062
25774
|
}
|
|
25063
25775
|
};
|
|
25064
25776
|
});
|
|
25065
|
-
const
|
|
25777
|
+
const gatingSign = input.gatingVitalSign ?? sources[0]?.vital.vital_sign ?? "freshness";
|
|
25778
|
+
const gatingLabel = VITAL_SIGN_LABELS[gatingSign] ?? String(gatingSign);
|
|
25066
25779
|
const varLabel = input.totalValueAtRisk != null && input.totalValueAtRisk > 0 ? `$${Math.round(input.totalValueAtRisk).toLocaleString("en-US")} at risk` : "material pipeline dollars at risk";
|
|
25067
25780
|
const plan = {
|
|
25068
25781
|
title: "Grounded recovery plan",
|
|
25069
25782
|
objective: input.objective,
|
|
25070
|
-
summary_30k: `${
|
|
25783
|
+
summary_30k: `${gatingLabel} is the gating pressure (${varLabel}). Sequence ${workstreams.length} playbook workstream(s) in layer order so data trust and handoffs unlock pipeline movement. Treat baselines as live vital readings; refine with /strategy after the first review.`,
|
|
25071
25784
|
hypothesis: "If plays execute in layer order against the live vital scores, the objective metrics should move into the stated ranges within one review cycle.",
|
|
25072
25785
|
target_segment: "Whole pipeline",
|
|
25073
25786
|
priority: "high",
|
|
25074
25787
|
review_cadence: "Weekly",
|
|
25075
25788
|
confidence: 0.45,
|
|
25076
|
-
constraints: ["
|
|
25077
|
-
assumptions: [
|
|
25078
|
-
|
|
25789
|
+
constraints: ["Confirm team capacity before staffing these workstreams"],
|
|
25790
|
+
assumptions: [
|
|
25791
|
+
"Outcome ranges are heuristic estimates from live vitals, not forecast models"
|
|
25792
|
+
],
|
|
25793
|
+
risks: ["Ranges may shift after the first review \u2014 refine the plan then"],
|
|
25079
25794
|
workstreams
|
|
25080
25795
|
};
|
|
25081
25796
|
return {
|
|
@@ -25090,6 +25805,7 @@ var init_strategist_validate = __esm({
|
|
|
25090
25805
|
"src/ai/strategist-validate.ts"() {
|
|
25091
25806
|
"use strict";
|
|
25092
25807
|
init_playbook();
|
|
25808
|
+
init_formatters();
|
|
25093
25809
|
init_health_score();
|
|
25094
25810
|
init_json_response();
|
|
25095
25811
|
NUMBER_RE = /\$?\s*(\d[\d,]*\.?\d*)\s*(m|k|b|million|thousand|billion)?\b/gi;
|
|
@@ -26766,16 +27482,25 @@ function renderCraftHandoffMarkdown(opts) {
|
|
|
26766
27482
|
lines.push("");
|
|
26767
27483
|
lines.push(plan.objective);
|
|
26768
27484
|
lines.push("");
|
|
26769
|
-
|
|
26770
|
-
|
|
26771
|
-
|
|
26772
|
-
|
|
27485
|
+
const constraint = renderConstraintHeading(opts.constraintLine).trimEnd();
|
|
27486
|
+
if (constraint) {
|
|
27487
|
+
lines.push(constraint);
|
|
27488
|
+
lines.push("");
|
|
27489
|
+
}
|
|
27490
|
+
const scope = renderScopeHeading(plan.constraints, opts.outOfScope).trimEnd();
|
|
27491
|
+
if (scope) {
|
|
27492
|
+
lines.push(scope);
|
|
27493
|
+
lines.push("");
|
|
27494
|
+
}
|
|
26773
27495
|
lines.push("## Hypothesis");
|
|
26774
27496
|
lines.push("");
|
|
26775
27497
|
lines.push(plan.hypothesis);
|
|
26776
27498
|
lines.push("");
|
|
26777
|
-
|
|
26778
|
-
|
|
27499
|
+
const killed = renderKilledAlternativeLine(opts.killedAlternative);
|
|
27500
|
+
if (killed) {
|
|
27501
|
+
lines.push(killed);
|
|
27502
|
+
lines.push("");
|
|
27503
|
+
}
|
|
26779
27504
|
lines.push(renderEffortHeading(plan.workstreams).trimEnd());
|
|
26780
27505
|
lines.push("");
|
|
26781
27506
|
lines.push("## Workstreams");
|
|
@@ -26784,14 +27509,16 @@ function renderCraftHandoffMarkdown(opts) {
|
|
|
26784
27509
|
lines.push(`### ${ws.order}. ${ws.title}`);
|
|
26785
27510
|
lines.push("");
|
|
26786
27511
|
lines.push(`- Problem: ${ws.problem}`);
|
|
26787
|
-
|
|
27512
|
+
if (ws.rationale && !isGenericWorkstreamRationale(ws.rationale)) {
|
|
27513
|
+
lines.push(`- Why this order: ${ws.rationale}`);
|
|
27514
|
+
}
|
|
26788
27515
|
if (ws.actions[0]) lines.push(`- First action (48h): ${ws.actions[0]}`);
|
|
26789
27516
|
lines.push(`- Effort: ~${Math.round(ws.effort_hours)} team-hours`);
|
|
26790
27517
|
lines.push(
|
|
26791
|
-
`- Exam: ${ws.expected_outcome.metric} ${ws.expected_outcome.baseline}
|
|
27518
|
+
`- Exam: ${ws.expected_outcome.metric} ${ws.expected_outcome.baseline} \u2192 ${ws.expected_outcome.target_range} by ${ws.expected_outcome.check_date} (${ws.expected_outcome.measured_by})`
|
|
26792
27519
|
);
|
|
26793
27520
|
lines.push(
|
|
26794
|
-
`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date})
|
|
27521
|
+
`- Contingency: if ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}) \u2192 ${ws.contingency.fallback}`
|
|
26795
27522
|
);
|
|
26796
27523
|
lines.push("");
|
|
26797
27524
|
}
|
|
@@ -26801,33 +27528,22 @@ function renderCraftHandoffMarkdown(opts) {
|
|
|
26801
27528
|
lines.push(opts.roundtableDigest);
|
|
26802
27529
|
lines.push("");
|
|
26803
27530
|
}
|
|
26804
|
-
|
|
27531
|
+
const risks = filterOperatorLines(plan.risks);
|
|
27532
|
+
if (risks.length > 0) {
|
|
26805
27533
|
lines.push("## Risks");
|
|
26806
27534
|
lines.push("");
|
|
26807
|
-
for (const r of
|
|
27535
|
+
for (const r of risks) lines.push(`- ${r}`);
|
|
26808
27536
|
lines.push("");
|
|
26809
27537
|
}
|
|
26810
|
-
|
|
27538
|
+
const assumptions = filterOperatorLines(plan.assumptions);
|
|
27539
|
+
if (assumptions.length > 0) {
|
|
26811
27540
|
lines.push("## Assumptions");
|
|
26812
27541
|
lines.push("");
|
|
26813
|
-
for (const a of
|
|
27542
|
+
for (const a of assumptions) lines.push(`- ${a}`);
|
|
26814
27543
|
lines.push("");
|
|
26815
27544
|
}
|
|
26816
27545
|
lines.push(renderReviewHeading({ cadence: plan.review_cadence, slug: opts.slug }).trimEnd());
|
|
26817
27546
|
lines.push("");
|
|
26818
|
-
if (opts.journalPath) {
|
|
26819
|
-
lines.push("## Craft log");
|
|
26820
|
-
lines.push("");
|
|
26821
|
-
lines.push(`Status: ${opts.status}`);
|
|
26822
|
-
lines.push(`Log: ${opts.journalPath}`);
|
|
26823
|
-
if (opts.libraryPath) lines.push(`Strategy library: ${opts.libraryPath}`);
|
|
26824
|
-
lines.push("");
|
|
26825
|
-
} else if (opts.libraryPath) {
|
|
26826
|
-
lines.push("## Strategy library");
|
|
26827
|
-
lines.push("");
|
|
26828
|
-
lines.push(opts.libraryPath);
|
|
26829
|
-
lines.push("");
|
|
26830
|
-
}
|
|
26831
27547
|
return lines.join("\n");
|
|
26832
27548
|
}
|
|
26833
27549
|
function writeCraftPlanHandoff(opts) {
|
|
@@ -26858,6 +27574,7 @@ var init_handoff = __esm({
|
|
|
26858
27574
|
"use strict";
|
|
26859
27575
|
init_exports_registry();
|
|
26860
27576
|
init_redact_write();
|
|
27577
|
+
init_strategy_signal();
|
|
26861
27578
|
init_library();
|
|
26862
27579
|
init_store3();
|
|
26863
27580
|
}
|
|
@@ -27063,42 +27780,38 @@ function printWrapped(text, width, prefix = INDENT, style) {
|
|
|
27063
27780
|
}
|
|
27064
27781
|
}
|
|
27065
27782
|
function outcomeLine(outcome) {
|
|
27066
|
-
return `${chalk17.bold(outcome.metric)}: ${outcome.baseline} ${chalk17.dim("
|
|
27783
|
+
return `${chalk17.bold(outcome.metric)}: ${outcome.baseline} ${chalk17.dim("\u2192")} ${chalk17.bold(outcome.target_range)} ${chalk17.dim(`by ${outcome.check_date}`)}`;
|
|
27067
27784
|
}
|
|
27068
27785
|
function printWorkstream(ws, width) {
|
|
27069
|
-
|
|
27070
|
-
console.log(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk17.bold(ws.title)}${plays}`);
|
|
27786
|
+
console.log(`${INDENT}${paint("accent", `${ws.order}.`)} ${chalk17.bold(ws.title)}`);
|
|
27071
27787
|
printWrapped(ws.problem, width - 5, INDENT + " ", (s) => chalk17.dim(s));
|
|
27072
|
-
if (ws.rationale) {
|
|
27073
|
-
printWrapped(`Reason: ${ws.rationale}`, width - 5, INDENT + " ", (s) => chalk17.dim(s));
|
|
27074
|
-
}
|
|
27075
27788
|
console.log(`${INDENT} ${outcomeLine(ws.expected_outcome)}`);
|
|
27076
27789
|
for (const li of ws.leading_indicators) {
|
|
27077
27790
|
console.log(`${INDENT} ${chalk17.dim("Lead:")} ${outcomeLine(li)}`);
|
|
27078
27791
|
}
|
|
27792
|
+
if (ws.actions.length > 0) {
|
|
27793
|
+
console.log(`${INDENT} ${chalk17.dim("First steps")}`);
|
|
27794
|
+
for (const action of ws.actions.slice(0, 3)) {
|
|
27795
|
+
printWrapped(`- ${action}`, width - 7, INDENT + " ", (s) => chalk17.dim(s));
|
|
27796
|
+
}
|
|
27797
|
+
}
|
|
27079
27798
|
if (ws.milestones.length > 0) {
|
|
27080
27799
|
console.log(`${INDENT} ${chalk17.dim("Milestones")}`);
|
|
27081
27800
|
for (const m of ws.milestones) {
|
|
27082
|
-
console.log(`${INDENT} ${paint("accent", m.due)} ${m.label}
|
|
27801
|
+
console.log(`${INDENT} ${paint("accent", m.due)} ${m.label}`);
|
|
27083
27802
|
}
|
|
27084
27803
|
}
|
|
27085
27804
|
if (ws.deliverables.length > 0) {
|
|
27086
27805
|
console.log(`${INDENT} ${chalk17.dim("Deliverables")}`);
|
|
27087
27806
|
for (const d of ws.deliverables) {
|
|
27088
|
-
console.log(`${INDENT} ${chalk17.dim("[ ]")} ${d.label} ${chalk17.dim(`
|
|
27089
|
-
}
|
|
27090
|
-
}
|
|
27091
|
-
if (ws.actions.length > 0) {
|
|
27092
|
-
console.log(`${INDENT} ${chalk17.dim("First steps")}`);
|
|
27093
|
-
for (const action of ws.actions.slice(0, 4)) {
|
|
27094
|
-
printWrapped(`- ${action}`, width - 7, INDENT + " ", (s) => chalk17.dim(s));
|
|
27807
|
+
console.log(`${INDENT} ${chalk17.dim("[ ]")} ${d.label} ${chalk17.dim(`due ${d.due}`)}`);
|
|
27095
27808
|
}
|
|
27096
27809
|
}
|
|
27097
27810
|
printWrapped(
|
|
27098
27811
|
`If ${ws.contingency.trigger} (check ${ws.contingency.trigger_check_date}), then ${ws.contingency.fallback}`,
|
|
27099
27812
|
width - 5,
|
|
27100
27813
|
INDENT + " ",
|
|
27101
|
-
(s) => chalk17.
|
|
27814
|
+
(s) => chalk17.dim(s)
|
|
27102
27815
|
);
|
|
27103
27816
|
console.log(`${INDENT} ${chalk17.dim(`~${Math.round(ws.effort_hours)} team hours`)}`);
|
|
27104
27817
|
console.log();
|
|
@@ -27110,31 +27823,34 @@ function printStrategyBrief(plan, stats) {
|
|
|
27110
27823
|
`${INDENT}${chalk17.bold(`Strategy brief \u2014 ${plan.title}`)} ${chalk17.dim(`confidence ${plan.confidence.toFixed(2)} \xB7 ${plan.priority} priority \xB7 review ${plan.review_cadence.toLowerCase()}`)}`
|
|
27111
27824
|
);
|
|
27112
27825
|
console.log(INDENT + chalk17.dim(hr(width)));
|
|
27113
|
-
|
|
27114
|
-
console.log();
|
|
27115
|
-
console.log(`${INDENT}${chalk17.dim("Summary")}`);
|
|
27826
|
+
console.log(`${INDENT}${chalk17.dim("The Call")}`);
|
|
27116
27827
|
printWrapped(plan.summary_30k, width);
|
|
27117
27828
|
console.log();
|
|
27829
|
+
printWrapped(`Objective: ${plan.objective}`, width, INDENT, (s) => paint("accent", s));
|
|
27830
|
+
console.log();
|
|
27118
27831
|
for (const ws of plan.workstreams) {
|
|
27119
27832
|
printWorkstream(ws, width);
|
|
27120
27833
|
}
|
|
27121
|
-
|
|
27834
|
+
const constraints = filterOperatorLines(plan.constraints);
|
|
27835
|
+
if (constraints.length > 0) {
|
|
27122
27836
|
console.log(`${INDENT}${chalk17.dim("Constraints")}`);
|
|
27123
|
-
for (const c of
|
|
27837
|
+
for (const c of constraints) {
|
|
27124
27838
|
printWrapped(`- ${c}`, width - 2, INDENT, (s) => chalk17.dim(s));
|
|
27125
27839
|
}
|
|
27126
27840
|
console.log();
|
|
27127
27841
|
}
|
|
27128
|
-
|
|
27842
|
+
const assumptions = filterOperatorLines(plan.assumptions);
|
|
27843
|
+
if (assumptions.length > 0) {
|
|
27129
27844
|
console.log(`${INDENT}${chalk17.dim("Assumptions (not verified, not targets)")}`);
|
|
27130
|
-
for (const a of
|
|
27845
|
+
for (const a of assumptions) {
|
|
27131
27846
|
printWrapped(`- ${a}`, width - 2, INDENT, (s) => chalk17.dim(s));
|
|
27132
27847
|
}
|
|
27133
27848
|
console.log();
|
|
27134
27849
|
}
|
|
27135
|
-
|
|
27850
|
+
const risks = filterOperatorLines(plan.risks);
|
|
27851
|
+
if (risks.length > 0) {
|
|
27136
27852
|
console.log(`${INDENT}${chalk17.dim("Risks")}`);
|
|
27137
|
-
for (const r of
|
|
27853
|
+
for (const r of risks) {
|
|
27138
27854
|
printWrapped(`- ${r}`, width - 2, INDENT, (s) => chalk17.dim(s));
|
|
27139
27855
|
}
|
|
27140
27856
|
console.log();
|
|
@@ -27146,6 +27862,12 @@ function printStrategyBrief(plan, stats) {
|
|
|
27146
27862
|
console.log(`${INDENT}${coverageStyled}${chalk17.dim(` \xB7 ~${Math.round(totalHours)} total team hours across ${plan.workstreams.length} workstream${plan.workstreams.length === 1 ? "" : "s"}`)}`);
|
|
27147
27863
|
console.log();
|
|
27148
27864
|
}
|
|
27865
|
+
function printStrategyBriefFooter(notices, meta) {
|
|
27866
|
+
if (usedGroundedFallback(notices)) {
|
|
27867
|
+
console.log(INDENT + chalk17.dim(STRATEGY_FALLBACK_BANNER));
|
|
27868
|
+
}
|
|
27869
|
+
printLlmAttribution(meta);
|
|
27870
|
+
}
|
|
27149
27871
|
function printCraftWrapUp(opts) {
|
|
27150
27872
|
console.log();
|
|
27151
27873
|
if (opts.status === "ready") {
|
|
@@ -27177,15 +27899,14 @@ function printCraftWrapUp(opts) {
|
|
|
27177
27899
|
}
|
|
27178
27900
|
console.log();
|
|
27179
27901
|
}
|
|
27180
|
-
function isCraftRoundNotice(text) {
|
|
27181
|
-
return /^craft \d+\/\d+/.test(text);
|
|
27182
|
-
}
|
|
27183
27902
|
var INDENT;
|
|
27184
27903
|
var init_strategy_brief = __esm({
|
|
27185
27904
|
"src/output/strategy-brief.ts"() {
|
|
27186
27905
|
"use strict";
|
|
27187
27906
|
init_theme();
|
|
27188
27907
|
init_layout();
|
|
27908
|
+
init_strategy_signal();
|
|
27909
|
+
init_llm_attribution();
|
|
27189
27910
|
INDENT = " ";
|
|
27190
27911
|
}
|
|
27191
27912
|
});
|
|
@@ -27548,10 +28269,7 @@ async function runStrategistSession(ctx) {
|
|
|
27548
28269
|
return;
|
|
27549
28270
|
}
|
|
27550
28271
|
printStrategyBrief(plan, stats);
|
|
27551
|
-
|
|
27552
|
-
console.log(" " + chalk18.dim(notice));
|
|
27553
|
-
}
|
|
27554
|
-
printLlmAttribution(meta);
|
|
28272
|
+
printStrategyBriefFooter(notices, meta);
|
|
27555
28273
|
console.log();
|
|
27556
28274
|
let saved = false;
|
|
27557
28275
|
if (ctx.rl) {
|
|
@@ -27600,14 +28318,7 @@ async function ensureSnapshot(ctx) {
|
|
|
27600
28318
|
if (ctx.snapshot.computeResult) return ctx.snapshot.computeResult;
|
|
27601
28319
|
const spinner = makeSpinner("Reading latest vitals\u2026");
|
|
27602
28320
|
try {
|
|
27603
|
-
const snapshot = await
|
|
27604
|
-
ctx.snapshot.computeResult = snapshot;
|
|
27605
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
27606
|
-
segmentId: s.segment.id,
|
|
27607
|
-
segmentName: s.segment.name,
|
|
27608
|
-
result: s.result
|
|
27609
|
-
}));
|
|
27610
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
28321
|
+
const snapshot = await computeAndCacheHealthSnapshot(ctx);
|
|
27611
28322
|
spinner.stop();
|
|
27612
28323
|
return snapshot;
|
|
27613
28324
|
} catch {
|
|
@@ -27624,10 +28335,7 @@ function printCraftReplResult(ctx, result, objective) {
|
|
|
27624
28335
|
} else {
|
|
27625
28336
|
console.log(" " + chalk18.dim("(No plan produced)"));
|
|
27626
28337
|
}
|
|
27627
|
-
|
|
27628
|
-
if (!isCraftRoundNotice(notice)) console.log(" " + chalk18.dim(notice));
|
|
27629
|
-
}
|
|
27630
|
-
printLlmAttribution(result.usage);
|
|
28338
|
+
printStrategyBriefFooter(result.notices, result.usage);
|
|
27631
28339
|
if (result.library_path) creditStrategySession(ctx);
|
|
27632
28340
|
printCraftWrapUp({
|
|
27633
28341
|
status: result.status,
|
|
@@ -27736,13 +28444,11 @@ var init_strategist_flow = __esm({
|
|
|
27736
28444
|
init_repl_api();
|
|
27737
28445
|
init_theme();
|
|
27738
28446
|
init_prompts();
|
|
27739
|
-
|
|
27740
|
-
init_divergence();
|
|
28447
|
+
init_context_snapshot();
|
|
27741
28448
|
init_strategist();
|
|
27742
28449
|
init_strategist2();
|
|
27743
28450
|
init_strategist_run();
|
|
27744
28451
|
init_strategy_brief();
|
|
27745
|
-
init_llm_attribution();
|
|
27746
28452
|
init_time_bank();
|
|
27747
28453
|
init_formatters();
|
|
27748
28454
|
init_store3();
|
|
@@ -27756,14 +28462,24 @@ var init_strategist_flow = __esm({
|
|
|
27756
28462
|
});
|
|
27757
28463
|
|
|
27758
28464
|
// src/conversation/ghost-hints.ts
|
|
27759
|
-
function
|
|
27760
|
-
|
|
28465
|
+
function ghostHintsForPhase(phase, ctx) {
|
|
28466
|
+
if (ctx && !shouldShowTeachingSuggestions(ctx)) return [];
|
|
28467
|
+
const staticHints = PHASE_GHOST_HINTS[phase] ?? [];
|
|
28468
|
+
if (phase === "explore" && ctx) {
|
|
28469
|
+
return buildExploreTeachingGhosts(ctx, staticHints);
|
|
28470
|
+
}
|
|
28471
|
+
return staticHints;
|
|
28472
|
+
}
|
|
28473
|
+
function ghostExamplesForPhase(phase, limit = 2, ctx) {
|
|
28474
|
+
const hints = ghostHintsForPhase(phase, ctx);
|
|
27761
28475
|
return hints.slice(0, limit).map((h) => h.replace(/^try\s+/i, ""));
|
|
27762
28476
|
}
|
|
27763
28477
|
var PHASE_GHOST_HINTS;
|
|
27764
28478
|
var init_ghost_hints = __esm({
|
|
27765
28479
|
"src/conversation/ghost-hints.ts"() {
|
|
27766
28480
|
"use strict";
|
|
28481
|
+
init_teaching_suggestions();
|
|
28482
|
+
init_suggested_asks();
|
|
27767
28483
|
PHASE_GHOST_HINTS = {
|
|
27768
28484
|
orient: [
|
|
27769
28485
|
'try "pipeline health"',
|
|
@@ -27801,7 +28517,8 @@ function buildSituationalAwarenessBlock(ctx, opts = {}) {
|
|
|
27801
28517
|
const action = resolveRecommendedAction(ctx);
|
|
27802
28518
|
const examples = ghostExamplesForPhase(
|
|
27803
28519
|
phase === "think" || phase === "strategize" || phase === "deliver" ? "explore" : phase,
|
|
27804
|
-
2
|
|
28520
|
+
2,
|
|
28521
|
+
ctx
|
|
27805
28522
|
);
|
|
27806
28523
|
const lines = [
|
|
27807
28524
|
"WHERE YOU ARE IN NTRP:",
|
|
@@ -28627,14 +29344,7 @@ async function runThinkTurn(input, ctx) {
|
|
|
28627
29344
|
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
28628
29345
|
const spinner2 = makeSpinner(metricsFirst ? "Loading session context\u2026" : "Computing vital signs\u2026");
|
|
28629
29346
|
try {
|
|
28630
|
-
snapshot = await
|
|
28631
|
-
ctx.snapshot.computeResult = snapshot;
|
|
28632
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
28633
|
-
segmentId: s.segment.id,
|
|
28634
|
-
segmentName: s.segment.name,
|
|
28635
|
-
result: s.result
|
|
28636
|
-
}));
|
|
28637
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
29347
|
+
snapshot = await computeAndCacheHealthSnapshot(ctx);
|
|
28638
29348
|
spinner2.succeed(metricsFirst ? "Session context ready" : "Health snapshot ready");
|
|
28639
29349
|
} catch (err) {
|
|
28640
29350
|
spinner2.fail("Could not compute health snapshot");
|
|
@@ -28645,7 +29355,10 @@ async function runThinkTurn(input, ctx) {
|
|
|
28645
29355
|
}
|
|
28646
29356
|
}
|
|
28647
29357
|
console.log();
|
|
28648
|
-
const memoryBlock = await buildMemoryBlock(input).catch(() =>
|
|
29358
|
+
const memoryBlock = await buildMemoryBlock(input).catch((err) => {
|
|
29359
|
+
debugError("think.buildMemoryBlock", err);
|
|
29360
|
+
return "";
|
|
29361
|
+
});
|
|
28649
29362
|
const spinner = makeSpinner("Thinking with you\u2026");
|
|
28650
29363
|
let lastAnswer = "";
|
|
28651
29364
|
let rawHistory = [];
|
|
@@ -28747,13 +29460,13 @@ var init_think = __esm({
|
|
|
28747
29460
|
init_agentic_loop();
|
|
28748
29461
|
init_thread();
|
|
28749
29462
|
init_store2();
|
|
28750
|
-
|
|
28751
|
-
init_divergence();
|
|
29463
|
+
init_context_snapshot();
|
|
28752
29464
|
init_repl_api();
|
|
28753
29465
|
init_theme();
|
|
28754
29466
|
init_markdown();
|
|
28755
29467
|
init_session_analysis();
|
|
28756
29468
|
init_time_bank();
|
|
29469
|
+
init_diagnostics();
|
|
28757
29470
|
}
|
|
28758
29471
|
});
|
|
28759
29472
|
|
|
@@ -30498,14 +31211,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
30498
31211
|
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
30499
31212
|
const spinner2 = makeSpinner(metricsFirst ? "Loading session context\u2026" : "Computing vital signs\u2026");
|
|
30500
31213
|
try {
|
|
30501
|
-
snapshot = await
|
|
30502
|
-
ctx.snapshot.computeResult = snapshot;
|
|
30503
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
30504
|
-
segmentId: s.segment.id,
|
|
30505
|
-
segmentName: s.segment.name,
|
|
30506
|
-
result: s.result
|
|
30507
|
-
}));
|
|
30508
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
31214
|
+
snapshot = await computeAndCacheHealthSnapshot(ctx);
|
|
30509
31215
|
spinner2.succeed(metricsFirst ? "Session context ready" : "Health snapshot ready");
|
|
30510
31216
|
} catch (err) {
|
|
30511
31217
|
spinner2.fail("Could not compute health snapshot");
|
|
@@ -30516,7 +31222,10 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
30516
31222
|
}
|
|
30517
31223
|
}
|
|
30518
31224
|
console.log();
|
|
30519
|
-
const memoryBlock = await buildMemoryBlock(input).catch(() =>
|
|
31225
|
+
const memoryBlock = await buildMemoryBlock(input).catch((err) => {
|
|
31226
|
+
debugError("nl.buildMemoryBlock", err);
|
|
31227
|
+
return "";
|
|
31228
|
+
});
|
|
30520
31229
|
const spinner = makeSpinner("Thinking\u2026");
|
|
30521
31230
|
let lastAnswer = "";
|
|
30522
31231
|
let rawHistory = [];
|
|
@@ -30641,14 +31350,14 @@ var init_nl = __esm({
|
|
|
30641
31350
|
init_explore_mode();
|
|
30642
31351
|
init_thread();
|
|
30643
31352
|
init_store2();
|
|
30644
|
-
|
|
30645
|
-
init_divergence();
|
|
31353
|
+
init_context_snapshot();
|
|
30646
31354
|
init_repl_api();
|
|
30647
31355
|
init_theme();
|
|
30648
31356
|
init_markdown();
|
|
30649
31357
|
init_smoke_protocol();
|
|
30650
31358
|
init_session_analysis();
|
|
30651
31359
|
init_time_bank();
|
|
31360
|
+
init_diagnostics();
|
|
30652
31361
|
}
|
|
30653
31362
|
});
|
|
30654
31363
|
|
|
@@ -31415,22 +32124,23 @@ var init_terminal = __esm({
|
|
|
31415
32124
|
});
|
|
31416
32125
|
|
|
31417
32126
|
// src/demo/taxonomy-cache.ts
|
|
31418
|
-
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, existsSync as
|
|
31419
|
-
import { homedir as
|
|
32127
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync17, existsSync as existsSync26, mkdirSync as mkdirSync14, unlinkSync as unlinkSync5 } from "fs";
|
|
32128
|
+
import { homedir as homedir6 } from "os";
|
|
31420
32129
|
import { join as join27 } from "path";
|
|
31421
32130
|
function ensureDir7() {
|
|
31422
|
-
if (!
|
|
32131
|
+
if (!existsSync26(NTRP_DIR4)) {
|
|
31423
32132
|
mkdirSync14(NTRP_DIR4, { recursive: true });
|
|
31424
32133
|
}
|
|
31425
32134
|
}
|
|
31426
32135
|
function loadCachedTaxonomy(profile) {
|
|
31427
|
-
if (!
|
|
32136
|
+
if (!existsSync26(TAXONOMY_PATH)) return null;
|
|
31428
32137
|
try {
|
|
31429
32138
|
const parsed = JSON.parse(readFileSync21(TAXONOMY_PATH, "utf-8"));
|
|
31430
32139
|
if (!parsed || typeof parsed !== "object") return null;
|
|
31431
32140
|
if (parsed.profile_updated_at !== profile.updated_at) return null;
|
|
31432
32141
|
return parsed;
|
|
31433
|
-
} catch {
|
|
32142
|
+
} catch (err) {
|
|
32143
|
+
debugError("demo.taxonomyCache.load", err, TAXONOMY_PATH);
|
|
31434
32144
|
return null;
|
|
31435
32145
|
}
|
|
31436
32146
|
}
|
|
@@ -31439,10 +32149,11 @@ function saveCachedTaxonomy(taxonomy) {
|
|
|
31439
32149
|
writeFileSync17(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
31440
32150
|
}
|
|
31441
32151
|
function invalidateTaxonomy() {
|
|
31442
|
-
if (
|
|
32152
|
+
if (existsSync26(TAXONOMY_PATH)) {
|
|
31443
32153
|
try {
|
|
31444
32154
|
unlinkSync5(TAXONOMY_PATH);
|
|
31445
|
-
} catch {
|
|
32155
|
+
} catch (err) {
|
|
32156
|
+
debugError("demo.taxonomyCache.invalidate", err, TAXONOMY_PATH);
|
|
31446
32157
|
}
|
|
31447
32158
|
}
|
|
31448
32159
|
}
|
|
@@ -31450,7 +32161,8 @@ var NTRP_DIR4, TAXONOMY_PATH;
|
|
|
31450
32161
|
var init_taxonomy_cache = __esm({
|
|
31451
32162
|
"src/demo/taxonomy-cache.ts"() {
|
|
31452
32163
|
"use strict";
|
|
31453
|
-
|
|
32164
|
+
init_diagnostics();
|
|
32165
|
+
NTRP_DIR4 = join27(homedir6(), ".ntrp");
|
|
31454
32166
|
TAXONOMY_PATH = join27(NTRP_DIR4, "demo-taxonomy.json");
|
|
31455
32167
|
}
|
|
31456
32168
|
});
|
|
@@ -31473,7 +32185,7 @@ async function buildDemoTaxonomy(profile, ctx) {
|
|
|
31473
32185
|
|
|
31474
32186
|
Produce the demo taxonomy now as STRICT JSON matching the schema in the system prompt.`;
|
|
31475
32187
|
const { text } = await llmCompleteText("demo_taxonomy", SYSTEM_PROMPT3, userMessage, 4096, ctx);
|
|
31476
|
-
const cleaned =
|
|
32188
|
+
const cleaned = stripJsonFences(text);
|
|
31477
32189
|
let parsed;
|
|
31478
32190
|
try {
|
|
31479
32191
|
parsed = JSON.parse(cleaned);
|
|
@@ -31482,12 +32194,6 @@ Produce the demo taxonomy now as STRICT JSON matching the schema in the system p
|
|
|
31482
32194
|
}
|
|
31483
32195
|
return validateTaxonomy(parsed, profile);
|
|
31484
32196
|
}
|
|
31485
|
-
function stripFences2(text) {
|
|
31486
|
-
const trimmed = text.trim();
|
|
31487
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
31488
|
-
if (fenced) return fenced[1].trim();
|
|
31489
|
-
return trimmed;
|
|
31490
|
-
}
|
|
31491
32197
|
function validateTaxonomy(raw, profile) {
|
|
31492
32198
|
const strArray3 = (key, min = 3) => {
|
|
31493
32199
|
const v = raw[key];
|
|
@@ -31609,6 +32315,7 @@ var init_demo_taxonomy = __esm({
|
|
|
31609
32315
|
"use strict";
|
|
31610
32316
|
init_repl_api();
|
|
31611
32317
|
init_complete();
|
|
32318
|
+
init_json_response();
|
|
31612
32319
|
SYSTEM_PROMPT3 = `You are a senior GTM market researcher. Given a company profile, produce a taxonomy of synthetic-but-plausible entities a pipeline-health tool would show this operator in their demo data.
|
|
31613
32320
|
|
|
31614
32321
|
CORE PRINCIPLES:
|
|
@@ -31909,7 +32616,7 @@ __export(inbox_setup_exports, {
|
|
|
31909
32616
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
31910
32617
|
});
|
|
31911
32618
|
import chalk30 from "chalk";
|
|
31912
|
-
import { existsSync as
|
|
32619
|
+
import { existsSync as existsSync27 } from "fs";
|
|
31913
32620
|
function markDemoOffered() {
|
|
31914
32621
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
31915
32622
|
}
|
|
@@ -31941,7 +32648,7 @@ function printSkipHint(beat) {
|
|
|
31941
32648
|
async function reuseInboxFolderIfPresent(session, beat, folderPath) {
|
|
31942
32649
|
if (getAiInboxDir()) return false;
|
|
31943
32650
|
const candidates = folderPath ? [folderPath] : [.../* @__PURE__ */ new Set([defaultAiInboxDir(), legacyAiInboxDir()])];
|
|
31944
|
-
const existing = candidates.find((p) =>
|
|
32651
|
+
const existing = candidates.find((p) => existsSync27(p));
|
|
31945
32652
|
if (!existing) return false;
|
|
31946
32653
|
console.log(" " + chalk30.dim("Pickup folder still on disk: ") + existing);
|
|
31947
32654
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
@@ -32047,7 +32754,7 @@ __export(ingest_exports, {
|
|
|
32047
32754
|
handler: () => handler2
|
|
32048
32755
|
});
|
|
32049
32756
|
import chalk31 from "chalk";
|
|
32050
|
-
import { readFileSync as readFileSync22, existsSync as
|
|
32757
|
+
import { readFileSync as readFileSync22, existsSync as existsSync28 } from "fs";
|
|
32051
32758
|
import { basename as basename7 } from "path";
|
|
32052
32759
|
async function handler2(args, ctx) {
|
|
32053
32760
|
const { positional, flags } = parseArgs2(args, [
|
|
@@ -32071,7 +32778,7 @@ async function handler2(args, ctx) {
|
|
|
32071
32778
|
console.error(chalk31.dim(" /ingest --demo [--scenario <name>]"));
|
|
32072
32779
|
process.exit(1);
|
|
32073
32780
|
}
|
|
32074
|
-
if (!
|
|
32781
|
+
if (!existsSync28(file)) {
|
|
32075
32782
|
console.error(chalk31.red(` File not found: ${file}`));
|
|
32076
32783
|
process.exit(1);
|
|
32077
32784
|
}
|
|
@@ -32260,8 +32967,8 @@ function renderMetricsReport(ctx, metrics, coverage, sourceType, options = {}) {
|
|
|
32260
32967
|
printFindings(options.findings);
|
|
32261
32968
|
}
|
|
32262
32969
|
if (options.interactive !== false) {
|
|
32263
|
-
const
|
|
32264
|
-
printMetricsNextSteps(ctx, options.companion ?? null,
|
|
32970
|
+
const insightAsks = deterministic.map((i) => i.suggested_ask).filter((q) => !!q);
|
|
32971
|
+
printMetricsNextSteps(ctx, options.companion ?? null, insightAsks);
|
|
32265
32972
|
}
|
|
32266
32973
|
}
|
|
32267
32974
|
function wrapInsight(text) {
|
|
@@ -32305,10 +33012,10 @@ function formatShort(n) {
|
|
|
32305
33012
|
if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
|
|
32306
33013
|
return String(Math.round(n));
|
|
32307
33014
|
}
|
|
32308
|
-
function printMetricsNextSteps(ctx, companion,
|
|
33015
|
+
function printMetricsNextSteps(ctx, companion, insightAsks) {
|
|
32309
33016
|
printCompanionFooter(ctx, companion, {
|
|
32310
33017
|
justCompleted: "revenue_metrics",
|
|
32311
|
-
|
|
33018
|
+
insightAsks
|
|
32312
33019
|
});
|
|
32313
33020
|
}
|
|
32314
33021
|
var GROUP_ORDER;
|
|
@@ -32435,7 +33142,16 @@ async function handler3(args, ctx) {
|
|
|
32435
33142
|
saveSessionState(ctx);
|
|
32436
33143
|
if (!ctx.oneShot && !isStructuredOutput(ctx.execution) && !ctx.suppressCompanionFooter) {
|
|
32437
33144
|
const companion = await resolveCompanionRecommendation(ctx);
|
|
32438
|
-
|
|
33145
|
+
let healthResult = ctx.snapshot.computeResult?.aggregate ?? null;
|
|
33146
|
+
if (!healthResult) {
|
|
33147
|
+
const { loadLatestDiagnosis: loadLatestDiagnosis2 } = await Promise.resolve().then(() => (init_queries(), queries_exports));
|
|
33148
|
+
const latest = await loadLatestDiagnosis2();
|
|
33149
|
+
healthResult = latest?.health ?? null;
|
|
33150
|
+
}
|
|
33151
|
+
printCompanionFooter(ctx, companion, {
|
|
33152
|
+
justCompleted: "gtm_health",
|
|
33153
|
+
health: healthResult
|
|
33154
|
+
});
|
|
32439
33155
|
}
|
|
32440
33156
|
ctx.suppressCompanionFooter = false;
|
|
32441
33157
|
if (!ctx.skipTimeBankDiagnoseCredit) {
|
|
@@ -32500,6 +33216,7 @@ async function runDiagnose(options, ctx) {
|
|
|
32500
33216
|
compact: options.compact,
|
|
32501
33217
|
ctx
|
|
32502
33218
|
});
|
|
33219
|
+
ctx.snapshot.computeResult = fullResult;
|
|
32503
33220
|
return buildDiagnoseSummary(fullResult.aggregate, findings);
|
|
32504
33221
|
} catch (err) {
|
|
32505
33222
|
const { isLlmAuthError: isLlmAuthError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
@@ -32621,7 +33338,7 @@ Research this company deeply using your training knowledge. Identify the canonic
|
|
|
32621
33338
|
const { text } = await llmCompleteText("onboard", SYSTEM_PROMPT4, userMessage, 2048, ctx, {
|
|
32622
33339
|
skipPseudonymize: true
|
|
32623
33340
|
});
|
|
32624
|
-
const cleaned =
|
|
33341
|
+
const cleaned = stripJsonFences(text);
|
|
32625
33342
|
let parsed;
|
|
32626
33343
|
try {
|
|
32627
33344
|
parsed = JSON.parse(cleaned);
|
|
@@ -32634,12 +33351,6 @@ Research this company deeply using your training knowledge. Identify the canonic
|
|
|
32634
33351
|
if (seed.company_url && !draft.company_url) draft.company_url = seed.company_url;
|
|
32635
33352
|
return draft;
|
|
32636
33353
|
}
|
|
32637
|
-
function stripFences3(text) {
|
|
32638
|
-
const trimmed = text.trim();
|
|
32639
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
32640
|
-
if (fenced) return fenced[1].trim();
|
|
32641
|
-
return trimmed;
|
|
32642
|
-
}
|
|
32643
33354
|
function validateDraft(raw) {
|
|
32644
33355
|
const out = {};
|
|
32645
33356
|
const str3 = (k) => {
|
|
@@ -32677,6 +33388,7 @@ var init_profile_draft = __esm({
|
|
|
32677
33388
|
init_repl_api();
|
|
32678
33389
|
init_complete();
|
|
32679
33390
|
init_lexicon_seed();
|
|
33391
|
+
init_json_response();
|
|
32680
33392
|
SYSTEM_PROMPT4 = `You are a senior GTM researcher with deep recall of the B2B / SaaS / services landscape. Given a company website (or, as a fallback, a plain company name), you produce a rich, specific profile of the business so a pipeline-health tool can tailor every answer to their reality.
|
|
32681
33393
|
|
|
32682
33394
|
RESEARCH DEPTH \u2014 this is the most important part:
|
|
@@ -32726,46 +33438,6 @@ Any field you are not confident about MUST be omitted entirely (do not include i
|
|
|
32726
33438
|
function normLabel2(s) {
|
|
32727
33439
|
return s.trim().toLowerCase();
|
|
32728
33440
|
}
|
|
32729
|
-
function parseJsonObject(text) {
|
|
32730
|
-
const cleaned = stripJsonFences(text);
|
|
32731
|
-
const start = cleaned.indexOf("{");
|
|
32732
|
-
if (start === -1) return null;
|
|
32733
|
-
let depth = 0;
|
|
32734
|
-
let inString = false;
|
|
32735
|
-
let escaped = false;
|
|
32736
|
-
let end = -1;
|
|
32737
|
-
for (let i = start; i < cleaned.length; i++) {
|
|
32738
|
-
const ch = cleaned[i];
|
|
32739
|
-
if (escaped) {
|
|
32740
|
-
escaped = false;
|
|
32741
|
-
continue;
|
|
32742
|
-
}
|
|
32743
|
-
if (ch === "\\") {
|
|
32744
|
-
if (inString) escaped = true;
|
|
32745
|
-
continue;
|
|
32746
|
-
}
|
|
32747
|
-
if (ch === '"') {
|
|
32748
|
-
inString = !inString;
|
|
32749
|
-
continue;
|
|
32750
|
-
}
|
|
32751
|
-
if (inString) continue;
|
|
32752
|
-
if (ch === "{") depth++;
|
|
32753
|
-
else if (ch === "}") {
|
|
32754
|
-
depth--;
|
|
32755
|
-
if (depth === 0) {
|
|
32756
|
-
end = i;
|
|
32757
|
-
break;
|
|
32758
|
-
}
|
|
32759
|
-
}
|
|
32760
|
-
}
|
|
32761
|
-
if (end === -1) return null;
|
|
32762
|
-
try {
|
|
32763
|
-
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
32764
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
32765
|
-
} catch {
|
|
32766
|
-
return null;
|
|
32767
|
-
}
|
|
32768
|
-
}
|
|
32769
33441
|
function sortQuestionRecommendedFirst(q) {
|
|
32770
33442
|
const flagged = q.options.find((o) => o.recommended);
|
|
32771
33443
|
const rec = flagged?.label ?? q.options[0]?.label;
|
|
@@ -32797,7 +33469,7 @@ function parseVerdict(raw) {
|
|
|
32797
33469
|
return null;
|
|
32798
33470
|
}
|
|
32799
33471
|
function parseOptionSetEval(text) {
|
|
32800
|
-
const rec =
|
|
33472
|
+
const rec = parseJsonObjectFromText(text);
|
|
32801
33473
|
if (!rec) return null;
|
|
32802
33474
|
if (!Array.isArray(rec.questions)) return null;
|
|
32803
33475
|
const questions = [];
|
|
@@ -32960,12 +33632,6 @@ OUTPUT \u2014 STRICT JSON only, no fences, no commentary:
|
|
|
32960
33632
|
});
|
|
32961
33633
|
|
|
32962
33634
|
// src/ai/profile-clarify.ts
|
|
32963
|
-
function stripFences4(text) {
|
|
32964
|
-
const trimmed = text.trim();
|
|
32965
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
32966
|
-
if (fenced) return fenced[1].trim();
|
|
32967
|
-
return trimmed;
|
|
32968
|
-
}
|
|
32969
33635
|
function formatDraft(draft) {
|
|
32970
33636
|
const parts = [];
|
|
32971
33637
|
if (draft.industry) parts.push(`Industry: ${draft.industry}`);
|
|
@@ -32980,7 +33646,7 @@ function formatDraft(draft) {
|
|
|
32980
33646
|
return parts.join("\n");
|
|
32981
33647
|
}
|
|
32982
33648
|
function parseClarifyingQuestions(text) {
|
|
32983
|
-
const cleaned =
|
|
33649
|
+
const cleaned = stripJsonFences(text);
|
|
32984
33650
|
let parsed;
|
|
32985
33651
|
try {
|
|
32986
33652
|
parsed = JSON.parse(cleaned);
|
|
@@ -33077,7 +33743,7 @@ ${draftBlock}${userBlock}${answersBlock}
|
|
|
33077
33743
|
|
|
33078
33744
|
Emit the refined profile now as STRICT JSON.`;
|
|
33079
33745
|
const { text } = await llmCompleteText("onboard", REFINE_SYSTEM_PROMPT, userMessage, 2048, ctx);
|
|
33080
|
-
const cleaned =
|
|
33746
|
+
const cleaned = stripJsonFences(text);
|
|
33081
33747
|
let parsed;
|
|
33082
33748
|
try {
|
|
33083
33749
|
parsed = JSON.parse(cleaned);
|
|
@@ -33122,6 +33788,7 @@ var init_profile_clarify = __esm({
|
|
|
33122
33788
|
"use strict";
|
|
33123
33789
|
init_repl_api();
|
|
33124
33790
|
init_complete();
|
|
33791
|
+
init_json_response();
|
|
33125
33792
|
init_option_set();
|
|
33126
33793
|
CLARIFY_SYSTEM_PROMPT = `You are a senior GTM researcher helping NTRP \u2014 a pipeline-health "stethoscope" \u2014 tune its understanding of a specific company before it diagnoses their sales funnel.
|
|
33127
33794
|
|
|
@@ -33442,155 +34109,6 @@ var init_profile2 = __esm({
|
|
|
33442
34109
|
}
|
|
33443
34110
|
});
|
|
33444
34111
|
|
|
33445
|
-
// src/conversation/onboard-tiers.ts
|
|
33446
|
-
var onboard_tiers_exports = {};
|
|
33447
|
-
__export(onboard_tiers_exports, {
|
|
33448
|
-
ONBOARD_TIER_ORDER: () => ONBOARD_TIER_ORDER,
|
|
33449
|
-
canRunDomainTier: () => canRunDomainTier,
|
|
33450
|
-
clearOnboardTierFlags: () => clearOnboardTierFlags,
|
|
33451
|
-
describeOnboardTier: () => describeOnboardTier,
|
|
33452
|
-
getOnboardTierFlag: () => getOnboardTierFlag,
|
|
33453
|
-
getOnboardTierStatus: () => getOnboardTierStatus,
|
|
33454
|
-
listCompletedOnboardTier: () => listCompletedOnboardTier,
|
|
33455
|
-
markDemoDataSeen: () => markDemoDataSeen,
|
|
33456
|
-
markOnboardTierComplete: () => markOnboardTierComplete,
|
|
33457
|
-
markProductionDataSeen: () => markProductionDataSeen,
|
|
33458
|
-
pathLooksPresent: () => pathLooksPresent,
|
|
33459
|
-
resetOnboardTierProgress: () => resetOnboardTierProgress,
|
|
33460
|
-
resolveNextOnboardTier: () => resolveNextOnboardTier
|
|
33461
|
-
});
|
|
33462
|
-
import { existsSync as existsSync28, statSync as statSync4 } from "fs";
|
|
33463
|
-
function flagSet(tier) {
|
|
33464
|
-
return Boolean(getConfigValue(TIER_CONFIG_KEYS[tier]));
|
|
33465
|
-
}
|
|
33466
|
-
function getOnboardTierFlag(tier) {
|
|
33467
|
-
return getConfigValue(TIER_CONFIG_KEYS[tier]);
|
|
33468
|
-
}
|
|
33469
|
-
function markOnboardTierComplete(...tiers) {
|
|
33470
|
-
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
33471
|
-
for (const tier of tiers) {
|
|
33472
|
-
if (!flagSet(tier)) setConfigValue(TIER_CONFIG_KEYS[tier], at);
|
|
33473
|
-
}
|
|
33474
|
-
}
|
|
33475
|
-
function clearOnboardTierFlags(...tiers) {
|
|
33476
|
-
for (const tier of tiers) {
|
|
33477
|
-
deleteConfigValue(TIER_CONFIG_KEYS[tier]);
|
|
33478
|
-
}
|
|
33479
|
-
}
|
|
33480
|
-
function resetOnboardTierProgress() {
|
|
33481
|
-
clearOnboardTierFlags(...ONBOARD_TIER_ORDER);
|
|
33482
|
-
}
|
|
33483
|
-
function hasProductionDataset(ctx) {
|
|
33484
|
-
const source = ctx?.dataset?.source;
|
|
33485
|
-
if (source && !source.startsWith("demo:")) {
|
|
33486
|
-
if (source.startsWith("/") || /^[A-Za-z]:[\\/]/.test(source) || source.startsWith("~")) {
|
|
33487
|
-
return true;
|
|
33488
|
-
}
|
|
33489
|
-
if (!source.includes(":") && existsSync28(source)) return true;
|
|
33490
|
-
if (source.startsWith("csv:") || source.startsWith("file:") || source.startsWith("folder:")) {
|
|
33491
|
-
return true;
|
|
33492
|
-
}
|
|
33493
|
-
}
|
|
33494
|
-
if (ctx?.attachments && ctx.attachments.length > 0) return true;
|
|
33495
|
-
return flagSet("production");
|
|
33496
|
-
}
|
|
33497
|
-
function hasDemoExperience(ctx) {
|
|
33498
|
-
if (flagSet("demo")) return true;
|
|
33499
|
-
if (getPreferredDemoScenario()) return true;
|
|
33500
|
-
const source = ctx?.dataset?.source;
|
|
33501
|
-
if (source?.startsWith("demo:")) return true;
|
|
33502
|
-
return false;
|
|
33503
|
-
}
|
|
33504
|
-
function listCompletedOnboardTier(ctx) {
|
|
33505
|
-
const done = [];
|
|
33506
|
-
const profileOk = flagSet("profile") || isProfileConfigured(loadProfile());
|
|
33507
|
-
if (profileOk) done.push("profile");
|
|
33508
|
-
else return done;
|
|
33509
|
-
if (flagSet("domain")) done.push("domain");
|
|
33510
|
-
else return done;
|
|
33511
|
-
if (hasDemoExperience(ctx)) done.push("demo");
|
|
33512
|
-
else return done;
|
|
33513
|
-
if (hasProductionDataset(ctx) || flagSet("production")) done.push("production");
|
|
33514
|
-
return done;
|
|
33515
|
-
}
|
|
33516
|
-
function resolveNextOnboardTier(ctx) {
|
|
33517
|
-
const done = new Set(listCompletedOnboardTier(ctx));
|
|
33518
|
-
for (const tier of ONBOARD_TIER_ORDER) {
|
|
33519
|
-
if (!done.has(tier)) return tier;
|
|
33520
|
-
}
|
|
33521
|
-
return null;
|
|
33522
|
-
}
|
|
33523
|
-
function getOnboardTierStatus(ctx) {
|
|
33524
|
-
const completed = listCompletedOnboardTier(ctx);
|
|
33525
|
-
const next = resolveNextOnboardTier(ctx);
|
|
33526
|
-
const meta = next ? TIER_META[next] : null;
|
|
33527
|
-
return {
|
|
33528
|
-
completed,
|
|
33529
|
-
next,
|
|
33530
|
-
nextLabel: meta?.label ?? null,
|
|
33531
|
-
nextHint: meta?.hint ?? null
|
|
33532
|
-
};
|
|
33533
|
-
}
|
|
33534
|
-
function describeOnboardTier(tier) {
|
|
33535
|
-
return TIER_META[tier];
|
|
33536
|
-
}
|
|
33537
|
-
function canRunDomainTier() {
|
|
33538
|
-
return isProfileConfigured(loadProfile()) && hasAnyLlmProvider();
|
|
33539
|
-
}
|
|
33540
|
-
function markProductionDataSeen() {
|
|
33541
|
-
markOnboardTierComplete("production");
|
|
33542
|
-
}
|
|
33543
|
-
function markDemoDataSeen() {
|
|
33544
|
-
markOnboardTierComplete("demo");
|
|
33545
|
-
}
|
|
33546
|
-
function pathLooksPresent(raw) {
|
|
33547
|
-
try {
|
|
33548
|
-
return existsSync28(raw) && (statSync4(raw).isFile() || statSync4(raw).isDirectory());
|
|
33549
|
-
} catch {
|
|
33550
|
-
return false;
|
|
33551
|
-
}
|
|
33552
|
-
}
|
|
33553
|
-
var ONBOARD_TIER_ORDER, TIER_CONFIG_KEYS, TIER_META;
|
|
33554
|
-
var init_onboard_tiers = __esm({
|
|
33555
|
-
"src/conversation/onboard-tiers.ts"() {
|
|
33556
|
-
"use strict";
|
|
33557
|
-
init_store();
|
|
33558
|
-
init_profile();
|
|
33559
|
-
init_repl_api();
|
|
33560
|
-
init_scenario_fit();
|
|
33561
|
-
ONBOARD_TIER_ORDER = [
|
|
33562
|
-
"profile",
|
|
33563
|
-
"domain",
|
|
33564
|
-
"demo",
|
|
33565
|
-
"production"
|
|
33566
|
-
];
|
|
33567
|
-
TIER_CONFIG_KEYS = {
|
|
33568
|
-
profile: "onboard-tier-profile",
|
|
33569
|
-
domain: "onboard-tier-domain",
|
|
33570
|
-
demo: "onboard-tier-demo",
|
|
33571
|
-
production: "onboard-tier-production"
|
|
33572
|
-
};
|
|
33573
|
-
TIER_META = {
|
|
33574
|
-
profile: {
|
|
33575
|
-
label: "Company profile",
|
|
33576
|
-
hint: "Name, industry, ICP \u2014 works without an API key"
|
|
33577
|
-
},
|
|
33578
|
-
domain: {
|
|
33579
|
-
label: "Domain research",
|
|
33580
|
-
hint: "Connect a key and let NTRP research your company"
|
|
33581
|
-
},
|
|
33582
|
-
demo: {
|
|
33583
|
-
label: "Sample data",
|
|
33584
|
-
hint: "Load a fitted demo book of business"
|
|
33585
|
-
},
|
|
33586
|
-
production: {
|
|
33587
|
-
label: "Your data",
|
|
33588
|
-
hint: "Drag-drop a CSV or folder path into the REPL"
|
|
33589
|
-
}
|
|
33590
|
-
};
|
|
33591
|
-
}
|
|
33592
|
-
});
|
|
33593
|
-
|
|
33594
34112
|
// src/conversation/voice-setup.ts
|
|
33595
34113
|
var voice_setup_exports = {};
|
|
33596
34114
|
__export(voice_setup_exports, {
|
|
@@ -33826,11 +34344,17 @@ async function runProfileTier(session, ctx, existing) {
|
|
|
33826
34344
|
invalidateTaxonomy();
|
|
33827
34345
|
creditOnboardComplete(ctx);
|
|
33828
34346
|
markOnboardTierComplete("profile");
|
|
34347
|
+
console.log();
|
|
34348
|
+
console.log(" " + chalk36.green("\u2713") + " " + bold(`Profile saved for ${candidate.company_name}`));
|
|
33829
34349
|
if (hasAnyLlmProvider()) {
|
|
33830
|
-
console.log();
|
|
33831
|
-
console.log(" " + chalk36.green("\u2713") + " " + bold(`Profile saved for ${candidate.company_name}`));
|
|
33832
34350
|
console.log(" " + chalk36.dim("A key is already connected \u2014 running domain research now."));
|
|
33833
34351
|
console.log();
|
|
34352
|
+
} else {
|
|
34353
|
+
console.log(" " + chalk36.dim(`~/.ntrp/profile.json`));
|
|
34354
|
+
console.log();
|
|
34355
|
+
await ensureLlmKeys(session);
|
|
34356
|
+
}
|
|
34357
|
+
if (hasAnyLlmProvider()) {
|
|
33834
34358
|
await runDomainResearchOnProfile(session, ctx, candidate);
|
|
33835
34359
|
markOnboardTierComplete("domain");
|
|
33836
34360
|
await offerInboxSkillSetup(session, { beat: "production" });
|
|
@@ -33839,10 +34363,6 @@ async function runProfileTier(session, ctx, existing) {
|
|
|
33839
34363
|
printNextTierHint(ctx);
|
|
33840
34364
|
return `Profile + domain research saved for ${candidate.company_name}`;
|
|
33841
34365
|
}
|
|
33842
|
-
console.log();
|
|
33843
|
-
console.log(" " + chalk36.green("\u2713") + " " + bold(`Profile saved for ${candidate.company_name}`));
|
|
33844
|
-
console.log(" " + chalk36.dim(`~/.ntrp/profile.json`));
|
|
33845
|
-
console.log();
|
|
33846
34366
|
await offerInboxSkillSetup(session, { beat: "production" });
|
|
33847
34367
|
const { offerVoiceSetup: offerVoiceSetup2 } = await Promise.resolve().then(() => (init_voice_setup(), voice_setup_exports));
|
|
33848
34368
|
await offerVoiceSetup2(ctx, session);
|
|
@@ -36045,7 +36565,8 @@ async function executeApprovedAction(id) {
|
|
|
36045
36565
|
title: typeof proposal.title === "string" ? proposal.title : "Repository export"
|
|
36046
36566
|
});
|
|
36047
36567
|
}
|
|
36048
|
-
} catch {
|
|
36568
|
+
} catch (err) {
|
|
36569
|
+
debugError("actions.recordExportWrite", err);
|
|
36049
36570
|
}
|
|
36050
36571
|
const executionId = await insertActionExecution({
|
|
36051
36572
|
proposal_id: proposal.id,
|
|
@@ -36078,6 +36599,7 @@ var init_actions = __esm({
|
|
|
36078
36599
|
init_errors2();
|
|
36079
36600
|
init_types2();
|
|
36080
36601
|
init_publish();
|
|
36602
|
+
init_diagnostics();
|
|
36081
36603
|
}
|
|
36082
36604
|
});
|
|
36083
36605
|
|
|
@@ -37410,10 +37932,7 @@ function renderCraftResult(result) {
|
|
|
37410
37932
|
console.log();
|
|
37411
37933
|
console.log(" " + chalk48.dim("(No plan produced)"));
|
|
37412
37934
|
}
|
|
37413
|
-
|
|
37414
|
-
console.log(" " + chalk48.dim(notice));
|
|
37415
|
-
}
|
|
37416
|
-
printLlmAttribution(result.usage);
|
|
37935
|
+
printStrategyBriefFooter(result.notices, result.usage);
|
|
37417
37936
|
printCraftWrapUp({
|
|
37418
37937
|
status: result.status,
|
|
37419
37938
|
library_path: result.library_path,
|
|
@@ -37537,7 +38056,6 @@ var init_strategy2 = __esm({
|
|
|
37537
38056
|
init_repl_api();
|
|
37538
38057
|
init_strategist_run();
|
|
37539
38058
|
init_strategy_brief();
|
|
37540
|
-
init_llm_attribution();
|
|
37541
38059
|
RESERVED_SUBCOMMANDS = /* @__PURE__ */ new Set(["ingest", "add", "list", "show", "sync", "sources", "review", "craft"]);
|
|
37542
38060
|
}
|
|
37543
38061
|
});
|
|
@@ -38666,18 +39184,7 @@ var init_setup2 = __esm({
|
|
|
38666
39184
|
|
|
38667
39185
|
// src/services/ask.ts
|
|
38668
39186
|
async function ensureAskSnapshot(ctx) {
|
|
38669
|
-
|
|
38670
|
-
if (!snapshot) {
|
|
38671
|
-
snapshot = await computeFullHealth();
|
|
38672
|
-
ctx.snapshot.computeResult = snapshot;
|
|
38673
|
-
const divInput = snapshot.segments.map((s) => ({
|
|
38674
|
-
segmentId: s.segment.id,
|
|
38675
|
-
segmentName: s.segment.name,
|
|
38676
|
-
result: s.result
|
|
38677
|
-
}));
|
|
38678
|
-
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
38679
|
-
}
|
|
38680
|
-
return snapshot;
|
|
39187
|
+
return ensureHealthSnapshot(ctx);
|
|
38681
39188
|
}
|
|
38682
39189
|
async function* streamAsk(question, ctx) {
|
|
38683
39190
|
if (isSmokeProtocolTrigger(question)) {
|
|
@@ -38701,7 +39208,10 @@ async function* streamAsk(question, ctx) {
|
|
|
38701
39208
|
await hydrateAnalysisFromPersistedState(ctx);
|
|
38702
39209
|
const snapshot = await ensureAskSnapshot(ctx);
|
|
38703
39210
|
const { buildMemoryBlock: buildMemoryBlock2 } = await Promise.resolve().then(() => (init_store2(), store_exports2));
|
|
38704
|
-
const memoryBlock = await buildMemoryBlock2(question).catch(() =>
|
|
39211
|
+
const memoryBlock = await buildMemoryBlock2(question).catch((err) => {
|
|
39212
|
+
debugError("ask.buildMemoryBlock", err);
|
|
39213
|
+
return "";
|
|
39214
|
+
});
|
|
38705
39215
|
const bundle = await loadSessionAnalysisBundle();
|
|
38706
39216
|
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
38707
39217
|
const responseMode = resolveExploreResponseMode(question, ctx, ctx.conversation.length);
|
|
@@ -38752,13 +39262,13 @@ var init_ask = __esm({
|
|
|
38752
39262
|
"use strict";
|
|
38753
39263
|
init_agentic_loop();
|
|
38754
39264
|
init_explore_mode();
|
|
38755
|
-
|
|
38756
|
-
init_divergence();
|
|
39265
|
+
init_context_snapshot();
|
|
38757
39266
|
init_repl_api();
|
|
38758
39267
|
init_context2();
|
|
38759
39268
|
init_situation();
|
|
38760
39269
|
init_session_analysis();
|
|
38761
39270
|
init_smoke_protocol();
|
|
39271
|
+
init_diagnostics();
|
|
38762
39272
|
}
|
|
38763
39273
|
});
|
|
38764
39274
|
|
|
@@ -38950,12 +39460,6 @@ var init_metrics = __esm({
|
|
|
38950
39460
|
});
|
|
38951
39461
|
|
|
38952
39462
|
// src/ai/feedback-apply.ts
|
|
38953
|
-
function stripFences5(text) {
|
|
38954
|
-
const trimmed = text.trim();
|
|
38955
|
-
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
38956
|
-
if (fenced) return fenced[1].trim();
|
|
38957
|
-
return trimmed;
|
|
38958
|
-
}
|
|
38959
39463
|
function formatProfile(p) {
|
|
38960
39464
|
const parts = [];
|
|
38961
39465
|
parts.push(`Industry: ${p.industry}`);
|
|
@@ -38981,7 +39485,7 @@ OPERATOR FEEDBACK:
|
|
|
38981
39485
|
|
|
38982
39486
|
Apply the feedback now as STRICT JSON.`;
|
|
38983
39487
|
const { text } = await llmCompleteText("feedback", SYSTEM_PROMPT5, userMessage, 1024, ctx);
|
|
38984
|
-
const cleaned =
|
|
39488
|
+
const cleaned = stripJsonFences(text);
|
|
38985
39489
|
let parsed;
|
|
38986
39490
|
try {
|
|
38987
39491
|
parsed = JSON.parse(cleaned);
|
|
@@ -39033,6 +39537,7 @@ var init_feedback_apply = __esm({
|
|
|
39033
39537
|
"use strict";
|
|
39034
39538
|
init_repl_api();
|
|
39035
39539
|
init_complete();
|
|
39540
|
+
init_json_response();
|
|
39036
39541
|
ALLOWED_MOTIONS3 = /* @__PURE__ */ new Set(["plg", "smb_velocity", "mid_market", "enterprise"]);
|
|
39037
39542
|
ALLOWED_CRMS3 = /* @__PURE__ */ new Set(["salesforce", "hubspot", "pipedrive", "other"]);
|
|
39038
39543
|
ALLOWED_ENGAGEMENT3 = /* @__PURE__ */ new Set(["outreach", "salesloft", "apollo", "none"]);
|
|
@@ -39388,7 +39893,8 @@ function recordFeedback(input) {
|
|
|
39388
39893
|
};
|
|
39389
39894
|
try {
|
|
39390
39895
|
appendFileSync7(join34(getMemoryDir(), "feedback.jsonl"), JSON.stringify(entry) + "\n");
|
|
39391
|
-
} catch {
|
|
39896
|
+
} catch (err) {
|
|
39897
|
+
debugError("memory.feedback.append", err);
|
|
39392
39898
|
}
|
|
39393
39899
|
if (input.rating === "positive") {
|
|
39394
39900
|
addFact({
|
|
@@ -39421,6 +39927,7 @@ var init_feedback2 = __esm({
|
|
|
39421
39927
|
"use strict";
|
|
39422
39928
|
init_store();
|
|
39423
39929
|
init_store2();
|
|
39930
|
+
init_diagnostics();
|
|
39424
39931
|
}
|
|
39425
39932
|
});
|
|
39426
39933
|
|
|
@@ -43524,7 +44031,6 @@ async function runConversationCompute(ctx) {
|
|
|
43524
44031
|
const summary = await diagnose(["--compact"], ctx);
|
|
43525
44032
|
markLensCompleted(ctx, "gtm_health");
|
|
43526
44033
|
ctx.stage = "analyzed";
|
|
43527
|
-
ctx.snapshot.computeResult = null;
|
|
43528
44034
|
invalidateGapAudit(ctx);
|
|
43529
44035
|
saveSessionState(ctx);
|
|
43530
44036
|
await resumeQueuedStrategist(ctx);
|
|
@@ -43593,7 +44099,7 @@ __export(ingest_chat_exports, {
|
|
|
43593
44099
|
});
|
|
43594
44100
|
import { existsSync as existsSync38, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
|
|
43595
44101
|
import { basename as basename10, join as join39, resolve as resolve9 } from "path";
|
|
43596
|
-
import { homedir as
|
|
44102
|
+
import { homedir as homedir7 } from "os";
|
|
43597
44103
|
import chalk85 from "chalk";
|
|
43598
44104
|
function extractFilePath(input) {
|
|
43599
44105
|
const trimmed = input.trim().replace(/^["']|["']$/g, "");
|
|
@@ -43634,7 +44140,7 @@ function looksLikePathToken(token) {
|
|
|
43634
44140
|
return false;
|
|
43635
44141
|
}
|
|
43636
44142
|
function expandPath(p) {
|
|
43637
|
-
if (p.startsWith("~/")) return resolve9(
|
|
44143
|
+
if (p.startsWith("~/")) return resolve9(homedir7(), p.slice(2));
|
|
43638
44144
|
return resolve9(p);
|
|
43639
44145
|
}
|
|
43640
44146
|
function looksLikeFilePath(input) {
|
|
@@ -44165,7 +44671,8 @@ function shouldOfferFirstRunFork() {
|
|
|
44165
44671
|
markFirstRunCompleted();
|
|
44166
44672
|
return false;
|
|
44167
44673
|
}
|
|
44168
|
-
} catch {
|
|
44674
|
+
} catch (err) {
|
|
44675
|
+
debugError("firstRun.sessionScan", err);
|
|
44169
44676
|
}
|
|
44170
44677
|
return true;
|
|
44171
44678
|
}
|
|
@@ -44178,7 +44685,8 @@ async function runFirstRunFork(ctx, options = {}) {
|
|
|
44178
44685
|
try {
|
|
44179
44686
|
const { offerFirstRunTour: offerFirstRunTour2 } = await Promise.resolve().then(() => (init_metric_tour(), metric_tour_exports));
|
|
44180
44687
|
await offerFirstRunTour2(ctx);
|
|
44181
|
-
} catch {
|
|
44688
|
+
} catch (err) {
|
|
44689
|
+
debugError("firstRun.tour", err);
|
|
44182
44690
|
}
|
|
44183
44691
|
const session = createPromptSession(ctx.rl, ctx);
|
|
44184
44692
|
try {
|
|
@@ -44306,7 +44814,8 @@ async function loadFirstRunDemo(ctx, scenario) {
|
|
|
44306
44814
|
let counts = {};
|
|
44307
44815
|
try {
|
|
44308
44816
|
counts = await getEntityCounts2();
|
|
44309
|
-
} catch {
|
|
44817
|
+
} catch (err) {
|
|
44818
|
+
debugError("firstRun.entityCounts", err);
|
|
44310
44819
|
}
|
|
44311
44820
|
ctx.dataset = {
|
|
44312
44821
|
label: `${scenario} demo`,
|
|
@@ -44334,6 +44843,7 @@ var init_first_run = __esm({
|
|
|
44334
44843
|
init_repl_globals();
|
|
44335
44844
|
init_global_admin();
|
|
44336
44845
|
init_activation();
|
|
44846
|
+
init_diagnostics();
|
|
44337
44847
|
FIRST_RUN_KEY = "first-run-completed";
|
|
44338
44848
|
}
|
|
44339
44849
|
});
|
|
@@ -45619,7 +46129,7 @@ function pickGhostHint(ctx) {
|
|
|
45619
46129
|
activeGhostHint = null;
|
|
45620
46130
|
if (shouldSuppressInlineSuggestion(ctx)) return;
|
|
45621
46131
|
if (resolveRecommendedAction(ctx)) return;
|
|
45622
|
-
const hints =
|
|
46132
|
+
const hints = ghostHintsForPhase(resolveConversationPhase(ctx), ctx);
|
|
45623
46133
|
if (!hints || hints.length === 0) return;
|
|
45624
46134
|
activeGhostHint = hints[ghostHintTurn++ % hints.length] ?? null;
|
|
45625
46135
|
}
|
|
@@ -45726,7 +46236,7 @@ async function runRepl(ctx, version) {
|
|
|
45726
46236
|
if (result.latest === ctx.updateHomePaintedLatest) return;
|
|
45727
46237
|
console.log(` ${formatUpdateNudge(result.current, result.latest)}`);
|
|
45728
46238
|
console.log();
|
|
45729
|
-
});
|
|
46239
|
+
}).catch((err) => debugError("repl.pendingUpdateCheck", err));
|
|
45730
46240
|
}
|
|
45731
46241
|
let pendingSuggestionRender = null;
|
|
45732
46242
|
const cancelPendingSuggestionRender = () => {
|
|
@@ -45786,7 +46296,8 @@ async function runRepl(ctx, version) {
|
|
|
45786
46296
|
const answer = rl.question(prompt);
|
|
45787
46297
|
scheduleInlineSuggestionRender();
|
|
45788
46298
|
rawLine = await answer;
|
|
45789
|
-
} catch {
|
|
46299
|
+
} catch (err) {
|
|
46300
|
+
debugError("repl.readLine", err);
|
|
45790
46301
|
resumeTranscriptCapture();
|
|
45791
46302
|
break;
|
|
45792
46303
|
}
|
|
@@ -45995,6 +46506,7 @@ var init_repl = __esm({
|
|
|
45995
46506
|
init_deepdive_complete();
|
|
45996
46507
|
init_thinkwithme_complete();
|
|
45997
46508
|
init_voice_complete();
|
|
46509
|
+
init_diagnostics();
|
|
45998
46510
|
init_inline_suggestion();
|
|
45999
46511
|
REPL_BUILTINS = [
|
|
46000
46512
|
"/help",
|
|
@@ -46060,6 +46572,7 @@ init_theme();
|
|
|
46060
46572
|
init_layout();
|
|
46061
46573
|
init_emit();
|
|
46062
46574
|
init_errors2();
|
|
46575
|
+
init_diagnostics();
|
|
46063
46576
|
init_types2();
|
|
46064
46577
|
init_version();
|
|
46065
46578
|
import chalk94 from "chalk";
|
|
@@ -46084,6 +46597,7 @@ async function closeDb() {
|
|
|
46084
46597
|
const { close: close2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
|
|
46085
46598
|
await close2();
|
|
46086
46599
|
}
|
|
46600
|
+
var fatalReporting = { command: "ntrp", structured: false };
|
|
46087
46601
|
async function main() {
|
|
46088
46602
|
ensureLlmConfigMigrated();
|
|
46089
46603
|
const args = parseArgs(process.argv);
|
|
@@ -46099,6 +46613,10 @@ async function main() {
|
|
|
46099
46613
|
if (!ctx.execution.color) {
|
|
46100
46614
|
chalk94.level = 0;
|
|
46101
46615
|
}
|
|
46616
|
+
fatalReporting = {
|
|
46617
|
+
command: firstToken(args.input) || "ntrp",
|
|
46618
|
+
structured: isStructuredOutput(ctx.execution)
|
|
46619
|
+
};
|
|
46102
46620
|
if (args.globals.stdin) {
|
|
46103
46621
|
args.input = (await readStdin()).trim();
|
|
46104
46622
|
}
|
|
@@ -46168,7 +46686,7 @@ async function main() {
|
|
|
46168
46686
|
printTrialNudge2(lic);
|
|
46169
46687
|
}
|
|
46170
46688
|
}
|
|
46171
|
-
void Promise.resolve().then(() => (init_discovery(), discovery_exports)).then((m) => m.refreshStaleProviderCaches()).catch(() =>
|
|
46689
|
+
void Promise.resolve().then(() => (init_discovery(), discovery_exports)).then((m) => m.refreshStaleProviderCaches()).catch((err) => debugError("llm.refreshStaleProviderCaches", err));
|
|
46172
46690
|
const { setActiveDbPath: setActiveDbPath2 } = await Promise.resolve().then(() => (init_connection(), connection_exports));
|
|
46173
46691
|
const { datasetPathForSession: datasetPathForSession2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
|
|
46174
46692
|
ctx.datasetPath = datasetPathForSession2(ctx.sessionId);
|
|
@@ -46197,10 +46715,25 @@ async function main() {
|
|
|
46197
46715
|
await closeDb();
|
|
46198
46716
|
process.exit(0);
|
|
46199
46717
|
}
|
|
46200
|
-
|
|
46201
|
-
|
|
46202
|
-
closeDb().
|
|
46718
|
+
async function die(err, scope) {
|
|
46719
|
+
debugError(scope, err);
|
|
46720
|
+
await closeDb().catch((closeErr) => debugError("db.close", closeErr));
|
|
46721
|
+
if (fatalReporting.structured) {
|
|
46722
|
+
emitError(fatalReporting.command, err);
|
|
46723
|
+
}
|
|
46724
|
+
console.error(chalk94.red(` ${describeError(err)}`));
|
|
46725
|
+
if (!(err instanceof NtrpError) && err instanceof Error && err.stack) {
|
|
46726
|
+
console.error(chalk94.dim(err.stack.split("\n").slice(1).join("\n")));
|
|
46727
|
+
}
|
|
46728
|
+
process.exit(err instanceof NtrpError ? err.exitCode : 1 /* RuntimeError */);
|
|
46729
|
+
}
|
|
46730
|
+
process.on("unhandledRejection", (reason) => {
|
|
46731
|
+
void die(reason, "unhandledRejection");
|
|
46732
|
+
});
|
|
46733
|
+
process.on("uncaughtException", (err) => {
|
|
46734
|
+
void die(err, "uncaughtException");
|
|
46203
46735
|
});
|
|
46736
|
+
main().then((shouldCloseDb) => shouldCloseDb ? closeDb() : void 0).catch((err) => die(err, "main"));
|
|
46204
46737
|
async function readStdin() {
|
|
46205
46738
|
const chunks = [];
|
|
46206
46739
|
for await (const chunk of process.stdin) {
|