@sonnechasser/ntrp 1.4.5 → 1.4.6
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 +335 -162
- package/dist/mcp/server.js +259 -84
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -10327,7 +10327,7 @@ handler: ../commands/thinkwithme.ts
|
|
|
10327
10327
|
|
|
10328
10328
|
Open a dedicated \`think \u203A\` channel to explore ideas, challenge assumptions, and pull evidence from your data.
|
|
10329
10329
|
Bare \`/thinkwithme\` enters the channel. Type \`/thinkwithme <topic>\` to seed the first turn.
|
|
10330
|
-
Requires an analysis and an AI key (\`/connect\`). Type \`done\` or \`cancel\` to return to
|
|
10330
|
+
Requires an analysis and an AI key (\`/connect\`). Type \`done\` or \`cancel\` to return to \u203A.
|
|
10331
10331
|
When you are ready to commit to a plan, say so or the partner can hand off to \`/strategy\`.`
|
|
10332
10332
|
},
|
|
10333
10333
|
{
|
|
@@ -11294,8 +11294,11 @@ function buildInvestigationTools() {
|
|
|
11294
11294
|
if (isWebRetrievalEnabled()) tools2.push(WEB_SEARCH_TOOL);
|
|
11295
11295
|
return tools2;
|
|
11296
11296
|
}
|
|
11297
|
-
function buildFreshNlTools() {
|
|
11298
|
-
const
|
|
11297
|
+
function buildFreshNlTools(opts = {}) {
|
|
11298
|
+
const conversationTools = opts.analysisReady ? CONVERSATION_TOOLS.filter(
|
|
11299
|
+
(t) => t.name !== "propose_scope" && t.name !== "confirm_scope" && t.name !== "run_compute"
|
|
11300
|
+
) : CONVERSATION_TOOLS;
|
|
11301
|
+
const tools2 = [...AGENTIC_TOOLS, ...conversationTools];
|
|
11299
11302
|
if (isWebRetrievalEnabled()) tools2.push(WEB_SEARCH_TOOL);
|
|
11300
11303
|
return tools2;
|
|
11301
11304
|
}
|
|
@@ -13871,7 +13874,6 @@ __export(phase_exports, {
|
|
|
13871
13874
|
buildConversationPrompt: () => buildConversationPrompt,
|
|
13872
13875
|
consumeOrientEmptyEnterCoach: () => consumeOrientEmptyEnterCoach,
|
|
13873
13876
|
formatPhaseLabel: () => formatPhaseLabel,
|
|
13874
|
-
getConversationPhaseBlock: () => getConversationPhaseBlock,
|
|
13875
13877
|
resolveConversationPhase: () => resolveConversationPhase,
|
|
13876
13878
|
sessionHasData: () => sessionHasData
|
|
13877
13879
|
});
|
|
@@ -13911,6 +13913,8 @@ function formatPhaseLabel(phase) {
|
|
|
13911
13913
|
switch (phase) {
|
|
13912
13914
|
case "orient":
|
|
13913
13915
|
return "setup";
|
|
13916
|
+
case "awaiting_data":
|
|
13917
|
+
return "data loading";
|
|
13914
13918
|
case "explore":
|
|
13915
13919
|
return "ready to ask";
|
|
13916
13920
|
case "think":
|
|
@@ -13919,15 +13923,24 @@ function formatPhaseLabel(phase) {
|
|
|
13919
13923
|
return phase.replace(/_/g, " ");
|
|
13920
13924
|
}
|
|
13921
13925
|
}
|
|
13926
|
+
function accentPromptHead(phase, sessionName) {
|
|
13927
|
+
const scope = sessionName ? ` ${sessionName}` : "";
|
|
13928
|
+
if (phase === "orient" || phase === "awaiting_data" || phase === "explore") {
|
|
13929
|
+
if (phase === "explore" && sessionName) {
|
|
13930
|
+
return paint("accent", `\u203A`) + chalk8.dim(`${scope} `);
|
|
13931
|
+
}
|
|
13932
|
+
return paint("accent", `\u203A `);
|
|
13933
|
+
}
|
|
13934
|
+
if (phase === "compute") {
|
|
13935
|
+
return paint("accent", `\u2026 `);
|
|
13936
|
+
}
|
|
13937
|
+
const word = PROMPT_LABELS[phase].replace(" \u203A", "");
|
|
13938
|
+
return paint("accent", `${word}${scope} \u203A `);
|
|
13939
|
+
}
|
|
13922
13940
|
function buildConversationPrompt(ctx) {
|
|
13923
13941
|
const phase = resolveConversationPhase(ctx);
|
|
13924
|
-
const
|
|
13925
|
-
const scope = ctx.sessionName ? ` ${ctx.sessionName}` : "";
|
|
13942
|
+
const sessionName = ctx.sessionName ?? "";
|
|
13926
13943
|
const action = resolveRecommendedAction(ctx);
|
|
13927
|
-
if (phase === "orient") {
|
|
13928
|
-
const enterHint2 = action ? chalk8.dim(`\u23CE ${action.hint} `) : "";
|
|
13929
|
-
return paint("accent", `${label} `) + enterHint2;
|
|
13930
|
-
}
|
|
13931
13944
|
if (phase === "explore") {
|
|
13932
13945
|
const mode = defaultExploreResponseMode(ctx, Math.floor(ctx.messages.length / 2));
|
|
13933
13946
|
const modeTag = mode === "brief" ? "brief" : "deep";
|
|
@@ -13935,34 +13948,15 @@ function buildConversationPrompt(ctx) {
|
|
|
13935
13948
|
const strategyWait = ctx.strategistState?.step === "awaiting_connect" ? chalk8.dim(" \xB7 strategy after /connect") : "";
|
|
13936
13949
|
const thinkWait = ctx.thinkState?.step === "awaiting_connect" ? chalk8.dim(" \xB7 think after /connect") : "";
|
|
13937
13950
|
const enterHint2 = action ? chalk8.dim(` \xB7 \u23CE ${action.hint}`) : "";
|
|
13938
|
-
return
|
|
13951
|
+
return accentPromptHead(phase, sessionName) + chalk8.dim(`${modeTag} \xB7 ${stack}`) + strategyWait + thinkWait + enterHint2 + " ";
|
|
13939
13952
|
}
|
|
13940
13953
|
if (phase === "think") {
|
|
13941
13954
|
const stack = canUseReplAi(ctx) ? formatActiveStackShort(ctx) : "no engine";
|
|
13942
13955
|
const enterHint2 = action ? chalk8.dim(` \xB7 \u23CE ${action.hint}`) : "";
|
|
13943
|
-
return
|
|
13956
|
+
return accentPromptHead(phase, sessionName) + chalk8.dim(stack) + enterHint2 + " ";
|
|
13944
13957
|
}
|
|
13945
13958
|
const enterHint = action ? chalk8.dim(`\u23CE ${action.hint} `) : "";
|
|
13946
|
-
return
|
|
13947
|
-
}
|
|
13948
|
-
function getConversationPhaseBlock(ctx) {
|
|
13949
|
-
const phase = resolveConversationPhase(ctx);
|
|
13950
|
-
const lines = [`Conversation phase: ${formatPhaseLabel(phase)}`];
|
|
13951
|
-
if (ctx.scope) {
|
|
13952
|
-
lines.push(`Intent: ${ctx.scope.intent_summary}`);
|
|
13953
|
-
lines.push(`Primary lens: ${ctx.scope.primary_lens}`);
|
|
13954
|
-
if (ctx.scope.audience) lines.push(`Audience: ${ctx.scope.audience}`);
|
|
13955
|
-
if (ctx.scope.time_horizon) lines.push(`Time horizon: ${ctx.scope.time_horizon}`);
|
|
13956
|
-
if (ctx.scope.confirmed_at) lines.push(`Scope confirmed: ${ctx.scope.confirmed_at}`);
|
|
13957
|
-
}
|
|
13958
|
-
if (ctx.dataset?.label) lines.push(`Dataset: ${ctx.dataset.label}`);
|
|
13959
|
-
if (ctx.gapAudit) {
|
|
13960
|
-
lines.push(`Can compute: ${ctx.gapAudit.can_compute}`);
|
|
13961
|
-
if (ctx.gapAudit.missing.length > 0) {
|
|
13962
|
-
lines.push(`Data gaps: ${ctx.gapAudit.missing.map((m) => m.label).join(", ")}`);
|
|
13963
|
-
}
|
|
13964
|
-
}
|
|
13965
|
-
return lines.join("\n");
|
|
13959
|
+
return accentPromptHead(phase, sessionName) + enterHint;
|
|
13966
13960
|
}
|
|
13967
13961
|
var PROMPT_LABELS;
|
|
13968
13962
|
var init_phase = __esm({
|
|
@@ -13978,9 +13972,9 @@ var init_phase = __esm({
|
|
|
13978
13972
|
PROMPT_LABELS = {
|
|
13979
13973
|
orient: "\u203A",
|
|
13980
13974
|
scope: "scope \u203A",
|
|
13981
|
-
awaiting_data: "
|
|
13975
|
+
awaiting_data: "\u203A",
|
|
13982
13976
|
compute: "\u2026",
|
|
13983
|
-
explore: "
|
|
13977
|
+
explore: "\u203A",
|
|
13984
13978
|
think: "think \u203A",
|
|
13985
13979
|
strategize: "strategy \u203A",
|
|
13986
13980
|
deliver: "ship \u203A"
|
|
@@ -16053,15 +16047,15 @@ var init_metric_definitions = __esm({
|
|
|
16053
16047
|
kind: "vital",
|
|
16054
16048
|
label: "Freshness",
|
|
16055
16049
|
group: "Vital Signs",
|
|
16056
|
-
tagline: "
|
|
16050
|
+
tagline: "Is the CRM still talking about live work \u2014 or a museum of old deals?",
|
|
16057
16051
|
how_computed: "NTRP computes a weighted average of people, organizations, and opportunities with recent activity. Open opportunities must also not be past-due. Default windows: people and organizations 90 days, opportunities 30 days. Weights are 35 / 30 / 35.",
|
|
16058
16052
|
formula_lines: [
|
|
16059
16053
|
"freshness = people%\xD70.35 + orgs%\xD70.30 + opps%\xD70.35",
|
|
16060
16054
|
"people/orgs fresh if activity within 90d",
|
|
16061
16055
|
"opps fresh if activity within 30d AND not past-due"
|
|
16062
16056
|
],
|
|
16063
|
-
meaning:
|
|
16064
|
-
expert_read:
|
|
16057
|
+
meaning: 'We treat Freshness as a truth check on the pipeline number. Quiet days mean different things by motion: in enterprise, 30 quiet days can be normal cadence; in a velocity book, that same silence often means the deal is already dead. Stale open amount is why we show "pipeline at risk."',
|
|
16058
|
+
expert_read: `A useful first cut is by owner and by stage \u2014 reds tend to clump on people or process, not sprinkle evenly. A sudden cliff is usually a broken integration or a departed rep, not gradual decay. Watch for the false positive too: bulk-imported records nobody has touched yet can look "stale" when they're just new.`,
|
|
16065
16059
|
deepdive: [
|
|
16066
16060
|
"Status bands: green 80 or more, yellow 60 or more, red below 60. Motion presets can change the windows.",
|
|
16067
16061
|
'Dollar translation: sum of amount on stale open opportunities \u2192 "pipeline at risk".',
|
|
@@ -16091,15 +16085,15 @@ var init_metric_definitions = __esm({
|
|
|
16091
16085
|
kind: "vital",
|
|
16092
16086
|
label: "Flow Rate",
|
|
16093
16087
|
group: "Vital Signs",
|
|
16094
|
-
tagline: "
|
|
16088
|
+
tagline: "Where does deal motion slow \u2014 and what's stuck behind that bottleneck?",
|
|
16095
16089
|
how_computed: "NTRP sets a base score from average open-deal age versus max_days. It then applies a penalty of up to 20 for the share of stuck deals. A deal is stuck when it has no update beyond stuck_days, or a past-due close. Status uses average open age, not the score alone.",
|
|
16096
16090
|
formula_lines: [
|
|
16097
16091
|
"base = 100 \xD7 (1 \u2212 avgOpenAge / max_days)",
|
|
16098
16092
|
"score = base \u2212 stuckSharePenalty (\u226420)",
|
|
16099
16093
|
"stuck = no update > stuck_days OR past-due close"
|
|
16100
16094
|
],
|
|
16101
|
-
meaning:
|
|
16102
|
-
expert_read: "
|
|
16095
|
+
meaning: 'We think of Flow Rate as velocity risk with a dollar tag. When deals stop advancing \u2014 and especially when close dates slip past due \u2014 next quarter starts to feel optimistic on paper. The dollar line is the amount still "stuck in pipeline."',
|
|
16096
|
+
expert_read: "We prefer stage age over deal age alone: find the stage where advancement collapses \u2014 it's usually one stage, not everywhere. Compare stuck age to this company's own median cycle rather than a generic norm. Stuck deals plus past-due closes are often a forecast-credibility problem before they're a revenue miss.",
|
|
16103
16097
|
deepdive: [
|
|
16104
16098
|
"Status uses average open age. Green is 45 days or less. Yellow is 90 days or less. Else red. Defaults: max_days 120, stuck_days 60.",
|
|
16105
16099
|
'Dollar translation: sum of amount on stuck deals \u2192 "stuck in pipeline".',
|
|
@@ -16131,15 +16125,15 @@ var init_metric_definitions = __esm({
|
|
|
16131
16125
|
kind: "vital",
|
|
16132
16126
|
label: "Drop Rate",
|
|
16133
16127
|
group: "Vital Signs",
|
|
16134
|
-
tagline: "Where
|
|
16128
|
+
tagline: "Where does paid demand fall between systems before anyone works it?",
|
|
16135
16129
|
how_computed: "NTRP blends cross-system retention and opportunity retention. Cross-system retention is marketing people also present in sales. Opportunity retention is open opportunities that are not abandoned. Default weights: cross-system 60 percent, opportunity retention 40 percent. Abandoned means open opportunities with no activity in 30 days.",
|
|
16136
16130
|
formula_lines: [
|
|
16137
16131
|
"score = crossSystemRetention\xD70.6 + oppRetention\xD70.4",
|
|
16138
16132
|
"cross-system = marketing people also in sales CRM",
|
|
16139
16133
|
"abandoned = open opps with no activity in 30d"
|
|
16140
16134
|
],
|
|
16141
|
-
meaning:
|
|
16142
|
-
expert_read:
|
|
16135
|
+
meaning: 'We read Drop Rate as the price of the handoff leak \u2014 budget already spent on leads that never reach a working rep. The dollar estimate is droppedCount \xD7 conversion \xD7 avg deal size: "est. lost at handoff."',
|
|
16136
|
+
expert_read: 'In practice this usually looks like a systems story: routing rules, unassigned territories, dead rep queues, or a sync gap between marketing and CRM \u2014 more often than "lazy reps." A useful first cut is by lead source; the leak tends to concentrate in one or two sources. The cheapest pipeline a business can buy is often the leads it already paid for.',
|
|
16143
16137
|
deepdive: [
|
|
16144
16138
|
"Status bands: green 80 or more, yellow 60 or more, red below 60.",
|
|
16145
16139
|
'Dollar translation: dropped \xD7 conversion \xD7 avg deal (fallback: drop% \xD7 open pipeline) \u2192 "est. lost at handoff".',
|
|
@@ -16170,15 +16164,15 @@ var init_metric_definitions = __esm({
|
|
|
16170
16164
|
kind: "vital",
|
|
16171
16165
|
label: "Signal:Noise",
|
|
16172
16166
|
group: "Vital Signs",
|
|
16173
|
-
tagline: "How much
|
|
16167
|
+
tagline: "How much of the team's effort still aims at deals that can close?",
|
|
16174
16168
|
how_computed: "Over a 90-day lookback, NTRP computes (signal activities / all activities) \xD7 100. Signal is activity linked to an open opportunity, a pipeline person, or a pipeline organization.",
|
|
16175
16169
|
formula_lines: [
|
|
16176
16170
|
"score = (signalCount / activityCount) \xD7 100",
|
|
16177
16171
|
"signal = linked to open opp / pipeline person / pipeline org",
|
|
16178
16172
|
"lookback = trailing 90 days"
|
|
16179
16173
|
],
|
|
16180
|
-
meaning:
|
|
16181
|
-
expert_read: "
|
|
16174
|
+
meaning: `We treat Signal:Noise as activity efficiency with a capacity price. Noise hours \xD7 hours_per_activity \xD7 rep_hourly_cost becomes "misdirected effort" \u2014 energy aimed at accounts that can't still close.`,
|
|
16175
|
+
expert_read: "A useful cut is by rep and by account status. Persistent noise often means reps keep working dead accounts they already know \u2014 stale targeting or coverage models more than a coaching gap. Also check whether activity is logged against closed or unlinked records; that can be a hygiene artifact pretending to be effort.",
|
|
16182
16176
|
deepdive: [
|
|
16183
16177
|
"Status bands: green 65 or more, yellow 40 or more, red below 40.",
|
|
16184
16178
|
"Dollar defaults: 0.25 hours/activity \xD7 $75/hr (config: hours_per_activity, rep_hourly_cost).",
|
|
@@ -16207,15 +16201,15 @@ var init_metric_definitions = __esm({
|
|
|
16207
16201
|
kind: "vital",
|
|
16208
16202
|
label: "Thread Depth",
|
|
16209
16203
|
group: "Vital Signs",
|
|
16210
|
-
tagline: "How fragile is
|
|
16204
|
+
tagline: "How fragile is revenue if one champion changes jobs?",
|
|
16211
16205
|
how_computed: "Percent of open deals with at least multi_thread_threshold distinct people active in the last 90 days. The default threshold is 2. People include opportunity-direct contacts and same-organization activity.",
|
|
16212
16206
|
formula_lines: [
|
|
16213
16207
|
"score = % open deals with \u22652 active people (90d)",
|
|
16214
16208
|
"people counted via opp contacts + same-org activity",
|
|
16215
16209
|
"threshold configurable (default 2)"
|
|
16216
16210
|
],
|
|
16217
|
-
meaning: "
|
|
16218
|
-
expert_read: "Weight by deal size
|
|
16211
|
+
meaning: "We read Thread Depth as resilience risk. One single-threaded mega-deal can outweigh ten small ones \u2014 and late-cycle single-threading is especially scary because champions change jobs. The dollar line sums amount on single-threaded deals.",
|
|
16212
|
+
expert_read: "Weight by deal size when you triage. Single-threading late in the cycle tends to matter more than early. In enterprise motions, thin threads are often a leading indicator of slipped quarters: the champion leaves and there's no second contact warm enough to carry the deal.",
|
|
16219
16213
|
deepdive: [
|
|
16220
16214
|
"Status bands: green 65 or more, yellow 40 or more, red below 40.",
|
|
16221
16215
|
'Dollar translation: sum of amount on single-threaded deals \u2192 "single-threaded".',
|
|
@@ -16247,14 +16241,14 @@ var init_metric_definitions = __esm({
|
|
|
16247
16241
|
kind: "saas",
|
|
16248
16242
|
label: "ARR",
|
|
16249
16243
|
group: "Revenue",
|
|
16250
|
-
tagline: "How big is the
|
|
16244
|
+
tagline: "How big is the engine \u2014 and what's the mix that got it there?",
|
|
16251
16245
|
how_computed: "Sum of amount on closed-won opportunities in the dataset. This is pipeline-inferred ARR when a pure subscription ledger is not available.",
|
|
16252
16246
|
formula_lines: [
|
|
16253
16247
|
"ARR \u2248 \u03A3 amount on closed-won opportunities",
|
|
16254
16248
|
"New + Expansion = growth \xB7 Churned + Contraction = leakage"
|
|
16255
16249
|
],
|
|
16256
|
-
meaning: "
|
|
16257
|
-
expert_read: "Always decompose growth into new versus expansion
|
|
16250
|
+
meaning: "We treat ARR as the size of the revenue engine \u2014 but the interesting story is the mix. New versus expansion growth, and how much leakage (churn + contraction) ate into it. From CRM exports we often infer this from closed-won amounts when a pure subscription ledger isn't available.",
|
|
16251
|
+
expert_read: "Always decompose growth into new versus expansion before you brief anyone \u2014 the mix is the story. Prefer this company's own trailing history over an external prior. When a number sits below its reliability_gate, we treat it as a hypothesis, not a fact.",
|
|
16258
16252
|
deepdive: [
|
|
16259
16253
|
"Companion metrics: New ARR, Expansion ARR, Churned ARR, Contraction ARR.",
|
|
16260
16254
|
"Estimation method may be ledger, pipeline_inferred, or snapshot. Read confidence and reliability_gate.",
|
|
@@ -16407,15 +16401,15 @@ var init_metric_definitions = __esm({
|
|
|
16407
16401
|
kind: "saas",
|
|
16408
16402
|
label: "Net Revenue Retention",
|
|
16409
16403
|
group: "Retention",
|
|
16410
|
-
tagline: "Would
|
|
16404
|
+
tagline: "Would the base still grow if new logo sales paused?",
|
|
16411
16405
|
how_computed: "startingArr = ARR + Churned + Contraction \u2212 Expansion. NRR = ((starting \u2212 Churned \u2212 Contraction + Expansion) / starting) \xD7 100.",
|
|
16412
16406
|
formula_lines: [
|
|
16413
16407
|
"starting = ARR + churned + contraction \u2212 expansion",
|
|
16414
16408
|
"NRR = (starting \u2212 churned \u2212 contraction + expansion) / starting \xD7 100",
|
|
16415
16409
|
"NRR = 100% + expansion% \u2212 contraction% \u2212 churn%"
|
|
16416
16410
|
],
|
|
16417
|
-
meaning: "
|
|
16418
|
-
expert_read: "Decompose before you judge
|
|
16411
|
+
meaning: "We love NRR because it answers a sharp question: would this business still grow if sales stopped selling new logos? Above 100% means the installed base compounds. From CRM we reconstruct the walk (+expansion \u2212contraction \u2212churn) and stay humble when the data is pipeline-inferred.",
|
|
16412
|
+
expert_read: "Decompose before you judge \u2014 the same 95% can be a churn/PMF problem or a no-expansion/packaging problem, and those have different owners. Rough priors by segment sit around 97% SMB, 108% mid-market, 118% enterprise medians; 110%+ is a strong signal at any stage.",
|
|
16419
16413
|
deepdive: [
|
|
16420
16414
|
"Always show the waterfall: +expansion \u2212contraction \u2212churn.",
|
|
16421
16415
|
"GRR is the floor. NRR adds expansion on top.",
|
|
@@ -16443,14 +16437,14 @@ var init_metric_definitions = __esm({
|
|
|
16443
16437
|
kind: "saas",
|
|
16444
16438
|
label: "Gross Revenue Retention",
|
|
16445
16439
|
group: "Retention",
|
|
16446
|
-
tagline: "How leaky is the bucket before expansion
|
|
16440
|
+
tagline: "How leaky is the bucket before expansion can hide it?",
|
|
16447
16441
|
how_computed: "GRR = ((startingArr \u2212 Churned \u2212 Contraction) / startingArr) \xD7 100. Expansion is excluded on purpose.",
|
|
16448
16442
|
formula_lines: [
|
|
16449
16443
|
"starting = ARR + churned + contraction \u2212 expansion",
|
|
16450
16444
|
"GRR = (starting \u2212 churned \u2212 contraction) / starting \xD7 100"
|
|
16451
16445
|
],
|
|
16452
|
-
meaning: "
|
|
16453
|
-
expert_read: "GRR
|
|
16446
|
+
meaning: "We treat GRR as the honesty metric \u2014 churn plus contraction only, with expansion deliberately left out. That way a shiny NRR can't paper over a leaky bucket. Rough priors: above 90% feels healthy; above 95% is strong for enterprise.",
|
|
16447
|
+
expert_read: "Always read GRR next to NRR. Expansion can make net retention look fine while the floor erodes. Owners tend to split: product/CS for logo churn, packaging for contraction.",
|
|
16454
16448
|
deepdive: [
|
|
16455
16449
|
"GRR never includes Expansion. That is the point.",
|
|
16456
16450
|
"Owners: product and CS for churn. Packaging for contraction."
|
|
@@ -16473,14 +16467,14 @@ var init_metric_definitions = __esm({
|
|
|
16473
16467
|
kind: "saas",
|
|
16474
16468
|
label: "Pipeline Coverage",
|
|
16475
16469
|
group: "Pipeline",
|
|
16476
|
-
tagline: "
|
|
16470
|
+
tagline: "Does next quarter already have enough real pipeline?",
|
|
16477
16471
|
how_computed: "Open pipeline amount divided by trailing-90-day closed-won amount.",
|
|
16478
16472
|
formula_lines: [
|
|
16479
16473
|
"Coverage = openPipeline / trailing_90d_won",
|
|
16480
16474
|
"required \u2248 1 / win_rate (discount for time left)"
|
|
16481
16475
|
],
|
|
16482
|
-
meaning:
|
|
16483
|
-
expert_read: "
|
|
16476
|
+
meaning: `We read coverage as an early "is next quarter already thin?" check. Priors scale with cycle length \u2014 roughly 3x for velocity/SMB, 4\u20135x for enterprise \u2014 but coverage without win rate is half a story. We also cross-check Freshness so zombies don't fake a healthy multiple.`,
|
|
16477
|
+
expert_read: "Required coverage is roughly 1 / win rate, discounted for time left in the period. Inflated stages and stale deals can manufacture coverage that isn't there. Pair with Win Rate and Freshness before you trust the brief.",
|
|
16484
16478
|
deepdive: [
|
|
16485
16479
|
"Always pair with Win Rate and Freshness.",
|
|
16486
16480
|
"Weighted Pipeline is the credibility-adjusted companion."
|
|
@@ -16565,14 +16559,14 @@ var init_metric_definitions = __esm({
|
|
|
16565
16559
|
kind: "saas",
|
|
16566
16560
|
label: "Pipeline Velocity",
|
|
16567
16561
|
group: "Pipeline",
|
|
16568
|
-
tagline: "
|
|
16562
|
+
tagline: "Four levers, one throughput number \u2014 which lever moved?",
|
|
16569
16563
|
how_computed: "(openOpps \xD7 avgDeal \xD7 winRate) / avgCycleDays. Requires 3 or more dated closed-won deals. Unit: $/day.",
|
|
16570
16564
|
formula_lines: [
|
|
16571
16565
|
"Velocity = (openOpps \xD7 avgDeal \xD7 winRate) / avgCycleDays",
|
|
16572
16566
|
"four levers: #opps \xB7 deal size \xB7 win rate \xB7 cycle days"
|
|
16573
16567
|
],
|
|
16574
|
-
meaning: "
|
|
16575
|
-
expert_read:
|
|
16568
|
+
meaning: "We like velocity because it's decision-ready: revenue throughput per day with four named levers (volume, size, win rate, cycle). When throughput changes, the interesting question is which lever moved \u2014 not just whether the number went up.",
|
|
16569
|
+
expert_read: 'Name the lever. A win-rate rise on falling opportunity volume often means qualification tightened, not that selling got "better." Needs a handful of dated wins; otherwise we leave it unavailable rather than invent a story.',
|
|
16576
16570
|
deepdive: [
|
|
16577
16571
|
"Needs 3 or more dated wins. Otherwise this metric is unavailable.",
|
|
16578
16572
|
"Pairs with Flow Rate (cycle) and Win Rate (conversion)."
|
|
@@ -16594,11 +16588,11 @@ var init_metric_definitions = __esm({
|
|
|
16594
16588
|
kind: "saas",
|
|
16595
16589
|
label: "Win Rate",
|
|
16596
16590
|
group: "Sales Efficiency",
|
|
16597
|
-
tagline: "Of
|
|
16591
|
+
tagline: "Of the deals we decide, how often do we win?",
|
|
16598
16592
|
how_computed: "closed-won / (won + lost) \xD7 100.",
|
|
16599
16593
|
formula_lines: ["Win Rate = won / (won + lost) \xD7 100"],
|
|
16600
|
-
meaning: "
|
|
16601
|
-
expert_read: "A rising win rate on falling opportunity volume
|
|
16594
|
+
meaning: "We read win rate as conversion of decided deals \u2014 are we winning the pipeline we create? Rough priors: ~25\u201335% SMB, ~18\u201325% mid-market, ~12\u201318% enterprise on qualified opportunities. It also sets how much coverage you need (~1 / win rate).",
|
|
16595
|
+
expert_read: "Watch the denominator. A rising win rate on falling opportunity volume often means qualification tightened, not that the team suddenly sells better. Cut by segment or source before company-wide coaching.",
|
|
16602
16596
|
deepdive: [
|
|
16603
16597
|
"Required coverage is about 1 / win rate.",
|
|
16604
16598
|
"Cut by segment or source before company-wide coaching."
|
|
@@ -18813,6 +18807,170 @@ var init_strategist_flow = __esm({
|
|
|
18813
18807
|
}
|
|
18814
18808
|
});
|
|
18815
18809
|
|
|
18810
|
+
// src/conversation/ghost-hints.ts
|
|
18811
|
+
function ghostExamplesForPhase(phase, limit = 2) {
|
|
18812
|
+
const hints = PHASE_GHOST_HINTS[phase] ?? [];
|
|
18813
|
+
return hints.slice(0, limit).map((h) => h.replace(/^try\s+/i, ""));
|
|
18814
|
+
}
|
|
18815
|
+
var PHASE_GHOST_HINTS;
|
|
18816
|
+
var init_ghost_hints = __esm({
|
|
18817
|
+
"src/conversation/ghost-hints.ts"() {
|
|
18818
|
+
"use strict";
|
|
18819
|
+
PHASE_GHOST_HINTS = {
|
|
18820
|
+
orient: [
|
|
18821
|
+
'try "pipeline health"',
|
|
18822
|
+
"try /deepdive",
|
|
18823
|
+
"try /deepdive guide",
|
|
18824
|
+
'try "is our retention real for the board?"',
|
|
18825
|
+
'try "what is the most expensive problem to solve?"',
|
|
18826
|
+
'try "board deck on Q3"'
|
|
18827
|
+
],
|
|
18828
|
+
awaiting_data: [
|
|
18829
|
+
'try "use demo data"',
|
|
18830
|
+
"try a CSV path",
|
|
18831
|
+
"try /ingest",
|
|
18832
|
+
'try "go ahead"'
|
|
18833
|
+
],
|
|
18834
|
+
explore: [
|
|
18835
|
+
'try "what is the most expensive problem?"',
|
|
18836
|
+
'try "how should we fix this?"',
|
|
18837
|
+
'try "which segment is weakest?"',
|
|
18838
|
+
'try "what is ARR?"',
|
|
18839
|
+
"try /deepdive freshness",
|
|
18840
|
+
'try "ship a board deck"'
|
|
18841
|
+
]
|
|
18842
|
+
};
|
|
18843
|
+
}
|
|
18844
|
+
});
|
|
18845
|
+
|
|
18846
|
+
// src/conversation/situation.ts
|
|
18847
|
+
function buildSituationalAwarenessBlock(ctx, opts = {}) {
|
|
18848
|
+
const phase = resolveConversationPhase(ctx);
|
|
18849
|
+
const channel = opts.channel ?? (phase === "think" ? "think" : "explore");
|
|
18850
|
+
const responseMode = opts.responseMode ?? (channel === "think" ? "deep" : defaultExploreResponseMode(ctx, Math.floor(ctx.messages.length / 2)));
|
|
18851
|
+
const analysisReady = isAnalysisReady(ctx);
|
|
18852
|
+
const action = resolveRecommendedAction(ctx);
|
|
18853
|
+
const examples = ghostExamplesForPhase(
|
|
18854
|
+
phase === "think" || phase === "strategize" || phase === "deliver" ? "explore" : phase,
|
|
18855
|
+
2
|
|
18856
|
+
);
|
|
18857
|
+
const lines = [
|
|
18858
|
+
"WHERE YOU ARE IN NTRP:",
|
|
18859
|
+
FUNNEL_SPINE,
|
|
18860
|
+
`Phase: ${phase} (${formatPhaseLabel(phase)}) \u2014 ${PHASE_MEANING[phase]}`
|
|
18861
|
+
];
|
|
18862
|
+
if (channel === "think") {
|
|
18863
|
+
lines.push("Channel: think \u2014 socratic partner; type done/cancel returns to \u203A");
|
|
18864
|
+
lines.push("Response mode: deep (tools on)");
|
|
18865
|
+
} else if (channel === "strategist") {
|
|
18866
|
+
lines.push("Channel: strategist \u2014 measurable plan engine owns sequencing");
|
|
18867
|
+
} else {
|
|
18868
|
+
lines.push(
|
|
18869
|
+
responseMode === "brief" ? "Response mode: brief (tools off; cite COMPLETED SESSION ANALYSIS; ~40\u201390 words)" : "Response mode: deep (tools on; new cuts and drill-downs)"
|
|
18870
|
+
);
|
|
18871
|
+
}
|
|
18872
|
+
if (ctx.scope) {
|
|
18873
|
+
const bits = [`Scope: ${ctx.scope.intent_summary}`, `lens=${ctx.scope.primary_lens}`];
|
|
18874
|
+
if (ctx.scope.audience) bits.push(`audience=${ctx.scope.audience}`);
|
|
18875
|
+
if (ctx.scope.time_horizon) bits.push(`horizon=${ctx.scope.time_horizon}`);
|
|
18876
|
+
lines.push(bits.join(" \xB7 "));
|
|
18877
|
+
if (ctx.scope.confirmed_at) lines.push(`Scope confirmed: ${ctx.scope.confirmed_at}`);
|
|
18878
|
+
}
|
|
18879
|
+
if (ctx.dataset?.label) {
|
|
18880
|
+
lines.push(`Dataset: ${ctx.dataset.label} \xB7 stage=${ctx.stage}`);
|
|
18881
|
+
} else if (sessionHasData(ctx)) {
|
|
18882
|
+
lines.push(`Dataset: loaded \xB7 stage=${ctx.stage}`);
|
|
18883
|
+
} else {
|
|
18884
|
+
lines.push(`Dataset: empty \xB7 stage=${ctx.stage}`);
|
|
18885
|
+
}
|
|
18886
|
+
if (ctx.gapAudit) {
|
|
18887
|
+
lines.push(`Can compute: ${ctx.gapAudit.can_compute}`);
|
|
18888
|
+
if (ctx.gapAudit.missing.length > 0) {
|
|
18889
|
+
lines.push(`Data gaps: ${ctx.gapAudit.missing.map((m) => m.label).join(", ")}`);
|
|
18890
|
+
}
|
|
18891
|
+
}
|
|
18892
|
+
if (analysisReady) {
|
|
18893
|
+
lines.push(
|
|
18894
|
+
"Already done: scope confirmed; compute ran; do not re-propose scope or re-run compute unless the user asks to restart"
|
|
18895
|
+
);
|
|
18896
|
+
lines.push(
|
|
18897
|
+
"Available now: answer from the artifact; suggest at most one of /strategy, /handoff, /deepdive <metric>, /thinkwithme, /playbook"
|
|
18898
|
+
);
|
|
18899
|
+
if (channel === "explore" && responseMode === "deep") {
|
|
18900
|
+
lines.push(
|
|
18901
|
+
"Tools: diagnostics + draft_strategy + draft_handoff (+ audit_data_gaps for re-check). Scope/compute orchestration is off."
|
|
18902
|
+
);
|
|
18903
|
+
}
|
|
18904
|
+
} else if (phase === "awaiting_data") {
|
|
18905
|
+
lines.push("Already done: scope confirmed; analysis not ready");
|
|
18906
|
+
lines.push(
|
|
18907
|
+
'Available now: tell the user to paste a CSV path, type "use demo data", or \u23CE go ahead when can_compute \u2014 you cannot open files'
|
|
18908
|
+
);
|
|
18909
|
+
} else if (phase === "scope") {
|
|
18910
|
+
lines.push("Already done: intent proposed; waiting on confirm");
|
|
18911
|
+
lines.push("Available now: wait for CLI confirm (\u23CE yes) \u2014 do not invent a different question");
|
|
18912
|
+
} else if (phase === "orient") {
|
|
18913
|
+
lines.push("Already done: nothing locked yet");
|
|
18914
|
+
lines.push("Available now: help the user name a focus; CLI will propose scope from their words");
|
|
18915
|
+
} else if (phase === "think") {
|
|
18916
|
+
lines.push("Already done: analysis complete; think channel open");
|
|
18917
|
+
lines.push("Available now: socratic exploration; draft_strategy / draft_handoff when ready to graduate");
|
|
18918
|
+
}
|
|
18919
|
+
lines.push(
|
|
18920
|
+
`Armed user next step: ${action ? `${action.hint} (\u23CE submits "${action.submit}")` : "none"}`
|
|
18921
|
+
);
|
|
18922
|
+
lines.push(`Engine connected: ${canUseReplAi(ctx) ? "yes" : "no"}`);
|
|
18923
|
+
if (examples.length > 0) {
|
|
18924
|
+
lines.push(`User may try: ${examples.join(" \xB7 ")}`);
|
|
18925
|
+
}
|
|
18926
|
+
lines.push(
|
|
18927
|
+
"Your job in this harness: fill the judgment gap after formula compute \u2014 connect dollars, name the expensive problem, do not restate scorecards the user already saw"
|
|
18928
|
+
);
|
|
18929
|
+
lines.push(
|
|
18930
|
+
"Pre-explore funnel gates are handled by the CLI; you speak in explore/think/strategist unless tools explicitly allow a restart"
|
|
18931
|
+
);
|
|
18932
|
+
return lines.join("\n");
|
|
18933
|
+
}
|
|
18934
|
+
function buildSituationalRuntimeFacts(ctx, opts = {}) {
|
|
18935
|
+
const phase = resolveConversationPhase(ctx);
|
|
18936
|
+
const channel = opts.channel ?? (phase === "think" ? "think" : "explore");
|
|
18937
|
+
const responseMode = opts.responseMode ?? (channel === "think" ? "deep" : defaultExploreResponseMode(ctx, Math.floor(ctx.messages.length / 2)));
|
|
18938
|
+
return {
|
|
18939
|
+
phase,
|
|
18940
|
+
mode: responseMode,
|
|
18941
|
+
lens: ctx.scope?.primary_lens,
|
|
18942
|
+
stage: ctx.stage,
|
|
18943
|
+
can_compute: ctx.gapAudit?.can_compute === void 0 ? void 0 : String(ctx.gapAudit.can_compute),
|
|
18944
|
+
engine: canUseReplAi(ctx) ? "connected" : "none"
|
|
18945
|
+
};
|
|
18946
|
+
}
|
|
18947
|
+
function getConversationPhaseBlock(ctx, opts = {}) {
|
|
18948
|
+
return buildSituationalAwarenessBlock(ctx, opts);
|
|
18949
|
+
}
|
|
18950
|
+
var FUNNEL_SPINE, PHASE_MEANING;
|
|
18951
|
+
var init_situation = __esm({
|
|
18952
|
+
"src/conversation/situation.ts"() {
|
|
18953
|
+
"use strict";
|
|
18954
|
+
init_context2();
|
|
18955
|
+
init_explore_mode();
|
|
18956
|
+
init_repl_api();
|
|
18957
|
+
init_ghost_hints();
|
|
18958
|
+
init_recommended_action();
|
|
18959
|
+
init_phase();
|
|
18960
|
+
FUNNEL_SPINE = "Funnel: orient \u2192 scope \u2192 data \u2192 compute \u2192 explore | side doors: think, strategy, ship";
|
|
18961
|
+
PHASE_MEANING = {
|
|
18962
|
+
orient: "setup \u2014 open chat; user has not locked a scope yet",
|
|
18963
|
+
scope: "scope confirm \u2014 CLI owns yes/adjust; do not re-propose unless asked",
|
|
18964
|
+
awaiting_data: "data gate \u2014 load CSV/demo or compute; CLI owns the gate",
|
|
18965
|
+
compute: "compute in progress \u2014 wait",
|
|
18966
|
+
explore: "analysis complete \u2014 free-form Q&A on this session's artifact",
|
|
18967
|
+
think: "socratic think channel \u2014 return to \u203A via done/cancel",
|
|
18968
|
+
strategize: "strategist flow \u2014 objective confirm or plan drafting",
|
|
18969
|
+
deliver: "ship/handoff wizard \u2014 CLI owns write confirms"
|
|
18970
|
+
};
|
|
18971
|
+
}
|
|
18972
|
+
});
|
|
18973
|
+
|
|
18816
18974
|
// src/services/think.ts
|
|
18817
18975
|
var think_exports = {};
|
|
18818
18976
|
__export(think_exports, {
|
|
@@ -18852,7 +19010,7 @@ async function runThinkTurn(input, ctx) {
|
|
|
18852
19010
|
setAgentContext(ctx);
|
|
18853
19011
|
try {
|
|
18854
19012
|
const analysisBlock = buildAnalysisBlock(ctx);
|
|
18855
|
-
const conversationBlock = getConversationPhaseBlock(ctx);
|
|
19013
|
+
const conversationBlock = getConversationPhaseBlock(ctx, { channel: "think", responseMode: "deep" });
|
|
18856
19014
|
const bundle = await loadSessionAnalysisBundle();
|
|
18857
19015
|
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
18858
19016
|
for await (const event of agenticFindings(snapshot, ctx.snapshot.divergences, {
|
|
@@ -18939,7 +19097,7 @@ var init_think = __esm({
|
|
|
18939
19097
|
"use strict";
|
|
18940
19098
|
init_spinner();
|
|
18941
19099
|
init_context2();
|
|
18942
|
-
|
|
19100
|
+
init_situation();
|
|
18943
19101
|
init_agent_context();
|
|
18944
19102
|
init_agentic_loop();
|
|
18945
19103
|
init_thread();
|
|
@@ -19003,7 +19161,7 @@ function printChannelIntro(seed) {
|
|
|
19003
19161
|
console.log(
|
|
19004
19162
|
" " + chalk19.dim(
|
|
19005
19163
|
"Socratic channel \u2014 explore, challenge assumptions, pull evidence. Type "
|
|
19006
|
-
) + chalk19.cyan("done") + chalk19.dim(" or ") + chalk19.cyan("cancel") + chalk19.dim(" to return to
|
|
19164
|
+
) + chalk19.cyan("done") + chalk19.dim(" or ") + chalk19.cyan("cancel") + chalk19.dim(" to return to \u203A.")
|
|
19007
19165
|
);
|
|
19008
19166
|
if (seed) {
|
|
19009
19167
|
console.log(" " + chalk19.dim("Seed: ") + seed);
|
|
@@ -19089,7 +19247,7 @@ function clearThinkFlow(ctx, reason) {
|
|
|
19089
19247
|
console.log();
|
|
19090
19248
|
console.log(
|
|
19091
19249
|
" " + chalk19.dim(
|
|
19092
|
-
reason === "done" ? "Think channel closed. Back to
|
|
19250
|
+
reason === "done" ? "Think channel closed. Back to \u203A." : "Think session cancelled. Continue exploration."
|
|
19093
19251
|
)
|
|
19094
19252
|
);
|
|
19095
19253
|
console.log();
|
|
@@ -20171,10 +20329,10 @@ async function runNaturalLanguage(input, ctx) {
|
|
|
20171
20329
|
setAgentContext(ctx);
|
|
20172
20330
|
try {
|
|
20173
20331
|
const analysisBlock = buildAnalysisBlock(ctx);
|
|
20174
|
-
const
|
|
20332
|
+
const responseMode = resolveExploreResponseMode(input, ctx, ctx.conversation.length);
|
|
20333
|
+
const conversationBlock = getConversationPhaseBlock(ctx, { responseMode });
|
|
20175
20334
|
const bundle = await loadSessionAnalysisBundle();
|
|
20176
20335
|
const sessionArtifact = hasAnyAnalysis(bundle) ? buildExploreContextBlock(bundle, ctx) : "";
|
|
20177
|
-
const responseMode = resolveExploreResponseMode(input, ctx, ctx.conversation.length);
|
|
20178
20336
|
for await (const event of agenticFindings(snapshot, ctx.snapshot.divergences, {
|
|
20179
20337
|
mode: "fresh",
|
|
20180
20338
|
userQuestion: input,
|
|
@@ -20272,6 +20430,7 @@ var init_nl = __esm({
|
|
|
20272
20430
|
"use strict";
|
|
20273
20431
|
init_spinner();
|
|
20274
20432
|
init_context2();
|
|
20433
|
+
init_situation();
|
|
20275
20434
|
init_phase();
|
|
20276
20435
|
init_agent_context();
|
|
20277
20436
|
init_orchestrator();
|
|
@@ -25499,12 +25658,12 @@ Cite numbers from here; call tools when you need a new cut or to pressure-test a
|
|
|
25499
25658
|
|
|
25500
25659
|
` : "";
|
|
25501
25660
|
const conversationSection = opts.conversationBlock ? `
|
|
25502
|
-
CONVERSATION STATE:
|
|
25503
25661
|
${opts.conversationBlock}
|
|
25504
25662
|
|
|
25505
25663
|
` : "";
|
|
25506
25664
|
const scratchSection = buildScratchBlock(opts.thinkState);
|
|
25507
25665
|
const socraticCraft = `HOW YOU THINK WITH THE USER (socratic partner \u2014 not a search box, not a strategist):
|
|
25666
|
+
- Pre-explore funnel gates are handled by the CLI; you speak in the think channel after analysis.
|
|
25508
25667
|
- You are a thinking partner. Prefer sharp questions that unlock the user's idea over dumping answers.
|
|
25509
25668
|
- Protect imagination: give "what if" space before the smell-test \u2014 do not kill creativity in the first sentence.
|
|
25510
25669
|
- Steelman the user's position before you attack it; then deliver one hard pushback grounded in evidence when possible.
|
|
@@ -25538,7 +25697,7 @@ ${buildCommandCatalogBlock()}
|
|
|
25538
25697
|
COMMAND SUGGESTION RULES:
|
|
25539
25698
|
- Suggest at most ONE command when it adds capability beyond your answer (e.g. \`/strategy\`, \`/handoff\`).
|
|
25540
25699
|
- Never claim a command was run. Never invent flags.
|
|
25541
|
-
- Type \`done\` or \`cancel\` leaves the think channel back to
|
|
25700
|
+
- Type \`done\` or \`cancel\` leaves the think channel back to \u203A.`;
|
|
25542
25701
|
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.
|
|
25543
25702
|
|
|
25544
25703
|
${companyContextSection3()}${operatorSection3()}${socraticCraft}
|
|
@@ -25588,7 +25747,7 @@ ${SAFETY_BLOCK}`;
|
|
|
25588
25747
|
const dynamic = [
|
|
25589
25748
|
"SESSION STATE (current \u2014 changes as the session progresses):",
|
|
25590
25749
|
...dynamicSections,
|
|
25591
|
-
buildRuntimeBlock()
|
|
25750
|
+
buildRuntimeBlock(opts.runtimeFacts ?? {})
|
|
25592
25751
|
].join("\n\n");
|
|
25593
25752
|
return { stable, dynamic };
|
|
25594
25753
|
}
|
|
@@ -25705,12 +25864,11 @@ The user already received the full report in the terminal. Answer follow-ups by
|
|
|
25705
25864
|
|
|
25706
25865
|
` : "";
|
|
25707
25866
|
const conversationSection = conversationBlock ? `
|
|
25708
|
-
CONVERSATION STATE:
|
|
25709
25867
|
${conversationBlock}
|
|
25710
|
-
Do not run compute until audit_data_gaps reports can_compute. If the user needs to load data, tell them to paste a CSV path or type "use demo data". You cannot open files.
|
|
25711
25868
|
|
|
25712
25869
|
` : "";
|
|
25713
25870
|
const conversationRules = `HOW A GREAT ANALYST CARRIES A CONVERSATION (read carefully \u2014 this is what separates you from a search box):
|
|
25871
|
+
- Pre-explore funnel gates (orient \u2192 scope \u2192 data \u2192 compute) are handled by the CLI. You speak after compute unless tools explicitly allow a restart.
|
|
25714
25872
|
- You have continuity. The conversation so far is provided to you as prior messages. Read it before answering. You remember every angle you have already explored, every number you have already surfaced, and every recommendation you have already made.
|
|
25715
25873
|
- Never repeat or re-recommend an analysis you have already presented in this conversation unless the user explicitly asks you to revisit, recall, or connect it. Repetition reads as forgetfulness and destroys trust.
|
|
25716
25874
|
- Each answer should ADVANCE the discussion: go deeper, introduce a genuinely new angle, challenge an assumption, or synthesize across what you have already found. Move the thinking forward.
|
|
@@ -25719,6 +25877,9 @@ Do not run compute until audit_data_gaps reports can_compute. If the user needs
|
|
|
25719
25877
|
- If you genuinely have nothing new to add on a topic, say so briefly and offer a different, higher-value direction \u2014 never pad with a recycled recommendation.`;
|
|
25720
25878
|
const briefJob = `YOUR JOB (BRIEF MODE \u2014 default after analysis is complete):
|
|
25721
25879
|
- Stay concise \u2014 roughly 40\u201390 words total, not a mini-report. Verdict first: your opening sentence is the headline the user should be able to repeat an hour later.
|
|
25880
|
+
- Lead with the single most expensive or most actionable implication from the completed session analysis \u2014 not a scorecard restatement.
|
|
25881
|
+
- Do not re-list all five vital signs unless the user asked for that inventory.
|
|
25882
|
+
- Prefer naming one playbook play or one next cut over a generic "want me to dig deeper?" close.
|
|
25722
25883
|
- You are at 30,000 ft by default. If they ask for detail or a new data cut, that routes to deep mode automatically \u2014 don't cram the weeds in here.
|
|
25723
25884
|
- Do NOT call tools unless the user explicitly asks for a new data cut (by rep, stage, list of deals, etc.). The completed analysis above is your source of truth.
|
|
25724
25885
|
- Name a playbook play only when the user asks what to do or which action to take.
|
|
@@ -25838,7 +25999,7 @@ ${briefCommandSection}` : "";
|
|
|
25838
25999
|
const dynamic = [
|
|
25839
26000
|
"SESSION STATE (current \u2014 changes as the session progresses):",
|
|
25840
26001
|
...dynamicSections,
|
|
25841
|
-
buildRuntimeBlock()
|
|
26002
|
+
buildRuntimeBlock(promptOptions.runtimeFacts ?? {})
|
|
25842
26003
|
].join("\n\n");
|
|
25843
26004
|
return { stable, dynamic };
|
|
25844
26005
|
}
|
|
@@ -25870,15 +26031,27 @@ async function* agenticFindings(computeResult, divergences, options) {
|
|
|
25870
26031
|
analysisBlock: options.analysisBlock,
|
|
25871
26032
|
conversationBlock: options.conversationBlock,
|
|
25872
26033
|
sessionArtifact: options.sessionArtifact,
|
|
25873
|
-
thinkState: options.ctx.thinkState
|
|
26034
|
+
thinkState: options.ctx.thinkState,
|
|
26035
|
+
runtimeFacts: buildSituationalRuntimeFacts(options.ctx, {
|
|
26036
|
+
channel: "think",
|
|
26037
|
+
responseMode: "deep"
|
|
26038
|
+
})
|
|
25874
26039
|
}) : mode === "fresh" ? buildFreshNlSystemPrompt(
|
|
25875
26040
|
options.sessionContext,
|
|
25876
26041
|
options.memoryBlock,
|
|
25877
26042
|
options.analysisBlock,
|
|
25878
26043
|
options.conversationBlock,
|
|
25879
|
-
{
|
|
26044
|
+
{
|
|
26045
|
+
responseMode,
|
|
26046
|
+
sessionArtifact: options.sessionArtifact,
|
|
26047
|
+
experiment,
|
|
26048
|
+
runtimeFacts: buildSituationalRuntimeFacts(options.ctx, {
|
|
26049
|
+
responseMode
|
|
26050
|
+
})
|
|
26051
|
+
}
|
|
25880
26052
|
) : buildSystemPrompt2();
|
|
25881
|
-
const
|
|
26053
|
+
const analysisReady = isAnalysisReady(options.ctx);
|
|
26054
|
+
const tools2 = useTools ? mode === "think" ? buildThinkTools() : mode === "fresh" ? buildFreshNlTools({ analysisReady }) : buildInvestigationTools() : [];
|
|
25882
26055
|
const toolCtx = { computeResult, divergences };
|
|
25883
26056
|
if (options.includeMetrics) {
|
|
25884
26057
|
try {
|
|
@@ -26047,6 +26220,8 @@ var init_agentic_loop = __esm({
|
|
|
26047
26220
|
init_prompt_parts();
|
|
26048
26221
|
init_think_prompt();
|
|
26049
26222
|
init_prompt();
|
|
26223
|
+
init_context2();
|
|
26224
|
+
init_situation();
|
|
26050
26225
|
MAX_ITERATIONS = 10;
|
|
26051
26226
|
INVESTIGATION_EVIDENCE_NUDGE_AFTER = 5;
|
|
26052
26227
|
BRIEF_MAX_TOKENS = 768;
|
|
@@ -26239,7 +26414,7 @@ async function* streamAsk(question, ctx) {
|
|
|
26239
26414
|
sessionContext: ctx.resumedSessionSummary,
|
|
26240
26415
|
includeMetrics: true,
|
|
26241
26416
|
analysisBlock: buildAnalysisBlock(ctx),
|
|
26242
|
-
conversationBlock: getConversationPhaseBlock(ctx),
|
|
26417
|
+
conversationBlock: getConversationPhaseBlock(ctx, { responseMode }),
|
|
26243
26418
|
sessionArtifact,
|
|
26244
26419
|
responseMode,
|
|
26245
26420
|
priorMessages: ctx.conversation,
|
|
@@ -26284,7 +26459,7 @@ var init_ask = __esm({
|
|
|
26284
26459
|
init_divergence();
|
|
26285
26460
|
init_repl_api();
|
|
26286
26461
|
init_context2();
|
|
26287
|
-
|
|
26462
|
+
init_situation();
|
|
26288
26463
|
init_session_analysis();
|
|
26289
26464
|
init_smoke_protocol();
|
|
26290
26465
|
}
|