@productbrain/mcp 0.0.1-beta.6290 → 0.0.1-beta.6296
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-MHKTOGAX.js → chunk-COH7GAUD.js} +107 -379
- package/dist/chunk-COH7GAUD.js.map +1 -0
- package/dist/{chunk-OT4PU7IF.js → chunk-Q2BQSINV.js} +1 -9
- package/dist/{chunk-OT4PU7IF.js.map → chunk-Q2BQSINV.js.map} +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{setup-KW27NKXI.js → setup-3C2GB2G6.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-MHKTOGAX.js.map +0 -1
- /package/dist/{setup-KW27NKXI.js.map → setup-3C2GB2G6.js.map} +0 -0
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
kernelMutation,
|
|
18
18
|
kernelQuery,
|
|
19
19
|
recordSessionActivity,
|
|
20
|
-
refreshWorkspaceGovernanceMode,
|
|
21
20
|
requireActiveSession,
|
|
22
21
|
requireVendorTriageAccess,
|
|
23
22
|
requireWriteAccess,
|
|
@@ -42,7 +41,7 @@ import {
|
|
|
42
41
|
trackSessionCaptureRate,
|
|
43
42
|
trackWriteBackHintServed,
|
|
44
43
|
trackZeroCaptureAuditFired
|
|
45
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-Q2BQSINV.js";
|
|
46
45
|
|
|
47
46
|
// src/server.ts
|
|
48
47
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -815,65 +814,6 @@ var ScopedCache = class {
|
|
|
815
814
|
};
|
|
816
815
|
var COLLECTION_CACHE_TTL_MS = 6e4;
|
|
817
816
|
|
|
818
|
-
// src/tools/coherency-refusal.ts
|
|
819
|
-
function asDescription(value) {
|
|
820
|
-
if (typeof value !== "string") return void 0;
|
|
821
|
-
const trimmed = value.trim();
|
|
822
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
823
|
-
}
|
|
824
|
-
var GENERIC_RECOVERY_LINE = "Respond and retry: link the entry to the strategic spine, pass `coherencyAcknowledgement`, or supply a typed `steeringOverrideReason`.";
|
|
825
|
-
function coherencyRefusedResult(toolName, entryId, refusal, options) {
|
|
826
|
-
const headingLevel = options?.headingLevel ?? 1;
|
|
827
|
-
const heading = "#".repeat(headingLevel);
|
|
828
|
-
const lines = [
|
|
829
|
-
`${heading} Write refused by the coherency gate`,
|
|
830
|
-
"",
|
|
831
|
-
refusal.reason ?? `The coherency gate refused this write on ${entryId}.`
|
|
832
|
-
];
|
|
833
|
-
if (refusal.subject?.name) {
|
|
834
|
-
lines.push(`On: ${refusal.subject.name}`);
|
|
835
|
-
}
|
|
836
|
-
if (refusal.mode) {
|
|
837
|
-
lines.push("", `Mode: ${refusal.mode}`);
|
|
838
|
-
}
|
|
839
|
-
const routes = refusal.routes;
|
|
840
|
-
const routeKeys = routes ? Object.keys(routes) : [];
|
|
841
|
-
if (routeKeys.length > 0) {
|
|
842
|
-
lines.push("", `${"#".repeat(headingLevel + 1)} Recovery routes`);
|
|
843
|
-
const beforeRoutes = lines.length;
|
|
844
|
-
if (routes && typeof routes.link === "object" && routes.link !== null) {
|
|
845
|
-
const link = routes.link;
|
|
846
|
-
lines.push(`- **link:** ${asDescription(link.description) ?? "Link the entry to the strategic spine."}`);
|
|
847
|
-
}
|
|
848
|
-
if (routes && typeof routes.acknowledge === "object" && routes.acknowledge !== null) {
|
|
849
|
-
const ack = routes.acknowledge;
|
|
850
|
-
const responses = ack.responses?.join(" | ") ?? "linked | accepted-fix | diverged";
|
|
851
|
-
lines.push(
|
|
852
|
-
`- **acknowledge** (\`${ack.param ?? "coherencyAcknowledgement"}\`): ${responses}${ack.minReasonChars ? ` (\u2265${ack.minReasonChars} chars for diverged)` : ""}. ${asDescription(ack.description) ?? ""}`.trim()
|
|
853
|
-
);
|
|
854
|
-
}
|
|
855
|
-
if (routes && typeof routes.override === "object" && routes.override !== null) {
|
|
856
|
-
const ov = routes.override;
|
|
857
|
-
lines.push(
|
|
858
|
-
`- **override** (\`${ov.param ?? "steeringOverrideReason"}\`): \u2265${ov.minChars ?? 12} chars. ${asDescription(ov.description) ?? ""}`.trim()
|
|
859
|
-
);
|
|
860
|
-
}
|
|
861
|
-
if (lines.length === beforeRoutes) {
|
|
862
|
-
lines.push(GENERIC_RECOVERY_LINE);
|
|
863
|
-
}
|
|
864
|
-
} else {
|
|
865
|
-
lines.push("", GENERIC_RECOVERY_LINE);
|
|
866
|
-
}
|
|
867
|
-
return failureResult(
|
|
868
|
-
lines.join("\n"),
|
|
869
|
-
"COHERENCY_REFUSED",
|
|
870
|
-
`Coherency gate refused ${toolName} on ${entryId}.`,
|
|
871
|
-
"Use one of the refusal's routes (link / coherencyAcknowledgement / steeringOverrideReason), then retry.",
|
|
872
|
-
void 0,
|
|
873
|
-
{ entryId, refusal }
|
|
874
|
-
);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
817
|
// src/lib/resolveCollection.ts
|
|
878
818
|
async function resolveCollection(params) {
|
|
879
819
|
const { name, description, typeHint, allowReviewRouting } = params;
|
|
@@ -1116,10 +1056,10 @@ var batchCaptureRealOutputSchema = z4.object({
|
|
|
1116
1056
|
entryId: z4.string(),
|
|
1117
1057
|
collection: z4.string(),
|
|
1118
1058
|
name: z4.string(),
|
|
1119
|
-
//
|
|
1120
|
-
//
|
|
1121
|
-
//
|
|
1122
|
-
//
|
|
1059
|
+
// WP-751 M3a-1b (TASK-189, DEC-1700): "proposed" and "draft_on_failure" are never emitted
|
|
1060
|
+
// by `buildBatchEntryFailure` or the create loop anymore — both were statuses the deleted
|
|
1061
|
+
// client-side auto-commit attempt could produce. Left in the enum (not narrowed) since this
|
|
1062
|
+
// is a shared output-contract schema; narrowing it is a separate, independently-scoped change.
|
|
1123
1063
|
status: z4.enum(["draft", "committed", "proposed", "draft_on_failure", "draft_spine_floor"]),
|
|
1124
1064
|
spineFloor: z4.object({ relationType: z4.string(), parentKind: z4.string(), exempt: z4.boolean() }).optional(),
|
|
1125
1065
|
classifiedBy: z4.enum(["llm", "heuristic", "explicit"]).optional(),
|
|
@@ -1131,14 +1071,11 @@ var batchCaptureRealOutputSchema = z4.object({
|
|
|
1131
1071
|
rejected: z4.array(z4.string()).optional()
|
|
1132
1072
|
}).optional(),
|
|
1133
1073
|
// TEN-2365: capture-time authority-domain proposal slug (PENDING ratification), when filed.
|
|
1134
|
-
domain: z4.string().optional()
|
|
1135
|
-
// WP-
|
|
1136
|
-
//
|
|
1137
|
-
|
|
1138
|
-
//
|
|
1139
|
-
// into structuredContent but previously undeclared here. Not `.strict()` so this one was
|
|
1140
|
-
// silently stripped rather than rejected — declared anyway so batch consumers actually see it.
|
|
1141
|
-
contradictionAdvisory: z4.record(z4.unknown()).optional()
|
|
1074
|
+
domain: z4.string().optional()
|
|
1075
|
+
// WP-751 M3a-1b (TASK-189, DEC-1700): `coherencyRefusal` and `contradictionAdvisory` are
|
|
1076
|
+
// deleted — both were only ever populated by the client-side auto-commit attempt
|
|
1077
|
+
// (smart-capture.ts), deleted entirely. Role is the only governance mode a reader honors
|
|
1078
|
+
// now, and role never attempted a client-side commit.
|
|
1142
1079
|
})),
|
|
1143
1080
|
total: z4.number(),
|
|
1144
1081
|
failed: z4.number(),
|
|
@@ -1828,7 +1765,6 @@ var captureSchema = z6.object({
|
|
|
1828
1765
|
to: z6.string().describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
|
|
1829
1766
|
type: z6.string().describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
|
|
1830
1767
|
})).optional().describe("Relations to create after capture. Skips auto-link discovery when provided."),
|
|
1831
|
-
autoCommit: z6.boolean().optional().describe("If true, commits the entry immediately after capture + linking. If omitted, Open mode workspaces auto-commit by default and consensus/role modes stay draft-first."),
|
|
1832
1768
|
sourceRef: z6.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."),
|
|
1833
1769
|
sourceExcerpt: z6.string().optional().describe("Verbatim excerpt from the source that backs this entry's claims. Stored as top-level entry field, not in data."),
|
|
1834
1770
|
// WP-316 S3: Preview gate — dry-run mode. Returns what would happen, no DB writes.
|
|
@@ -1863,9 +1799,6 @@ var batchCaptureSchema = z6.object({
|
|
|
1863
1799
|
// ownerTeamEntryId deleted — team derives from the role's own placement.
|
|
1864
1800
|
ownerRoleEntryId: z6.string().max(200).optional().describe("Owning role entry ID/ref (rung 2); its home team is derived automatically.")
|
|
1865
1801
|
})).min(1).max(50).describe("Array of entries to capture"),
|
|
1866
|
-
autoCommit: z6.boolean().optional().describe(
|
|
1867
|
-
"If true, commits created entries immediately after linking. If omitted, Open mode workspaces commit by default and consensus/role modes stay draft-first."
|
|
1868
|
-
),
|
|
1869
1802
|
// WP-316 S3: Preview gate — dry-run mode. Returns what would happen, no DB writes.
|
|
1870
1803
|
preview: z6.boolean().optional().describe("If true, validates all captures without writing. Returns what would happen. Default false.")
|
|
1871
1804
|
});
|
|
@@ -2149,14 +2082,10 @@ var captureSuccessOutputSchema = z6.object({
|
|
|
2149
2082
|
}).optional(),
|
|
2150
2083
|
// TASK-160: the server's birth-floor verdict must survive the strict output contract.
|
|
2151
2084
|
spineFloor: z6.object({ relationType: z6.string(), parentKind: z6.string(), exempt: z6.boolean() }).optional(),
|
|
2152
|
-
// WP-
|
|
2153
|
-
//
|
|
2154
|
-
//
|
|
2155
|
-
|
|
2156
|
-
// WP-485 Slice 2b round 4 (Codex P2, FEAT-1370): the server's contradiction advisory is emitted
|
|
2157
|
-
// into structuredContent but was missing here — since this schema is `.strict()`, a real capture
|
|
2158
|
-
// response carrying it failed validation with `unrecognized_keys`. Loose record, same rationale as coherencyRefusal above.
|
|
2159
|
-
contradictionAdvisory: z6.record(z6.unknown()).optional(),
|
|
2085
|
+
// WP-751 M3a-1b (TASK-189, DEC-1700): `coherencyRefusal` and `contradictionAdvisory` are
|
|
2086
|
+
// deleted — both were only ever populated by the client-side auto-commit attempt (surfacing
|
|
2087
|
+
// its own gate refusal, or the commit response's contradiction advisory), and that attempt is
|
|
2088
|
+
// deleted entirely. Neither field is set anywhere in this file anymore.
|
|
2160
2089
|
fallbackApplied: z6.boolean().optional()
|
|
2161
2090
|
// round 2 (PRRT_kwDORS4ils6cTNbF): classifier field above still names the original (pre-substitution) prediction
|
|
2162
2091
|
}).strict();
|
|
@@ -2168,10 +2097,6 @@ var captureOutputSchema = z6.union([
|
|
|
2168
2097
|
captureSinglePreviewOutputSchema,
|
|
2169
2098
|
captureClassifierOnlyOutputSchema
|
|
2170
2099
|
]);
|
|
2171
|
-
function shouldAutoCommitCapture(autoCommit, governanceMode) {
|
|
2172
|
-
if (governanceMode !== "open") return false;
|
|
2173
|
-
return autoCommit === true || autoCommit === void 0;
|
|
2174
|
-
}
|
|
2175
2100
|
function buildDataFromFields(fields, descriptionField, descriptionValue) {
|
|
2176
2101
|
const data = {};
|
|
2177
2102
|
for (const field of fields) {
|
|
@@ -2199,7 +2124,6 @@ var captureCompoundSchema = z6.object({
|
|
|
2199
2124
|
to: z6.string().max(200).describe("Target entry ID (e.g. '<PREFIX>-<n>')"),
|
|
2200
2125
|
type: z6.string().max(200).describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
|
|
2201
2126
|
})).max(20).optional().describe("For 'capture': relations to create after capture. Skips auto-link discovery when provided."),
|
|
2202
|
-
autoCommit: z6.boolean().optional().describe("For 'capture'/'batch': if true, commits immediately after capture + linking."),
|
|
2203
2127
|
sourceRef: z6.string().max(2e3).optional().describe("For 'capture': URI or path of the source document backing this entry."),
|
|
2204
2128
|
sourceExcerpt: z6.string().max(5e3).optional().describe("For 'capture': verbatim excerpt from the source backing this entry's claims."),
|
|
2205
2129
|
preview: z6.boolean().optional().describe("For 'capture'/'batch': if true, validates without writing."),
|
|
@@ -2215,7 +2139,6 @@ var captureSingleVariant = captureSchema.extend({ action: z6.literal("capture")
|
|
|
2215
2139
|
var captureBatchVariant = z6.object({
|
|
2216
2140
|
action: z6.literal("batch"),
|
|
2217
2141
|
items: z6.array(captureItemSchema).min(1).max(50),
|
|
2218
|
-
autoCommit: z6.boolean().optional(),
|
|
2219
2142
|
preview: z6.boolean().optional()
|
|
2220
2143
|
});
|
|
2221
2144
|
var captureActionUnion = z6.discriminatedUnion("action", [
|
|
@@ -2224,21 +2147,21 @@ var captureActionUnion = z6.discriminatedUnion("action", [
|
|
|
2224
2147
|
]);
|
|
2225
2148
|
var CAPTURE_ACTION_SPECS = {
|
|
2226
2149
|
capture: {
|
|
2227
|
-
params: ["collection", "name", "description", "context", "entryId", "canonicalKey", "data", "links", "
|
|
2150
|
+
params: ["collection", "name", "description", "context", "entryId", "canonicalKey", "data", "links", "sourceRef", "sourceExcerpt", "preview", "suggestOnly", "format"],
|
|
2228
2151
|
description: "name and description are required."
|
|
2229
2152
|
},
|
|
2230
|
-
batch: { params: ["items", "
|
|
2153
|
+
batch: { params: ["items", "preview"], description: "items (1-50, each may carry inline relations) is required." }
|
|
2231
2154
|
};
|
|
2232
2155
|
function registerSmartCaptureTools(server) {
|
|
2233
2156
|
const captureHandlers = {
|
|
2234
2157
|
capture: (data) => handleCapture(server, data),
|
|
2235
|
-
batch: (data) => handleBatchCapture(server, { entries: data.items,
|
|
2158
|
+
batch: (data) => handleBatchCapture(server, { entries: data.items, preview: data.preview })
|
|
2236
2159
|
};
|
|
2237
2160
|
server.registerTool(
|
|
2238
2161
|
"capture",
|
|
2239
2162
|
{
|
|
2240
2163
|
title: "Capture",
|
|
2241
|
-
description: "The tool for creating knowledge entries \u2014 single or batch. Two actions:\n\n- **capture**: Create a single entry. Creates an entry, auto-links related entries, and returns a quality scorecard \u2014 all in one call. Provide a name and description; `collection` is optional \u2014 the classifier auto-routes when omitted.\n- **batch**: Create multiple entries in one call via `items[]` (absorbs batch-capture). Each item may carry inline `relations` created right after that entry is captured.\n\n**action=capture details:**\nSupported collections with smart profiles: tensions, business-rules, glossary, decisions, features, audiences, strategy, standards, chains, insights, principles.\nAll other collections get an ENT-{random} ID and sensible defaults.\n\n**Explicit data:** When you know the schema, pass `data: { field: value }` to set fields directly. Top-level `name` and `description` always win for those fields. `data` wins over inference for all other fields.\n\n**Compound capture:** Pass `links` to create relations in the same call (skips auto-link discovery).
|
|
2164
|
+
description: "The tool for creating knowledge entries \u2014 single or batch. Two actions:\n\n- **capture**: Create a single entry. Creates an entry, auto-links related entries, and returns a quality scorecard \u2014 all in one call. Provide a name and description; `collection` is optional \u2014 the classifier auto-routes when omitted.\n- **batch**: Create multiple entries in one call via `items[]` (absorbs batch-capture). Each item may carry inline `relations` created right after that entry is captured.\n\n**action=capture details:**\nSupported collections with smart profiles: tensions, business-rules, glossary, decisions, features, audiences, strategy, standards, chains, insights, principles.\nAll other collections get an ENT-{random} ID and sensible defaults.\n\n**Explicit data:** When you know the schema, pass `data: { field: value }` to set fields directly. Top-level `name` and `description` always win for those fields. `data` wins over inference for all other fields.\n\n**Compound capture:** Pass `links` to create relations in the same call (skips auto-link discovery). Entries are always created as `draft` \u2014 use `entries action=commit` to promote when ready.",
|
|
2242
2165
|
inputSchema: captureCompoundSchema,
|
|
2243
2166
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
2244
2167
|
},
|
|
@@ -2253,7 +2176,7 @@ function registerSmartCaptureTools(server) {
|
|
|
2253
2176
|
})
|
|
2254
2177
|
);
|
|
2255
2178
|
}
|
|
2256
|
-
async function handleCapture(server, { collection, name, description, context, entryId, canonicalKey, data: userData, links,
|
|
2179
|
+
async function handleCapture(server, { collection, name, description, context, entryId, canonicalKey, data: userData, links, sourceRef, sourceExcerpt, preview, suggestOnly, format, ownerRoleEntryId }) {
|
|
2257
2180
|
requireWriteAccess();
|
|
2258
2181
|
const timingStart = Date.now();
|
|
2259
2182
|
const wsCtx = await getWorkspaceContext();
|
|
@@ -2403,7 +2326,6 @@ ${groundingReport.governance.map((g) => `- **${g.entryId}** ${g.name} [${g.colle
|
|
|
2403
2326
|
canonicalKey,
|
|
2404
2327
|
data: userData,
|
|
2405
2328
|
links,
|
|
2406
|
-
autoCommit,
|
|
2407
2329
|
sourceRef,
|
|
2408
2330
|
sourceExcerpt,
|
|
2409
2331
|
ownerRoleEntryId
|
|
@@ -2553,7 +2475,6 @@ ${groundingReport.governance.map((g) => `- **${g.entryId}** ${g.name} [${g.colle
|
|
|
2553
2475
|
canonicalKey,
|
|
2554
2476
|
data: userData,
|
|
2555
2477
|
links,
|
|
2556
|
-
autoCommit,
|
|
2557
2478
|
sourceRef,
|
|
2558
2479
|
sourceExcerpt,
|
|
2559
2480
|
ownerRoleEntryId
|
|
@@ -2632,7 +2553,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2632
2553
|
const linksSuggested = [];
|
|
2633
2554
|
const userLinkResults = [];
|
|
2634
2555
|
const advisedLinks = [];
|
|
2635
|
-
const conflictCandidates = groundingReport.duplicates.filter((candidate) => candidate.matchType === "possible_duplicate" && candidate.entryId !== finalEntryId).slice(0, 3).map((candidate) => ({ entryId: candidate.entryId, name: candidate.name, collection: candidate.collectionSlug }));
|
|
2636
2556
|
const pendingRelations = [];
|
|
2637
2557
|
const skipAutoDiscovery = links && links.length > 0;
|
|
2638
2558
|
const searchQuery = extractSearchTerms(name, description);
|
|
@@ -2829,12 +2749,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2829
2749
|
} catch {
|
|
2830
2750
|
}
|
|
2831
2751
|
}
|
|
2832
|
-
const freshGovernanceMode = await refreshWorkspaceGovernanceMode();
|
|
2833
|
-
const shouldAutoCommit = !spineFloor && shouldAutoCommitCapture(autoCommit, freshGovernanceMode);
|
|
2834
2752
|
let finalStatus = "draft";
|
|
2835
|
-
let commitError = null;
|
|
2836
|
-
let commitRefusal = null;
|
|
2837
|
-
let contradictionAdvisory = null;
|
|
2838
2753
|
if (wasAutoCommittedServerSide) {
|
|
2839
2754
|
finalStatus = "committed";
|
|
2840
2755
|
await recordSessionActivity({ entryModified: internalId });
|
|
@@ -2844,38 +2759,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2844
2759
|
commit_method: "auto",
|
|
2845
2760
|
surface: "mcp_capture"
|
|
2846
2761
|
});
|
|
2847
|
-
} else if (shouldAutoCommit && finalEntryId) {
|
|
2848
|
-
try {
|
|
2849
|
-
const semanticConflicts = await discoverSemanticConflicts(name, description, resolvedCollection);
|
|
2850
|
-
const commitResult = await kernelMutation("chain.commitEntry", {
|
|
2851
|
-
entryId: finalEntryId,
|
|
2852
|
-
author: agentId ? `agent:${agentId}` : void 0,
|
|
2853
|
-
sessionId: agentId ?? void 0,
|
|
2854
|
-
...semanticConflicts.length > 0 ? { conflicts: semanticConflicts } : {}
|
|
2855
|
-
});
|
|
2856
|
-
const refusalReason = coherencyRefusalReason(commitResult);
|
|
2857
|
-
if (refusalReason) {
|
|
2858
|
-
commitError = `coherency gate refused this accept \u2014 ${refusalReason}`;
|
|
2859
|
-
commitRefusal = commitResultField(commitResult, "refusal") ?? null;
|
|
2860
|
-
finalStatus = "draft_on_failure";
|
|
2861
|
-
} else {
|
|
2862
|
-
finalStatus = commitResultField(commitResult, "status") === "proposal_created" ? "proposed" : "committed";
|
|
2863
|
-
contradictionAdvisory = commitResultField(commitResult, "contradictionAdvisory") ?? null;
|
|
2864
|
-
if (finalStatus === "committed") {
|
|
2865
|
-
await recordSessionActivity({ entryModified: internalId });
|
|
2866
|
-
trackChainEntryCommitted(wsCtx.workspaceId, {
|
|
2867
|
-
entry_id: finalEntryId,
|
|
2868
|
-
collection: resolvedCollection,
|
|
2869
|
-
commit_method: "auto",
|
|
2870
|
-
surface: "mcp_capture"
|
|
2871
|
-
});
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
} catch (e) {
|
|
2875
|
-
commitError = e instanceof Error ? e.message : "unknown error";
|
|
2876
|
-
finalStatus = "draft_on_failure";
|
|
2877
|
-
await recordCommitFailure({ entryId: finalEntryId, error: e, sessionId: agentId, server });
|
|
2878
|
-
}
|
|
2879
2762
|
}
|
|
2880
2763
|
const lines = [
|
|
2881
2764
|
`# Captured: ${finalEntryId || name}`,
|
|
@@ -2928,54 +2811,18 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2928
2811
|
for (const r of userLinkResults) lines.push(`- ${r.label}`);
|
|
2929
2812
|
}
|
|
2930
2813
|
if (finalStatus === "committed") {
|
|
2931
|
-
const wasAutoCommitted = autoCommit === void 0 && freshGovernanceMode === "open";
|
|
2932
2814
|
lines.push("");
|
|
2933
2815
|
lines.push(`## Accepted: ${finalEntryId}`);
|
|
2934
|
-
|
|
2935
|
-
lines.push(`**${name}** added to your knowledge base.`);
|
|
2936
|
-
} else {
|
|
2937
|
-
lines.push(`**${name}** promoted to SSOT on the Chain.`);
|
|
2938
|
-
}
|
|
2816
|
+
lines.push(`**${name}** added to your knowledge base.`);
|
|
2939
2817
|
if (await isGoverned(resolvedCollection)) {
|
|
2940
2818
|
lines.push(`_Note: \`${resolvedCollection}\` is a governed collection \u2014 ensure this entry has been reviewed._`);
|
|
2941
2819
|
}
|
|
2942
|
-
if (contradictionAdvisory) {
|
|
2943
|
-
lines.push("");
|
|
2944
|
-
lines.push("## Contradiction advisory");
|
|
2945
|
-
lines.push(contradictionAdvisory.reason);
|
|
2946
|
-
for (const c of contradictionAdvisory.conflictDetails) {
|
|
2947
|
-
lines.push(`- \`${c.chainEntryId}\` ${c.chainEntryName}: ${c.explanation}`);
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
} else if (finalStatus === "proposed") {
|
|
2951
|
-
lines.push("");
|
|
2952
|
-
lines.push(`## Proposal created: ${finalEntryId}`);
|
|
2953
|
-
lines.push(`**${name}** requires consent before it can be accepted, so a proposal was created instead of accepting directly.`);
|
|
2954
|
-
} else if (commitError) {
|
|
2955
|
-
lines.push("");
|
|
2956
|
-
lines.push("## Accept failed");
|
|
2957
|
-
if (commitRefusal) {
|
|
2958
|
-
const refusalText = coherencyRefusedResult("capture", finalEntryId, commitRefusal, { headingLevel: 3 }).content[0].text;
|
|
2959
|
-
lines.push(...refusalText.split("\n"));
|
|
2960
|
-
lines.push(`Entry saved as draft \u2014 use \`entries action=commit entryId="${finalEntryId}"\` to promote when ready.`);
|
|
2961
|
-
} else {
|
|
2962
|
-
lines.push(`Error: ${commitError}. Entry saved as draft \u2014 use \`entries action=commit entryId="${finalEntryId}"\` to promote when ready.`);
|
|
2963
|
-
}
|
|
2964
2820
|
} else if (spineFloor) {
|
|
2965
2821
|
lines.push("");
|
|
2966
2822
|
lines.push("## Saved as draft");
|
|
2967
2823
|
lines.push(`**${name}** ${spineFloor.exempt ? SPINE_FLOOR_EXEMPT_SENTENCE : SPINE_FLOOR_SENTENCE}`);
|
|
2968
2824
|
lines.push(`Next: \`entries action=commit entryId="${finalEntryId}"\`.`);
|
|
2969
2825
|
}
|
|
2970
|
-
if (shouldAutoCommit && conflictCandidates.length > 0) {
|
|
2971
|
-
lines.push("");
|
|
2972
|
-
lines.push("## Potential Overlaps");
|
|
2973
|
-
lines.push(`Auto-accepted with advisory: ${conflictCandidates.length} similar ${conflictCandidates.length === 1 ? "entry" : "entries"} found.`);
|
|
2974
|
-
for (const c of conflictCandidates) {
|
|
2975
|
-
lines.push(`- \`${c.entryId}\`: ${c.name} [${c.collection}]`);
|
|
2976
|
-
}
|
|
2977
|
-
lines.push("Consider reviewing for duplicates.");
|
|
2978
|
-
}
|
|
2979
2826
|
if (linksSuggested.length > 0) {
|
|
2980
2827
|
lines.push("");
|
|
2981
2828
|
lines.push("## Suggested links (review and use `relations action=create`)");
|
|
@@ -3088,11 +2935,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3088
2935
|
if (hasQualityGaps) {
|
|
3089
2936
|
lines.push(`2. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 fill missing fields`);
|
|
3090
2937
|
}
|
|
3091
|
-
} else if (finalStatus === "proposed") {
|
|
3092
|
-
lines.push(`1. **Connect it:** \`relations action=suggest entryId="${eid}"\` \u2014 discover additional links to support the proposal`);
|
|
3093
|
-
if (hasQualityGaps) {
|
|
3094
|
-
lines.push(`2. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 strengthen the entry before approval`);
|
|
3095
|
-
}
|
|
3096
2938
|
} else {
|
|
3097
2939
|
if (userLinkResults.length === 0) {
|
|
3098
2940
|
lines.push(`1. **Connect it:** \`relations action=suggest entryId="${eid}"\` \u2014 discover what this should link to`);
|
|
@@ -3117,7 +2959,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3117
2959
|
} catch {
|
|
3118
2960
|
}
|
|
3119
2961
|
const next = [];
|
|
3120
|
-
if (finalStatus === "committed"
|
|
2962
|
+
if (finalStatus === "committed") {
|
|
3121
2963
|
next.push({ tool: "relations", description: "Discover connections", parameters: { action: "suggest", entryId: finalEntryId } });
|
|
3122
2964
|
} else {
|
|
3123
2965
|
if (userLinkResults.length === 0) {
|
|
@@ -3127,7 +2969,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3127
2969
|
next.push({ tool: "entries", description: "Accept onto Chain", parameters: { action: "commit", entryId: finalEntryId } });
|
|
3128
2970
|
}
|
|
3129
2971
|
const qualityPhrase = verdictQualityPhrase(verdictResult);
|
|
3130
|
-
const summary = finalStatus === "committed" ? `Captured and accepted ${finalEntryId} (${name}) onto ${resolvedCollection}. ${qualityPhrase}` :
|
|
2972
|
+
const summary = finalStatus === "committed" ? `Captured and accepted ${finalEntryId} (${name}) onto ${resolvedCollection}. ${qualityPhrase}` : `Captured ${finalEntryId} (${name}) as draft in ${resolvedCollection}. ${qualityPhrase}`;
|
|
3131
2973
|
const expectedFields = (col.fields ?? []).map((f) => ({
|
|
3132
2974
|
key: f.key,
|
|
3133
2975
|
type: f.type,
|
|
@@ -3161,14 +3003,6 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3161
3003
|
collection: resolvedCollection,
|
|
3162
3004
|
name,
|
|
3163
3005
|
status: finalStatus,
|
|
3164
|
-
// WP-465 surface parity: thread full refusal (routes + mode) for coherency-refused auto-commits.
|
|
3165
|
-
...commitRefusal ? { coherencyRefusal: commitRefusal } : {},
|
|
3166
|
-
// WP-485 Slice 2b round 3 (Codex P2, FEAT-1370): mirror the server's contradiction
|
|
3167
|
-
// advisory into the structured payload — it was rendered in the human text (the
|
|
3168
|
-
// "## Contradiction advisory" section above) but silently absent from
|
|
3169
|
-
// structuredContent, so a structured-only MCP consumer saw a committed capture
|
|
3170
|
-
// with no warning even though the text response carried one.
|
|
3171
|
-
...contradictionAdvisory ? { contradictionAdvisory } : {},
|
|
3172
3006
|
// WP-480 S1: the server verdict rides `qualityVerdict` (named breaking
|
|
3173
3007
|
// output-contract change, pre-launch).
|
|
3174
3008
|
qualityVerdict: verdictResult?.verdict ? { ...verdictResult.verdict, source: verdictResult.source ?? "heuristic" } : void 0,
|
|
@@ -3203,14 +3037,12 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
3203
3037
|
};
|
|
3204
3038
|
return toolResult;
|
|
3205
3039
|
}
|
|
3206
|
-
async function handleBatchCapture(server, { entries,
|
|
3040
|
+
async function handleBatchCapture(server, { entries, preview }) {
|
|
3207
3041
|
requireWriteAccess();
|
|
3208
3042
|
const batchTimingStart = Date.now();
|
|
3209
3043
|
const agentId = getAgentSessionId();
|
|
3210
3044
|
const createdBy = agentId ? `agent:${agentId}` : "capture";
|
|
3211
3045
|
const wsCtx = await getWorkspaceContext();
|
|
3212
|
-
const freshGovernanceModeForBatch = await refreshWorkspaceGovernanceMode();
|
|
3213
|
-
const shouldAutoCommitBatch = shouldAutoCommitCapture(autoCommit, freshGovernanceModeForBatch);
|
|
3214
3046
|
let autoCommitApplied = false;
|
|
3215
3047
|
const needsClassification = entries.map((e, i) => ({ ...e, index: i })).filter((e) => !e.collection);
|
|
3216
3048
|
let classificationMap = /* @__PURE__ */ new Map();
|
|
@@ -3465,10 +3297,7 @@ async function handleBatchCapture(server, { entries, autoCommit, preview }) {
|
|
|
3465
3297
|
if (!preview) resolveGapsForEntry(entry.name, result2.entryId);
|
|
3466
3298
|
const batchWasAutoCommittedServerSide = result2.status === "active";
|
|
3467
3299
|
const batchSpineFloor = result2.spineFloor;
|
|
3468
|
-
|
|
3469
|
-
let commitError;
|
|
3470
|
-
let commitRefusal;
|
|
3471
|
-
let contradictionAdvisory;
|
|
3300
|
+
const finalStatus = batchWasAutoCommittedServerSide ? "committed" : batchSpineFloor ? "draft_spine_floor" : "draft";
|
|
3472
3301
|
if (batchWasAutoCommittedServerSide) {
|
|
3473
3302
|
if (!preview) autoCommitApplied = true;
|
|
3474
3303
|
await recordSessionActivity({ entryModified: internalId });
|
|
@@ -3565,40 +3394,6 @@ async function handleBatchCapture(server, { entries, autoCommit, preview }) {
|
|
|
3565
3394
|
if (relationsProposedCount > 0) {
|
|
3566
3395
|
batchEntryWarnings.push(`${relationsProposedCount} inline relation(s) converted to an agent proposal (misuse pattern) \u2014 review in Cortex UI.`);
|
|
3567
3396
|
}
|
|
3568
|
-
if (!preview && shouldAutoCommitBatch && !batchWasAutoCommittedServerSide && !batchSpineFloor) {
|
|
3569
|
-
try {
|
|
3570
|
-
const semanticConflicts = await discoverSemanticConflicts(entry.name, entry.description, resolvedSlug);
|
|
3571
|
-
autoCommitApplied = true;
|
|
3572
|
-
const commitResult = await kernelMutation("chain.commitEntry", {
|
|
3573
|
-
entryId: finalEntryId,
|
|
3574
|
-
author: agentId ? `agent:${agentId}` : void 0,
|
|
3575
|
-
sessionId: agentId ?? void 0,
|
|
3576
|
-
...semanticConflicts.length > 0 ? { conflicts: semanticConflicts } : {}
|
|
3577
|
-
});
|
|
3578
|
-
const refusalReason = coherencyRefusalReason(commitResult);
|
|
3579
|
-
if (refusalReason) {
|
|
3580
|
-
commitError = `coherency gate refused this accept \u2014 ${refusalReason}`;
|
|
3581
|
-
commitRefusal = commitResultField(commitResult, "refusal") ?? void 0;
|
|
3582
|
-
finalStatus = "draft_on_failure";
|
|
3583
|
-
} else {
|
|
3584
|
-
finalStatus = commitResultField(commitResult, "status") === "proposal_created" ? "proposed" : "committed";
|
|
3585
|
-
contradictionAdvisory = commitResultField(commitResult, "contradictionAdvisory") ?? void 0;
|
|
3586
|
-
if (finalStatus === "committed") {
|
|
3587
|
-
await recordSessionActivity({ entryModified: internalId });
|
|
3588
|
-
trackChainEntryCommitted(wsCtx.workspaceId, {
|
|
3589
|
-
entry_id: finalEntryId,
|
|
3590
|
-
collection: resolvedSlug ?? void 0,
|
|
3591
|
-
commit_method: "auto",
|
|
3592
|
-
surface: "mcp_capture"
|
|
3593
|
-
});
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
} catch (error) {
|
|
3597
|
-
commitError = error instanceof Error ? error.message : String(error);
|
|
3598
|
-
finalStatus = "draft_on_failure";
|
|
3599
|
-
await recordCommitFailure({ entryId: finalEntryId, error, sessionId: agentId, server });
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
3397
|
const entryNorm = result2.normalization;
|
|
3603
3398
|
results.push({
|
|
3604
3399
|
entryIdx,
|
|
@@ -3620,14 +3415,8 @@ async function handleBatchCapture(server, { entries, autoCommit, preview }) {
|
|
|
3620
3415
|
// Fix 1 (TEN-2918 follow-up): not caller-supplied — the preview projection
|
|
3621
3416
|
// must not report this predicted-but-duplicable ID as concrete.
|
|
3622
3417
|
...!entry.entryId ? { entryIdAssignedAtCapture: true } : {},
|
|
3623
|
-
...commitError ? { commitError } : {},
|
|
3624
|
-
// WP-465 surface parity: thread full refusal payload for coherency-refused batch entries.
|
|
3625
|
-
...commitRefusal ? { commitRefusal } : {},
|
|
3626
3418
|
...batchSpineFloor ? { spineFloor: batchSpineFloor } : {},
|
|
3627
3419
|
// WP-745 TASK-160: read by the render below.
|
|
3628
|
-
// WP-485 Slice 2b (R2, FEAT-1370): the server's contradiction advisory (DEC-1321 —
|
|
3629
|
-
// advisory only, never blocking) — visible, never a silent success.
|
|
3630
|
-
...contradictionAdvisory ? { contradictionAdvisory } : {},
|
|
3631
3420
|
...batchEntryWarnings.length > 0 ? { warnings: batchEntryWarnings } : {},
|
|
3632
3421
|
...entryNorm && (Object.keys(entryNorm.remapped).length > 0 || entryNorm.rejected.length > 0) && {
|
|
3633
3422
|
normalization: { remapped: entryNorm.remapped, rejected: entryNorm.rejected }
|
|
@@ -3672,7 +3461,7 @@ async function handleBatchCapture(server, { entries, autoCommit, preview }) {
|
|
|
3672
3461
|
logger: "product-brain"
|
|
3673
3462
|
});
|
|
3674
3463
|
if (preview) {
|
|
3675
|
-
return buildBatchPreviewFromResults(created, failed, skippedLowConfidence, entries.length, entries
|
|
3464
|
+
return buildBatchPreviewFromResults(created, failed, skippedLowConfidence, entries.length, entries);
|
|
3676
3465
|
}
|
|
3677
3466
|
const totalAutoLinks = created.reduce((sum, r) => sum + r.autoLinks, 0);
|
|
3678
3467
|
const totalAdvisedLinks = created.reduce((sum, r) => sum + r.advisedLinks, 0);
|
|
@@ -3738,41 +3527,20 @@ _Use \`entries action=move\` to correct any misclassified entries._`);
|
|
|
3738
3527
|
lines.push("");
|
|
3739
3528
|
lines.push("_Re-capture these with an explicit `collection` or use the suggested collection._");
|
|
3740
3529
|
}
|
|
3741
|
-
if (
|
|
3530
|
+
if (spineFloored.length > 0) {
|
|
3742
3531
|
lines.push("");
|
|
3743
3532
|
lines.push("## Saved as draft");
|
|
3744
|
-
for (const r of
|
|
3745
|
-
|
|
3746
|
-
lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 ${r.spineFloor.exempt ? SPINE_FLOOR_EXEMPT_SENTENCE : SPINE_FLOOR_SENTENCE} Next: \`entries action=commit entryId="${r.entryId}"\`.`);
|
|
3747
|
-
} else if (r.commitRefusal) {
|
|
3748
|
-
lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}]`);
|
|
3749
|
-
const refusalText = coherencyRefusedResult("batch-capture", r.entryId ?? "", r.commitRefusal, { headingLevel: 3 }).content[0].text;
|
|
3750
|
-
lines.push(...refusalText.split("\n"));
|
|
3751
|
-
} else {
|
|
3752
|
-
lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 ${r.commitError}`);
|
|
3753
|
-
}
|
|
3533
|
+
for (const r of spineFloored) {
|
|
3534
|
+
lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 ${r.spineFloor.exempt ? SPINE_FLOOR_EXEMPT_SENTENCE : SPINE_FLOOR_SENTENCE} Next: \`entries action=commit entryId="${r.entryId}"\`.`);
|
|
3754
3535
|
}
|
|
3755
3536
|
}
|
|
3756
|
-
const
|
|
3757
|
-
if (
|
|
3537
|
+
const entriesWithOverlaps = created.filter((r) => r.overlapCount && r.overlapCount > 0);
|
|
3538
|
+
if (entriesWithOverlaps.length > 0) {
|
|
3758
3539
|
lines.push("");
|
|
3759
|
-
lines.push("##
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
lines.push(` - \`${c.chainEntryId}\` ${c.chainEntryName}: ${c.explanation}`);
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
}
|
|
3767
|
-
if (autoCommitApplied) {
|
|
3768
|
-
const entriesWithOverlaps = created.filter((r) => r.overlapCount && r.overlapCount > 0);
|
|
3769
|
-
if (entriesWithOverlaps.length > 0) {
|
|
3770
|
-
lines.push("");
|
|
3771
|
-
lines.push("## Related-entry Suggestions");
|
|
3772
|
-
lines.push(`${entriesWithOverlaps.length} ${entriesWithOverlaps.length === 1 ? "entry has" : "entries have"} link candidates. Review their relevance before linking:`);
|
|
3773
|
-
for (const r of entriesWithOverlaps) {
|
|
3774
|
-
lines.push(`- **${r.entryId}**: ${r.name} \u2014 ${r.overlapCount} link ${r.overlapCount === 1 ? "candidate" : "candidates"}`);
|
|
3775
|
-
}
|
|
3540
|
+
lines.push("## Related-entry Suggestions");
|
|
3541
|
+
lines.push(`${entriesWithOverlaps.length} ${entriesWithOverlaps.length === 1 ? "entry has" : "entries have"} link candidates. Review their relevance before linking:`);
|
|
3542
|
+
for (const r of entriesWithOverlaps) {
|
|
3543
|
+
lines.push(`- **${r.entryId}**: ${r.name} \u2014 ${r.overlapCount} link ${r.overlapCount === 1 ? "candidate" : "candidates"}`);
|
|
3776
3544
|
}
|
|
3777
3545
|
}
|
|
3778
3546
|
const entriesWithWarnings = created.filter((r) => r.warnings && r.warnings.length > 0);
|
|
@@ -3848,20 +3616,16 @@ _Use \`entries action=move\` to correct any misclassified entries._`);
|
|
|
3848
3616
|
...r.confidenceTier ? { confidenceTier: r.confidenceTier } : {},
|
|
3849
3617
|
...r.warnings?.length ? { warnings: r.warnings } : {},
|
|
3850
3618
|
...r.normalization ? { normalization: r.normalization } : {},
|
|
3851
|
-
|
|
3852
|
-
//
|
|
3853
|
-
|
|
3619
|
+
// WP-751 M3a-1b (TASK-189, DEC-1700): `commitError`, `coherencyRefusal` (from
|
|
3620
|
+
// `r.commitRefusal`), and `contradictionAdvisory` are deleted — all three were
|
|
3621
|
+
// only ever populated by the client-side auto-commit attempt, deleted entirely.
|
|
3854
3622
|
...r.spineFloor ? { spineFloor: r.spineFloor } : {},
|
|
3855
3623
|
// WP-745 TASK-160: the projection a machine consumer receives; was human-render only (Copilot review, PR #901).
|
|
3856
3624
|
...r.authorityDomain ? { domain: r.authorityDomain.slug } : {},
|
|
3857
3625
|
// TEN-957 (WP-484 S2): inline relations created for this entry.
|
|
3858
3626
|
...r.relationsCreated ? { relationsCreated: r.relationsCreated } : {},
|
|
3859
3627
|
// Finding #14: proposal outcomes are distinct from writes — never folded in.
|
|
3860
|
-
...r.relationsProposed ? { relationsProposed: r.relationsProposed } : {}
|
|
3861
|
-
// WP-485 Slice 2b round 2 (Codex P1): the server's per-entry contradiction
|
|
3862
|
-
// advisory — was reaching `results` but dropped here, the final projection the
|
|
3863
|
-
// caller actually receives. Visible, never a silent success.
|
|
3864
|
-
...r.contradictionAdvisory ? { contradictionAdvisory: r.contradictionAdvisory } : {}
|
|
3628
|
+
...r.relationsProposed ? { relationsProposed: r.relationsProposed } : {}
|
|
3865
3629
|
})),
|
|
3866
3630
|
total: created.length,
|
|
3867
3631
|
failed: failed.length,
|
|
@@ -3903,46 +3667,6 @@ _Use \`entries action=move\` to correct any misclassified entries._`);
|
|
|
3903
3667
|
}
|
|
3904
3668
|
};
|
|
3905
3669
|
}
|
|
3906
|
-
function coherencyRefusalReason(commitResult) {
|
|
3907
|
-
if (!commitResult || typeof commitResult !== "object" || !("status" in commitResult) || commitResult.status !== "coherency_refused") {
|
|
3908
|
-
return null;
|
|
3909
|
-
}
|
|
3910
|
-
const refusal = "refusal" in commitResult ? commitResult.refusal : void 0;
|
|
3911
|
-
return refusal?.reason ?? "refused by the coherency gate";
|
|
3912
|
-
}
|
|
3913
|
-
function commitResultField(commitResult, field) {
|
|
3914
|
-
if (!commitResult || typeof commitResult !== "object" || !(field in commitResult)) return void 0;
|
|
3915
|
-
return commitResult[field];
|
|
3916
|
-
}
|
|
3917
|
-
async function recordCommitFailure({
|
|
3918
|
-
entryId,
|
|
3919
|
-
error,
|
|
3920
|
-
sessionId,
|
|
3921
|
-
server
|
|
3922
|
-
}) {
|
|
3923
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3924
|
-
const logLine = `commitEntry failed for ${entryId}: ${errorMessage}${sessionId ? ` (session: ${sessionId})` : ""}`;
|
|
3925
|
-
try {
|
|
3926
|
-
await server.sendLoggingMessage({ level: "error", data: logLine, logger: "product-brain" });
|
|
3927
|
-
} catch {
|
|
3928
|
-
}
|
|
3929
|
-
if (sessionId) {
|
|
3930
|
-
try {
|
|
3931
|
-
const tenName = `commitEntry failed for ${entryId} \u2014 ${errorMessage}`.slice(0, 250);
|
|
3932
|
-
await kernelMutation("chain.createEntry", {
|
|
3933
|
-
collectionSlug: "tensions",
|
|
3934
|
-
name: tenName,
|
|
3935
|
-
// Failure audit TENs intentionally stay draft — they need explicit human review,
|
|
3936
|
-
// not auto-commit, even in Open mode.
|
|
3937
|
-
status: "draft",
|
|
3938
|
-
data: {},
|
|
3939
|
-
createdBy: `agent:${sessionId}`,
|
|
3940
|
-
sessionId
|
|
3941
|
-
});
|
|
3942
|
-
} catch {
|
|
3943
|
-
}
|
|
3944
|
-
}
|
|
3945
|
-
}
|
|
3946
3670
|
async function runContradictionCheck(name, description, excludeEntryId, excludeDocId) {
|
|
3947
3671
|
const warnings = [];
|
|
3948
3672
|
try {
|
|
@@ -3983,25 +3707,6 @@ async function runContradictionCheck(name, description, excludeEntryId, excludeD
|
|
|
3983
3707
|
}
|
|
3984
3708
|
return warnings;
|
|
3985
3709
|
}
|
|
3986
|
-
async function discoverSemanticConflicts(name, description, collectionHint) {
|
|
3987
|
-
try {
|
|
3988
|
-
const conflicts = await kernelQuery("chain.detectSemanticConflicts", {
|
|
3989
|
-
name,
|
|
3990
|
-
description,
|
|
3991
|
-
...collectionHint ? { collectionHint } : {}
|
|
3992
|
-
});
|
|
3993
|
-
return (conflicts ?? []).map((c) => ({
|
|
3994
|
-
chainEntryId: c.chainEntryId,
|
|
3995
|
-
chainEntryName: c.chainEntryName,
|
|
3996
|
-
...c.chainEntryDescription ? { chainEntryDescription: c.chainEntryDescription } : {},
|
|
3997
|
-
type: c.type,
|
|
3998
|
-
confidence: c.confidence,
|
|
3999
|
-
explanation: c.explanation
|
|
4000
|
-
}));
|
|
4001
|
-
} catch {
|
|
4002
|
-
return [];
|
|
4003
|
-
}
|
|
4004
|
-
}
|
|
4005
3710
|
|
|
4006
3711
|
// src/tools/conflict-preflight.ts
|
|
4007
3712
|
async function runConflictPreflight(name, description, collectionHint) {
|
|
@@ -4027,6 +3732,65 @@ async function runConflictPreflight(name, description, collectionHint) {
|
|
|
4027
3732
|
}
|
|
4028
3733
|
}
|
|
4029
3734
|
|
|
3735
|
+
// src/tools/coherency-refusal.ts
|
|
3736
|
+
function asDescription(value) {
|
|
3737
|
+
if (typeof value !== "string") return void 0;
|
|
3738
|
+
const trimmed = value.trim();
|
|
3739
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3740
|
+
}
|
|
3741
|
+
var GENERIC_RECOVERY_LINE = "Respond and retry: link the entry to the strategic spine, pass `coherencyAcknowledgement`, or supply a typed `steeringOverrideReason`.";
|
|
3742
|
+
function coherencyRefusedResult(toolName, entryId, refusal, options) {
|
|
3743
|
+
const headingLevel = options?.headingLevel ?? 1;
|
|
3744
|
+
const heading = "#".repeat(headingLevel);
|
|
3745
|
+
const lines = [
|
|
3746
|
+
`${heading} Write refused by the coherency gate`,
|
|
3747
|
+
"",
|
|
3748
|
+
refusal.reason ?? `The coherency gate refused this write on ${entryId}.`
|
|
3749
|
+
];
|
|
3750
|
+
if (refusal.subject?.name) {
|
|
3751
|
+
lines.push(`On: ${refusal.subject.name}`);
|
|
3752
|
+
}
|
|
3753
|
+
if (refusal.mode) {
|
|
3754
|
+
lines.push("", `Mode: ${refusal.mode}`);
|
|
3755
|
+
}
|
|
3756
|
+
const routes = refusal.routes;
|
|
3757
|
+
const routeKeys = routes ? Object.keys(routes) : [];
|
|
3758
|
+
if (routeKeys.length > 0) {
|
|
3759
|
+
lines.push("", `${"#".repeat(headingLevel + 1)} Recovery routes`);
|
|
3760
|
+
const beforeRoutes = lines.length;
|
|
3761
|
+
if (routes && typeof routes.link === "object" && routes.link !== null) {
|
|
3762
|
+
const link = routes.link;
|
|
3763
|
+
lines.push(`- **link:** ${asDescription(link.description) ?? "Link the entry to the strategic spine."}`);
|
|
3764
|
+
}
|
|
3765
|
+
if (routes && typeof routes.acknowledge === "object" && routes.acknowledge !== null) {
|
|
3766
|
+
const ack = routes.acknowledge;
|
|
3767
|
+
const responses = ack.responses?.join(" | ") ?? "linked | accepted-fix | diverged";
|
|
3768
|
+
lines.push(
|
|
3769
|
+
`- **acknowledge** (\`${ack.param ?? "coherencyAcknowledgement"}\`): ${responses}${ack.minReasonChars ? ` (\u2265${ack.minReasonChars} chars for diverged)` : ""}. ${asDescription(ack.description) ?? ""}`.trim()
|
|
3770
|
+
);
|
|
3771
|
+
}
|
|
3772
|
+
if (routes && typeof routes.override === "object" && routes.override !== null) {
|
|
3773
|
+
const ov = routes.override;
|
|
3774
|
+
lines.push(
|
|
3775
|
+
`- **override** (\`${ov.param ?? "steeringOverrideReason"}\`): \u2265${ov.minChars ?? 12} chars. ${asDescription(ov.description) ?? ""}`.trim()
|
|
3776
|
+
);
|
|
3777
|
+
}
|
|
3778
|
+
if (lines.length === beforeRoutes) {
|
|
3779
|
+
lines.push(GENERIC_RECOVERY_LINE);
|
|
3780
|
+
}
|
|
3781
|
+
} else {
|
|
3782
|
+
lines.push("", GENERIC_RECOVERY_LINE);
|
|
3783
|
+
}
|
|
3784
|
+
return failureResult(
|
|
3785
|
+
lines.join("\n"),
|
|
3786
|
+
"COHERENCY_REFUSED",
|
|
3787
|
+
`Coherency gate refused ${toolName} on ${entryId}.`,
|
|
3788
|
+
"Use one of the refusal's routes (link / coherencyAcknowledgement / steeringOverrideReason), then retry.",
|
|
3789
|
+
void 0,
|
|
3790
|
+
{ entryId, refusal }
|
|
3791
|
+
);
|
|
3792
|
+
}
|
|
3793
|
+
|
|
4030
3794
|
// src/tools/knowledge/getHistory.ts
|
|
4031
3795
|
import { z as z7 } from "zod/v3";
|
|
4032
3796
|
var getHistorySchema = z7.object({
|
|
@@ -9496,7 +9260,7 @@ async function handleRecordActivation({
|
|
|
9496
9260
|
// src/tools/orient.ts
|
|
9497
9261
|
var PURPOSE_GAP_PREFIX = "purpose-gap-";
|
|
9498
9262
|
var READINESS_GAPS_MAX = 50;
|
|
9499
|
-
function formatRetrievalMissLines(verdict
|
|
9263
|
+
function formatRetrievalMissLines(verdict) {
|
|
9500
9264
|
const lines = [];
|
|
9501
9265
|
if (verdict.served !== "ok") {
|
|
9502
9266
|
if (verdict.served === "miss") {
|
|
@@ -9505,18 +9269,8 @@ function formatRetrievalMissLines(verdict, domain) {
|
|
|
9505
9269
|
lines.push(`\xB7 weak serve (provisional, ${verdict.rubricVersion}): ${verdict.kind ?? "thin"}`);
|
|
9506
9270
|
}
|
|
9507
9271
|
}
|
|
9508
|
-
if (domain?.reason) lines.push(` Reason: ${domain.reason}`);
|
|
9509
|
-
if (domain?.nextAction) lines.push(` Next action: ${domain.nextAction}`);
|
|
9510
|
-
if (domain?.evidenceGap) lines.push(` Evidence gap: ${domain.evidenceGap}`);
|
|
9511
9272
|
return lines;
|
|
9512
9273
|
}
|
|
9513
|
-
function domainDetailFromRetrieval(retrieval) {
|
|
9514
|
-
if (!retrieval) return null;
|
|
9515
|
-
const reason = typeof retrieval.fallbackReason === "string" ? retrieval.fallbackReason : null;
|
|
9516
|
-
const nextAction = typeof retrieval.fallbackAction === "string" ? retrieval.fallbackAction : null;
|
|
9517
|
-
const evidenceGap = typeof retrieval.evidenceGap === "string" ? retrieval.evidenceGap : null;
|
|
9518
|
-
return reason || nextAction || evidenceGap ? { reason, nextAction, evidenceGap } : null;
|
|
9519
|
-
}
|
|
9520
9274
|
async function markOrientedWithSnapshotFallback(agentSessionId, coherenceSnapshot) {
|
|
9521
9275
|
try {
|
|
9522
9276
|
await kernelCall("agent.markOriented", {
|
|
@@ -9548,15 +9302,15 @@ var orientSchema = z22.object({
|
|
|
9548
9302
|
task: z22.string().max(2e3).optional().describe(
|
|
9549
9303
|
"For 'task': natural-language task description for task-scoped context. For 'start': what you're about to work on. When provided to 'task', orient returns scored, relevant entries for the task."
|
|
9550
9304
|
),
|
|
9551
|
-
|
|
9552
|
-
//
|
|
9553
|
-
//
|
|
9305
|
+
// D1d (WP-754/TASK-204): nested to match the server's StartupResolutionSignals shape, every
|
|
9306
|
+
// field optional, sanitized server-side. Domain resolution (this envelope's original purpose)
|
|
9307
|
+
// is deleted; the sole surviving consumer is `context.served` activation telemetry.
|
|
9554
9308
|
startupSignals: z22.object({
|
|
9555
9309
|
changedPaths: z22.array(z22.string()).max(25).optional().describe("Paths changed in the current working tree, if known (e.g. from a prior git status/diff tool call)."),
|
|
9556
9310
|
reviewedArtifactRefs: z22.array(z22.string()).max(25).optional().describe("Chain entry IDs the caller has already reviewed this session, if tracked."),
|
|
9557
9311
|
branchName: z22.string().max(120).optional().describe("Current git branch name, if known."),
|
|
9558
9312
|
worktreeName: z22.string().max(120).optional().describe("Current worktree/directory name, if known.")
|
|
9559
|
-
}).optional().describe("For 'task': best-effort startup signals
|
|
9313
|
+
}).optional().describe("For 'task': best-effort startup signals \u2014 changedPaths/reviewedArtifactRefs/branchName/worktreeName. All optional; omit fields you don't know."),
|
|
9560
9314
|
invocationPath: z22.enum(["session-start", "session-close", "manual-orient", "handshake", "unknown"]).optional().describe("For 'task': how this orient was invoked. Defaults to 'manual-orient' (the direct `orient task=...` call shape) when omitted."),
|
|
9561
9315
|
confirmedEntryCount: z22.number().int().min(0).optional().describe(
|
|
9562
9316
|
"For 'record-activation': confirmed-entry count from the Phase 4 capture loop. The mutation enforces >=10."
|
|
@@ -9577,7 +9331,6 @@ var orientTaskVariant = z22.object({
|
|
|
9577
9331
|
mode: z22.enum(["full", "brief"]).optional().default("full"),
|
|
9578
9332
|
tier: z22.enum(["summary", "standard", "full"]).optional(),
|
|
9579
9333
|
task: z22.string().max(2e3).optional(),
|
|
9580
|
-
scope: z22.string().max(200).optional(),
|
|
9581
9334
|
// WP-486 Slice 1 (FEAT-1371) — mirrors orientSchema's top-level declaration above; see its
|
|
9582
9335
|
// doc comment for the shape rationale.
|
|
9583
9336
|
startupSignals: z22.object({
|
|
@@ -9601,7 +9354,7 @@ var orientActionUnion = z22.discriminatedUnion("action", [
|
|
|
9601
9354
|
]);
|
|
9602
9355
|
var ORIENT_ACTION_SPECS = {
|
|
9603
9356
|
start: { params: ["task"], description: "Universal session opener; task is optional." },
|
|
9604
|
-
task: { params: ["mode", "tier", "task"
|
|
9357
|
+
task: { params: ["mode", "tier", "task"], description: "Task-grounded context loader; all params optional." },
|
|
9605
9358
|
"record-activation": {
|
|
9606
9359
|
params: ["confirmedEntryCount", "entriesAcrossCollections", "retrievalDemoConfirmed"],
|
|
9607
9360
|
description: "All three fields are required."
|
|
@@ -9633,7 +9386,6 @@ function registerOrientTool(server) {
|
|
|
9633
9386
|
mode: data.mode,
|
|
9634
9387
|
tier: data.tier,
|
|
9635
9388
|
task: data.task,
|
|
9636
|
-
scope: data.scope,
|
|
9637
9389
|
startupSignals: data.startupSignals,
|
|
9638
9390
|
invocationPath: data.invocationPath
|
|
9639
9391
|
}),
|
|
@@ -9647,7 +9399,7 @@ function registerOrientTool(server) {
|
|
|
9647
9399
|
"orient",
|
|
9648
9400
|
{
|
|
9649
9401
|
title: "Orient",
|
|
9650
|
-
description: "Workspace orientation and session bootstrapping. Three actions:\n\n- **start**: Universal opener (absorbs `start_pb`) \u2014 say 'Start PB' to begin every session. Stage-aware: fresh workspaces (blank/seeded) get the SKILL-pb-setup body verbatim; active workspaces (grounded/connected) get a standup briefing. Orients the CURRENT session \u2014 it does not create one; for writes, call `session action=start` first.\n- **task**: Task-grounded context loader (the original `orient` behavior). Use after `action=start` to load governance and context for a specific task. Completing orientation unlocks write tools.\n- **record-activation**: Chat-only activation receipt writer (absorbs `record_activation`) \u2014 closes the install-to-activation arc for chat-only surfaces.\n\n**tier** (action=task): Controls payload depth.\n- `summary` (~10 KB) \u2014 compact, calibrated for task-scoped retrieval.\n- `standard` (~256 KB) \u2014 rich, suited to workspace standups.\n- `full` \u2014 complete payload, no cap. Use with deliberation.\n\nThe spine (vision, purpose, strategy highlights) is served in full and a grounding status flags when context was reduced.\n\nDefaults: `summary` when `task` is provided; `standard` when `task` is absent. Explicit `tier` always wins.\n\n**mode** (action=task): `full` (default) returns full context. `brief` returns compact summary \u2014 mapped to tier=summary internally
|
|
9402
|
+
description: "Workspace orientation and session bootstrapping. Three actions:\n\n- **start**: Universal opener (absorbs `start_pb`) \u2014 say 'Start PB' to begin every session. Stage-aware: fresh workspaces (blank/seeded) get the SKILL-pb-setup body verbatim; active workspaces (grounded/connected) get a standup briefing. Orients the CURRENT session \u2014 it does not create one; for writes, call `session action=start` first.\n- **task**: Task-grounded context loader (the original `orient` behavior). Use after `action=start` to load governance and context for a specific task. Completing orientation unlocks write tools.\n- **record-activation**: Chat-only activation receipt writer (absorbs `record_activation`) \u2014 closes the install-to-activation arc for chat-only surfaces.\n\n**tier** (action=task): Controls payload depth.\n- `summary` (~10 KB) \u2014 compact, calibrated for task-scoped retrieval.\n- `standard` (~256 KB) \u2014 rich, suited to workspace standups.\n- `full` \u2014 complete payload, no cap. Use with deliberation.\n\nThe spine (vision, purpose, strategy highlights) is served in full and a grounding status flags when context was reduced.\n\nDefaults: `summary` when `task` is provided; `standard` when `task` is absent. Explicit `tier` always wins.\n\n**mode** (action=task): `full` (default) returns full context. `brief` returns compact summary \u2014 mapped to tier=summary internally.",
|
|
9651
9403
|
inputSchema: orientSchema,
|
|
9652
9404
|
// Mixed read/write noun (§3 R3): 'start'/'record-activation' write session state.
|
|
9653
9405
|
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: false }
|
|
@@ -9663,12 +9415,9 @@ function registerOrientTool(server) {
|
|
|
9663
9415
|
})
|
|
9664
9416
|
);
|
|
9665
9417
|
}
|
|
9666
|
-
async function _handleOrient({ mode = "full", tier, task,
|
|
9418
|
+
async function _handleOrient({ mode = "full", tier, task, startupSignals, invocationPath }) {
|
|
9667
9419
|
const errors = [];
|
|
9668
9420
|
const callTier = tier ?? (mode === "brief" ? "summary" : void 0);
|
|
9669
|
-
if (scope && !task) {
|
|
9670
|
-
errors.push("--scope requires --task to filter governance. Scope was ignored.");
|
|
9671
|
-
}
|
|
9672
9421
|
const agentSessionId = getAgentSessionId();
|
|
9673
9422
|
if (isSessionOriented() && mode === "brief" && !task) {
|
|
9674
9423
|
return buildAlreadyOrientedResult(kernelQuery, agentSessionId, taskGroundingWarningLines());
|
|
@@ -9686,7 +9435,6 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9686
9435
|
try {
|
|
9687
9436
|
const orientArgs = {};
|
|
9688
9437
|
if (task) orientArgs.task = task;
|
|
9689
|
-
if (scope) orientArgs.scope = scope;
|
|
9690
9438
|
if (startupSignals && Object.keys(startupSignals).length > 0) orientArgs.startupSignals = startupSignals;
|
|
9691
9439
|
if (invocationPath) {
|
|
9692
9440
|
orientArgs.invocationPath = invocationPath;
|
|
@@ -9787,10 +9535,7 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9787
9535
|
}
|
|
9788
9536
|
lines.push("");
|
|
9789
9537
|
lines.push(...taskGroundingWarningLines(task, hasTaskGrounding));
|
|
9790
|
-
const retrievalMissLines = orientView?.retrievalMiss ? formatRetrievalMissLines(
|
|
9791
|
-
orientView.retrievalMiss,
|
|
9792
|
-
domainDetailFromRetrieval(orientView.startup?.domainRetrieval)
|
|
9793
|
-
) : [];
|
|
9538
|
+
const retrievalMissLines = orientView?.retrievalMiss ? formatRetrievalMissLines(orientView.retrievalMiss) : [];
|
|
9794
9539
|
if (retrievalMissLines.length > 0) {
|
|
9795
9540
|
lines.push(...retrievalMissLines);
|
|
9796
9541
|
lines.push("");
|
|
@@ -9822,15 +9567,6 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
9822
9567
|
const degradedLineBrief = buildDegradedPreloadsLine(orientView?.degradedPreloads);
|
|
9823
9568
|
if (degradedLineBrief) lines.push(degradedLineBrief);
|
|
9824
9569
|
lines.push(...buildRitualsSurfaceLines(orientView, { fetchFailed: orientFetchFailed || orientView?.fetchFailed === true }));
|
|
9825
|
-
if (orientView?.startup?.domainRetrieval) {
|
|
9826
|
-
const retrieval = orientView.startup.domainRetrieval;
|
|
9827
|
-
const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
|
|
9828
|
-
lines.push(`Domain retrieval: ${retrieval.state}${scopedTo}`);
|
|
9829
|
-
if (retrieval.activeSource) lines.push(`Active source: ${retrieval.activeSource}`);
|
|
9830
|
-
if (retrieval.directRatifiedCount != null || retrieval.inheritedRatifiedCount != null || retrieval.orgFallbackCount != null) {
|
|
9831
|
-
lines.push(`Domain relation evidence: ${retrieval.directRatifiedCount ?? 0} direct, ${retrieval.inheritedRatifiedCount ?? 0} inherited, ${retrieval.orgFallbackCount ?? 0} org fallback`);
|
|
9832
|
-
}
|
|
9833
|
-
}
|
|
9834
9570
|
if (orientView?.writeBackHints && orientView.writeBackHints.length > 0) {
|
|
9835
9571
|
lines.push("");
|
|
9836
9572
|
lines.push("Write-back hints (what to capture this session):");
|
|
@@ -10086,13 +9822,6 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
10086
9822
|
lines.push(`> \u26A0\uFE0F ${tc.groundingNote}`);
|
|
10087
9823
|
lines.push("");
|
|
10088
9824
|
}
|
|
10089
|
-
if (orientView.startup?.domainRetrieval) {
|
|
10090
|
-
const retrieval = orientView.startup.domainRetrieval;
|
|
10091
|
-
const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
|
|
10092
|
-
lines.push(`_Domain retrieval: ${retrieval.state}${scopedTo}_`);
|
|
10093
|
-
if (retrieval.activeSource) lines.push(`_Active source: ${retrieval.activeSource}_`);
|
|
10094
|
-
lines.push("");
|
|
10095
|
-
}
|
|
10096
9825
|
lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
|
|
10097
9826
|
lines.push("");
|
|
10098
9827
|
for (const e of tc.context) {
|
|
@@ -10390,7 +10119,6 @@ async function _handleOrient({ mode = "full", tier, task, scope, startupSignals,
|
|
|
10390
10119
|
// PR #539 review fix
|
|
10391
10120
|
// RCRT miss-alarm: machine-readable verdict, parity with the rendered prose alarm.
|
|
10392
10121
|
...orientView?.retrievalMiss ? { retrievalMiss: orientView.retrievalMiss } : {},
|
|
10393
|
-
domainRetrieval: orientView?.startup?.domainRetrieval,
|
|
10394
10122
|
orientationStatus,
|
|
10395
10123
|
nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.',
|
|
10396
10124
|
...orientView?._budget ? { _budget: orientView._budget } : {},
|
|
@@ -16956,4 +16684,4 @@ export {
|
|
|
16956
16684
|
createProductBrainServer,
|
|
16957
16685
|
initFeatureFlags
|
|
16958
16686
|
};
|
|
16959
|
-
//# sourceMappingURL=chunk-
|
|
16687
|
+
//# sourceMappingURL=chunk-COH7GAUD.js.map
|