@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.
- package/dist/{chunk-ZF6N62QQ.js → chunk-26IS4THT.js} +213 -14
- package/dist/chunk-26IS4THT.js.map +1 -0
- package/dist/http.js +126 -88
- package/dist/http.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZF6N62QQ.js.map +0 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
15538
|
-
domain:
|
|
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-
|
|
15925
|
+
//# sourceMappingURL=chunk-26IS4THT.js.map
|