@sonnechasser/ntrp 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/findings-stream-smoke.js +185 -0
- package/dist/ai/findings-stream-smoke.js.map +1 -0
- package/dist/ai/guardrails-smoke.js +2143 -323
- package/dist/ai/guardrails-smoke.js.map +1 -1
- package/dist/conversation/loop-guard-smoke.js +20395 -9338
- package/dist/conversation/loop-guard-smoke.js.map +1 -1
- package/dist/demo/whimsy-smoke.js +5 -0
- package/dist/demo/whimsy-smoke.js.map +1 -1
- package/dist/index.js +8179 -7211
- package/dist/index.js.map +1 -1
- package/dist/investigation/quality-eval-cli.js +21742 -0
- package/dist/investigation/quality-eval-cli.js.map +1 -0
- package/dist/investigation/verbosity-cli.js +1977 -329
- package/dist/investigation/verbosity-cli.js.map +1 -1
- package/dist/mcp/server.js +6286 -5191
- package/dist/mcp/server.js.map +1 -1
- package/dist/services/transcript-smoke.js +1 -0
- package/dist/services/transcript-smoke.js.map +1 -1
- package/dist/strategist/strategist-smoke.js +233 -10
- package/dist/strategist/strategist-smoke.js.map +1 -1
- package/dist/whimsy/time-bank-smoke.js +4910 -3329
- package/dist/whimsy/time-bank-smoke.js.map +1 -1
- package/package.json +3 -1
|
@@ -62,6 +62,10 @@ var init_context = __esm({
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
// src/output/formatters.ts
|
|
65
|
+
function formatDollarImpact(value, label) {
|
|
66
|
+
if (value != null && value > 0) return `${formatDollarValue(value)} ${label ?? ""}`.trim();
|
|
67
|
+
return "N/A";
|
|
68
|
+
}
|
|
65
69
|
function formatScore(score) {
|
|
66
70
|
return `${Math.round(score)}`;
|
|
67
71
|
}
|
|
@@ -685,20 +689,20 @@ function isClosedConnectionError(err) {
|
|
|
685
689
|
function closeConnection(c) {
|
|
686
690
|
const close2 = c.close;
|
|
687
691
|
if (typeof close2 !== "function") return Promise.resolve();
|
|
688
|
-
return new Promise((
|
|
692
|
+
return new Promise((resolve8) => {
|
|
689
693
|
try {
|
|
690
|
-
close2.call(c, () =>
|
|
694
|
+
close2.call(c, () => resolve8());
|
|
691
695
|
} catch {
|
|
692
|
-
|
|
696
|
+
resolve8();
|
|
693
697
|
}
|
|
694
698
|
});
|
|
695
699
|
}
|
|
696
700
|
function isConnectionAlive(c) {
|
|
697
|
-
return new Promise((
|
|
701
|
+
return new Promise((resolve8) => {
|
|
698
702
|
try {
|
|
699
|
-
c.all("SELECT 1", (err) =>
|
|
703
|
+
c.all("SELECT 1", (err) => resolve8(!err));
|
|
700
704
|
} catch {
|
|
701
|
-
|
|
705
|
+
resolve8(false);
|
|
702
706
|
}
|
|
703
707
|
});
|
|
704
708
|
}
|
|
@@ -712,8 +716,8 @@ async function discardConnection() {
|
|
|
712
716
|
await closeConnection(currentConn).catch(() => void 0);
|
|
713
717
|
}
|
|
714
718
|
if (currentDb) {
|
|
715
|
-
await new Promise((
|
|
716
|
-
currentDb.close(() =>
|
|
719
|
+
await new Promise((resolve8) => {
|
|
720
|
+
currentDb.close(() => resolve8());
|
|
717
721
|
}).catch(() => void 0);
|
|
718
722
|
}
|
|
719
723
|
}
|
|
@@ -728,10 +732,10 @@ async function withReconnect(op) {
|
|
|
728
732
|
}
|
|
729
733
|
async function execAllOnce(sql, params) {
|
|
730
734
|
const c = await getConnection();
|
|
731
|
-
return new Promise((
|
|
735
|
+
return new Promise((resolve8, reject) => {
|
|
732
736
|
const cb = (err, rows) => {
|
|
733
737
|
if (err) reject(err);
|
|
734
|
-
else
|
|
738
|
+
else resolve8(rows ?? []);
|
|
735
739
|
};
|
|
736
740
|
if (params.length > 0) {
|
|
737
741
|
const stmt = c.prepare(sql);
|
|
@@ -746,18 +750,18 @@ async function execAllOnce(sql, params) {
|
|
|
746
750
|
}
|
|
747
751
|
async function runOnce(sql, params = []) {
|
|
748
752
|
const c = await getConnection();
|
|
749
|
-
return new Promise((
|
|
753
|
+
return new Promise((resolve8, reject) => {
|
|
750
754
|
if (params.length > 0) {
|
|
751
755
|
const stmt = c.prepare(sql);
|
|
752
756
|
stmt.run(...params, (err) => {
|
|
753
757
|
stmt.finalize();
|
|
754
758
|
if (err) reject(err);
|
|
755
|
-
else
|
|
759
|
+
else resolve8();
|
|
756
760
|
});
|
|
757
761
|
} else {
|
|
758
762
|
c.run(sql, (err) => {
|
|
759
763
|
if (err) reject(err);
|
|
760
|
-
else
|
|
764
|
+
else resolve8();
|
|
761
765
|
});
|
|
762
766
|
}
|
|
763
767
|
});
|
|
@@ -2381,20 +2385,12 @@ function bold(text) {
|
|
|
2381
2385
|
}
|
|
2382
2386
|
function badge(label, tone = "muted") {
|
|
2383
2387
|
const normalized = ` ${label.toUpperCase()} `;
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
return chalk.hex(TOKENS.warning)(normalized);
|
|
2389
|
-
case "error":
|
|
2390
|
-
return chalk.hex(TOKENS.error)(normalized);
|
|
2391
|
-
case "info":
|
|
2392
|
-
return chalk.hex(TOKENS.info)(normalized);
|
|
2393
|
-
case "accent":
|
|
2394
|
-
return chalk.hex(TOKENS.accent)(normalized);
|
|
2395
|
-
case "muted":
|
|
2396
|
-
return chalk.dim(normalized);
|
|
2388
|
+
if (tone === "muted") return chalk.dim(normalized);
|
|
2389
|
+
const color = BADGE_TONE_COLORS[tone];
|
|
2390
|
+
if (chalk.level >= 2) {
|
|
2391
|
+
return chalk.bgHex(color).hex(BADGE_TEXT).bold(normalized);
|
|
2397
2392
|
}
|
|
2393
|
+
return chalk.hex(color)(normalized);
|
|
2398
2394
|
}
|
|
2399
2395
|
function sectionHeading(label) {
|
|
2400
2396
|
return `${paint("accent", "\u25B8")} ${paint("accent", bold(label))}`;
|
|
@@ -2403,9 +2399,27 @@ function actionHint(label, command, detail) {
|
|
|
2403
2399
|
const suffix = detail ? chalk.dim(` ${detail}`) : "";
|
|
2404
2400
|
return `${chalk.dim(label)} ${paint("accent", command)}${suffix}`;
|
|
2405
2401
|
}
|
|
2402
|
+
function statusDot(status) {
|
|
2403
|
+
if (status === "neutral") return chalk.dim("\u25CB");
|
|
2404
|
+
return chalk.hex(STATUS[status])("\u25CF");
|
|
2405
|
+
}
|
|
2406
|
+
function statusPaint(status) {
|
|
2407
|
+
if (status === "neutral") return chalk.dim;
|
|
2408
|
+
return chalk.hex(STATUS[status]);
|
|
2409
|
+
}
|
|
2410
|
+
function severityPaint(severity) {
|
|
2411
|
+
switch (severity) {
|
|
2412
|
+
case "critical":
|
|
2413
|
+
return chalk.hex(STATUS.red);
|
|
2414
|
+
case "warning":
|
|
2415
|
+
return chalk.hex(STATUS.yellow);
|
|
2416
|
+
default:
|
|
2417
|
+
return chalk.hex(TOKENS.info);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2406
2420
|
function scoreBar(score, status, width = 14) {
|
|
2407
2421
|
const filled = Math.round(score / 100 * width);
|
|
2408
|
-
const color = chalk.hex(
|
|
2422
|
+
const color = chalk.hex(STATUS[status]);
|
|
2409
2423
|
let filledPart = "";
|
|
2410
2424
|
for (let i = 0; i < filled; i++) {
|
|
2411
2425
|
filledPart += i % 2 === 0 ? "\u2588" : "\u2593";
|
|
@@ -2413,15 +2427,16 @@ function scoreBar(score, status, width = 14) {
|
|
|
2413
2427
|
const emptyPart = "\u2591".repeat(width - filled);
|
|
2414
2428
|
return color(filledPart) + chalk.dim(emptyPart);
|
|
2415
2429
|
}
|
|
2416
|
-
var
|
|
2430
|
+
var STATUS, TOKENS, BADGE_TONE_COLORS, BADGE_TEXT;
|
|
2417
2431
|
var init_theme = __esm({
|
|
2418
2432
|
"src/ui/theme.ts"() {
|
|
2419
2433
|
"use strict";
|
|
2420
2434
|
init_formatters();
|
|
2421
|
-
|
|
2435
|
+
STATUS = {
|
|
2422
2436
|
green: "#22c55e",
|
|
2423
2437
|
yellow: "#eab308",
|
|
2424
|
-
red: "#ef4444"
|
|
2438
|
+
red: "#ef4444",
|
|
2439
|
+
neutral: "#64748b"
|
|
2425
2440
|
};
|
|
2426
2441
|
TOKENS = {
|
|
2427
2442
|
accent: "#14b8a6",
|
|
@@ -2430,11 +2445,20 @@ var init_theme = __esm({
|
|
|
2430
2445
|
borderMuted: "#1e293b",
|
|
2431
2446
|
dim: "#64748b",
|
|
2432
2447
|
text: "#e2e8f0",
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
success:
|
|
2436
|
-
|
|
2448
|
+
info: "#3b82f6",
|
|
2449
|
+
...STATUS,
|
|
2450
|
+
success: STATUS.green,
|
|
2451
|
+
warning: STATUS.yellow,
|
|
2452
|
+
error: STATUS.red
|
|
2453
|
+
};
|
|
2454
|
+
BADGE_TONE_COLORS = {
|
|
2455
|
+
success: TOKENS.success,
|
|
2456
|
+
warning: TOKENS.warning,
|
|
2457
|
+
error: TOKENS.error,
|
|
2458
|
+
info: TOKENS.info,
|
|
2459
|
+
accent: TOKENS.accent
|
|
2437
2460
|
};
|
|
2461
|
+
BADGE_TEXT = "#0f172a";
|
|
2438
2462
|
}
|
|
2439
2463
|
});
|
|
2440
2464
|
|
|
@@ -3009,13 +3033,22 @@ function printTimeBankCelebration(milestone, totalMinutes) {
|
|
|
3009
3033
|
seed: rotationSeed(state2) + 1
|
|
3010
3034
|
});
|
|
3011
3035
|
console.log();
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3036
|
+
const head = paint("accent", `\u2726 ${milestone.title}`) + chalk2.dim(` \u2014 ${formatHoursLabel(totalHours)} saved`);
|
|
3037
|
+
const tail = perspective ? chalk2.dim(" \xB7 ") + chalk2.dim.italic(formatPerspectiveLine(perspective, totalHours)) : "";
|
|
3038
|
+
console.log(" " + head + tail);
|
|
3039
|
+
const message = stripLeadingTitle(milestone.message, milestone.title);
|
|
3040
|
+
if (message) {
|
|
3041
|
+
console.log(" " + chalk2.dim(message));
|
|
3016
3042
|
}
|
|
3017
3043
|
console.log();
|
|
3018
3044
|
}
|
|
3045
|
+
function stripLeadingTitle(message, title) {
|
|
3046
|
+
const trimmed = message.trim();
|
|
3047
|
+
if (trimmed.toLowerCase().startsWith(title.toLowerCase())) {
|
|
3048
|
+
return trimmed.slice(title.length).replace(/^[.!,:;\s—–-]+/, "").trim();
|
|
3049
|
+
}
|
|
3050
|
+
return trimmed;
|
|
3051
|
+
}
|
|
3019
3052
|
function formatHoursLabel(hours) {
|
|
3020
3053
|
if (hours < 1) return `${Math.round(hours * 60)}m`;
|
|
3021
3054
|
if (hours < 10) return `${hours.toFixed(1)}h`;
|
|
@@ -4383,6 +4416,14 @@ function resolveEffectiveTier(ctx, surface) {
|
|
|
4383
4416
|
function resolveEffectiveModelOverride(ctx) {
|
|
4384
4417
|
return getSessionModelOverride(ctx) ?? loadLlmConfig().modelOverride;
|
|
4385
4418
|
}
|
|
4419
|
+
function resolveModelForActive(ctx, surface) {
|
|
4420
|
+
const provider = resolveActiveProvider(ctx);
|
|
4421
|
+
const tier = resolveEffectiveTier(ctx, surface);
|
|
4422
|
+
const override = resolveEffectiveModelOverride(ctx);
|
|
4423
|
+
const providerOverride = overrideForProvider(override, provider, provider);
|
|
4424
|
+
const modelId = resolveModelSafe(provider, tier, providerOverride);
|
|
4425
|
+
return { provider, tier, modelId };
|
|
4426
|
+
}
|
|
4386
4427
|
function resolveProviderOrder(ctx) {
|
|
4387
4428
|
const active = resolveActiveProvider(ctx);
|
|
4388
4429
|
const order = [active];
|
|
@@ -4396,6 +4437,10 @@ function resolveProviderOrder(ctx) {
|
|
|
4396
4437
|
}
|
|
4397
4438
|
return order;
|
|
4398
4439
|
}
|
|
4440
|
+
function formatActiveStackShort(ctx, surface = "agentic_investigation") {
|
|
4441
|
+
const { provider, tier } = resolveModelForActive(ctx, surface);
|
|
4442
|
+
return `${provider} \xB7 ${tier}`;
|
|
4443
|
+
}
|
|
4399
4444
|
var init_session_state = __esm({
|
|
4400
4445
|
"src/ai/llm/session-state.ts"() {
|
|
4401
4446
|
"use strict";
|
|
@@ -6297,7 +6342,7 @@ async function distillSessionFactsWithTimeout(ctx, sessionId, timeoutMs = DISTIL
|
|
|
6297
6342
|
});
|
|
6298
6343
|
const raced = await Promise.race([
|
|
6299
6344
|
work,
|
|
6300
|
-
new Promise((
|
|
6345
|
+
new Promise((resolve8) => setTimeout(() => resolve8(-1), timeoutMs))
|
|
6301
6346
|
]);
|
|
6302
6347
|
if (raced >= 0) return { count: raced, background };
|
|
6303
6348
|
if (settled) return { count: await background, background };
|
|
@@ -6383,7 +6428,9 @@ function isSessionStale(s) {
|
|
|
6383
6428
|
return Date.now() - s.mtime > STALE_SESSION_MS;
|
|
6384
6429
|
}
|
|
6385
6430
|
function isAnalysisReady(ctx) {
|
|
6386
|
-
if (ctx.stage !== "analyzed" || ctx.analysis.completed.length === 0)
|
|
6431
|
+
if (ctx.stage !== "analyzed" && ctx.stage !== "delivered" || ctx.analysis.completed.length === 0) {
|
|
6432
|
+
return false;
|
|
6433
|
+
}
|
|
6387
6434
|
if (!ctx.dataset) return false;
|
|
6388
6435
|
const counts = ctx.dataset.counts ?? {};
|
|
6389
6436
|
return Object.values(counts).some((n) => n > 0);
|
|
@@ -6470,6 +6517,7 @@ function buildSessionFileSnapshot(ctx) {
|
|
|
6470
6517
|
if (ctx.attachments && ctx.attachments.length > 0) file.attachments = ctx.attachments;
|
|
6471
6518
|
if (ctx.llm && Object.keys(ctx.llm).length > 0) file.llm = ctx.llm;
|
|
6472
6519
|
if (ctx.strategistState) file.strategist = ctx.strategistState;
|
|
6520
|
+
if (ctx.pendingAsk) file.pending_ask = ctx.pendingAsk;
|
|
6473
6521
|
return file;
|
|
6474
6522
|
}
|
|
6475
6523
|
function defaultSessionAnalysis(primary = "gtm_health") {
|
|
@@ -6812,6 +6860,9 @@ async function finalizeSession(ctx, stage) {
|
|
|
6812
6860
|
if (ctx.strategistState) {
|
|
6813
6861
|
file.strategist = ctx.strategistState;
|
|
6814
6862
|
}
|
|
6863
|
+
if (ctx.pendingAsk) {
|
|
6864
|
+
file.pending_ask = ctx.pendingAsk;
|
|
6865
|
+
}
|
|
6815
6866
|
try {
|
|
6816
6867
|
writeFileSync11(ctx.sessionFile, JSON.stringify(file, null, 2) + "\n");
|
|
6817
6868
|
} catch {
|
|
@@ -6880,10 +6931,12 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
6880
6931
|
ctx.attachments = opts.attachments ?? [];
|
|
6881
6932
|
ctx.llm = opts.llm;
|
|
6882
6933
|
ctx.strategistState = opts.strategistState;
|
|
6934
|
+
ctx.pendingAsk = opts.pendingAsk;
|
|
6883
6935
|
ctx.gapAudit = void 0;
|
|
6884
6936
|
ctx.deliverIntent = false;
|
|
6885
6937
|
ctx.computeInProgress = false;
|
|
6886
6938
|
ctx.wizardDepth = 0;
|
|
6939
|
+
ctx.welcomeLogoShown = false;
|
|
6887
6940
|
ctx.snapshot = { computeResult: null, divergences: [] };
|
|
6888
6941
|
rebindSessionTranscript(ctx);
|
|
6889
6942
|
}
|
|
@@ -9985,7 +10038,42 @@ var init_explore_mode = __esm({
|
|
|
9985
10038
|
}
|
|
9986
10039
|
});
|
|
9987
10040
|
|
|
10041
|
+
// src/conversation/recommended-action.ts
|
|
10042
|
+
function resolveRecommendedAction(ctx) {
|
|
10043
|
+
const phase = resolveConversationPhase(ctx);
|
|
10044
|
+
switch (phase) {
|
|
10045
|
+
case "explore":
|
|
10046
|
+
if (ctx.stage === "delivered") return { submit: "/end", hint: "home" };
|
|
10047
|
+
return canUseReplAi(ctx) ? null : { submit: "/connect", hint: "/connect" };
|
|
10048
|
+
case "awaiting_data":
|
|
10049
|
+
if (ctx.gapAudit?.can_compute) return { submit: "go ahead", hint: "go ahead" };
|
|
10050
|
+
if (!sessionHasData(ctx)) return { submit: "use demo data", hint: "use demo data" };
|
|
10051
|
+
return null;
|
|
10052
|
+
case "scope":
|
|
10053
|
+
return { submit: "yes", hint: "yes" };
|
|
10054
|
+
case "strategize":
|
|
10055
|
+
return ctx.strategistState?.step === "objective_confirm" ? { submit: "yes", hint: "yes" } : null;
|
|
10056
|
+
default:
|
|
10057
|
+
return null;
|
|
10058
|
+
}
|
|
10059
|
+
}
|
|
10060
|
+
var init_recommended_action = __esm({
|
|
10061
|
+
"src/conversation/recommended-action.ts"() {
|
|
10062
|
+
"use strict";
|
|
10063
|
+
init_repl_api();
|
|
10064
|
+
init_phase();
|
|
10065
|
+
}
|
|
10066
|
+
});
|
|
10067
|
+
|
|
9988
10068
|
// src/conversation/phase.ts
|
|
10069
|
+
var phase_exports = {};
|
|
10070
|
+
__export(phase_exports, {
|
|
10071
|
+
buildConversationPrompt: () => buildConversationPrompt,
|
|
10072
|
+
formatPhaseLabel: () => formatPhaseLabel,
|
|
10073
|
+
getConversationPhaseBlock: () => getConversationPhaseBlock,
|
|
10074
|
+
resolveConversationPhase: () => resolveConversationPhase,
|
|
10075
|
+
sessionHasData: () => sessionHasData
|
|
10076
|
+
});
|
|
9989
10077
|
import chalk3 from "chalk";
|
|
9990
10078
|
function sessionHasData(ctx) {
|
|
9991
10079
|
const counts = ctx.dataset?.counts ?? {};
|
|
@@ -9994,7 +10082,7 @@ function sessionHasData(ctx) {
|
|
|
9994
10082
|
function resolveConversationPhase(ctx) {
|
|
9995
10083
|
if (ctx.deliverIntent) return "deliver";
|
|
9996
10084
|
if (ctx.computeInProgress) return "compute";
|
|
9997
|
-
if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis") {
|
|
10085
|
+
if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis" && ctx.strategistState.step !== "awaiting_connect") {
|
|
9998
10086
|
return "strategize";
|
|
9999
10087
|
}
|
|
10000
10088
|
if (isAnalysisReady(ctx)) return "explore";
|
|
@@ -10016,6 +10104,25 @@ function formatPhaseLabel(phase) {
|
|
|
10016
10104
|
return phase.replace(/_/g, " ");
|
|
10017
10105
|
}
|
|
10018
10106
|
}
|
|
10107
|
+
function buildConversationPrompt(ctx) {
|
|
10108
|
+
const phase = resolveConversationPhase(ctx);
|
|
10109
|
+
const label = PROMPT_LABELS[phase];
|
|
10110
|
+
const scope = ctx.sessionName ? ` ${ctx.sessionName}` : "";
|
|
10111
|
+
if (phase === "orient") {
|
|
10112
|
+
return paint("accent", `${label} `);
|
|
10113
|
+
}
|
|
10114
|
+
const action = resolveRecommendedAction(ctx);
|
|
10115
|
+
if (phase === "explore") {
|
|
10116
|
+
const mode = defaultExploreResponseMode(ctx, Math.floor(ctx.messages.length / 2));
|
|
10117
|
+
const modeTag = mode === "brief" ? "brief" : "deep";
|
|
10118
|
+
const stack = canUseReplAi(ctx) ? formatActiveStackShort(ctx) : "no engine";
|
|
10119
|
+
const strategyWait = ctx.strategistState?.step === "awaiting_connect" ? chalk3.dim(" \xB7 strategy after /connect") : "";
|
|
10120
|
+
const enterHint2 = action ? chalk3.dim(` \xB7 \u23CE ${action.hint}`) : "";
|
|
10121
|
+
return paint("accent", `ask${scope} \u203A `) + chalk3.dim(`${modeTag} \xB7 ${stack}`) + strategyWait + enterHint2 + " ";
|
|
10122
|
+
}
|
|
10123
|
+
const enterHint = action ? chalk3.dim(`\u23CE ${action.hint} `) : "";
|
|
10124
|
+
return paint("accent", `${label.replace(" \u203A", "")}${scope} \u203A `) + enterHint;
|
|
10125
|
+
}
|
|
10019
10126
|
function getConversationPhaseBlock(ctx) {
|
|
10020
10127
|
const phase = resolveConversationPhase(ctx);
|
|
10021
10128
|
const lines = [`Conversation phase: ${formatPhaseLabel(phase)}`];
|
|
@@ -10035,6 +10142,7 @@ function getConversationPhaseBlock(ctx) {
|
|
|
10035
10142
|
}
|
|
10036
10143
|
return lines.join("\n");
|
|
10037
10144
|
}
|
|
10145
|
+
var PROMPT_LABELS;
|
|
10038
10146
|
var init_phase = __esm({
|
|
10039
10147
|
"src/conversation/phase.ts"() {
|
|
10040
10148
|
"use strict";
|
|
@@ -10043,6 +10151,16 @@ var init_phase = __esm({
|
|
|
10043
10151
|
init_explore_mode();
|
|
10044
10152
|
init_session_state();
|
|
10045
10153
|
init_theme();
|
|
10154
|
+
init_recommended_action();
|
|
10155
|
+
PROMPT_LABELS = {
|
|
10156
|
+
orient: "\u203A",
|
|
10157
|
+
scope: "scope \u203A",
|
|
10158
|
+
awaiting_data: "data \u203A",
|
|
10159
|
+
compute: "\u2026",
|
|
10160
|
+
explore: "ask \u203A",
|
|
10161
|
+
strategize: "strategy \u203A",
|
|
10162
|
+
deliver: "ship \u203A"
|
|
10163
|
+
};
|
|
10046
10164
|
}
|
|
10047
10165
|
});
|
|
10048
10166
|
|
|
@@ -10284,8 +10402,13 @@ var init_repl_globals = __esm({
|
|
|
10284
10402
|
});
|
|
10285
10403
|
|
|
10286
10404
|
// src/cli/prompts.ts
|
|
10405
|
+
var prompts_exports = {};
|
|
10406
|
+
__export(prompts_exports, {
|
|
10407
|
+
createPromptSession: () => createPromptSession
|
|
10408
|
+
});
|
|
10287
10409
|
import { createInterface } from "readline/promises";
|
|
10288
10410
|
import { clearLine, cursorTo } from "readline";
|
|
10411
|
+
import { StringDecoder } from "string_decoder";
|
|
10289
10412
|
import chalk4 from "chalk";
|
|
10290
10413
|
function marker() {
|
|
10291
10414
|
return paint("accent", "ntrp \u203A ");
|
|
@@ -10390,16 +10513,24 @@ function createPromptSession(existing, ctx) {
|
|
|
10390
10513
|
replRl.line = "";
|
|
10391
10514
|
replRl.cursor = 0;
|
|
10392
10515
|
}
|
|
10516
|
+
const wasRaw = stdin.isRaw === true;
|
|
10393
10517
|
if (stdin.isTTY) stdin.setRawMode(true);
|
|
10394
10518
|
rl.pause();
|
|
10519
|
+
const keypressListeners = stdin.rawListeners("keypress");
|
|
10520
|
+
for (const listener of keypressListeners) {
|
|
10521
|
+
stdin.removeListener("keypress", listener);
|
|
10522
|
+
}
|
|
10395
10523
|
process.stdout.write("\n" + prompt);
|
|
10396
10524
|
try {
|
|
10397
|
-
return await new Promise((
|
|
10525
|
+
return await new Promise((resolve8, reject) => {
|
|
10398
10526
|
let value = "";
|
|
10399
10527
|
let settled = false;
|
|
10400
10528
|
const cleanup = () => {
|
|
10401
10529
|
stdin.off("data", onData);
|
|
10402
|
-
|
|
10530
|
+
for (const listener of keypressListeners) {
|
|
10531
|
+
stdin.addListener("keypress", listener);
|
|
10532
|
+
}
|
|
10533
|
+
if (stdin.isTTY) stdin.setRawMode(wasRaw);
|
|
10403
10534
|
clearLine(process.stdout, 0);
|
|
10404
10535
|
cursorTo(process.stdout, 0);
|
|
10405
10536
|
rl.resume();
|
|
@@ -10418,16 +10549,16 @@ function createPromptSession(existing, ctx) {
|
|
|
10418
10549
|
}
|
|
10419
10550
|
};
|
|
10420
10551
|
stdin.resume();
|
|
10421
|
-
|
|
10552
|
+
const decoder = new StringDecoder("utf8");
|
|
10422
10553
|
const onData = (chunk) => {
|
|
10423
|
-
const cleaned = stripTerminalArtifacts(chunk);
|
|
10554
|
+
const cleaned = stripTerminalArtifacts(typeof chunk === "string" ? chunk : decoder.write(chunk));
|
|
10424
10555
|
for (const char of cleaned) {
|
|
10425
10556
|
if (char === "\r" || char === "\n") {
|
|
10426
10557
|
finish(() => {
|
|
10427
10558
|
process.stdout.write("\n");
|
|
10428
10559
|
const trimmed = stripTerminalArtifacts(value).trim();
|
|
10429
10560
|
assertNotGlobalReplCommand(trimmed);
|
|
10430
|
-
|
|
10561
|
+
resolve8(trimmed);
|
|
10431
10562
|
});
|
|
10432
10563
|
return;
|
|
10433
10564
|
}
|
|
@@ -10443,7 +10574,7 @@ function createPromptSession(existing, ctx) {
|
|
|
10443
10574
|
process.stdout.write("\n");
|
|
10444
10575
|
const trimmed = stripTerminalArtifacts(value).trim();
|
|
10445
10576
|
assertNotGlobalReplCommand(trimmed);
|
|
10446
|
-
|
|
10577
|
+
resolve8(trimmed);
|
|
10447
10578
|
});
|
|
10448
10579
|
return;
|
|
10449
10580
|
}
|
|
@@ -10470,6 +10601,7 @@ function createPromptSession(existing, ctx) {
|
|
|
10470
10601
|
for (; ; ) {
|
|
10471
10602
|
const value = await readMaskedLine(secretPromptLine(question), maskChar);
|
|
10472
10603
|
if (!value) {
|
|
10604
|
+
if (opts.allowEmpty) return "";
|
|
10473
10605
|
console.log(" " + chalk4.red("This one is required."));
|
|
10474
10606
|
continue;
|
|
10475
10607
|
}
|
|
@@ -10512,29 +10644,43 @@ var init_prompts = __esm({
|
|
|
10512
10644
|
|
|
10513
10645
|
// src/conversation/gap-card.ts
|
|
10514
10646
|
import chalk5 from "chalk";
|
|
10515
|
-
function printGapCard(audit) {
|
|
10647
|
+
function printGapCard(audit, opts = {}) {
|
|
10516
10648
|
console.log();
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
console.log(" " + chalk5.green("\u2713") + " " + chalk5.dim(`${item.label}: ${item.detail}`));
|
|
10649
|
+
if (opts.skipSatisfied) {
|
|
10650
|
+
if (audit.missing.length > 0) {
|
|
10651
|
+
console.log(" " + chalk5.bold("Data check"));
|
|
10521
10652
|
}
|
|
10653
|
+
} else if (audit.satisfied.length > 0) {
|
|
10654
|
+
const bits = audit.satisfied.map((item) => item.detail);
|
|
10655
|
+
console.log(
|
|
10656
|
+
" " + chalk5.green("\u2713") + " " + chalk5.bold("Data check") + chalk5.dim(" \u2014 " + bits.join(" \xB7 "))
|
|
10657
|
+
);
|
|
10658
|
+
} else {
|
|
10659
|
+
console.log(" " + chalk5.bold("Data check"));
|
|
10522
10660
|
}
|
|
10523
10661
|
for (const item of audit.missing) {
|
|
10524
10662
|
console.log(" " + chalk5.red("\u2717") + " " + item.label + chalk5.dim(` \u2014 ${item.why}`));
|
|
10525
10663
|
console.log(" " + chalk5.dim(item.suggestion));
|
|
10526
10664
|
}
|
|
10527
|
-
|
|
10528
|
-
|
|
10665
|
+
if (audit.optional.length > 0) {
|
|
10666
|
+
const heads = audit.optional.map((item) => item.detail.split(" \u2014 ")[0] ?? item.detail);
|
|
10667
|
+
const joined = heads.join(" \xB7 ");
|
|
10668
|
+
if (joined.length <= 100) {
|
|
10669
|
+
console.log(" " + chalk5.yellow("~") + " " + chalk5.dim(joined));
|
|
10670
|
+
} else {
|
|
10671
|
+
for (const item of audit.optional) {
|
|
10672
|
+
console.log(" " + chalk5.yellow("~") + " " + chalk5.dim(`${item.label}: ${item.detail}`));
|
|
10673
|
+
}
|
|
10674
|
+
}
|
|
10529
10675
|
}
|
|
10530
10676
|
console.log();
|
|
10531
10677
|
if (audit.can_compute) {
|
|
10532
10678
|
console.log(
|
|
10533
|
-
" " + chalk5.dim("Ready to compute \u2014
|
|
10679
|
+
" " + chalk5.dim("Ready to compute \u2014 press ") + chalk5.cyan("\u23CE") + chalk5.dim(" or say ") + chalk5.cyan('"go ahead"')
|
|
10534
10680
|
);
|
|
10535
10681
|
} else if (audit.missing.length > 0) {
|
|
10536
10682
|
console.log(
|
|
10537
|
-
" " + chalk5.dim("Load data
|
|
10683
|
+
" " + chalk5.dim("Load data \u2014 paste a CSV path, or press ") + chalk5.cyan("\u23CE") + chalk5.dim(" to ") + chalk5.cyan("use demo data")
|
|
10538
10684
|
);
|
|
10539
10685
|
}
|
|
10540
10686
|
console.log();
|
|
@@ -10545,6 +10691,22 @@ var init_gap_card = __esm({
|
|
|
10545
10691
|
}
|
|
10546
10692
|
});
|
|
10547
10693
|
|
|
10694
|
+
// src/ui/spinner.ts
|
|
10695
|
+
import ora from "ora";
|
|
10696
|
+
function makeSpinner(text, opts = {}) {
|
|
10697
|
+
return ora({
|
|
10698
|
+
text,
|
|
10699
|
+
color: "cyan",
|
|
10700
|
+
indent: opts.indent ?? 2,
|
|
10701
|
+
discardStdin: false
|
|
10702
|
+
}).start();
|
|
10703
|
+
}
|
|
10704
|
+
var init_spinner = __esm({
|
|
10705
|
+
"src/ui/spinner.ts"() {
|
|
10706
|
+
"use strict";
|
|
10707
|
+
}
|
|
10708
|
+
});
|
|
10709
|
+
|
|
10548
10710
|
// src/metrics/companion.ts
|
|
10549
10711
|
import chalk6 from "chalk";
|
|
10550
10712
|
function getCompanionRecommendation(input) {
|
|
@@ -11556,7 +11718,7 @@ Restraint: you diagnose and prescribe the system; you do not build it here. Name
|
|
|
11556
11718
|
- THEN THE DRIVERS. Support the headline with 2-3 distinct, non-overlapping drivers, each with its own number. If two points share a cause, merge them.
|
|
11557
11719
|
- THEN THE SO-WHAT. Close with what it means for the decision at hand: the action, the owner-shaped next step, or the sharpest next cut.
|
|
11558
11720
|
- THE RECALL TEST. A busy executive should be able to repeat your headline and one number to their CEO an hour later. If they couldn't, tighten it.
|
|
11559
|
-
- ALTITUDE CONTROL. Answer at the altitude asked: a high-level question gets the 30,000-ft story (one narrative sentence, three numbers max) with an offer to descend; a "how do we fix it" question
|
|
11721
|
+
- ALTITUDE CONTROL. Answer at the altitude asked: a high-level question gets the 30,000-ft story (one narrative sentence, three numbers max) with an offer to descend; a "how do we fix it" / plan-of-attack question prefers draft_strategy (or, if answering one play inline: one play + mechanism + what to verify \u2014 observe and recommend, never invent a multi-week Phase 1/2/3 program).`;
|
|
11560
11722
|
FINDINGS_SCHEMA_BLOCK = `[
|
|
11561
11723
|
{
|
|
11562
11724
|
"severity": "critical" | "warning" | "info",
|
|
@@ -11799,24 +11961,31 @@ function padRight(text, width) {
|
|
|
11799
11961
|
const gap = Math.max(0, width - visibleWidth(text));
|
|
11800
11962
|
return `${text}${" ".repeat(gap)}`;
|
|
11801
11963
|
}
|
|
11964
|
+
function padLeft(text, width) {
|
|
11965
|
+
const gap = Math.max(0, width - visibleWidth(text));
|
|
11966
|
+
return `${" ".repeat(gap)}${text}`;
|
|
11967
|
+
}
|
|
11802
11968
|
function truncateVisible(text, maxVisible, ellipsis = "\u2026") {
|
|
11803
11969
|
if (visibleWidth(text) <= maxVisible) return text;
|
|
11804
11970
|
if (maxVisible <= ellipsis.length) return stripAnsi2(text).slice(0, maxVisible);
|
|
11805
11971
|
const target = maxVisible - ellipsis.length;
|
|
11806
11972
|
let visible = 0;
|
|
11807
11973
|
let i = 0;
|
|
11974
|
+
let sawAnsi = false;
|
|
11808
11975
|
while (i < text.length && visible < target) {
|
|
11809
11976
|
if (text[i] === "\x1B") {
|
|
11810
11977
|
const match = text.slice(i).match(/^\u001B\[[0-9;]*m/);
|
|
11811
11978
|
if (match) {
|
|
11812
11979
|
i += match[0].length;
|
|
11980
|
+
sawAnsi = true;
|
|
11813
11981
|
continue;
|
|
11814
11982
|
}
|
|
11815
11983
|
}
|
|
11816
11984
|
visible++;
|
|
11817
11985
|
i++;
|
|
11818
11986
|
}
|
|
11819
|
-
|
|
11987
|
+
const reset = sawAnsi ? "\x1B[0m" : "";
|
|
11988
|
+
return text.slice(0, i) + reset + ellipsis;
|
|
11820
11989
|
}
|
|
11821
11990
|
function hr(width, ch = "\u2500") {
|
|
11822
11991
|
return ch.repeat(Math.max(0, width));
|
|
@@ -11847,6 +12016,11 @@ function wrapWords(text, maxW) {
|
|
|
11847
12016
|
function termWidth() {
|
|
11848
12017
|
return process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
|
|
11849
12018
|
}
|
|
12019
|
+
function resolveCardWidth(opts = {}) {
|
|
12020
|
+
const { min = 60, max = 100, margin = 4 } = opts;
|
|
12021
|
+
const usable = Math.max(20, termWidth() - margin);
|
|
12022
|
+
return Math.max(Math.min(min, usable), Math.min(usable, max));
|
|
12023
|
+
}
|
|
11850
12024
|
var ANSI_RE;
|
|
11851
12025
|
var init_layout = __esm({
|
|
11852
12026
|
"src/ui/layout.ts"() {
|
|
@@ -12101,7 +12275,6 @@ var init_llm_attribution = __esm({
|
|
|
12101
12275
|
|
|
12102
12276
|
// src/output/terminal.ts
|
|
12103
12277
|
import chalk9 from "chalk";
|
|
12104
|
-
import ora from "ora";
|
|
12105
12278
|
import Table2 from "cli-table3";
|
|
12106
12279
|
function centerPad(text, width) {
|
|
12107
12280
|
if (text.length >= width) return text;
|
|
@@ -12109,19 +12282,6 @@ function centerPad(text, width) {
|
|
|
12109
12282
|
const left = Math.floor(gap / 2);
|
|
12110
12283
|
return " ".repeat(left) + text + " ".repeat(gap - left);
|
|
12111
12284
|
}
|
|
12112
|
-
function statusColor(status) {
|
|
12113
|
-
switch (status) {
|
|
12114
|
-
case "green":
|
|
12115
|
-
return chalk9.green;
|
|
12116
|
-
case "yellow":
|
|
12117
|
-
return chalk9.yellow;
|
|
12118
|
-
case "red":
|
|
12119
|
-
return chalk9.red;
|
|
12120
|
-
}
|
|
12121
|
-
}
|
|
12122
|
-
function statusDot(status) {
|
|
12123
|
-
return statusColor(status)("\u25CF");
|
|
12124
|
-
}
|
|
12125
12285
|
function statusBadge(status) {
|
|
12126
12286
|
switch (status) {
|
|
12127
12287
|
case "green":
|
|
@@ -12136,9 +12296,9 @@ function printHeading(label, detail) {
|
|
|
12136
12296
|
console.log(` ${sectionHeading(label)}${detail ? chalk9.dim(` ${detail}`) : ""}`);
|
|
12137
12297
|
}
|
|
12138
12298
|
function printResultCard(title, rows) {
|
|
12139
|
-
const width =
|
|
12299
|
+
const width = resolveCardWidth({ min: 60, max: 100, margin: 4 });
|
|
12140
12300
|
const inner = width - 4;
|
|
12141
|
-
const border =
|
|
12301
|
+
const border = (s) => paint("border", s);
|
|
12142
12302
|
console.log();
|
|
12143
12303
|
console.log(` ${border(`\u256D${"\u2500".repeat(width - 2)}\u256E`)}`);
|
|
12144
12304
|
console.log(` ${border("\u2502 ")}${padRight(sectionHeading(title), inner)}${border(" \u2502")}`);
|
|
@@ -12154,20 +12314,33 @@ function printVitalSignRow(vs) {
|
|
|
12154
12314
|
const label = VITAL_SIGN_LABELS[vs.vital_sign].padEnd(18);
|
|
12155
12315
|
const bar = scoreBar(vs.score, vs.status);
|
|
12156
12316
|
const score = String(Math.round(vs.score)).padStart(4);
|
|
12157
|
-
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${
|
|
12317
|
+
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${paint("success", formatCurrency(vs.dollar_value))} ${chalk9.dim(vs.dollar_label ?? "")}` : chalk9.dim("\u2014");
|
|
12158
12318
|
console.log(` ${dot} ${label} ${bar} ${chalk9.bold(score)} ${chalk9.dim("\u2502")} ${impact}`);
|
|
12159
12319
|
}
|
|
12160
12320
|
function printHealthSummary(result, _pipelineMetrics) {
|
|
12161
12321
|
const scoreStr = `${chalk9.bold(String(Math.round(result.overall_score)))}${chalk9.dim("/100")}`;
|
|
12162
|
-
const impact = result.total_value_at_risk != null && result.total_value_at_risk > 0 ? `${
|
|
12322
|
+
const impact = result.total_value_at_risk != null && result.total_value_at_risk > 0 ? `${paint("success", formatCurrency(result.total_value_at_risk))} ${chalk9.dim("total at risk")}` : chalk9.dim("No dollar-weighted risk detected");
|
|
12163
12323
|
const next = result.overall_status === "red" ? actionHint("Next:", "/playbook", "review recommended plays") : result.overall_status === "yellow" ? actionHint("Next:", "/diagnose --deep", "investigate the weak signal") : actionHint("Next:", "/report", "export the clean snapshot");
|
|
12164
12324
|
printResultCard("Overall Health", [
|
|
12165
|
-
`${chalk9.dim("Score")}
|
|
12166
|
-
`${chalk9.dim("
|
|
12167
|
-
`${chalk9.dim("Revenue")}
|
|
12325
|
+
`${chalk9.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}`,
|
|
12326
|
+
`${chalk9.dim("Held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`,
|
|
12327
|
+
`${chalk9.dim("Revenue")} ${impact}`,
|
|
12168
12328
|
next
|
|
12169
12329
|
]);
|
|
12170
12330
|
}
|
|
12331
|
+
function printHealthLine(result) {
|
|
12332
|
+
const score = `${chalk9.bold(String(Math.round(result.overall_score)))}${chalk9.dim("/100")}`;
|
|
12333
|
+
const parts = [
|
|
12334
|
+
`${chalk9.dim("Health")} ${score} ${statusBadge(result.overall_status)}`,
|
|
12335
|
+
`${chalk9.dim("held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`
|
|
12336
|
+
];
|
|
12337
|
+
if (result.total_value_at_risk != null && result.total_value_at_risk > 0) {
|
|
12338
|
+
parts.push(`${paint("success", formatCurrency(result.total_value_at_risk))} ${chalk9.dim("total at risk")}`);
|
|
12339
|
+
}
|
|
12340
|
+
console.log();
|
|
12341
|
+
console.log(" " + parts.join(chalk9.dim(" \xB7 ")));
|
|
12342
|
+
console.log();
|
|
12343
|
+
}
|
|
12171
12344
|
function printVitalSigns(vitals) {
|
|
12172
12345
|
console.log();
|
|
12173
12346
|
printHeading("Vital Signs");
|
|
@@ -12190,7 +12363,7 @@ function printSegmentSummary(segments) {
|
|
|
12190
12363
|
}
|
|
12191
12364
|
console.log();
|
|
12192
12365
|
}
|
|
12193
|
-
function printTopProblems(segments, limit = 7) {
|
|
12366
|
+
function printTopProblems(segments, limit = 7, opts = {}) {
|
|
12194
12367
|
if (segments.length === 0) return;
|
|
12195
12368
|
const problems = [];
|
|
12196
12369
|
for (const seg of segments) {
|
|
@@ -12208,6 +12381,7 @@ function printTopProblems(segments, limit = 7) {
|
|
|
12208
12381
|
}
|
|
12209
12382
|
}
|
|
12210
12383
|
if (problems.length === 0) {
|
|
12384
|
+
if (opts.compact) return;
|
|
12211
12385
|
printHeading("Top Problems");
|
|
12212
12386
|
console.log();
|
|
12213
12387
|
console.log(" " + chalk9.dim("No dollar-weighted problems found across segments."));
|
|
@@ -12220,6 +12394,22 @@ function printTopProblems(segments, limit = 7) {
|
|
|
12220
12394
|
const vitalW = Math.max("Vital Sign".length, ...top.map((p) => p.vitalSignLabel.length));
|
|
12221
12395
|
const dollarStrs = top.map((p) => formatCurrency(p.dollarValue));
|
|
12222
12396
|
const dollarW = Math.max(...dollarStrs.map((s) => s.length));
|
|
12397
|
+
if (opts.compact) {
|
|
12398
|
+
console.log(` ${sectionHeading("Top Problems")}`);
|
|
12399
|
+
for (let i = 0; i < top.length; i++) {
|
|
12400
|
+
const p = top[i];
|
|
12401
|
+
console.log(
|
|
12402
|
+
` ${statusDot(p.status)} ${p.segment.padEnd(segW)} ${p.vitalSignLabel.padEnd(vitalW)} ${paint("success", dollarStrs[i].padStart(dollarW))} ${chalk9.dim(p.dollarLabel)}`
|
|
12403
|
+
);
|
|
12404
|
+
}
|
|
12405
|
+
if (problems.length > top.length) {
|
|
12406
|
+
console.log(
|
|
12407
|
+
" " + chalk9.dim(`${problems.length - top.length} more \u2014 `) + paint("accent", "/diagnose") + chalk9.dim(" for the full report")
|
|
12408
|
+
);
|
|
12409
|
+
}
|
|
12410
|
+
console.log();
|
|
12411
|
+
return;
|
|
12412
|
+
}
|
|
12223
12413
|
const labelW = Math.max("".length, ...top.map((p) => p.dollarLabel.length));
|
|
12224
12414
|
const impactW = dollarW + 2 + labelW;
|
|
12225
12415
|
console.log(
|
|
@@ -12237,7 +12427,7 @@ function printTopProblems(segments, limit = 7) {
|
|
|
12237
12427
|
const dot = statusDot(p.status);
|
|
12238
12428
|
const seg = p.segment.padEnd(segW);
|
|
12239
12429
|
const vital = p.vitalSignLabel.padEnd(vitalW);
|
|
12240
|
-
const dollar =
|
|
12430
|
+
const dollar = paint("success", dollarStrs[i].padStart(dollarW));
|
|
12241
12431
|
const label = chalk9.dim(p.dollarLabel);
|
|
12242
12432
|
console.log(` ${dot} ${seg} ${vital} ${dollar} ${label}`);
|
|
12243
12433
|
}
|
|
@@ -12247,26 +12437,26 @@ function printTopProblems(segments, limit = 7) {
|
|
|
12247
12437
|
}
|
|
12248
12438
|
console.log();
|
|
12249
12439
|
}
|
|
12440
|
+
function printFindingCard(finding) {
|
|
12441
|
+
const dot = severityPaint(finding.severity)("\u25CF");
|
|
12442
|
+
const dollarTag = finding.dollar_value != null && finding.dollar_value > 0 ? ` ${chalk9.dim("\xB7")} ${paint("success", formatDollarValue(finding.dollar_value))}` : "";
|
|
12443
|
+
console.log(` ${dot} ${chalk9.bold(finding.segment)}${dollarTag}`);
|
|
12444
|
+
printMarkdown(finding.finding, { indent: 2 });
|
|
12445
|
+
if (finding.recommended_plays && finding.recommended_plays.length > 0) {
|
|
12446
|
+
for (const play of finding.recommended_plays) {
|
|
12447
|
+
console.log(
|
|
12448
|
+
` ${chalk9.dim("Consider:")} ${paint("accent", play.play_name)} ${chalk9.dim("\u2192")} ${chalk9.dim("/playbook " + play.play_id)}`
|
|
12449
|
+
);
|
|
12450
|
+
}
|
|
12451
|
+
}
|
|
12452
|
+
console.log();
|
|
12453
|
+
}
|
|
12250
12454
|
function printFindings(findings) {
|
|
12251
12455
|
if (findings.length === 0) {
|
|
12252
12456
|
console.log(chalk9.dim(" No findings generated."));
|
|
12253
12457
|
return;
|
|
12254
12458
|
}
|
|
12255
|
-
for (const finding of findings)
|
|
12256
|
-
const sevColor = finding.severity === "critical" ? chalk9.red : finding.severity === "warning" ? chalk9.yellow : chalk9.blue;
|
|
12257
|
-
const dot = sevColor("\u25CF");
|
|
12258
|
-
const dollarTag = finding.dollar_value != null && finding.dollar_value > 0 ? ` ${chalk9.dim("\xB7")} ${chalk9.green(formatDollarValue(finding.dollar_value))}` : "";
|
|
12259
|
-
console.log(` ${dot} ${chalk9.bold(finding.segment)}${dollarTag}`);
|
|
12260
|
-
printMarkdown(finding.finding, { indent: 2 });
|
|
12261
|
-
if (finding.recommended_plays && finding.recommended_plays.length > 0) {
|
|
12262
|
-
for (const play of finding.recommended_plays) {
|
|
12263
|
-
console.log(
|
|
12264
|
-
` ${chalk9.dim("Consider:")} ${paint("accent", play.play_name)} ${chalk9.dim("\u2192")} ${chalk9.dim("/playbook " + play.play_id)}`
|
|
12265
|
-
);
|
|
12266
|
-
}
|
|
12267
|
-
}
|
|
12268
|
-
console.log();
|
|
12269
|
-
}
|
|
12459
|
+
for (const finding of findings) printFindingCard(finding);
|
|
12270
12460
|
}
|
|
12271
12461
|
function printEntityCounts(counts) {
|
|
12272
12462
|
const table = new Table2({
|
|
@@ -12281,23 +12471,23 @@ function printEntityCounts(counts) {
|
|
|
12281
12471
|
console.log();
|
|
12282
12472
|
}
|
|
12283
12473
|
function printSegmentDetail(seg, aggregate) {
|
|
12284
|
-
const color =
|
|
12474
|
+
const color = statusPaint(seg.result.overall_status);
|
|
12285
12475
|
console.log();
|
|
12286
12476
|
printHeading(seg.segment.name);
|
|
12287
12477
|
console.log(
|
|
12288
|
-
` ${statusDot(seg.result.overall_status)} ${color(chalk9.bold(formatScore(seg.result.overall_score)))}${chalk9.dim("/100")} ${chalk9.dim("
|
|
12478
|
+
` ${statusDot(seg.result.overall_status)} ${color(chalk9.bold(formatScore(seg.result.overall_score)))}${chalk9.dim("/100")} ${chalk9.dim("Held back by:")} ${paint("accent", VITAL_SIGN_LABELS[seg.result.gating_vital_sign])}`
|
|
12289
12479
|
);
|
|
12290
12480
|
console.log();
|
|
12291
12481
|
for (const vs of seg.result.vital_signs) {
|
|
12292
12482
|
const aggVs = aggregate.vital_signs.find((a) => a.vital_sign === vs.vital_sign);
|
|
12293
12483
|
const delta = aggVs ? vs.score - aggVs.score : 0;
|
|
12294
|
-
const deltaStr = delta >= 0 ?
|
|
12484
|
+
const deltaStr = delta >= 0 ? paint("success", `+${Math.round(delta)}`) : paint("error", `${Math.round(delta)}`);
|
|
12295
12485
|
const dot = statusDot(vs.status);
|
|
12296
12486
|
const label = VITAL_SIGN_LABELS[vs.vital_sign].padEnd(18);
|
|
12297
12487
|
const bar = scoreBar(vs.score, vs.status);
|
|
12298
12488
|
const score = String(Math.round(vs.score)).padStart(4);
|
|
12299
|
-
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${
|
|
12300
|
-
console.log(` ${dot} ${label} ${bar} ${chalk9.bold(score)} ${deltaStr
|
|
12489
|
+
const impact = vs.dollar_value != null && vs.dollar_value > 0 ? `${paint("success", formatCurrency(vs.dollar_value))} ${chalk9.dim(vs.dollar_label ?? "")}` : chalk9.dim("\u2014");
|
|
12490
|
+
console.log(` ${dot} ${label} ${bar} ${chalk9.bold(score)} ${padLeft(deltaStr, 4)} ${chalk9.dim("\u2502")} ${impact}`);
|
|
12301
12491
|
}
|
|
12302
12492
|
console.log();
|
|
12303
12493
|
}
|
|
@@ -12305,11 +12495,11 @@ function formatToolName(name) {
|
|
|
12305
12495
|
return name.replace(/_/g, " ").replace(/^(get|query)\s+/, "examining ");
|
|
12306
12496
|
}
|
|
12307
12497
|
async function renderDiagnoseStream(options) {
|
|
12308
|
-
const { diagnoseGenerator, runFindings, storeFindings, deep } = options;
|
|
12498
|
+
const { diagnoseGenerator, runFindings, storeFindings, deep, compact } = options;
|
|
12309
12499
|
console.log();
|
|
12310
12500
|
printHeading("Vital Signs");
|
|
12311
12501
|
console.log();
|
|
12312
|
-
const spinner =
|
|
12502
|
+
const spinner = makeSpinner("Prefetching snapshot\u2026");
|
|
12313
12503
|
let fullResult = null;
|
|
12314
12504
|
try {
|
|
12315
12505
|
for await (const event of diagnoseGenerator()) {
|
|
@@ -12344,25 +12534,44 @@ async function renderDiagnoseStream(options) {
|
|
|
12344
12534
|
if (!fullResult) {
|
|
12345
12535
|
throw new Error("Diagnose stream ended without a complete event");
|
|
12346
12536
|
}
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12537
|
+
if (compact) {
|
|
12538
|
+
printHealthLine(fullResult.aggregate);
|
|
12539
|
+
if (fullResult.segments.length > 0) {
|
|
12540
|
+
printTopProblems(fullResult.segments, 3, { compact: true });
|
|
12541
|
+
}
|
|
12542
|
+
} else {
|
|
12543
|
+
console.log();
|
|
12544
|
+
printHealthSummary(fullResult.aggregate);
|
|
12545
|
+
if (fullResult.segments.length > 0) {
|
|
12546
|
+
printTopProblems(fullResult.segments);
|
|
12547
|
+
}
|
|
12351
12548
|
}
|
|
12352
12549
|
let collectedFindings = [];
|
|
12353
12550
|
if (runFindings) {
|
|
12354
|
-
const findingsSpinner =
|
|
12355
|
-
text: deep ? "Investigating (agentic)\u2026" : "Generating findings\u2026",
|
|
12356
|
-
indent: 2,
|
|
12357
|
-
discardStdin: false
|
|
12358
|
-
}).start();
|
|
12551
|
+
const findingsSpinner = makeSpinner(deep ? "Investigating (agentic)\u2026" : "Generating findings\u2026");
|
|
12359
12552
|
let toolCalls = 0;
|
|
12360
|
-
let
|
|
12553
|
+
let printed = 0;
|
|
12554
|
+
let headingPrinted = false;
|
|
12361
12555
|
let modelUsed = "";
|
|
12362
12556
|
let providerUsed;
|
|
12363
12557
|
let failover;
|
|
12364
12558
|
let notices;
|
|
12365
12559
|
let rawPrompt = "";
|
|
12560
|
+
const printStreamedFinding = (finding, stillStreaming) => {
|
|
12561
|
+
findingsSpinner.stop();
|
|
12562
|
+
if (!headingPrinted) {
|
|
12563
|
+
printHeading(deep ? "Investigation Findings" : "Findings");
|
|
12564
|
+
console.log();
|
|
12565
|
+
headingPrinted = true;
|
|
12566
|
+
}
|
|
12567
|
+
printFindingCard(finding);
|
|
12568
|
+
printed++;
|
|
12569
|
+
if (stillStreaming) {
|
|
12570
|
+
findingsSpinner.start(
|
|
12571
|
+
`Found ${printed} finding${printed === 1 ? "" : "s"} \u2014 still investigating\u2026`
|
|
12572
|
+
);
|
|
12573
|
+
}
|
|
12574
|
+
};
|
|
12366
12575
|
try {
|
|
12367
12576
|
for await (const event of runFindings(fullResult)) {
|
|
12368
12577
|
if (event.type === "tool_call") {
|
|
@@ -12370,9 +12579,11 @@ async function renderDiagnoseStream(options) {
|
|
|
12370
12579
|
findingsSpinner.text = formatToolName(event.name);
|
|
12371
12580
|
} else if (event.type === "finding") {
|
|
12372
12581
|
collectedFindings.push(event.finding);
|
|
12373
|
-
|
|
12374
|
-
findingsSpinner.text = `Found ${collectedFindings.length} finding${collectedFindings.length === 1 ? "" : "s"}\u2026`;
|
|
12582
|
+
printStreamedFinding(event.finding, true);
|
|
12375
12583
|
} else if (event.type === "done") {
|
|
12584
|
+
for (const finding of event.findings.slice(printed)) {
|
|
12585
|
+
printStreamedFinding(finding, false);
|
|
12586
|
+
}
|
|
12376
12587
|
collectedFindings = event.findings;
|
|
12377
12588
|
modelUsed = event.model_used;
|
|
12378
12589
|
providerUsed = event.provider_used;
|
|
@@ -12382,10 +12593,20 @@ async function renderDiagnoseStream(options) {
|
|
|
12382
12593
|
}
|
|
12383
12594
|
}
|
|
12384
12595
|
findingsSpinner.stop();
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12596
|
+
if (!headingPrinted) {
|
|
12597
|
+
printHeading(deep ? "Investigation Findings" : "Findings");
|
|
12598
|
+
console.log();
|
|
12599
|
+
}
|
|
12600
|
+
if (collectedFindings.length === 0) {
|
|
12601
|
+
console.log(chalk9.dim(" No findings generated."));
|
|
12602
|
+
console.log();
|
|
12603
|
+
}
|
|
12604
|
+
if (toolCalls > 0) {
|
|
12605
|
+
console.log(
|
|
12606
|
+
chalk9.dim(` Investigated with ${toolCalls} tool call${toolCalls === 1 ? "" : "s"}`)
|
|
12607
|
+
);
|
|
12608
|
+
console.log();
|
|
12609
|
+
}
|
|
12389
12610
|
if (storeFindings) {
|
|
12390
12611
|
await storeFindings({
|
|
12391
12612
|
findings: collectedFindings,
|
|
@@ -12395,9 +12616,6 @@ async function renderDiagnoseStream(options) {
|
|
|
12395
12616
|
failover
|
|
12396
12617
|
});
|
|
12397
12618
|
}
|
|
12398
|
-
printHeading(deep ? "Investigation Findings" : "Findings");
|
|
12399
|
-
console.log();
|
|
12400
|
-
printFindings(collectedFindings);
|
|
12401
12619
|
printLlmAttribution({
|
|
12402
12620
|
model_used: modelUsed,
|
|
12403
12621
|
provider_used: providerUsed,
|
|
@@ -12411,18 +12629,6 @@ async function renderDiagnoseStream(options) {
|
|
|
12411
12629
|
}
|
|
12412
12630
|
return { fullResult, findings: collectedFindings };
|
|
12413
12631
|
}
|
|
12414
|
-
function metricStatusDot(status) {
|
|
12415
|
-
switch (status) {
|
|
12416
|
-
case "green":
|
|
12417
|
-
return chalk9.green("\u25CF");
|
|
12418
|
-
case "yellow":
|
|
12419
|
-
return chalk9.yellow("\u25CF");
|
|
12420
|
-
case "red":
|
|
12421
|
-
return chalk9.red("\u25CF");
|
|
12422
|
-
case "neutral":
|
|
12423
|
-
return chalk9.dim("\u25CB");
|
|
12424
|
-
}
|
|
12425
|
-
}
|
|
12426
12632
|
function printMetricsTable(metrics, groupOrder) {
|
|
12427
12633
|
for (const group of groupOrder) {
|
|
12428
12634
|
const groupMetrics = metrics.filter((m) => m.group === group);
|
|
@@ -12430,10 +12636,10 @@ function printMetricsTable(metrics, groupOrder) {
|
|
|
12430
12636
|
printHeading(group);
|
|
12431
12637
|
console.log();
|
|
12432
12638
|
for (const m of groupMetrics) {
|
|
12433
|
-
const dot = m.unavailable_reason ?
|
|
12639
|
+
const dot = m.unavailable_reason ? statusDot("neutral") : statusDot(m.status);
|
|
12434
12640
|
const label = m.label.padEnd(28);
|
|
12435
12641
|
const valueStr = m.unavailable_reason ? chalk9.dim("--") : chalk9.bold(m.formatted);
|
|
12436
|
-
const confTag = m.confidence != null && m.confidence < 80 && m.confidence_label ?
|
|
12642
|
+
const confTag = m.confidence != null && m.confidence < 80 && m.confidence_label ? paint("warning", ` ${m.confidence_label} (${m.confidence})`) : "";
|
|
12437
12643
|
const note = m.unavailable_reason ? chalk9.dim(m.unavailable_reason) : m.benchmark_note ? chalk9.dim(m.benchmark_note) : "";
|
|
12438
12644
|
console.log(` ${dot} ${label} ${valueStr}${confTag}${note ? " " + note : ""}`);
|
|
12439
12645
|
if (m.reliability_gate?.requirements?.length && (m.confidence ?? 100) < 80) {
|
|
@@ -12449,6 +12655,7 @@ function printMetricsTable(metrics, groupOrder) {
|
|
|
12449
12655
|
var init_terminal = __esm({
|
|
12450
12656
|
"src/output/terminal.ts"() {
|
|
12451
12657
|
"use strict";
|
|
12658
|
+
init_spinner();
|
|
12452
12659
|
init_formatters();
|
|
12453
12660
|
init_markdown();
|
|
12454
12661
|
init_theme();
|
|
@@ -12487,7 +12694,7 @@ function renderMetricsReport(ctx, metrics, coverage, sourceType, options = {}) {
|
|
|
12487
12694
|
console.log(" " + bold("Pattern checks"));
|
|
12488
12695
|
console.log();
|
|
12489
12696
|
for (const insight of deterministic.slice(0, 5)) {
|
|
12490
|
-
const dot = insight.severity === "warning" ?
|
|
12697
|
+
const dot = insight.severity === "warning" ? statusDot("yellow") : insight.severity === "critical" ? statusDot("red") : statusDot("neutral");
|
|
12491
12698
|
if (insight.headline) continue;
|
|
12492
12699
|
console.log(` ${dot} ${chalk10.dim(wrapInsight(insight.message))}`);
|
|
12493
12700
|
}
|
|
@@ -12603,6 +12810,69 @@ var init_divergence = __esm({
|
|
|
12603
12810
|
}
|
|
12604
12811
|
});
|
|
12605
12812
|
|
|
12813
|
+
// src/ai/json-stream.ts
|
|
12814
|
+
var StreamingJsonArrayParser;
|
|
12815
|
+
var init_json_stream = __esm({
|
|
12816
|
+
"src/ai/json-stream.ts"() {
|
|
12817
|
+
"use strict";
|
|
12818
|
+
StreamingJsonArrayParser = class {
|
|
12819
|
+
buf = "";
|
|
12820
|
+
pos = 0;
|
|
12821
|
+
inArray = false;
|
|
12822
|
+
arrayClosed = false;
|
|
12823
|
+
inString = false;
|
|
12824
|
+
escaped = false;
|
|
12825
|
+
depth = 0;
|
|
12826
|
+
elementStart = -1;
|
|
12827
|
+
/** Feed a chunk; returns the objects completed by this chunk, in order. */
|
|
12828
|
+
push(chunk) {
|
|
12829
|
+
this.buf += chunk;
|
|
12830
|
+
const out = [];
|
|
12831
|
+
if (this.arrayClosed) return out;
|
|
12832
|
+
while (this.pos < this.buf.length) {
|
|
12833
|
+
const ch = this.buf[this.pos];
|
|
12834
|
+
if (!this.inArray) {
|
|
12835
|
+
if (ch === "[") this.inArray = true;
|
|
12836
|
+
this.pos++;
|
|
12837
|
+
continue;
|
|
12838
|
+
}
|
|
12839
|
+
if (this.inString) {
|
|
12840
|
+
if (this.escaped) this.escaped = false;
|
|
12841
|
+
else if (ch === "\\") this.escaped = true;
|
|
12842
|
+
else if (ch === '"') this.inString = false;
|
|
12843
|
+
this.pos++;
|
|
12844
|
+
continue;
|
|
12845
|
+
}
|
|
12846
|
+
if (ch === '"') {
|
|
12847
|
+
this.inString = true;
|
|
12848
|
+
} else if (ch === "{") {
|
|
12849
|
+
if (this.depth === 0) this.elementStart = this.pos;
|
|
12850
|
+
this.depth++;
|
|
12851
|
+
} else if (ch === "}") {
|
|
12852
|
+
if (this.depth > 0) {
|
|
12853
|
+
this.depth--;
|
|
12854
|
+
if (this.depth === 0 && this.elementStart >= 0) {
|
|
12855
|
+
const raw = this.buf.slice(this.elementStart, this.pos + 1);
|
|
12856
|
+
this.elementStart = -1;
|
|
12857
|
+
try {
|
|
12858
|
+
out.push(JSON.parse(raw));
|
|
12859
|
+
} catch {
|
|
12860
|
+
}
|
|
12861
|
+
}
|
|
12862
|
+
}
|
|
12863
|
+
} else if (ch === "]" && this.depth === 0) {
|
|
12864
|
+
this.arrayClosed = true;
|
|
12865
|
+
this.pos++;
|
|
12866
|
+
break;
|
|
12867
|
+
}
|
|
12868
|
+
this.pos++;
|
|
12869
|
+
}
|
|
12870
|
+
return out;
|
|
12871
|
+
}
|
|
12872
|
+
};
|
|
12873
|
+
}
|
|
12874
|
+
});
|
|
12875
|
+
|
|
12606
12876
|
// src/ai/findings.ts
|
|
12607
12877
|
function companyContextSection() {
|
|
12608
12878
|
const block = buildCompanyProfileBlock();
|
|
@@ -12711,6 +12981,8 @@ async function* streamFindings(input, ctx) {
|
|
|
12711
12981
|
assertReplAi(ctx);
|
|
12712
12982
|
const userMessage = buildUserMessage(input);
|
|
12713
12983
|
let meta = { model_used: "unknown", provider_used: "anthropic", failover: false };
|
|
12984
|
+
const scanner = new StreamingJsonArrayParser();
|
|
12985
|
+
let streamed = 0;
|
|
12714
12986
|
for await (const event of streamWithFailover(
|
|
12715
12987
|
{
|
|
12716
12988
|
surface: "findings",
|
|
@@ -12720,12 +12992,18 @@ async function* streamFindings(input, ctx) {
|
|
|
12720
12992
|
},
|
|
12721
12993
|
{ ctx }
|
|
12722
12994
|
)) {
|
|
12995
|
+
if (event.type === "text_delta") {
|
|
12996
|
+
for (const parsed of scanner.push(event.text)) {
|
|
12997
|
+
streamed++;
|
|
12998
|
+
yield { type: "finding", finding: parsed };
|
|
12999
|
+
}
|
|
13000
|
+
}
|
|
12723
13001
|
if (event.type === "done") {
|
|
12724
13002
|
meta = event.meta;
|
|
12725
13003
|
const parsed = parseJsonArrayFromText(event.response.text);
|
|
12726
13004
|
if (parsed === null) throw new Error("AI response is not valid JSON");
|
|
12727
13005
|
const findings = parsed;
|
|
12728
|
-
for (const finding of findings) yield { type: "finding", finding };
|
|
13006
|
+
for (const finding of findings.slice(streamed)) yield { type: "finding", finding };
|
|
12729
13007
|
yield {
|
|
12730
13008
|
type: "done",
|
|
12731
13009
|
findings,
|
|
@@ -12744,6 +13022,7 @@ var init_findings = __esm({
|
|
|
12744
13022
|
init_failover();
|
|
12745
13023
|
init_prompt_parts();
|
|
12746
13024
|
init_json_response();
|
|
13025
|
+
init_json_stream();
|
|
12747
13026
|
}
|
|
12748
13027
|
});
|
|
12749
13028
|
|
|
@@ -13021,15 +13300,15 @@ __export(diagnose_exports, {
|
|
|
13021
13300
|
handler: () => handler
|
|
13022
13301
|
});
|
|
13023
13302
|
import chalk11 from "chalk";
|
|
13024
|
-
import ora2 from "ora";
|
|
13025
13303
|
async function handler(args, ctx) {
|
|
13026
13304
|
await hydrateAnalysisFromPersistedState(ctx);
|
|
13027
|
-
const { flags } = parseArgs(args, ["findings", "deep"]);
|
|
13305
|
+
const { flags } = parseArgs(args, ["findings", "deep", "compact"]);
|
|
13028
13306
|
const options = {
|
|
13029
13307
|
findings: getBool(flags, "findings") || getBool(flags, "deep"),
|
|
13030
13308
|
deep: getBool(flags, "deep"),
|
|
13031
13309
|
segments: !getFalse(flags, "segments"),
|
|
13032
|
-
segment: getString(flags, "segment")
|
|
13310
|
+
segment: getString(flags, "segment"),
|
|
13311
|
+
compact: getBool(flags, "compact")
|
|
13033
13312
|
};
|
|
13034
13313
|
if (isStructuredOutput(ctx.execution)) {
|
|
13035
13314
|
try {
|
|
@@ -13073,10 +13352,11 @@ async function handler(args, ctx) {
|
|
|
13073
13352
|
markLensCompleted(ctx, "gtm_health");
|
|
13074
13353
|
if (ctx.stage === "new") ctx.stage = "analyzed";
|
|
13075
13354
|
saveSessionState(ctx);
|
|
13076
|
-
if (!ctx.oneShot && !isStructuredOutput(ctx.execution)) {
|
|
13355
|
+
if (!ctx.oneShot && !isStructuredOutput(ctx.execution) && !ctx.suppressCompanionFooter) {
|
|
13077
13356
|
const companion = await resolveCompanionRecommendation(ctx);
|
|
13078
13357
|
printCompanionFooter(ctx, companion, { justCompleted: "gtm_health" });
|
|
13079
13358
|
}
|
|
13359
|
+
ctx.suppressCompanionFooter = false;
|
|
13080
13360
|
if (!ctx.skipTimeBankDiagnoseCredit) {
|
|
13081
13361
|
creditDiagnoseComplete(ctx, options.findings);
|
|
13082
13362
|
}
|
|
@@ -13135,7 +13415,8 @@ async function runDiagnose(options, ctx) {
|
|
|
13135
13415
|
diagnoseGenerator,
|
|
13136
13416
|
runFindings,
|
|
13137
13417
|
storeFindings: storeFindingsFn,
|
|
13138
|
-
deep: options.deep
|
|
13418
|
+
deep: options.deep,
|
|
13419
|
+
compact: options.compact
|
|
13139
13420
|
});
|
|
13140
13421
|
return buildDiagnoseSummary(fullResult.aggregate, findings);
|
|
13141
13422
|
} catch (err) {
|
|
@@ -13144,7 +13425,7 @@ async function runDiagnose(options, ctx) {
|
|
|
13144
13425
|
}
|
|
13145
13426
|
}
|
|
13146
13427
|
async function runSegmentDiagnose(options) {
|
|
13147
|
-
const spinner =
|
|
13428
|
+
const spinner = makeSpinner("Computing vital signs\u2026");
|
|
13148
13429
|
let result;
|
|
13149
13430
|
try {
|
|
13150
13431
|
result = await computeFullHealth();
|
|
@@ -13203,6 +13484,7 @@ function buildDiagnoseSummary(aggregate, findings) {
|
|
|
13203
13484
|
var init_diagnose = __esm({
|
|
13204
13485
|
"src/commands/diagnose.ts"() {
|
|
13205
13486
|
"use strict";
|
|
13487
|
+
init_spinner();
|
|
13206
13488
|
init_schema();
|
|
13207
13489
|
init_segments();
|
|
13208
13490
|
init_divergence();
|
|
@@ -14261,11 +14543,90 @@ function validateStrategistPlan(raw, opts) {
|
|
|
14261
14543
|
};
|
|
14262
14544
|
return { plan, issues, measurableTargets, totalTargets };
|
|
14263
14545
|
}
|
|
14546
|
+
function buildGroundedFallbackPlan(input) {
|
|
14547
|
+
const today = parseIsoDate(input.todayIso) ?? /* @__PURE__ */ new Date();
|
|
14548
|
+
const triggered = matchTriggeredPlays(input.vitals, LAYERS);
|
|
14549
|
+
const issues = [
|
|
14550
|
+
"LLM plan JSON invalid \u2014 using grounded fallback from triggered plays and live vitals"
|
|
14551
|
+
];
|
|
14552
|
+
const sources = triggered.length > 0 ? triggered.slice(0, 3) : input.vitals.slice().sort((a, b) => a.score - b.score).slice(0, 2).map((vital) => {
|
|
14553
|
+
const play = getPlaybook().find((p) => p.trigger_vital_sign === vital.vital_sign) ?? getPlaybook()[0];
|
|
14554
|
+
return { play, vital, layer: 1 };
|
|
14555
|
+
});
|
|
14556
|
+
const workstreams = sources.map(({ play, vital }, index) => {
|
|
14557
|
+
const score = Math.round(vital.score);
|
|
14558
|
+
const dollar = vital.dollar_value != null && vital.dollar_value > 0 ? `$${Math.round(vital.dollar_value).toLocaleString("en-US")}` : null;
|
|
14559
|
+
const baseline = dollar ?? String(score);
|
|
14560
|
+
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));
|
|
14561
|
+
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));
|
|
14562
|
+
const checkDate = toIso(addDays(today, 21 + index * 7));
|
|
14563
|
+
const outcome = {
|
|
14564
|
+
metric: vital.vital_sign,
|
|
14565
|
+
baseline,
|
|
14566
|
+
target_range: `${baseline} -> ${targetLow}-${targetHigh}`,
|
|
14567
|
+
check_date: checkDate,
|
|
14568
|
+
measured_by: `${vital.vital_sign} vital sign`
|
|
14569
|
+
};
|
|
14570
|
+
return {
|
|
14571
|
+
order: index + 1,
|
|
14572
|
+
title: play.name,
|
|
14573
|
+
problem: `${vital.vital_sign} score ${score} (${vital.status})${dollar ? ` \u2014 ${dollar} ${vital.dollar_label ?? ""}`.trimEnd() : ""}`,
|
|
14574
|
+
rationale: index === 0 ? "Layer-order first: clean or unblock the gating vital before downstream work" : "Next in dependency order after the prior workstream",
|
|
14575
|
+
play_ids: [play.id],
|
|
14576
|
+
actions: play.steps.slice(0, 3),
|
|
14577
|
+
effort_hours: 8 + index * 4,
|
|
14578
|
+
milestones: [
|
|
14579
|
+
{
|
|
14580
|
+
label: `Check ${vital.vital_sign} movement`,
|
|
14581
|
+
due: checkDate,
|
|
14582
|
+
verification: `${vital.vital_sign} score moves toward ${targetLow}-${targetHigh} (baseline ${baseline})`
|
|
14583
|
+
}
|
|
14584
|
+
],
|
|
14585
|
+
deliverables: [
|
|
14586
|
+
{
|
|
14587
|
+
label: `${play.name} triage list`,
|
|
14588
|
+
kind: "artifact",
|
|
14589
|
+
due: toIso(addDays(today, 7 + index * 7))
|
|
14590
|
+
}
|
|
14591
|
+
],
|
|
14592
|
+
expected_outcome: outcome,
|
|
14593
|
+
leading_indicators: [],
|
|
14594
|
+
contingency: {
|
|
14595
|
+
trigger: `${vital.vital_sign} flat or worse at first check`,
|
|
14596
|
+
trigger_check_date: checkDate,
|
|
14597
|
+
fallback: "Descope to the single highest-dollar entity cohort and re-run /strategy review"
|
|
14598
|
+
}
|
|
14599
|
+
};
|
|
14600
|
+
});
|
|
14601
|
+
const gating = input.gatingVitalSign ?? sources[0]?.vital.vital_sign ?? "freshness";
|
|
14602
|
+
const varLabel = input.totalValueAtRisk != null && input.totalValueAtRisk > 0 ? `$${Math.round(input.totalValueAtRisk).toLocaleString("en-US")} at risk` : "material pipeline dollars at risk";
|
|
14603
|
+
const plan = {
|
|
14604
|
+
title: "Grounded recovery plan",
|
|
14605
|
+
objective: input.objective,
|
|
14606
|
+
summary_30k: `${gating} is the gating pressure (${varLabel}). This fallback sequences ${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.`,
|
|
14607
|
+
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.",
|
|
14608
|
+
target_segment: "Whole pipeline",
|
|
14609
|
+
priority: "high",
|
|
14610
|
+
review_cadence: "Weekly",
|
|
14611
|
+
confidence: 0.45,
|
|
14612
|
+
constraints: ["Generated without a validated LLM plan JSON \u2014 confirm capacity before staffing"],
|
|
14613
|
+
assumptions: ["Outcome ranges are heuristic halves/increments of live vitals, not model-authored forecasts"],
|
|
14614
|
+
risks: ["Fallback plans lack stress-test revisions \u2014 run /strategy once the engine emits valid JSON"],
|
|
14615
|
+
workstreams
|
|
14616
|
+
};
|
|
14617
|
+
return {
|
|
14618
|
+
plan,
|
|
14619
|
+
issues,
|
|
14620
|
+
measurableTargets: workstreams.length,
|
|
14621
|
+
totalTargets: workstreams.length
|
|
14622
|
+
};
|
|
14623
|
+
}
|
|
14264
14624
|
var NUMBER_RE, SUFFIX_MULTIPLIER, INSTRUMENT_TOKENS, ISO_DATE_RE;
|
|
14265
14625
|
var init_strategist_validate = __esm({
|
|
14266
14626
|
"src/ai/strategist-validate.ts"() {
|
|
14267
14627
|
"use strict";
|
|
14268
14628
|
init_playbook();
|
|
14629
|
+
init_health_score();
|
|
14269
14630
|
init_json_response();
|
|
14270
14631
|
NUMBER_RE = /\$?\s*(\d[\d,]*\.?\d*)\s*(m|k|b|million|thousand|billion)?\b/gi;
|
|
14271
14632
|
SUFFIX_MULTIPLIER = {
|
|
@@ -14406,14 +14767,14 @@ async function* strategistPlanSession(options) {
|
|
|
14406
14767
|
let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
|
|
14407
14768
|
const loopGuard = new ToolLoopGuard();
|
|
14408
14769
|
const allowedTools = new Set(tools.map((t) => t.name));
|
|
14409
|
-
const callLlm = async (surface, withTools) => {
|
|
14770
|
+
const callLlm = async (surface, withTools, maxTokens = STAGE_MAX_TOKENS) => {
|
|
14410
14771
|
const attempt = () => completeWithFailover(
|
|
14411
14772
|
{
|
|
14412
14773
|
surface,
|
|
14413
14774
|
messages,
|
|
14414
14775
|
system: systemPrompt,
|
|
14415
14776
|
tools: withTools && tools.length > 0 ? tools : void 0,
|
|
14416
|
-
max_tokens:
|
|
14777
|
+
max_tokens: maxTokens
|
|
14417
14778
|
},
|
|
14418
14779
|
{ tier: tierForSurface(surface, llmCfg.tier), ctx: options.ctx }
|
|
14419
14780
|
);
|
|
@@ -14428,11 +14789,17 @@ async function* strategistPlanSession(options) {
|
|
|
14428
14789
|
lastMeta = result.meta;
|
|
14429
14790
|
return result.response;
|
|
14430
14791
|
};
|
|
14431
|
-
async function* runStage(surface, maxRounds, budgetNudge) {
|
|
14792
|
+
async function* runStage(surface, maxRounds, budgetNudge, requireJson = false) {
|
|
14432
14793
|
for (let round = 0; round < maxRounds; round++) {
|
|
14433
14794
|
const response = await callLlm(surface, true);
|
|
14434
14795
|
if (response.tool_calls.length === 0) {
|
|
14435
14796
|
messages.push(response.assistant_message);
|
|
14797
|
+
if (requireJson && !parseJsonObjectFromText(response.text)) {
|
|
14798
|
+
messages.push({ role: "user", content: budgetNudge });
|
|
14799
|
+
const forced = await callLlm(surface, false, PLAN_JSON_MAX_TOKENS);
|
|
14800
|
+
messages.push(forced.assistant_message);
|
|
14801
|
+
return forced.text;
|
|
14802
|
+
}
|
|
14436
14803
|
return response.text;
|
|
14437
14804
|
}
|
|
14438
14805
|
messages.push(response.assistant_message);
|
|
@@ -14449,10 +14816,26 @@ async function* strategistPlanSession(options) {
|
|
|
14449
14816
|
}
|
|
14450
14817
|
}
|
|
14451
14818
|
messages.push({ role: "user", content: budgetNudge });
|
|
14452
|
-
const final = await callLlm(surface, false);
|
|
14819
|
+
const final = await callLlm(surface, false, requireJson ? PLAN_JSON_MAX_TOKENS : STAGE_MAX_TOKENS);
|
|
14453
14820
|
messages.push(final.assistant_message);
|
|
14454
14821
|
return final.text;
|
|
14455
14822
|
}
|
|
14823
|
+
const vitalsForFallback = options.computeResult.aggregate.vital_signs.map((v) => ({
|
|
14824
|
+
vital_sign: v.vital_sign,
|
|
14825
|
+
score: v.score,
|
|
14826
|
+
status: v.status,
|
|
14827
|
+
dollar_value: v.dollar_value,
|
|
14828
|
+
dollar_label: v.dollar_label
|
|
14829
|
+
}));
|
|
14830
|
+
function groundedFallback() {
|
|
14831
|
+
return buildGroundedFallbackPlan({
|
|
14832
|
+
objective: options.objective,
|
|
14833
|
+
vitals: vitalsForFallback,
|
|
14834
|
+
todayIso,
|
|
14835
|
+
gatingVitalSign: options.computeResult.aggregate.gating_vital_sign,
|
|
14836
|
+
totalValueAtRisk: options.computeResult.aggregate.total_value_at_risk
|
|
14837
|
+
});
|
|
14838
|
+
}
|
|
14456
14839
|
yield { type: "stage", stage: "ground", label: STAGE_LABELS.ground };
|
|
14457
14840
|
const digestText = yield* runStage(
|
|
14458
14841
|
"strategist",
|
|
@@ -14466,34 +14849,78 @@ async function* strategistPlanSession(options) {
|
|
|
14466
14849
|
const digestForEvidence = digest ? JSON.stringify(digest) : digestText;
|
|
14467
14850
|
yield { type: "stage", stage: "backcast", label: STAGE_LABELS.backcast };
|
|
14468
14851
|
messages.push({ role: "user", content: buildBackcastMessage(options.objective) });
|
|
14469
|
-
yield* runStage(
|
|
14852
|
+
const backcastText = yield* runStage(
|
|
14470
14853
|
"strategist",
|
|
14471
14854
|
BACKCAST_MAX_ROUNDS,
|
|
14472
|
-
"Tool budget reached for planning. Respond with the full plan JSON now \u2014 strict JSON only."
|
|
14855
|
+
"Tool budget reached for planning. Respond with the full plan JSON now \u2014 strict JSON only. No hypotheses, no prose.",
|
|
14856
|
+
true
|
|
14473
14857
|
);
|
|
14858
|
+
const evidenceTextEarly = `${digestForEvidence}
|
|
14859
|
+
${healthSnapshot}`;
|
|
14860
|
+
let candidatePlan = validatePlanText(
|
|
14861
|
+
backcastText,
|
|
14862
|
+
evidenceTextEarly,
|
|
14863
|
+
todayIso
|
|
14864
|
+
);
|
|
14865
|
+
if (!candidatePlan) {
|
|
14866
|
+
const why = describePlanValidationFailure(backcastText, evidenceTextEarly, todayIso);
|
|
14867
|
+
messages.push({
|
|
14868
|
+
role: "user",
|
|
14869
|
+
content: `Backcast output did not validate (${why}). Respond with ONLY the full plan JSON object now \u2014 no hypotheses, no tools.
|
|
14870
|
+
Keep \u22643 workstreams. Schema:
|
|
14871
|
+
${STRATEGIST_PLAN_SCHEMA_BLOCK}`
|
|
14872
|
+
});
|
|
14873
|
+
const forced = await callLlm("strategist", false, PLAN_JSON_MAX_TOKENS);
|
|
14874
|
+
messages.push(forced.assistant_message);
|
|
14875
|
+
candidatePlan = validatePlanText(forced.text, evidenceTextEarly, todayIso);
|
|
14876
|
+
}
|
|
14877
|
+
if (!candidatePlan) {
|
|
14878
|
+
candidatePlan = groundedFallback();
|
|
14879
|
+
yield {
|
|
14880
|
+
type: "notice",
|
|
14881
|
+
text: "Backcast JSON invalid \u2014 armed grounded playbook fallback if stress-test also fails."
|
|
14882
|
+
};
|
|
14883
|
+
} else {
|
|
14884
|
+
yield {
|
|
14885
|
+
type: "notice",
|
|
14886
|
+
text: "Backcast plan validated \u2014 will use it if the stress-test revision fails validation."
|
|
14887
|
+
};
|
|
14888
|
+
}
|
|
14474
14889
|
yield { type: "stage", stage: "stress", label: STAGE_LABELS.stress };
|
|
14475
14890
|
messages.push({ role: "user", content: buildStressTestMessage() });
|
|
14476
14891
|
const finalText = yield* runStage(
|
|
14477
14892
|
"strategist_stress",
|
|
14478
14893
|
STRESS_MAX_ROUNDS,
|
|
14479
|
-
"Tool budget reached. Respond with the FINAL revised plan JSON now \u2014 strict JSON only."
|
|
14894
|
+
"Tool budget reached. Respond with the FINAL revised plan JSON now \u2014 strict JSON only. No prose. \u22643 workstreams.",
|
|
14895
|
+
true
|
|
14480
14896
|
);
|
|
14481
|
-
const evidenceText =
|
|
14482
|
-
${healthSnapshot}`;
|
|
14897
|
+
const evidenceText = evidenceTextEarly;
|
|
14483
14898
|
let validated = validatePlanText(finalText, evidenceText, todayIso);
|
|
14484
14899
|
if (!validated) {
|
|
14900
|
+
const why = describePlanValidationFailure(finalText, evidenceText, todayIso);
|
|
14485
14901
|
messages.push({
|
|
14486
14902
|
role: "user",
|
|
14487
|
-
content:
|
|
14903
|
+
content: `That response did not validate as a usable plan. Reason: ${why}
|
|
14904
|
+
Respond with ONLY the corrected plan JSON object in the required schema (title, objective, summary_30k, workstreams with measurable expected_outcome, dated milestones, contingency). \u22643 workstreams. Copy baselines from the health snapshot numbers exactly.`
|
|
14488
14905
|
});
|
|
14489
|
-
const retry = await callLlm("strategist_stress", false);
|
|
14906
|
+
const retry = await callLlm("strategist_stress", false, PLAN_JSON_MAX_TOKENS);
|
|
14490
14907
|
messages.push(retry.assistant_message);
|
|
14491
14908
|
validated = validatePlanText(retry.text, evidenceText, todayIso);
|
|
14492
14909
|
}
|
|
14910
|
+
if (!validated && candidatePlan) {
|
|
14911
|
+
const fromFallback = candidatePlan.issues.some((i) => i.includes("grounded fallback"));
|
|
14912
|
+
yield {
|
|
14913
|
+
type: "notice",
|
|
14914
|
+
text: fromFallback ? "Stress-test revision invalid \u2014 using grounded playbook fallback plan." : "Stress-test revision invalid \u2014 using backcast plan."
|
|
14915
|
+
};
|
|
14916
|
+
validated = candidatePlan;
|
|
14917
|
+
}
|
|
14493
14918
|
if (!validated) {
|
|
14494
|
-
|
|
14495
|
-
|
|
14496
|
-
|
|
14919
|
+
validated = groundedFallback();
|
|
14920
|
+
yield {
|
|
14921
|
+
type: "notice",
|
|
14922
|
+
text: "Strategist JSON failed validation \u2014 emitting grounded fallback plan from live vitals."
|
|
14923
|
+
};
|
|
14497
14924
|
}
|
|
14498
14925
|
for (const issue of validated.issues) {
|
|
14499
14926
|
yield { type: "notice", text: issue };
|
|
@@ -14519,7 +14946,21 @@ function validatePlanText(text, evidenceText, todayIso) {
|
|
|
14519
14946
|
if (!raw) return null;
|
|
14520
14947
|
return validateStrategistPlan(raw, { evidenceText, todayIso });
|
|
14521
14948
|
}
|
|
14522
|
-
|
|
14949
|
+
function describePlanValidationFailure(text, evidenceText, todayIso) {
|
|
14950
|
+
const raw = parseJsonObjectFromText(text);
|
|
14951
|
+
if (!raw) {
|
|
14952
|
+
if (text.includes("{") && !text.trim().endsWith("}")) {
|
|
14953
|
+
return "JSON object appears truncated (increase brevity: \u22643 workstreams) or incomplete";
|
|
14954
|
+
}
|
|
14955
|
+
return "not parseable as a JSON object (prose or truncated output)";
|
|
14956
|
+
}
|
|
14957
|
+
const result = validateStrategistPlan(raw, { evidenceText, todayIso });
|
|
14958
|
+
if (!result) {
|
|
14959
|
+
return "JSON parsed but no usable workstreams remained after measurability checks (need \u22651 workstream with measurable outcome or dated milestone)";
|
|
14960
|
+
}
|
|
14961
|
+
return "unknown validation failure";
|
|
14962
|
+
}
|
|
14963
|
+
var GROUND_MAX_ROUNDS, BACKCAST_MAX_ROUNDS, STRESS_MAX_ROUNDS, STAGE_MAX_TOKENS, PLAN_JSON_MAX_TOKENS, STAGE_LABELS;
|
|
14523
14964
|
var init_strategist2 = __esm({
|
|
14524
14965
|
"src/ai/strategist.ts"() {
|
|
14525
14966
|
"use strict";
|
|
@@ -14535,10 +14976,12 @@ var init_strategist2 = __esm({
|
|
|
14535
14976
|
init_thread();
|
|
14536
14977
|
init_strategist_prompt();
|
|
14537
14978
|
init_strategist_validate();
|
|
14979
|
+
init_strategist_prompt();
|
|
14538
14980
|
GROUND_MAX_ROUNDS = 6;
|
|
14539
14981
|
BACKCAST_MAX_ROUNDS = 4;
|
|
14540
14982
|
STRESS_MAX_ROUNDS = 2;
|
|
14541
14983
|
STAGE_MAX_TOKENS = 4096;
|
|
14984
|
+
PLAN_JSON_MAX_TOKENS = 8192;
|
|
14542
14985
|
STAGE_LABELS = {
|
|
14543
14986
|
ground: "Grounding \u2014 reading health, metrics, segments, history",
|
|
14544
14987
|
backcast: "Sequencing \u2014 backcasting from objective",
|
|
@@ -14657,9 +15100,9 @@ __export(strategist_flow_exports, {
|
|
|
14657
15100
|
promptQueuedAiStrategist: () => promptQueuedAiStrategist,
|
|
14658
15101
|
queueStrategistForAnalysis: () => queueStrategistForAnalysis,
|
|
14659
15102
|
resumeStrategistAfterCompute: () => resumeStrategistAfterCompute,
|
|
15103
|
+
resumeStrategistAfterConnect: () => resumeStrategistAfterConnect,
|
|
14660
15104
|
startStrategistFlow: () => startStrategistFlow
|
|
14661
15105
|
});
|
|
14662
|
-
import ora3 from "ora";
|
|
14663
15106
|
import chalk13 from "chalk";
|
|
14664
15107
|
function isStrategistIntent(input) {
|
|
14665
15108
|
const line = input.trim();
|
|
@@ -14799,9 +15242,13 @@ async function runStrategistSession(ctx) {
|
|
|
14799
15242
|
if (!canUseReplAi(ctx)) {
|
|
14800
15243
|
await printKeylessSkeletonPlan(ctx, objective);
|
|
14801
15244
|
if (ctx.scope) ctx.scope.intent_summary = objective;
|
|
14802
|
-
ctx.strategistState =
|
|
15245
|
+
ctx.strategistState = {
|
|
15246
|
+
...state2,
|
|
15247
|
+
step: "awaiting_connect",
|
|
15248
|
+
objective
|
|
15249
|
+
};
|
|
14803
15250
|
saveSessionState(ctx);
|
|
14804
|
-
return "Skeleton plan (
|
|
15251
|
+
return "Skeleton plan (awaiting connect)";
|
|
14805
15252
|
}
|
|
14806
15253
|
if (ctx.rl && !state2.constraintsNote) {
|
|
14807
15254
|
const prompts = createPromptSession(ctx.rl, ctx);
|
|
@@ -14817,7 +15264,7 @@ async function runStrategistSession(ctx) {
|
|
|
14817
15264
|
}
|
|
14818
15265
|
}
|
|
14819
15266
|
console.log();
|
|
14820
|
-
const spinner =
|
|
15267
|
+
const spinner = makeSpinner("Grounding\u2026");
|
|
14821
15268
|
let plan = null;
|
|
14822
15269
|
let stats = { measurable_targets: 0, total_targets: 0 };
|
|
14823
15270
|
let baselineBatchId = null;
|
|
@@ -14932,7 +15379,7 @@ async function runStrategistSession(ctx) {
|
|
|
14932
15379
|
}
|
|
14933
15380
|
async function ensureSnapshot(ctx) {
|
|
14934
15381
|
if (ctx.snapshot.computeResult) return ctx.snapshot.computeResult;
|
|
14935
|
-
const spinner =
|
|
15382
|
+
const spinner = makeSpinner("Reading latest vitals\u2026");
|
|
14936
15383
|
try {
|
|
14937
15384
|
const snapshot = await computeFullHealth();
|
|
14938
15385
|
ctx.snapshot.computeResult = snapshot;
|
|
@@ -14960,7 +15407,7 @@ function printObjectiveCard(ctx, objective, proposed) {
|
|
|
14960
15407
|
);
|
|
14961
15408
|
console.log();
|
|
14962
15409
|
console.log(
|
|
14963
|
-
" " + chalk13.dim("Confirm? ") + chalk13.cyan("yes") + chalk13.dim(" \xB7 ") + chalk13.cyan("adjust") + chalk13.dim(" \xB7 ") + chalk13.cyan("cancel")
|
|
15410
|
+
" " + chalk13.dim("Confirm? ") + chalk13.cyan("\u23CE yes") + chalk13.dim(" \xB7 ") + chalk13.cyan("adjust") + chalk13.dim(" \xB7 ") + chalk13.cyan("cancel")
|
|
14964
15411
|
);
|
|
14965
15412
|
console.log();
|
|
14966
15413
|
}
|
|
@@ -15006,17 +15453,28 @@ async function printKeylessSkeletonPlan(ctx, objective) {
|
|
|
15006
15453
|
}
|
|
15007
15454
|
}
|
|
15008
15455
|
console.log(
|
|
15009
|
-
" " + chalk13.dim("For the full strategist \u2014 milestones, outcome ranges, contingencies \u2014 run ") + paint("accent", "/connect") + chalk13.dim(" and paste any provider's key.")
|
|
15456
|
+
" " + chalk13.dim("For the full strategist \u2014 milestones, outcome ranges, contingencies \u2014 press ") + paint("accent", "\u23CE") + chalk13.dim(" to run ") + paint("accent", "/connect") + chalk13.dim(" and paste any provider's key.")
|
|
15010
15457
|
);
|
|
15011
15458
|
console.log(
|
|
15012
|
-
" " + chalk13.dim("Objective kept \u2014
|
|
15459
|
+
" " + chalk13.dim("Objective kept \u2014 after ") + paint("accent", "/connect") + chalk13.dim(" I'll bring back the confirm card so you can run the full plan.")
|
|
15013
15460
|
);
|
|
15014
15461
|
console.log();
|
|
15015
15462
|
}
|
|
15463
|
+
async function resumeStrategistAfterConnect(ctx) {
|
|
15464
|
+
const state2 = ctx.strategistState;
|
|
15465
|
+
if (!state2 || state2.step !== "awaiting_connect" || !state2.objective) return false;
|
|
15466
|
+
state2.step = "objective_confirm";
|
|
15467
|
+
saveSessionState(ctx);
|
|
15468
|
+
console.log();
|
|
15469
|
+
console.log(" " + paint("accent", "Engine connected \u2014 ready to build the full strategy."));
|
|
15470
|
+
printObjectiveCard(ctx, state2.objective, true);
|
|
15471
|
+
return true;
|
|
15472
|
+
}
|
|
15016
15473
|
var STRATEGIST_INTENT_RE, CANCEL_RE, CONFIRM_RE, ADJUST_RE, QUESTION_RE;
|
|
15017
15474
|
var init_strategist_flow = __esm({
|
|
15018
15475
|
"src/conversation/strategist-flow.ts"() {
|
|
15019
15476
|
"use strict";
|
|
15477
|
+
init_spinner();
|
|
15020
15478
|
init_context2();
|
|
15021
15479
|
init_handoff_draft();
|
|
15022
15480
|
init_repl_api();
|
|
@@ -15038,99 +15496,1003 @@ var init_strategist_flow = __esm({
|
|
|
15038
15496
|
}
|
|
15039
15497
|
});
|
|
15040
15498
|
|
|
15041
|
-
// src/conversation/
|
|
15042
|
-
var
|
|
15043
|
-
__export(
|
|
15044
|
-
|
|
15045
|
-
|
|
15499
|
+
// src/conversation/keyless-ask.ts
|
|
15500
|
+
var keyless_ask_exports = {};
|
|
15501
|
+
__export(keyless_ask_exports, {
|
|
15502
|
+
isKeylessVitalsAsk: () => isKeylessVitalsAsk,
|
|
15503
|
+
tryKeylessAskAnswer: () => tryKeylessAskAnswer
|
|
15046
15504
|
});
|
|
15047
|
-
import ora4 from "ora";
|
|
15048
15505
|
import chalk14 from "chalk";
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
findings: false,
|
|
15060
|
-
sessionAnalysis: ctx.analysis
|
|
15061
|
-
});
|
|
15062
|
-
spinner.succeed("SaaS metrics computed");
|
|
15063
|
-
ctx.analysis.coverage = result.coverage;
|
|
15064
|
-
ctx.analysis.data_source_type = result.data_source_type;
|
|
15065
|
-
ctx.analysis.recommended = result.companion_recommendation;
|
|
15066
|
-
ctx.analysis.headline = extractHeadlineMetrics2(result.metrics.aggregate.metrics);
|
|
15067
|
-
markLensCompleted(ctx, "revenue_metrics");
|
|
15068
|
-
ctx.stage = "analyzed";
|
|
15069
|
-
ctx.snapshot.computeResult = null;
|
|
15070
|
-
invalidateGapAudit(ctx);
|
|
15071
|
-
saveSessionState(ctx);
|
|
15072
|
-
creditGapCompute(ctx);
|
|
15073
|
-
creditMetricsComplete(ctx, false);
|
|
15074
|
-
renderMetricsReport2(ctx, result.metrics.aggregate.metrics, result.coverage, result.data_source_type, {
|
|
15075
|
-
snapshot: result.snapshot,
|
|
15076
|
-
findings: result.findings,
|
|
15077
|
-
companion: result.companion_recommendation ?? null,
|
|
15078
|
-
interactive: true
|
|
15079
|
-
});
|
|
15080
|
-
await resumeQueuedStrategist(ctx);
|
|
15081
|
-
return "SaaS metrics ready";
|
|
15082
|
-
} catch (err) {
|
|
15083
|
-
spinner.fail("Metrics failed");
|
|
15084
|
-
throw err;
|
|
15085
|
-
}
|
|
15506
|
+
function isKeylessVitalsAsk(input) {
|
|
15507
|
+
return KEYLESS_ASK_RE.test(input.trim());
|
|
15508
|
+
}
|
|
15509
|
+
function pickMostExpensive(vitals) {
|
|
15510
|
+
let best = null;
|
|
15511
|
+
for (const vs of vitals) {
|
|
15512
|
+
const dollars = vs.dollar_value ?? 0;
|
|
15513
|
+
if (!best) {
|
|
15514
|
+
best = vs;
|
|
15515
|
+
continue;
|
|
15086
15516
|
}
|
|
15087
|
-
const
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
const summary = await diagnose([], ctx);
|
|
15091
|
-
markLensCompleted(ctx, "gtm_health");
|
|
15092
|
-
ctx.stage = "analyzed";
|
|
15093
|
-
ctx.snapshot.computeResult = null;
|
|
15094
|
-
invalidateGapAudit(ctx);
|
|
15095
|
-
saveSessionState(ctx);
|
|
15096
|
-
const companion = await resolveCompanionRecommendation(ctx);
|
|
15097
|
-
printCompanionFooter(ctx, companion, { justCompleted: "gtm_health" });
|
|
15098
|
-
await resumeQueuedStrategist(ctx);
|
|
15099
|
-
return typeof summary === "string" ? summary : "Health analysis ready";
|
|
15100
|
-
} catch (err) {
|
|
15101
|
-
console.error(" " + chalk14.red(String(err.message ?? err)));
|
|
15102
|
-
return;
|
|
15103
|
-
} finally {
|
|
15104
|
-
ctx.computeInProgress = false;
|
|
15105
|
-
ctx.deliverIntent = false;
|
|
15517
|
+
const bestDollars = best.dollar_value ?? 0;
|
|
15518
|
+
if (dollars > bestDollars) best = vs;
|
|
15519
|
+
else if (dollars === bestDollars && vs.score < best.score) best = vs;
|
|
15106
15520
|
}
|
|
15521
|
+
return best;
|
|
15107
15522
|
}
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
await resumeStrategistAfterCompute2(ctx);
|
|
15523
|
+
function formatVitalLine(vs) {
|
|
15524
|
+
const label = VITAL_SIGN_LABELS[vs.vital_sign] ?? vs.vital_sign;
|
|
15525
|
+
const dollars = vs.dollar_value != null && vs.dollar_value > 0 ? `${formatCurrency(vs.dollar_value)} ${vs.dollar_label ?? ""}`.trim() : null;
|
|
15526
|
+
return dollars ? `${label} \u2014 score ${Math.round(vs.score)}, ${dollars}` : `${label} \u2014 score ${Math.round(vs.score)} (${vs.status})`;
|
|
15113
15527
|
}
|
|
15114
|
-
function
|
|
15115
|
-
|
|
15528
|
+
function formatRunnerBit(vs) {
|
|
15529
|
+
const label = VITAL_SIGN_LABELS[vs.vital_sign] ?? vs.vital_sign;
|
|
15530
|
+
const dollars = `${formatCurrency(vs.dollar_value ?? 0)} ${vs.dollar_label ?? ""}`.trim();
|
|
15531
|
+
return `${label} ${dollars}`;
|
|
15532
|
+
}
|
|
15533
|
+
async function tryKeylessAskAnswer(ctx, input, opts = {}) {
|
|
15534
|
+
if (!isKeylessVitalsAsk(input)) return false;
|
|
15535
|
+
let snapshot = ctx.snapshot.computeResult;
|
|
15536
|
+
if (!snapshot) {
|
|
15537
|
+
try {
|
|
15538
|
+
snapshot = await computeFullHealth();
|
|
15539
|
+
ctx.snapshot.computeResult = snapshot;
|
|
15540
|
+
} catch {
|
|
15541
|
+
return false;
|
|
15542
|
+
}
|
|
15543
|
+
}
|
|
15544
|
+
const aggregate = snapshot.aggregate;
|
|
15545
|
+
const gating = aggregate.vital_signs.find((v) => v.vital_sign === aggregate.gating_vital_sign);
|
|
15546
|
+
const expensive = pickMostExpensive(aggregate.vital_signs);
|
|
15547
|
+
const primary = expensive ?? gating;
|
|
15548
|
+
if (!primary) return false;
|
|
15549
|
+
const label = VITAL_SIGN_LABELS[primary.vital_sign] ?? primary.vital_sign;
|
|
15550
|
+
const dollarBit = primary.dollar_value != null && primary.dollar_value > 0 ? `${formatCurrency(primary.dollar_value)} ${primary.dollar_label ?? ""}`.trim() : null;
|
|
15551
|
+
const headline = dollarBit ? `${label} \u2014 ${dollarBit} \u2014 is the most expensive problem to solve right now.` : `${label} (score ${Math.round(primary.score)}, ${primary.status}) is the problem to fix first.`;
|
|
15552
|
+
const runners = [...aggregate.vital_signs].filter((v) => v.vital_sign !== primary.vital_sign && (v.dollar_value ?? 0) > 0).sort((a, b) => (b.dollar_value ?? 0) - (a.dollar_value ?? 0)).slice(0, 2);
|
|
15553
|
+
console.log();
|
|
15554
|
+
console.log(" " + chalk14.bold(headline));
|
|
15555
|
+
if (opts.fromResume) {
|
|
15556
|
+
if (runners.length > 0) {
|
|
15557
|
+
console.log(
|
|
15558
|
+
" " + chalk14.dim("Next after that: ") + chalk14.dim(runners.map(formatRunnerBit).join(" \xB7 "))
|
|
15559
|
+
);
|
|
15560
|
+
}
|
|
15561
|
+
} else {
|
|
15562
|
+
console.log();
|
|
15563
|
+
if (gating && gating.vital_sign !== primary.vital_sign) {
|
|
15564
|
+
console.log(
|
|
15565
|
+
" " + chalk14.dim("Gating vital: ") + paint("accent", VITAL_SIGN_LABELS[gating.vital_sign]) + chalk14.dim(` (score ${Math.round(gating.score)}) \u2014 it bounds what you can trust downstream.`)
|
|
15566
|
+
);
|
|
15567
|
+
}
|
|
15568
|
+
if (runners.length > 0) {
|
|
15569
|
+
console.log(" " + chalk14.dim("Also on the board:"));
|
|
15570
|
+
for (const vs of runners) {
|
|
15571
|
+
console.log(" " + chalk14.dim("\xB7 ") + formatVitalLine(vs));
|
|
15572
|
+
}
|
|
15573
|
+
}
|
|
15574
|
+
if (aggregate.total_value_at_risk != null && aggregate.total_value_at_risk > 0) {
|
|
15575
|
+
console.log(
|
|
15576
|
+
" " + chalk14.dim("Total at risk: ") + chalk14.green(formatCurrency(aggregate.total_value_at_risk))
|
|
15577
|
+
);
|
|
15578
|
+
}
|
|
15579
|
+
}
|
|
15580
|
+
console.log();
|
|
15581
|
+
console.log(
|
|
15582
|
+
" " + chalk14.dim("Press ") + paint("accent", "\u23CE") + chalk14.dim(" to connect a key (") + paint("accent", "/connect") + chalk14.dim(") for the why and the plan \u2014 I'll finish this question when you do.")
|
|
15583
|
+
);
|
|
15584
|
+
console.log();
|
|
15585
|
+
if (!opts.fromResume) {
|
|
15586
|
+
recordMessage(ctx, "user", input);
|
|
15587
|
+
}
|
|
15588
|
+
recordMessage(ctx, "agent", headline);
|
|
15589
|
+
saveSessionState(ctx);
|
|
15590
|
+
return true;
|
|
15591
|
+
}
|
|
15592
|
+
var KEYLESS_ASK_RE;
|
|
15593
|
+
var init_keyless_ask = __esm({
|
|
15594
|
+
"src/conversation/keyless-ask.ts"() {
|
|
15595
|
+
"use strict";
|
|
15596
|
+
init_context2();
|
|
15597
|
+
init_formatters();
|
|
15598
|
+
init_theme();
|
|
15599
|
+
init_health_score();
|
|
15600
|
+
KEYLESS_ASK_RE = /\b(most expensive|biggest risk|expensive problem|what should (i|we) fix|fix first|biggest problem|largest risk|where (are we|do we) (bleed|leak|hurt))\b/i;
|
|
15601
|
+
}
|
|
15602
|
+
});
|
|
15603
|
+
|
|
15604
|
+
// src/conversation/orchestrator.ts
|
|
15605
|
+
import chalk15 from "chalk";
|
|
15606
|
+
import { writeFileSync as writeFileSync13 } from "fs";
|
|
15607
|
+
import { join as join19 } from "path";
|
|
15608
|
+
async function handleExploreWithoutKey(ctx, input) {
|
|
15609
|
+
if (isKeylessVitalsAsk(input)) {
|
|
15610
|
+
queuePendingAsk(ctx, input, "explore");
|
|
15611
|
+
const answered = await tryKeylessAskAnswer(ctx, input);
|
|
15612
|
+
if (answered) {
|
|
15613
|
+
if (ctx.pendingAsk) {
|
|
15614
|
+
ctx.pendingAsk = { ...ctx.pendingAsk, keylessAnswered: true };
|
|
15615
|
+
saveSessionState(ctx);
|
|
15616
|
+
}
|
|
15617
|
+
return;
|
|
15618
|
+
}
|
|
15619
|
+
}
|
|
15620
|
+
const holder = ctx;
|
|
15621
|
+
const hits = (holder[NO_KEY_NUDGES] ?? 0) + 1;
|
|
15622
|
+
holder[NO_KEY_NUDGES] = hits;
|
|
15623
|
+
recordMessage(ctx, "user", input);
|
|
15624
|
+
if (looksLikeQuestion(input)) {
|
|
15625
|
+
queuePendingAsk(ctx, input, "explore");
|
|
15626
|
+
}
|
|
15627
|
+
if (hits === 1) {
|
|
15628
|
+
console.log();
|
|
15629
|
+
console.log(" " + chalk15.red("AI interpretation needs an LLM API key saved in config."));
|
|
15630
|
+
console.log(
|
|
15631
|
+
" " + chalk15.dim("Run ") + paint("accent", "/connect") + chalk15.dim(" and paste any provider's key (Anthropic, OpenAI, Groq, Gemini, ...).")
|
|
15632
|
+
);
|
|
15633
|
+
console.log(" " + chalk15.dim("Number crunching works without a key \u2014 only Q&A in the REPL needs one."));
|
|
15634
|
+
if (ctx.pendingAsk) {
|
|
15635
|
+
console.log(
|
|
15636
|
+
" " + chalk15.dim("Your question is queued \u2014 I'll answer it right after ") + paint("accent", "/connect") + chalk15.dim(".")
|
|
15637
|
+
);
|
|
15638
|
+
}
|
|
15639
|
+
if (ctx.gapAudit) {
|
|
15640
|
+
printGapCard(ctx.gapAudit);
|
|
15641
|
+
}
|
|
15642
|
+
console.log();
|
|
15643
|
+
recordMessage(
|
|
15644
|
+
ctx,
|
|
15645
|
+
"agent",
|
|
15646
|
+
"No LLM engine connected \u2014 Q&A needs a key. Pointed to /connect."
|
|
15647
|
+
);
|
|
15648
|
+
return;
|
|
15649
|
+
}
|
|
15650
|
+
console.log();
|
|
15651
|
+
console.log(" " + chalk15.yellow("Still no engine connected \u2014 Q&A stays offline until you run ") + paint("accent", "/connect") + chalk15.yellow("."));
|
|
15652
|
+
console.log(" " + chalk15.dim("These work without one:"));
|
|
15653
|
+
console.log(" " + paint("accent", "/playbook") + chalk15.dim(" recommended plays from your computed vitals"));
|
|
15654
|
+
console.log(" " + chalk15.cyan('"how should we fix this?"') + chalk15.dim(" deterministic skeleton plan"));
|
|
15655
|
+
console.log(" " + paint("accent", "/handoff") + chalk15.dim(" export this analysis for another tool"));
|
|
15656
|
+
console.log();
|
|
15657
|
+
recordMessage(
|
|
15658
|
+
ctx,
|
|
15659
|
+
"agent",
|
|
15660
|
+
"No LLM engine connected \u2014 offered keyless paths (/playbook, skeleton plan, /handoff)."
|
|
15661
|
+
);
|
|
15662
|
+
}
|
|
15663
|
+
var NO_KEY_NUDGES;
|
|
15664
|
+
var init_orchestrator = __esm({
|
|
15665
|
+
"src/conversation/orchestrator.ts"() {
|
|
15666
|
+
"use strict";
|
|
15667
|
+
init_context2();
|
|
15668
|
+
init_store();
|
|
15669
|
+
init_theme();
|
|
15670
|
+
init_phase();
|
|
15671
|
+
init_scope();
|
|
15672
|
+
init_gap_audit();
|
|
15673
|
+
init_gap_card();
|
|
15674
|
+
init_compute2();
|
|
15675
|
+
init_handoff_draft();
|
|
15676
|
+
init_prompts();
|
|
15677
|
+
init_time_bank();
|
|
15678
|
+
init_pending_ask();
|
|
15679
|
+
init_keyless_ask();
|
|
15680
|
+
NO_KEY_NUDGES = /* @__PURE__ */ Symbol.for("ntrp.noKeyNudges");
|
|
15681
|
+
}
|
|
15682
|
+
});
|
|
15683
|
+
|
|
15684
|
+
// src/repositories/bundle.ts
|
|
15685
|
+
async function buildRepositoryExportPackage(options) {
|
|
15686
|
+
await initSchema();
|
|
15687
|
+
const bundle = await loadSessionAnalysisBundle();
|
|
15688
|
+
const diagnosis = bundle.diagnosis;
|
|
15689
|
+
if (!diagnosis) {
|
|
15690
|
+
if (bundle.metrics) {
|
|
15691
|
+
throw new NtrpError(
|
|
15692
|
+
"diagnosis_required",
|
|
15693
|
+
"Publish packages require a GTM health snapshot. Run /diagnose (companion) or /handoff report for metrics-only export.",
|
|
15694
|
+
4 /* NoData */
|
|
15695
|
+
);
|
|
15696
|
+
}
|
|
15697
|
+
if (!hasAnyAnalysis(bundle)) {
|
|
15698
|
+
throw new NtrpError("diagnosis_required", "No analysis data found. Run /new, /diagnose, or /metrics first.", 4 /* NoData */);
|
|
15699
|
+
}
|
|
15700
|
+
throw new NtrpError("diagnosis_required", "No diagnosis data found. Run /diagnose first.", 4 /* NoData */);
|
|
15701
|
+
}
|
|
15702
|
+
const strategies = await listStrategies("all");
|
|
15703
|
+
const strategiesWithSources = await Promise.all(
|
|
15704
|
+
strategies.map(async (strategy) => ({
|
|
15705
|
+
strategy,
|
|
15706
|
+
sources: await listStrategySources(strategy.id)
|
|
15707
|
+
}))
|
|
15708
|
+
);
|
|
15709
|
+
const proposals = await listActionProposals(100);
|
|
15710
|
+
const actions = await Promise.all(
|
|
15711
|
+
proposals.map(async (proposal) => ({
|
|
15712
|
+
proposal,
|
|
15713
|
+
executions: await listActionExecutions(proposal.id)
|
|
15714
|
+
}))
|
|
15715
|
+
);
|
|
15716
|
+
const sections = buildSections({
|
|
15717
|
+
diagnosis,
|
|
15718
|
+
strategiesCount: strategiesWithSources.length,
|
|
15719
|
+
actionsCount: actions.length
|
|
15720
|
+
});
|
|
15721
|
+
return {
|
|
15722
|
+
schema_version: "ntrp.repository_export.v1",
|
|
15723
|
+
export_id: uuid(),
|
|
15724
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15725
|
+
target: options.target,
|
|
15726
|
+
summary: {
|
|
15727
|
+
overall_score: diagnosis.health.overall_score,
|
|
15728
|
+
overall_status: diagnosis.health.overall_status,
|
|
15729
|
+
gating_vital_sign: diagnosis.health.gating_vital_sign,
|
|
15730
|
+
total_value_at_risk: diagnosis.health.total_value_at_risk ?? null,
|
|
15731
|
+
findings_count: diagnosis.findings.length,
|
|
15732
|
+
strategies_count: strategiesWithSources.length,
|
|
15733
|
+
action_proposals_count: actions.length
|
|
15734
|
+
},
|
|
15735
|
+
diagnosis: {
|
|
15736
|
+
health: diagnosis.health,
|
|
15737
|
+
segments: diagnosis.segments.map((segment) => ({
|
|
15738
|
+
segment: segment.segment,
|
|
15739
|
+
result: segment.result
|
|
15740
|
+
})),
|
|
15741
|
+
findings: diagnosis.findings,
|
|
15742
|
+
entity_counts: diagnosis.entityCounts,
|
|
15743
|
+
upload_batch_id: diagnosis.uploadBatchId
|
|
15744
|
+
},
|
|
15745
|
+
strategies: strategiesWithSources,
|
|
15746
|
+
actions,
|
|
15747
|
+
sections,
|
|
15748
|
+
provenance: {
|
|
15749
|
+
command: options.command ?? "publish",
|
|
15750
|
+
model_or_fixture: options.modelOrFixture,
|
|
15751
|
+
source: options.source ?? "local_duckdb",
|
|
15752
|
+
notes: [
|
|
15753
|
+
"Generated from the latest persisted diagnosis.",
|
|
15754
|
+
"Repository writes are approval-gated through local action proposals."
|
|
15755
|
+
]
|
|
15756
|
+
}
|
|
15757
|
+
};
|
|
15758
|
+
}
|
|
15759
|
+
function buildSections(input) {
|
|
15760
|
+
const { diagnosis } = input;
|
|
15761
|
+
const health = diagnosis.health;
|
|
15762
|
+
return [
|
|
15763
|
+
{
|
|
15764
|
+
id: "cover",
|
|
15765
|
+
title: "Cover Summary",
|
|
15766
|
+
summary: `${health.overall_score}/100 ${health.overall_status}, gated by ${health.gating_vital_sign}`,
|
|
15767
|
+
markdown: [
|
|
15768
|
+
`Overall score: **${health.overall_score}/100** (${health.overall_status})`,
|
|
15769
|
+
`Gating vital sign: **${VITAL_SIGN_LABELS[health.gating_vital_sign]}**`,
|
|
15770
|
+
`Total value at risk: **${health.total_value_at_risk ? formatCurrency(health.total_value_at_risk) : "N/A"}**`,
|
|
15771
|
+
`Findings: **${diagnosis.findings.length}**`,
|
|
15772
|
+
`Strategies: **${input.strategiesCount}**`,
|
|
15773
|
+
`Action proposals: **${input.actionsCount}**`
|
|
15774
|
+
].join("\n\n")
|
|
15775
|
+
},
|
|
15776
|
+
{
|
|
15777
|
+
id: "vital-signs",
|
|
15778
|
+
title: "Vital Signs",
|
|
15779
|
+
summary: `${health.vital_signs.length} vital signs`,
|
|
15780
|
+
markdown: health.vital_signs.map((vs) => `- **${VITAL_SIGN_LABELS[vs.vital_sign]}:** ${Math.round(vs.score)}/100 (${vs.status}) \u2014 ${formatDollarImpact(vs.dollar_value, vs.dollar_label)}`).join("\n"),
|
|
15781
|
+
children: health.vital_signs.map((vs) => ({
|
|
15782
|
+
id: `vital-${vs.vital_sign}`,
|
|
15783
|
+
title: `${VITAL_SIGN_LABELS[vs.vital_sign]}: ${Math.round(vs.score)}/100`,
|
|
15784
|
+
summary: formatDollarImpact(vs.dollar_value, vs.dollar_label),
|
|
15785
|
+
markdown: [
|
|
15786
|
+
`Status: **${vs.status}**`,
|
|
15787
|
+
`Value: **${formatDollarImpact(vs.dollar_value, vs.dollar_label)}**`,
|
|
15788
|
+
`Flagged entities: **${vs.entity_details.length}**`,
|
|
15789
|
+
"",
|
|
15790
|
+
"Components:",
|
|
15791
|
+
"```json",
|
|
15792
|
+
JSON.stringify(vs.components, null, 2),
|
|
15793
|
+
"```",
|
|
15794
|
+
"",
|
|
15795
|
+
"Top entity details:",
|
|
15796
|
+
"```json",
|
|
15797
|
+
JSON.stringify(vs.entity_details.slice(0, 25), null, 2),
|
|
15798
|
+
"```"
|
|
15799
|
+
].join("\n"),
|
|
15800
|
+
metadata: { vital_sign: vs.vital_sign }
|
|
15801
|
+
}))
|
|
15802
|
+
},
|
|
15803
|
+
{
|
|
15804
|
+
id: "findings",
|
|
15805
|
+
title: "Findings and Deep Analysis",
|
|
15806
|
+
summary: `${diagnosis.findings.length} findings`,
|
|
15807
|
+
markdown: diagnosis.findings.length > 0 ? diagnosis.findings.map((finding) => `- **${finding.severity.toUpperCase()}** ${finding.segment}: ${finding.finding}`).join("\n") : "_No findings recorded._",
|
|
15808
|
+
children: diagnosis.findings.map((finding, index) => ({
|
|
15809
|
+
id: `finding-${index + 1}`,
|
|
15810
|
+
title: `${finding.severity.toUpperCase()} \u2014 ${finding.segment}`,
|
|
15811
|
+
summary: finding.dollar_value ? formatCurrency(finding.dollar_value) : void 0,
|
|
15812
|
+
markdown: [
|
|
15813
|
+
finding.finding,
|
|
15814
|
+
"",
|
|
15815
|
+
finding.recommended_plays && finding.recommended_plays.length > 0 ? `Recommended plays: ${finding.recommended_plays.map((play) => `**${play.play_name}**`).join(", ")}` : "Recommended plays: _None recorded._",
|
|
15816
|
+
"",
|
|
15817
|
+
"Scores:",
|
|
15818
|
+
"```json",
|
|
15819
|
+
JSON.stringify(finding.vital_signs, null, 2),
|
|
15820
|
+
"```"
|
|
15821
|
+
].join("\n")
|
|
15822
|
+
}))
|
|
15823
|
+
},
|
|
15824
|
+
{
|
|
15825
|
+
id: "segments",
|
|
15826
|
+
title: "Segments",
|
|
15827
|
+
summary: `${diagnosis.segments.length} segments`,
|
|
15828
|
+
markdown: diagnosis.segments.length > 0 ? diagnosis.segments.map((segment) => `- **${segment.segment.name}:** ${Math.round(segment.result.overall_score)}/100 (${segment.result.overall_status}), gated by ${segment.result.gating_vital_sign}`).join("\n") : "_No segments recorded._"
|
|
15829
|
+
}
|
|
15830
|
+
];
|
|
15831
|
+
}
|
|
15832
|
+
var init_bundle = __esm({
|
|
15833
|
+
"src/repositories/bundle.ts"() {
|
|
15834
|
+
"use strict";
|
|
15835
|
+
init_queries();
|
|
15836
|
+
init_session_analysis();
|
|
15837
|
+
init_schema();
|
|
15838
|
+
init_formatters();
|
|
15839
|
+
init_errors2();
|
|
15840
|
+
init_types2();
|
|
15841
|
+
}
|
|
15842
|
+
});
|
|
15843
|
+
|
|
15844
|
+
// src/repositories/markdown.ts
|
|
15845
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync14 } from "fs";
|
|
15846
|
+
import { basename as basename3, dirname as dirname2, join as join20, resolve as resolve6 } from "path";
|
|
15847
|
+
import { stringify as stringifyYaml2 } from "yaml";
|
|
15848
|
+
function renderMarkdownFiles(pkg) {
|
|
15849
|
+
const bundleJson = JSON.stringify(pkg, null, 2) + "\n";
|
|
15850
|
+
const vitalDetails = renderVitalEvidence(pkg);
|
|
15851
|
+
const findings = renderFindings(pkg);
|
|
15852
|
+
const receipts = JSON.stringify({
|
|
15853
|
+
export_id: pkg.export_id,
|
|
15854
|
+
generated_at: pkg.generated_at,
|
|
15855
|
+
actions: pkg.actions
|
|
15856
|
+
}, null, 2) + "\n";
|
|
15857
|
+
const strategyFiles = pkg.strategies.map((entry) => ({
|
|
15858
|
+
relativePath: `strategies/${safeFilename(entry.strategy.slug || entry.strategy.title)}`,
|
|
15859
|
+
contents: renderStrategy(entry),
|
|
15860
|
+
description: `Strategy: ${entry.strategy.title}`
|
|
15861
|
+
}));
|
|
15862
|
+
return [
|
|
15863
|
+
{
|
|
15864
|
+
relativePath: "index.md",
|
|
15865
|
+
contents: renderIndex(pkg),
|
|
15866
|
+
description: "Repository export index"
|
|
15867
|
+
},
|
|
15868
|
+
{
|
|
15869
|
+
relativePath: "evidence/vital-signs.md",
|
|
15870
|
+
contents: vitalDetails,
|
|
15871
|
+
description: "Detailed vital-sign evidence"
|
|
15872
|
+
},
|
|
15873
|
+
{
|
|
15874
|
+
relativePath: "evidence/findings.md",
|
|
15875
|
+
contents: findings,
|
|
15876
|
+
description: "Findings and recommended plays"
|
|
15877
|
+
},
|
|
15878
|
+
...strategyFiles,
|
|
15879
|
+
{
|
|
15880
|
+
relativePath: "receipts/actions.json",
|
|
15881
|
+
contents: receipts,
|
|
15882
|
+
description: "Action proposal and execution receipts"
|
|
15883
|
+
},
|
|
15884
|
+
{
|
|
15885
|
+
relativePath: "bundle.json",
|
|
15886
|
+
contents: bundleJson,
|
|
15887
|
+
description: "Canonical repository export package"
|
|
15888
|
+
}
|
|
15889
|
+
];
|
|
15890
|
+
}
|
|
15891
|
+
function renderIndex(pkg) {
|
|
15892
|
+
const frontmatter = stringifyYaml2({
|
|
15893
|
+
export_id: pkg.export_id,
|
|
15894
|
+
generated_at: pkg.generated_at,
|
|
15895
|
+
target: pkg.target.kind,
|
|
15896
|
+
overall_score: pkg.summary.overall_score,
|
|
15897
|
+
overall_status: pkg.summary.overall_status,
|
|
15898
|
+
gating_vital_sign: pkg.summary.gating_vital_sign,
|
|
15899
|
+
total_value_at_risk: pkg.summary.total_value_at_risk,
|
|
15900
|
+
tags: ["ntrp", "repository-export", pkg.summary.gating_vital_sign.replace(/_/g, "-")]
|
|
15901
|
+
}).trim();
|
|
15902
|
+
return [
|
|
15903
|
+
"---",
|
|
15904
|
+
frontmatter,
|
|
15905
|
+
"---",
|
|
15906
|
+
"",
|
|
15907
|
+
"# NTRP Repository Export",
|
|
15908
|
+
"",
|
|
15909
|
+
`Generated: ${pkg.generated_at}`,
|
|
15910
|
+
"",
|
|
15911
|
+
`Overall score: **${pkg.summary.overall_score}/100** (${pkg.summary.overall_status})`,
|
|
15912
|
+
`Gating vital sign: **${VITAL_SIGN_LABELS[pkg.summary.gating_vital_sign]}**`,
|
|
15913
|
+
`Total value at risk: **${pkg.summary.total_value_at_risk ? formatCurrency(pkg.summary.total_value_at_risk) : "N/A"}**`,
|
|
15914
|
+
"",
|
|
15915
|
+
"## Sections",
|
|
15916
|
+
"",
|
|
15917
|
+
...pkg.sections.map(renderSection),
|
|
15918
|
+
"## Files",
|
|
15919
|
+
"",
|
|
15920
|
+
"- [[evidence/vital-signs|Vital-sign evidence]]",
|
|
15921
|
+
"- [[evidence/findings|Findings]]",
|
|
15922
|
+
"- `bundle.json`",
|
|
15923
|
+
"- `receipts/actions.json`",
|
|
15924
|
+
""
|
|
15925
|
+
].join("\n");
|
|
15926
|
+
}
|
|
15927
|
+
function renderSection(section) {
|
|
15928
|
+
const childMarkdown = section.children && section.children.length > 0 ? ["", ...section.children.map(renderNestedSection)].join("\n") : "";
|
|
15929
|
+
return [
|
|
15930
|
+
"<details>",
|
|
15931
|
+
`<summary>${escapeSummary(section.title)}${section.summary ? ` \u2014 ${escapeSummary(section.summary)}` : ""}</summary>`,
|
|
15932
|
+
"",
|
|
15933
|
+
section.markdown,
|
|
15934
|
+
childMarkdown,
|
|
15935
|
+
"",
|
|
15936
|
+
"</details>",
|
|
15937
|
+
""
|
|
15938
|
+
].join("\n");
|
|
15939
|
+
}
|
|
15940
|
+
function renderNestedSection(section) {
|
|
15941
|
+
return [
|
|
15942
|
+
"<details>",
|
|
15943
|
+
`<summary>${escapeSummary(section.title)}${section.summary ? ` \u2014 ${escapeSummary(section.summary)}` : ""}</summary>`,
|
|
15944
|
+
"",
|
|
15945
|
+
section.markdown,
|
|
15946
|
+
"",
|
|
15947
|
+
"</details>"
|
|
15948
|
+
].join("\n");
|
|
15949
|
+
}
|
|
15950
|
+
function renderVitalEvidence(pkg) {
|
|
15951
|
+
const lines = ["# Vital-Sign Evidence", ""];
|
|
15952
|
+
for (const vs of pkg.diagnosis.health.vital_signs) {
|
|
15953
|
+
lines.push(`## ${VITAL_SIGN_LABELS[vs.vital_sign]}`);
|
|
15954
|
+
lines.push("");
|
|
15955
|
+
lines.push(`Score: **${Math.round(vs.score)}/100** (${vs.status})`);
|
|
15956
|
+
lines.push(`Value: **${formatDollarImpact(vs.dollar_value, vs.dollar_label)}**`);
|
|
15957
|
+
lines.push(`Flagged entities: **${vs.entity_details.length}**`);
|
|
15958
|
+
lines.push("");
|
|
15959
|
+
lines.push("<details>");
|
|
15960
|
+
lines.push("<summary>Components</summary>");
|
|
15961
|
+
lines.push("");
|
|
15962
|
+
lines.push("```json");
|
|
15963
|
+
lines.push(JSON.stringify(vs.components, null, 2));
|
|
15964
|
+
lines.push("```");
|
|
15965
|
+
lines.push("");
|
|
15966
|
+
lines.push("</details>");
|
|
15967
|
+
lines.push("");
|
|
15968
|
+
lines.push("<details>");
|
|
15969
|
+
lines.push("<summary>Entity details</summary>");
|
|
15970
|
+
lines.push("");
|
|
15971
|
+
lines.push("```json");
|
|
15972
|
+
lines.push(JSON.stringify(vs.entity_details, null, 2));
|
|
15973
|
+
lines.push("```");
|
|
15974
|
+
lines.push("");
|
|
15975
|
+
lines.push("</details>");
|
|
15976
|
+
lines.push("");
|
|
15977
|
+
}
|
|
15978
|
+
return lines.join("\n");
|
|
15979
|
+
}
|
|
15980
|
+
function renderFindings(pkg) {
|
|
15981
|
+
if (pkg.diagnosis.findings.length === 0) return "# Findings\n\n_No findings recorded._\n";
|
|
15982
|
+
const lines = ["# Findings", ""];
|
|
15983
|
+
for (const finding of pkg.diagnosis.findings) {
|
|
15984
|
+
lines.push(`## ${finding.severity.toUpperCase()} \u2014 ${finding.segment}`);
|
|
15985
|
+
lines.push("");
|
|
15986
|
+
lines.push(finding.finding);
|
|
15987
|
+
lines.push("");
|
|
15988
|
+
if (finding.dollar_value) lines.push(`Dollar value: **${formatCurrency(finding.dollar_value)}**`);
|
|
15989
|
+
if (finding.recommended_plays && finding.recommended_plays.length > 0) {
|
|
15990
|
+
lines.push(`Recommended plays: ${finding.recommended_plays.map((play) => `**${play.play_name}**`).join(", ")}`);
|
|
15991
|
+
}
|
|
15992
|
+
lines.push("");
|
|
15993
|
+
}
|
|
15994
|
+
return lines.join("\n");
|
|
15995
|
+
}
|
|
15996
|
+
function renderStrategy(entry) {
|
|
15997
|
+
const { strategy, sources } = entry;
|
|
15998
|
+
const frontmatter = stringifyYaml2({
|
|
15999
|
+
id: strategy.id,
|
|
16000
|
+
slug: strategy.slug,
|
|
16001
|
+
status: strategy.status,
|
|
16002
|
+
priority: strategy.priority,
|
|
16003
|
+
linked_play_ids: strategy.linked_play_ids,
|
|
16004
|
+
source_count: sources.length,
|
|
16005
|
+
updated_at: strategy.updated_at
|
|
16006
|
+
}).trim();
|
|
16007
|
+
return [
|
|
16008
|
+
"---",
|
|
16009
|
+
frontmatter,
|
|
16010
|
+
"---",
|
|
16011
|
+
"",
|
|
16012
|
+
`# ${strategy.title}`,
|
|
16013
|
+
"",
|
|
16014
|
+
`Goal: ${strategy.goal}`,
|
|
16015
|
+
"",
|
|
16016
|
+
`Hypothesis: ${strategy.hypothesis}`,
|
|
16017
|
+
"",
|
|
16018
|
+
`Target segment: ${strategy.target_segment}`,
|
|
16019
|
+
"",
|
|
16020
|
+
"## Recommended Actions",
|
|
16021
|
+
"",
|
|
16022
|
+
strategy.recommended_actions.length > 0 ? strategy.recommended_actions.map((action) => `- ${action}`).join("\n") : "_None specified._",
|
|
16023
|
+
"",
|
|
16024
|
+
"## Source Metadata",
|
|
16025
|
+
"",
|
|
16026
|
+
"```json",
|
|
16027
|
+
JSON.stringify(sources, null, 2),
|
|
16028
|
+
"```",
|
|
16029
|
+
""
|
|
16030
|
+
].join("\n");
|
|
16031
|
+
}
|
|
16032
|
+
function getRootPath(target) {
|
|
16033
|
+
return resolve6(target.directory ?? `ntrp-repository-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`);
|
|
16034
|
+
}
|
|
16035
|
+
function safeFilename(value) {
|
|
16036
|
+
return (basename3(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "strategy") + ".md";
|
|
16037
|
+
}
|
|
16038
|
+
function escapeSummary(value) {
|
|
16039
|
+
return value.replace(/[<>]/g, "");
|
|
16040
|
+
}
|
|
16041
|
+
var markdownRepositoryAdapter;
|
|
16042
|
+
var init_markdown2 = __esm({
|
|
16043
|
+
"src/repositories/markdown.ts"() {
|
|
16044
|
+
"use strict";
|
|
16045
|
+
init_formatters();
|
|
16046
|
+
markdownRepositoryAdapter = {
|
|
16047
|
+
kind: "markdown",
|
|
16048
|
+
describeTarget(target) {
|
|
16049
|
+
return target.directory ? `local markdown folder ${resolve6(target.directory)}` : "local markdown folder";
|
|
16050
|
+
},
|
|
16051
|
+
planWrite(pkg) {
|
|
16052
|
+
const files = renderMarkdownFiles(pkg);
|
|
16053
|
+
return {
|
|
16054
|
+
target: pkg.target,
|
|
16055
|
+
root_path: getRootPath(pkg.target),
|
|
16056
|
+
files: files.map((file) => ({
|
|
16057
|
+
path: file.relativePath,
|
|
16058
|
+
bytes: Buffer.byteLength(file.contents, "utf-8"),
|
|
16059
|
+
description: file.description
|
|
16060
|
+
}))
|
|
16061
|
+
};
|
|
16062
|
+
},
|
|
16063
|
+
write(pkg) {
|
|
16064
|
+
const root = getRootPath(pkg.target);
|
|
16065
|
+
const files = renderMarkdownFiles(pkg);
|
|
16066
|
+
mkdirSync8(root, { recursive: true });
|
|
16067
|
+
const written = [];
|
|
16068
|
+
for (const file of files) {
|
|
16069
|
+
const absolutePath = join20(root, file.relativePath);
|
|
16070
|
+
mkdirSync8(dirname2(absolutePath), { recursive: true });
|
|
16071
|
+
writeFileSync14(absolutePath, file.contents, "utf-8");
|
|
16072
|
+
written.push(absolutePath);
|
|
16073
|
+
}
|
|
16074
|
+
return {
|
|
16075
|
+
mode: "repository_export",
|
|
16076
|
+
target: pkg.target,
|
|
16077
|
+
root_path: root,
|
|
16078
|
+
files_written: written,
|
|
16079
|
+
bundle_id: pkg.export_id,
|
|
16080
|
+
message: `Wrote ${written.length} repository export files to ${root}.`,
|
|
16081
|
+
external_side_effects: false
|
|
16082
|
+
};
|
|
16083
|
+
}
|
|
16084
|
+
};
|
|
16085
|
+
}
|
|
16086
|
+
});
|
|
16087
|
+
|
|
16088
|
+
// src/repositories/adapters.ts
|
|
16089
|
+
function getRepositoryAdapter(kind) {
|
|
16090
|
+
switch (kind) {
|
|
16091
|
+
case "markdown":
|
|
16092
|
+
return markdownRepositoryAdapter;
|
|
16093
|
+
case "notion":
|
|
16094
|
+
case "airtable":
|
|
16095
|
+
case "github":
|
|
16096
|
+
throw new NtrpError("repository_target_planned", `${kind} repository exports are planned but not implemented yet. Use --target markdown for now.`, 2 /* Usage */);
|
|
16097
|
+
}
|
|
16098
|
+
}
|
|
16099
|
+
var init_adapters = __esm({
|
|
16100
|
+
"src/repositories/adapters.ts"() {
|
|
16101
|
+
"use strict";
|
|
16102
|
+
init_markdown2();
|
|
16103
|
+
init_errors2();
|
|
16104
|
+
init_types2();
|
|
16105
|
+
}
|
|
16106
|
+
});
|
|
16107
|
+
|
|
16108
|
+
// src/services/publish.ts
|
|
16109
|
+
async function proposeRepositoryExport(options) {
|
|
16110
|
+
await initSchema();
|
|
16111
|
+
const pkg = await buildPackage(options, "publish propose");
|
|
16112
|
+
const adapter = getRepositoryAdapter(pkg.target.kind);
|
|
16113
|
+
const plan = adapter.planWrite(pkg);
|
|
16114
|
+
const id = await insertActionProposal({
|
|
16115
|
+
handle_title: options.source === "smoke_protocol" ? "Monkey Pelican Export" : "NTRP Repository Export",
|
|
16116
|
+
kind: "repository_export",
|
|
16117
|
+
title: options.source === "smoke_protocol" ? "Monkey Pelican Export" : `Publish NTRP evidence bundle to ${pkg.target.kind}`,
|
|
16118
|
+
summary: `Export diagnosis, findings, strategies, evidence, and action receipts to ${adapter.describeTarget(pkg.target)}.`,
|
|
16119
|
+
permission_class: "execute",
|
|
16120
|
+
status: "pending_approval",
|
|
16121
|
+
target: {
|
|
16122
|
+
connector_id: `${pkg.target.kind}-repository`,
|
|
16123
|
+
connector_type: pkg.target.kind,
|
|
16124
|
+
operation: "write_repository_export"
|
|
16125
|
+
},
|
|
16126
|
+
payload: {
|
|
16127
|
+
repository_export: pkg,
|
|
16128
|
+
write_plan: plan
|
|
16129
|
+
},
|
|
16130
|
+
dry_run: {
|
|
16131
|
+
mode: "dry_run",
|
|
16132
|
+
summary: `Would write ${plan.files.length} file${plan.files.length === 1 ? "" : "s"} to ${plan.root_path}.`,
|
|
16133
|
+
would_execute: false,
|
|
16134
|
+
expected_mutations: plan.files.map((file) => `${file.path} (${file.description})`),
|
|
16135
|
+
risk_notes: [
|
|
16136
|
+
"Requires explicit local approval before writing files.",
|
|
16137
|
+
"Markdown target writes only to the local filesystem.",
|
|
16138
|
+
"Notion and Airtable targets are planned adapter mappings only in this slice."
|
|
16139
|
+
]
|
|
16140
|
+
},
|
|
16141
|
+
source: options.source ?? "publish"
|
|
16142
|
+
});
|
|
16143
|
+
const proposal = await getActionProposal(id);
|
|
16144
|
+
if (!proposal) {
|
|
16145
|
+
throw new NtrpError("publish_proposal_missing", `Publish proposal was not found after insert: ${id}`, 1 /* RuntimeError */);
|
|
16146
|
+
}
|
|
16147
|
+
return { action: "propose", proposal, plan };
|
|
16148
|
+
}
|
|
16149
|
+
async function buildPackage(options, command) {
|
|
16150
|
+
const target = {
|
|
16151
|
+
kind: options.target,
|
|
16152
|
+
directory: options.directory
|
|
16153
|
+
};
|
|
16154
|
+
return buildRepositoryExportPackage({
|
|
16155
|
+
target,
|
|
16156
|
+
command,
|
|
16157
|
+
source: options.source ?? "publish",
|
|
16158
|
+
modelOrFixture: options.modelOrFixture
|
|
16159
|
+
});
|
|
16160
|
+
}
|
|
16161
|
+
var init_publish = __esm({
|
|
16162
|
+
"src/services/publish.ts"() {
|
|
16163
|
+
"use strict";
|
|
16164
|
+
init_queries();
|
|
16165
|
+
init_schema();
|
|
16166
|
+
init_errors2();
|
|
16167
|
+
init_types2();
|
|
16168
|
+
init_bundle();
|
|
16169
|
+
init_adapters();
|
|
16170
|
+
}
|
|
16171
|
+
});
|
|
16172
|
+
|
|
16173
|
+
// src/services/smoke-protocol.ts
|
|
16174
|
+
import { join as join21 } from "path";
|
|
16175
|
+
function isSmokeProtocolTrigger(input) {
|
|
16176
|
+
return normalize(input).includes(SMOKE_TRIGGER_PHRASE);
|
|
16177
|
+
}
|
|
16178
|
+
async function runSmokeProtocol(_input, ctx) {
|
|
16179
|
+
const diagnosis = await runDiagnosis({ findings: false, segments: true });
|
|
16180
|
+
ctx.snapshot.computeResult = {
|
|
16181
|
+
aggregate: diagnosis.health,
|
|
16182
|
+
segments: diagnosis.segments
|
|
16183
|
+
};
|
|
16184
|
+
ctx.snapshot.divergences = diagnosis.divergences;
|
|
16185
|
+
const gatingVital = diagnosis.health.vital_signs.find((vs) => vs.vital_sign === diagnosis.health.gating_vital_sign) ?? diagnosis.health.vital_signs[0];
|
|
16186
|
+
const play = getPlaysForVitalSign(diagnosis.health.gating_vital_sign)[0];
|
|
16187
|
+
const finding = buildSmokeFinding(
|
|
16188
|
+
diagnosis.health.vital_signs,
|
|
16189
|
+
gatingVital,
|
|
16190
|
+
play?.id ?? "review-playbook",
|
|
16191
|
+
play?.name ?? "Review the playbook"
|
|
16192
|
+
);
|
|
16193
|
+
await insertFinding({
|
|
16194
|
+
findings: [finding],
|
|
16195
|
+
model_used: "smoke-protocol-v1",
|
|
16196
|
+
raw_prompt: `Smoke trigger: ${SMOKE_TRIGGER_PHRASE}`
|
|
16197
|
+
});
|
|
16198
|
+
const strategyResult = await addStrategyText(renderSmokeStrategy(finding, play?.name ?? "Review the playbook"), {
|
|
16199
|
+
useAi: false,
|
|
16200
|
+
sourceMetadata: {
|
|
16201
|
+
connector_kind: "smoke_protocol",
|
|
16202
|
+
connector_name: "Monkey Pelican Trigger",
|
|
16203
|
+
sync_mode: "local_fixture",
|
|
16204
|
+
write_back: "approval_required_future",
|
|
16205
|
+
trigger_phrase: SMOKE_TRIGGER_PHRASE
|
|
16206
|
+
}
|
|
16207
|
+
});
|
|
16208
|
+
const proposalResult = await proposeRepositoryExport({
|
|
16209
|
+
target: "markdown",
|
|
16210
|
+
directory: join21(getExportsDir(), "repository-smoke"),
|
|
16211
|
+
source: "smoke_protocol",
|
|
16212
|
+
modelOrFixture: "smoke-protocol-v1"
|
|
16213
|
+
});
|
|
16214
|
+
const answer = renderSmokeAnswer({
|
|
16215
|
+
overallScore: diagnosis.health.overall_score,
|
|
16216
|
+
overallStatus: diagnosis.health.overall_status,
|
|
16217
|
+
gatingVital: diagnosis.health.gating_vital_sign,
|
|
16218
|
+
totalValueAtRisk: diagnosis.health.total_value_at_risk,
|
|
16219
|
+
finding,
|
|
16220
|
+
strategyTitle: strategyResult.strategy.title,
|
|
16221
|
+
strategyPath: strategyResult.library_path
|
|
16222
|
+
});
|
|
16223
|
+
return {
|
|
16224
|
+
answer,
|
|
16225
|
+
finding,
|
|
16226
|
+
strategy: strategyResult.strategy,
|
|
16227
|
+
action_proposal: proposalResult.proposal,
|
|
16228
|
+
health: {
|
|
16229
|
+
overall_score: diagnosis.health.overall_score,
|
|
16230
|
+
overall_status: diagnosis.health.overall_status,
|
|
16231
|
+
gating_vital_sign: diagnosis.health.gating_vital_sign,
|
|
16232
|
+
total_value_at_risk: diagnosis.health.total_value_at_risk ?? null
|
|
16233
|
+
}
|
|
16234
|
+
};
|
|
16235
|
+
}
|
|
16236
|
+
function buildSmokeFinding(vitals, gatingVital, playId, playName) {
|
|
16237
|
+
const vitalSigns = Object.fromEntries(vitals.map((vs) => [vs.vital_sign, Math.round(vs.score)]));
|
|
16238
|
+
const value = gatingVital?.dollar_value ?? null;
|
|
16239
|
+
const valueLabel = gatingVital?.dollar_label ?? "value at risk";
|
|
16240
|
+
const score = Math.round(gatingVital?.score ?? 0);
|
|
16241
|
+
const vital = gatingVital?.vital_sign ?? "freshness";
|
|
16242
|
+
const formattedValue = value === null ? "N/A" : formatCurrency(value);
|
|
16243
|
+
return {
|
|
16244
|
+
severity: gatingVital?.status === "red" ? "critical" : gatingVital?.status === "yellow" ? "warning" : "info",
|
|
16245
|
+
segment: "All Pipeline",
|
|
16246
|
+
finding: `**${formattedValue} ${valueLabel}** is the smoke-test headline. The current gating vital sign is **${vital}** at **${score}/100**, so the placeholder deep analysis would recommend **${playName}** as the next play.`,
|
|
16247
|
+
vital_signs: vitalSigns,
|
|
16248
|
+
entity_count: gatingVital?.entity_details.length ?? 0,
|
|
16249
|
+
recommended_focus: vital,
|
|
16250
|
+
dollar_value: value,
|
|
16251
|
+
recommended_plays: [{
|
|
16252
|
+
play_id: playId,
|
|
16253
|
+
play_name: playName,
|
|
16254
|
+
rationale: "Selected from the current gating vital sign to exercise the diagnosis-to-play smoke workflow."
|
|
16255
|
+
}]
|
|
16256
|
+
};
|
|
16257
|
+
}
|
|
16258
|
+
function renderSmokeStrategy(finding, playName) {
|
|
16259
|
+
return `# Smoke Test: ${playName}
|
|
16260
|
+
|
|
16261
|
+
Goal: Validate the flow from natural-language trigger to diagnosis, deep-analysis-style response, saved strategy, and approval-gated library write-back.
|
|
16262
|
+
|
|
16263
|
+
Target Segment: ${finding.segment}
|
|
16264
|
+
|
|
16265
|
+
Recommended play: ${playName}
|
|
16266
|
+
|
|
16267
|
+
Smoke finding: ${finding.finding.replace(/\*\*/g, "")}
|
|
16268
|
+
`;
|
|
16269
|
+
}
|
|
16270
|
+
function renderSmokeAnswer(input) {
|
|
16271
|
+
const totalValue = input.totalValueAtRisk === null ? "N/A" : formatCurrency(input.totalValueAtRisk);
|
|
16272
|
+
return `### Smoke Protocol Complete
|
|
16273
|
+
|
|
16274
|
+
I treated the monkey/pelican phrase as a local smoke trigger and ran the placeholder protocol without calling an AI model.
|
|
16275
|
+
|
|
16276
|
+
- Diagnosis: overall score **${Math.round(input.overallScore)}/100** (${input.overallStatus}), gated by **${input.gatingVital}**, with **${totalValue}** total value at risk.
|
|
16277
|
+
- Deep analysis fixture: ${input.finding.finding}
|
|
16278
|
+
- Saved play/strategy: **${input.strategyTitle}** at \`${input.strategyPath}\`.
|
|
16279
|
+
- Repository export prepared for an approval-gated Obsidian-compatible markdown bundle.
|
|
16280
|
+
|
|
16281
|
+
---
|
|
16282
|
+
*Next: run \`/actions continue\` to approve the export, then \`/actions continue\` again to write it to the repository.*`;
|
|
16283
|
+
}
|
|
16284
|
+
function normalize(input) {
|
|
16285
|
+
return input.trim().toLowerCase().replace(/\s+/g, " ");
|
|
16286
|
+
}
|
|
16287
|
+
var SMOKE_TRIGGER_PHRASE;
|
|
16288
|
+
var init_smoke_protocol = __esm({
|
|
16289
|
+
"src/services/smoke-protocol.ts"() {
|
|
16290
|
+
"use strict";
|
|
16291
|
+
init_queries();
|
|
16292
|
+
init_diagnosis();
|
|
16293
|
+
init_strategy();
|
|
16294
|
+
init_publish();
|
|
16295
|
+
init_playbook();
|
|
16296
|
+
init_store();
|
|
16297
|
+
init_formatters();
|
|
16298
|
+
SMOKE_TRIGGER_PHRASE = "the monkey is green and riding a pelican";
|
|
16299
|
+
}
|
|
16300
|
+
});
|
|
16301
|
+
|
|
16302
|
+
// src/cli/nl.ts
|
|
16303
|
+
var nl_exports = {};
|
|
16304
|
+
__export(nl_exports, {
|
|
16305
|
+
runNaturalLanguage: () => runNaturalLanguage
|
|
16306
|
+
});
|
|
16307
|
+
import chalk16 from "chalk";
|
|
16308
|
+
async function runNaturalLanguage(input, ctx) {
|
|
16309
|
+
if (isSmokeProtocolTrigger(input)) {
|
|
16310
|
+
recordMessage(ctx, "user", input);
|
|
16311
|
+
console.log();
|
|
16312
|
+
const spinner2 = makeSpinner("Running smoke protocol\u2026");
|
|
16313
|
+
try {
|
|
16314
|
+
const result = await runSmokeProtocol(input, ctx);
|
|
16315
|
+
spinner2.succeed("Smoke protocol complete");
|
|
16316
|
+
printAnswer(result.answer);
|
|
16317
|
+
recordMessage(ctx, "agent", result.answer);
|
|
16318
|
+
console.log();
|
|
16319
|
+
return extractSummary(result.answer);
|
|
16320
|
+
} catch (err) {
|
|
16321
|
+
spinner2.fail("Smoke protocol failed");
|
|
16322
|
+
console.error(" " + chalk16.red(String(err.message ?? err)));
|
|
16323
|
+
console.log();
|
|
16324
|
+
return;
|
|
16325
|
+
}
|
|
16326
|
+
}
|
|
16327
|
+
const phase = resolveConversationPhase(ctx);
|
|
16328
|
+
if (!canUseReplAi(ctx) && (phase === "explore" || phase === "deliver")) {
|
|
16329
|
+
await handleExploreWithoutKey(ctx, input);
|
|
16330
|
+
return;
|
|
16331
|
+
}
|
|
16332
|
+
if (!canUseReplAi(ctx)) {
|
|
16333
|
+
return;
|
|
16334
|
+
}
|
|
16335
|
+
recordMessage(ctx, "user", input);
|
|
16336
|
+
let snapshot = ctx.snapshot.computeResult;
|
|
16337
|
+
if (!snapshot) {
|
|
16338
|
+
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
16339
|
+
const spinner2 = makeSpinner(metricsFirst ? "Loading session context\u2026" : "Computing vital signs\u2026");
|
|
16340
|
+
try {
|
|
16341
|
+
snapshot = await computeFullHealth();
|
|
16342
|
+
ctx.snapshot.computeResult = snapshot;
|
|
16343
|
+
const divInput = snapshot.segments.map((s) => ({
|
|
16344
|
+
segmentId: s.segment.id,
|
|
16345
|
+
segmentName: s.segment.name,
|
|
16346
|
+
result: s.result
|
|
16347
|
+
}));
|
|
16348
|
+
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
16349
|
+
spinner2.succeed(metricsFirst ? "Session context ready" : "Health snapshot ready");
|
|
16350
|
+
} catch (err) {
|
|
16351
|
+
spinner2.fail("Could not compute health snapshot");
|
|
16352
|
+
console.error(" " + chalk16.red(String(err.message ?? err)));
|
|
16353
|
+
console.log(" " + chalk16.dim("Run ") + paint("accent", "/new") + chalk16.dim(" \u2192 pick Demo to load sample data."));
|
|
16354
|
+
console.log();
|
|
16355
|
+
return;
|
|
16356
|
+
}
|
|
16357
|
+
}
|
|
16358
|
+
console.log();
|
|
16359
|
+
const memoryBlock = await buildMemoryBlock(input).catch(() => "");
|
|
16360
|
+
const spinner = makeSpinner("Thinking\u2026");
|
|
16361
|
+
let lastAnswer = "";
|
|
16362
|
+
let rawHistory = [];
|
|
16363
|
+
const toolsUsed = [];
|
|
16364
|
+
setAgentContext(ctx);
|
|
16365
|
+
try {
|
|
16366
|
+
const analysisBlock = buildAnalysisBlock(ctx);
|
|
16367
|
+
const conversationBlock = getConversationPhaseBlock(ctx);
|
|
16368
|
+
const bundle = await loadSessionAnalysisBundle();
|
|
16369
|
+
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
16370
|
+
const responseMode = resolveExploreResponseMode(input, ctx, ctx.conversation.length);
|
|
16371
|
+
for await (const event of agenticFindings(snapshot, ctx.snapshot.divergences, {
|
|
16372
|
+
mode: "fresh",
|
|
16373
|
+
userQuestion: input,
|
|
16374
|
+
sessionContext: ctx.resumedSessionSummary,
|
|
16375
|
+
includeMetrics: true,
|
|
16376
|
+
analysisBlock,
|
|
16377
|
+
conversationBlock,
|
|
16378
|
+
sessionArtifact,
|
|
16379
|
+
responseMode,
|
|
16380
|
+
priorMessages: ctx.conversation,
|
|
16381
|
+
memoryBlock,
|
|
16382
|
+
ctx
|
|
16383
|
+
})) {
|
|
16384
|
+
switch (event.type) {
|
|
16385
|
+
case "tool_call":
|
|
16386
|
+
toolsUsed.push(event.name);
|
|
16387
|
+
spinner.text = `Querying ${event.name}\u2026`;
|
|
16388
|
+
break;
|
|
16389
|
+
case "thinking":
|
|
16390
|
+
spinner.stop();
|
|
16391
|
+
console.log(" " + chalk16.dim.italic(event.text));
|
|
16392
|
+
spinner.start("Thinking\u2026");
|
|
16393
|
+
break;
|
|
16394
|
+
case "answer":
|
|
16395
|
+
spinner.stop();
|
|
16396
|
+
lastAnswer = event.text;
|
|
16397
|
+
printAnswer(event.text);
|
|
16398
|
+
break;
|
|
16399
|
+
case "finding":
|
|
16400
|
+
spinner.stop();
|
|
16401
|
+
printFindingInline(event.finding);
|
|
16402
|
+
break;
|
|
16403
|
+
case "done":
|
|
16404
|
+
spinner.stop();
|
|
16405
|
+
rawHistory = event.conversation_history;
|
|
16406
|
+
break;
|
|
16407
|
+
}
|
|
16408
|
+
}
|
|
16409
|
+
} catch (err) {
|
|
16410
|
+
spinner.fail("Error while investigating");
|
|
16411
|
+
console.error(" " + chalk16.red(String(err.message ?? err)));
|
|
16412
|
+
console.log();
|
|
16413
|
+
return;
|
|
16414
|
+
} finally {
|
|
16415
|
+
setAgentContext(null);
|
|
16416
|
+
}
|
|
16417
|
+
if (rawHistory.length > 0) {
|
|
16418
|
+
ctx.conversation = distillThread(rawHistory);
|
|
16419
|
+
}
|
|
16420
|
+
if (!lastAnswer) {
|
|
16421
|
+
console.log(" " + chalk16.dim("(no answer returned)"));
|
|
16422
|
+
} else {
|
|
16423
|
+
recordMessage(ctx, "agent", lastAnswer);
|
|
16424
|
+
if (ctx.pendingAsk) {
|
|
16425
|
+
const { clearPendingAsk: clearPendingAsk2 } = await Promise.resolve().then(() => (init_pending_ask(), pending_ask_exports));
|
|
16426
|
+
clearPendingAsk2(ctx);
|
|
16427
|
+
} else {
|
|
16428
|
+
saveSessionState(ctx);
|
|
16429
|
+
}
|
|
16430
|
+
creditNlAnswer(ctx, Math.floor(ctx.messages.length / 2));
|
|
16431
|
+
recordAnalysis({ question: input, answer: lastAnswer, tools: toolsUsed, session_id: ctx.sessionId });
|
|
16432
|
+
ctx.lastExchange = { question: input, answer: lastAnswer };
|
|
16433
|
+
}
|
|
16434
|
+
console.log();
|
|
16435
|
+
const { promptQueuedAiStrategist: promptQueuedAiStrategist2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
16436
|
+
promptQueuedAiStrategist2(ctx);
|
|
16437
|
+
return lastAnswer ? extractSummary(lastAnswer) : void 0;
|
|
16438
|
+
}
|
|
16439
|
+
function printAnswer(text) {
|
|
16440
|
+
printMarkdown(text, { indent: 2 });
|
|
16441
|
+
}
|
|
16442
|
+
function extractSummary(text) {
|
|
16443
|
+
const plain = text.replace(/#{1,6}\s+/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^[-*]\s+/gm, "").trim();
|
|
16444
|
+
const sentenceMatch = plain.match(/^(.+?[.!?])(?:\s|$)/);
|
|
16445
|
+
const sentence = sentenceMatch ? sentenceMatch[1] : plain.split("\n")[0];
|
|
16446
|
+
if (sentence.length <= 60) return sentence;
|
|
16447
|
+
const truncated = sentence.slice(0, 60).replace(/\s+\S*$/, "");
|
|
16448
|
+
return truncated + "\u2026";
|
|
16449
|
+
}
|
|
16450
|
+
function printFindingInline(finding) {
|
|
16451
|
+
const sev = finding.severity;
|
|
16452
|
+
console.log();
|
|
16453
|
+
console.log(" " + severityPaint(sev)(`[${sev}]`) + " " + chalk16.bold(finding.segment));
|
|
16454
|
+
printMarkdown(finding.finding, { indent: 2 });
|
|
16455
|
+
const play = finding.recommended_plays?.[0];
|
|
16456
|
+
if (play) console.log(" " + chalk16.dim("\u2192 " + play.play_name + " \u2014 " + play.rationale));
|
|
15116
16457
|
}
|
|
15117
|
-
var
|
|
15118
|
-
"src/
|
|
16458
|
+
var init_nl = __esm({
|
|
16459
|
+
"src/cli/nl.ts"() {
|
|
15119
16460
|
"use strict";
|
|
16461
|
+
init_spinner();
|
|
15120
16462
|
init_context2();
|
|
15121
|
-
|
|
15122
|
-
|
|
16463
|
+
init_phase();
|
|
16464
|
+
init_agent_context();
|
|
16465
|
+
init_orchestrator();
|
|
16466
|
+
init_agentic_loop();
|
|
16467
|
+
init_explore_mode();
|
|
16468
|
+
init_thread();
|
|
16469
|
+
init_store2();
|
|
16470
|
+
init_health_score();
|
|
16471
|
+
init_divergence();
|
|
16472
|
+
init_repl_api();
|
|
16473
|
+
init_theme();
|
|
16474
|
+
init_markdown();
|
|
16475
|
+
init_smoke_protocol();
|
|
16476
|
+
init_session_analysis();
|
|
15123
16477
|
init_time_bank();
|
|
15124
16478
|
}
|
|
15125
16479
|
});
|
|
15126
16480
|
|
|
15127
16481
|
// src/config/demo.ts
|
|
15128
|
-
|
|
16482
|
+
var demo_exports = {};
|
|
16483
|
+
__export(demo_exports, {
|
|
16484
|
+
DEMO_DISABLED_MESSAGE: () => DEMO_DISABLED_MESSAGE,
|
|
16485
|
+
guardDemoEnabled: () => guardDemoEnabled,
|
|
16486
|
+
isDemoEnabled: () => isDemoEnabled,
|
|
16487
|
+
printDemoDisabled: () => printDemoDisabled,
|
|
16488
|
+
setDemoEnabled: () => setDemoEnabled
|
|
16489
|
+
});
|
|
16490
|
+
import chalk17 from "chalk";
|
|
15129
16491
|
function printDemoDisabled() {
|
|
15130
16492
|
console.log();
|
|
15131
|
-
console.log(" " +
|
|
16493
|
+
console.log(" " + chalk17.red(DEMO_DISABLED_MESSAGE));
|
|
15132
16494
|
console.log(
|
|
15133
|
-
" " +
|
|
16495
|
+
" " + chalk17.dim("Re-enable with ") + paint("accent", "/config set demo-enabled true") + chalk17.dim(".")
|
|
15134
16496
|
);
|
|
15135
16497
|
console.log();
|
|
15136
16498
|
}
|
|
@@ -15145,6 +16507,11 @@ function isDemoEnabled() {
|
|
|
15145
16507
|
if (value === false || value === "false") return false;
|
|
15146
16508
|
return true;
|
|
15147
16509
|
}
|
|
16510
|
+
function setDemoEnabled(enabled) {
|
|
16511
|
+
const config = loadConfig();
|
|
16512
|
+
config["demo-enabled"] = enabled;
|
|
16513
|
+
saveConfig(config);
|
|
16514
|
+
}
|
|
15148
16515
|
var DEMO_DISABLED_MESSAGE;
|
|
15149
16516
|
var init_demo = __esm({
|
|
15150
16517
|
"src/config/demo.ts"() {
|
|
@@ -15155,6 +16522,225 @@ var init_demo = __esm({
|
|
|
15155
16522
|
}
|
|
15156
16523
|
});
|
|
15157
16524
|
|
|
16525
|
+
// src/conversation/pending-ask.ts
|
|
16526
|
+
var pending_ask_exports = {};
|
|
16527
|
+
__export(pending_ask_exports, {
|
|
16528
|
+
cancelPendingAskNotice: () => cancelPendingAskNotice,
|
|
16529
|
+
clearPendingAsk: () => clearPendingAsk,
|
|
16530
|
+
isClearAutoConfirmIntent: () => isClearAutoConfirmIntent,
|
|
16531
|
+
looksLikeQuestion: () => looksLikeQuestion,
|
|
16532
|
+
offerDemoToAnswer: () => offerDemoToAnswer,
|
|
16533
|
+
printFocusChip: () => printFocusChip,
|
|
16534
|
+
queuePendingAsk: () => queuePendingAsk,
|
|
16535
|
+
resumePendingAsk: () => resumePendingAsk
|
|
16536
|
+
});
|
|
16537
|
+
import chalk18 from "chalk";
|
|
16538
|
+
function looksLikeQuestion(input) {
|
|
16539
|
+
const text = input.trim();
|
|
16540
|
+
if (!text) return false;
|
|
16541
|
+
if (/\?\s*$/.test(text)) return true;
|
|
16542
|
+
if (/^(what|why|how|which|where|who|is|are|can|should|do|does|did|will|would|could)\b/i.test(text)) {
|
|
16543
|
+
return true;
|
|
16544
|
+
}
|
|
16545
|
+
return /\b(most expensive|biggest risk|what should (i|we)|fix first|plan of attack)\b/i.test(text);
|
|
16546
|
+
}
|
|
16547
|
+
function isClearAutoConfirmIntent(input) {
|
|
16548
|
+
const text = input.trim();
|
|
16549
|
+
return /\b(most expensive|biggest risk|expensive problem|what should (i|we) fix|fix first|plan of attack)\b/i.test(text) || /\b(pipeline health|stuck deals|handoff leak|stale pipeline|vital signs?)\b/i.test(text) || /\b(is (our |my )?retention real|nrr real|board.*(retention|nrr))\b/i.test(text);
|
|
16550
|
+
}
|
|
16551
|
+
function queuePendingAsk(ctx, text, origin) {
|
|
16552
|
+
const trimmed = text.trim();
|
|
16553
|
+
if (!trimmed) return;
|
|
16554
|
+
ctx.pendingAsk = {
|
|
16555
|
+
text: trimmed,
|
|
16556
|
+
queued_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16557
|
+
origin,
|
|
16558
|
+
keylessAnswered: ctx.pendingAsk?.keylessAnswered
|
|
16559
|
+
};
|
|
16560
|
+
saveSessionState(ctx);
|
|
16561
|
+
}
|
|
16562
|
+
function clearPendingAsk(ctx) {
|
|
16563
|
+
if (!ctx.pendingAsk) return;
|
|
16564
|
+
ctx.pendingAsk = void 0;
|
|
16565
|
+
saveSessionState(ctx);
|
|
16566
|
+
}
|
|
16567
|
+
function printFocusChip(ctx) {
|
|
16568
|
+
if (!ctx.scope) return;
|
|
16569
|
+
const lens = ctx.scope.primary_lens === "revenue_metrics" ? "SaaS metrics" : "pipeline health";
|
|
16570
|
+
const period = ctx.scope.time_horizon ? ` \xB7 ${ctx.scope.time_horizon}` : "";
|
|
16571
|
+
console.log();
|
|
16572
|
+
console.log(
|
|
16573
|
+
" " + chalk18.dim("Focus: ") + paint("accent", lens) + chalk18.dim(period) + chalk18.dim(" \u2014 type ") + chalk18.cyan("adjust") + chalk18.dim(" to change")
|
|
16574
|
+
);
|
|
16575
|
+
console.log();
|
|
16576
|
+
}
|
|
16577
|
+
async function resumePendingAsk(ctx) {
|
|
16578
|
+
const pending = ctx.pendingAsk;
|
|
16579
|
+
if (!pending?.text) return false;
|
|
16580
|
+
ctx.computeInProgress = false;
|
|
16581
|
+
if (canUseReplAi(ctx)) {
|
|
16582
|
+
console.log();
|
|
16583
|
+
console.log(
|
|
16584
|
+
" " + chalk18.dim(
|
|
16585
|
+
pending.keylessAnswered ? "Picking up your question with the connected engine\u2026" : "Picking up your question\u2026"
|
|
16586
|
+
)
|
|
16587
|
+
);
|
|
16588
|
+
console.log();
|
|
16589
|
+
const { runNaturalLanguage: runNaturalLanguage2 } = await Promise.resolve().then(() => (init_nl(), nl_exports));
|
|
16590
|
+
await runNaturalLanguage2(pending.text, ctx);
|
|
16591
|
+
clearPendingAsk(ctx);
|
|
16592
|
+
return true;
|
|
16593
|
+
}
|
|
16594
|
+
const { tryKeylessAskAnswer: tryKeylessAskAnswer2 } = await Promise.resolve().then(() => (init_keyless_ask(), keyless_ask_exports));
|
|
16595
|
+
const answered = await tryKeylessAskAnswer2(ctx, pending.text, { fromResume: true });
|
|
16596
|
+
if (answered) {
|
|
16597
|
+
ctx.pendingAsk = { ...pending, keylessAnswered: true };
|
|
16598
|
+
saveSessionState(ctx);
|
|
16599
|
+
return true;
|
|
16600
|
+
}
|
|
16601
|
+
return false;
|
|
16602
|
+
}
|
|
16603
|
+
async function offerDemoToAnswer(ctx) {
|
|
16604
|
+
if (!ctx.pendingAsk || !ctx.rl) return false;
|
|
16605
|
+
const { guardDemoEnabled: guardDemoEnabled2, isDemoEnabled: isDemoEnabled2 } = await Promise.resolve().then(() => (init_demo(), demo_exports));
|
|
16606
|
+
if (!isDemoEnabled2()) return false;
|
|
16607
|
+
const { sessionHasData: sessionHasData2 } = await Promise.resolve().then(() => (init_phase(), phase_exports));
|
|
16608
|
+
if (sessionHasData2(ctx)) return false;
|
|
16609
|
+
const { createPromptSession: createPromptSession2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
|
|
16610
|
+
const { loadDemoFromChat: loadDemoFromChat2 } = await Promise.resolve().then(() => (init_ingest_chat(), ingest_chat_exports));
|
|
16611
|
+
const prompts = createPromptSession2(ctx.rl, ctx);
|
|
16612
|
+
try {
|
|
16613
|
+
console.log();
|
|
16614
|
+
const go = await prompts.confirm("Use demo data to answer this?", true);
|
|
16615
|
+
if (!go) {
|
|
16616
|
+
console.log(
|
|
16617
|
+
" " + chalk18.dim("Paste a CSV path when ready, or say ") + chalk18.cyan("use demo data") + chalk18.dim(".")
|
|
16618
|
+
);
|
|
16619
|
+
console.log();
|
|
16620
|
+
return false;
|
|
16621
|
+
}
|
|
16622
|
+
} finally {
|
|
16623
|
+
prompts.close();
|
|
16624
|
+
}
|
|
16625
|
+
if (!guardDemoEnabled2()) return false;
|
|
16626
|
+
await loadDemoFromChat2(ctx, void 0, { autoCompute: true });
|
|
16627
|
+
return true;
|
|
16628
|
+
}
|
|
16629
|
+
function cancelPendingAskNotice(ctx) {
|
|
16630
|
+
if (!ctx.pendingAsk) return;
|
|
16631
|
+
recordMessage(ctx, "agent", "Cleared queued question.");
|
|
16632
|
+
clearPendingAsk(ctx);
|
|
16633
|
+
}
|
|
16634
|
+
var init_pending_ask = __esm({
|
|
16635
|
+
"src/conversation/pending-ask.ts"() {
|
|
16636
|
+
"use strict";
|
|
16637
|
+
init_context2();
|
|
16638
|
+
init_repl_api();
|
|
16639
|
+
init_theme();
|
|
16640
|
+
}
|
|
16641
|
+
});
|
|
16642
|
+
|
|
16643
|
+
// src/conversation/compute.ts
|
|
16644
|
+
var compute_exports2 = {};
|
|
16645
|
+
__export(compute_exports2, {
|
|
16646
|
+
isComputeIntent: () => isComputeIntent,
|
|
16647
|
+
runConversationCompute: () => runConversationCompute
|
|
16648
|
+
});
|
|
16649
|
+
import chalk19 from "chalk";
|
|
16650
|
+
async function runConversationCompute(ctx) {
|
|
16651
|
+
const lens = ctx.scope?.primary_lens ?? ctx.analysis.primary;
|
|
16652
|
+
ctx.computeInProgress = true;
|
|
16653
|
+
const willAnswer = Boolean(ctx.pendingAsk?.text) && !ctx.strategistState;
|
|
16654
|
+
try {
|
|
16655
|
+
if (lens === "revenue_metrics") {
|
|
16656
|
+
const { runMetricsAnalysis: runMetricsAnalysis2, extractHeadlineMetrics: extractHeadlineMetrics2 } = await Promise.resolve().then(() => (init_metrics_analysis(), metrics_analysis_exports));
|
|
16657
|
+
const { renderMetricsReport: renderMetricsReport2 } = await Promise.resolve().then(() => (init_metrics_report(), metrics_report_exports));
|
|
16658
|
+
const spinner = makeSpinner("Computing SaaS metrics\u2026");
|
|
16659
|
+
try {
|
|
16660
|
+
const result = await runMetricsAnalysis2({
|
|
16661
|
+
findings: false,
|
|
16662
|
+
sessionAnalysis: ctx.analysis
|
|
16663
|
+
});
|
|
16664
|
+
spinner.succeed("SaaS metrics computed");
|
|
16665
|
+
ctx.analysis.coverage = result.coverage;
|
|
16666
|
+
ctx.analysis.data_source_type = result.data_source_type;
|
|
16667
|
+
ctx.analysis.recommended = result.companion_recommendation;
|
|
16668
|
+
ctx.analysis.headline = extractHeadlineMetrics2(result.metrics.aggregate.metrics);
|
|
16669
|
+
markLensCompleted(ctx, "revenue_metrics");
|
|
16670
|
+
ctx.stage = "analyzed";
|
|
16671
|
+
ctx.snapshot.computeResult = null;
|
|
16672
|
+
invalidateGapAudit(ctx);
|
|
16673
|
+
saveSessionState(ctx);
|
|
16674
|
+
renderMetricsReport2(ctx, result.metrics.aggregate.metrics, result.coverage, result.data_source_type, {
|
|
16675
|
+
snapshot: result.snapshot,
|
|
16676
|
+
findings: result.findings,
|
|
16677
|
+
companion: result.companion_recommendation ?? null,
|
|
16678
|
+
interactive: !willAnswer
|
|
16679
|
+
});
|
|
16680
|
+
await resumeQueuedStrategist(ctx);
|
|
16681
|
+
await closeComputeTurn(ctx, willAnswer, "revenue_metrics");
|
|
16682
|
+
creditGapCompute(ctx);
|
|
16683
|
+
creditMetricsComplete(ctx, false);
|
|
16684
|
+
return "SaaS metrics ready";
|
|
16685
|
+
} catch (err) {
|
|
16686
|
+
spinner.fail("Metrics failed");
|
|
16687
|
+
throw err;
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
const { handler: diagnose } = await Promise.resolve().then(() => (init_diagnose(), diagnose_exports));
|
|
16691
|
+
ctx.skipTimeBankDiagnoseCredit = true;
|
|
16692
|
+
ctx.suppressCompanionFooter = willAnswer;
|
|
16693
|
+
const summary = await diagnose(["--compact"], ctx);
|
|
16694
|
+
markLensCompleted(ctx, "gtm_health");
|
|
16695
|
+
ctx.stage = "analyzed";
|
|
16696
|
+
ctx.snapshot.computeResult = null;
|
|
16697
|
+
invalidateGapAudit(ctx);
|
|
16698
|
+
saveSessionState(ctx);
|
|
16699
|
+
await resumeQueuedStrategist(ctx);
|
|
16700
|
+
await closeComputeTurn(ctx, willAnswer, "gtm_health");
|
|
16701
|
+
creditGapCompute(ctx);
|
|
16702
|
+
return typeof summary === "string" ? summary : "Health analysis ready";
|
|
16703
|
+
} catch (err) {
|
|
16704
|
+
console.error(" " + chalk19.red(String(err.message ?? err)));
|
|
16705
|
+
return;
|
|
16706
|
+
} finally {
|
|
16707
|
+
ctx.computeInProgress = false;
|
|
16708
|
+
ctx.deliverIntent = false;
|
|
16709
|
+
}
|
|
16710
|
+
}
|
|
16711
|
+
async function resumeQueuedStrategist(ctx) {
|
|
16712
|
+
if (ctx.strategistState?.step !== "awaiting_analysis") return;
|
|
16713
|
+
ctx.computeInProgress = false;
|
|
16714
|
+
const { resumeStrategistAfterCompute: resumeStrategistAfterCompute2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
16715
|
+
await resumeStrategistAfterCompute2(ctx);
|
|
16716
|
+
}
|
|
16717
|
+
async function resumePendingAskAfterCompute(ctx) {
|
|
16718
|
+
if (!ctx.pendingAsk?.text) return false;
|
|
16719
|
+
if (ctx.strategistState) return false;
|
|
16720
|
+
const { resumePendingAsk: resumePendingAsk2 } = await Promise.resolve().then(() => (init_pending_ask(), pending_ask_exports));
|
|
16721
|
+
return resumePendingAsk2(ctx);
|
|
16722
|
+
}
|
|
16723
|
+
async function closeComputeTurn(ctx, suppressedFooter, lens) {
|
|
16724
|
+
const answered = await resumePendingAskAfterCompute(ctx);
|
|
16725
|
+
if (suppressedFooter && !answered) {
|
|
16726
|
+
const companion = await resolveCompanionRecommendation(ctx);
|
|
16727
|
+
printCompanionFooter(ctx, companion, { justCompleted: lens });
|
|
16728
|
+
}
|
|
16729
|
+
}
|
|
16730
|
+
function isComputeIntent(input) {
|
|
16731
|
+
return /\b(run analysis|compute|go ahead|analyze|let'?s go|do it)\b/i.test(input.trim());
|
|
16732
|
+
}
|
|
16733
|
+
var init_compute2 = __esm({
|
|
16734
|
+
"src/conversation/compute.ts"() {
|
|
16735
|
+
"use strict";
|
|
16736
|
+
init_spinner();
|
|
16737
|
+
init_context2();
|
|
16738
|
+
init_gap_audit();
|
|
16739
|
+
init_companion();
|
|
16740
|
+
init_time_bank();
|
|
16741
|
+
}
|
|
16742
|
+
});
|
|
16743
|
+
|
|
15158
16744
|
// src/pipeline/csv-parse.ts
|
|
15159
16745
|
var csv_parse_exports = {};
|
|
15160
16746
|
__export(csv_parse_exports, {
|
|
@@ -15707,6 +17293,7 @@ function blendScenarios(_research) {
|
|
|
15707
17293
|
label: "Research-Derived Blend",
|
|
15708
17294
|
description: "Realistic data with mild-to-moderate problems across all vital signs.",
|
|
15709
17295
|
story: "Generated with default research blend. Problems are seeded across all vital signs at realistic levels.",
|
|
17296
|
+
hook: "Mild-to-moderate problems seeded across all five vitals.",
|
|
15710
17297
|
// Bump all problems slightly above baseline for discoverability
|
|
15711
17298
|
staleContactRatio: 0.2,
|
|
15712
17299
|
pastCloseDateRatio: 0.15,
|
|
@@ -15751,6 +17338,7 @@ var init_scenarios = __esm({
|
|
|
15751
17338
|
label: "The Hidden Crisis",
|
|
15752
17339
|
description: "Overall health looks yellow but Enterprise is deep red, masked by strong SMB numbers.",
|
|
15753
17340
|
story: "Your aggregate numbers look okay \u2014 but when you break it by segment, Enterprise is dying. 60% of enterprise contacts have gone dark, deals are single-threaded, and SMB is carrying the average.",
|
|
17341
|
+
hook: "SMB is carrying the average while Enterprise dies quietly.",
|
|
15754
17342
|
staleContactRatio: 0.3,
|
|
15755
17343
|
staleContactRatioEnterprise: 0.6,
|
|
15756
17344
|
staleContactRatioSmb: 0.1,
|
|
@@ -15765,6 +17353,7 @@ var init_scenarios = __esm({
|
|
|
15765
17353
|
label: "The Leaky Bucket",
|
|
15766
17354
|
description: "Marketing generates plenty of leads but 40% vanish at handoff to sales.",
|
|
15767
17355
|
story: "Marketing is doing its job \u2014 MQLs are flowing. But 40% of qualified leads never show up in sales workflows. They're falling through the cracks at handoff, and nobody's noticing because marketing reports MQL count and sales reports pipeline value.",
|
|
17356
|
+
hook: "MQLs flow in, then 40% vanish at the sales handoff.",
|
|
15768
17357
|
mqlDropRatio: 0.4,
|
|
15769
17358
|
qualifiedNoOutreachRatio: 0.35,
|
|
15770
17359
|
staleContactRatio: 0.2
|
|
@@ -15775,6 +17364,7 @@ var init_scenarios = __esm({
|
|
|
15775
17364
|
label: "The Stale Pipeline",
|
|
15776
17365
|
description: "Big pipeline number but half the deals are zombies stuck in late stages.",
|
|
15777
17366
|
story: "The pipeline report says $5M. But look closer: half those deals have close dates in the past, 40% are stuck in Negotiation for 120+ days, and nobody's touching them. You're forecasting on fiction.",
|
|
17367
|
+
hook: "Half the pipeline is zombies \u2014 you're forecasting on fiction.",
|
|
15778
17368
|
pastCloseDateRatio: 0.5,
|
|
15779
17369
|
stuckDealRatio: 0.4,
|
|
15780
17370
|
stuckInNegotiationDays: 120,
|
|
@@ -15787,6 +17377,7 @@ var init_scenarios = __esm({
|
|
|
15787
17377
|
label: "The Lone Wolf",
|
|
15788
17378
|
description: "One rep has great numbers but every single deal is single-threaded.",
|
|
15789
17379
|
story: "Your top rep is crushing it on paper \u2014 biggest pipeline, highest close rate. But every deal has exactly one contact. One champion goes on vacation, gets promoted, or leaves, and the entire pipeline collapses.",
|
|
17380
|
+
hook: "Top rep, huge pipeline, one contact per deal \u2014 one exit from collapse.",
|
|
15790
17381
|
loneWolfRepIndex: 0,
|
|
15791
17382
|
loneWolfSingleThreadRatio: 1,
|
|
15792
17383
|
singleThreadRatio: 0.15
|
|
@@ -15797,6 +17388,7 @@ var init_scenarios = __esm({
|
|
|
15797
17388
|
label: "The Busy Bees",
|
|
15798
17389
|
description: "High activity volume across the team, but most of it hits dead ends.",
|
|
15799
17390
|
story: "Your team is busy. Activity metrics look great \u2014 calls are up, emails are up, meetings are up. But 60% of that activity is aimed at contacts with no associated pipeline. Reps are spraying, not aiming.",
|
|
17391
|
+
hook: "Reps are spraying, not aiming.",
|
|
15800
17392
|
activityVolumeMultiplier: 3,
|
|
15801
17393
|
noiseActivityRatio: 0.6,
|
|
15802
17394
|
staleContactRatio: 0.2
|
|
@@ -17878,12 +19470,12 @@ var init_generator = __esm({
|
|
|
17878
19470
|
});
|
|
17879
19471
|
|
|
17880
19472
|
// src/demo/taxonomy-cache.ts
|
|
17881
|
-
import { readFileSync as readFileSync17, writeFileSync as
|
|
19473
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync15, existsSync as existsSync18, mkdirSync as mkdirSync9, unlinkSync as unlinkSync3 } from "fs";
|
|
17882
19474
|
import { homedir as homedir5 } from "os";
|
|
17883
|
-
import { join as
|
|
19475
|
+
import { join as join22 } from "path";
|
|
17884
19476
|
function ensureDir5() {
|
|
17885
19477
|
if (!existsSync18(NTRP_DIR4)) {
|
|
17886
|
-
|
|
19478
|
+
mkdirSync9(NTRP_DIR4, { recursive: true });
|
|
17887
19479
|
}
|
|
17888
19480
|
}
|
|
17889
19481
|
function loadCachedTaxonomy(profile) {
|
|
@@ -17899,14 +19491,14 @@ function loadCachedTaxonomy(profile) {
|
|
|
17899
19491
|
}
|
|
17900
19492
|
function saveCachedTaxonomy(taxonomy) {
|
|
17901
19493
|
ensureDir5();
|
|
17902
|
-
|
|
19494
|
+
writeFileSync15(TAXONOMY_PATH, JSON.stringify(taxonomy, null, 2) + "\n");
|
|
17903
19495
|
}
|
|
17904
19496
|
var NTRP_DIR4, TAXONOMY_PATH;
|
|
17905
19497
|
var init_taxonomy_cache = __esm({
|
|
17906
19498
|
"src/demo/taxonomy-cache.ts"() {
|
|
17907
19499
|
"use strict";
|
|
17908
|
-
NTRP_DIR4 =
|
|
17909
|
-
TAXONOMY_PATH =
|
|
19500
|
+
NTRP_DIR4 = join22(homedir5(), ".ntrp");
|
|
19501
|
+
TAXONOMY_PATH = join22(NTRP_DIR4, "demo-taxonomy.json");
|
|
17910
19502
|
}
|
|
17911
19503
|
});
|
|
17912
19504
|
|
|
@@ -18142,16 +19734,16 @@ var generate_exports = {};
|
|
|
18142
19734
|
__export(generate_exports, {
|
|
18143
19735
|
handler: () => handler2
|
|
18144
19736
|
});
|
|
18145
|
-
import
|
|
18146
|
-
import ora5 from "ora";
|
|
19737
|
+
import chalk20 from "chalk";
|
|
18147
19738
|
async function handler2(args, ctx) {
|
|
18148
|
-
const { flags } = parseArgs(args, ["list-scenarios", "regen-taxonomy"]);
|
|
19739
|
+
const { flags } = parseArgs(args, ["list-scenarios", "regen-taxonomy", "brief"]);
|
|
18149
19740
|
const quiet = ctx.execution.quiet;
|
|
19741
|
+
const brief = getBool(flags, "brief");
|
|
18150
19742
|
if (getBool(flags, "list-scenarios")) {
|
|
18151
|
-
console.log(
|
|
19743
|
+
console.log(chalk20.bold("\n Available Scenarios:\n"));
|
|
18152
19744
|
for (const s of SCENARIO_LIST) {
|
|
18153
|
-
console.log(` ${
|
|
18154
|
-
console.log(` ${
|
|
19745
|
+
console.log(` ${chalk20.cyan(s.key.padEnd(20))} ${s.label}`);
|
|
19746
|
+
console.log(` ${chalk20.dim(" ".repeat(20))} ${s.description}
|
|
18155
19747
|
`);
|
|
18156
19748
|
}
|
|
18157
19749
|
return true;
|
|
@@ -18161,9 +19753,9 @@ async function handler2(args, ctx) {
|
|
|
18161
19753
|
const skipProfile = getFalse(flags, "profile");
|
|
18162
19754
|
if (!isProfileConfigured(profile) && !skipProfile) {
|
|
18163
19755
|
console.error();
|
|
18164
|
-
console.error(" " +
|
|
18165
|
-
console.error(" " +
|
|
18166
|
-
console.error(" " +
|
|
19756
|
+
console.error(" " + chalk20.red("No company profile found."));
|
|
19757
|
+
console.error(" " + chalk20.dim("Run ") + paint("accent", "/onboard") + chalk20.dim(" first for a richer demo,"));
|
|
19758
|
+
console.error(" " + chalk20.dim("or pass ") + paint("accent", "--no-profile") + chalk20.dim(" to skip."));
|
|
18167
19759
|
console.error();
|
|
18168
19760
|
markFailure(ctx);
|
|
18169
19761
|
return false;
|
|
@@ -18171,8 +19763,8 @@ async function handler2(args, ctx) {
|
|
|
18171
19763
|
const explicitScenario = getString(flags, "scenario", "s");
|
|
18172
19764
|
const resolvedScenario = resolveScenarioInput(explicitScenario);
|
|
18173
19765
|
if (resolvedScenario === null) {
|
|
18174
|
-
console.error(
|
|
18175
|
-
console.log(
|
|
19766
|
+
console.error(chalk20.red(` Unknown scenario: ${explicitScenario}`));
|
|
19767
|
+
console.log(chalk20.dim(` Valid: ${SCENARIO_LIST.map((s) => s.key).join(", ")}`));
|
|
18176
19768
|
markFailure(ctx);
|
|
18177
19769
|
return false;
|
|
18178
19770
|
}
|
|
@@ -18185,11 +19777,15 @@ async function handler2(args, ctx) {
|
|
|
18185
19777
|
if (!explicitScenario && !quiet) {
|
|
18186
19778
|
const s = getScenario(scenario);
|
|
18187
19779
|
console.log();
|
|
18188
|
-
|
|
18189
|
-
|
|
18190
|
-
|
|
19780
|
+
if (brief) {
|
|
19781
|
+
console.log(" " + paint("accent", "\u2713 Demo: ") + bold(s.label) + chalk20.dim(" \u2014 " + s.hook));
|
|
19782
|
+
} else {
|
|
19783
|
+
console.log(" " + paint("accent", "\u2713 Scenario: ") + bold(s.label));
|
|
19784
|
+
console.log(" " + chalk20.dim(s.story));
|
|
19785
|
+
console.log();
|
|
19786
|
+
}
|
|
18191
19787
|
}
|
|
18192
|
-
const spinner = quiet ? null :
|
|
19788
|
+
const spinner = quiet ? null : makeSpinner("Initializing database\u2026");
|
|
18193
19789
|
try {
|
|
18194
19790
|
await initSchema();
|
|
18195
19791
|
const metricsLens = ctx.analysis.primary === "revenue_metrics";
|
|
@@ -18212,17 +19808,21 @@ async function handler2(args, ctx) {
|
|
|
18212
19808
|
const result = await generateDemoData(config);
|
|
18213
19809
|
if (result.mode === "direct") {
|
|
18214
19810
|
if (spinner) {
|
|
18215
|
-
|
|
18216
|
-
|
|
18217
|
-
|
|
19811
|
+
if (brief) {
|
|
19812
|
+
spinner.succeed(`Demo loaded \u2014 ${briefCounts(result.counts)}`);
|
|
19813
|
+
} else {
|
|
19814
|
+
spinner.succeed(`Generated demo data for "${chalk20.cyan(scenario)}" scenario`);
|
|
19815
|
+
console.log();
|
|
19816
|
+
printEntityCounts(result.counts);
|
|
19817
|
+
}
|
|
18218
19818
|
}
|
|
18219
|
-
if (!quiet && ctx.analysis.primary !== "revenue_metrics") {
|
|
18220
|
-
console.log(
|
|
19819
|
+
if (!quiet && !brief && ctx.analysis.primary !== "revenue_metrics") {
|
|
19820
|
+
console.log(chalk20.dim("\n Run /diagnose to compute vital signs.\n"));
|
|
18221
19821
|
}
|
|
18222
19822
|
}
|
|
18223
19823
|
} catch (err) {
|
|
18224
19824
|
if (spinner) spinner.fail("Generation failed");
|
|
18225
|
-
console.error(
|
|
19825
|
+
console.error(chalk20.red(String(err)));
|
|
18226
19826
|
markFailure(ctx);
|
|
18227
19827
|
return false;
|
|
18228
19828
|
}
|
|
@@ -18233,13 +19833,26 @@ function markFailure(ctx) {
|
|
|
18233
19833
|
process.exitCode = 1;
|
|
18234
19834
|
}
|
|
18235
19835
|
}
|
|
19836
|
+
function briefCounts(counts) {
|
|
19837
|
+
const fmt = (n) => n >= 1e4 ? `${(n / 1e3).toFixed(1)}K` : n.toLocaleString("en-US");
|
|
19838
|
+
const parts = [];
|
|
19839
|
+
const take = (key, label) => {
|
|
19840
|
+
const n = counts[key];
|
|
19841
|
+
if (n && n > 0) parts.push(`${fmt(n)} ${label}`);
|
|
19842
|
+
};
|
|
19843
|
+
take("organizations", "accounts");
|
|
19844
|
+
take("people", "contacts");
|
|
19845
|
+
take("opportunities", "deals");
|
|
19846
|
+
take("activities", "activities");
|
|
19847
|
+
return parts.join(" \xB7 ");
|
|
19848
|
+
}
|
|
18236
19849
|
async function loadOrBuildTaxonomy(profile, forceRegen, ctx) {
|
|
18237
19850
|
if (!forceRegen) {
|
|
18238
19851
|
const cached2 = loadCachedTaxonomy(profile);
|
|
18239
19852
|
if (cached2) return cached2;
|
|
18240
19853
|
}
|
|
18241
19854
|
const spinnerText = forceRegen ? "Rebuilding market taxonomy\u2026" : "Researching your market taxonomy\u2026";
|
|
18242
|
-
const spinner =
|
|
19855
|
+
const spinner = makeSpinner(spinnerText);
|
|
18243
19856
|
try {
|
|
18244
19857
|
const taxonomy = await buildDemoTaxonomy(profile, ctx);
|
|
18245
19858
|
saveCachedTaxonomy(taxonomy);
|
|
@@ -18247,13 +19860,14 @@ async function loadOrBuildTaxonomy(profile, forceRegen, ctx) {
|
|
|
18247
19860
|
return taxonomy;
|
|
18248
19861
|
} catch (err) {
|
|
18249
19862
|
spinner.fail("Couldn't build market taxonomy \u2014 using generic data pools");
|
|
18250
|
-
console.log(" " +
|
|
19863
|
+
console.log(" " + chalk20.dim(String(err.message ?? err)));
|
|
18251
19864
|
return void 0;
|
|
18252
19865
|
}
|
|
18253
19866
|
}
|
|
18254
19867
|
var init_generate = __esm({
|
|
18255
19868
|
"src/commands/generate.ts"() {
|
|
18256
19869
|
"use strict";
|
|
19870
|
+
init_spinner();
|
|
18257
19871
|
init_schema();
|
|
18258
19872
|
init_generator();
|
|
18259
19873
|
init_scenarios();
|
|
@@ -18337,10 +19951,9 @@ var ingest_exports = {};
|
|
|
18337
19951
|
__export(ingest_exports, {
|
|
18338
19952
|
handler: () => handler3
|
|
18339
19953
|
});
|
|
18340
|
-
import
|
|
18341
|
-
import ora6 from "ora";
|
|
19954
|
+
import chalk21 from "chalk";
|
|
18342
19955
|
import { readFileSync as readFileSync18, existsSync as existsSync19 } from "fs";
|
|
18343
|
-
import { basename as
|
|
19956
|
+
import { basename as basename4 } from "path";
|
|
18344
19957
|
async function handler3(args, ctx) {
|
|
18345
19958
|
const { positional, flags } = parseArgs(args, [
|
|
18346
19959
|
"skip-resolve",
|
|
@@ -18359,28 +19972,28 @@ async function handler3(args, ctx) {
|
|
|
18359
19972
|
const source = getString(flags, "source", "s") ?? "salesforce";
|
|
18360
19973
|
const skipResolve = getBool(flags, "skip-resolve");
|
|
18361
19974
|
if (!file) {
|
|
18362
|
-
console.error(
|
|
18363
|
-
console.error(
|
|
19975
|
+
console.error(chalk21.red(" Usage: /ingest <file> [--source salesforce|hubspot|outreach]"));
|
|
19976
|
+
console.error(chalk21.dim(" /ingest --demo [--scenario <name>]"));
|
|
18364
19977
|
process.exit(1);
|
|
18365
19978
|
}
|
|
18366
19979
|
if (!existsSync19(file)) {
|
|
18367
|
-
console.error(
|
|
19980
|
+
console.error(chalk21.red(` File not found: ${file}`));
|
|
18368
19981
|
process.exit(1);
|
|
18369
19982
|
}
|
|
18370
19983
|
const profile = loadProfile();
|
|
18371
19984
|
const skipProfile = getFalse(flags, "profile");
|
|
18372
19985
|
if (!profile && !skipProfile) {
|
|
18373
19986
|
console.error();
|
|
18374
|
-
console.error(" " +
|
|
18375
|
-
console.error(" " +
|
|
18376
|
-
console.error(" " +
|
|
19987
|
+
console.error(" " + chalk21.red("No company profile found."));
|
|
19988
|
+
console.error(" " + chalk21.dim("Run ") + paint("accent", "/onboard") + chalk21.dim(" first for better column mapping,"));
|
|
19989
|
+
console.error(" " + chalk21.dim("or pass ") + paint("accent", "--no-profile") + chalk21.dim(" to skip."));
|
|
18377
19990
|
console.error();
|
|
18378
19991
|
process.exit(1);
|
|
18379
19992
|
}
|
|
18380
|
-
const spinner =
|
|
19993
|
+
const spinner = makeSpinner("Initializing database\u2026");
|
|
18381
19994
|
try {
|
|
18382
19995
|
await initSchema();
|
|
18383
|
-
spinner.text = "Parsing CSV
|
|
19996
|
+
spinner.text = "Parsing CSV\u2026";
|
|
18384
19997
|
const content = readFileSync18(file, "utf-8");
|
|
18385
19998
|
const { rows, headers } = parseCSV(content);
|
|
18386
19999
|
if (rows.length === 0) {
|
|
@@ -18389,11 +20002,11 @@ async function handler3(args, ctx) {
|
|
|
18389
20002
|
}
|
|
18390
20003
|
const { detectRevenueLedgerHeaders: detectRevenueLedgerHeaders2 } = await Promise.resolve().then(() => (init_classify_source(), classify_source_exports));
|
|
18391
20004
|
if (detectRevenueLedgerHeaders2(headers)) {
|
|
18392
|
-
spinner.text = "Importing revenue ledger rows
|
|
20005
|
+
spinner.text = "Importing revenue ledger rows\u2026";
|
|
18393
20006
|
const { importRevenueRows: importRevenueRows2 } = await Promise.resolve().then(() => (init_revenue_importer(), revenue_importer_exports));
|
|
18394
20007
|
const uploadId2 = await insertCSVUpload({
|
|
18395
20008
|
source_system: source,
|
|
18396
|
-
original_filename:
|
|
20009
|
+
original_filename: basename4(file),
|
|
18397
20010
|
row_count: rows.length,
|
|
18398
20011
|
column_mappings: { entity_type: "revenue_ledger" },
|
|
18399
20012
|
status: "processing"
|
|
@@ -18405,28 +20018,28 @@ async function handler3(args, ctx) {
|
|
|
18405
20018
|
row_count: result2.imported
|
|
18406
20019
|
});
|
|
18407
20020
|
spinner.succeed(
|
|
18408
|
-
`Imported ${
|
|
20021
|
+
`Imported ${chalk21.bold(result2.imported.toString())} revenue events from ${chalk21.dim(basename4(file))}`
|
|
18409
20022
|
);
|
|
18410
20023
|
if (result2.errors.length > 0) {
|
|
18411
|
-
console.log(
|
|
20024
|
+
console.log(chalk21.yellow(` ${result2.errors.length} rows skipped`));
|
|
18412
20025
|
}
|
|
18413
20026
|
if (ctx.analysis) {
|
|
18414
20027
|
ctx.analysis.data_source_type = "revenue_ledger";
|
|
18415
20028
|
}
|
|
18416
|
-
console.log(
|
|
18417
|
-
return `${result2.imported} revenue events from ${
|
|
20029
|
+
console.log(chalk21.dim(" Run ") + chalk21.cyan("/metrics") + chalk21.dim(" for SaaS metrics with ledger-backed retention."));
|
|
20030
|
+
return `${result2.imported} revenue events from ${basename4(file)}`;
|
|
18418
20031
|
}
|
|
18419
|
-
spinner.text = "Detecting entity type
|
|
20032
|
+
spinner.text = "Detecting entity type\u2026";
|
|
18420
20033
|
const detection = detectEntityType(headers, source);
|
|
18421
20034
|
if (!detection) {
|
|
18422
20035
|
spinner.fail(`Could not auto-detect entity type for source: ${source}`);
|
|
18423
|
-
console.log(
|
|
20036
|
+
console.log(chalk21.dim(" Headers found: " + headers.join(", ")));
|
|
18424
20037
|
process.exit(1);
|
|
18425
20038
|
}
|
|
18426
20039
|
spinner.text = `Importing ${rows.length} ${detection.entityType} rows...`;
|
|
18427
20040
|
const uploadId = await insertCSVUpload({
|
|
18428
20041
|
source_system: source,
|
|
18429
|
-
original_filename:
|
|
20042
|
+
original_filename: basename4(file),
|
|
18430
20043
|
row_count: rows.length,
|
|
18431
20044
|
column_mappings: detection.mappings,
|
|
18432
20045
|
status: "processing"
|
|
@@ -18443,19 +20056,19 @@ async function handler3(args, ctx) {
|
|
|
18443
20056
|
row_count: result.imported
|
|
18444
20057
|
});
|
|
18445
20058
|
spinner.succeed(
|
|
18446
|
-
`Imported ${
|
|
20059
|
+
`Imported ${chalk21.bold(result.imported.toString())} ${detection.entityType} from ${chalk21.dim(basename4(file))} (${source})`
|
|
18447
20060
|
);
|
|
18448
20061
|
if (result.errors.length > 0) {
|
|
18449
|
-
console.log(
|
|
20062
|
+
console.log(chalk21.yellow(` ${result.errors.length} rows skipped`));
|
|
18450
20063
|
for (const err of result.errors.slice(0, 3)) {
|
|
18451
|
-
console.log(
|
|
20064
|
+
console.log(chalk21.dim(` - ${err}`));
|
|
18452
20065
|
}
|
|
18453
20066
|
if (result.errors.length > 3) {
|
|
18454
|
-
console.log(
|
|
20067
|
+
console.log(chalk21.dim(` ... and ${result.errors.length - 3} more`));
|
|
18455
20068
|
}
|
|
18456
20069
|
}
|
|
18457
20070
|
if (!skipResolve) {
|
|
18458
|
-
const resolveSpinner =
|
|
20071
|
+
const resolveSpinner = makeSpinner("Running identity resolution\u2026");
|
|
18459
20072
|
const resolved = await resolveIdentities();
|
|
18460
20073
|
if (resolved.resolved > 0) {
|
|
18461
20074
|
resolveSpinner.succeed(
|
|
@@ -18465,16 +20078,17 @@ async function handler3(args, ctx) {
|
|
|
18465
20078
|
resolveSpinner.succeed("No duplicates found");
|
|
18466
20079
|
}
|
|
18467
20080
|
}
|
|
18468
|
-
return `${result.imported} ${detection.entityType} from ${
|
|
20081
|
+
return `${result.imported} ${detection.entityType} from ${basename4(file)}`;
|
|
18469
20082
|
} catch (err) {
|
|
18470
20083
|
spinner.fail("Import failed");
|
|
18471
|
-
console.error(
|
|
20084
|
+
console.error(chalk21.red(String(err)));
|
|
18472
20085
|
process.exit(1);
|
|
18473
20086
|
}
|
|
18474
20087
|
}
|
|
18475
20088
|
var init_ingest = __esm({
|
|
18476
20089
|
"src/commands/ingest.ts"() {
|
|
18477
20090
|
"use strict";
|
|
20091
|
+
init_spinner();
|
|
18478
20092
|
init_schema();
|
|
18479
20093
|
init_csv_parse();
|
|
18480
20094
|
init_csv_detect();
|
|
@@ -18498,9 +20112,9 @@ __export(ingest_chat_exports, {
|
|
|
18498
20112
|
looksLikeFilePath: () => looksLikeFilePath
|
|
18499
20113
|
});
|
|
18500
20114
|
import { existsSync as existsSync20 } from "fs";
|
|
18501
|
-
import { basename as
|
|
20115
|
+
import { basename as basename5, resolve as resolve7 } from "path";
|
|
18502
20116
|
import { homedir as homedir6 } from "os";
|
|
18503
|
-
import
|
|
20117
|
+
import chalk22 from "chalk";
|
|
18504
20118
|
function extractFilePath(input) {
|
|
18505
20119
|
const trimmed = input.trim();
|
|
18506
20120
|
const patterns = [
|
|
@@ -18527,23 +20141,23 @@ function extractFilePath(input) {
|
|
|
18527
20141
|
return null;
|
|
18528
20142
|
}
|
|
18529
20143
|
function expandPath(p) {
|
|
18530
|
-
if (p.startsWith("~/")) return
|
|
18531
|
-
return
|
|
20144
|
+
if (p.startsWith("~/")) return resolve7(homedir6(), p.slice(2));
|
|
20145
|
+
return resolve7(p);
|
|
18532
20146
|
}
|
|
18533
20147
|
function looksLikeFilePath(input) {
|
|
18534
20148
|
return extractFilePath(input) !== null;
|
|
18535
20149
|
}
|
|
18536
20150
|
async function ingestFromChat(ctx, filePath) {
|
|
18537
20151
|
if (!ctx.rl) {
|
|
18538
|
-
console.log(" " +
|
|
20152
|
+
console.log(" " + chalk22.red("Ingest confirm requires interactive mode."));
|
|
18539
20153
|
return false;
|
|
18540
20154
|
}
|
|
18541
|
-
const name =
|
|
20155
|
+
const name = basename5(filePath);
|
|
18542
20156
|
const prompts = createPromptSession(ctx.rl, ctx);
|
|
18543
20157
|
try {
|
|
18544
20158
|
const ok = await prompts.confirm(`Ingest ${name} as CRM export?`, true);
|
|
18545
20159
|
if (!ok) {
|
|
18546
|
-
console.log(" " +
|
|
20160
|
+
console.log(" " + chalk22.dim("Ingest cancelled."));
|
|
18547
20161
|
return false;
|
|
18548
20162
|
}
|
|
18549
20163
|
} finally {
|
|
@@ -18571,7 +20185,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
18571
20185
|
false
|
|
18572
20186
|
);
|
|
18573
20187
|
if (useAi) {
|
|
18574
|
-
console.log(" " +
|
|
20188
|
+
console.log(" " + chalk22.dim("AI column mapping is not wired to ingest yet \u2014 trying standard ingest."));
|
|
18575
20189
|
}
|
|
18576
20190
|
} finally {
|
|
18577
20191
|
prompts2.close();
|
|
@@ -18598,12 +20212,18 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
18598
20212
|
invalidateGapAudit(ctx);
|
|
18599
20213
|
saveSessionState(ctx);
|
|
18600
20214
|
console.log();
|
|
18601
|
-
console.log(" " + paint("accent", "\u2713 Data loaded") +
|
|
20215
|
+
console.log(" " + paint("accent", "\u2713 Data loaded") + chalk22.dim(` \u2014 ${name}`));
|
|
18602
20216
|
recordMessage(ctx, "user", `[ingested ${name}]`);
|
|
18603
20217
|
recordMessage(ctx, "agent", `Loaded ${name}. Checking what we can analyze\u2026`);
|
|
18604
20218
|
const audit = await refreshGapAudit(ctx);
|
|
18605
20219
|
printGapCard(audit);
|
|
18606
20220
|
if (audit.can_compute && ctx.scope?.confirmed_at) {
|
|
20221
|
+
if (ctx.pendingAsk) {
|
|
20222
|
+
console.log();
|
|
20223
|
+
console.log(" " + chalk22.dim("Computing so I can answer\u2026"));
|
|
20224
|
+
await runConversationCompute(ctx);
|
|
20225
|
+
return true;
|
|
20226
|
+
}
|
|
18607
20227
|
const auto = await maybeAutoCompute(ctx);
|
|
18608
20228
|
if (auto) return true;
|
|
18609
20229
|
}
|
|
@@ -18626,10 +20246,10 @@ async function maybeAutoCompute(ctx) {
|
|
|
18626
20246
|
function isDemoIntent(input) {
|
|
18627
20247
|
return /\b(use demo|demo data|sample data|try demo|load demo)\b/i.test(input.trim());
|
|
18628
20248
|
}
|
|
18629
|
-
async function loadDemoFromChat(ctx, scenario) {
|
|
20249
|
+
async function loadDemoFromChat(ctx, scenario, opts = {}) {
|
|
18630
20250
|
if (!guardDemoEnabled()) return false;
|
|
18631
20251
|
const { handler: demo } = await Promise.resolve().then(() => (init_generate(), generate_exports));
|
|
18632
|
-
const args = ["--no-profile"];
|
|
20252
|
+
const args = ["--no-profile", "--brief"];
|
|
18633
20253
|
if (scenario) args.push("--scenario", scenario);
|
|
18634
20254
|
const ok = await demo(args, ctx);
|
|
18635
20255
|
if (!ok) return false;
|
|
@@ -18650,15 +20270,23 @@ async function loadDemoFromChat(ctx, scenario) {
|
|
|
18650
20270
|
ctx.scope = proposal.scope;
|
|
18651
20271
|
ctx.scope.confirmed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
18652
20272
|
ctx.analysis.primary = ctx.scope.primary_lens;
|
|
20273
|
+
} else if (!ctx.scope.confirmed_at) {
|
|
20274
|
+
ctx.scope = { ...ctx.scope, confirmed_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20275
|
+
ctx.analysis.primary = ctx.scope.primary_lens;
|
|
18653
20276
|
}
|
|
18654
20277
|
invalidateGapAudit(ctx);
|
|
18655
20278
|
saveSessionState(ctx);
|
|
18656
|
-
console.log();
|
|
18657
|
-
console.log(" " + paint("accent", "\u2713 Demo data loaded"));
|
|
18658
20279
|
recordMessage(ctx, "user", "use demo data");
|
|
18659
20280
|
recordMessage(ctx, "agent", "Demo dataset ready. Here's what we can work with:");
|
|
18660
20281
|
const audit = await refreshGapAudit(ctx);
|
|
18661
|
-
|
|
20282
|
+
const shouldAuto = opts.autoCompute || Boolean(ctx.pendingAsk && audit.can_compute && ctx.scope?.confirmed_at);
|
|
20283
|
+
if (shouldAuto && audit.can_compute) {
|
|
20284
|
+
console.log();
|
|
20285
|
+
console.log(" " + chalk22.dim("Computing so I can answer\u2026"));
|
|
20286
|
+
await runConversationCompute(ctx);
|
|
20287
|
+
return true;
|
|
20288
|
+
}
|
|
20289
|
+
printGapCard(audit, { skipSatisfied: true });
|
|
18662
20290
|
return true;
|
|
18663
20291
|
}
|
|
18664
20292
|
var init_ingest_chat = __esm({
|
|
@@ -19363,12 +20991,15 @@ Do not run compute until audit_data_gaps reports can_compute. Prefer ingest_file
|
|
|
19363
20991
|
- If the question is ambiguous, ask one sharp clarifying question \u2014 still scannable (one line).`;
|
|
19364
20992
|
const deepJob = `YOUR JOB (DEEP MODE \u2014 user asked for detail or a new data cut):
|
|
19365
20993
|
- Decide whether you need tools to answer, whether a direct answer is enough, or both. Don't re-call a tool whose result you already have from earlier in the conversation.
|
|
19366
|
-
- Match the altitude asked. A high-level question gets the one-sentence story with three numbers max, then an offer to descend.
|
|
20994
|
+
- Match the altitude asked. A high-level question gets the one-sentence story with three numbers max, then an offer to descend.
|
|
20995
|
+
- Plan-of-attack / "what should we do" questions: prefer draft_strategy with a crisp objective \u2014 do not improvise a multi-week Phase 1/2/3 program inline.
|
|
20996
|
+
- If answering a single-play "how" inline (not a full program): one play + mechanism + what to verify \u2014 read get_play_detail when needed. No phased multi-week roadmap.
|
|
20997
|
+
- After a successful draft_strategy tool result: reply with at most 1\u20132 sentences introducing the handoff. Do not write the plan \u2014 the strategist engine owns grounding, sequencing, and stress-test.
|
|
19367
20998
|
- Lead with the answer, then structure \u2014 headline, drivers, so-what. Avoid walls of text.
|
|
19368
20999
|
- When you use numbers, include dollar values where available and lead with financial impact.
|
|
19369
21000
|
- Reference specific segments, vital signs, or plays by name when it helps the user act.
|
|
19370
21001
|
- Reference the company by name and use language appropriate to their industry and ICP.
|
|
19371
|
-
-
|
|
21002
|
+
- Descriptive questions (what is happening, why) stay normal Q&A; naming a single playbook play is fine without the strategist.
|
|
19372
21003
|
- If the question is genuinely ambiguous given everything you already know, ask one sharp clarifying question instead of guessing.`;
|
|
19373
21004
|
const analystSection = responseMode === "brief" && experiment === "production" ? `ANALYST INSTINCT (apply lightly in brief mode \u2014 one causal link max, no full triage):
|
|
19374
21005
|
- Lead with the single most expensive or urgent point relevant to this question.
|
|
@@ -19553,18 +21184,19 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
19553
21184
|
yield doneEvent(findings3, lastMeta, initialContext, messages);
|
|
19554
21185
|
return;
|
|
19555
21186
|
}
|
|
21187
|
+
messages.push(response.assistant_message);
|
|
19556
21188
|
let findings2 = parseFindings(fullText);
|
|
19557
|
-
if (findings2.length === 0
|
|
19558
|
-
messages.push(
|
|
19559
|
-
messages
|
|
19560
|
-
role: "user",
|
|
19561
|
-
content: "Please format your findings as the JSON array specified in your instructions. Respond with ONLY the JSON array, no other text."
|
|
19562
|
-
});
|
|
19563
|
-
const retry = await callLlm(messages, maxTokens, tools.length > 0);
|
|
21189
|
+
if (findings2.length === 0) {
|
|
21190
|
+
messages.push({ role: "user", content: FINDINGS_JSON_NUDGE });
|
|
21191
|
+
const retry = await callLlm(messages, maxTokens, false);
|
|
19564
21192
|
findings2 = parseFindings(retry.text);
|
|
19565
21193
|
messages.push(retry.assistant_message);
|
|
19566
|
-
}
|
|
19567
|
-
|
|
21194
|
+
}
|
|
21195
|
+
if (findings2.length === 0) {
|
|
21196
|
+
messages.push({ role: "user", content: FINDINGS_JSON_SCHEMA_NUDGE });
|
|
21197
|
+
const retry2 = await callLlm(messages, maxTokens, false);
|
|
21198
|
+
findings2 = parseFindings(retry2.text);
|
|
21199
|
+
messages.push(retry2.assistant_message);
|
|
19568
21200
|
}
|
|
19569
21201
|
for (const finding of findings2) yield { type: "finding", finding };
|
|
19570
21202
|
yield doneEvent(findings2, lastMeta, initialContext, messages);
|
|
@@ -19582,13 +21214,19 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
19582
21214
|
});
|
|
19583
21215
|
messages.push({ role: "tool", tool_call_id: tc.id, content: result });
|
|
19584
21216
|
}
|
|
21217
|
+
if (mode === "investigation" && iteration + 1 === INVESTIGATION_EVIDENCE_NUDGE_AFTER && iteration + 1 < MAX_ITERATIONS) {
|
|
21218
|
+
messages.push({
|
|
21219
|
+
role: "user",
|
|
21220
|
+
content: "You have enough evidence from the tools above. Next response: emit your findings as ONLY the JSON array (no more tool calls unless a single critical gap remains)."
|
|
21221
|
+
});
|
|
21222
|
+
}
|
|
19585
21223
|
}
|
|
19586
21224
|
if (mode === "fresh") {
|
|
19587
21225
|
messages.push({
|
|
19588
21226
|
role: "user",
|
|
19589
21227
|
content: "You've reached the tool budget. Please answer the user's question now with what you've learned."
|
|
19590
21228
|
});
|
|
19591
|
-
const final2 = await callLlm(messages, responseMode === "brief" ? BRIEF_MAX_TOKENS : 2048,
|
|
21229
|
+
const final2 = await callLlm(messages, responseMode === "brief" ? BRIEF_MAX_TOKENS : 2048, false);
|
|
19592
21230
|
if (final2.text.trim()) yield { type: "answer", text: final2.text.trim() };
|
|
19593
21231
|
messages.push(final2.assistant_message);
|
|
19594
21232
|
yield doneEvent([], lastMeta, initialContext, messages);
|
|
@@ -19598,10 +21236,16 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
19598
21236
|
role: "user",
|
|
19599
21237
|
content: "You've reached the investigation limit. Please provide your findings now as a JSON array."
|
|
19600
21238
|
});
|
|
19601
|
-
|
|
19602
|
-
|
|
19603
|
-
for (const finding of findings) yield { type: "finding", finding };
|
|
21239
|
+
let final = await callLlm(messages, 4096, false);
|
|
21240
|
+
let findings = parseFindings(final.text);
|
|
19604
21241
|
messages.push(final.assistant_message);
|
|
21242
|
+
if (findings.length === 0) {
|
|
21243
|
+
messages.push({ role: "user", content: FINDINGS_JSON_SCHEMA_NUDGE });
|
|
21244
|
+
final = await callLlm(messages, 4096, false);
|
|
21245
|
+
findings = parseFindings(final.text);
|
|
21246
|
+
messages.push(final.assistant_message);
|
|
21247
|
+
}
|
|
21248
|
+
for (const finding of findings) yield { type: "finding", finding };
|
|
19605
21249
|
yield doneEvent(findings, lastMeta, initialContext, messages);
|
|
19606
21250
|
}
|
|
19607
21251
|
function buildInitialContext(computeResult, divergences, userQuestion) {
|
|
@@ -19635,7 +21279,7 @@ Here is the current GTM health snapshot as background. Use it plus any tools you
|
|
|
19635
21279
|
function parseFindings(text) {
|
|
19636
21280
|
return parseJsonArrayFromText(text) ?? [];
|
|
19637
21281
|
}
|
|
19638
|
-
var MAX_ITERATIONS, BRIEF_MAX_TOKENS, DEEP_MAX_TOKENS;
|
|
21282
|
+
var MAX_ITERATIONS, INVESTIGATION_EVIDENCE_NUDGE_AFTER, BRIEF_MAX_TOKENS, DEEP_MAX_TOKENS, FINDINGS_JSON_NUDGE, FINDINGS_JSON_SCHEMA_NUDGE;
|
|
19639
21283
|
var init_agentic_loop = __esm({
|
|
19640
21284
|
"src/ai/agentic-loop.ts"() {
|
|
19641
21285
|
"use strict";
|
|
@@ -19652,8 +21296,12 @@ var init_agentic_loop = __esm({
|
|
|
19652
21296
|
init_thread();
|
|
19653
21297
|
init_prompt_parts();
|
|
19654
21298
|
MAX_ITERATIONS = 10;
|
|
21299
|
+
INVESTIGATION_EVIDENCE_NUDGE_AFTER = 5;
|
|
19655
21300
|
BRIEF_MAX_TOKENS = 768;
|
|
19656
21301
|
DEEP_MAX_TOKENS = 4096;
|
|
21302
|
+
FINDINGS_JSON_NUDGE = "Please format your findings as the JSON array specified in your instructions. Respond with ONLY the JSON array, no other text.";
|
|
21303
|
+
FINDINGS_JSON_SCHEMA_NUDGE = `Respond with ONLY a JSON array of finding objects \u2014 no prose, no markdown fences. Each object needs: severity, segment, finding, vital_signs, entity_count, recommended_focus, dollar_value, recommended_plays. Shape:
|
|
21304
|
+
${FINDINGS_SCHEMA_BLOCK}`;
|
|
19657
21305
|
}
|
|
19658
21306
|
});
|
|
19659
21307
|
|