@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.
@@ -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 ask \u203A.
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 tools2 = [...AGENTIC_TOOLS, ...CONVERSATION_TOOLS];
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 label = PROMPT_LABELS[phase];
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 paint("accent", `ask${scope} \u203A `) + chalk8.dim(`${modeTag} \xB7 ${stack}`) + strategyWait + thinkWait + enterHint2 + " ";
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 paint("accent", `think${scope} \u203A `) + chalk8.dim(stack) + enterHint2 + " ";
13956
+ return accentPromptHead(phase, sessionName) + chalk8.dim(stack) + enterHint2 + " ";
13944
13957
  }
13945
13958
  const enterHint = action ? chalk8.dim(`\u23CE ${action.hint} `) : "";
13946
- return paint("accent", `${label.replace(" \u203A", "")}${scope} \u203A `) + enterHint;
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: "data \u203A",
13975
+ awaiting_data: "\u203A",
13982
13976
  compute: "\u2026",
13983
- explore: "ask \u203A",
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: "Does the CRM report which records are still active?",
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: "Board question: how much of this pipeline is real versus fiction? Dollar value equals the sum of amount on stale opportunities: pipeline at risk.",
16064
- expert_read: "Cut by owner and by stage first. Freshness reds concentrate on people or process. They rarely spread evenly. In a long-cycle enterprise motion, 30 quiet days can be normal cadence. In a velocity motion, 30 quiet days is a dead deal. A sudden cliff usually means a broken integration or a departed rep. It is not gradual decay. Check this false positive: bulk-imported records that nobody has touched yet.",
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: "How fast do deals move, and where do they stop?",
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: "Board question: is next quarter slipping because deals are stuck? Dollar value equals the amount stuck in pipeline.",
16102
- expert_read: "Cut by stage age, not only deal age. Find the stage where deals stop. That is usually one stage. Compare stuck-deal age to this company's own median cycle. Do not use a generic norm. Stuck deals plus past-due close dates signal optimistic forecasting. That is a credibility problem before it is a revenue problem.",
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 do leads drop between systems?",
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: "Board question: how much pipeline did we pay for and never work? Dollar value = droppedCount \xD7 conversionRate \xD7 avgDealSize \u2014 est. lost at handoff.",
16142
- expert_read: "This is almost always a systems failure. Causes include routing rules, unassigned territories, dead rep queues, or a sync gap between marketing and CRM. It is not lazy reps. First cut by lead source. The leak usually concentrates in one or two sources. The cheapest pipeline this business can buy is the leads it already paid for.",
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 activity is aimed at deals that can still close?",
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: "Board question: are we burning capacity on dead accounts? Dollar value = noiseCount \xD7 hours_per_activity \xD7 rep_hourly_cost \u2014 misdirected effort.",
16181
- expert_read: "Cut by rep and by account status. Noise usually means reps work dead accounts they already know. Targeting and account lists are stale. Persistent noise is a coverage-model problem, not a coaching problem. Check whether activity is logged against closed or unlinked records. That is often a hygiene artifact.",
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 the pipeline if one champion stops?",
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: "Board question: how much revenue is lost if one contact changes jobs? Dollar value equals the sum of amount on single-threaded deals.",
16218
- expert_read: "Weight by deal size. One single-threaded mega-deal outweighs ten small ones. Single-threading late in the cycle is far more dangerous than early. In enterprise motions, Thread Depth is a leading indicator of slipped quarters. Champions change jobs. There is no second contact.",
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 revenue engine, and from where?",
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: "Board question: how fast are we growing, and from where? Always decompose growth into new versus expansion. The mix is the story.",
16257
- expert_read: "Always decompose growth into new versus expansion. The mix is the story. Prefer this company's own trailing history over any external prior. A number below its reliability_gate is a hypothesis, not a fact.",
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 this business grow if sales stopped selling?",
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: "Board question: would this business grow if sales stopped selling? A value above 100% means growth from existing customers.",
16418
- expert_read: "Decompose before you judge. The same 95% can be a churn problem (product/PMF) or a no-expansion problem (packaging/motion). Those have different owners. Priors by segment: about 97% SMB, about 108% mid-market, about 118% enterprise medians. 110% or more is a strong signal at any stage.",
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 hides it?",
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: "Board question: how leaky is the bucket before expansion hides it? Prior: above 90% is healthy. Above 95% is strong for enterprise.",
16453
- expert_read: "GRR is the honesty metric. Expansion can make NRR look fine while GRR erodes. Always read both.",
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: "Is next quarter already at risk?",
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: "Board question: is next quarter already at risk? Priors scale with cycle length: about 3x for velocity/SMB, 4 to 5x for enterprise.",
16483
- expert_read: "Coverage has no meaning without win rate. Required coverage is about 1 / win rate, discounted for time left in the period. Inflated stages and zombie deals fake coverage. Cross-check with Freshness before you trust it.",
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: "Revenue throughput per day. Four levers, one number.",
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: "Board question: which lever moved when throughput changed? This is the most decision-ready pipeline metric.",
16575
- expert_read: "When velocity changes, name which lever moved. A win-rate rise on falling opportunity volume is qualification tightening, not improvement.",
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 decided deals, how often do we win?",
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: "Board question: are we converting the pipeline we create? Priors: 25\u201335% SMB, 18\u201325% mid-market, 12\u201318% enterprise on qualified opportunities.",
16601
- expert_read: "A rising win rate on falling opportunity volume is qualification tightening, not improvement. Check the denominator.",
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
- init_phase();
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 ask \u203A.")
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 ask \u203A." : "Think session cancelled. Continue exploration."
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 conversationBlock = getConversationPhaseBlock(ctx);
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 ask \u203A.`;
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
- { responseMode, sessionArtifact: options.sessionArtifact, experiment }
26044
+ {
26045
+ responseMode,
26046
+ sessionArtifact: options.sessionArtifact,
26047
+ experiment,
26048
+ runtimeFacts: buildSituationalRuntimeFacts(options.ctx, {
26049
+ responseMode
26050
+ })
26051
+ }
25880
26052
  ) : buildSystemPrompt2();
25881
- const tools2 = useTools ? mode === "think" ? buildThinkTools() : mode === "fresh" ? buildFreshNlTools() : buildInvestigationTools() : [];
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
- init_phase();
26462
+ init_situation();
26288
26463
  init_session_analysis();
26289
26464
  init_smoke_protocol();
26290
26465
  }