@productbrain/mcp 0.0.1-beta.11174 → 0.0.1-beta.11180
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.
|
@@ -146,8 +146,6 @@ var metaSchema = z.object({
|
|
|
146
146
|
timing: z.object({
|
|
147
147
|
classifyMs: z.number().optional(),
|
|
148
148
|
createMs: z.number().optional(),
|
|
149
|
-
autoLinkMs: z.number().optional(),
|
|
150
|
-
userLinksMs: z.number().optional(),
|
|
151
149
|
qualityMs: z.number().optional(),
|
|
152
150
|
totalMs: z.number()
|
|
153
151
|
}).passthrough().optional(),
|
|
@@ -673,24 +671,11 @@ This client stopped waiting after ${error.elapsedMs}ms. ${recovery}`
|
|
|
673
671
|
})
|
|
674
672
|
};
|
|
675
673
|
}
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
let replayedLinks = [];
|
|
679
|
-
if (requested.length > 0) {
|
|
680
|
-
const batch = await kernelMutation("chain.createEntryRelations", {
|
|
681
|
-
relations: requested.map((l) => ({ fromEntryId: replay.entryId, toEntryId: l.to, type: l.type, proposedBy: "user" })),
|
|
682
|
-
sessionId
|
|
683
|
-
}).catch((err) => ({ results: requested.map((l) => ({ toEntryId: l.to, type: l.type, ok: false, error: err instanceof Error ? err.message : "unknown error" })) }));
|
|
684
|
-
replayedLinks = requested.map((l) => {
|
|
685
|
-
const row = batch.results.find((r) => r.toEntryId === l.to && r.type === l.type);
|
|
686
|
-
if (row?.ok) return { to: l.to, type: l.type, outcome: "written" };
|
|
687
|
-
const error = row?.error ?? "no answer for this link";
|
|
688
|
-
return /^Duplicate relation/.test(error) ? { to: l.to, type: l.type, outcome: "existing" } : { to: l.to, type: l.type, outcome: "failed", error };
|
|
689
|
-
});
|
|
690
|
-
}
|
|
674
|
+
function resumeReplayedCapture(replay, name) {
|
|
675
|
+
const links = replay.links ?? [];
|
|
691
676
|
const status2 = isTrustAccepted(replay.status) ? "committed" : "draft";
|
|
692
677
|
const summary = `Already captured as ${replay.entryId}; this re-send returned that entry and wrote no second one.`;
|
|
693
|
-
const linkLines =
|
|
678
|
+
const linkLines = links.map((l) => `- ${l.line}`);
|
|
694
679
|
return {
|
|
695
680
|
content: [{
|
|
696
681
|
type: "text",
|
|
@@ -709,7 +694,7 @@ Read it with \`context action=get\`.`
|
|
|
709
694
|
name,
|
|
710
695
|
status: status2,
|
|
711
696
|
replayed: true,
|
|
712
|
-
...
|
|
697
|
+
...links.length ? { links } : {}
|
|
713
698
|
})
|
|
714
699
|
};
|
|
715
700
|
}
|
|
@@ -1156,6 +1141,7 @@ var LABEL_DESCRIPTION_MAX_CHARS = 2e3;
|
|
|
1156
1141
|
var RELATION_BATCH_MAX = 25;
|
|
1157
1142
|
|
|
1158
1143
|
// src/tools/smart-capture.ts
|
|
1144
|
+
var CAPTURE_LINK_OUTCOMES = ["written", "refused"];
|
|
1159
1145
|
var GENERIC_PROFILE = {
|
|
1160
1146
|
governedDraft: false,
|
|
1161
1147
|
descriptionField: null,
|
|
@@ -1203,7 +1189,7 @@ var captureSchema = z4.object({
|
|
|
1203
1189
|
links: z4.array(z4.object({
|
|
1204
1190
|
to: z4.string().describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
|
|
1205
1191
|
type: z4.string().describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
|
|
1206
|
-
})).optional().describe("
|
|
1192
|
+
})).optional().describe("Links written with the entry in one transaction; each returns written or refused with a reason; a refused link fails nothing."),
|
|
1207
1193
|
sourceRef: z4.string().optional().describe("URI or path of the source document backing this entry (e.g. 'meeting-2026-03-28.md', 'import://batch-5'). Stored as top-level entry field, not in data."),
|
|
1208
1194
|
sourceExcerpt: z4.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Stored as top-level entry field, not in data."),
|
|
1209
1195
|
// WP-316 S3: Preview gate — dry-run mode. Returns what would happen, no DB writes.
|
|
@@ -1489,7 +1475,8 @@ var captureSuccessOutputSchema = z4.object({
|
|
|
1489
1475
|
// client-side auto-commit attempt that alone populated them (its gate refusal, the commit's advisory).
|
|
1490
1476
|
replayed: z4.literal(true).optional(),
|
|
1491
1477
|
// TASK-898: a re-sent capture answered with the first capture's entry
|
|
1492
|
-
|
|
1478
|
+
// TASK-897: the kernel's outcome per link, on a create and on a replay.
|
|
1479
|
+
links: z4.array(z4.object({ toEntryId: z4.string(), type: z4.string(), outcome: z4.enum(CAPTURE_LINK_OUTCOMES), reason: z4.string().optional(), line: z4.string() }).passthrough()).optional(),
|
|
1493
1480
|
fallbackApplied: z4.boolean().optional()
|
|
1494
1481
|
// round 2 (PRRT_kwDORS4ils6cTNbF): classifier field above still names the original (pre-substitution) prediction
|
|
1495
1482
|
}).strict();
|
|
@@ -1515,7 +1502,7 @@ var captureCompoundSchema = z4.object({
|
|
|
1515
1502
|
links: z4.array(z4.object({
|
|
1516
1503
|
to: z4.string().max(200).describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
|
|
1517
1504
|
type: z4.string().max(200).describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
|
|
1518
|
-
})).max(RELATION_BATCH_MAX).optional().describe("
|
|
1505
|
+
})).max(RELATION_BATCH_MAX).optional().describe("Links written with the entry in one transaction; each returns written or refused with a reason; a refused link fails nothing."),
|
|
1519
1506
|
sourceRef: z4.string().max(2e3).optional().describe("URI or path of the source document backing this entry."),
|
|
1520
1507
|
sourceExcerpt: z4.string().max(SOURCE_EXCERPT_MAX_CHARS).optional().describe("Verbatim excerpt from the source backing this entry's claims."),
|
|
1521
1508
|
preview: z4.boolean().optional().describe("If true, validates without writing."),
|
|
@@ -1640,7 +1627,6 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1640
1627
|
context,
|
|
1641
1628
|
data,
|
|
1642
1629
|
entryId: "",
|
|
1643
|
-
linksCreated: [],
|
|
1644
1630
|
collectionFields: col.fields ?? []
|
|
1645
1631
|
});
|
|
1646
1632
|
for (const [key, val] of Object.entries(inferred)) {
|
|
@@ -1695,6 +1681,7 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1695
1681
|
let coachingFromCreate;
|
|
1696
1682
|
let wasAutoCommittedServerSide = false;
|
|
1697
1683
|
let spineFloor;
|
|
1684
|
+
let linkOutcomes = [];
|
|
1698
1685
|
try {
|
|
1699
1686
|
const result2 = await kernelMutation("chain.createEntry", {
|
|
1700
1687
|
collectionSlug: resolvedCollection,
|
|
@@ -1712,16 +1699,11 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1712
1699
|
// WP-513: explicit rung-2 owner override (WP-753 O1b: its team derives server-side). TASK-681: `team` places a role or team.
|
|
1713
1700
|
...ownerRoleEntryId ? { ownerRoleEntryId } : {},
|
|
1714
1701
|
...team ? { teamRef: team } : {},
|
|
1715
|
-
//
|
|
1716
|
-
//
|
|
1717
|
-
|
|
1718
|
-
// A capture without links writes no relations (TASK-599): no defer, it keeps its LLM run.
|
|
1719
|
-
// Fail-safe: if the relations batch then creates nothing, the deferred row
|
|
1720
|
-
// keeps its delivered heuristic verdict with llmStatus 'skipped' (same
|
|
1721
|
-
// accepted shape as the CLI dying between create and re-eval).
|
|
1722
|
-
...links && links.length > 0 ? { deferLLMSchedule: true } : {}
|
|
1702
|
+
// TASK-897 (DEC-2113): the caller's links ride in this one request; the kernel resolves each target, judges the
|
|
1703
|
+
// verb and writes the row before its one coaching verdict, and answers per link (a refused link fails nothing).
|
|
1704
|
+
...links && links.length > 0 ? { links: links.map((l) => ({ toEntryId: l.to, type: l.type })) } : {}
|
|
1723
1705
|
});
|
|
1724
|
-
if (result2 && "replayed" in result2) return resumeReplayedCapture(result2, name
|
|
1706
|
+
if (result2 && "replayed" in result2) return resumeReplayedCapture(result2, name);
|
|
1725
1707
|
if (result2 && isPreviewCreateEntryResult(result2)) {
|
|
1726
1708
|
const preflight = await resolvePreviewPreflight(result2, links);
|
|
1727
1709
|
const dupCount = groundingReport.duplicates.length;
|
|
@@ -1791,6 +1773,7 @@ No DB writes \u2014 call without \`preview:true\` to capture for real.${result2.
|
|
|
1791
1773
|
normalizationMeta = created.normalization;
|
|
1792
1774
|
formativeHints = created.qualityHints ?? [];
|
|
1793
1775
|
coachingFromCreate = created.coaching;
|
|
1776
|
+
linkOutcomes = created.links ?? [];
|
|
1794
1777
|
resolveGapsForEntry(name, created.entryId);
|
|
1795
1778
|
const createdStatus = created.status;
|
|
1796
1779
|
if (createdStatus !== void 0 && isTrustAccepted(createdStatus)) wasAutoCommittedServerSide = true;
|
|
@@ -1826,78 +1809,8 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1826
1809
|
throw error;
|
|
1827
1810
|
}
|
|
1828
1811
|
const tAfterCreate = Date.now();
|
|
1829
|
-
const
|
|
1830
|
-
const userLinkResults = [];
|
|
1831
|
-
const pendingRelations = [];
|
|
1832
|
-
if (links && links.length > 0 && finalEntryId) {
|
|
1833
|
-
const slugByPrefix = new Map((await getCollections()).filter((c) => c.idPrefix).map((c) => [c.idPrefix, c.slug]));
|
|
1834
|
-
for (const link of links) {
|
|
1835
|
-
pendingRelations.push({
|
|
1836
|
-
fromEntryId: finalEntryId,
|
|
1837
|
-
toEntryId: link.to,
|
|
1838
|
-
type: link.type,
|
|
1839
|
-
// WP-474 E4-REWIRE: user-chosen type — mark provenance, NO confidence (human-chosen).
|
|
1840
|
-
proposedBy: "user",
|
|
1841
|
-
meta: { targetName: link.to, targetCollection: slugByPrefix.get(link.to.split("-")[0]) ?? "unknown" }
|
|
1842
|
-
});
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
const resolvedCK = canonicalKey;
|
|
1846
|
-
const batchRelationsPromise = pendingRelations.length > 0 ? kernelMutation(
|
|
1847
|
-
"chain.createEntryRelations",
|
|
1848
|
-
{
|
|
1849
|
-
// WP-474 E4-REWIRE: carry provenance through to the server seam.
|
|
1850
|
-
relations: pendingRelations.slice(0, 25).map((r) => ({
|
|
1851
|
-
fromEntryId: r.fromEntryId,
|
|
1852
|
-
toEntryId: r.toEntryId,
|
|
1853
|
-
type: r.type,
|
|
1854
|
-
...r.proposedBy !== void 0 ? { proposedBy: r.proposedBy } : {}
|
|
1855
|
-
})),
|
|
1856
|
-
sessionId: agentId ?? void 0
|
|
1857
|
-
}
|
|
1858
|
-
).catch((err) => {
|
|
1859
|
-
entryWarnings.push(`Linking partially failed: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
1860
|
-
return null;
|
|
1861
|
-
}) : Promise.resolve(null);
|
|
1862
|
-
const cardinalityPromise = resolvedCK ? kernelQuery("chain.checkCardinalityWarning", { canonicalKey: resolvedCK, excludeEntryId: internalId }).catch(() => null) : Promise.resolve(null);
|
|
1863
|
-
const coachingPromise = coachingFromCreate !== void 0 ? Promise.resolve(coachingFromCreate) : kernelMutation("quality.evaluateHeuristicAndSchedule", {
|
|
1864
|
-
entryId: finalEntryId,
|
|
1865
|
-
context: "capture"
|
|
1866
|
-
}).catch(() => null);
|
|
1867
|
-
const [batchResult, cardinalityCheck, coachingAtCreate] = await Promise.all([
|
|
1868
|
-
batchRelationsPromise,
|
|
1869
|
-
cardinalityPromise,
|
|
1870
|
-
coachingPromise
|
|
1871
|
-
]);
|
|
1872
|
-
let verdictResult = coachingAtCreate;
|
|
1873
|
-
if ((batchResult?.created ?? 0) > 0) {
|
|
1874
|
-
const reEval = await kernelMutation("quality.evaluateHeuristicAndSchedule", {
|
|
1875
|
-
entryId: finalEntryId,
|
|
1876
|
-
context: "capture"
|
|
1877
|
-
}).catch(() => null);
|
|
1878
|
-
if (reEval?.verdict) verdictResult = reEval;
|
|
1879
|
-
}
|
|
1880
|
-
const tAfterRelations = Date.now();
|
|
1881
|
-
if (batchResult) {
|
|
1882
|
-
for (let i = 0; i < pendingRelations.length; i++) {
|
|
1883
|
-
const pending = pendingRelations[i];
|
|
1884
|
-
const result2 = batchResult.results[i];
|
|
1885
|
-
if (!result2) continue;
|
|
1886
|
-
if (result2.ok) {
|
|
1887
|
-
userLinkResults.push({ label: `\u2713 ${pending.type} \u2192 ${pending.toEntryId}`, ok: true });
|
|
1888
|
-
linksCreated.push({
|
|
1889
|
-
targetEntryId: pending.toEntryId,
|
|
1890
|
-
targetName: pending.meta.targetName,
|
|
1891
|
-
targetCollection: pending.meta.targetCollection,
|
|
1892
|
-
relationType: pending.type
|
|
1893
|
-
});
|
|
1894
|
-
} else {
|
|
1895
|
-
userLinkResults.push({ label: `\u2717 ${pending.type} \u2192 ${pending.toEntryId}: ${result2.error ?? "failed"}`, ok: false });
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1812
|
+
const verdictResult = coachingFromCreate;
|
|
1899
1813
|
const tAfterQuality = Date.now();
|
|
1900
|
-
const cardinalityWarning = cardinalityCheck?.warning ?? null;
|
|
1901
1814
|
let coachingSection = "";
|
|
1902
1815
|
if (verdictResult?.verdict && verdictResult.verdict.tier !== "passive" && verdictResult.verdict.criteria.length > 0) {
|
|
1903
1816
|
coachingSection = formatRubricCoaching(verdictResult);
|
|
@@ -1969,10 +1882,10 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1969
1882
|
lines.push("");
|
|
1970
1883
|
lines.push(`**View in Cortex:** ${studioUrl}`);
|
|
1971
1884
|
}
|
|
1972
|
-
if (
|
|
1885
|
+
if (linkOutcomes.length > 0) {
|
|
1973
1886
|
lines.push("");
|
|
1974
1887
|
lines.push("## Requested links");
|
|
1975
|
-
for (const
|
|
1888
|
+
for (const l of linkOutcomes) lines.push(`- ${l.line}`);
|
|
1976
1889
|
}
|
|
1977
1890
|
if (finalStatus === "committed") {
|
|
1978
1891
|
lines.push("");
|
|
@@ -2005,9 +1918,16 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2005
1918
|
hint_fields: formativeHints.map((h) => h.field)
|
|
2006
1919
|
});
|
|
2007
1920
|
}
|
|
2008
|
-
const isWorkPackageOrBetCapture = isWorkPackageCapture ||
|
|
2009
|
-
const isBetOrGoal = isWorkPackageOrBetCapture ||
|
|
2010
|
-
|
|
1921
|
+
const isWorkPackageOrBetCapture = isWorkPackageCapture || canonicalKey === "bet";
|
|
1922
|
+
const isBetOrGoal = isWorkPackageOrBetCapture || canonicalKey === "goal";
|
|
1923
|
+
let hasStrategyLink = false;
|
|
1924
|
+
if (isBetOrGoal) {
|
|
1925
|
+
const written = linkOutcomes.filter((l) => l.outcome === "written");
|
|
1926
|
+
if (written.length > 0) {
|
|
1927
|
+
const slugByPrefix = new Map((await getCollections()).filter((c) => c.idPrefix).map((c) => [c.idPrefix, c.slug]));
|
|
1928
|
+
hasStrategyLink = written.some((l) => slugByPrefix.get(l.toEntryId.split("-")[0]) === "strategy");
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
2011
1931
|
if (isBetOrGoal && !hasStrategyLink) {
|
|
2012
1932
|
lines.push("");
|
|
2013
1933
|
lines.push(
|
|
@@ -2015,10 +1935,6 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2015
1935
|
);
|
|
2016
1936
|
await recordSessionActivity({ strategyLinkWarnedForEntryId: internalId });
|
|
2017
1937
|
}
|
|
2018
|
-
if (cardinalityWarning) {
|
|
2019
|
-
lines.push("");
|
|
2020
|
-
lines.push(`**Cardinality warning:** ${cardinalityWarning}`);
|
|
2021
|
-
}
|
|
2022
1938
|
if (entryWarnings.length > 0) {
|
|
2023
1939
|
lines.push("");
|
|
2024
1940
|
lines.push("## Validation Warnings");
|
|
@@ -2040,12 +1956,12 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2040
1956
|
lines.push(`2. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 fill missing fields`);
|
|
2041
1957
|
}
|
|
2042
1958
|
} else {
|
|
2043
|
-
if (
|
|
1959
|
+
if (linkOutcomes.length === 0) {
|
|
2044
1960
|
lines.push(`1. **Connect it:** find related entries with \`context action=search\`, then \`relations action=create from="${eid}" to="<ID>" type="<verb>"\``);
|
|
2045
1961
|
}
|
|
2046
|
-
lines.push(`${
|
|
1962
|
+
lines.push(`${linkOutcomes.length === 0 ? "2" : "1"}. **Review the possibility:** Keep this visible draft while it is an alternative. Accept only when commitment is intended and your role is authorized. Types reserved for human acceptance require human ratification. An audit advisory is not a reason to accept it.`);
|
|
2047
1963
|
if (hasQualityGaps) {
|
|
2048
|
-
lines.push(`${
|
|
1964
|
+
lines.push(`${linkOutcomes.length === 0 ? "3" : "2"}. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 fill missing fields`);
|
|
2049
1965
|
}
|
|
2050
1966
|
}
|
|
2051
1967
|
try {
|
|
@@ -2088,8 +2004,7 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2088
2004
|
const timing = {
|
|
2089
2005
|
classifyMs: tAfterClassify - timingStart,
|
|
2090
2006
|
createMs: tAfterCreate - tAfterClassify,
|
|
2091
|
-
|
|
2092
|
-
qualityMs: tAfterQuality - tAfterRelations,
|
|
2007
|
+
qualityMs: tAfterQuality - tAfterCreate,
|
|
2093
2008
|
totalMs
|
|
2094
2009
|
};
|
|
2095
2010
|
const toolResult = {
|
|
@@ -2120,6 +2035,8 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2120
2035
|
qualityHints: formativeHints,
|
|
2121
2036
|
// WP-745 TASK-160 (PR #901 review): the machine-readable projection — was reaching the human text only.
|
|
2122
2037
|
...spineFloor && { spineFloor },
|
|
2038
|
+
...linkOutcomes.length > 0 && { links: linkOutcomes },
|
|
2039
|
+
// TASK-897
|
|
2123
2040
|
// WP-318 S2: the possible-duplicate report — always included (empty when no ask match)
|
|
2124
2041
|
groundingReport
|
|
2125
2042
|
},
|
|
@@ -12910,4 +12827,4 @@ export {
|
|
|
12910
12827
|
createProductBrainServer,
|
|
12911
12828
|
initFeatureFlags
|
|
12912
12829
|
};
|
|
12913
|
-
//# sourceMappingURL=chunk-
|
|
12830
|
+
//# sourceMappingURL=chunk-SW67SRBA.js.map
|