@sonnechasser/ntrp 1.3.9 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2616 -990
- package/dist/mcp/server.js +1155 -166
- package/package.json +3 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1369,6 +1369,26 @@ function buildSessionContextDoc(file, opts = {}) {
|
|
|
1369
1369
|
if (file.strategist.origin) lines.push(`- Origin: ${file.strategist.origin}`);
|
|
1370
1370
|
lines.push("");
|
|
1371
1371
|
}
|
|
1372
|
+
if (file.think) {
|
|
1373
|
+
lines.push("## Think (in flight)");
|
|
1374
|
+
lines.push("");
|
|
1375
|
+
lines.push(`- Step: ${file.think.step}`);
|
|
1376
|
+
if (file.think.seed) lines.push(`- Seed: ${file.think.seed}`);
|
|
1377
|
+
if (file.think.origin) lines.push(`- Origin: ${file.think.origin}`);
|
|
1378
|
+
if (file.think.open_questions?.length) {
|
|
1379
|
+
lines.push("- Open questions:");
|
|
1380
|
+
for (const q of file.think.open_questions) lines.push(` - ${q}`);
|
|
1381
|
+
}
|
|
1382
|
+
if (file.think.challenged_assumptions?.length) {
|
|
1383
|
+
lines.push("- Challenged assumptions:");
|
|
1384
|
+
for (const a of file.think.challenged_assumptions) lines.push(` - ${a}`);
|
|
1385
|
+
}
|
|
1386
|
+
if (file.think.working_hypotheses?.length) {
|
|
1387
|
+
lines.push("- Working hypotheses:");
|
|
1388
|
+
for (const h of file.think.working_hypotheses) lines.push(` - ${h}`);
|
|
1389
|
+
}
|
|
1390
|
+
lines.push("");
|
|
1391
|
+
}
|
|
1372
1392
|
lines.push("## Deliverables");
|
|
1373
1393
|
lines.push("");
|
|
1374
1394
|
if (file.deliverables && file.deliverables.length > 0) {
|
|
@@ -8090,6 +8110,7 @@ function initContext(oneShot, execution) {
|
|
|
8090
8110
|
snapshot: { computeResult: null, divergences: [] },
|
|
8091
8111
|
messages: [],
|
|
8092
8112
|
conversation: [],
|
|
8113
|
+
thinkConversation: [],
|
|
8093
8114
|
stage: "new",
|
|
8094
8115
|
deliverables: [],
|
|
8095
8116
|
analysis: defaultSessionAnalysis(),
|
|
@@ -8110,12 +8131,14 @@ function buildSessionFileSnapshot(ctx) {
|
|
|
8110
8131
|
if (ctx.dataset) file.dataset = ctx.dataset;
|
|
8111
8132
|
if (ctx.deliverables.length > 0) file.deliverables = ctx.deliverables;
|
|
8112
8133
|
if (ctx.conversation.length > 0) file.thread = ctx.conversation;
|
|
8134
|
+
if ((ctx.thinkConversation?.length ?? 0) > 0) file.think_thread = ctx.thinkConversation;
|
|
8113
8135
|
if (ctx.resumedFromId) file.resumed_from = ctx.resumedFromId;
|
|
8114
8136
|
if (ctx.analysis) file.analysis = ctx.analysis;
|
|
8115
8137
|
if (ctx.scope) file.scope = ctx.scope;
|
|
8116
8138
|
if (ctx.attachments && ctx.attachments.length > 0) file.attachments = ctx.attachments;
|
|
8117
8139
|
if (ctx.llm && Object.keys(ctx.llm).length > 0) file.llm = ctx.llm;
|
|
8118
8140
|
if (ctx.strategistState) file.strategist = ctx.strategistState;
|
|
8141
|
+
if (ctx.thinkState) file.think = ctx.thinkState;
|
|
8119
8142
|
if (ctx.pendingAsk) file.pending_ask = ctx.pendingAsk;
|
|
8120
8143
|
return file;
|
|
8121
8144
|
}
|
|
@@ -8242,6 +8265,9 @@ function loadSessionFile(id) {
|
|
|
8242
8265
|
if (session.thread?.length) {
|
|
8243
8266
|
session.thread = normalizeThread(session.thread);
|
|
8244
8267
|
}
|
|
8268
|
+
if (session.think_thread?.length) {
|
|
8269
|
+
session.think_thread = normalizeThread(session.think_thread);
|
|
8270
|
+
}
|
|
8245
8271
|
return session;
|
|
8246
8272
|
} catch {
|
|
8247
8273
|
return null;
|
|
@@ -8444,6 +8470,9 @@ async function finalizeSession(ctx, stage) {
|
|
|
8444
8470
|
if (ctx.conversation.length > 0) {
|
|
8445
8471
|
file.thread = ctx.conversation;
|
|
8446
8472
|
}
|
|
8473
|
+
if ((ctx.thinkConversation?.length ?? 0) > 0) {
|
|
8474
|
+
file.think_thread = ctx.thinkConversation;
|
|
8475
|
+
}
|
|
8447
8476
|
if (ctx.analysis) {
|
|
8448
8477
|
file.analysis = ctx.analysis;
|
|
8449
8478
|
}
|
|
@@ -8459,6 +8488,9 @@ async function finalizeSession(ctx, stage) {
|
|
|
8459
8488
|
if (ctx.strategistState) {
|
|
8460
8489
|
file.strategist = ctx.strategistState;
|
|
8461
8490
|
}
|
|
8491
|
+
if (ctx.thinkState) {
|
|
8492
|
+
file.think = ctx.thinkState;
|
|
8493
|
+
}
|
|
8462
8494
|
if (ctx.pendingAsk) {
|
|
8463
8495
|
file.pending_ask = ctx.pendingAsk;
|
|
8464
8496
|
}
|
|
@@ -8520,6 +8552,7 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
8520
8552
|
ctx.sessionName = opts.sessionName;
|
|
8521
8553
|
ctx.messages = opts.messages;
|
|
8522
8554
|
ctx.conversation = opts.conversation ?? [];
|
|
8555
|
+
ctx.thinkConversation = opts.thinkConversation ?? [];
|
|
8523
8556
|
ctx.resumedFromId = opts.resumedFromId;
|
|
8524
8557
|
ctx.resumedSessionSummary = opts.resumedSessionSummary;
|
|
8525
8558
|
ctx.stage = opts.stage ?? "new";
|
|
@@ -8530,6 +8563,7 @@ function resetContextForSwitch(ctx, opts) {
|
|
|
8530
8563
|
ctx.attachments = opts.attachments ?? [];
|
|
8531
8564
|
ctx.llm = opts.llm;
|
|
8532
8565
|
ctx.strategistState = opts.strategistState;
|
|
8566
|
+
ctx.thinkState = opts.thinkState;
|
|
8533
8567
|
ctx.pendingAsk = opts.pendingAsk;
|
|
8534
8568
|
ctx.gapAudit = void 0;
|
|
8535
8569
|
ctx.deliverIntent = false;
|
|
@@ -10215,6 +10249,21 @@ Bare \`/deepdive\` runs the full onboarding tour. Type \`/deepdive guide\` to ju
|
|
|
10215
10249
|
Type \`/deepdive handoff\` for the ship-to-Claude slide. Type \`/deepdive <metric>\` to jump to one metric card.
|
|
10216
10250
|
Type \`/deepdive list\` to print the catalog. Works without an AI key. Re-run anytime from the homescreen.
|
|
10217
10251
|
Live values overlay when an analysis exists.`
|
|
10252
|
+
},
|
|
10253
|
+
{
|
|
10254
|
+
name: "thinkwithme",
|
|
10255
|
+
raw: `---
|
|
10256
|
+
name: thinkwithme
|
|
10257
|
+
description: Socratic co-thinking channel \u2014 explore and pressure-test
|
|
10258
|
+
section: Navigation
|
|
10259
|
+
args: [topic]
|
|
10260
|
+
handler: ../commands/thinkwithme.ts
|
|
10261
|
+
---
|
|
10262
|
+
|
|
10263
|
+
Open a dedicated \`think \u203A\` channel to explore ideas, challenge assumptions, and pull evidence from your data.
|
|
10264
|
+
Bare \`/thinkwithme\` enters the channel. Type \`/thinkwithme <topic>\` to seed the first turn.
|
|
10265
|
+
Requires an analysis and an AI key (\`/connect\`). Type \`done\` or \`cancel\` to return to ask \u203A.
|
|
10266
|
+
When you are ready to commit to a plan, say so or the partner can hand off to \`/strategy\`.`
|
|
10218
10267
|
},
|
|
10219
10268
|
{
|
|
10220
10269
|
name: "status",
|
|
@@ -10400,6 +10449,7 @@ handler: ../commands/config.ts
|
|
|
10400
10449
|
Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
10401
10450
|
\`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
|
|
10402
10451
|
\`llm-primary\` (default provider), \`llm-tier\`, \`llm-auto-failover\`,
|
|
10452
|
+
\`voice-personality\`, \`voice-roast\`,
|
|
10403
10453
|
\`default-format\`, \`export-dir\`, \`ai-inbox-dir\` (or type \`/inbox set\`).
|
|
10404
10454
|
|
|
10405
10455
|
Setting a provider key opens a hidden prompt and auto-discovers that provider's models.
|
|
@@ -10432,6 +10482,21 @@ handler: ../commands/tier.ts
|
|
|
10432
10482
|
Set the quality and cost tier for this session. Agentic surfaces respect the tier.
|
|
10433
10483
|
Some single-shot surfaces keep fixed defaults. Type \`/tier list\` to highlight the active stack.
|
|
10434
10484
|
Add \`--default\` to persist to config.`
|
|
10485
|
+
},
|
|
10486
|
+
{
|
|
10487
|
+
name: "voice",
|
|
10488
|
+
raw: `---
|
|
10489
|
+
name: voice
|
|
10490
|
+
description: Set personality and roast (how NTRP talks)
|
|
10491
|
+
section: Settings
|
|
10492
|
+
args: [list|personality <level>|roast <level>|<personality> <roast>]
|
|
10493
|
+
handler: ../commands/voice.ts
|
|
10494
|
+
---
|
|
10495
|
+
|
|
10496
|
+
Set how NTRP talks. Personality is robotic, composed, casual, or loose.
|
|
10497
|
+
Roast is light, medium, dark, or heavy. Communication only \u2014 scores and dollars do not change.
|
|
10498
|
+
Type \`/voice\` to pick from a numbered list (Enter keeps the current setting). Type \`/voice list\` to print the catalog. Type \`/voice casual heavy\` to set both.
|
|
10499
|
+
This writes to config immediately.`
|
|
10435
10500
|
},
|
|
10436
10501
|
{
|
|
10437
10502
|
name: "model",
|
|
@@ -10599,7 +10664,7 @@ function formatCatalogLine(meta) {
|
|
|
10599
10664
|
const note = DESTRUCTIVE_COMMAND_NOTES[meta.name] ? ` (${DESTRUCTIVE_COMMAND_NOTES[meta.name]})` : "";
|
|
10600
10665
|
return `- /${meta.name}${args} \u2014 ${meta.description}${note}`;
|
|
10601
10666
|
}
|
|
10602
|
-
var ANALYST_FILE_NAME, ANALYST_FILE_MAX_CHARS, EXECUTION_BIAS_BLOCK, ANALYST_INSTINCT_BLOCK, SAFETY_BLOCK, VITAL_SIGNS_BLOCK, PLAYBOOK_BLOCK, DESTRUCTIVE_COMMAND_NOTES, METRICS_BLOCK, GTM_ENGINEERING_BLOCK, PYRAMID_OUTPUT_BLOCK
|
|
10667
|
+
var ANALYST_FILE_NAME, ANALYST_FILE_MAX_CHARS, EXECUTION_BIAS_BLOCK, ANALYST_INSTINCT_BLOCK, SAFETY_BLOCK, VITAL_SIGNS_BLOCK, PLAYBOOK_BLOCK, DESTRUCTIVE_COMMAND_NOTES, METRICS_BLOCK, GTM_ENGINEERING_BLOCK, PYRAMID_OUTPUT_BLOCK;
|
|
10603
10668
|
var init_prompt_parts = __esm({
|
|
10604
10669
|
"src/ai/prompt-parts.ts"() {
|
|
10605
10670
|
"use strict";
|
|
@@ -10684,21 +10749,102 @@ Restraint: you diagnose and prescribe the system; you do not build it here. Name
|
|
|
10684
10749
|
- 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.
|
|
10685
10750
|
- 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.
|
|
10686
10751
|
- 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).`;
|
|
10687
|
-
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10752
|
+
}
|
|
10753
|
+
});
|
|
10754
|
+
|
|
10755
|
+
// src/voice/catalog.ts
|
|
10756
|
+
function parsePersonality(raw) {
|
|
10757
|
+
if (!raw) return null;
|
|
10758
|
+
return PERSONALITY_ALIASES[raw.trim().toLowerCase()] ?? null;
|
|
10759
|
+
}
|
|
10760
|
+
function parseRoast(raw) {
|
|
10761
|
+
if (!raw) return null;
|
|
10762
|
+
return ROAST_ALIASES[raw.trim().toLowerCase()] ?? null;
|
|
10763
|
+
}
|
|
10764
|
+
function formatVoiceStatusDetail(prefs) {
|
|
10765
|
+
return `${prefs.personality} \xB7 ${prefs.roast} roast`;
|
|
10766
|
+
}
|
|
10767
|
+
var DEFAULT_PERSONALITY, DEFAULT_ROAST, PERSONALITY_KEY, ROAST_KEY, PERSONALITY_ALIASES, ROAST_ALIASES;
|
|
10768
|
+
var init_catalog2 = __esm({
|
|
10769
|
+
"src/voice/catalog.ts"() {
|
|
10770
|
+
"use strict";
|
|
10771
|
+
DEFAULT_PERSONALITY = "composed";
|
|
10772
|
+
DEFAULT_ROAST = "light";
|
|
10773
|
+
PERSONALITY_KEY = "voice-personality";
|
|
10774
|
+
ROAST_KEY = "voice-roast";
|
|
10775
|
+
PERSONALITY_ALIASES = {
|
|
10776
|
+
robotic: "robotic",
|
|
10777
|
+
machine: "robotic",
|
|
10778
|
+
ste: "robotic",
|
|
10779
|
+
composed: "composed",
|
|
10780
|
+
formal: "composed",
|
|
10781
|
+
professional: "composed",
|
|
10782
|
+
balanced: "composed",
|
|
10783
|
+
casual: "casual",
|
|
10784
|
+
warm: "casual",
|
|
10785
|
+
loose: "loose",
|
|
10786
|
+
vibe: "loose",
|
|
10787
|
+
relaxed: "loose"
|
|
10788
|
+
};
|
|
10789
|
+
ROAST_ALIASES = {
|
|
10790
|
+
light: "light",
|
|
10791
|
+
gentle: "light",
|
|
10792
|
+
soft: "light",
|
|
10793
|
+
medium: "medium",
|
|
10794
|
+
mid: "medium",
|
|
10795
|
+
dark: "dark",
|
|
10796
|
+
blunt: "dark",
|
|
10797
|
+
surgical: "dark",
|
|
10798
|
+
heavy: "heavy",
|
|
10799
|
+
burnt: "heavy",
|
|
10800
|
+
extra: "heavy",
|
|
10801
|
+
max: "heavy"
|
|
10802
|
+
};
|
|
10803
|
+
}
|
|
10804
|
+
});
|
|
10805
|
+
|
|
10806
|
+
// src/voice/prefs.ts
|
|
10807
|
+
function loadVoicePrefs() {
|
|
10808
|
+
return {
|
|
10809
|
+
personality: parsePersonality(getConfigValue(PERSONALITY_KEY)) ?? DEFAULT_PERSONALITY,
|
|
10810
|
+
roast: parseRoast(getConfigValue(ROAST_KEY)) ?? DEFAULT_ROAST
|
|
10811
|
+
};
|
|
10812
|
+
}
|
|
10813
|
+
var init_prefs = __esm({
|
|
10814
|
+
"src/voice/prefs.ts"() {
|
|
10815
|
+
"use strict";
|
|
10816
|
+
init_store();
|
|
10817
|
+
init_catalog2();
|
|
10818
|
+
}
|
|
10819
|
+
});
|
|
10820
|
+
|
|
10821
|
+
// src/voice/prompt.ts
|
|
10822
|
+
function buildUserVisibleProseBlock(prefs) {
|
|
10823
|
+
const voice2 = prefs ?? loadVoicePrefs();
|
|
10824
|
+
if (voice2.personality === "robotic") {
|
|
10825
|
+
return `${ROBOTIC_STE_BLOCK}
|
|
10826
|
+
VOICE (communication only \u2014 never change the numbers):
|
|
10827
|
+
- Personality: robotic.
|
|
10828
|
+
${ROAST_RULES[voice2.roast]}`;
|
|
10829
|
+
}
|
|
10830
|
+
return `USER-VISIBLE TEXT (the human reads these sentences \u2014 not this system prompt):
|
|
10831
|
+
${PERSONALITY_RULES[voice2.personality]}
|
|
10832
|
+
${ROAST_RULES[voice2.roast]}
|
|
10833
|
+
${SHARED_INVARIANTS}`;
|
|
10834
|
+
}
|
|
10835
|
+
function buildFindingsFindingHint(prefs) {
|
|
10836
|
+
const voice2 = prefs ?? loadVoicePrefs();
|
|
10837
|
+
const register = voice2.personality === "robotic" ? "STE-100 sentences. No contractions, slang, or filler." : voice2.personality === "composed" ? "Composed consultant sentences. Light contractions OK." : voice2.personality === "casual" ? "Casual consultant sentences. Warm, still short." : "Loose consultant sentences. Personality in the wrap, not the math.";
|
|
10838
|
+
const roast = voice2.roast === "light" ? "Light roast: what is working, then what to improve." : voice2.roast === "medium" ? "Medium roast: name the miss. No padding." : voice2.roast === "dark" ? "Dark roast: surgical. Verdict + dollar in one breath." : "Heavy roast: maximum roast after the number, never instead of it.";
|
|
10839
|
+
return `Pyramid-shaped, 2-3 sentences: (1) HEADLINE \u2014 verdict + dollar figure in one short sentence (\u226420 words); (2) EVIDENCE \u2014 the one or two numbers that prove it; (3) SO-WHAT \u2014 the consequence or the action. ${register} ${roast} Keep dollar figures and play names. Do not invent numbers. An executive should be able to repeat sentence 1 from memory.`;
|
|
10840
|
+
}
|
|
10841
|
+
function buildFindingsSchemaBlock(prefs) {
|
|
10842
|
+
const finding = buildFindingsFindingHint(prefs);
|
|
10843
|
+
return `[
|
|
10698
10844
|
{
|
|
10699
10845
|
"severity": "critical" | "warning" | "info",
|
|
10700
10846
|
"segment": "segment name or 'Overall'",
|
|
10701
|
-
"finding":
|
|
10847
|
+
"finding": ${JSON.stringify(finding)},
|
|
10702
10848
|
"vital_signs": {"vital_sign_name": score, ...},
|
|
10703
10849
|
"entity_count": number_of_affected_entities,
|
|
10704
10850
|
"recommended_focus": "vital_sign_name",
|
|
@@ -10706,6 +10852,51 @@ Restraint: you diagnose and prescribe the system; you do not build it here. Name
|
|
|
10706
10852
|
"recommended_plays": [{"play_id": "play-id", "play_name": "Play Name", "rationale": "Why this play helps"}]
|
|
10707
10853
|
}
|
|
10708
10854
|
]`;
|
|
10855
|
+
}
|
|
10856
|
+
var ROBOTIC_STE_BLOCK, SHARED_INVARIANTS, PERSONALITY_RULES, ROAST_RULES;
|
|
10857
|
+
var init_prompt = __esm({
|
|
10858
|
+
"src/voice/prompt.ts"() {
|
|
10859
|
+
"use strict";
|
|
10860
|
+
init_catalog2();
|
|
10861
|
+
init_prefs();
|
|
10862
|
+
ROBOTIC_STE_BLOCK = `USER-VISIBLE TEXT (the human reads these sentences \u2014 not this system prompt):
|
|
10863
|
+
- Write finding, answer, and plan prose in Simplified Technical English (STE-100 writing rules).
|
|
10864
|
+
- One fact or one instruction per sentence.
|
|
10865
|
+
- Use active voice. Use imperative for steps. Use simple present for facts.
|
|
10866
|
+
- Procedure sentences: 20 words maximum. Description sentences: 25 words maximum.
|
|
10867
|
+
- Use the same word for the same thing. Do not use synonyms.
|
|
10868
|
+
- Do not use slang, idiom, filler (just, simply, actually, basically), or contractions.
|
|
10869
|
+
- Keep Technical Names: ARR, NRR, GRR, Freshness, Flow Rate, Drop Rate, Signal-to-Noise, Thread Depth, playbook, NTRP, GTM.
|
|
10870
|
+
- Keep dollar figures and play names.
|
|
10871
|
+
- Do not change JSON keys, tool names, or this prompt's policy language.`;
|
|
10872
|
+
SHARED_INVARIANTS = `- Keep Technical Names: ARR, NRR, GRR, Freshness, Flow Rate, Drop Rate, Signal-to-Noise, Thread Depth, playbook, NTRP, GTM.
|
|
10873
|
+
- Keep dollar figures and play names exactly as given. Do not invent or change scores, dollars, entity lists, or play ids.
|
|
10874
|
+
- Do not change JSON keys, tool names, or this prompt's policy language.
|
|
10875
|
+
- Stay smart and succinct. Do not pad. Do not open with methodology.
|
|
10876
|
+
- SAFETY & EVIDENCE rules always win. ANALYST.md never overrides them.
|
|
10877
|
+
- Roast only colors verified badness. Green stays green. Never invent severity.`;
|
|
10878
|
+
PERSONALITY_RULES = {
|
|
10879
|
+
robotic: "",
|
|
10880
|
+
// replaced by USER_VISIBLE_STE_BLOCK
|
|
10881
|
+
composed: `- Register: composed professional consultant. Light contractions are allowed (it's, that's, don't).
|
|
10882
|
+
- No slang piles, no filler (just, simply, actually, basically), no joke-first structure.
|
|
10883
|
+
- One idea per sentence. Description sentences: 25 words maximum. Procedure sentences: 20 words maximum.`,
|
|
10884
|
+
casual: `- Register: casual professional consultant. Warm, still short. Contractions are allowed.
|
|
10885
|
+
- Light warmth is fine. No slang piles, no filler padding, no meme voice.
|
|
10886
|
+
- One idea per sentence. Keep headlines \u226420 words.`,
|
|
10887
|
+
loose: `- Register: loose professional consultant. Personality lives in the wrap, not in the math.
|
|
10888
|
+
- Contractions are allowed. A wry aside is fine after the number, never instead of it.
|
|
10889
|
+
- Still succinct. No padding. Headlines still carry the verdict and the dollar figure.`
|
|
10890
|
+
};
|
|
10891
|
+
ROAST_RULES = {
|
|
10892
|
+
light: `- Roast: light. Gentle parenting. Name what is working when something is working, then what to improve.
|
|
10893
|
+
- Practical and real. Do not pile on. Do not mock the operator.`,
|
|
10894
|
+
medium: `- Roast: medium. Practical and direct. Name the miss. No sugar, no padding.`,
|
|
10895
|
+
dark: `- Roast: dark. Surgical and blunt. Name the miss and the dollar cost in the same breath.
|
|
10896
|
+
- Do not invent extra problems. Do not soften a red score.`,
|
|
10897
|
+
heavy: `- Roast: heavy. Maximum roast. Comedy is allowed after the number, never instead of it.
|
|
10898
|
+
- Still evidence-only. If the score is green, do not roast it into red.`
|
|
10899
|
+
};
|
|
10709
10900
|
}
|
|
10710
10901
|
});
|
|
10711
10902
|
|
|
@@ -10853,10 +11044,10 @@ ${buildPlaybookBlock()}
|
|
|
10853
11044
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
10854
11045
|
${PYRAMID_OUTPUT_BLOCK}
|
|
10855
11046
|
|
|
10856
|
-
${
|
|
11047
|
+
${buildUserVisibleProseBlock()}
|
|
10857
11048
|
|
|
10858
11049
|
Respond with JSON only \u2014 an array of finding objects:
|
|
10859
|
-
${
|
|
11050
|
+
${buildFindingsSchemaBlock()}
|
|
10860
11051
|
|
|
10861
11052
|
Rules:
|
|
10862
11053
|
- Maximum 8 findings
|
|
@@ -10960,6 +11151,7 @@ var init_findings = __esm({
|
|
|
10960
11151
|
init_repl_api();
|
|
10961
11152
|
init_failover();
|
|
10962
11153
|
init_prompt_parts();
|
|
11154
|
+
init_prompt();
|
|
10963
11155
|
init_json_response();
|
|
10964
11156
|
init_json_stream();
|
|
10965
11157
|
init_playbook();
|
|
@@ -11041,13 +11233,21 @@ function buildFreshNlTools() {
|
|
|
11041
11233
|
if (isWebRetrievalEnabled()) tools2.push(WEB_SEARCH_TOOL);
|
|
11042
11234
|
return tools2;
|
|
11043
11235
|
}
|
|
11236
|
+
function buildThinkTools() {
|
|
11237
|
+
const tools2 = [
|
|
11238
|
+
...AGENTIC_TOOLS,
|
|
11239
|
+
...THINK_CHANNEL_TOOLS
|
|
11240
|
+
];
|
|
11241
|
+
if (isWebRetrievalEnabled()) tools2.push(WEB_SEARCH_TOOL);
|
|
11242
|
+
return tools2;
|
|
11243
|
+
}
|
|
11044
11244
|
function allRegisteredToolSchemas() {
|
|
11045
|
-
return [...AGENTIC_TOOLS, ...CONVERSATION_TOOLS, WEB_SEARCH_TOOL];
|
|
11245
|
+
return [...AGENTIC_TOOLS, ...CONVERSATION_TOOLS, ...THINK_CHANNEL_TOOLS, WEB_SEARCH_TOOL];
|
|
11046
11246
|
}
|
|
11047
11247
|
function getToolSchema(name) {
|
|
11048
11248
|
return allRegisteredToolSchemas().find((t) => t.name === name);
|
|
11049
11249
|
}
|
|
11050
|
-
var WEB_SEARCH_TOOL, CONVERSATION_TOOLS, AGENTIC_TOOLS;
|
|
11250
|
+
var WEB_SEARCH_TOOL, CONVERSATION_TOOLS, THINK_CHANNEL_TOOLS, AGENTIC_TOOLS;
|
|
11051
11251
|
var init_tool_schemas = __esm({
|
|
11052
11252
|
"src/ai/tool-schemas.ts"() {
|
|
11053
11253
|
"use strict";
|
|
@@ -11124,6 +11324,64 @@ var init_tool_schemas = __esm({
|
|
|
11124
11324
|
}
|
|
11125
11325
|
}
|
|
11126
11326
|
];
|
|
11327
|
+
THINK_CHANNEL_TOOLS = [
|
|
11328
|
+
{
|
|
11329
|
+
name: "draft_handoff",
|
|
11330
|
+
description: "Draft a handoff prompt combining analysis numbers and conversation thread.",
|
|
11331
|
+
parameters: {
|
|
11332
|
+
type: "object",
|
|
11333
|
+
properties: {
|
|
11334
|
+
target: {
|
|
11335
|
+
type: "string",
|
|
11336
|
+
enum: ["deck", "asana", "clay", "plan"],
|
|
11337
|
+
description: "Deliverable type. Defaults to plan."
|
|
11338
|
+
}
|
|
11339
|
+
}
|
|
11340
|
+
}
|
|
11341
|
+
},
|
|
11342
|
+
{
|
|
11343
|
+
name: "draft_strategy",
|
|
11344
|
+
description: "Hand off to the strategist brain from the think channel when the user is ready to commit to a plan. Do not improvise a multi-week roadmap inline \u2014 call this instead.",
|
|
11345
|
+
parameters: {
|
|
11346
|
+
type: "object",
|
|
11347
|
+
properties: {
|
|
11348
|
+
objective: {
|
|
11349
|
+
type: "string",
|
|
11350
|
+
maxLength: 2e3,
|
|
11351
|
+
description: "The measurable objective to plan toward, in the user's terms."
|
|
11352
|
+
}
|
|
11353
|
+
},
|
|
11354
|
+
required: ["objective"]
|
|
11355
|
+
}
|
|
11356
|
+
},
|
|
11357
|
+
{
|
|
11358
|
+
name: "update_think_scratch",
|
|
11359
|
+
description: "Update the think-channel working scratch: open questions, challenged assumptions, and working hypotheses. Pass only the arrays you want to replace; omitted fields stay unchanged. Call when the conversation advances a question, surfaces a challenged assumption, or forms a hypothesis.",
|
|
11360
|
+
parameters: {
|
|
11361
|
+
type: "object",
|
|
11362
|
+
properties: {
|
|
11363
|
+
open_questions: {
|
|
11364
|
+
type: "array",
|
|
11365
|
+
items: { type: "string", maxLength: 500 },
|
|
11366
|
+
maxItems: 12,
|
|
11367
|
+
description: "Current open questions (replaces the list when provided)."
|
|
11368
|
+
},
|
|
11369
|
+
challenged_assumptions: {
|
|
11370
|
+
type: "array",
|
|
11371
|
+
items: { type: "string", maxLength: 500 },
|
|
11372
|
+
maxItems: 12,
|
|
11373
|
+
description: "Assumptions that have been pressure-tested (replaces when provided)."
|
|
11374
|
+
},
|
|
11375
|
+
working_hypotheses: {
|
|
11376
|
+
type: "array",
|
|
11377
|
+
items: { type: "string", maxLength: 500 },
|
|
11378
|
+
maxItems: 12,
|
|
11379
|
+
description: "Working hypotheses under consideration (replaces when provided)."
|
|
11380
|
+
}
|
|
11381
|
+
}
|
|
11382
|
+
}
|
|
11383
|
+
}
|
|
11384
|
+
];
|
|
11127
11385
|
AGENTIC_TOOLS = [
|
|
11128
11386
|
{
|
|
11129
11387
|
name: "get_health_summary",
|
|
@@ -12892,6 +13150,15 @@ var init_layout = __esm({
|
|
|
12892
13150
|
}
|
|
12893
13151
|
});
|
|
12894
13152
|
|
|
13153
|
+
// src/whimsy/home-taglines.ts
|
|
13154
|
+
var HOME_TAGLINE_WHIMSY_CHANCE;
|
|
13155
|
+
var init_home_taglines = __esm({
|
|
13156
|
+
"src/whimsy/home-taglines.ts"() {
|
|
13157
|
+
"use strict";
|
|
13158
|
+
HOME_TAGLINE_WHIMSY_CHANCE = 1 / 8;
|
|
13159
|
+
}
|
|
13160
|
+
});
|
|
13161
|
+
|
|
12895
13162
|
// src/ui/banner.ts
|
|
12896
13163
|
import chalk4 from "chalk";
|
|
12897
13164
|
var init_banner = __esm({
|
|
@@ -12899,6 +13166,8 @@ var init_banner = __esm({
|
|
|
12899
13166
|
"use strict";
|
|
12900
13167
|
init_theme();
|
|
12901
13168
|
init_layout();
|
|
13169
|
+
init_home_taglines();
|
|
13170
|
+
init_home_taglines();
|
|
12902
13171
|
}
|
|
12903
13172
|
});
|
|
12904
13173
|
|
|
@@ -13241,8 +13510,8 @@ function resolveAskMultiInput(raw, options) {
|
|
|
13241
13510
|
function resolveChooseInput(raw, choices, defaultValue) {
|
|
13242
13511
|
if (choices.length === 0) return null;
|
|
13243
13512
|
const defaultIdx = resolveChooseDefaultIndex(choices, defaultValue);
|
|
13244
|
-
const
|
|
13245
|
-
const n = Number(
|
|
13513
|
+
const pick3 = raw.trim() || String(defaultIdx + 1);
|
|
13514
|
+
const n = Number(pick3);
|
|
13246
13515
|
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
13247
13516
|
return choices[n - 1].value;
|
|
13248
13517
|
}
|
|
@@ -13515,6 +13784,8 @@ function resolveRecommendedAction(ctx) {
|
|
|
13515
13784
|
return { submit: "yes", hint: "yes" };
|
|
13516
13785
|
case "strategize":
|
|
13517
13786
|
return ctx.strategistState?.step === "objective_confirm" ? { submit: "yes", hint: "yes" } : null;
|
|
13787
|
+
case "think":
|
|
13788
|
+
return null;
|
|
13518
13789
|
default:
|
|
13519
13790
|
return null;
|
|
13520
13791
|
}
|
|
@@ -13549,6 +13820,9 @@ function resolveConversationPhase(ctx) {
|
|
|
13549
13820
|
if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis" && ctx.strategistState.step !== "awaiting_connect") {
|
|
13550
13821
|
return "strategize";
|
|
13551
13822
|
}
|
|
13823
|
+
if (ctx.thinkState?.step === "active") {
|
|
13824
|
+
return "think";
|
|
13825
|
+
}
|
|
13552
13826
|
if (isAnalysisReady(ctx)) return "explore";
|
|
13553
13827
|
const scope = ctx.scope;
|
|
13554
13828
|
if (scope?.confirmed_at) {
|
|
@@ -13573,6 +13847,8 @@ function formatPhaseLabel(phase) {
|
|
|
13573
13847
|
return "setup";
|
|
13574
13848
|
case "explore":
|
|
13575
13849
|
return "ready to ask";
|
|
13850
|
+
case "think":
|
|
13851
|
+
return "thinking together";
|
|
13576
13852
|
default:
|
|
13577
13853
|
return phase.replace(/_/g, " ");
|
|
13578
13854
|
}
|
|
@@ -13591,8 +13867,14 @@ function buildConversationPrompt(ctx) {
|
|
|
13591
13867
|
const modeTag = mode === "brief" ? "brief" : "deep";
|
|
13592
13868
|
const stack = canUseReplAi(ctx) ? formatActiveStackShort(ctx) : "no engine";
|
|
13593
13869
|
const strategyWait = ctx.strategistState?.step === "awaiting_connect" ? chalk8.dim(" \xB7 strategy after /connect") : "";
|
|
13870
|
+
const thinkWait = ctx.thinkState?.step === "awaiting_connect" ? chalk8.dim(" \xB7 think after /connect") : "";
|
|
13594
13871
|
const enterHint2 = action ? chalk8.dim(` \xB7 \u23CE ${action.hint}`) : "";
|
|
13595
|
-
return paint("accent", `ask${scope} \u203A `) + chalk8.dim(`${modeTag} \xB7 ${stack}`) + strategyWait + enterHint2 + " ";
|
|
13872
|
+
return paint("accent", `ask${scope} \u203A `) + chalk8.dim(`${modeTag} \xB7 ${stack}`) + strategyWait + thinkWait + enterHint2 + " ";
|
|
13873
|
+
}
|
|
13874
|
+
if (phase === "think") {
|
|
13875
|
+
const stack = canUseReplAi(ctx) ? formatActiveStackShort(ctx) : "no engine";
|
|
13876
|
+
const enterHint2 = action ? chalk8.dim(` \xB7 \u23CE ${action.hint}`) : "";
|
|
13877
|
+
return paint("accent", `think${scope} \u203A `) + chalk8.dim(stack) + enterHint2 + " ";
|
|
13596
13878
|
}
|
|
13597
13879
|
const enterHint = action ? chalk8.dim(`\u23CE ${action.hint} `) : "";
|
|
13598
13880
|
return paint("accent", `${label.replace(" \u203A", "")}${scope} \u203A `) + enterHint;
|
|
@@ -13633,6 +13915,7 @@ var init_phase = __esm({
|
|
|
13633
13915
|
awaiting_data: "data \u203A",
|
|
13634
13916
|
compute: "\u2026",
|
|
13635
13917
|
explore: "ask \u203A",
|
|
13918
|
+
think: "think \u203A",
|
|
13636
13919
|
strategize: "strategy \u203A",
|
|
13637
13920
|
deliver: "ship \u203A"
|
|
13638
13921
|
};
|
|
@@ -13985,14 +14268,14 @@ ${ANALYST_INSTINCT_BLOCK}
|
|
|
13985
14268
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
13986
14269
|
${PYRAMID_OUTPUT_BLOCK}
|
|
13987
14270
|
|
|
13988
|
-
${
|
|
14271
|
+
${buildUserVisibleProseBlock()}
|
|
13989
14272
|
|
|
13990
14273
|
Respond with JSON only \u2014 an array of finding objects:
|
|
13991
14274
|
[
|
|
13992
14275
|
{
|
|
13993
14276
|
"severity": "critical" | "warning" | "info",
|
|
13994
14277
|
"segment": "Overall",
|
|
13995
|
-
"finding":
|
|
14278
|
+
"finding": ${JSON.stringify(buildFindingsFindingHint())},
|
|
13996
14279
|
"vital_signs": {},
|
|
13997
14280
|
"entity_count": 0,
|
|
13998
14281
|
"recommended_focus": "nrr",
|
|
@@ -14070,6 +14353,7 @@ var init_metrics_findings = __esm({
|
|
|
14070
14353
|
init_repl_api();
|
|
14071
14354
|
init_failover();
|
|
14072
14355
|
init_prompt_parts();
|
|
14356
|
+
init_prompt();
|
|
14073
14357
|
init_playbook();
|
|
14074
14358
|
init_metrics_benchmarks();
|
|
14075
14359
|
init_profile();
|
|
@@ -14437,6 +14721,158 @@ var init_slides = __esm({
|
|
|
14437
14721
|
}
|
|
14438
14722
|
});
|
|
14439
14723
|
|
|
14724
|
+
// src/voice/copy.ts
|
|
14725
|
+
function voice(prefs) {
|
|
14726
|
+
return prefs ?? loadVoicePrefs();
|
|
14727
|
+
}
|
|
14728
|
+
function voiceKeylessHeadline(input, prefs) {
|
|
14729
|
+
const v = voice(prefs);
|
|
14730
|
+
const { label, dollarBit, score, status } = input;
|
|
14731
|
+
if (dollarBit) {
|
|
14732
|
+
return pick2(v, {
|
|
14733
|
+
robotic: `${label} \u2014 ${dollarBit} \u2014 is the most expensive problem to solve right now.`,
|
|
14734
|
+
composed: `${label} is the most expensive problem right now: ${dollarBit}.`,
|
|
14735
|
+
casual: `${label} is the expensive one \u2014 ${dollarBit}.`,
|
|
14736
|
+
loose: {
|
|
14737
|
+
light: `${label} is the expensive problem: ${dollarBit}.`,
|
|
14738
|
+
medium: `${label} is eating ${dollarBit}. Fix that first.`,
|
|
14739
|
+
dark: `${label} is eating ${dollarBit}. That is the miss.`,
|
|
14740
|
+
heavy: `${label} is eating ${dollarBit}. That is the fire.`
|
|
14741
|
+
}
|
|
14742
|
+
});
|
|
14743
|
+
}
|
|
14744
|
+
return pick2(v, {
|
|
14745
|
+
robotic: `${label} (score ${Math.round(score)}, ${status}) is the problem to fix first.`,
|
|
14746
|
+
composed: `${label} (score ${Math.round(score)}) is the problem to fix first.`,
|
|
14747
|
+
casual: `Start with ${label} \u2014 score ${Math.round(score)}, ${status}.`,
|
|
14748
|
+
loose: {
|
|
14749
|
+
light: `Start with ${label} (score ${Math.round(score)}).`,
|
|
14750
|
+
medium: `${label} at ${Math.round(score)} is the one to fix first.`,
|
|
14751
|
+
dark: `${label} at ${Math.round(score)} is the miss.`,
|
|
14752
|
+
heavy: `${label} at ${Math.round(score)} is the joke, and not the funny kind.`
|
|
14753
|
+
}
|
|
14754
|
+
});
|
|
14755
|
+
}
|
|
14756
|
+
function voiceKeylessGating(_label, _score, prefs) {
|
|
14757
|
+
const v = voice(prefs);
|
|
14758
|
+
return pick2(v, {
|
|
14759
|
+
robotic: `it bounds what you can trust downstream.`,
|
|
14760
|
+
composed: `it bounds what you can trust downstream.`,
|
|
14761
|
+
casual: `nothing downstream is trustworthy until this moves.`,
|
|
14762
|
+
loose: {
|
|
14763
|
+
light: `it caps what you can trust downstream.`,
|
|
14764
|
+
medium: `fix this before you trust anything downstream.`,
|
|
14765
|
+
dark: `everything downstream is a story until this moves.`,
|
|
14766
|
+
heavy: `everything downstream is fan fiction until this moves.`
|
|
14767
|
+
}
|
|
14768
|
+
});
|
|
14769
|
+
}
|
|
14770
|
+
function voiceKeylessGatingWrap(label, score, prefs) {
|
|
14771
|
+
const clause = voiceKeylessGating(label, score, prefs);
|
|
14772
|
+
return `(score ${Math.round(score)}) \u2014 ${clause}`;
|
|
14773
|
+
}
|
|
14774
|
+
function voiceKeylessAlsoLabel(prefs) {
|
|
14775
|
+
const v = voice(prefs);
|
|
14776
|
+
return pick2(v, {
|
|
14777
|
+
robotic: "Also on the board:",
|
|
14778
|
+
composed: "Also on the board:",
|
|
14779
|
+
casual: "Also in play:",
|
|
14780
|
+
loose: {
|
|
14781
|
+
light: "Also in play:",
|
|
14782
|
+
medium: "Also costing you:",
|
|
14783
|
+
dark: "Also on fire:",
|
|
14784
|
+
heavy: "Also embarrassing:"
|
|
14785
|
+
}
|
|
14786
|
+
});
|
|
14787
|
+
}
|
|
14788
|
+
function voiceKeylessNextLabel(prefs) {
|
|
14789
|
+
const v = voice(prefs);
|
|
14790
|
+
return pick2(v, {
|
|
14791
|
+
robotic: "Next after that: ",
|
|
14792
|
+
composed: "Next after that: ",
|
|
14793
|
+
casual: "Then: ",
|
|
14794
|
+
loose: "Then: "
|
|
14795
|
+
});
|
|
14796
|
+
}
|
|
14797
|
+
function voiceKeylessConnectCta(prefs) {
|
|
14798
|
+
const v = voice(prefs);
|
|
14799
|
+
return pick2(v, {
|
|
14800
|
+
robotic: `Press \u23CE to connect a key (/connect) for the why and the plan. NTRP will finish this question after you connect.`,
|
|
14801
|
+
composed: `Press \u23CE to connect a key (/connect) for the why and the plan. NTRP will finish this question after you connect.`,
|
|
14802
|
+
casual: `Press \u23CE or type /connect for the why and the plan. NTRP will finish this question after you connect.`,
|
|
14803
|
+
loose: `Press \u23CE or type /connect when you want the why. NTRP will finish this question after you connect.`
|
|
14804
|
+
});
|
|
14805
|
+
}
|
|
14806
|
+
function voiceGapReadyCta(prefs) {
|
|
14807
|
+
const v = voice(prefs);
|
|
14808
|
+
return pick2(v, {
|
|
14809
|
+
robotic: `Ready to compute. Press \u23CE or type "go ahead"`,
|
|
14810
|
+
composed: `Ready to compute. Press \u23CE or type "go ahead"`,
|
|
14811
|
+
casual: `Ready. Press \u23CE or type "go ahead"`,
|
|
14812
|
+
loose: `Ready. Press \u23CE or type "go ahead"`
|
|
14813
|
+
});
|
|
14814
|
+
}
|
|
14815
|
+
function voiceGapLoadCta(prefs) {
|
|
14816
|
+
const v = voice(prefs);
|
|
14817
|
+
return pick2(v, {
|
|
14818
|
+
robotic: `Load data. Paste a CSV path, or press \u23CE to use demo data`,
|
|
14819
|
+
composed: `Load data. Paste a CSV path, or press \u23CE to use demo data`,
|
|
14820
|
+
casual: `Load data \u2014 paste a CSV, or press \u23CE to use demo data`,
|
|
14821
|
+
loose: `No data yet. Paste a CSV, or press \u23CE to use demo data`
|
|
14822
|
+
});
|
|
14823
|
+
}
|
|
14824
|
+
function voiceHealthGloss(status, prefs) {
|
|
14825
|
+
const v = voice(prefs);
|
|
14826
|
+
if (status === "green") {
|
|
14827
|
+
return v.personality === "robotic" ? null : "holding";
|
|
14828
|
+
}
|
|
14829
|
+
if (status === "yellow") {
|
|
14830
|
+
return pick2(v, {
|
|
14831
|
+
robotic: "watch this",
|
|
14832
|
+
composed: "watch this",
|
|
14833
|
+
casual: "soft",
|
|
14834
|
+
loose: {
|
|
14835
|
+
light: "watch this",
|
|
14836
|
+
medium: "soft",
|
|
14837
|
+
dark: "thin ice",
|
|
14838
|
+
heavy: "limping"
|
|
14839
|
+
}
|
|
14840
|
+
});
|
|
14841
|
+
}
|
|
14842
|
+
return pick2(v, {
|
|
14843
|
+
robotic: "needs attention",
|
|
14844
|
+
composed: "needs attention",
|
|
14845
|
+
casual: "the leak",
|
|
14846
|
+
loose: {
|
|
14847
|
+
light: "needs attention",
|
|
14848
|
+
medium: "the leak",
|
|
14849
|
+
dark: "the miss",
|
|
14850
|
+
heavy: "this is the fire"
|
|
14851
|
+
}
|
|
14852
|
+
});
|
|
14853
|
+
}
|
|
14854
|
+
function voiceHandoffAddendum(prefs) {
|
|
14855
|
+
const v = voice(prefs);
|
|
14856
|
+
return [
|
|
14857
|
+
`Write in this voice: personality=${v.personality}, roast=${v.roast} (${formatVoiceStatusDetail(v)}).`,
|
|
14858
|
+
"Ground every recommendation in the specific numbers provided. Do not invent data.",
|
|
14859
|
+
v.personality === "robotic" ? "STE-100 for the prose you produce. No contractions." : "Stay smart and succinct. Personality in the wrap, not in the math.",
|
|
14860
|
+
v.roast === "light" ? "Light roast: name what is working, then what to improve." : v.roast === "medium" ? "Medium roast: name the miss. No padding." : v.roast === "dark" ? "Dark roast: surgical. Verdict and dollar in the same breath." : "Heavy roast: maximum roast after the number, never instead of it. Green stays green."
|
|
14861
|
+
].join(" ");
|
|
14862
|
+
}
|
|
14863
|
+
function pick2(prefs, map) {
|
|
14864
|
+
const entry = map[prefs.personality];
|
|
14865
|
+
if (typeof entry === "string") return entry;
|
|
14866
|
+
return entry[prefs.roast];
|
|
14867
|
+
}
|
|
14868
|
+
var init_copy = __esm({
|
|
14869
|
+
"src/voice/copy.ts"() {
|
|
14870
|
+
"use strict";
|
|
14871
|
+
init_catalog2();
|
|
14872
|
+
init_prefs();
|
|
14873
|
+
}
|
|
14874
|
+
});
|
|
14875
|
+
|
|
14440
14876
|
// src/output/terminal.ts
|
|
14441
14877
|
import chalk13 from "chalk";
|
|
14442
14878
|
import Table2 from "cli-table3";
|
|
@@ -14484,9 +14920,10 @@ function printVitalSignRow(vs) {
|
|
|
14484
14920
|
function printHealthSummary(result, _pipelineMetrics) {
|
|
14485
14921
|
const scoreStr = `${chalk13.bold(String(Math.round(result.overall_score)))}${chalk13.dim("/100")}`;
|
|
14486
14922
|
const impact = result.total_value_at_risk != null && result.total_value_at_risk > 0 ? `${paint("success", formatCurrency(result.total_value_at_risk))} ${chalk13.dim("total at risk")}` : chalk13.dim("No dollar-weighted risk detected");
|
|
14923
|
+
const gloss = voiceHealthGloss(result.overall_status);
|
|
14487
14924
|
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");
|
|
14488
14925
|
printResultCard("Overall Health", [
|
|
14489
|
-
`${chalk13.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}`,
|
|
14926
|
+
`${chalk13.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}${gloss ? chalk13.dim(` ${gloss}`) : ""}`,
|
|
14490
14927
|
`${chalk13.dim("Held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`,
|
|
14491
14928
|
`${chalk13.dim("Revenue")} ${impact}`,
|
|
14492
14929
|
next
|
|
@@ -14498,8 +14935,9 @@ function printHealthSummary(result, _pipelineMetrics) {
|
|
|
14498
14935
|
}
|
|
14499
14936
|
function printHealthLine(result) {
|
|
14500
14937
|
const score = `${chalk13.bold(String(Math.round(result.overall_score)))}${chalk13.dim("/100")}`;
|
|
14938
|
+
const gloss = voiceHealthGloss(result.overall_status);
|
|
14501
14939
|
const parts = [
|
|
14502
|
-
`${chalk13.dim("Health")} ${score} ${statusBadge(result.overall_status)}`,
|
|
14940
|
+
`${chalk13.dim("Health")} ${score} ${statusBadge(result.overall_status)}${gloss ? chalk13.dim(` ${gloss}`) : ""}`,
|
|
14503
14941
|
`${chalk13.dim("held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`
|
|
14504
14942
|
];
|
|
14505
14943
|
if (result.total_value_at_risk != null && result.total_value_at_risk > 0) {
|
|
@@ -14840,6 +15278,7 @@ var init_terminal = __esm({
|
|
|
14840
15278
|
init_layout();
|
|
14841
15279
|
init_llm_attribution();
|
|
14842
15280
|
init_slides();
|
|
15281
|
+
init_copy();
|
|
14843
15282
|
}
|
|
14844
15283
|
});
|
|
14845
15284
|
|
|
@@ -16514,6 +16953,8 @@ function wrapForTarget(target, analysisBlock, conversationBlock, openQuestions,
|
|
|
16514
16953
|
"",
|
|
16515
16954
|
"Ground every recommendation in the specific numbers provided. Do not invent data.",
|
|
16516
16955
|
"",
|
|
16956
|
+
voiceHandoffAddendum(),
|
|
16957
|
+
"",
|
|
16517
16958
|
"---",
|
|
16518
16959
|
"",
|
|
16519
16960
|
analysisBlock,
|
|
@@ -16572,6 +17013,7 @@ var init_handoff_draft = __esm({
|
|
|
16572
17013
|
init_profile();
|
|
16573
17014
|
init_session_analysis();
|
|
16574
17015
|
init_metric_explainers();
|
|
17016
|
+
init_copy();
|
|
16575
17017
|
QUESTION_LEAD_RE = /^\s*(what|why|how|when|where|who|which|is|are|was|were|do|does|did|explain|tell me|help me understand)\b/i;
|
|
16576
17018
|
SHIP_INTENT_RE = /\b(ship|export|deliver|write[- ]?up|board memo|action plan|turn (this|it|that) into|(draft|create|make|build|prepare|generate|send)\s+(me\s+)?(a\s+|the\s+)?hand[- ]?off|hand[- ]?off\s+(prompt|doc|document|plan))\b/i;
|
|
16577
17019
|
}
|
|
@@ -16901,7 +17343,7 @@ ALTITUDE CONTRACT (the plan must work at every altitude a client reads it at):
|
|
|
16901
17343
|
- 10,000 FT: each workstream's title + problem line is a delegation unit \u2014 a one-liner an owner could receive in Slack and know what they own, why it's theirs, and what number they move.
|
|
16902
17344
|
- GROUND LEVEL: actions are Monday-morning prescriptive. You have built this before \u2014 read the linked play's full detail with get_play_detail and prescribe its known-good sequence adapted to THIS company's numbers and constraints. The first action of every workstream must be startable within 48 hours with no new tooling.
|
|
16903
17345
|
|
|
16904
|
-
${
|
|
17346
|
+
${buildUserVisibleProseBlock()}
|
|
16905
17347
|
|
|
16906
17348
|
GTM ENGINEERING (plans install systems, not heroics):
|
|
16907
17349
|
${GTM_ENGINEERING_BLOCK}
|
|
@@ -16991,6 +17433,7 @@ var init_strategist_prompt = __esm({
|
|
|
16991
17433
|
"src/ai/strategist-prompt.ts"() {
|
|
16992
17434
|
"use strict";
|
|
16993
17435
|
init_prompt_parts();
|
|
17436
|
+
init_prompt();
|
|
16994
17437
|
STRATEGIST_PLAN_SCHEMA_BLOCK = `{
|
|
16995
17438
|
"title": "Short plan name, e.g. 'Q4 Pipeline Recovery'",
|
|
16996
17439
|
"objective": "The measurable destination, restated precisely",
|
|
@@ -18304,45 +18747,372 @@ var init_strategist_flow = __esm({
|
|
|
18304
18747
|
}
|
|
18305
18748
|
});
|
|
18306
18749
|
|
|
18307
|
-
// src/
|
|
18750
|
+
// src/services/think.ts
|
|
18751
|
+
var think_exports = {};
|
|
18752
|
+
__export(think_exports, {
|
|
18753
|
+
runThinkTurn: () => runThinkTurn
|
|
18754
|
+
});
|
|
18308
18755
|
import chalk18 from "chalk";
|
|
18756
|
+
async function runThinkTurn(input, ctx) {
|
|
18757
|
+
assertReplAi(ctx);
|
|
18758
|
+
let snapshot = ctx.snapshot.computeResult;
|
|
18759
|
+
if (!snapshot) {
|
|
18760
|
+
const metricsFirst = prefersMetricsFirstContext(ctx);
|
|
18761
|
+
const spinner2 = makeSpinner(metricsFirst ? "Loading session context\u2026" : "Computing vital signs\u2026");
|
|
18762
|
+
try {
|
|
18763
|
+
snapshot = await computeFullHealth();
|
|
18764
|
+
ctx.snapshot.computeResult = snapshot;
|
|
18765
|
+
const divInput = snapshot.segments.map((s) => ({
|
|
18766
|
+
segmentId: s.segment.id,
|
|
18767
|
+
segmentName: s.segment.name,
|
|
18768
|
+
result: s.result
|
|
18769
|
+
}));
|
|
18770
|
+
ctx.snapshot.divergences = detectDivergences(snapshot.aggregate, divInput).divergences;
|
|
18771
|
+
spinner2.succeed(metricsFirst ? "Session context ready" : "Health snapshot ready");
|
|
18772
|
+
} catch (err) {
|
|
18773
|
+
spinner2.fail("Could not compute health snapshot");
|
|
18774
|
+
console.error(" " + chalk18.red(String(err.message ?? err)));
|
|
18775
|
+
console.log(" " + chalk18.dim("Run ") + paint("accent", "/new") + chalk18.dim(" \u2192 pick Demo to load sample data."));
|
|
18776
|
+
console.log();
|
|
18777
|
+
return;
|
|
18778
|
+
}
|
|
18779
|
+
}
|
|
18780
|
+
console.log();
|
|
18781
|
+
const memoryBlock = await buildMemoryBlock(input).catch(() => "");
|
|
18782
|
+
const spinner = makeSpinner("Thinking with you\u2026");
|
|
18783
|
+
let lastAnswer = "";
|
|
18784
|
+
let rawHistory = [];
|
|
18785
|
+
const toolsUsed = [];
|
|
18786
|
+
setAgentContext(ctx);
|
|
18787
|
+
try {
|
|
18788
|
+
const analysisBlock = buildAnalysisBlock(ctx);
|
|
18789
|
+
const conversationBlock = getConversationPhaseBlock(ctx);
|
|
18790
|
+
const bundle = await loadSessionAnalysisBundle();
|
|
18791
|
+
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
18792
|
+
for await (const event of agenticFindings(snapshot, ctx.snapshot.divergences, {
|
|
18793
|
+
mode: "think",
|
|
18794
|
+
userQuestion: input,
|
|
18795
|
+
sessionContext: ctx.resumedSessionSummary,
|
|
18796
|
+
includeMetrics: true,
|
|
18797
|
+
analysisBlock,
|
|
18798
|
+
conversationBlock,
|
|
18799
|
+
sessionArtifact,
|
|
18800
|
+
priorMessages: ctx.thinkConversation,
|
|
18801
|
+
memoryBlock,
|
|
18802
|
+
ctx
|
|
18803
|
+
})) {
|
|
18804
|
+
switch (event.type) {
|
|
18805
|
+
case "tool_call":
|
|
18806
|
+
toolsUsed.push(event.name);
|
|
18807
|
+
spinner.text = `Querying ${event.name}\u2026`;
|
|
18808
|
+
break;
|
|
18809
|
+
case "thinking":
|
|
18810
|
+
spinner.stop();
|
|
18811
|
+
console.log(" " + chalk18.dim.italic(event.text));
|
|
18812
|
+
spinner.start("Thinking with you\u2026");
|
|
18813
|
+
break;
|
|
18814
|
+
case "answer":
|
|
18815
|
+
spinner.stop();
|
|
18816
|
+
lastAnswer = event.text;
|
|
18817
|
+
printMarkdown(event.text, { indent: 2 });
|
|
18818
|
+
break;
|
|
18819
|
+
case "finding":
|
|
18820
|
+
spinner.stop();
|
|
18821
|
+
printFindingInline(event.finding);
|
|
18822
|
+
break;
|
|
18823
|
+
case "done":
|
|
18824
|
+
spinner.stop();
|
|
18825
|
+
rawHistory = event.conversation_history;
|
|
18826
|
+
break;
|
|
18827
|
+
}
|
|
18828
|
+
}
|
|
18829
|
+
} catch (err) {
|
|
18830
|
+
spinner.fail("Error while thinking");
|
|
18831
|
+
console.error(" " + chalk18.red(String(err.message ?? err)));
|
|
18832
|
+
console.log();
|
|
18833
|
+
return;
|
|
18834
|
+
} finally {
|
|
18835
|
+
setAgentContext(null);
|
|
18836
|
+
}
|
|
18837
|
+
if (rawHistory.length > 0) {
|
|
18838
|
+
ctx.thinkConversation = distillThread(rawHistory);
|
|
18839
|
+
}
|
|
18840
|
+
if (!lastAnswer) {
|
|
18841
|
+
console.log(" " + chalk18.dim("(no answer returned)"));
|
|
18842
|
+
} else {
|
|
18843
|
+
recordMessage(ctx, "agent", lastAnswer);
|
|
18844
|
+
saveSessionState(ctx);
|
|
18845
|
+
creditNlAnswer(ctx, Math.floor(ctx.messages.length / 2));
|
|
18846
|
+
recordAnalysis({
|
|
18847
|
+
question: `[think] ${input}`,
|
|
18848
|
+
answer: lastAnswer,
|
|
18849
|
+
tools: toolsUsed,
|
|
18850
|
+
session_id: ctx.sessionId
|
|
18851
|
+
});
|
|
18852
|
+
ctx.lastExchange = { question: input, answer: lastAnswer };
|
|
18853
|
+
}
|
|
18854
|
+
console.log();
|
|
18855
|
+
return lastAnswer ? extractSummary(lastAnswer) : void 0;
|
|
18856
|
+
}
|
|
18857
|
+
function extractSummary(text) {
|
|
18858
|
+
const plain = text.replace(/#{1,6}\s+/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^[-*]\s+/gm, "").trim();
|
|
18859
|
+
const sentenceMatch = plain.match(/^(.+?[.!?])(?:\s|$)/);
|
|
18860
|
+
const sentence = sentenceMatch ? sentenceMatch[1] : plain.split("\n")[0];
|
|
18861
|
+
if (sentence.length <= 60) return sentence;
|
|
18862
|
+
return sentence.slice(0, 60).replace(/\s+\S*$/, "") + "\u2026";
|
|
18863
|
+
}
|
|
18864
|
+
function printFindingInline(finding) {
|
|
18865
|
+
const sev = finding.severity;
|
|
18866
|
+
console.log();
|
|
18867
|
+
console.log(
|
|
18868
|
+
" " + severityPaint(sev)(sev.toUpperCase()) + " " + (finding.finding ?? "").slice(0, 120)
|
|
18869
|
+
);
|
|
18870
|
+
}
|
|
18871
|
+
var init_think = __esm({
|
|
18872
|
+
"src/services/think.ts"() {
|
|
18873
|
+
"use strict";
|
|
18874
|
+
init_spinner();
|
|
18875
|
+
init_context2();
|
|
18876
|
+
init_phase();
|
|
18877
|
+
init_agent_context();
|
|
18878
|
+
init_agentic_loop();
|
|
18879
|
+
init_thread();
|
|
18880
|
+
init_store2();
|
|
18881
|
+
init_health_score();
|
|
18882
|
+
init_divergence();
|
|
18883
|
+
init_repl_api();
|
|
18884
|
+
init_theme();
|
|
18885
|
+
init_markdown();
|
|
18886
|
+
init_session_analysis();
|
|
18887
|
+
init_time_bank();
|
|
18888
|
+
}
|
|
18889
|
+
});
|
|
18890
|
+
|
|
18891
|
+
// src/conversation/think-flow.ts
|
|
18892
|
+
var think_flow_exports = {};
|
|
18893
|
+
__export(think_flow_exports, {
|
|
18894
|
+
clearThinkFlow: () => clearThinkFlow,
|
|
18895
|
+
extractThinkSeed: () => extractThinkSeed,
|
|
18896
|
+
handleThinkFlow: () => handleThinkFlow,
|
|
18897
|
+
isThinkIntent: () => isThinkIntent,
|
|
18898
|
+
queueThinkForAnalysis: () => queueThinkForAnalysis,
|
|
18899
|
+
resumeThinkAfterCompute: () => resumeThinkAfterCompute,
|
|
18900
|
+
resumeThinkAfterConnect: () => resumeThinkAfterConnect,
|
|
18901
|
+
startThinkFlow: () => startThinkFlow
|
|
18902
|
+
});
|
|
18903
|
+
import chalk19 from "chalk";
|
|
18904
|
+
function isThinkIntent(input) {
|
|
18905
|
+
const line = input.trim();
|
|
18906
|
+
if (!line) return false;
|
|
18907
|
+
if (isShipIntent(line)) return false;
|
|
18908
|
+
if (isStrategistIntent(line)) return false;
|
|
18909
|
+
return THINK_INTENT_RE.test(line);
|
|
18910
|
+
}
|
|
18911
|
+
function extractThinkSeed(input) {
|
|
18912
|
+
const cleaned = input.trim().replace(/^(hey|ok|okay|please|can you|could you|help me|let'?s|i want to|i'?d like to)\s+/i, "").replace(/^(think\s+with\s+me|pressure[- ]?test|dig\s+into|think\s+through)\s*(about|on|around|this|:)?\s*/i, "").replace(/^(what\s+am\s+i\s+missing\s*(about|on|with|here)?)\s*/i, "").trim();
|
|
18913
|
+
return cleaned.length >= 4 ? cleaned : input.trim();
|
|
18914
|
+
}
|
|
18915
|
+
function queueThinkForAnalysis(ctx, opts) {
|
|
18916
|
+
ctx.thinkState = {
|
|
18917
|
+
step: "awaiting_analysis",
|
|
18918
|
+
seed: opts.seed,
|
|
18919
|
+
origin: opts.origin,
|
|
18920
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
18921
|
+
};
|
|
18922
|
+
saveSessionState(ctx);
|
|
18923
|
+
console.log();
|
|
18924
|
+
console.log(
|
|
18925
|
+
" " + chalk19.dim("Think session queued. NTRP opens the channel after analysis.")
|
|
18926
|
+
);
|
|
18927
|
+
if (opts.origin !== "nl") {
|
|
18928
|
+
console.log(
|
|
18929
|
+
" " + chalk19.dim("Type what to look at. Paste a CSV path. Or type ") + chalk19.cyan("use demo data") + chalk19.dim(".")
|
|
18930
|
+
);
|
|
18931
|
+
}
|
|
18932
|
+
console.log();
|
|
18933
|
+
}
|
|
18934
|
+
function printChannelIntro(seed) {
|
|
18935
|
+
console.log();
|
|
18936
|
+
console.log(" " + paint("accent", "Think with me"));
|
|
18937
|
+
console.log(
|
|
18938
|
+
" " + chalk19.dim(
|
|
18939
|
+
"Socratic channel \u2014 explore, challenge assumptions, pull evidence. Type "
|
|
18940
|
+
) + chalk19.cyan("done") + chalk19.dim(" or ") + chalk19.cyan("cancel") + chalk19.dim(" to return to ask \u203A.")
|
|
18941
|
+
);
|
|
18942
|
+
if (seed) {
|
|
18943
|
+
console.log(" " + chalk19.dim("Seed: ") + seed);
|
|
18944
|
+
}
|
|
18945
|
+
console.log();
|
|
18946
|
+
}
|
|
18947
|
+
function armKeylessThink(ctx, opts) {
|
|
18948
|
+
ctx.thinkState = {
|
|
18949
|
+
step: "awaiting_connect",
|
|
18950
|
+
seed: opts.seed,
|
|
18951
|
+
origin: opts.origin,
|
|
18952
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
18953
|
+
};
|
|
18954
|
+
saveSessionState(ctx);
|
|
18955
|
+
console.log();
|
|
18956
|
+
console.log(" " + chalk19.dim("Think channel needs an AI key."));
|
|
18957
|
+
console.log(
|
|
18958
|
+
" " + chalk19.dim("Type ") + paint("accent", "/connect") + chalk19.dim(" and paste a key. Seed kept \u2014 the channel opens after connect.")
|
|
18959
|
+
);
|
|
18960
|
+
console.log();
|
|
18961
|
+
}
|
|
18962
|
+
async function startThinkFlow(ctx, opts) {
|
|
18963
|
+
if (!isAnalysisReady(ctx)) {
|
|
18964
|
+
queueThinkForAnalysis(ctx, opts);
|
|
18965
|
+
return "Think queued";
|
|
18966
|
+
}
|
|
18967
|
+
if (!canUseReplAi(ctx)) {
|
|
18968
|
+
armKeylessThink(ctx, opts);
|
|
18969
|
+
return "Think awaiting connect";
|
|
18970
|
+
}
|
|
18971
|
+
const seed = opts.seed?.trim() || void 0;
|
|
18972
|
+
ctx.thinkState = {
|
|
18973
|
+
step: "active",
|
|
18974
|
+
seed,
|
|
18975
|
+
origin: opts.origin,
|
|
18976
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18977
|
+
open_questions: [],
|
|
18978
|
+
challenged_assumptions: [],
|
|
18979
|
+
working_hypotheses: []
|
|
18980
|
+
};
|
|
18981
|
+
if (ctx.thinkConversation.length === 0 && seed) {
|
|
18982
|
+
}
|
|
18983
|
+
saveSessionState(ctx);
|
|
18984
|
+
printChannelIntro(seed);
|
|
18985
|
+
recordMessage(ctx, "agent", seed ? `Think channel opened: ${seed}` : "Think channel opened");
|
|
18986
|
+
if (seed) {
|
|
18987
|
+
recordMessage(ctx, "user", seed);
|
|
18988
|
+
const { runThinkTurn: runThinkTurn2 } = await Promise.resolve().then(() => (init_think(), think_exports));
|
|
18989
|
+
await runThinkTurn2(seed, ctx);
|
|
18990
|
+
}
|
|
18991
|
+
return "Think channel open";
|
|
18992
|
+
}
|
|
18993
|
+
async function resumeThinkAfterCompute(ctx) {
|
|
18994
|
+
const state2 = ctx.thinkState;
|
|
18995
|
+
if (!state2 || state2.step !== "awaiting_analysis") return;
|
|
18996
|
+
if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis" && ctx.strategistState.step !== "awaiting_connect") {
|
|
18997
|
+
return;
|
|
18998
|
+
}
|
|
18999
|
+
if (ctx.strategistState?.step === "awaiting_analysis") return;
|
|
19000
|
+
console.log();
|
|
19001
|
+
console.log(" " + paint("accent", "Analysis is ready. The think channel continues."));
|
|
19002
|
+
await startThinkFlow(ctx, {
|
|
19003
|
+
seed: state2.seed,
|
|
19004
|
+
origin: state2.origin ?? "nl"
|
|
19005
|
+
});
|
|
19006
|
+
}
|
|
19007
|
+
async function resumeThinkAfterConnect(ctx) {
|
|
19008
|
+
const state2 = ctx.thinkState;
|
|
19009
|
+
if (!state2 || state2.step !== "awaiting_connect") return false;
|
|
19010
|
+
if (!canUseReplAi(ctx)) return false;
|
|
19011
|
+
console.log();
|
|
19012
|
+
console.log(" " + paint("accent", "Key connected. Opening the think channel."));
|
|
19013
|
+
await startThinkFlow(ctx, {
|
|
19014
|
+
seed: state2.seed,
|
|
19015
|
+
origin: state2.origin ?? "nl"
|
|
19016
|
+
});
|
|
19017
|
+
return true;
|
|
19018
|
+
}
|
|
19019
|
+
function clearThinkFlow(ctx, reason) {
|
|
19020
|
+
ctx.thinkState = void 0;
|
|
19021
|
+
saveSessionState(ctx);
|
|
19022
|
+
if (reason === "handoff") return;
|
|
19023
|
+
console.log();
|
|
19024
|
+
console.log(
|
|
19025
|
+
" " + chalk19.dim(
|
|
19026
|
+
reason === "done" ? "Think channel closed. Back to ask \u203A." : "Think session cancelled. Continue exploration."
|
|
19027
|
+
)
|
|
19028
|
+
);
|
|
19029
|
+
console.log();
|
|
19030
|
+
}
|
|
19031
|
+
async function handleThinkFlow(input, ctx) {
|
|
19032
|
+
const state2 = ctx.thinkState;
|
|
19033
|
+
if (!state2 || state2.step !== "active") return;
|
|
19034
|
+
const line = input.trim();
|
|
19035
|
+
recordMessage(ctx, "user", line);
|
|
19036
|
+
if (CANCEL_RE2.test(line) || DONE_RE.test(line)) {
|
|
19037
|
+
clearThinkFlow(ctx, CANCEL_RE2.test(line) ? "cancel" : "done");
|
|
19038
|
+
recordMessage(ctx, "agent", "Think channel closed");
|
|
19039
|
+
return "Think closed";
|
|
19040
|
+
}
|
|
19041
|
+
if (isStrategistIntent(line)) {
|
|
19042
|
+
clearThinkFlow(ctx, "handoff");
|
|
19043
|
+
const summary = await startStrategistFlow(ctx, {
|
|
19044
|
+
seed: extractObjectiveSeed(line),
|
|
19045
|
+
origin: "nl"
|
|
19046
|
+
}) ?? void 0;
|
|
19047
|
+
return summary ?? "Handed off to strategist";
|
|
19048
|
+
}
|
|
19049
|
+
if (!canUseReplAi(ctx)) {
|
|
19050
|
+
armKeylessThink(ctx, { seed: state2.seed ?? line, origin: state2.origin ?? "nl" });
|
|
19051
|
+
return "Think awaiting connect";
|
|
19052
|
+
}
|
|
19053
|
+
const { runThinkTurn: runThinkTurn2 } = await Promise.resolve().then(() => (init_think(), think_exports));
|
|
19054
|
+
await runThinkTurn2(line, ctx);
|
|
19055
|
+
if (ctx.strategistState && ctx.strategistState.step !== "awaiting_analysis" && ctx.strategistState.step !== "awaiting_connect") {
|
|
19056
|
+
clearThinkFlow(ctx, "handoff");
|
|
19057
|
+
const { promptQueuedAiStrategist: promptQueuedAiStrategist2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
19058
|
+
promptQueuedAiStrategist2(ctx);
|
|
19059
|
+
}
|
|
19060
|
+
return "Think turn";
|
|
19061
|
+
}
|
|
19062
|
+
var THINK_INTENT_RE, CANCEL_RE2, DONE_RE;
|
|
19063
|
+
var init_think_flow = __esm({
|
|
19064
|
+
"src/conversation/think-flow.ts"() {
|
|
19065
|
+
"use strict";
|
|
19066
|
+
init_context2();
|
|
19067
|
+
init_repl_api();
|
|
19068
|
+
init_theme();
|
|
19069
|
+
init_handoff_draft();
|
|
19070
|
+
init_strategist_flow();
|
|
19071
|
+
THINK_INTENT_RE = /\b(think\s+with\s+me|pressure[- ]?test|what\s+am\s+i\s+missing|challenge\s+(my\s+)?(assumption|thinking|hypothesis)|let'?s\s+(dig|explore|think|pressure)|dig\s+into|steelman|devil'?s\s+advocate|think\s+through|brainstorm\s+(with\s+me|this)|socratic)\b/i;
|
|
19072
|
+
CANCEL_RE2 = /^(cancel|stop|quit|abort|never\s?mind|nevermind|forget it)\s*[.!]?\s*$/i;
|
|
19073
|
+
DONE_RE = /^(done|enough|enough for now|that'?s enough|leave|exit think)\s*[.!]?\s*$/i;
|
|
19074
|
+
}
|
|
19075
|
+
});
|
|
19076
|
+
|
|
19077
|
+
// src/conversation/gap-card.ts
|
|
19078
|
+
import chalk20 from "chalk";
|
|
18309
19079
|
function printGapCard(audit, opts = {}) {
|
|
18310
19080
|
console.log();
|
|
18311
19081
|
if (opts.skipSatisfied) {
|
|
18312
19082
|
if (audit.missing.length > 0) {
|
|
18313
|
-
console.log(" " +
|
|
19083
|
+
console.log(" " + chalk20.bold("Data check"));
|
|
18314
19084
|
}
|
|
18315
19085
|
} else if (audit.satisfied.length > 0) {
|
|
18316
19086
|
const bits = audit.satisfied.map((item) => item.detail);
|
|
18317
19087
|
console.log(
|
|
18318
|
-
" " +
|
|
19088
|
+
" " + chalk20.green("\u2713") + " " + chalk20.bold("Data check") + chalk20.dim(" \u2014 " + bits.join(" \xB7 "))
|
|
18319
19089
|
);
|
|
18320
19090
|
} else {
|
|
18321
|
-
console.log(" " +
|
|
19091
|
+
console.log(" " + chalk20.bold("Data check"));
|
|
18322
19092
|
}
|
|
18323
19093
|
for (const item of audit.missing) {
|
|
18324
|
-
console.log(" " +
|
|
18325
|
-
console.log(" " +
|
|
19094
|
+
console.log(" " + chalk20.red("\u2717") + " " + item.label + chalk20.dim(` \u2014 ${item.why}`));
|
|
19095
|
+
console.log(" " + chalk20.dim(item.suggestion));
|
|
18326
19096
|
}
|
|
18327
19097
|
if (audit.optional.length > 0) {
|
|
18328
19098
|
const heads = audit.optional.map((item) => item.detail.split(" \u2014 ")[0] ?? item.detail);
|
|
18329
19099
|
const joined = heads.join(" \xB7 ");
|
|
18330
19100
|
if (joined.length <= 100) {
|
|
18331
|
-
console.log(" " +
|
|
19101
|
+
console.log(" " + chalk20.yellow("~") + " " + chalk20.dim(joined));
|
|
18332
19102
|
} else {
|
|
18333
19103
|
for (const item of audit.optional) {
|
|
18334
|
-
console.log(" " +
|
|
19104
|
+
console.log(" " + chalk20.yellow("~") + " " + chalk20.dim(`${item.label}: ${item.detail}`));
|
|
18335
19105
|
}
|
|
18336
19106
|
}
|
|
18337
19107
|
}
|
|
18338
19108
|
console.log();
|
|
18339
19109
|
if (audit.can_compute) {
|
|
18340
19110
|
console.log(
|
|
18341
|
-
" " +
|
|
19111
|
+
" " + chalk20.dim(voiceGapReadyCta())
|
|
18342
19112
|
);
|
|
18343
19113
|
} else if (audit.missing.length > 0) {
|
|
18344
19114
|
console.log(
|
|
18345
|
-
" " +
|
|
19115
|
+
" " + chalk20.dim(voiceGapLoadCta())
|
|
18346
19116
|
);
|
|
18347
19117
|
}
|
|
18348
19118
|
console.log();
|
|
@@ -18350,6 +19120,7 @@ function printGapCard(audit, opts = {}) {
|
|
|
18350
19120
|
var init_gap_card = __esm({
|
|
18351
19121
|
"src/conversation/gap-card.ts"() {
|
|
18352
19122
|
"use strict";
|
|
19123
|
+
init_copy();
|
|
18353
19124
|
}
|
|
18354
19125
|
});
|
|
18355
19126
|
|
|
@@ -18359,7 +19130,7 @@ __export(keyless_ask_exports, {
|
|
|
18359
19130
|
isKeylessVitalsAsk: () => isKeylessVitalsAsk,
|
|
18360
19131
|
tryKeylessAskAnswer: () => tryKeylessAskAnswer
|
|
18361
19132
|
});
|
|
18362
|
-
import
|
|
19133
|
+
import chalk21 from "chalk";
|
|
18363
19134
|
function isKeylessVitalsAsk(input) {
|
|
18364
19135
|
return KEYLESS_ASK_RE.test(input.trim());
|
|
18365
19136
|
}
|
|
@@ -18405,39 +19176,42 @@ async function tryKeylessAskAnswer(ctx, input, opts = {}) {
|
|
|
18405
19176
|
if (!primary) return false;
|
|
18406
19177
|
const label = VITAL_SIGN_LABELS[primary.vital_sign] ?? primary.vital_sign;
|
|
18407
19178
|
const dollarBit = primary.dollar_value != null && primary.dollar_value > 0 ? `${formatCurrency(primary.dollar_value)} ${primary.dollar_label ?? ""}`.trim() : null;
|
|
18408
|
-
const headline =
|
|
19179
|
+
const headline = voiceKeylessHeadline({
|
|
19180
|
+
label,
|
|
19181
|
+
dollarBit,
|
|
19182
|
+
score: primary.score,
|
|
19183
|
+
status: primary.status
|
|
19184
|
+
});
|
|
18409
19185
|
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);
|
|
18410
19186
|
console.log();
|
|
18411
|
-
console.log(" " +
|
|
19187
|
+
console.log(" " + chalk21.bold(headline));
|
|
18412
19188
|
if (opts.fromResume) {
|
|
18413
19189
|
if (runners.length > 0) {
|
|
18414
19190
|
console.log(
|
|
18415
|
-
" " +
|
|
19191
|
+
" " + chalk21.dim(voiceKeylessNextLabel()) + chalk21.dim(runners.map(formatRunnerBit).join(" \xB7 "))
|
|
18416
19192
|
);
|
|
18417
19193
|
}
|
|
18418
19194
|
} else {
|
|
18419
19195
|
console.log();
|
|
18420
19196
|
if (gating && gating.vital_sign !== primary.vital_sign) {
|
|
18421
19197
|
console.log(
|
|
18422
|
-
" " +
|
|
19198
|
+
" " + chalk21.dim("Gating vital: ") + paint("accent", VITAL_SIGN_LABELS[gating.vital_sign]) + chalk21.dim(voiceKeylessGatingWrap(VITAL_SIGN_LABELS[gating.vital_sign], gating.score))
|
|
18423
19199
|
);
|
|
18424
19200
|
}
|
|
18425
19201
|
if (runners.length > 0) {
|
|
18426
|
-
console.log(" " +
|
|
19202
|
+
console.log(" " + chalk21.dim(voiceKeylessAlsoLabel()));
|
|
18427
19203
|
for (const vs of runners) {
|
|
18428
|
-
console.log(" " +
|
|
19204
|
+
console.log(" " + chalk21.dim("\xB7 ") + formatVitalLine(vs));
|
|
18429
19205
|
}
|
|
18430
19206
|
}
|
|
18431
19207
|
if (aggregate.total_value_at_risk != null && aggregate.total_value_at_risk > 0) {
|
|
18432
19208
|
console.log(
|
|
18433
|
-
" " +
|
|
19209
|
+
" " + chalk21.dim("Total at risk: ") + chalk21.green(formatCurrency(aggregate.total_value_at_risk))
|
|
18434
19210
|
);
|
|
18435
19211
|
}
|
|
18436
19212
|
}
|
|
18437
19213
|
console.log();
|
|
18438
|
-
console.log(
|
|
18439
|
-
" " + chalk19.dim("Press ") + paint("accent", "\u23CE") + chalk19.dim(" to connect a key (") + paint("accent", "/connect") + chalk19.dim(") for the why and the plan. NTRP will finish this question after you connect.")
|
|
18440
|
-
);
|
|
19214
|
+
console.log(" " + chalk21.dim(voiceKeylessConnectCta()));
|
|
18441
19215
|
console.log();
|
|
18442
19216
|
if (!opts.fromResume) {
|
|
18443
19217
|
recordMessage(ctx, "user", input);
|
|
@@ -18454,12 +19228,13 @@ var init_keyless_ask = __esm({
|
|
|
18454
19228
|
init_formatters();
|
|
18455
19229
|
init_theme();
|
|
18456
19230
|
init_health_score();
|
|
19231
|
+
init_copy();
|
|
18457
19232
|
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;
|
|
18458
19233
|
}
|
|
18459
19234
|
});
|
|
18460
19235
|
|
|
18461
19236
|
// src/conversation/keyless-definitions.ts
|
|
18462
|
-
import
|
|
19237
|
+
import chalk22 from "chalk";
|
|
18463
19238
|
function isPossessiveMetricAsk(input) {
|
|
18464
19239
|
return POSSESSIVE_RE.test(input.trim());
|
|
18465
19240
|
}
|
|
@@ -18512,9 +19287,9 @@ function tryKeylessDefinitionAnswer(ctx, input) {
|
|
|
18512
19287
|
const bench = explainer.benchmarkHint?.(motion);
|
|
18513
19288
|
console.log();
|
|
18514
19289
|
console.log(
|
|
18515
|
-
" " + sectionHeading(explainer.label) +
|
|
19290
|
+
" " + sectionHeading(explainer.label) + chalk22.dim(` \xB7 ${explainer.kind === "vital" ? "vital sign" : "SaaS metric"}`)
|
|
18516
19291
|
);
|
|
18517
|
-
console.log(" " +
|
|
19292
|
+
console.log(" " + chalk22.dim(explainer.tagline));
|
|
18518
19293
|
console.log();
|
|
18519
19294
|
console.log(" " + bold("Meaning"));
|
|
18520
19295
|
printWrapped2(explainer.meaning, " ");
|
|
@@ -18526,16 +19301,16 @@ function tryKeylessDefinitionAnswer(ctx, input) {
|
|
|
18526
19301
|
}
|
|
18527
19302
|
if (bench) {
|
|
18528
19303
|
console.log();
|
|
18529
|
-
console.log(" " +
|
|
19304
|
+
console.log(" " + chalk22.dim(`Benchmark \xB7 ${bench}`));
|
|
18530
19305
|
}
|
|
18531
19306
|
if (explainer.dollar_label) {
|
|
18532
19307
|
console.log(
|
|
18533
|
-
" " +
|
|
19308
|
+
" " + chalk22.dim(`Dollar translation \xB7 ${explainer.dollar_label}`)
|
|
18534
19309
|
);
|
|
18535
19310
|
}
|
|
18536
19311
|
console.log();
|
|
18537
19312
|
console.log(
|
|
18538
|
-
" " +
|
|
19313
|
+
" " + chalk22.dim("More: ") + paint("accent", `/deepdive ${explainer.id}`) + chalk22.dim(" \xB7 full tour: ") + paint("accent", "/deepdive")
|
|
18539
19314
|
);
|
|
18540
19315
|
console.log();
|
|
18541
19316
|
recordMessage(ctx, "user", input);
|
|
@@ -18565,7 +19340,7 @@ var init_keyless_definitions = __esm({
|
|
|
18565
19340
|
});
|
|
18566
19341
|
|
|
18567
19342
|
// src/conversation/orchestrator.ts
|
|
18568
|
-
import
|
|
19343
|
+
import chalk23 from "chalk";
|
|
18569
19344
|
async function handleExploreWithoutKey(ctx, input) {
|
|
18570
19345
|
if (isDefinitionAsk(input) && tryKeylessDefinitionAnswer(ctx, input)) {
|
|
18571
19346
|
return;
|
|
@@ -18595,7 +19370,7 @@ async function handleExploreWithoutKey(ctx, input) {
|
|
|
18595
19370
|
);
|
|
18596
19371
|
if (ctx.pendingAsk) {
|
|
18597
19372
|
console.log(
|
|
18598
|
-
" " +
|
|
19373
|
+
" " + chalk23.dim("Your question is stored. NTRP will answer it after you connect.")
|
|
18599
19374
|
);
|
|
18600
19375
|
}
|
|
18601
19376
|
if (ctx.gapAudit) {
|
|
@@ -18610,12 +19385,12 @@ async function handleExploreWithoutKey(ctx, input) {
|
|
|
18610
19385
|
return;
|
|
18611
19386
|
}
|
|
18612
19387
|
console.log();
|
|
18613
|
-
console.log(" " +
|
|
18614
|
-
console.log(" " +
|
|
18615
|
-
console.log(" " + paint("accent", "/deepdive") +
|
|
18616
|
-
console.log(" " + paint("accent", "/playbook") +
|
|
18617
|
-
console.log(" " +
|
|
18618
|
-
console.log(" " + paint("accent", "/handoff") +
|
|
19388
|
+
console.log(" " + chalk23.yellow("No key is connected. Q&A stays off until you type ") + paint("accent", "/connect") + chalk23.yellow("."));
|
|
19389
|
+
console.log(" " + chalk23.dim("These commands work without a key:"));
|
|
19390
|
+
console.log(" " + paint("accent", "/deepdive") + chalk23.dim(" slides for numbers and how to use NTRP"));
|
|
19391
|
+
console.log(" " + paint("accent", "/playbook") + chalk23.dim(" recommended plays from your computed vitals"));
|
|
19392
|
+
console.log(" " + chalk23.cyan('"how should we fix this?"') + chalk23.dim(" a simple plan. No AI."));
|
|
19393
|
+
console.log(" " + paint("accent", "/handoff") + chalk23.dim(" write this analysis for another tool"));
|
|
18619
19394
|
console.log();
|
|
18620
19395
|
recordMessage(
|
|
18621
19396
|
ctx,
|
|
@@ -19270,7 +20045,7 @@ var nl_exports = {};
|
|
|
19270
20045
|
__export(nl_exports, {
|
|
19271
20046
|
runNaturalLanguage: () => runNaturalLanguage
|
|
19272
20047
|
});
|
|
19273
|
-
import
|
|
20048
|
+
import chalk24 from "chalk";
|
|
19274
20049
|
async function runNaturalLanguage(input, ctx) {
|
|
19275
20050
|
if (isSmokeProtocolTrigger(input)) {
|
|
19276
20051
|
recordMessage(ctx, "user", input);
|
|
@@ -19282,10 +20057,10 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19282
20057
|
printAnswer(result.answer);
|
|
19283
20058
|
recordMessage(ctx, "agent", result.answer);
|
|
19284
20059
|
console.log();
|
|
19285
|
-
return
|
|
20060
|
+
return extractSummary2(result.answer);
|
|
19286
20061
|
} catch (err) {
|
|
19287
20062
|
spinner2.fail("Smoke protocol failed");
|
|
19288
|
-
console.error(" " +
|
|
20063
|
+
console.error(" " + chalk24.red(String(err.message ?? err)));
|
|
19289
20064
|
console.log();
|
|
19290
20065
|
return;
|
|
19291
20066
|
}
|
|
@@ -19315,8 +20090,8 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19315
20090
|
spinner2.succeed(metricsFirst ? "Session context ready" : "Health snapshot ready");
|
|
19316
20091
|
} catch (err) {
|
|
19317
20092
|
spinner2.fail("Could not compute health snapshot");
|
|
19318
|
-
console.error(" " +
|
|
19319
|
-
console.log(" " +
|
|
20093
|
+
console.error(" " + chalk24.red(String(err.message ?? err)));
|
|
20094
|
+
console.log(" " + chalk24.dim("Run ") + paint("accent", "/new") + chalk24.dim(" \u2192 pick Demo to load sample data."));
|
|
19320
20095
|
console.log();
|
|
19321
20096
|
return;
|
|
19322
20097
|
}
|
|
@@ -19354,7 +20129,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19354
20129
|
break;
|
|
19355
20130
|
case "thinking":
|
|
19356
20131
|
spinner.stop();
|
|
19357
|
-
console.log(" " +
|
|
20132
|
+
console.log(" " + chalk24.dim.italic(event.text));
|
|
19358
20133
|
spinner.start("Thinking\u2026");
|
|
19359
20134
|
break;
|
|
19360
20135
|
case "answer":
|
|
@@ -19364,7 +20139,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19364
20139
|
break;
|
|
19365
20140
|
case "finding":
|
|
19366
20141
|
spinner.stop();
|
|
19367
|
-
|
|
20142
|
+
printFindingInline2(event.finding);
|
|
19368
20143
|
break;
|
|
19369
20144
|
case "done":
|
|
19370
20145
|
spinner.stop();
|
|
@@ -19374,7 +20149,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19374
20149
|
}
|
|
19375
20150
|
} catch (err) {
|
|
19376
20151
|
spinner.fail("Error while investigating");
|
|
19377
|
-
console.error(" " +
|
|
20152
|
+
console.error(" " + chalk24.red(String(err.message ?? err)));
|
|
19378
20153
|
console.log();
|
|
19379
20154
|
return;
|
|
19380
20155
|
} finally {
|
|
@@ -19384,7 +20159,7 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19384
20159
|
ctx.conversation = distillThread(rawHistory);
|
|
19385
20160
|
}
|
|
19386
20161
|
if (!lastAnswer) {
|
|
19387
|
-
console.log(" " +
|
|
20162
|
+
console.log(" " + chalk24.dim("(no answer returned)"));
|
|
19388
20163
|
} else {
|
|
19389
20164
|
recordMessage(ctx, "agent", lastAnswer);
|
|
19390
20165
|
if (ctx.pendingAsk) {
|
|
@@ -19400,12 +20175,12 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
19400
20175
|
console.log();
|
|
19401
20176
|
const { promptQueuedAiStrategist: promptQueuedAiStrategist2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
19402
20177
|
promptQueuedAiStrategist2(ctx);
|
|
19403
|
-
return lastAnswer ?
|
|
20178
|
+
return lastAnswer ? extractSummary2(lastAnswer) : void 0;
|
|
19404
20179
|
}
|
|
19405
20180
|
function printAnswer(text) {
|
|
19406
20181
|
printMarkdown(text, { indent: 2 });
|
|
19407
20182
|
}
|
|
19408
|
-
function
|
|
20183
|
+
function extractSummary2(text) {
|
|
19409
20184
|
const plain = text.replace(/#{1,6}\s+/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^[-*]\s+/gm, "").trim();
|
|
19410
20185
|
const sentenceMatch = plain.match(/^(.+?[.!?])(?:\s|$)/);
|
|
19411
20186
|
const sentence = sentenceMatch ? sentenceMatch[1] : plain.split("\n")[0];
|
|
@@ -19413,16 +20188,16 @@ function extractSummary(text) {
|
|
|
19413
20188
|
const truncated = sentence.slice(0, 60).replace(/\s+\S*$/, "");
|
|
19414
20189
|
return truncated + "\u2026";
|
|
19415
20190
|
}
|
|
19416
|
-
function
|
|
20191
|
+
function printFindingInline2(finding) {
|
|
19417
20192
|
const sev = finding.severity;
|
|
19418
20193
|
console.log();
|
|
19419
|
-
console.log(" " + severityPaint(sev)(`[${sev}]`) + " " +
|
|
20194
|
+
console.log(" " + severityPaint(sev)(`[${sev}]`) + " " + chalk24.bold(finding.segment));
|
|
19420
20195
|
printMarkdown(finding.finding, { indent: 2 });
|
|
19421
20196
|
const play = finding.recommended_plays?.[0];
|
|
19422
|
-
if (play) console.log(" " +
|
|
20197
|
+
if (play) console.log(" " + chalk24.dim("\u2192 " + play.play_name + " \u2014 " + play.rationale));
|
|
19423
20198
|
if (finding.recommended_focus) {
|
|
19424
20199
|
console.log(
|
|
19425
|
-
" " +
|
|
20200
|
+
" " + chalk24.dim("How this works: ") + paint("accent", `/deepdive ${finding.recommended_focus}`)
|
|
19426
20201
|
);
|
|
19427
20202
|
}
|
|
19428
20203
|
}
|
|
@@ -19458,12 +20233,12 @@ __export(demo_exports, {
|
|
|
19458
20233
|
printDemoDisabled: () => printDemoDisabled,
|
|
19459
20234
|
setDemoEnabled: () => setDemoEnabled
|
|
19460
20235
|
});
|
|
19461
|
-
import
|
|
20236
|
+
import chalk25 from "chalk";
|
|
19462
20237
|
function printDemoDisabled() {
|
|
19463
20238
|
console.log();
|
|
19464
|
-
console.log(" " +
|
|
20239
|
+
console.log(" " + chalk25.red(DEMO_DISABLED_MESSAGE));
|
|
19465
20240
|
console.log(
|
|
19466
|
-
" " +
|
|
20241
|
+
" " + chalk25.dim("Re-enable with ") + paint("accent", "/config set demo-enabled true") + chalk25.dim(".")
|
|
19467
20242
|
);
|
|
19468
20243
|
console.log();
|
|
19469
20244
|
}
|
|
@@ -22765,16 +23540,16 @@ var generate_exports = {};
|
|
|
22765
23540
|
__export(generate_exports, {
|
|
22766
23541
|
handler: () => handler2
|
|
22767
23542
|
});
|
|
22768
|
-
import
|
|
23543
|
+
import chalk26 from "chalk";
|
|
22769
23544
|
async function handler2(args, ctx) {
|
|
22770
23545
|
const { flags } = parseArgs(args, ["list-scenarios", "regen-taxonomy", "brief"]);
|
|
22771
23546
|
const quiet = ctx.execution.quiet;
|
|
22772
23547
|
const brief = getBool(flags, "brief");
|
|
22773
23548
|
if (getBool(flags, "list-scenarios")) {
|
|
22774
|
-
console.log(
|
|
23549
|
+
console.log(chalk26.bold("\n Available Scenarios:\n"));
|
|
22775
23550
|
for (const s of SCENARIO_LIST) {
|
|
22776
|
-
console.log(` ${
|
|
22777
|
-
console.log(` ${
|
|
23551
|
+
console.log(` ${chalk26.cyan(s.key.padEnd(20))} ${s.label}`);
|
|
23552
|
+
console.log(` ${chalk26.dim(" ".repeat(20))} ${s.description}
|
|
22778
23553
|
`);
|
|
22779
23554
|
}
|
|
22780
23555
|
return true;
|
|
@@ -22784,9 +23559,9 @@ async function handler2(args, ctx) {
|
|
|
22784
23559
|
const skipProfile = getFalse(flags, "profile");
|
|
22785
23560
|
if (!isProfileConfigured(profile) && !skipProfile) {
|
|
22786
23561
|
console.error();
|
|
22787
|
-
console.error(" " +
|
|
22788
|
-
console.error(" " +
|
|
22789
|
-
console.error(" " +
|
|
23562
|
+
console.error(" " + chalk26.red("No company profile found."));
|
|
23563
|
+
console.error(" " + chalk26.dim("Run ") + paint("accent", "/onboard") + chalk26.dim(" first for a richer demo,"));
|
|
23564
|
+
console.error(" " + chalk26.dim("or pass ") + paint("accent", "--no-profile") + chalk26.dim(" to skip."));
|
|
22790
23565
|
console.error();
|
|
22791
23566
|
markFailure(ctx);
|
|
22792
23567
|
return false;
|
|
@@ -22794,8 +23569,8 @@ async function handler2(args, ctx) {
|
|
|
22794
23569
|
const explicitScenario = getString(flags, "scenario", "s");
|
|
22795
23570
|
const resolvedScenario = resolveScenarioInput(explicitScenario);
|
|
22796
23571
|
if (resolvedScenario === null) {
|
|
22797
|
-
console.error(
|
|
22798
|
-
console.log(
|
|
23572
|
+
console.error(chalk26.red(` Unknown scenario: ${explicitScenario}`));
|
|
23573
|
+
console.log(chalk26.dim(` Valid: ${SCENARIO_LIST.map((s) => s.key).join(", ")}`));
|
|
22799
23574
|
markFailure(ctx);
|
|
22800
23575
|
return false;
|
|
22801
23576
|
}
|
|
@@ -22809,10 +23584,10 @@ async function handler2(args, ctx) {
|
|
|
22809
23584
|
const s = getScenario(scenario);
|
|
22810
23585
|
console.log();
|
|
22811
23586
|
if (brief) {
|
|
22812
|
-
console.log(" " + paint("accent", "\u2713 Demo: ") + bold(s.label) +
|
|
23587
|
+
console.log(" " + paint("accent", "\u2713 Demo: ") + bold(s.label) + chalk26.dim(" \u2014 " + s.hook));
|
|
22813
23588
|
} else {
|
|
22814
23589
|
console.log(" " + paint("accent", "\u2713 Scenario: ") + bold(s.label));
|
|
22815
|
-
console.log(" " +
|
|
23590
|
+
console.log(" " + chalk26.dim(s.story));
|
|
22816
23591
|
console.log();
|
|
22817
23592
|
}
|
|
22818
23593
|
}
|
|
@@ -22842,18 +23617,18 @@ async function handler2(args, ctx) {
|
|
|
22842
23617
|
if (brief) {
|
|
22843
23618
|
spinner.succeed(`Demo loaded \u2014 ${briefCounts(result.counts)}`);
|
|
22844
23619
|
} else {
|
|
22845
|
-
spinner.succeed(`Generated demo data for "${
|
|
23620
|
+
spinner.succeed(`Generated demo data for "${chalk26.cyan(scenario)}" scenario`);
|
|
22846
23621
|
console.log();
|
|
22847
23622
|
printEntityCounts(result.counts);
|
|
22848
23623
|
}
|
|
22849
23624
|
}
|
|
22850
23625
|
if (!quiet && !brief && ctx.analysis.primary !== "revenue_metrics") {
|
|
22851
|
-
console.log(
|
|
23626
|
+
console.log(chalk26.dim("\n Run /diagnose to compute vital signs.\n"));
|
|
22852
23627
|
}
|
|
22853
23628
|
}
|
|
22854
23629
|
} catch (err) {
|
|
22855
23630
|
if (spinner) spinner.fail("Generation failed");
|
|
22856
|
-
console.error(
|
|
23631
|
+
console.error(chalk26.red(String(err)));
|
|
22857
23632
|
markFailure(ctx);
|
|
22858
23633
|
return false;
|
|
22859
23634
|
}
|
|
@@ -22891,7 +23666,7 @@ async function loadOrBuildTaxonomy(profile, forceRegen, ctx) {
|
|
|
22891
23666
|
return taxonomy;
|
|
22892
23667
|
} catch (err) {
|
|
22893
23668
|
spinner.fail("Couldn't build market taxonomy \u2014 using generic data pools");
|
|
22894
|
-
console.log(" " +
|
|
23669
|
+
console.log(" " + chalk26.dim(String(err.message ?? err)));
|
|
22895
23670
|
return void 0;
|
|
22896
23671
|
}
|
|
22897
23672
|
}
|
|
@@ -22986,7 +23761,7 @@ __export(inbox_setup_exports, {
|
|
|
22986
23761
|
reuseInboxFolderIfPresent: () => reuseInboxFolderIfPresent,
|
|
22987
23762
|
shouldOfferInboxSkillSetup: () => shouldOfferInboxSkillSetup
|
|
22988
23763
|
});
|
|
22989
|
-
import
|
|
23764
|
+
import chalk27 from "chalk";
|
|
22990
23765
|
import { existsSync as existsSync22 } from "fs";
|
|
22991
23766
|
function markDemoOffered() {
|
|
22992
23767
|
setConfigValue("ai-inbox-nudge-seen", "true");
|
|
@@ -23008,27 +23783,27 @@ function printSkipHint(beat) {
|
|
|
23008
23783
|
const skillCmd = paint("accent", "/inbox skill");
|
|
23009
23784
|
if (beat === "demo") {
|
|
23010
23785
|
console.log(
|
|
23011
|
-
" " +
|
|
23786
|
+
" " + chalk27.dim("Skipped. NTRP will ask once when you load your own data. Or type ") + setCmd + chalk27.dim(" then ") + skillCmd
|
|
23012
23787
|
);
|
|
23013
23788
|
return;
|
|
23014
23789
|
}
|
|
23015
23790
|
console.log(
|
|
23016
|
-
" " +
|
|
23791
|
+
" " + chalk27.dim("Skipped. NTRP will not ask again. Type ") + setCmd + chalk27.dim(" then ") + skillCmd + chalk27.dim(" at any time.")
|
|
23017
23792
|
);
|
|
23018
23793
|
}
|
|
23019
23794
|
async function reuseInboxFolderIfPresent(session, beat, folderPath = defaultAiInboxDir()) {
|
|
23020
23795
|
if (getAiInboxDir()) return false;
|
|
23021
23796
|
if (!existsSync22(folderPath)) return false;
|
|
23022
|
-
console.log(" " +
|
|
23797
|
+
console.log(" " + chalk27.dim("Pickup folder still on disk: ") + folderPath);
|
|
23023
23798
|
const reuse = await session.confirm("Reuse this pickup folder?", true);
|
|
23024
23799
|
if (!reuse) return false;
|
|
23025
23800
|
const resolved = setAiInboxDir(folderPath);
|
|
23026
23801
|
markDemoOffered();
|
|
23027
23802
|
if (beat === "production") markProductionOffered();
|
|
23028
23803
|
console.log();
|
|
23029
|
-
console.log(" " + paint("accent", "Inbox ready") +
|
|
23804
|
+
console.log(" " + paint("accent", "Inbox ready") + chalk27.dim(" ") + resolved);
|
|
23030
23805
|
console.log(
|
|
23031
|
-
" " +
|
|
23806
|
+
" " + chalk27.dim("Folder reused. Type ") + paint("accent", "/inbox skill") + chalk27.dim(" to print the finder again.")
|
|
23032
23807
|
);
|
|
23033
23808
|
console.log();
|
|
23034
23809
|
return true;
|
|
@@ -23039,12 +23814,12 @@ async function offerInboxSkillSetup(session, opts = {}) {
|
|
|
23039
23814
|
console.log();
|
|
23040
23815
|
console.log(" " + bold("Teach Claude where handoffs live"));
|
|
23041
23816
|
console.log(
|
|
23042
|
-
" " +
|
|
23817
|
+
" " + chalk27.dim(
|
|
23043
23818
|
"Optional. NTRP copies every handoff into one folder. You paste instructions once; later /handoff just writes the file."
|
|
23044
23819
|
)
|
|
23045
23820
|
);
|
|
23046
23821
|
if (beat === "production" && getConfigValue("ai-inbox-nudge-seen") === "true") {
|
|
23047
|
-
console.log(" " +
|
|
23822
|
+
console.log(" " + chalk27.dim("You skipped this during demo."));
|
|
23048
23823
|
}
|
|
23049
23824
|
console.log();
|
|
23050
23825
|
if (await reuseInboxFolderIfPresent(session, beat)) return;
|
|
@@ -23075,16 +23850,16 @@ async function offerInboxSkillSetup(session, opts = {}) {
|
|
|
23075
23850
|
markDemoOffered();
|
|
23076
23851
|
if (beat === "production") markProductionOffered();
|
|
23077
23852
|
console.log();
|
|
23078
|
-
console.log(" " + paint("accent", "Inbox ready") +
|
|
23853
|
+
console.log(" " + paint("accent", "Inbox ready") + chalk27.dim(" ") + resolved);
|
|
23079
23854
|
if (n > 0) {
|
|
23080
|
-
console.log(" " +
|
|
23855
|
+
console.log(" " + chalk27.dim(`Synced ${n} recent export${n === 1 ? "" : "s"}.`));
|
|
23081
23856
|
}
|
|
23082
23857
|
console.log(
|
|
23083
|
-
" " +
|
|
23858
|
+
" " + chalk27.dim("Paste this skill into Claude once. Later, tell Claude to open the latest NTRP handoff.")
|
|
23084
23859
|
);
|
|
23085
23860
|
printStandingSkill();
|
|
23086
23861
|
await session.askPressEnter("Paste the skill into Claude. Then continue");
|
|
23087
|
-
console.log(" " +
|
|
23862
|
+
console.log(" " + chalk27.dim("Done. Later handoffs write to that folder. The skill is not printed again."));
|
|
23088
23863
|
console.log();
|
|
23089
23864
|
}
|
|
23090
23865
|
async function maybeOfferInboxOnProduction(ctx) {
|
|
@@ -23117,7 +23892,7 @@ var ingest_exports = {};
|
|
|
23117
23892
|
__export(ingest_exports, {
|
|
23118
23893
|
handler: () => handler3
|
|
23119
23894
|
});
|
|
23120
|
-
import
|
|
23895
|
+
import chalk28 from "chalk";
|
|
23121
23896
|
import { readFileSync as readFileSync20, existsSync as existsSync23 } from "fs";
|
|
23122
23897
|
import { basename as basename6 } from "path";
|
|
23123
23898
|
async function handler3(args, ctx) {
|
|
@@ -23138,21 +23913,21 @@ async function handler3(args, ctx) {
|
|
|
23138
23913
|
const source = getString(flags, "source", "s") ?? "salesforce";
|
|
23139
23914
|
const skipResolve = getBool(flags, "skip-resolve");
|
|
23140
23915
|
if (!file) {
|
|
23141
|
-
console.error(
|
|
23142
|
-
console.error(
|
|
23916
|
+
console.error(chalk28.red(" Usage: /ingest <file> [--source salesforce|hubspot|outreach]"));
|
|
23917
|
+
console.error(chalk28.dim(" /ingest --demo [--scenario <name>]"));
|
|
23143
23918
|
process.exit(1);
|
|
23144
23919
|
}
|
|
23145
23920
|
if (!existsSync23(file)) {
|
|
23146
|
-
console.error(
|
|
23921
|
+
console.error(chalk28.red(` File not found: ${file}`));
|
|
23147
23922
|
process.exit(1);
|
|
23148
23923
|
}
|
|
23149
23924
|
const profile = loadProfile();
|
|
23150
23925
|
const skipProfile = getFalse(flags, "profile");
|
|
23151
23926
|
if (!profile && !skipProfile) {
|
|
23152
23927
|
console.error();
|
|
23153
|
-
console.error(" " +
|
|
23154
|
-
console.error(" " +
|
|
23155
|
-
console.error(" " +
|
|
23928
|
+
console.error(" " + chalk28.red("No company profile found."));
|
|
23929
|
+
console.error(" " + chalk28.dim("Run ") + paint("accent", "/onboard") + chalk28.dim(" first for better column mapping,"));
|
|
23930
|
+
console.error(" " + chalk28.dim("or pass ") + paint("accent", "--no-profile") + chalk28.dim(" to skip."));
|
|
23156
23931
|
console.error();
|
|
23157
23932
|
process.exit(1);
|
|
23158
23933
|
}
|
|
@@ -23184,15 +23959,15 @@ async function handler3(args, ctx) {
|
|
|
23184
23959
|
row_count: result2.imported
|
|
23185
23960
|
});
|
|
23186
23961
|
spinner.succeed(
|
|
23187
|
-
`Imported ${
|
|
23962
|
+
`Imported ${chalk28.bold(result2.imported.toString())} revenue events from ${chalk28.dim(basename6(file))}`
|
|
23188
23963
|
);
|
|
23189
23964
|
if (result2.errors.length > 0) {
|
|
23190
|
-
console.log(
|
|
23965
|
+
console.log(chalk28.yellow(` ${result2.errors.length} rows skipped`));
|
|
23191
23966
|
}
|
|
23192
23967
|
if (ctx.analysis) {
|
|
23193
23968
|
ctx.analysis.data_source_type = "revenue_ledger";
|
|
23194
23969
|
}
|
|
23195
|
-
console.log(
|
|
23970
|
+
console.log(chalk28.dim(" Run ") + chalk28.cyan("/metrics") + chalk28.dim(" for SaaS metrics with ledger-backed retention."));
|
|
23196
23971
|
const { maybeOfferInboxOnProduction: maybeOfferInboxOnProduction3 } = await Promise.resolve().then(() => (init_inbox_setup(), inbox_setup_exports));
|
|
23197
23972
|
await maybeOfferInboxOnProduction3(ctx);
|
|
23198
23973
|
return `${result2.imported} revenue events from ${basename6(file)}`;
|
|
@@ -23201,7 +23976,7 @@ async function handler3(args, ctx) {
|
|
|
23201
23976
|
const detection = detectEntityType(headers, source);
|
|
23202
23977
|
if (!detection) {
|
|
23203
23978
|
spinner.fail(`Could not auto-detect entity type for source: ${source}`);
|
|
23204
|
-
console.log(
|
|
23979
|
+
console.log(chalk28.dim(" Headers found: " + headers.join(", ")));
|
|
23205
23980
|
process.exit(1);
|
|
23206
23981
|
}
|
|
23207
23982
|
spinner.text = `Importing ${rows.length} ${detection.entityType} rows...`;
|
|
@@ -23224,15 +23999,15 @@ async function handler3(args, ctx) {
|
|
|
23224
23999
|
row_count: result.imported
|
|
23225
24000
|
});
|
|
23226
24001
|
spinner.succeed(
|
|
23227
|
-
`Imported ${
|
|
24002
|
+
`Imported ${chalk28.bold(result.imported.toString())} ${detection.entityType} from ${chalk28.dim(basename6(file))} (${source})`
|
|
23228
24003
|
);
|
|
23229
24004
|
if (result.errors.length > 0) {
|
|
23230
|
-
console.log(
|
|
24005
|
+
console.log(chalk28.yellow(` ${result.errors.length} rows skipped`));
|
|
23231
24006
|
for (const err of result.errors.slice(0, 3)) {
|
|
23232
|
-
console.log(
|
|
24007
|
+
console.log(chalk28.dim(` - ${err}`));
|
|
23233
24008
|
}
|
|
23234
24009
|
if (result.errors.length > 3) {
|
|
23235
|
-
console.log(
|
|
24010
|
+
console.log(chalk28.dim(` ... and ${result.errors.length - 3} more`));
|
|
23236
24011
|
}
|
|
23237
24012
|
}
|
|
23238
24013
|
if (!skipResolve) {
|
|
@@ -23251,7 +24026,7 @@ async function handler3(args, ctx) {
|
|
|
23251
24026
|
return `${result.imported} ${detection.entityType} from ${basename6(file)}`;
|
|
23252
24027
|
} catch (err) {
|
|
23253
24028
|
spinner.fail("Import failed");
|
|
23254
|
-
console.error(
|
|
24029
|
+
console.error(chalk28.red(String(err)));
|
|
23255
24030
|
process.exit(1);
|
|
23256
24031
|
}
|
|
23257
24032
|
}
|
|
@@ -23363,13 +24138,13 @@ __export(demo_fit_exports, {
|
|
|
23363
24138
|
resolveDemoScenarioForLoad: () => resolveDemoScenarioForLoad,
|
|
23364
24139
|
runDemoFitQuiz: () => runDemoFitQuiz
|
|
23365
24140
|
});
|
|
23366
|
-
import
|
|
24141
|
+
import chalk29 from "chalk";
|
|
23367
24142
|
async function runDemoFitQuiz(session, opts = {}) {
|
|
23368
24143
|
if (opts.intro !== false) {
|
|
23369
24144
|
console.log();
|
|
23370
24145
|
console.log(" " + bold("Fit a sample book of business"));
|
|
23371
24146
|
console.log(
|
|
23372
|
-
" " +
|
|
24147
|
+
" " + chalk29.dim(
|
|
23373
24148
|
"No API key needed. Two questions about how you sell, then you pick which of seven sample pipelines feels closest."
|
|
23374
24149
|
)
|
|
23375
24150
|
);
|
|
@@ -23387,8 +24162,8 @@ async function runDemoFitQuiz(session, opts = {}) {
|
|
|
23387
24162
|
);
|
|
23388
24163
|
const recommended = inferDemoScenario({ salesMotion: motion, dealBand });
|
|
23389
24164
|
console.log();
|
|
23390
|
-
console.log(" " +
|
|
23391
|
-
console.log(" " +
|
|
24165
|
+
console.log(" " + chalk29.dim("Recommended: ") + paint("accent", getScenario(recommended.scenario).label));
|
|
24166
|
+
console.log(" " + chalk29.dim(recommended.reason));
|
|
23392
24167
|
const scenario = await session.choose(
|
|
23393
24168
|
"Which of these sample books feels closest to the one you manage?",
|
|
23394
24169
|
scenarioMenuChoices(),
|
|
@@ -23408,8 +24183,8 @@ async function offerFittedDemoAfterOnboard(session, ctx, profile) {
|
|
|
23408
24183
|
const s = getScenario(fit.scenario);
|
|
23409
24184
|
console.log();
|
|
23410
24185
|
console.log(" " + bold("A sample pipeline that looks like you"));
|
|
23411
|
-
console.log(" " + paint("accent", s.label) +
|
|
23412
|
-
console.log(" " +
|
|
24186
|
+
console.log(" " + paint("accent", s.label) + chalk29.dim(" \u2014 " + s.hook));
|
|
24187
|
+
console.log(" " + chalk29.dim(fit.reason));
|
|
23413
24188
|
console.log();
|
|
23414
24189
|
const action = await session.choose(
|
|
23415
24190
|
"Try NTRP on that book of business?",
|
|
@@ -23466,8 +24241,8 @@ async function resolveDemoScenarioForLoad(ctx, opts = {}) {
|
|
|
23466
24241
|
if (!opts.forceQuiz && isProfileConfigured(profile) && profile) {
|
|
23467
24242
|
const fit = await resolveProfileFit(profile, ctx);
|
|
23468
24243
|
console.log();
|
|
23469
|
-
console.log(" " +
|
|
23470
|
-
console.log(" " +
|
|
24244
|
+
console.log(" " + chalk29.dim("Recommended: ") + paint("accent", getScenario(fit.scenario).label));
|
|
24245
|
+
console.log(" " + chalk29.dim(fit.reason));
|
|
23471
24246
|
const scenario = await session.choose(
|
|
23472
24247
|
"Which sample book of business?",
|
|
23473
24248
|
scenarioMenuChoices(),
|
|
@@ -23531,7 +24306,7 @@ __export(ingest_chat_exports, {
|
|
|
23531
24306
|
import { existsSync as existsSync24 } from "fs";
|
|
23532
24307
|
import { basename as basename7, resolve as resolve9 } from "path";
|
|
23533
24308
|
import { homedir as homedir8 } from "os";
|
|
23534
|
-
import
|
|
24309
|
+
import chalk30 from "chalk";
|
|
23535
24310
|
function extractFilePath(input) {
|
|
23536
24311
|
const trimmed = input.trim();
|
|
23537
24312
|
const patterns = [
|
|
@@ -23566,7 +24341,7 @@ function looksLikeFilePath(input) {
|
|
|
23566
24341
|
}
|
|
23567
24342
|
async function ingestFromChat(ctx, filePath) {
|
|
23568
24343
|
if (!ctx.rl) {
|
|
23569
|
-
console.log(" " +
|
|
24344
|
+
console.log(" " + chalk30.red("Ingest confirm requires interactive mode."));
|
|
23570
24345
|
return false;
|
|
23571
24346
|
}
|
|
23572
24347
|
const name = basename7(filePath);
|
|
@@ -23574,7 +24349,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
23574
24349
|
try {
|
|
23575
24350
|
const ok = await prompts.confirm(`Ingest ${name} as CRM export?`, true);
|
|
23576
24351
|
if (!ok) {
|
|
23577
|
-
console.log(" " +
|
|
24352
|
+
console.log(" " + chalk30.dim("Ingest cancelled."));
|
|
23578
24353
|
return false;
|
|
23579
24354
|
}
|
|
23580
24355
|
} finally {
|
|
@@ -23602,7 +24377,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
23602
24377
|
true
|
|
23603
24378
|
);
|
|
23604
24379
|
if (useAi) {
|
|
23605
|
-
console.log(" " +
|
|
24380
|
+
console.log(" " + chalk30.dim("AI column mapping is not wired to ingest yet \u2014 trying standard ingest."));
|
|
23606
24381
|
}
|
|
23607
24382
|
} finally {
|
|
23608
24383
|
prompts2.close();
|
|
@@ -23629,7 +24404,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
23629
24404
|
invalidateGapAudit(ctx);
|
|
23630
24405
|
saveSessionState(ctx);
|
|
23631
24406
|
console.log();
|
|
23632
|
-
console.log(" " + paint("accent", "\u2713 Data loaded") +
|
|
24407
|
+
console.log(" " + paint("accent", "\u2713 Data loaded") + chalk30.dim(` \u2014 ${name}`));
|
|
23633
24408
|
recordMessage(ctx, "user", `[ingested ${name}]`);
|
|
23634
24409
|
recordMessage(ctx, "agent", `Loaded ${name}. Checking what we can analyze\u2026`);
|
|
23635
24410
|
const audit = await refreshGapAudit(ctx);
|
|
@@ -23637,7 +24412,7 @@ async function ingestFromChat(ctx, filePath) {
|
|
|
23637
24412
|
if (audit.can_compute && ctx.scope?.confirmed_at) {
|
|
23638
24413
|
if (ctx.pendingAsk) {
|
|
23639
24414
|
console.log();
|
|
23640
|
-
console.log(" " +
|
|
24415
|
+
console.log(" " + chalk30.dim("Computing so I can answer\u2026"));
|
|
23641
24416
|
await runConversationCompute(ctx);
|
|
23642
24417
|
return true;
|
|
23643
24418
|
}
|
|
@@ -23685,7 +24460,7 @@ async function loadDemoFromChat(ctx, scenario, opts = {}) {
|
|
|
23685
24460
|
const { getScenario: getScenario2 } = await Promise.resolve().then(() => (init_scenarios(), scenarios_exports));
|
|
23686
24461
|
const s = getScenario2(chosen);
|
|
23687
24462
|
console.log();
|
|
23688
|
-
console.log(" " + paint("accent", "Fitting ") + s.label +
|
|
24463
|
+
console.log(" " + paint("accent", "Fitting ") + s.label + chalk30.dim(" \u2014 " + s.hook));
|
|
23689
24464
|
}
|
|
23690
24465
|
const { handler: demo } = await Promise.resolve().then(() => (init_generate(), generate_exports));
|
|
23691
24466
|
const args = ["--no-profile", "--brief"];
|
|
@@ -23721,7 +24496,7 @@ async function loadDemoFromChat(ctx, scenario, opts = {}) {
|
|
|
23721
24496
|
const shouldAuto = opts.autoCompute || Boolean(ctx.pendingAsk && audit.can_compute && ctx.scope?.confirmed_at);
|
|
23722
24497
|
if (shouldAuto && audit.can_compute) {
|
|
23723
24498
|
console.log();
|
|
23724
|
-
console.log(" " +
|
|
24499
|
+
console.log(" " + chalk30.dim("Computing so I can answer\u2026"));
|
|
23725
24500
|
await runConversationCompute(ctx);
|
|
23726
24501
|
return true;
|
|
23727
24502
|
}
|
|
@@ -23754,7 +24529,7 @@ __export(pending_ask_exports, {
|
|
|
23754
24529
|
queuePendingAsk: () => queuePendingAsk,
|
|
23755
24530
|
resumePendingAsk: () => resumePendingAsk
|
|
23756
24531
|
});
|
|
23757
|
-
import
|
|
24532
|
+
import chalk31 from "chalk";
|
|
23758
24533
|
function looksLikeQuestion(input) {
|
|
23759
24534
|
const text = input.trim();
|
|
23760
24535
|
if (!text) return false;
|
|
@@ -23790,7 +24565,7 @@ function printFocusChip(ctx) {
|
|
|
23790
24565
|
const period = ctx.scope.time_horizon ? ` \xB7 ${ctx.scope.time_horizon}` : "";
|
|
23791
24566
|
console.log();
|
|
23792
24567
|
console.log(
|
|
23793
|
-
" " +
|
|
24568
|
+
" " + chalk31.dim("Focus: ") + paint("accent", lens) + chalk31.dim(period) + chalk31.dim(" \u2014 type ") + chalk31.cyan("adjust") + chalk31.dim(" to change")
|
|
23794
24569
|
);
|
|
23795
24570
|
console.log();
|
|
23796
24571
|
}
|
|
@@ -23801,7 +24576,7 @@ async function resumePendingAsk(ctx) {
|
|
|
23801
24576
|
if (canUseReplAi(ctx)) {
|
|
23802
24577
|
console.log();
|
|
23803
24578
|
console.log(
|
|
23804
|
-
" " +
|
|
24579
|
+
" " + chalk31.dim(
|
|
23805
24580
|
pending.keylessAnswered ? "Picking up your question with the connected engine\u2026" : "Picking up your question\u2026"
|
|
23806
24581
|
)
|
|
23807
24582
|
);
|
|
@@ -23834,7 +24609,7 @@ async function offerDemoToAnswer(ctx) {
|
|
|
23834
24609
|
const go = await prompts.confirm("Use demo data to answer this?", true);
|
|
23835
24610
|
if (!go) {
|
|
23836
24611
|
console.log(
|
|
23837
|
-
" " +
|
|
24612
|
+
" " + chalk31.dim("Paste a CSV path when ready, or say ") + chalk31.cyan("use demo data") + chalk31.dim(".")
|
|
23838
24613
|
);
|
|
23839
24614
|
console.log();
|
|
23840
24615
|
return false;
|
|
@@ -23866,11 +24641,11 @@ __export(compute_exports2, {
|
|
|
23866
24641
|
isComputeIntent: () => isComputeIntent,
|
|
23867
24642
|
runConversationCompute: () => runConversationCompute
|
|
23868
24643
|
});
|
|
23869
|
-
import
|
|
24644
|
+
import chalk32 from "chalk";
|
|
23870
24645
|
async function runConversationCompute(ctx) {
|
|
23871
24646
|
const lens = ctx.scope?.primary_lens ?? ctx.analysis.primary;
|
|
23872
24647
|
ctx.computeInProgress = true;
|
|
23873
|
-
const willAnswer = Boolean(ctx.pendingAsk?.text) && !ctx.strategistState;
|
|
24648
|
+
const willAnswer = Boolean(ctx.pendingAsk?.text) && !ctx.strategistState && ctx.thinkState?.step !== "awaiting_analysis";
|
|
23874
24649
|
try {
|
|
23875
24650
|
if (lens === "revenue_metrics") {
|
|
23876
24651
|
const { runMetricsAnalysis: runMetricsAnalysis2, extractHeadlineMetrics: extractHeadlineMetrics2 } = await Promise.resolve().then(() => (init_metrics_analysis(), metrics_analysis_exports));
|
|
@@ -23898,6 +24673,7 @@ async function runConversationCompute(ctx) {
|
|
|
23898
24673
|
interactive: !willAnswer
|
|
23899
24674
|
});
|
|
23900
24675
|
await resumeQueuedStrategist(ctx);
|
|
24676
|
+
await resumeQueuedThink(ctx);
|
|
23901
24677
|
await closeComputeTurn(ctx, willAnswer, "revenue_metrics");
|
|
23902
24678
|
creditGapCompute(ctx);
|
|
23903
24679
|
creditMetricsComplete(ctx, false);
|
|
@@ -23917,11 +24693,12 @@ async function runConversationCompute(ctx) {
|
|
|
23917
24693
|
invalidateGapAudit(ctx);
|
|
23918
24694
|
saveSessionState(ctx);
|
|
23919
24695
|
await resumeQueuedStrategist(ctx);
|
|
24696
|
+
await resumeQueuedThink(ctx);
|
|
23920
24697
|
await closeComputeTurn(ctx, willAnswer, "gtm_health");
|
|
23921
24698
|
creditGapCompute(ctx);
|
|
23922
24699
|
return typeof summary === "string" ? summary : "Health analysis ready";
|
|
23923
24700
|
} catch (err) {
|
|
23924
|
-
console.error(" " +
|
|
24701
|
+
console.error(" " + chalk32.red(String(err.message ?? err)));
|
|
23925
24702
|
return;
|
|
23926
24703
|
} finally {
|
|
23927
24704
|
ctx.computeInProgress = false;
|
|
@@ -23934,9 +24711,16 @@ async function resumeQueuedStrategist(ctx) {
|
|
|
23934
24711
|
const { resumeStrategistAfterCompute: resumeStrategistAfterCompute2 } = await Promise.resolve().then(() => (init_strategist_flow(), strategist_flow_exports));
|
|
23935
24712
|
await resumeStrategistAfterCompute2(ctx);
|
|
23936
24713
|
}
|
|
24714
|
+
async function resumeQueuedThink(ctx) {
|
|
24715
|
+
if (ctx.thinkState?.step !== "awaiting_analysis") return;
|
|
24716
|
+
ctx.computeInProgress = false;
|
|
24717
|
+
const { resumeThinkAfterCompute: resumeThinkAfterCompute2 } = await Promise.resolve().then(() => (init_think_flow(), think_flow_exports));
|
|
24718
|
+
await resumeThinkAfterCompute2(ctx);
|
|
24719
|
+
}
|
|
23937
24720
|
async function resumePendingAskAfterCompute(ctx) {
|
|
23938
24721
|
if (!ctx.pendingAsk?.text) return false;
|
|
23939
24722
|
if (ctx.strategistState) return false;
|
|
24723
|
+
if (ctx.thinkState?.step === "active" || ctx.thinkState?.step === "awaiting_analysis") return false;
|
|
23940
24724
|
const { resumePendingAsk: resumePendingAsk2 } = await Promise.resolve().then(() => (init_pending_ask(), pending_ask_exports));
|
|
23941
24725
|
return resumePendingAsk2(ctx);
|
|
23942
24726
|
}
|
|
@@ -24405,6 +25189,7 @@ async function handleDraftStrategy(input) {
|
|
|
24405
25189
|
if (!objective) return { error: "objective is required." };
|
|
24406
25190
|
if (!isAnalysisReady2(ctx)) {
|
|
24407
25191
|
ctx.strategistState = { step: "awaiting_analysis", objective, origin: "ai" };
|
|
25192
|
+
if (ctx.thinkState) ctx.thinkState = void 0;
|
|
24408
25193
|
saveSessionState2(ctx);
|
|
24409
25194
|
return {
|
|
24410
25195
|
queued: true,
|
|
@@ -24413,6 +25198,7 @@ async function handleDraftStrategy(input) {
|
|
|
24413
25198
|
};
|
|
24414
25199
|
}
|
|
24415
25200
|
ctx.strategistState = { step: "objective_confirm", objective, origin: "ai" };
|
|
25201
|
+
if (ctx.thinkState) ctx.thinkState = void 0;
|
|
24416
25202
|
saveSessionState2(ctx);
|
|
24417
25203
|
return {
|
|
24418
25204
|
launched: true,
|
|
@@ -24420,6 +25206,32 @@ async function handleDraftStrategy(input) {
|
|
|
24420
25206
|
note: "Strategist handoff armed. After your reply the user sees an objective confirmation card and the engine runs a full grounding/backcast/stress-test session. Keep your reply to one or two sentences introducing the handoff \u2014 do NOT write the plan yourself."
|
|
24421
25207
|
};
|
|
24422
25208
|
}
|
|
25209
|
+
async function handleUpdateThinkScratch(input) {
|
|
25210
|
+
const { getAgentContext: getAgentContext2 } = await Promise.resolve().then(() => (init_agent_context(), agent_context_exports));
|
|
25211
|
+
const { saveSessionState: saveSessionState2 } = await Promise.resolve().then(() => (init_context2(), context_exports));
|
|
25212
|
+
const ctx = getAgentContext2();
|
|
25213
|
+
if (!ctx) return { error: "No active session context." };
|
|
25214
|
+
if (!ctx.thinkState || ctx.thinkState.step !== "active") {
|
|
25215
|
+
return { error: "Think channel is not active." };
|
|
25216
|
+
}
|
|
25217
|
+
const asStringList = (value) => {
|
|
25218
|
+
if (!Array.isArray(value)) return void 0;
|
|
25219
|
+
return value.filter((v) => typeof v === "string").map((s) => s.trim()).filter(Boolean).slice(0, 12);
|
|
25220
|
+
};
|
|
25221
|
+
const open = asStringList(input.open_questions);
|
|
25222
|
+
const challenged = asStringList(input.challenged_assumptions);
|
|
25223
|
+
const hypotheses = asStringList(input.working_hypotheses);
|
|
25224
|
+
if (open) ctx.thinkState.open_questions = open;
|
|
25225
|
+
if (challenged) ctx.thinkState.challenged_assumptions = challenged;
|
|
25226
|
+
if (hypotheses) ctx.thinkState.working_hypotheses = hypotheses;
|
|
25227
|
+
saveSessionState2(ctx);
|
|
25228
|
+
return {
|
|
25229
|
+
updated: true,
|
|
25230
|
+
open_questions: ctx.thinkState.open_questions ?? [],
|
|
25231
|
+
challenged_assumptions: ctx.thinkState.challenged_assumptions ?? [],
|
|
25232
|
+
working_hypotheses: ctx.thinkState.working_hypotheses ?? []
|
|
25233
|
+
};
|
|
25234
|
+
}
|
|
24423
25235
|
async function handleGetSessionBrief(input) {
|
|
24424
25236
|
const raw = typeof input.session_id === "string" ? input.session_id.trim() : "";
|
|
24425
25237
|
if (!raw) return { error: "session_id is required." };
|
|
@@ -24545,13 +25357,182 @@ var init_tool_handlers = __esm({
|
|
|
24545
25357
|
audit_data_gaps: (_, __) => handleAuditDataGaps(),
|
|
24546
25358
|
run_compute: (_, __) => handleRunCompute(),
|
|
24547
25359
|
draft_handoff: (input, _) => handleDraftHandoff(input),
|
|
24548
|
-
draft_strategy: (input, _) => handleDraftStrategy(input)
|
|
25360
|
+
draft_strategy: (input, _) => handleDraftStrategy(input),
|
|
25361
|
+
update_think_scratch: (input, _) => handleUpdateThinkScratch(input)
|
|
24549
25362
|
};
|
|
24550
25363
|
}
|
|
24551
25364
|
});
|
|
24552
25365
|
|
|
24553
|
-
// src/ai/
|
|
25366
|
+
// src/ai/think-prompt.ts
|
|
24554
25367
|
function companyContextSection3() {
|
|
25368
|
+
const block = buildCompanyProfileBlock();
|
|
25369
|
+
return block ? `COMPANY CONTEXT:
|
|
25370
|
+
${block}
|
|
25371
|
+
|
|
25372
|
+
` : "";
|
|
25373
|
+
}
|
|
25374
|
+
function operatorSection3() {
|
|
25375
|
+
const block = buildOperatorBlock();
|
|
25376
|
+
return block ? `${block}
|
|
25377
|
+
|
|
25378
|
+
` : "";
|
|
25379
|
+
}
|
|
25380
|
+
function buildScratchBlock(state2) {
|
|
25381
|
+
if (!state2) return "";
|
|
25382
|
+
const lines = [];
|
|
25383
|
+
if (state2.seed) lines.push(`Seed topic: ${state2.seed}`);
|
|
25384
|
+
if (state2.open_questions?.length) {
|
|
25385
|
+
lines.push("Open questions:");
|
|
25386
|
+
for (const q of state2.open_questions) lines.push(`- ${q}`);
|
|
25387
|
+
}
|
|
25388
|
+
if (state2.challenged_assumptions?.length) {
|
|
25389
|
+
lines.push("Challenged assumptions:");
|
|
25390
|
+
for (const a of state2.challenged_assumptions) lines.push(`- ${a}`);
|
|
25391
|
+
}
|
|
25392
|
+
if (state2.working_hypotheses?.length) {
|
|
25393
|
+
lines.push("Working hypotheses:");
|
|
25394
|
+
for (const h of state2.working_hypotheses) lines.push(`- ${h}`);
|
|
25395
|
+
}
|
|
25396
|
+
if (lines.length === 0) return "";
|
|
25397
|
+
return `
|
|
25398
|
+
THINK SCRATCH (session working memory \u2014 update via update_think_scratch):
|
|
25399
|
+
${lines.join("\n")}
|
|
25400
|
+
`;
|
|
25401
|
+
}
|
|
25402
|
+
function buildThinkWithMeSystemPrompt(opts = {}) {
|
|
25403
|
+
const sessionBlock = opts.sessionContext ? `
|
|
25404
|
+
PREVIOUS SESSION CONTEXT:
|
|
25405
|
+
The user resumed an earlier session. Here is what they were investigating before:
|
|
25406
|
+
${opts.sessionContext}
|
|
25407
|
+
Treat this as already-established background. Pick up where it left off \u2014 do not re-introduce it as if it were new.
|
|
25408
|
+
|
|
25409
|
+
` : "";
|
|
25410
|
+
const memorySection = opts.memoryBlock ? `
|
|
25411
|
+
WHAT YOU ALREADY KNOW ABOUT THIS BUSINESS (durable memory):
|
|
25412
|
+
${opts.memoryBlock}
|
|
25413
|
+
Reference this naturally. Do not re-derive things you already know; build on them.
|
|
25414
|
+
|
|
25415
|
+
` : "";
|
|
25416
|
+
const analysisSection = opts.analysisBlock ? `
|
|
25417
|
+
SESSION ANALYSIS CONTEXT:
|
|
25418
|
+
${opts.analysisBlock}
|
|
25419
|
+
Use get_revenue_metrics and get_revenue_metrics_timeseries when the user asks about SaaS metrics, retention, or period trends.
|
|
25420
|
+
|
|
25421
|
+
` : "";
|
|
25422
|
+
const artifactSection = opts.sessionArtifact ? `
|
|
25423
|
+
COMPLETED SESSION ANALYSIS:
|
|
25424
|
+
${opts.sessionArtifact}
|
|
25425
|
+
Cite numbers from here; call tools when you need a new cut or to pressure-test a claim.
|
|
25426
|
+
|
|
25427
|
+
` : "";
|
|
25428
|
+
const conversationSection = opts.conversationBlock ? `
|
|
25429
|
+
CONVERSATION STATE:
|
|
25430
|
+
${opts.conversationBlock}
|
|
25431
|
+
|
|
25432
|
+
` : "";
|
|
25433
|
+
const scratchSection = buildScratchBlock(opts.thinkState);
|
|
25434
|
+
const socraticCraft = `HOW YOU THINK WITH THE USER (socratic partner \u2014 not a search box, not a strategist):
|
|
25435
|
+
- You are a thinking partner. Prefer sharp questions that unlock the user's idea over dumping answers.
|
|
25436
|
+
- Protect imagination: give "what if" space before the smell-test \u2014 do not kill creativity in the first sentence.
|
|
25437
|
+
- Steelman the user's position before you attack it; then deliver one hard pushback grounded in evidence when possible.
|
|
25438
|
+
- Ground claims about THIS pipeline in tool results. Label speculation explicitly ("hypothesis:", "speculation:").
|
|
25439
|
+
- Each turn should ADVANCE the thread: a new question, a challenge, a synthesis, or a fork \u2014 never rehash.
|
|
25440
|
+
- If the question is ambiguous, ask at most ONE clarifying question. Otherwise choose a fork and state it.
|
|
25441
|
+
- Never invent a multi-week roadmap inline. When they want commitment, call draft_strategy with a crisp objective.
|
|
25442
|
+
- Keep open_questions, challenged_assumptions, and working_hypotheses current via update_think_scratch.
|
|
25443
|
+
- You have continuity via prior think-channel messages. Never repeat an angle already covered unless asked.`;
|
|
25444
|
+
const jobSection = `YOUR JOB (THINK CHANNEL \u2014 always deep):
|
|
25445
|
+
- Decide whether you need tools, a direct answer, or both. Do not re-call a tool whose result you already have.
|
|
25446
|
+
- Lead with the answer or the question that matters most, then structure.
|
|
25447
|
+
- When you use numbers, include dollar values where available and lead with financial impact.
|
|
25448
|
+
- Descriptive exploration stays in this channel; plan-of-attack questions hand off via draft_strategy.
|
|
25449
|
+
- After a successful draft_strategy tool result: reply with at most 1\u20132 sentences introducing the handoff. Do not write the plan.`;
|
|
25450
|
+
const formattingSection = `
|
|
25451
|
+
FORMATTING (your answer is rendered in a terminal via a small markdown renderer):
|
|
25452
|
+
- Use **bold** for every dollar figure, score, play name, and person name.
|
|
25453
|
+
- Use *italics* for conversational asides and your closing follow-up question.
|
|
25454
|
+
- Use ### for section headings \u2014 never # or ##. The renderer flattens depth.
|
|
25455
|
+
- For multi-point answers, prefer a one-line lead + bullet list over long paragraph blocks.
|
|
25456
|
+
- Keep paragraphs to 3-4 sentences.
|
|
25457
|
+
- Prefer short tables (\u22643 columns, \u22645 rows, cells \u226430 chars).
|
|
25458
|
+
- End with a dim horizontal rule (---) followed by one italicized follow-up question or fork.
|
|
25459
|
+
|
|
25460
|
+
${buildUserVisibleProseBlock()}
|
|
25461
|
+
`;
|
|
25462
|
+
const commandSection = `PRESET COMMANDS (slash commands the user can type \u2014 you may SUGGEST these; you cannot run them):
|
|
25463
|
+
${buildCommandCatalogBlock()}
|
|
25464
|
+
|
|
25465
|
+
COMMAND SUGGESTION RULES:
|
|
25466
|
+
- Suggest at most ONE command when it adds capability beyond your answer (e.g. \`/strategy\`, \`/handoff\`).
|
|
25467
|
+
- Never claim a command was run. Never invent flags.
|
|
25468
|
+
- Type \`done\` or \`cancel\` leaves the think channel back to ask \u203A.`;
|
|
25469
|
+
const stable = `You are a world-class GTM operating partner in a dedicated THINK WITH ME channel. The user wants collaborative exploration \u2014 imagination, pressure-testing, and evidence \u2014 not a slide deck and not a finished strategy plan.
|
|
25470
|
+
|
|
25471
|
+
${companyContextSection3()}${operatorSection3()}${socraticCraft}
|
|
25472
|
+
|
|
25473
|
+
ANALYST INSTINCT (judgment a CEO pays for \u2014 apply by default):
|
|
25474
|
+
${ANALYST_INSTINCT_BLOCK}
|
|
25475
|
+
|
|
25476
|
+
${jobSection}
|
|
25477
|
+
|
|
25478
|
+
EXECUTION BIAS (how you work):
|
|
25479
|
+
${EXECUTION_BIAS_BLOCK}
|
|
25480
|
+
|
|
25481
|
+
GTM ENGINEERING (how recommendations become systems):
|
|
25482
|
+
${GTM_ENGINEERING_BLOCK}
|
|
25483
|
+
|
|
25484
|
+
OUTPUT DOCTRINE (how answers are structured for recall):
|
|
25485
|
+
${PYRAMID_OUTPUT_BLOCK}
|
|
25486
|
+
|
|
25487
|
+
${buildUserVisibleProseBlock()}
|
|
25488
|
+
|
|
25489
|
+
CONTEXT:
|
|
25490
|
+
The user's health scores and top divergences were included as JSON at the start of this conversation. Use them, the think-channel history, and SESSION STATE as background and as hints for which tools to call.
|
|
25491
|
+
|
|
25492
|
+
VITAL SIGNS EXPLAINED (with dollar translations):
|
|
25493
|
+
${VITAL_SIGNS_BLOCK}
|
|
25494
|
+
|
|
25495
|
+
PLAYBOOK \u2014 name a play when it helps the user act (not a full program):
|
|
25496
|
+
${buildPlaybookBlock()}
|
|
25497
|
+
|
|
25498
|
+
${commandSection}
|
|
25499
|
+
${formattingSection}
|
|
25500
|
+
OUTPUT RULES:
|
|
25501
|
+
- Respond in plain text markdown (not JSON). You do NOT need to emit the findings schema.
|
|
25502
|
+
- Include specific numbers from tool results, never guess.
|
|
25503
|
+
- When you have enough information, answer or ask \u2014 don't call tools you don't need.
|
|
25504
|
+
|
|
25505
|
+
SAFETY & EVIDENCE (non-negotiable):
|
|
25506
|
+
${SAFETY_BLOCK}`;
|
|
25507
|
+
const dynamicSections = [
|
|
25508
|
+
sessionBlock,
|
|
25509
|
+
memorySection,
|
|
25510
|
+
analysisSection,
|
|
25511
|
+
artifactSection,
|
|
25512
|
+
conversationSection,
|
|
25513
|
+
scratchSection
|
|
25514
|
+
].map((s) => s.trim()).filter(Boolean);
|
|
25515
|
+
const dynamic = [
|
|
25516
|
+
"SESSION STATE (current \u2014 changes as the session progresses):",
|
|
25517
|
+
...dynamicSections,
|
|
25518
|
+
buildRuntimeBlock()
|
|
25519
|
+
].join("\n\n");
|
|
25520
|
+
return { stable, dynamic };
|
|
25521
|
+
}
|
|
25522
|
+
var init_think_prompt = __esm({
|
|
25523
|
+
"src/ai/think-prompt.ts"() {
|
|
25524
|
+
"use strict";
|
|
25525
|
+
init_prompt_parts();
|
|
25526
|
+
init_prompt();
|
|
25527
|
+
}
|
|
25528
|
+
});
|
|
25529
|
+
|
|
25530
|
+
// src/ai/agentic-loop.ts
|
|
25531
|
+
function findingsJsonSchemaNudge() {
|
|
25532
|
+
return `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:
|
|
25533
|
+
${buildFindingsSchemaBlock()}`;
|
|
25534
|
+
}
|
|
25535
|
+
function companyContextSection4() {
|
|
24555
25536
|
const block = buildCompanyProfileBlock();
|
|
24556
25537
|
if (!block) return "";
|
|
24557
25538
|
return `COMPANY CONTEXT (use this to make every answer specific to the business \u2014 use industry-appropriate language, anchor dollar figures to their deal size):
|
|
@@ -24559,7 +25540,7 @@ ${block}
|
|
|
24559
25540
|
|
|
24560
25541
|
`;
|
|
24561
25542
|
}
|
|
24562
|
-
function
|
|
25543
|
+
function operatorSection4() {
|
|
24563
25544
|
const block = buildOperatorBlock();
|
|
24564
25545
|
if (!block) return "";
|
|
24565
25546
|
return `${block}
|
|
@@ -24569,7 +25550,7 @@ function operatorSection3() {
|
|
|
24569
25550
|
function buildSystemPrompt2() {
|
|
24570
25551
|
const stable = `You are an expert GTM health investigator. You have tools to query a local database of CRM and pipeline data. Your job is to investigate the health scores you've been given and discover the specific root causes behind any problems.
|
|
24571
25552
|
|
|
24572
|
-
${
|
|
25553
|
+
${companyContextSection4()}${operatorSection4()}INVESTIGATION APPROACH:
|
|
24573
25554
|
1. Start by examining the health summary to understand the overall picture
|
|
24574
25555
|
2. Drill into the lowest-scoring vital signs using get_vital_sign_detail
|
|
24575
25556
|
3. Check divergences to find segments that are significantly worse than average
|
|
@@ -24595,7 +25576,7 @@ ${buildPlaybookBlock()}
|
|
|
24595
25576
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
24596
25577
|
${PYRAMID_OUTPUT_BLOCK}
|
|
24597
25578
|
|
|
24598
|
-
${
|
|
25579
|
+
${buildUserVisibleProseBlock()}
|
|
24599
25580
|
|
|
24600
25581
|
RULES:
|
|
24601
25582
|
- Build evidence BEFORE claiming a finding. Call at least 2 tools before emitting a finding.
|
|
@@ -24617,7 +25598,7 @@ FORMATTING (the \`finding\` field on each finding is rendered in a terminal via
|
|
|
24617
25598
|
- Never place emoji inside a table cell or heading \u2014 they break column alignment.
|
|
24618
25599
|
|
|
24619
25600
|
OUTPUT FORMAT \u2014 when you're ready to present findings, respond with ONLY a JSON array:
|
|
24620
|
-
${
|
|
25601
|
+
${buildFindingsSchemaBlock()}
|
|
24621
25602
|
|
|
24622
25603
|
Keep investigating until you have a clear picture. Don't guess \u2014 use the tools.`;
|
|
24623
25604
|
return { stable, dynamic: buildRuntimeBlock() };
|
|
@@ -24700,7 +25681,7 @@ FORMATTING (your answer is rendered in a terminal via a small markdown renderer)
|
|
|
24700
25681
|
- Use \`inline code\` for CRM field names, SQL snippets, or literal values.
|
|
24701
25682
|
- End with a dim horizontal rule (---) followed by one italicized follow-up question.
|
|
24702
25683
|
|
|
24703
|
-
${
|
|
25684
|
+
${buildUserVisibleProseBlock()}
|
|
24704
25685
|
` : `
|
|
24705
25686
|
FORMATTING (brief mode \u2014 scannable, not chunky):
|
|
24706
25687
|
- Open with ONE direct sentence that answers the question. Then a blank line before any list.
|
|
@@ -24715,7 +25696,7 @@ FORMATTING (brief mode \u2014 scannable, not chunky):
|
|
|
24715
25696
|
- No ### headings, no tables, no --- footer, no closing "want me to dig deeper?" question.
|
|
24716
25697
|
- Single-point answers can stay one sentence \u2014 don't force bullets when one line is enough.
|
|
24717
25698
|
|
|
24718
|
-
${
|
|
25699
|
+
${buildUserVisibleProseBlock()}
|
|
24719
25700
|
`;
|
|
24720
25701
|
const commandStyleRule = responseMode === "brief" ? '- Style in brief mode: a suggestion is at most ONE short trailing line after your answer (e.g. "`/segment compare enterprise smb` gives the full side-by-side.") \u2014 no heading, no footer.' : "- Style: weave the suggestion naturally into your close \u2014 one sentence with the exact invocation in `inline code`.";
|
|
24721
25702
|
const commandSection = `PRESET COMMANDS (slash commands the user can type at the prompt \u2014 you may SUGGEST these; you have no ability to run them):
|
|
@@ -24743,7 +25724,7 @@ ${GTM_ENGINEERING_BLOCK}
|
|
|
24743
25724
|
OUTPUT DOCTRINE (how answers are structured for recall):
|
|
24744
25725
|
${PYRAMID_OUTPUT_BLOCK}
|
|
24745
25726
|
|
|
24746
|
-
${
|
|
25727
|
+
${buildUserVisibleProseBlock()}
|
|
24747
25728
|
|
|
24748
25729
|
`;
|
|
24749
25730
|
const referenceSection = responseMode === "brief" ? "" : `CONTEXT:
|
|
@@ -24758,7 +25739,7 @@ ${buildPlaybookBlock()}
|
|
|
24758
25739
|
${commandSection}`;
|
|
24759
25740
|
const stable = `You are a world-class GTM operating partner \u2014 the kind of analyst a CEO keeps on speed dial. You are exceptionally well-read, rigorous, and commercially sharp, and you have tools to query a local database of this company's CRM and pipeline data. The user is having an ongoing, free-form conversation with you about their go-to-market health and SaaS metrics.
|
|
24760
25741
|
|
|
24761
|
-
${
|
|
25742
|
+
${companyContextSection4()}${operatorSection4()}${conversationRules}
|
|
24762
25743
|
|
|
24763
25744
|
${analystSection}
|
|
24764
25745
|
|
|
@@ -24804,20 +25785,27 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24804
25785
|
assertReplAi(options.ctx);
|
|
24805
25786
|
const mode = options.mode ?? "investigation";
|
|
24806
25787
|
const experiment = options.experiment ?? "production";
|
|
24807
|
-
const responseMode = experiment === "baseline" || experiment === "a" || experiment === "b" ? "deep" : options.responseMode ?? (options.sessionArtifact ? "brief" : "deep");
|
|
24808
|
-
const useTools = mode === "investigation" || responseMode === "deep";
|
|
25788
|
+
const responseMode = mode === "think" ? "deep" : experiment === "baseline" || experiment === "a" || experiment === "b" ? "deep" : options.responseMode ?? (options.sessionArtifact ? "brief" : "deep");
|
|
25789
|
+
const useTools = mode === "investigation" || mode === "think" || responseMode === "deep";
|
|
24809
25790
|
const maxTokens = mode === "fresh" && responseMode === "brief" ? BRIEF_MAX_TOKENS : DEEP_MAX_TOKENS;
|
|
24810
25791
|
const surface = mode === "fresh" ? responseMode === "brief" ? "agentic_fresh_brief" : "agentic_investigation" : "agentic_investigation";
|
|
24811
25792
|
const llmCfg = loadLlmConfig();
|
|
24812
25793
|
const tier = tierForSurface(surface, llmCfg.tier);
|
|
24813
|
-
const systemPrompt = mode === "
|
|
25794
|
+
const systemPrompt = mode === "think" ? buildThinkWithMeSystemPrompt({
|
|
25795
|
+
sessionContext: options.sessionContext,
|
|
25796
|
+
memoryBlock: options.memoryBlock,
|
|
25797
|
+
analysisBlock: options.analysisBlock,
|
|
25798
|
+
conversationBlock: options.conversationBlock,
|
|
25799
|
+
sessionArtifact: options.sessionArtifact,
|
|
25800
|
+
thinkState: options.ctx.thinkState
|
|
25801
|
+
}) : mode === "fresh" ? buildFreshNlSystemPrompt(
|
|
24814
25802
|
options.sessionContext,
|
|
24815
25803
|
options.memoryBlock,
|
|
24816
25804
|
options.analysisBlock,
|
|
24817
25805
|
options.conversationBlock,
|
|
24818
25806
|
{ responseMode, sessionArtifact: options.sessionArtifact, experiment }
|
|
24819
25807
|
) : buildSystemPrompt2();
|
|
24820
|
-
const tools2 = useTools ? mode === "fresh" ? buildFreshNlTools() : buildInvestigationTools() : [];
|
|
25808
|
+
const tools2 = useTools ? mode === "think" ? buildThinkTools() : mode === "fresh" ? buildFreshNlTools() : buildInvestigationTools() : [];
|
|
24821
25809
|
const toolCtx = { computeResult, divergences };
|
|
24822
25810
|
if (options.includeMetrics) {
|
|
24823
25811
|
try {
|
|
@@ -24829,7 +25817,8 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24829
25817
|
const initialContext = buildInitialContext(computeResult, divergences, options.userQuestion);
|
|
24830
25818
|
const priorRaw = options.priorMessages ?? [];
|
|
24831
25819
|
const priorMessages = priorRaw.length > 0 && typeof priorRaw[0] === "object" && priorRaw[0] !== null && "content" in priorRaw[0] ? normalizeThread(priorRaw) : priorRaw;
|
|
24832
|
-
const
|
|
25820
|
+
const conversational = mode === "fresh" || mode === "think";
|
|
25821
|
+
const messages = conversational && priorMessages.length > 0 && options.userQuestion ? [...priorMessages, { role: "user", content: options.userQuestion }] : [{ role: "user", content: initialContext }];
|
|
24833
25822
|
let lastMeta = { provider_used: "anthropic", model_used: "unknown" };
|
|
24834
25823
|
const loopGuard = new ToolLoopGuard();
|
|
24835
25824
|
const allowedTools = new Set(tools2.map((t) => t.name));
|
|
@@ -24859,7 +25848,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24859
25848
|
const response = await callLlm(messages, maxTokens, true);
|
|
24860
25849
|
if (response.tool_calls.length === 0) {
|
|
24861
25850
|
const fullText = response.text;
|
|
24862
|
-
if (
|
|
25851
|
+
if (conversational) {
|
|
24863
25852
|
const findings3 = parseFindings(fullText);
|
|
24864
25853
|
if (findings3.length > 0) {
|
|
24865
25854
|
for (const finding of findings3) yield { type: "finding", finding };
|
|
@@ -24879,7 +25868,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24879
25868
|
messages.push(retry.assistant_message);
|
|
24880
25869
|
}
|
|
24881
25870
|
if (findings2.length === 0) {
|
|
24882
|
-
messages.push({ role: "user", content:
|
|
25871
|
+
messages.push({ role: "user", content: findingsJsonSchemaNudge() });
|
|
24883
25872
|
const retry2 = await callLlm(messages, maxTokens, false);
|
|
24884
25873
|
findings2 = parseFindings(retry2.text);
|
|
24885
25874
|
messages.push(retry2.assistant_message);
|
|
@@ -24907,7 +25896,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24907
25896
|
});
|
|
24908
25897
|
}
|
|
24909
25898
|
}
|
|
24910
|
-
if (
|
|
25899
|
+
if (conversational) {
|
|
24911
25900
|
messages.push({
|
|
24912
25901
|
role: "user",
|
|
24913
25902
|
content: "You've reached the tool budget. Please answer the user's question now with what you've learned."
|
|
@@ -24926,7 +25915,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
24926
25915
|
let findings = parseFindings(final.text);
|
|
24927
25916
|
messages.push(final.assistant_message);
|
|
24928
25917
|
if (findings.length === 0) {
|
|
24929
|
-
messages.push({ role: "user", content:
|
|
25918
|
+
messages.push({ role: "user", content: findingsJsonSchemaNudge() });
|
|
24930
25919
|
final = await callLlm(messages, 4096, false);
|
|
24931
25920
|
findings = parseFindings(final.text);
|
|
24932
25921
|
messages.push(final.assistant_message);
|
|
@@ -24965,7 +25954,7 @@ Here is the current GTM health snapshot as background. Use it plus any tools you
|
|
|
24965
25954
|
function parseFindings(text) {
|
|
24966
25955
|
return (parseJsonArrayFromText(text) ?? []).map(withKnownRecommendedPlays);
|
|
24967
25956
|
}
|
|
24968
|
-
var MAX_ITERATIONS, INVESTIGATION_EVIDENCE_NUDGE_AFTER, BRIEF_MAX_TOKENS, DEEP_MAX_TOKENS, FINDINGS_JSON_NUDGE
|
|
25957
|
+
var MAX_ITERATIONS, INVESTIGATION_EVIDENCE_NUDGE_AFTER, BRIEF_MAX_TOKENS, DEEP_MAX_TOKENS, FINDINGS_JSON_NUDGE;
|
|
24969
25958
|
var init_agentic_loop = __esm({
|
|
24970
25959
|
"src/ai/agentic-loop.ts"() {
|
|
24971
25960
|
"use strict";
|
|
@@ -24983,13 +25972,13 @@ var init_agentic_loop = __esm({
|
|
|
24983
25972
|
init_thread();
|
|
24984
25973
|
init_untrusted();
|
|
24985
25974
|
init_prompt_parts();
|
|
25975
|
+
init_think_prompt();
|
|
25976
|
+
init_prompt();
|
|
24986
25977
|
MAX_ITERATIONS = 10;
|
|
24987
25978
|
INVESTIGATION_EVIDENCE_NUDGE_AFTER = 5;
|
|
24988
25979
|
BRIEF_MAX_TOKENS = 768;
|
|
24989
25980
|
DEEP_MAX_TOKENS = 4096;
|
|
24990
25981
|
FINDINGS_JSON_NUDGE = "Please format your findings as the JSON array specified in your instructions. Respond with ONLY the JSON array, no other text.";
|
|
24991
|
-
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:
|
|
24992
|
-
${FINDINGS_SCHEMA_BLOCK}`;
|
|
24993
25982
|
}
|
|
24994
25983
|
});
|
|
24995
25984
|
|