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

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();
@@ -13387,6 +13391,15 @@ function formatScanReport(result) {
13387
13391
  // src/tools/orient.ts
13388
13392
  import { z as z22 } from "zod";
13389
13393
  var PURPOSE_GAP_PREFIX = "purpose-gap-";
13394
+ function reportOrientWrapperBytes(toolResult, truncated) {
13395
+ try {
13396
+ const fullToolOutput = JSON.stringify({ content: toolResult.content ?? [] });
13397
+ const bytes = Buffer.byteLength(fullToolOutput, "utf8");
13398
+ void kernelMutation("agent.reportOrientMetric", { bytes, truncated }).catch(() => {
13399
+ });
13400
+ } catch {
13401
+ }
13402
+ }
13390
13403
  async function markOrientedWithSnapshotFallback(agentSessionId, coherenceSnapshot) {
13391
13404
  try {
13392
13405
  await kernelCall("agent.markOriented", {
@@ -13589,7 +13602,7 @@ function registerOrientTool(server) {
13589
13602
  orientationStatus2 = "failed";
13590
13603
  }
13591
13604
  }
13592
- return {
13605
+ const blankResult = {
13593
13606
  content: [{ type: "text", text: scanLines.join("\n") }],
13594
13607
  structuredContent: success(
13595
13608
  "Workspace is blank. Use the start tool for guided setup.",
@@ -13597,6 +13610,8 @@ function registerOrientTool(server) {
13597
13610
  [{ tool: "start", description: "Guided workspace setup", parameters: {} }]
13598
13611
  )
13599
13612
  };
13613
+ reportOrientWrapperBytes(blankResult, false);
13614
+ return blankResult;
13600
13615
  }
13601
13616
  const lines = [];
13602
13617
  const isLowReadiness = readiness && readiness.score < 50;
@@ -13740,7 +13755,7 @@ function registerOrientTool(server) {
13740
13755
  lines.push("");
13741
13756
  for (const err of errors) lines.push(`- ${err}`);
13742
13757
  }
13743
- return {
13758
+ const briefResult = {
13744
13759
  content: [{ type: "text", text: lines.join("\n") }],
13745
13760
  structuredContent: success(
13746
13761
  `Oriented (brief). Stage: ${readiness?.stage ?? "unknown"}.`,
@@ -13756,6 +13771,8 @@ function registerOrientTool(server) {
13756
13771
  }
13757
13772
  )
13758
13773
  };
13774
+ reportOrientWrapperBytes(briefResult, orientEntries?._truncated === true);
13775
+ return briefResult;
13759
13776
  }
13760
13777
  const orientStage = readiness?.stage ?? "seeded";
13761
13778
  if (isLowReadiness && wsCtx?.createdAt) {
@@ -14123,7 +14140,7 @@ function registerOrientTool(server) {
14123
14140
  lines.push("---");
14124
14141
  lines.push("_No active agent session. Call `session action=start` to begin a tracked session._");
14125
14142
  }
14126
- return {
14143
+ const fullResult = {
14127
14144
  content: [{ type: "text", text: lines.join("\n") }],
14128
14145
  structuredContent: success(
14129
14146
  `Oriented (full). Stage: ${orientStage}. ${isLowReadiness ? "Low readiness \u2014 gaps remain." : "Ready."}`,
@@ -14140,6 +14157,8 @@ function registerOrientTool(server) {
14140
14157
  }
14141
14158
  )
14142
14159
  };
14160
+ reportOrientWrapperBytes(fullResult, orientEntries?._truncated === true);
14161
+ return fullResult;
14143
14162
  })
14144
14163
  );
14145
14164
  }
@@ -14897,6 +14916,109 @@ function registerGovernanceTools(server) {
14897
14916
  trackWriteTool(tool);
14898
14917
  }
14899
14918
 
14919
+ // src/tools/documents.ts
14920
+ import { z as z26 } from "zod";
14921
+ var DOCUMENTS_ACTIONS = ["get-last-verified-brief"];
14922
+ var documentsSchema = z26.object({
14923
+ action: z26.enum(DOCUMENTS_ACTIONS).describe(
14924
+ "'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')."
14925
+ ),
14926
+ templateId: z26.string().describe(
14927
+ "Brief template identifier \u2014 currently 'steering-brief' is the only registered template."
14928
+ ),
14929
+ scopeKey: z26.string().describe(
14930
+ "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."
14931
+ )
14932
+ });
14933
+ function registerDocumentsTools(server) {
14934
+ server.registerTool(
14935
+ "documents",
14936
+ {
14937
+ title: "Documents",
14938
+ 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.",
14939
+ inputSchema: documentsSchema,
14940
+ annotations: {
14941
+ readOnlyHint: true,
14942
+ destructiveHint: false,
14943
+ idempotentHint: true,
14944
+ openWorldHint: false
14945
+ }
14946
+ },
14947
+ thinWrapper(async (args) => {
14948
+ const parsed = parseOrFail(documentsSchema, args);
14949
+ if (!parsed.ok) return parsed.result;
14950
+ const { action, templateId, scopeKey } = parsed.data;
14951
+ return runWithToolContext(
14952
+ { tool: "documents", action },
14953
+ () => handleGetLastVerifiedBrief(templateId, scopeKey)
14954
+ );
14955
+ })
14956
+ );
14957
+ }
14958
+ async function handleGetLastVerifiedBrief(templateId, scopeKey) {
14959
+ const result = await kernelQuery(
14960
+ "chainwork.getLastVerifiedBrief",
14961
+ { templateId, scopeKey }
14962
+ );
14963
+ if (!result.exists) {
14964
+ const text = `No verified brief found for template '${templateId}', scope '${scopeKey}'.
14965
+
14966
+ 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>'.`;
14967
+ return {
14968
+ content: [{ type: "text", text }],
14969
+ structuredContent: success(
14970
+ `No verified brief found for ${templateId} / ${scopeKey}.`,
14971
+ { exists: false }
14972
+ )
14973
+ };
14974
+ }
14975
+ const verifiedAtIso = new Date(result.verifiedAt).toISOString();
14976
+ if (result.summary === void 0) {
14977
+ const text = `Verified brief found for template '${templateId}', scope '${scopeKey}', verified at ${verifiedAtIso}.
14978
+
14979
+ 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.`;
14980
+ return {
14981
+ content: [{ type: "text", text }],
14982
+ structuredContent: success(
14983
+ `Legacy verified brief at ${verifiedAtIso} (no summary).`,
14984
+ { exists: true, summary: void 0, verifiedAt: result.verifiedAt }
14985
+ )
14986
+ };
14987
+ }
14988
+ const summary = result.summary;
14989
+ const lines = [
14990
+ `## Last verified brief \u2014 ${templateId} / ${scopeKey}`,
14991
+ `Verified at: ${verifiedAtIso} by ${summary.verifiedBy}`,
14992
+ `Chain synced at: ${new Date(summary.chainSyncedAt).toISOString()}`,
14993
+ "",
14994
+ `### At a glance (verbatim)`,
14995
+ summary.verifiedAtGlance || "(empty)",
14996
+ "",
14997
+ `### Workstreams at verification (${summary.verifiedWorkstreams.length})`
14998
+ ];
14999
+ for (const ws of summary.verifiedWorkstreams) {
15000
+ lines.push(
15001
+ `- **${ws.entryId}** ${ws.name} \u2014 status: ${ws.status}` + (ws.whatChanged ? ` \u2014 ${ws.whatChanged}` : "")
15002
+ );
15003
+ }
15004
+ if (summary.citedEntryIds.length > 0) {
15005
+ lines.push("");
15006
+ lines.push(`### Cited entries (${summary.citedEntryIds.length})`);
15007
+ lines.push(summary.citedEntryIds.join(", "));
15008
+ }
15009
+ return {
15010
+ content: [{ type: "text", text: lines.join("\n") }],
15011
+ structuredContent: success(
15012
+ `Verified brief found at ${verifiedAtIso} with ${summary.verifiedWorkstreams.length} workstream snapshot(s).`,
15013
+ {
15014
+ exists: true,
15015
+ summary,
15016
+ verifiedAt: result.verifiedAt
15017
+ }
15018
+ )
15019
+ };
15020
+ }
15021
+
14900
15022
  // src/resources/index.ts
14901
15023
  import { existsSync as existsSync4 } from "fs";
14902
15024
  import { dirname as dirname2, join, resolve as resolve5 } from "path";
@@ -15402,12 +15524,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
15402
15524
  }
15403
15525
 
15404
15526
  // src/prompts/index.ts
15405
- import { z as z26 } from "zod";
15527
+ import { z as z27 } from "zod";
15406
15528
  function registerPrompts(server) {
15407
15529
  server.prompt(
15408
15530
  "review-against-rules",
15409
15531
  "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')") },
15532
+ { domain: z27.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
15411
15533
  async ({ domain }) => {
15412
15534
  const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
15413
15535
  const rules = entries.filter((e) => e.data?.domain === domain);
@@ -15460,7 +15582,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
15460
15582
  server.prompt(
15461
15583
  "name-check",
15462
15584
  "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')") },
15585
+ { names: z27.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
15464
15586
  async ({ names }) => {
15465
15587
  const terms = await kernelQuery("chain.listEntries", { collectionSlug: "glossary" });
15466
15588
  const glossaryContext = terms.map(
@@ -15496,7 +15618,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
15496
15618
  server.prompt(
15497
15619
  "draft-decision-record",
15498
15620
  "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...')") },
15621
+ { context: z27.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
15500
15622
  async ({ context }) => {
15501
15623
  const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
15502
15624
  const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
@@ -15534,8 +15656,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
15534
15656
  "draft-rule-from-context",
15535
15657
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
15536
15658
  {
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')")
15659
+ observation: z27.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
15660
+ domain: z27.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
15539
15661
  },
15540
15662
  async ({ observation, domain }) => {
15541
15663
  const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
@@ -15679,6 +15801,7 @@ function createProductBrainServer() {
15679
15801
  registerFacilitateTools(server);
15680
15802
  registerAuditTools(server);
15681
15803
  registerGovernanceTools(server);
15804
+ registerDocumentsTools(server);
15682
15805
  registerResources(server);
15683
15806
  registerPrompts(server);
15684
15807
  return server;
@@ -15723,4 +15846,4 @@ export {
15723
15846
  createProductBrainServer,
15724
15847
  initFeatureFlags
15725
15848
  };
15726
- //# sourceMappingURL=chunk-ZF6N62QQ.js.map
15849
+ //# sourceMappingURL=chunk-GJFGULJV.js.map