@productbrain/mcp 0.0.1-beta.190 → 0.0.1-beta.192

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.
@@ -351,7 +351,11 @@ var TOUCH_EXCLUDED = /* @__PURE__ */ new Set([
351
351
  "agent.markOriented",
352
352
  "agent.recordActivity",
353
353
  "agent.recordWrapup",
354
- "agent.closeSession"
354
+ "agent.closeSession",
355
+ // WP-376 α.3: orient byte report is itself a heartbeat-equivalent observability
356
+ // write that already patches the same agentSessions row. A follow-up touchSession
357
+ // would create a redundant second write per orient call (DEC-50 OCC anti-pattern).
358
+ "agent.reportOrientMetric"
355
359
  ]);
356
360
  async function callGateway(fn, args) {
357
361
  const siteUrl = await resolveDeploymentUrl();
@@ -10966,7 +10970,8 @@ function formatWhatNeedsAttentionLines(wna) {
10966
10970
  const hasOverdue = (wna.overdueBets?.length ?? 0) > 0;
10967
10971
  const hasBlocked = (wna.blockedBets?.length ?? 0) > 0;
10968
10972
  const hasCritical = (wna.criticalPathBets?.length ?? 0) > 0;
10969
- if (!hasOverdue && !hasBlocked && !hasCritical) return [];
10973
+ const hasOutcomeBlockers = (wna.overdueOutcomeBlockers?.length ?? 0) > 0;
10974
+ if (!hasOverdue && !hasBlocked && !hasCritical && !hasOutcomeBlockers) return [];
10970
10975
  const lines = ["## What Needs Attention", ""];
10971
10976
  if (hasOverdue) {
10972
10977
  lines.push("**Overdue:**");
@@ -10983,6 +10988,13 @@ function formatWhatNeedsAttentionLines(wna) {
10983
10988
  for (const id of wna.criticalPathBets ?? []) lines.push(`- \`${id}\``);
10984
10989
  lines.push("");
10985
10990
  }
10991
+ if (hasOutcomeBlockers) {
10992
+ lines.push("**Overdue outcome verifications (advisory):**");
10993
+ for (const b of wna.overdueOutcomeBlockers ?? []) {
10994
+ lines.push(`- \`${b.betId}\` \u2192 \`${b.krId}\` (${b.daysOverdue}d overdue \u2014 ${b.scheduledVerificationDate})`);
10995
+ }
10996
+ lines.push("");
10997
+ }
10986
10998
  return lines;
10987
10999
  }
10988
11000
  function formatWhatNeedsAttentionBrief(wna) {
@@ -10990,11 +11002,16 @@ function formatWhatNeedsAttentionBrief(wna) {
10990
11002
  const overdue = wna.overdueBets ?? [];
10991
11003
  const blocked = wna.blockedBets ?? [];
10992
11004
  const critical = wna.criticalPathBets ?? [];
10993
- if (overdue.length === 0 && blocked.length === 0 && critical.length === 0) return [];
11005
+ const outcomeBlockers = wna.overdueOutcomeBlockers ?? [];
11006
+ if (overdue.length === 0 && blocked.length === 0 && critical.length === 0 && outcomeBlockers.length === 0)
11007
+ return [];
10994
11008
  const bullets = [
10995
11009
  ...overdue.slice(0, 2).map((id) => `\`${id}\` (overdue)`),
10996
11010
  ...blocked.slice(0, 2).map((id) => `\`${id}\` (blocked)`),
10997
- ...critical.slice(0, 1).map((id) => `\`${id}\` (critical path)`)
11011
+ ...critical.slice(0, 1).map((id) => `\`${id}\` (critical path)`),
11012
+ ...outcomeBlockers.length > 0 ? [
11013
+ `${outcomeBlockers.length} bet${outcomeBlockers.length > 1 ? "s" : ""} with overdue outcome verification`
11014
+ ] : []
10998
11015
  ].slice(0, 3);
10999
11016
  if (bullets.length === 0) return [];
11000
11017
  const lines = ["**What needs attention:**", ...bullets.map((b) => `- ${b}`), ""];
@@ -13387,6 +13404,15 @@ function formatScanReport(result) {
13387
13404
  // src/tools/orient.ts
13388
13405
  import { z as z22 } from "zod";
13389
13406
  var PURPOSE_GAP_PREFIX = "purpose-gap-";
13407
+ function reportOrientWrapperBytes(toolResult, truncated) {
13408
+ try {
13409
+ const fullToolOutput = JSON.stringify({ content: toolResult.content ?? [] });
13410
+ const bytes = Buffer.byteLength(fullToolOutput, "utf8");
13411
+ void kernelMutation("agent.reportOrientMetric", { bytes, truncated }).catch(() => {
13412
+ });
13413
+ } catch {
13414
+ }
13415
+ }
13390
13416
  async function markOrientedWithSnapshotFallback(agentSessionId, coherenceSnapshot) {
13391
13417
  try {
13392
13418
  await kernelCall("agent.markOriented", {
@@ -13589,7 +13615,7 @@ function registerOrientTool(server) {
13589
13615
  orientationStatus2 = "failed";
13590
13616
  }
13591
13617
  }
13592
- return {
13618
+ const blankResult = {
13593
13619
  content: [{ type: "text", text: scanLines.join("\n") }],
13594
13620
  structuredContent: success(
13595
13621
  "Workspace is blank. Use the start tool for guided setup.",
@@ -13597,6 +13623,8 @@ function registerOrientTool(server) {
13597
13623
  [{ tool: "start", description: "Guided workspace setup", parameters: {} }]
13598
13624
  )
13599
13625
  };
13626
+ reportOrientWrapperBytes(blankResult, false);
13627
+ return blankResult;
13600
13628
  }
13601
13629
  const lines = [];
13602
13630
  const isLowReadiness = readiness && readiness.score < 50;
@@ -13740,7 +13768,7 @@ function registerOrientTool(server) {
13740
13768
  lines.push("");
13741
13769
  for (const err of errors) lines.push(`- ${err}`);
13742
13770
  }
13743
- return {
13771
+ const briefResult = {
13744
13772
  content: [{ type: "text", text: lines.join("\n") }],
13745
13773
  structuredContent: success(
13746
13774
  `Oriented (brief). Stage: ${readiness?.stage ?? "unknown"}.`,
@@ -13756,6 +13784,8 @@ function registerOrientTool(server) {
13756
13784
  }
13757
13785
  )
13758
13786
  };
13787
+ reportOrientWrapperBytes(briefResult, orientEntries?._truncated === true);
13788
+ return briefResult;
13759
13789
  }
13760
13790
  const orientStage = readiness?.stage ?? "seeded";
13761
13791
  if (isLowReadiness && wsCtx?.createdAt) {
@@ -14123,7 +14153,7 @@ function registerOrientTool(server) {
14123
14153
  lines.push("---");
14124
14154
  lines.push("_No active agent session. Call `session action=start` to begin a tracked session._");
14125
14155
  }
14126
- return {
14156
+ const fullResult = {
14127
14157
  content: [{ type: "text", text: lines.join("\n") }],
14128
14158
  structuredContent: success(
14129
14159
  `Oriented (full). Stage: ${orientStage}. ${isLowReadiness ? "Low readiness \u2014 gaps remain." : "Ready."}`,
@@ -14140,6 +14170,8 @@ function registerOrientTool(server) {
14140
14170
  }
14141
14171
  )
14142
14172
  };
14173
+ reportOrientWrapperBytes(fullResult, orientEntries?._truncated === true);
14174
+ return fullResult;
14143
14175
  })
14144
14176
  );
14145
14177
  }
@@ -14897,6 +14929,109 @@ function registerGovernanceTools(server) {
14897
14929
  trackWriteTool(tool);
14898
14930
  }
14899
14931
 
14932
+ // src/tools/documents.ts
14933
+ import { z as z26 } from "zod";
14934
+ var DOCUMENTS_ACTIONS = ["get-last-verified-brief"];
14935
+ var documentsSchema = z26.object({
14936
+ action: z26.enum(DOCUMENTS_ACTIONS).describe(
14937
+ "'get-last-verified-brief': fetch the most recent verified brief snapshot for a (templateId, scopeKey) pair. Returns the verified summary so agents can build delta narratives ('since you last verified, X workstreams advanced')."
14938
+ ),
14939
+ templateId: z26.string().describe(
14940
+ "Brief template identifier \u2014 currently 'steering-brief' is the only registered template."
14941
+ ),
14942
+ scopeKey: z26.string().describe(
14943
+ "Canonical scope key. Use 'workspace:<workspaceId>' for the full workspace brief, or 'initiative:<INI-ID>' for an initiative-scoped brief. The same formula is applied at write time (chainwork/docKernel/scopeKey.ts), so passing the wrong shape returns exists:false."
14944
+ )
14945
+ });
14946
+ function registerDocumentsTools(server) {
14947
+ server.registerTool(
14948
+ "documents",
14949
+ {
14950
+ title: "Documents",
14951
+ description: "Read agent-facing snapshots of verified briefs.\n\n**Actions:**\n- `get-last-verified-brief`: returns the most recent verified-brief snapshot for a (templateId, scopeKey) pair, including the at-a-glance markdown, workstream snapshots, cited entry IDs, and verification timestamp.\n\nUse this to build delta narratives ('since the last verified brief on Tuesday, two workstreams advanced and one new tension was captured').\n\nReturns `{ exists: false }` when no row matches. Returns `{ exists: true, summary: <snapshot>, verifiedAt }` when an S4 row is found, or `{ exists: true, summary: undefined, verifiedAt }` for legacy backfilled rows that pre-date snapshot capture.",
14952
+ inputSchema: documentsSchema,
14953
+ annotations: {
14954
+ readOnlyHint: true,
14955
+ destructiveHint: false,
14956
+ idempotentHint: true,
14957
+ openWorldHint: false
14958
+ }
14959
+ },
14960
+ thinWrapper(async (args) => {
14961
+ const parsed = parseOrFail(documentsSchema, args);
14962
+ if (!parsed.ok) return parsed.result;
14963
+ const { action, templateId, scopeKey } = parsed.data;
14964
+ return runWithToolContext(
14965
+ { tool: "documents", action },
14966
+ () => handleGetLastVerifiedBrief(templateId, scopeKey)
14967
+ );
14968
+ })
14969
+ );
14970
+ }
14971
+ async function handleGetLastVerifiedBrief(templateId, scopeKey) {
14972
+ const result = await kernelQuery(
14973
+ "chainwork.getLastVerifiedBrief",
14974
+ { templateId, scopeKey }
14975
+ );
14976
+ if (!result.exists) {
14977
+ const text = `No verified brief found for template '${templateId}', scope '${scopeKey}'.
14978
+
14979
+ This is normal for a never-verified initiative or workspace. If you expected a row here, check that the scope key matches the format produced at write time: 'workspace:<workspaceId>' or 'initiative:<INI-ID>'.`;
14980
+ return {
14981
+ content: [{ type: "text", text }],
14982
+ structuredContent: success(
14983
+ `No verified brief found for ${templateId} / ${scopeKey}.`,
14984
+ { exists: false }
14985
+ )
14986
+ };
14987
+ }
14988
+ const verifiedAtIso = new Date(result.verifiedAt).toISOString();
14989
+ if (result.summary === void 0) {
14990
+ const text = `Verified brief found for template '${templateId}', scope '${scopeKey}', verified at ${verifiedAtIso}.
14991
+
14992
+ This row pre-dates snapshot capture (legacy backfill), so no summary is available. The verification timestamp itself is still authoritative \u2014 you can reason about staleness, just not delta narratives.`;
14993
+ return {
14994
+ content: [{ type: "text", text }],
14995
+ structuredContent: success(
14996
+ `Legacy verified brief at ${verifiedAtIso} (no summary).`,
14997
+ { exists: true, summary: void 0, verifiedAt: result.verifiedAt }
14998
+ )
14999
+ };
15000
+ }
15001
+ const summary = result.summary;
15002
+ const lines = [
15003
+ `## Last verified brief \u2014 ${templateId} / ${scopeKey}`,
15004
+ `Verified at: ${verifiedAtIso} by ${summary.verifiedBy}`,
15005
+ `Chain synced at: ${new Date(summary.chainSyncedAt).toISOString()}`,
15006
+ "",
15007
+ `### At a glance (verbatim)`,
15008
+ summary.verifiedAtGlance || "(empty)",
15009
+ "",
15010
+ `### Workstreams at verification (${summary.verifiedWorkstreams.length})`
15011
+ ];
15012
+ for (const ws of summary.verifiedWorkstreams) {
15013
+ lines.push(
15014
+ `- **${ws.entryId}** ${ws.name} \u2014 status: ${ws.status}` + (ws.whatChanged ? ` \u2014 ${ws.whatChanged}` : "")
15015
+ );
15016
+ }
15017
+ if (summary.citedEntryIds.length > 0) {
15018
+ lines.push("");
15019
+ lines.push(`### Cited entries (${summary.citedEntryIds.length})`);
15020
+ lines.push(summary.citedEntryIds.join(", "));
15021
+ }
15022
+ return {
15023
+ content: [{ type: "text", text: lines.join("\n") }],
15024
+ structuredContent: success(
15025
+ `Verified brief found at ${verifiedAtIso} with ${summary.verifiedWorkstreams.length} workstream snapshot(s).`,
15026
+ {
15027
+ exists: true,
15028
+ summary,
15029
+ verifiedAt: result.verifiedAt
15030
+ }
15031
+ )
15032
+ };
15033
+ }
15034
+
14900
15035
  // src/resources/index.ts
14901
15036
  import { existsSync as existsSync4 } from "fs";
14902
15037
  import { dirname as dirname2, join, resolve as resolve5 } from "path";
@@ -15402,12 +15537,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
15402
15537
  }
15403
15538
 
15404
15539
  // src/prompts/index.ts
15405
- import { z as z26 } from "zod";
15540
+ import { z as z27 } from "zod";
15406
15541
  function registerPrompts(server) {
15407
15542
  server.prompt(
15408
15543
  "review-against-rules",
15409
15544
  "Review code or a design decision against all business rules for a given domain. Fetches the rules and asks you to do a structured compliance review.",
15410
- { domain: z26.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
15545
+ { domain: z27.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
15411
15546
  async ({ domain }) => {
15412
15547
  const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
15413
15548
  const rules = entries.filter((e) => e.data?.domain === domain);
@@ -15460,7 +15595,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
15460
15595
  server.prompt(
15461
15596
  "name-check",
15462
15597
  "Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
15463
- { names: z26.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
15598
+ { names: z27.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
15464
15599
  async ({ names }) => {
15465
15600
  const terms = await kernelQuery("chain.listEntries", { collectionSlug: "glossary" });
15466
15601
  const glossaryContext = terms.map(
@@ -15496,7 +15631,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
15496
15631
  server.prompt(
15497
15632
  "draft-decision-record",
15498
15633
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
15499
- { context: z26.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
15634
+ { context: z27.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
15500
15635
  async ({ context }) => {
15501
15636
  const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
15502
15637
  const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
@@ -15534,8 +15669,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
15534
15669
  "draft-rule-from-context",
15535
15670
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
15536
15671
  {
15537
- observation: z26.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
15538
- domain: z26.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
15672
+ observation: z27.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
15673
+ domain: z27.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
15539
15674
  },
15540
15675
  async ({ observation, domain }) => {
15541
15676
  const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
@@ -15679,6 +15814,7 @@ function createProductBrainServer() {
15679
15814
  registerFacilitateTools(server);
15680
15815
  registerAuditTools(server);
15681
15816
  registerGovernanceTools(server);
15817
+ registerDocumentsTools(server);
15682
15818
  registerResources(server);
15683
15819
  registerPrompts(server);
15684
15820
  return server;
@@ -15703,6 +15839,69 @@ function readProcessEnvFlag(name) {
15703
15839
  return void 0;
15704
15840
  }
15705
15841
  var KILL = readViteEnvFlag("VITE_FEATURE_KILL_SWITCH") === "true" || readProcessEnvFlag("FEATURE_KILL_SWITCH") === "true";
15842
+ var WS = {
15843
+ RANDY_DEV_PRIMARY: "p5796jhec0nhyp417ekrt8x4w181t41f",
15844
+ RANDY_DEV_SECONDARY: "p5791wr86cj0thx2vxpqavj0mn82rssd",
15845
+ RANDY_PROD_PRIMARY: "mx784e9jjdqhsk2r0098bpvtes84e792",
15846
+ RANDY_PROD_SECONDARY: "mx74q0mkwn4r920q2837qk7dsx84pq60"
15847
+ };
15848
+ var FLAGS = {
15849
+ // WP-355 — MVP unlock: full Spine navigation (all modes + access patterns visible)
15850
+ "mvp-unlock-spine": {
15851
+ default: false,
15852
+ workspaceAllow: [
15853
+ WS.RANDY_DEV_PRIMARY,
15854
+ WS.RANDY_DEV_SECONDARY,
15855
+ WS.RANDY_PROD_PRIMARY,
15856
+ WS.RANDY_PROD_SECONDARY
15857
+ ]
15858
+ },
15859
+ // WP-355 — MVP unlock: Bridge full view (beyond locked /bridge dashboard)
15860
+ "mvp-unlock-bridge-full": {
15861
+ default: false,
15862
+ workspaceAllow: [
15863
+ WS.RANDY_DEV_PRIMARY,
15864
+ WS.RANDY_DEV_SECONDARY,
15865
+ WS.RANDY_PROD_PRIMARY,
15866
+ WS.RANDY_PROD_SECONDARY
15867
+ ]
15868
+ },
15869
+ // WP-355 — MVP unlock: Rail full context (beyond Rail context-only mode)
15870
+ "mvp-unlock-rail-full": {
15871
+ default: false,
15872
+ workspaceAllow: [
15873
+ WS.RANDY_DEV_PRIMARY,
15874
+ WS.RANDY_DEV_SECONDARY,
15875
+ WS.RANDY_PROD_PRIMARY,
15876
+ WS.RANDY_PROD_SECONDARY
15877
+ ]
15878
+ },
15879
+ // WP-355 — MVP unlock: Import surface enabled
15880
+ "mvp-unlock-import": {
15881
+ default: false,
15882
+ workspaceAllow: [
15883
+ WS.RANDY_DEV_PRIMARY,
15884
+ WS.RANDY_DEV_SECONDARY,
15885
+ WS.RANDY_PROD_PRIMARY,
15886
+ WS.RANDY_PROD_SECONDARY
15887
+ ]
15888
+ },
15889
+ // WP-355 — Brain Chat: withheld from MVP by default; allowlisted for Randy's workspaces.
15890
+ "mvp-unlock-brain-chat": {
15891
+ default: false,
15892
+ workspaceAllow: [
15893
+ WS.RANDY_DEV_PRIMARY,
15894
+ WS.RANDY_DEV_SECONDARY,
15895
+ WS.RANDY_PROD_PRIMARY,
15896
+ WS.RANDY_PROD_SECONDARY
15897
+ ]
15898
+ },
15899
+ // WP-373 E3 — Routing Resolver SSOT killswitch.
15900
+ // When true, the new pure resolver short-circuits and mount sites execute the
15901
+ // legacy inline redirect/error logic verbatim. Default false: resolver runs.
15902
+ // FEATURE_KILL_SWITCH env beats this flag (precedence per .claude/rules/feature-flags.md).
15903
+ "routing-resolver-killswitch": false
15904
+ };
15706
15905
 
15707
15906
  // src/featureFlags.ts
15708
15907
  function initFeatureFlags(_posthogClient) {
@@ -15723,4 +15922,4 @@ export {
15723
15922
  createProductBrainServer,
15724
15923
  initFeatureFlags
15725
15924
  };
15726
- //# sourceMappingURL=chunk-ZF6N62QQ.js.map
15925
+ //# sourceMappingURL=chunk-26IS4THT.js.map