@productbrain/mcp 0.0.1-beta.10771 → 0.0.1-beta.10778
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.
|
@@ -625,98 +625,80 @@ async function foldWhyIntoData(slug, why, userData) {
|
|
|
625
625
|
// src/tools/smart-capture.ts
|
|
626
626
|
import { z as z4 } from "zod/v3";
|
|
627
627
|
|
|
628
|
+
// src/lib/statusCategories.ts
|
|
629
|
+
var STATUS_CATEGORIES = ["draft", "current", "past", "discarded"];
|
|
630
|
+
var STATUS_CATEGORY_SET = new Set(STATUS_CATEGORIES);
|
|
631
|
+
var TRUST_ACCEPTED_WORDS = ["accepted", "active", "current"];
|
|
632
|
+
function isTrustAccepted(status2) {
|
|
633
|
+
return TRUST_ACCEPTED_WORDS.includes(status2);
|
|
634
|
+
}
|
|
635
|
+
var TRUST_ARCHIVED_WORDS = ["archived", "deprecated", "past", "discarded"];
|
|
636
|
+
var ENTRY_STATUS_READ_WORDS = ["draft", ...TRUST_ACCEPTED_WORDS, ...TRUST_ARCHIVED_WORDS];
|
|
637
|
+
var ENTRY_STATUS_WRITE_WORDS = ["draft", "archived"];
|
|
638
|
+
|
|
628
639
|
// src/lib/captureTimeoutOutcome.ts
|
|
629
640
|
function isGatewayTimeout(error) {
|
|
630
641
|
return error?.name === "GatewayTimeoutError";
|
|
631
642
|
}
|
|
632
|
-
function buildCaptureOutcomeUnknownResult(error,
|
|
633
|
-
|
|
634
|
-
const recovery = `Search for "${name}" before retrying \u2014 a retry will duplicate it if the write landed. If found, read back its commit status and coaching output before assuming either is missing.`;
|
|
643
|
+
function buildCaptureOutcomeUnknownResult(error, msg) {
|
|
644
|
+
const recovery = error.mayHaveLanded ? "Re-send the same capture; the kernel replays it if the first one landed, so it never writes twice." : "Re-run the preview; nothing was written.";
|
|
635
645
|
return {
|
|
636
646
|
content: [{
|
|
637
647
|
type: "text",
|
|
638
|
-
text: `# Capture
|
|
648
|
+
text: `# Capture Timed Out
|
|
639
649
|
|
|
640
650
|
${msg}
|
|
641
651
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
**What definitely did NOT run:** relation linking \u2014 a separate mutation issued after the create returns, so it is never reached when create itself times out.
|
|
645
|
-
|
|
646
|
-
**What may also have run \u2014 outcome unknown, same reason as the entry itself:** auto-commit and quality coaching. Both execute INSIDE the create call, before it returns, so if the server finished after this client stopped waiting, they ran too. Do not assume either is absent \u2014 read the entry back to find out.
|
|
647
|
-
|
|
648
|
-
**Do this next:** ${recovery}`
|
|
652
|
+
This client stopped waiting after ${error.elapsedMs}ms. ${recovery}`
|
|
649
653
|
}],
|
|
650
|
-
structuredContent: failure(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
{
|
|
656
|
-
// Structured so a caller can branch on this without parsing prose — the
|
|
657
|
-
// reconciliation signal TEN-1126 says the capture path never had.
|
|
658
|
-
partialSuccess: true,
|
|
659
|
-
mayHaveLanded: error.mayHaveLanded,
|
|
660
|
-
budgetMs: error.budgetMs,
|
|
661
|
-
elapsedMs: error.elapsedMs,
|
|
662
|
-
completedBeforeTimeout: [],
|
|
663
|
-
// WP-575 review (P1): genuinely definite — relation linking is a separate mutation
|
|
664
|
-
// never reached when create throws. See the doc comment above for why `commit` and
|
|
665
|
-
// `coaching` were removed from this list.
|
|
666
|
-
didNotRun: ["relations"],
|
|
667
|
-
// WP-575 review (P1): distinct from `didNotRun` on purpose — these ran INSIDE the
|
|
668
|
-
// same server call as the create, so their outcome is exactly as unknown as the
|
|
669
|
-
// entry's. A caller must read the entry back, never assume either ran or didn't.
|
|
670
|
-
mayHaveRun: ["commit", "coaching"]
|
|
671
|
-
}
|
|
672
|
-
)
|
|
654
|
+
structuredContent: failure("CAPTURE_OUTCOME_UNKNOWN", msg, recovery, [], {
|
|
655
|
+
mayHaveLanded: error.mayHaveLanded,
|
|
656
|
+
budgetMs: error.budgetMs,
|
|
657
|
+
elapsedMs: error.elapsedMs
|
|
658
|
+
})
|
|
673
659
|
};
|
|
674
660
|
}
|
|
675
|
-
function
|
|
676
|
-
const
|
|
661
|
+
async function resumeReplayedCapture(replay, name, links, sessionId) {
|
|
662
|
+
const requested = (links ?? []).slice(0, 25);
|
|
663
|
+
let replayedLinks = [];
|
|
664
|
+
if (requested.length > 0) {
|
|
665
|
+
const batch = await kernelMutation("chain.createEntryRelations", {
|
|
666
|
+
relations: requested.map((l) => ({ fromEntryId: replay.entryId, toEntryId: l.to, type: l.type, proposedBy: "user" })),
|
|
667
|
+
sessionId
|
|
668
|
+
}).catch((err) => ({ results: requested.map((l) => ({ toEntryId: l.to, type: l.type, ok: false, error: err instanceof Error ? err.message : "unknown error" })) }));
|
|
669
|
+
replayedLinks = requested.map((l) => {
|
|
670
|
+
const row = batch.results.find((r) => r.toEntryId === l.to && r.type === l.type);
|
|
671
|
+
if (row?.ok) return { to: l.to, type: l.type, outcome: "written" };
|
|
672
|
+
const error = row?.error ?? "no answer for this link";
|
|
673
|
+
return /^Duplicate relation/.test(error) ? { to: l.to, type: l.type, outcome: "existing" } : { to: l.to, type: l.type, outcome: "failed", error };
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
const status2 = isTrustAccepted(replay.status) ? "committed" : "draft";
|
|
677
|
+
const summary = `Already captured as ${replay.entryId}; this re-send returned that entry and wrote no second one.`;
|
|
678
|
+
const linkLines = replayedLinks.map((l) => `- ${l.type} ${l.to}: ${l.outcome === "written" ? "written now" : l.outcome === "existing" ? "already there" : `not written (${l.error})`}`);
|
|
677
679
|
return {
|
|
678
680
|
content: [{
|
|
679
681
|
type: "text",
|
|
680
|
-
text: `#
|
|
682
|
+
text: `# Already Captured: ${replay.entryId}
|
|
681
683
|
|
|
682
|
-
${
|
|
683
|
-
|
|
684
|
-
**What is known:** the request reached the server and this client gave up waiting after ${error.elapsedMs}ms. This was a **preview**, and the server returns its validation result before performing any write \u2014 so no entry, auto-commit, quality coaching, or relation linking happened.
|
|
684
|
+
${summary}${linkLines.length ? `
|
|
685
685
|
|
|
686
|
-
**
|
|
686
|
+
**Links:**
|
|
687
|
+
${linkLines.join("\n")}` : ""}
|
|
687
688
|
|
|
688
|
-
|
|
689
|
+
Read it with \`context action=get\`.`
|
|
689
690
|
}],
|
|
690
|
-
structuredContent:
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
mayHaveLanded: false,
|
|
699
|
-
budgetMs: error.budgetMs,
|
|
700
|
-
elapsedMs: error.elapsedMs,
|
|
701
|
-
completedBeforeTimeout: [],
|
|
702
|
-
didNotRun: ["entry", "relations", "commit", "coaching"],
|
|
703
|
-
mayHaveRun: []
|
|
704
|
-
}
|
|
705
|
-
)
|
|
691
|
+
structuredContent: success(summary, {
|
|
692
|
+
entryId: replay.entryId,
|
|
693
|
+
collection: replay.collection,
|
|
694
|
+
name,
|
|
695
|
+
status: status2,
|
|
696
|
+
replayed: true,
|
|
697
|
+
...replayedLinks.length ? { replayedLinks } : {}
|
|
698
|
+
})
|
|
706
699
|
};
|
|
707
700
|
}
|
|
708
701
|
|
|
709
|
-
// src/lib/statusCategories.ts
|
|
710
|
-
var STATUS_CATEGORIES = ["draft", "current", "past", "discarded"];
|
|
711
|
-
var STATUS_CATEGORY_SET = new Set(STATUS_CATEGORIES);
|
|
712
|
-
var TRUST_ACCEPTED_WORDS = ["accepted", "active", "current"];
|
|
713
|
-
function isTrustAccepted(status2) {
|
|
714
|
-
return TRUST_ACCEPTED_WORDS.includes(status2);
|
|
715
|
-
}
|
|
716
|
-
var TRUST_ARCHIVED_WORDS = ["archived", "deprecated", "past", "discarded"];
|
|
717
|
-
var ENTRY_STATUS_READ_WORDS = ["draft", ...TRUST_ACCEPTED_WORDS, ...TRUST_ARCHIVED_WORDS];
|
|
718
|
-
var ENTRY_STATUS_WRITE_WORDS = ["draft", "archived"];
|
|
719
|
-
|
|
720
702
|
// src/lib/scopedCache.ts
|
|
721
703
|
var ScopedCache = class {
|
|
722
704
|
constructor(ttlMs, maxScopes = 100) {
|
|
@@ -1466,8 +1448,7 @@ var captureSuccessOutputSchema = z4.object({
|
|
|
1466
1448
|
collection: z4.string(),
|
|
1467
1449
|
name: z4.string(),
|
|
1468
1450
|
status: z4.enum(["draft", "committed", "proposed", "draft_on_failure"]),
|
|
1469
|
-
// WP-480 S1: `qualityScore` (
|
|
1470
|
-
// breaking output-contract change. The server verdict stays as `qualityVerdict`.
|
|
1451
|
+
// WP-480 S1: `qualityScore` deleted (accepted breaking output change); the server verdict stays as `qualityVerdict`.
|
|
1471
1452
|
qualityVerdict: z4.record(z4.unknown()).optional(),
|
|
1472
1453
|
qualityHints: z4.array(z4.record(z4.unknown())).optional(),
|
|
1473
1454
|
groundingReport: captureSinglePreviewOutputSchema.shape.groundingReport.optional(),
|
|
@@ -1485,10 +1466,11 @@ var captureSuccessOutputSchema = z4.object({
|
|
|
1485
1466
|
})).optional(),
|
|
1486
1467
|
// TASK-160: the server's birth-floor verdict must survive the strict output contract.
|
|
1487
1468
|
spineFloor: z4.object({ relationType: z4.string(), parentKind: z4.string(), exempt: z4.boolean() }).optional(),
|
|
1488
|
-
// WP-751 M3a-1b (TASK-189, DEC-1700): `coherencyRefusal` and `contradictionAdvisory` are
|
|
1489
|
-
//
|
|
1490
|
-
|
|
1491
|
-
//
|
|
1469
|
+
// WP-751 M3a-1b (TASK-189, DEC-1700): `coherencyRefusal` and `contradictionAdvisory` are deleted with the
|
|
1470
|
+
// client-side auto-commit attempt that alone populated them (its gate refusal, the commit's advisory).
|
|
1471
|
+
replayed: z4.literal(true).optional(),
|
|
1472
|
+
// TASK-898: a re-sent capture answered with the first capture's entry
|
|
1473
|
+
replayedLinks: z4.array(z4.object({ to: z4.string(), type: z4.string(), outcome: z4.enum(["written", "existing", "failed"]), error: z4.string().optional() })).optional(),
|
|
1492
1474
|
fallbackApplied: z4.boolean().optional()
|
|
1493
1475
|
// round 2 (PRRT_kwDORS4ils6cTNbF): classifier field above still names the original (pre-substitution) prediction
|
|
1494
1476
|
}).strict();
|
|
@@ -1726,6 +1708,7 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1726
1708
|
// accepted shape as the CLI dying between create and re-eval).
|
|
1727
1709
|
...links && links.length > 0 ? { deferLLMSchedule: true } : {}
|
|
1728
1710
|
});
|
|
1711
|
+
if (result2 && "replayed" in result2) return resumeReplayedCapture(result2, name, links, agentId ?? void 0);
|
|
1729
1712
|
if (result2 && isPreviewCreateEntryResult(result2)) {
|
|
1730
1713
|
const preflight = await resolvePreviewPreflight(result2, links);
|
|
1731
1714
|
const dupCount = groundingReport.duplicates.length;
|
|
@@ -1800,7 +1783,7 @@ No DB writes \u2014 call without \`preview:true\` to capture for real.${result2.
|
|
|
1800
1783
|
spineFloor = created.spineFloor;
|
|
1801
1784
|
} catch (error) {
|
|
1802
1785
|
const msg = error instanceof Error ? error.message : String(error);
|
|
1803
|
-
if (isGatewayTimeout(error)) return buildCaptureOutcomeUnknownResult(error,
|
|
1786
|
+
if (isGatewayTimeout(error)) return buildCaptureOutcomeUnknownResult(error, msg);
|
|
1804
1787
|
if (msg.includes("Duplicate") || msg.includes("already exists")) {
|
|
1805
1788
|
return {
|
|
1806
1789
|
content: [{
|
|
@@ -12849,4 +12832,4 @@ export {
|
|
|
12849
12832
|
createProductBrainServer,
|
|
12850
12833
|
initFeatureFlags
|
|
12851
12834
|
};
|
|
12852
|
-
//# sourceMappingURL=chunk-
|
|
12835
|
+
//# sourceMappingURL=chunk-W3NKFJSJ.js.map
|