@sonnechasser/ntrp 1.4.0 → 1.4.2
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 +1986 -1052
- package/dist/mcp/server.js +389 -59
- package/package.json +2 -1
package/dist/mcp/server.js
CHANGED
|
@@ -8131,7 +8131,7 @@ function buildSessionFileSnapshot(ctx) {
|
|
|
8131
8131
|
if (ctx.dataset) file.dataset = ctx.dataset;
|
|
8132
8132
|
if (ctx.deliverables.length > 0) file.deliverables = ctx.deliverables;
|
|
8133
8133
|
if (ctx.conversation.length > 0) file.thread = ctx.conversation;
|
|
8134
|
-
if (ctx.thinkConversation
|
|
8134
|
+
if ((ctx.thinkConversation?.length ?? 0) > 0) file.think_thread = ctx.thinkConversation;
|
|
8135
8135
|
if (ctx.resumedFromId) file.resumed_from = ctx.resumedFromId;
|
|
8136
8136
|
if (ctx.analysis) file.analysis = ctx.analysis;
|
|
8137
8137
|
if (ctx.scope) file.scope = ctx.scope;
|
|
@@ -8470,7 +8470,7 @@ async function finalizeSession(ctx, stage) {
|
|
|
8470
8470
|
if (ctx.conversation.length > 0) {
|
|
8471
8471
|
file.thread = ctx.conversation;
|
|
8472
8472
|
}
|
|
8473
|
-
if (ctx.thinkConversation
|
|
8473
|
+
if ((ctx.thinkConversation?.length ?? 0) > 0) {
|
|
8474
8474
|
file.think_thread = ctx.thinkConversation;
|
|
8475
8475
|
}
|
|
8476
8476
|
if (ctx.analysis) {
|
|
@@ -10449,6 +10449,7 @@ handler: ../commands/config.ts
|
|
|
10449
10449
|
Manage CLI configuration stored at \`~/.ntrp/config.json\`. Useful keys:
|
|
10450
10450
|
\`api-key\` (Anthropic), \`openai-api-key\` (and \`groq-api-key\`, \`google-api-key\`, ...),
|
|
10451
10451
|
\`llm-primary\` (default provider), \`llm-tier\`, \`llm-auto-failover\`,
|
|
10452
|
+
\`voice-personality\`, \`voice-roast\`,
|
|
10452
10453
|
\`default-format\`, \`export-dir\`, \`ai-inbox-dir\` (or type \`/inbox set\`).
|
|
10453
10454
|
|
|
10454
10455
|
Setting a provider key opens a hidden prompt and auto-discovers that provider's models.
|
|
@@ -10481,6 +10482,21 @@ handler: ../commands/tier.ts
|
|
|
10481
10482
|
Set the quality and cost tier for this session. Agentic surfaces respect the tier.
|
|
10482
10483
|
Some single-shot surfaces keep fixed defaults. Type \`/tier list\` to highlight the active stack.
|
|
10483
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.`
|
|
10484
10500
|
},
|
|
10485
10501
|
{
|
|
10486
10502
|
name: "model",
|
|
@@ -10648,7 +10664,7 @@ function formatCatalogLine(meta) {
|
|
|
10648
10664
|
const note = DESTRUCTIVE_COMMAND_NOTES[meta.name] ? ` (${DESTRUCTIVE_COMMAND_NOTES[meta.name]})` : "";
|
|
10649
10665
|
return `- /${meta.name}${args} \u2014 ${meta.description}${note}`;
|
|
10650
10666
|
}
|
|
10651
|
-
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;
|
|
10652
10668
|
var init_prompt_parts = __esm({
|
|
10653
10669
|
"src/ai/prompt-parts.ts"() {
|
|
10654
10670
|
"use strict";
|
|
@@ -10733,21 +10749,102 @@ Restraint: you diagnose and prescribe the system; you do not build it here. Name
|
|
|
10733
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.
|
|
10734
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.
|
|
10735
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).`;
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
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 `[
|
|
10747
10844
|
{
|
|
10748
10845
|
"severity": "critical" | "warning" | "info",
|
|
10749
10846
|
"segment": "segment name or 'Overall'",
|
|
10750
|
-
"finding":
|
|
10847
|
+
"finding": ${JSON.stringify(finding)},
|
|
10751
10848
|
"vital_signs": {"vital_sign_name": score, ...},
|
|
10752
10849
|
"entity_count": number_of_affected_entities,
|
|
10753
10850
|
"recommended_focus": "vital_sign_name",
|
|
@@ -10755,6 +10852,51 @@ Restraint: you diagnose and prescribe the system; you do not build it here. Name
|
|
|
10755
10852
|
"recommended_plays": [{"play_id": "play-id", "play_name": "Play Name", "rationale": "Why this play helps"}]
|
|
10756
10853
|
}
|
|
10757
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
|
+
};
|
|
10758
10900
|
}
|
|
10759
10901
|
});
|
|
10760
10902
|
|
|
@@ -10902,10 +11044,10 @@ ${buildPlaybookBlock()}
|
|
|
10902
11044
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
10903
11045
|
${PYRAMID_OUTPUT_BLOCK}
|
|
10904
11046
|
|
|
10905
|
-
${
|
|
11047
|
+
${buildUserVisibleProseBlock()}
|
|
10906
11048
|
|
|
10907
11049
|
Respond with JSON only \u2014 an array of finding objects:
|
|
10908
|
-
${
|
|
11050
|
+
${buildFindingsSchemaBlock()}
|
|
10909
11051
|
|
|
10910
11052
|
Rules:
|
|
10911
11053
|
- Maximum 8 findings
|
|
@@ -11009,6 +11151,7 @@ var init_findings = __esm({
|
|
|
11009
11151
|
init_repl_api();
|
|
11010
11152
|
init_failover();
|
|
11011
11153
|
init_prompt_parts();
|
|
11154
|
+
init_prompt();
|
|
11012
11155
|
init_json_response();
|
|
11013
11156
|
init_json_stream();
|
|
11014
11157
|
init_playbook();
|
|
@@ -13007,6 +13150,15 @@ var init_layout = __esm({
|
|
|
13007
13150
|
}
|
|
13008
13151
|
});
|
|
13009
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
|
+
|
|
13010
13162
|
// src/ui/banner.ts
|
|
13011
13163
|
import chalk4 from "chalk";
|
|
13012
13164
|
var init_banner = __esm({
|
|
@@ -13014,6 +13166,8 @@ var init_banner = __esm({
|
|
|
13014
13166
|
"use strict";
|
|
13015
13167
|
init_theme();
|
|
13016
13168
|
init_layout();
|
|
13169
|
+
init_home_taglines();
|
|
13170
|
+
init_home_taglines();
|
|
13017
13171
|
}
|
|
13018
13172
|
});
|
|
13019
13173
|
|
|
@@ -13356,8 +13510,8 @@ function resolveAskMultiInput(raw, options) {
|
|
|
13356
13510
|
function resolveChooseInput(raw, choices, defaultValue) {
|
|
13357
13511
|
if (choices.length === 0) return null;
|
|
13358
13512
|
const defaultIdx = resolveChooseDefaultIndex(choices, defaultValue);
|
|
13359
|
-
const
|
|
13360
|
-
const n = Number(
|
|
13513
|
+
const pick3 = raw.trim() || String(defaultIdx + 1);
|
|
13514
|
+
const n = Number(pick3);
|
|
13361
13515
|
if (Number.isInteger(n) && n >= 1 && n <= choices.length) {
|
|
13362
13516
|
return choices[n - 1].value;
|
|
13363
13517
|
}
|
|
@@ -14114,14 +14268,14 @@ ${ANALYST_INSTINCT_BLOCK}
|
|
|
14114
14268
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
14115
14269
|
${PYRAMID_OUTPUT_BLOCK}
|
|
14116
14270
|
|
|
14117
|
-
${
|
|
14271
|
+
${buildUserVisibleProseBlock()}
|
|
14118
14272
|
|
|
14119
14273
|
Respond with JSON only \u2014 an array of finding objects:
|
|
14120
14274
|
[
|
|
14121
14275
|
{
|
|
14122
14276
|
"severity": "critical" | "warning" | "info",
|
|
14123
14277
|
"segment": "Overall",
|
|
14124
|
-
"finding":
|
|
14278
|
+
"finding": ${JSON.stringify(buildFindingsFindingHint())},
|
|
14125
14279
|
"vital_signs": {},
|
|
14126
14280
|
"entity_count": 0,
|
|
14127
14281
|
"recommended_focus": "nrr",
|
|
@@ -14199,6 +14353,7 @@ var init_metrics_findings = __esm({
|
|
|
14199
14353
|
init_repl_api();
|
|
14200
14354
|
init_failover();
|
|
14201
14355
|
init_prompt_parts();
|
|
14356
|
+
init_prompt();
|
|
14202
14357
|
init_playbook();
|
|
14203
14358
|
init_metrics_benchmarks();
|
|
14204
14359
|
init_profile();
|
|
@@ -14566,6 +14721,158 @@ var init_slides = __esm({
|
|
|
14566
14721
|
}
|
|
14567
14722
|
});
|
|
14568
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
|
+
|
|
14569
14876
|
// src/output/terminal.ts
|
|
14570
14877
|
import chalk13 from "chalk";
|
|
14571
14878
|
import Table2 from "cli-table3";
|
|
@@ -14613,9 +14920,10 @@ function printVitalSignRow(vs) {
|
|
|
14613
14920
|
function printHealthSummary(result, _pipelineMetrics) {
|
|
14614
14921
|
const scoreStr = `${chalk13.bold(String(Math.round(result.overall_score)))}${chalk13.dim("/100")}`;
|
|
14615
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);
|
|
14616
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");
|
|
14617
14925
|
printResultCard("Overall Health", [
|
|
14618
|
-
`${chalk13.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}`,
|
|
14926
|
+
`${chalk13.dim("Score")} ${scoreStr} ${statusBadge(result.overall_status)}${gloss ? chalk13.dim(` ${gloss}`) : ""}`,
|
|
14619
14927
|
`${chalk13.dim("Held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`,
|
|
14620
14928
|
`${chalk13.dim("Revenue")} ${impact}`,
|
|
14621
14929
|
next
|
|
@@ -14627,8 +14935,9 @@ function printHealthSummary(result, _pipelineMetrics) {
|
|
|
14627
14935
|
}
|
|
14628
14936
|
function printHealthLine(result) {
|
|
14629
14937
|
const score = `${chalk13.bold(String(Math.round(result.overall_score)))}${chalk13.dim("/100")}`;
|
|
14938
|
+
const gloss = voiceHealthGloss(result.overall_status);
|
|
14630
14939
|
const parts = [
|
|
14631
|
-
`${chalk13.dim("Health")} ${score} ${statusBadge(result.overall_status)}`,
|
|
14940
|
+
`${chalk13.dim("Health")} ${score} ${statusBadge(result.overall_status)}${gloss ? chalk13.dim(` ${gloss}`) : ""}`,
|
|
14632
14941
|
`${chalk13.dim("held back by")} ${paint("accent", VITAL_SIGN_LABELS[result.gating_vital_sign])}`
|
|
14633
14942
|
];
|
|
14634
14943
|
if (result.total_value_at_risk != null && result.total_value_at_risk > 0) {
|
|
@@ -14969,6 +15278,7 @@ var init_terminal = __esm({
|
|
|
14969
15278
|
init_layout();
|
|
14970
15279
|
init_llm_attribution();
|
|
14971
15280
|
init_slides();
|
|
15281
|
+
init_copy();
|
|
14972
15282
|
}
|
|
14973
15283
|
});
|
|
14974
15284
|
|
|
@@ -16643,6 +16953,8 @@ function wrapForTarget(target, analysisBlock, conversationBlock, openQuestions,
|
|
|
16643
16953
|
"",
|
|
16644
16954
|
"Ground every recommendation in the specific numbers provided. Do not invent data.",
|
|
16645
16955
|
"",
|
|
16956
|
+
voiceHandoffAddendum(),
|
|
16957
|
+
"",
|
|
16646
16958
|
"---",
|
|
16647
16959
|
"",
|
|
16648
16960
|
analysisBlock,
|
|
@@ -16701,6 +17013,7 @@ var init_handoff_draft = __esm({
|
|
|
16701
17013
|
init_profile();
|
|
16702
17014
|
init_session_analysis();
|
|
16703
17015
|
init_metric_explainers();
|
|
17016
|
+
init_copy();
|
|
16704
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;
|
|
16705
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;
|
|
16706
17019
|
}
|
|
@@ -17030,7 +17343,7 @@ ALTITUDE CONTRACT (the plan must work at every altitude a client reads it at):
|
|
|
17030
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.
|
|
17031
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.
|
|
17032
17345
|
|
|
17033
|
-
${
|
|
17346
|
+
${buildUserVisibleProseBlock()}
|
|
17034
17347
|
|
|
17035
17348
|
GTM ENGINEERING (plans install systems, not heroics):
|
|
17036
17349
|
${GTM_ENGINEERING_BLOCK}
|
|
@@ -17120,6 +17433,7 @@ var init_strategist_prompt = __esm({
|
|
|
17120
17433
|
"src/ai/strategist-prompt.ts"() {
|
|
17121
17434
|
"use strict";
|
|
17122
17435
|
init_prompt_parts();
|
|
17436
|
+
init_prompt();
|
|
17123
17437
|
STRATEGIST_PLAN_SCHEMA_BLOCK = `{
|
|
17124
17438
|
"title": "Short plan name, e.g. 'Q4 Pipeline Recovery'",
|
|
17125
17439
|
"objective": "The measurable destination, restated precisely",
|
|
@@ -18794,11 +19108,11 @@ function printGapCard(audit, opts = {}) {
|
|
|
18794
19108
|
console.log();
|
|
18795
19109
|
if (audit.can_compute) {
|
|
18796
19110
|
console.log(
|
|
18797
|
-
" " + chalk20.dim(
|
|
19111
|
+
" " + chalk20.dim(voiceGapReadyCta())
|
|
18798
19112
|
);
|
|
18799
19113
|
} else if (audit.missing.length > 0) {
|
|
18800
19114
|
console.log(
|
|
18801
|
-
" " + chalk20.dim(
|
|
19115
|
+
" " + chalk20.dim(voiceGapLoadCta())
|
|
18802
19116
|
);
|
|
18803
19117
|
}
|
|
18804
19118
|
console.log();
|
|
@@ -18806,6 +19120,7 @@ function printGapCard(audit, opts = {}) {
|
|
|
18806
19120
|
var init_gap_card = __esm({
|
|
18807
19121
|
"src/conversation/gap-card.ts"() {
|
|
18808
19122
|
"use strict";
|
|
19123
|
+
init_copy();
|
|
18809
19124
|
}
|
|
18810
19125
|
});
|
|
18811
19126
|
|
|
@@ -18861,25 +19176,30 @@ async function tryKeylessAskAnswer(ctx, input, opts = {}) {
|
|
|
18861
19176
|
if (!primary) return false;
|
|
18862
19177
|
const label = VITAL_SIGN_LABELS[primary.vital_sign] ?? primary.vital_sign;
|
|
18863
19178
|
const dollarBit = primary.dollar_value != null && primary.dollar_value > 0 ? `${formatCurrency(primary.dollar_value)} ${primary.dollar_label ?? ""}`.trim() : null;
|
|
18864
|
-
const headline =
|
|
19179
|
+
const headline = voiceKeylessHeadline({
|
|
19180
|
+
label,
|
|
19181
|
+
dollarBit,
|
|
19182
|
+
score: primary.score,
|
|
19183
|
+
status: primary.status
|
|
19184
|
+
});
|
|
18865
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);
|
|
18866
19186
|
console.log();
|
|
18867
19187
|
console.log(" " + chalk21.bold(headline));
|
|
18868
19188
|
if (opts.fromResume) {
|
|
18869
19189
|
if (runners.length > 0) {
|
|
18870
19190
|
console.log(
|
|
18871
|
-
" " + chalk21.dim(
|
|
19191
|
+
" " + chalk21.dim(voiceKeylessNextLabel()) + chalk21.dim(runners.map(formatRunnerBit).join(" \xB7 "))
|
|
18872
19192
|
);
|
|
18873
19193
|
}
|
|
18874
19194
|
} else {
|
|
18875
19195
|
console.log();
|
|
18876
19196
|
if (gating && gating.vital_sign !== primary.vital_sign) {
|
|
18877
19197
|
console.log(
|
|
18878
|
-
" " + chalk21.dim("Gating vital: ") + paint("accent", VITAL_SIGN_LABELS[gating.vital_sign]) + chalk21.dim(
|
|
19198
|
+
" " + chalk21.dim("Gating vital: ") + paint("accent", VITAL_SIGN_LABELS[gating.vital_sign]) + chalk21.dim(voiceKeylessGatingWrap(VITAL_SIGN_LABELS[gating.vital_sign], gating.score))
|
|
18879
19199
|
);
|
|
18880
19200
|
}
|
|
18881
19201
|
if (runners.length > 0) {
|
|
18882
|
-
console.log(" " + chalk21.dim(
|
|
19202
|
+
console.log(" " + chalk21.dim(voiceKeylessAlsoLabel()));
|
|
18883
19203
|
for (const vs of runners) {
|
|
18884
19204
|
console.log(" " + chalk21.dim("\xB7 ") + formatVitalLine(vs));
|
|
18885
19205
|
}
|
|
@@ -18891,9 +19211,7 @@ async function tryKeylessAskAnswer(ctx, input, opts = {}) {
|
|
|
18891
19211
|
}
|
|
18892
19212
|
}
|
|
18893
19213
|
console.log();
|
|
18894
|
-
console.log(
|
|
18895
|
-
" " + chalk21.dim("Press ") + paint("accent", "\u23CE") + chalk21.dim(" to connect a key (") + paint("accent", "/connect") + chalk21.dim(") for the why and the plan. NTRP will finish this question after you connect.")
|
|
18896
|
-
);
|
|
19214
|
+
console.log(" " + chalk21.dim(voiceKeylessConnectCta()));
|
|
18897
19215
|
console.log();
|
|
18898
19216
|
if (!opts.fromResume) {
|
|
18899
19217
|
recordMessage(ctx, "user", input);
|
|
@@ -18910,6 +19228,7 @@ var init_keyless_ask = __esm({
|
|
|
18910
19228
|
init_formatters();
|
|
18911
19229
|
init_theme();
|
|
18912
19230
|
init_health_score();
|
|
19231
|
+
init_copy();
|
|
18913
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;
|
|
18914
19233
|
}
|
|
18915
19234
|
});
|
|
@@ -25138,7 +25457,7 @@ FORMATTING (your answer is rendered in a terminal via a small markdown renderer)
|
|
|
25138
25457
|
- Prefer short tables (\u22643 columns, \u22645 rows, cells \u226430 chars).
|
|
25139
25458
|
- End with a dim horizontal rule (---) followed by one italicized follow-up question or fork.
|
|
25140
25459
|
|
|
25141
|
-
${
|
|
25460
|
+
${buildUserVisibleProseBlock()}
|
|
25142
25461
|
`;
|
|
25143
25462
|
const commandSection = `PRESET COMMANDS (slash commands the user can type \u2014 you may SUGGEST these; you cannot run them):
|
|
25144
25463
|
${buildCommandCatalogBlock()}
|
|
@@ -25165,7 +25484,7 @@ ${GTM_ENGINEERING_BLOCK}
|
|
|
25165
25484
|
OUTPUT DOCTRINE (how answers are structured for recall):
|
|
25166
25485
|
${PYRAMID_OUTPUT_BLOCK}
|
|
25167
25486
|
|
|
25168
|
-
${
|
|
25487
|
+
${buildUserVisibleProseBlock()}
|
|
25169
25488
|
|
|
25170
25489
|
CONTEXT:
|
|
25171
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.
|
|
@@ -25204,10 +25523,15 @@ var init_think_prompt = __esm({
|
|
|
25204
25523
|
"src/ai/think-prompt.ts"() {
|
|
25205
25524
|
"use strict";
|
|
25206
25525
|
init_prompt_parts();
|
|
25526
|
+
init_prompt();
|
|
25207
25527
|
}
|
|
25208
25528
|
});
|
|
25209
25529
|
|
|
25210
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
|
+
}
|
|
25211
25535
|
function companyContextSection4() {
|
|
25212
25536
|
const block = buildCompanyProfileBlock();
|
|
25213
25537
|
if (!block) return "";
|
|
@@ -25252,7 +25576,7 @@ ${buildPlaybookBlock()}
|
|
|
25252
25576
|
OUTPUT DOCTRINE (how findings are structured for recall):
|
|
25253
25577
|
${PYRAMID_OUTPUT_BLOCK}
|
|
25254
25578
|
|
|
25255
|
-
${
|
|
25579
|
+
${buildUserVisibleProseBlock()}
|
|
25256
25580
|
|
|
25257
25581
|
RULES:
|
|
25258
25582
|
- Build evidence BEFORE claiming a finding. Call at least 2 tools before emitting a finding.
|
|
@@ -25274,7 +25598,7 @@ FORMATTING (the \`finding\` field on each finding is rendered in a terminal via
|
|
|
25274
25598
|
- Never place emoji inside a table cell or heading \u2014 they break column alignment.
|
|
25275
25599
|
|
|
25276
25600
|
OUTPUT FORMAT \u2014 when you're ready to present findings, respond with ONLY a JSON array:
|
|
25277
|
-
${
|
|
25601
|
+
${buildFindingsSchemaBlock()}
|
|
25278
25602
|
|
|
25279
25603
|
Keep investigating until you have a clear picture. Don't guess \u2014 use the tools.`;
|
|
25280
25604
|
return { stable, dynamic: buildRuntimeBlock() };
|
|
@@ -25357,7 +25681,7 @@ FORMATTING (your answer is rendered in a terminal via a small markdown renderer)
|
|
|
25357
25681
|
- Use \`inline code\` for CRM field names, SQL snippets, or literal values.
|
|
25358
25682
|
- End with a dim horizontal rule (---) followed by one italicized follow-up question.
|
|
25359
25683
|
|
|
25360
|
-
${
|
|
25684
|
+
${buildUserVisibleProseBlock()}
|
|
25361
25685
|
` : `
|
|
25362
25686
|
FORMATTING (brief mode \u2014 scannable, not chunky):
|
|
25363
25687
|
- Open with ONE direct sentence that answers the question. Then a blank line before any list.
|
|
@@ -25372,7 +25696,7 @@ FORMATTING (brief mode \u2014 scannable, not chunky):
|
|
|
25372
25696
|
- No ### headings, no tables, no --- footer, no closing "want me to dig deeper?" question.
|
|
25373
25697
|
- Single-point answers can stay one sentence \u2014 don't force bullets when one line is enough.
|
|
25374
25698
|
|
|
25375
|
-
${
|
|
25699
|
+
${buildUserVisibleProseBlock()}
|
|
25376
25700
|
`;
|
|
25377
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`.";
|
|
25378
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):
|
|
@@ -25400,7 +25724,7 @@ ${GTM_ENGINEERING_BLOCK}
|
|
|
25400
25724
|
OUTPUT DOCTRINE (how answers are structured for recall):
|
|
25401
25725
|
${PYRAMID_OUTPUT_BLOCK}
|
|
25402
25726
|
|
|
25403
|
-
${
|
|
25727
|
+
${buildUserVisibleProseBlock()}
|
|
25404
25728
|
|
|
25405
25729
|
`;
|
|
25406
25730
|
const referenceSection = responseMode === "brief" ? "" : `CONTEXT:
|
|
@@ -25544,7 +25868,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
25544
25868
|
messages.push(retry.assistant_message);
|
|
25545
25869
|
}
|
|
25546
25870
|
if (findings2.length === 0) {
|
|
25547
|
-
messages.push({ role: "user", content:
|
|
25871
|
+
messages.push({ role: "user", content: findingsJsonSchemaNudge() });
|
|
25548
25872
|
const retry2 = await callLlm(messages, maxTokens, false);
|
|
25549
25873
|
findings2 = parseFindings(retry2.text);
|
|
25550
25874
|
messages.push(retry2.assistant_message);
|
|
@@ -25591,7 +25915,7 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
25591
25915
|
let findings = parseFindings(final.text);
|
|
25592
25916
|
messages.push(final.assistant_message);
|
|
25593
25917
|
if (findings.length === 0) {
|
|
25594
|
-
messages.push({ role: "user", content:
|
|
25918
|
+
messages.push({ role: "user", content: findingsJsonSchemaNudge() });
|
|
25595
25919
|
final = await callLlm(messages, 4096, false);
|
|
25596
25920
|
findings = parseFindings(final.text);
|
|
25597
25921
|
messages.push(final.assistant_message);
|
|
@@ -25630,7 +25954,7 @@ Here is the current GTM health snapshot as background. Use it plus any tools you
|
|
|
25630
25954
|
function parseFindings(text) {
|
|
25631
25955
|
return (parseJsonArrayFromText(text) ?? []).map(withKnownRecommendedPlays);
|
|
25632
25956
|
}
|
|
25633
|
-
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;
|
|
25634
25958
|
var init_agentic_loop = __esm({
|
|
25635
25959
|
"src/ai/agentic-loop.ts"() {
|
|
25636
25960
|
"use strict";
|
|
@@ -25649,13 +25973,12 @@ var init_agentic_loop = __esm({
|
|
|
25649
25973
|
init_untrusted();
|
|
25650
25974
|
init_prompt_parts();
|
|
25651
25975
|
init_think_prompt();
|
|
25976
|
+
init_prompt();
|
|
25652
25977
|
MAX_ITERATIONS = 10;
|
|
25653
25978
|
INVESTIGATION_EVIDENCE_NUDGE_AFTER = 5;
|
|
25654
25979
|
BRIEF_MAX_TOKENS = 768;
|
|
25655
25980
|
DEEP_MAX_TOKENS = 4096;
|
|
25656
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.";
|
|
25657
|
-
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:
|
|
25658
|
-
${FINDINGS_SCHEMA_BLOCK}`;
|
|
25659
25982
|
}
|
|
25660
25983
|
});
|
|
25661
25984
|
|
|
@@ -25959,22 +26282,29 @@ var init_setup = __esm({
|
|
|
25959
26282
|
import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
|
|
25960
26283
|
import { dirname as dirname5, join as join26 } from "path";
|
|
25961
26284
|
import { fileURLToPath } from "url";
|
|
26285
|
+
function readVersionFromPackageJson(packageJsonPath) {
|
|
26286
|
+
if (!existsSync26(packageJsonPath)) return null;
|
|
26287
|
+
try {
|
|
26288
|
+
const pkg = JSON.parse(readFileSync22(packageJsonPath, "utf-8"));
|
|
26289
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
26290
|
+
} catch {
|
|
26291
|
+
}
|
|
26292
|
+
return null;
|
|
26293
|
+
}
|
|
26294
|
+
function readVersionNearEntry(entryPath) {
|
|
26295
|
+
const start = dirname5(entryPath);
|
|
26296
|
+
for (const rel of [join26(start, "..", "package.json"), join26(start, "../..", "package.json")]) {
|
|
26297
|
+
const version = readVersionFromPackageJson(rel);
|
|
26298
|
+
if (version) return version;
|
|
26299
|
+
}
|
|
26300
|
+
return null;
|
|
26301
|
+
}
|
|
26302
|
+
function readInstalledVersionFromDisk() {
|
|
26303
|
+
return readVersionNearEntry(fileURLToPath(import.meta.url));
|
|
26304
|
+
}
|
|
25962
26305
|
function getInstalledVersion() {
|
|
25963
26306
|
if (cachedVersion) return cachedVersion;
|
|
25964
|
-
|
|
25965
|
-
for (const rel of ["../package.json", "../../package.json"]) {
|
|
25966
|
-
const path = join26(start, rel);
|
|
25967
|
-
if (!existsSync26(path)) continue;
|
|
25968
|
-
try {
|
|
25969
|
-
const pkg = JSON.parse(readFileSync22(path, "utf-8"));
|
|
25970
|
-
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
25971
|
-
cachedVersion = pkg.version;
|
|
25972
|
-
return cachedVersion;
|
|
25973
|
-
}
|
|
25974
|
-
} catch {
|
|
25975
|
-
}
|
|
25976
|
-
}
|
|
25977
|
-
cachedVersion = "0.0.0";
|
|
26307
|
+
cachedVersion = readInstalledVersionFromDisk() ?? "0.0.0";
|
|
25978
26308
|
return cachedVersion;
|
|
25979
26309
|
}
|
|
25980
26310
|
var cachedVersion;
|