@productbrain/mcp 0.0.1-beta.11217 → 0.0.1-beta.11229
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-Y7YDQNWU.js → chunk-RMVLWNVO.js} +142 -723
- package/dist/chunk-RMVLWNVO.js.map +1 -0
- package/dist/{chunk-OSTJZIKJ.js → chunk-Z4MDUM6J.js} +1 -63
- package/dist/chunk-Z4MDUM6J.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{setup-6LRESAX5.js → setup-FT3HINSD.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-OSTJZIKJ.js.map +0 -1
- package/dist/chunk-Y7YDQNWU.js.map +0 -1
- /package/dist/{setup-6LRESAX5.js.map → setup-FT3HINSD.js.map} +0 -0
|
@@ -32,21 +32,16 @@ import {
|
|
|
32
32
|
runWithToolContext,
|
|
33
33
|
setSessionOriented,
|
|
34
34
|
startAgentSession,
|
|
35
|
-
trackCaptureClassifierAutoRouted,
|
|
36
|
-
trackCaptureClassifierEvaluated,
|
|
37
|
-
trackCaptureClassifierFallback,
|
|
38
35
|
trackCaptureContractMiss,
|
|
39
36
|
trackCaptureQualityHints,
|
|
40
37
|
trackChainEntryCommitted,
|
|
41
|
-
trackClassifierDivergence,
|
|
42
|
-
trackCollectionClassified,
|
|
43
38
|
trackCommitErrorByCode,
|
|
44
39
|
trackKnowledgeGap,
|
|
45
40
|
trackQualityCheck,
|
|
46
41
|
trackQualityVerdict,
|
|
47
42
|
trackSessionCaptureRate,
|
|
48
43
|
trackZeroCaptureAuditFired
|
|
49
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-Z4MDUM6J.js";
|
|
50
45
|
|
|
51
46
|
// src/server.ts
|
|
52
47
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -578,10 +573,6 @@ function clearSessionGaps() {
|
|
|
578
573
|
const sessionKey = currentSessionKey();
|
|
579
574
|
sessionGaps.delete(sessionKey);
|
|
580
575
|
}
|
|
581
|
-
function resolveGapsForEntry(entryName, entryId) {
|
|
582
|
-
kernelMutation("gaps.resolve", { entryName, entryId }).catch(() => {
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
576
|
|
|
586
577
|
// src/compound-tool.ts
|
|
587
578
|
function backpressureResult(toolName, action, spec, detail) {
|
|
@@ -699,226 +690,6 @@ Read it with \`context action=get\`.`
|
|
|
699
690
|
};
|
|
700
691
|
}
|
|
701
692
|
|
|
702
|
-
// src/lib/scopedCache.ts
|
|
703
|
-
var ScopedCache = class {
|
|
704
|
-
constructor(ttlMs, maxScopes = 100) {
|
|
705
|
-
this.ttlMs = ttlMs;
|
|
706
|
-
this.maxScopes = maxScopes;
|
|
707
|
-
}
|
|
708
|
-
store = /* @__PURE__ */ new Map();
|
|
709
|
-
get(scope) {
|
|
710
|
-
const e = this.store.get(scope);
|
|
711
|
-
if (!e) return void 0;
|
|
712
|
-
if (Date.now() > e.expiresAt) {
|
|
713
|
-
this.store.delete(scope);
|
|
714
|
-
return void 0;
|
|
715
|
-
}
|
|
716
|
-
return e.value;
|
|
717
|
-
}
|
|
718
|
-
set(scope, value) {
|
|
719
|
-
this.store.set(scope, { value, expiresAt: Date.now() + this.ttlMs });
|
|
720
|
-
if (this.store.size > this.maxScopes) this.evict();
|
|
721
|
-
}
|
|
722
|
-
delete(scope) {
|
|
723
|
-
this.store.delete(scope);
|
|
724
|
-
}
|
|
725
|
-
clear() {
|
|
726
|
-
this.store.clear();
|
|
727
|
-
}
|
|
728
|
-
size() {
|
|
729
|
-
return this.store.size;
|
|
730
|
-
}
|
|
731
|
-
/** Drop expired entries first; if still over budget, drop the oldest-expiring. */
|
|
732
|
-
evict() {
|
|
733
|
-
const now = Date.now();
|
|
734
|
-
for (const [k, e] of this.store) {
|
|
735
|
-
if (now > e.expiresAt) this.store.delete(k);
|
|
736
|
-
}
|
|
737
|
-
if (this.store.size > this.maxScopes) {
|
|
738
|
-
const sorted = [...this.store.entries()].sort((a, b) => a[1].expiresAt - b[1].expiresAt);
|
|
739
|
-
const overBy = sorted.length - this.maxScopes;
|
|
740
|
-
for (let i = 0; i < overBy; i++) {
|
|
741
|
-
this.store.delete(sorted[i][0]);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
var COLLECTION_CACHE_TTL_MS = 6e4;
|
|
747
|
-
|
|
748
|
-
// src/lib/smartProfileCollections.ts
|
|
749
|
-
var SMART_PROFILE_COLLECTIONS = [
|
|
750
|
-
"tensions",
|
|
751
|
-
"glossary",
|
|
752
|
-
"decisions",
|
|
753
|
-
"strategy",
|
|
754
|
-
"insights"
|
|
755
|
-
];
|
|
756
|
-
|
|
757
|
-
// src/lib/resolveCollection.ts
|
|
758
|
-
async function resolveCollection(params) {
|
|
759
|
-
const { name, description, typeHint, allowReviewRouting } = params;
|
|
760
|
-
const sessionId = getAgentSessionId();
|
|
761
|
-
const result2 = await kernelCall("chain.resolveCollection", {
|
|
762
|
-
entryName: name,
|
|
763
|
-
entryDescription: description,
|
|
764
|
-
...typeHint ? { typeHint } : {},
|
|
765
|
-
...allowReviewRouting ? { allowReviewRouting } : {},
|
|
766
|
-
...sessionId ? { agentSessionId: sessionId } : {}
|
|
767
|
-
});
|
|
768
|
-
return result2 ?? null;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// src/lib/inferSourceDate.ts
|
|
772
|
-
function inferSourceDate(...values) {
|
|
773
|
-
const combined = values.filter((value) => typeof value === "string" && value.trim().length > 0).join("\n");
|
|
774
|
-
if (!combined) return void 0;
|
|
775
|
-
const isoMatch = combined.match(/\b(20\d{2}-\d{2}-\d{2})\b/);
|
|
776
|
-
if (isoMatch) return isoMatch[1];
|
|
777
|
-
const monthMatch = combined.match(
|
|
778
|
-
/\b(january|february|march|april|may|june|july|august|september|october|november|december)\s+(\d{1,2})(?:st|nd|rd|th)?(?:,?\s+(20\d{2}))?\b/i
|
|
779
|
-
);
|
|
780
|
-
if (!monthMatch) return void 0;
|
|
781
|
-
const monthIndex = [
|
|
782
|
-
"january",
|
|
783
|
-
"february",
|
|
784
|
-
"march",
|
|
785
|
-
"april",
|
|
786
|
-
"may",
|
|
787
|
-
"june",
|
|
788
|
-
"july",
|
|
789
|
-
"august",
|
|
790
|
-
"september",
|
|
791
|
-
"october",
|
|
792
|
-
"november",
|
|
793
|
-
"december"
|
|
794
|
-
].indexOf(monthMatch[1].toLowerCase());
|
|
795
|
-
if (monthIndex < 0) return void 0;
|
|
796
|
-
const inferredYear = monthMatch[3] ?? combined.match(/\b(20\d{2})\b/)?.[1] ?? String((/* @__PURE__ */ new Date()).getUTCFullYear());
|
|
797
|
-
const month = String(monthIndex + 1).padStart(2, "0");
|
|
798
|
-
const day = monthMatch[2].padStart(2, "0");
|
|
799
|
-
return `${inferredYear}-${month}-${day}`;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
// src/lib/collectionCache.ts
|
|
803
|
-
var collectionCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
804
|
-
async function getCollections() {
|
|
805
|
-
const scope = cacheScope();
|
|
806
|
-
const hit = collectionCache.get(scope);
|
|
807
|
-
if (hit) return hit.collections;
|
|
808
|
-
const result2 = await kernelQuery("chain.listCollections") ?? [];
|
|
809
|
-
const bySlug = new Map(result2.map((c) => [c.slug, c]));
|
|
810
|
-
collectionCache.set(scope, { collections: result2, bySlug });
|
|
811
|
-
return result2;
|
|
812
|
-
}
|
|
813
|
-
async function getCollectionBySlug(slug) {
|
|
814
|
-
await getCollections();
|
|
815
|
-
return collectionCache.get(cacheScope())?.bySlug.get(slug);
|
|
816
|
-
}
|
|
817
|
-
function invalidateCollectionCacheForScope() {
|
|
818
|
-
collectionCache.delete(cacheScope());
|
|
819
|
-
}
|
|
820
|
-
async function isGoverned(slug) {
|
|
821
|
-
const col = await getCollectionBySlug(slug);
|
|
822
|
-
return !!col?.governed;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// src/lib/entryDataNormalization.ts
|
|
826
|
-
function buildDataFromFields(fields, descriptionField, descriptionValue) {
|
|
827
|
-
if (!descriptionField) return null;
|
|
828
|
-
const data = {};
|
|
829
|
-
for (const field of fields) {
|
|
830
|
-
if (field.key === descriptionField) {
|
|
831
|
-
data[field.key] = descriptionValue;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return data;
|
|
835
|
-
}
|
|
836
|
-
function resolveDescriptionField(fields, descriptionFieldKey) {
|
|
837
|
-
const verifiedDescriptionFieldKey = descriptionFieldKey && fields.some((field) => field.key === descriptionFieldKey) ? descriptionFieldKey : void 0;
|
|
838
|
-
const literalDescriptionField = fields.find(
|
|
839
|
-
(field) => field.key === "description" && (field.type === "text" || field.type === "string")
|
|
840
|
-
)?.key;
|
|
841
|
-
const layoutDescriptionField = fields.find(
|
|
842
|
-
(field) => field.zone === "body" || field.displayHint === "textarea" || field.displayHint === "section"
|
|
843
|
-
)?.key;
|
|
844
|
-
const freeTextCandidates = fields.filter((field) => field.zone !== "meta" && field.zone !== "header");
|
|
845
|
-
const firstTextField = freeTextCandidates.find((field) => field.type === "text")?.key;
|
|
846
|
-
const firstStringField = freeTextCandidates.find((field) => field.type === "string")?.key;
|
|
847
|
-
return verifiedDescriptionFieldKey || literalDescriptionField || layoutDescriptionField || firstTextField || firstStringField || null;
|
|
848
|
-
}
|
|
849
|
-
function noDescriptionFieldMessage(collectionSlug) {
|
|
850
|
-
return `\`${collectionSlug}\` has no field for captured free text \u2014 no descriptionFieldKey is declared, no layout body field, and no text/string field outside its meta/header zone. Ask a platform admin to declare a descriptionFieldKey (or a body-zoned text field) on this collection before capturing into it.`;
|
|
851
|
-
}
|
|
852
|
-
function resolveProfileDescriptionField(col, fieldsOverride) {
|
|
853
|
-
const fields = fieldsOverride ?? col.fields ?? [];
|
|
854
|
-
return resolveDescriptionField(fields, col.descriptionFieldKey);
|
|
855
|
-
}
|
|
856
|
-
function resolveGenericDescriptionField(fieldsOverride) {
|
|
857
|
-
return fieldsOverride ? resolveDescriptionField(fieldsOverride, void 0) : null;
|
|
858
|
-
}
|
|
859
|
-
function buildDataFromFieldsOrRefuse(fields, descriptionField, descriptionValue, collectionSlug) {
|
|
860
|
-
if (!descriptionField) {
|
|
861
|
-
const msg = noDescriptionFieldMessage(collectionSlug);
|
|
862
|
-
const recovery = `Pass the text via an existing field in \`data\` (see \`collections action=get\` for ${collectionSlug}'s field list), or ask a platform admin to declare a descriptionFieldKey (or a body-zoned text field) on ${collectionSlug}.`;
|
|
863
|
-
return {
|
|
864
|
-
refusal: failureResult(msg, "VALIDATION_FAILED", `Collection '${collectionSlug}' has no field for free text.`, recovery)
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
return { data: buildDataFromFields(fields, descriptionField, descriptionValue), descriptionField };
|
|
868
|
-
}
|
|
869
|
-
function isEmptyValue(v) {
|
|
870
|
-
return v == null || (Array.isArray(v) ? v.length === 0 : String(v).trim() === "");
|
|
871
|
-
}
|
|
872
|
-
function isNonEmptyValue(v) {
|
|
873
|
-
return v != null && (Array.isArray(v) ? v.length > 0 : String(v).trim() !== "");
|
|
874
|
-
}
|
|
875
|
-
function levenshtein(a, b) {
|
|
876
|
-
const m = a.length;
|
|
877
|
-
const n = b.length;
|
|
878
|
-
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
|
|
879
|
-
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
880
|
-
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
881
|
-
for (let i = 1; i <= m; i++) {
|
|
882
|
-
for (let j = 1; j <= n; j++) {
|
|
883
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
884
|
-
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
return dp[m][n];
|
|
888
|
-
}
|
|
889
|
-
var APPETITE_OPTIONS = ["small", "medium", "large"];
|
|
890
|
-
function findClosestOption(value, options) {
|
|
891
|
-
const lower = value.toLowerCase();
|
|
892
|
-
const exact = options.find((o) => o.toLowerCase() === lower);
|
|
893
|
-
if (exact) return { opt: exact, dist: 0 };
|
|
894
|
-
return options.reduce(
|
|
895
|
-
(acc, opt) => {
|
|
896
|
-
const d = levenshtein(lower, opt.toLowerCase());
|
|
897
|
-
return d < acc.dist ? { opt, dist: d } : acc;
|
|
898
|
-
},
|
|
899
|
-
{ opt: options[0], dist: Infinity }
|
|
900
|
-
);
|
|
901
|
-
}
|
|
902
|
-
function canonicalizeSelects(data, fields, isBet) {
|
|
903
|
-
if (isBet && data.appetite != null && typeof data.appetite === "string") {
|
|
904
|
-
const val = data.appetite.trim();
|
|
905
|
-
if (val) {
|
|
906
|
-
const { opt, dist } = findClosestOption(val, APPETITE_OPTIONS);
|
|
907
|
-
if (dist <= 4) data.appetite = opt;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
for (const field of fields) {
|
|
911
|
-
if (field.type !== "select" || !field.options?.length) continue;
|
|
912
|
-
if (field.key === "appetite" && isBet) continue;
|
|
913
|
-
const val = data[field.key];
|
|
914
|
-
if (val == null || typeof val !== "string") continue;
|
|
915
|
-
const trimmed = val.trim();
|
|
916
|
-
if (!trimmed) continue;
|
|
917
|
-
const { opt, dist } = findClosestOption(trimmed, field.options);
|
|
918
|
-
if (dist <= 4) data[field.key] = opt;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
|
|
922
693
|
// src/lib/captureForRealAction.ts
|
|
923
694
|
function buildCaptureForRealNextAction(payload) {
|
|
924
695
|
const {
|
|
@@ -1130,9 +901,6 @@ var captureSinglePreviewOutputSchema = z3.object({
|
|
|
1130
901
|
taskAlignment: taskAlignmentSchema.optional()
|
|
1131
902
|
}).strict();
|
|
1132
903
|
|
|
1133
|
-
// src/lib/readinessNotices.ts
|
|
1134
|
-
var READINESS_PROVISIONAL_LINE = "Readiness is provisional: its sample omitted accepted entries. Counts are lower bounds, apparent gaps may already be covered.";
|
|
1135
|
-
|
|
1136
904
|
// src/lib/limits.ts
|
|
1137
905
|
var ENTRY_BODY_MAX_CHARS = 15e4;
|
|
1138
906
|
var ENTRY_NAME_MAX_CHARS = 250;
|
|
@@ -1142,46 +910,13 @@ var RELATION_BATCH_MAX = 25;
|
|
|
1142
910
|
|
|
1143
911
|
// src/tools/smart-capture.ts
|
|
1144
912
|
var CAPTURE_LINK_OUTCOMES = ["written", "refused"];
|
|
1145
|
-
var GENERIC_PROFILE = {
|
|
1146
|
-
governedDraft: false,
|
|
1147
|
-
descriptionField: null,
|
|
1148
|
-
// TEN-3704 F4: never a hard-coded "description" — see resolveGenericDescriptionField
|
|
1149
|
-
defaults: []
|
|
1150
|
-
};
|
|
1151
|
-
async function buildRuntimeProfile(slug, fieldsOverride) {
|
|
1152
|
-
const col = await getCollectionBySlug(slug);
|
|
1153
|
-
if (!col) return { ...GENERIC_PROFILE, descriptionField: resolveGenericDescriptionField(fieldsOverride) };
|
|
1154
|
-
const descriptionField = resolveProfileDescriptionField(col, fieldsOverride);
|
|
1155
|
-
return {
|
|
1156
|
-
governedDraft: col.governed === true,
|
|
1157
|
-
descriptionField,
|
|
1158
|
-
defaults: []
|
|
1159
|
-
};
|
|
1160
|
-
}
|
|
1161
|
-
var profileCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
1162
|
-
async function getProfile(slug, fieldsOverride) {
|
|
1163
|
-
const scope = cacheScope();
|
|
1164
|
-
let map = profileCache.get(scope);
|
|
1165
|
-
if (!map) {
|
|
1166
|
-
map = /* @__PURE__ */ new Map();
|
|
1167
|
-
profileCache.set(scope, map);
|
|
1168
|
-
}
|
|
1169
|
-
const cached = map.get(slug);
|
|
1170
|
-
if (cached) return cached;
|
|
1171
|
-
const profile = await buildRuntimeProfile(slug, fieldsOverride);
|
|
1172
|
-
map.set(slug, profile);
|
|
1173
|
-
return profile;
|
|
1174
|
-
}
|
|
1175
|
-
function invalidateProfileCacheForScope() {
|
|
1176
|
-
profileCache.delete(cacheScope());
|
|
1177
|
-
}
|
|
1178
913
|
var SPINE_FLOOR_SENTENCE = "kept as a draft; it can be accepted once its spine above is accepted \u2014 the promote door checks the whole spine and names the first missing rung.";
|
|
1179
914
|
var SPINE_FLOOR_EXEMPT_SENTENCE = "kept as a draft, though this workspace is not yet enforcing the spine above it \u2014 it can be accepted as-is.";
|
|
1180
915
|
var captureSchema = z4.object({
|
|
1181
|
-
collection: z4.string().optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014
|
|
916
|
+
collection: z4.string().optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
|
|
1182
917
|
name: z4.string().describe("Display name \u2014 be specific (e.g. 'Convex adjacency list won't scale for graph traversal')"),
|
|
1183
918
|
description: z4.string().describe("Full context \u2014 what's happening, why it matters, what you observed"),
|
|
1184
|
-
context: z4.string().optional().describe("Optional additional context (e.g. 'Observed during context gather calls taking 700ms+')"),
|
|
919
|
+
context: z4.string().optional().describe("Optional additional context (e.g. 'Observed during context gather calls taking 700ms+'); appended to the description, stored only when the description is."),
|
|
1185
920
|
canonicalKey: z4.string().optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
|
|
1186
921
|
data: z4.record(z4.unknown()).optional().describe("Explicit field values when you know the schema (e.g. canonical_key, cardinality_rule, required_fields). Merged with inferred values; user-provided wins."),
|
|
1187
922
|
why: z4.string().optional().describe("Reasoning behind this entry \u2014 hard-required for purpose/vision/strategy/objective/decision/rule/key_result/product."),
|
|
@@ -1218,236 +953,10 @@ var captureClassifierSchema = z4.object({
|
|
|
1218
953
|
),
|
|
1219
954
|
agentProvidedCollection: z4.string().optional(),
|
|
1220
955
|
overrideCommand: z4.string().optional(),
|
|
1221
|
-
classifiedBy: z4.
|
|
1222
|
-
confidenceTier: z4.
|
|
956
|
+
classifiedBy: z4.string().optional(),
|
|
957
|
+
confidenceTier: z4.string().optional(),
|
|
1223
958
|
reasoning: z4.string().optional()
|
|
1224
959
|
});
|
|
1225
|
-
function trackClassifierTelemetry(params) {
|
|
1226
|
-
const telemetry = {
|
|
1227
|
-
predicted_collection: params.predictedCollection,
|
|
1228
|
-
confidence: params.confidence,
|
|
1229
|
-
auto_routed: params.autoRouted,
|
|
1230
|
-
reason_category: params.reasonCategory,
|
|
1231
|
-
explicit_collection_provided: params.explicitCollectionProvided
|
|
1232
|
-
};
|
|
1233
|
-
trackCaptureClassifierEvaluated(params.workspaceId, telemetry);
|
|
1234
|
-
if (params.outcome === "auto-routed") {
|
|
1235
|
-
trackCaptureClassifierAutoRouted(params.workspaceId, telemetry);
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
trackCaptureClassifierFallback(params.workspaceId, telemetry);
|
|
1239
|
-
}
|
|
1240
|
-
function buildClassifierUnknownResult() {
|
|
1241
|
-
return {
|
|
1242
|
-
content: [{
|
|
1243
|
-
type: "text",
|
|
1244
|
-
text: "I could not infer a collection confidently from this input.\n\nPlease provide `collection`, or rewrite with clearer intent (decision/problem/definition/insight/work-package)."
|
|
1245
|
-
}],
|
|
1246
|
-
structuredContent: failure(
|
|
1247
|
-
"VALIDATION_ERROR",
|
|
1248
|
-
"Could not infer collection from input.",
|
|
1249
|
-
"Provide collection explicitly, or rewrite with clearer intent.",
|
|
1250
|
-
[{ tool: "collections", description: "List available collections", parameters: { action: "list" } }],
|
|
1251
|
-
{ classifier: { enabled: true, autoRouted: false, agrees: false, abstained: true, topConfidence: 0, confidence: 0, reasons: [], candidates: [] } }
|
|
1252
|
-
)
|
|
1253
|
-
};
|
|
1254
|
-
}
|
|
1255
|
-
function buildLowConfidenceResult(resolved, classifierMeta) {
|
|
1256
|
-
const allCandidates = [
|
|
1257
|
-
{ collection: resolved.collection, confidence: resolved.confidence },
|
|
1258
|
-
...resolved.alternatives
|
|
1259
|
-
];
|
|
1260
|
-
const candidateFieldCounts = /* @__PURE__ */ new Map();
|
|
1261
|
-
for (const c of classifierMeta.candidates) {
|
|
1262
|
-
if (c.requiredFieldCount !== void 0) {
|
|
1263
|
-
candidateFieldCounts.set(c.collection, c.requiredFieldCount);
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
const suggestions = allCandidates.map((c) => {
|
|
1267
|
-
const fieldCost = candidateFieldCounts.get(c.collection);
|
|
1268
|
-
const fieldNote = fieldCost !== void 0 ? `, ${fieldCost} required field${fieldCost === 1 ? "" : "s"}` : "";
|
|
1269
|
-
return `- \`${c.collection}\` (${c.confidence}% confidence${fieldNote})`;
|
|
1270
|
-
}).join("\n");
|
|
1271
|
-
const textBody = `Low confidence classification (${resolved.confidence}%, classified by ${resolved.classifiedBy}).
|
|
1272
|
-
|
|
1273
|
-
Predicted: \`${resolved.collection}\`
|
|
1274
|
-
Reason: ${resolved.reasoning || "low signal"}
|
|
1275
|
-
|
|
1276
|
-
Choose one of these and retry with \`collection\`:
|
|
1277
|
-
${suggestions}
|
|
1278
|
-
|
|
1279
|
-
Correction path: rerun with your chosen \`collection\`, or capture and use \`entries action=move\` later.`;
|
|
1280
|
-
return {
|
|
1281
|
-
content: [{ type: "text", text: textBody }],
|
|
1282
|
-
structuredContent: failure(
|
|
1283
|
-
"VALIDATION_ERROR",
|
|
1284
|
-
`Low confidence routing to '${resolved.collection}' (${resolved.confidence}%, ${resolved.classifiedBy}).`,
|
|
1285
|
-
"Rerun with explicit collection.",
|
|
1286
|
-
allCandidates.map((c) => ({
|
|
1287
|
-
tool: "capture",
|
|
1288
|
-
description: `Capture to ${c.collection}`,
|
|
1289
|
-
parameters: { collection: c.collection }
|
|
1290
|
-
})),
|
|
1291
|
-
{ classifier: classifierMeta }
|
|
1292
|
-
)
|
|
1293
|
-
};
|
|
1294
|
-
}
|
|
1295
|
-
function buildClassifierMeta(resolved, overrides = {}) {
|
|
1296
|
-
return {
|
|
1297
|
-
enabled: true,
|
|
1298
|
-
autoRouted: false,
|
|
1299
|
-
agrees: true,
|
|
1300
|
-
abstained: false,
|
|
1301
|
-
topConfidence: resolved.confidence,
|
|
1302
|
-
confidence: resolved.confidence,
|
|
1303
|
-
reasons: resolved.reasoning ? [resolved.reasoning] : [],
|
|
1304
|
-
candidates: [
|
|
1305
|
-
{ collection: resolved.collection, confidence: resolved.confidence },
|
|
1306
|
-
...resolved.alternatives.map((a) => ({ collection: a.collection, confidence: a.confidence }))
|
|
1307
|
-
].slice(0, 3),
|
|
1308
|
-
classifiedBy: resolved.classifiedBy,
|
|
1309
|
-
confidenceTier: resolved.tier,
|
|
1310
|
-
reasoning: resolved.reasoning,
|
|
1311
|
-
...overrides
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
async function enrichCandidatesWithFieldCounts(meta) {
|
|
1315
|
-
try {
|
|
1316
|
-
const cols = await getCollections();
|
|
1317
|
-
const reqBySlug = new Map(
|
|
1318
|
-
cols.map((c) => [c.slug, (c.fields ?? []).filter((f) => f.required === true).length])
|
|
1319
|
-
);
|
|
1320
|
-
meta.candidates = meta.candidates.map((c) => ({
|
|
1321
|
-
...c,
|
|
1322
|
-
requiredFieldCount: reqBySlug.get(c.collection) ?? 0
|
|
1323
|
-
}));
|
|
1324
|
-
} catch {
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
function emitClassificationTelemetry(resolved, workspaceId, opts) {
|
|
1328
|
-
const topAlt = resolved.alternatives?.[0] ?? null;
|
|
1329
|
-
trackCollectionClassified(workspaceId, {
|
|
1330
|
-
collection_slug: resolved.collection,
|
|
1331
|
-
thinking_layer: resolved.thinkingLayer ?? null,
|
|
1332
|
-
confidence: resolved.confidence,
|
|
1333
|
-
classified_by: resolved.classifiedBy,
|
|
1334
|
-
confidence_tier: resolved.tier,
|
|
1335
|
-
alternative_slug: topAlt?.collection ?? null,
|
|
1336
|
-
alternative_confidence: topAlt?.confidence ?? null,
|
|
1337
|
-
explicit_collection_provided: opts.explicitCollectionProvided,
|
|
1338
|
-
auto_routed: opts.autoRouted
|
|
1339
|
-
});
|
|
1340
|
-
}
|
|
1341
|
-
async function resolveCaptureCollection(params) {
|
|
1342
|
-
const {
|
|
1343
|
-
collection,
|
|
1344
|
-
name,
|
|
1345
|
-
description,
|
|
1346
|
-
workspaceId,
|
|
1347
|
-
explicitCollectionProvided
|
|
1348
|
-
} = params;
|
|
1349
|
-
if (collection) {
|
|
1350
|
-
const resolved2 = await resolveCollection({ name, description });
|
|
1351
|
-
if (resolved2) {
|
|
1352
|
-
const agrees = resolved2.collection === collection;
|
|
1353
|
-
const classifierMeta2 = buildClassifierMeta(resolved2, {
|
|
1354
|
-
autoRouted: false,
|
|
1355
|
-
agrees,
|
|
1356
|
-
agentProvidedCollection: collection,
|
|
1357
|
-
...!agrees && {
|
|
1358
|
-
overrideCommand: `capture collection="${resolved2.collection}"`
|
|
1359
|
-
}
|
|
1360
|
-
});
|
|
1361
|
-
await enrichCandidatesWithFieldCounts(classifierMeta2);
|
|
1362
|
-
emitClassificationTelemetry(resolved2, workspaceId, {
|
|
1363
|
-
explicitCollectionProvided: true,
|
|
1364
|
-
autoRouted: false
|
|
1365
|
-
});
|
|
1366
|
-
if (!agrees) {
|
|
1367
|
-
trackClassifierTelemetry({
|
|
1368
|
-
workspaceId,
|
|
1369
|
-
predictedCollection: resolved2.collection,
|
|
1370
|
-
confidence: resolved2.confidence,
|
|
1371
|
-
autoRouted: false,
|
|
1372
|
-
reasonCategory: "low-confidence",
|
|
1373
|
-
explicitCollectionProvided: true,
|
|
1374
|
-
outcome: "fallback"
|
|
1375
|
-
});
|
|
1376
|
-
const agentInCandidates = resolved2.alternatives.some((a) => a.collection === collection) || resolved2.collection === collection;
|
|
1377
|
-
trackClassifierDivergence(workspaceId, {
|
|
1378
|
-
classifier_collection: resolved2.collection,
|
|
1379
|
-
agent_collection: collection,
|
|
1380
|
-
classifier_confidence: resolved2.confidence,
|
|
1381
|
-
classifier_tier: resolved2.tier,
|
|
1382
|
-
agent_in_candidates: agentInCandidates
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
return { resolvedCollection: collection, classifierMeta: classifierMeta2 };
|
|
1386
|
-
}
|
|
1387
|
-
return {
|
|
1388
|
-
resolvedCollection: collection,
|
|
1389
|
-
classifierMeta: {
|
|
1390
|
-
enabled: true,
|
|
1391
|
-
autoRouted: false,
|
|
1392
|
-
agrees: false,
|
|
1393
|
-
abstained: true,
|
|
1394
|
-
topConfidence: 0,
|
|
1395
|
-
confidence: 0,
|
|
1396
|
-
reasons: [],
|
|
1397
|
-
candidates: [],
|
|
1398
|
-
agentProvidedCollection: collection
|
|
1399
|
-
}
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
|
-
const resolved = await resolveCollection({ name, description });
|
|
1403
|
-
if (!resolved) {
|
|
1404
|
-
trackClassifierTelemetry({
|
|
1405
|
-
workspaceId,
|
|
1406
|
-
predictedCollection: "unknown",
|
|
1407
|
-
confidence: 0,
|
|
1408
|
-
autoRouted: false,
|
|
1409
|
-
reasonCategory: "low-confidence",
|
|
1410
|
-
explicitCollectionProvided,
|
|
1411
|
-
outcome: "fallback"
|
|
1412
|
-
});
|
|
1413
|
-
return { earlyResult: buildClassifierUnknownResult() };
|
|
1414
|
-
}
|
|
1415
|
-
const autoRoute = resolved.tier !== "low";
|
|
1416
|
-
const classifierMeta = buildClassifierMeta(resolved, { autoRouted: autoRoute });
|
|
1417
|
-
await enrichCandidatesWithFieldCounts(classifierMeta);
|
|
1418
|
-
emitClassificationTelemetry(resolved, workspaceId, {
|
|
1419
|
-
explicitCollectionProvided,
|
|
1420
|
-
autoRouted: autoRoute
|
|
1421
|
-
});
|
|
1422
|
-
if (!autoRoute) {
|
|
1423
|
-
trackClassifierTelemetry({
|
|
1424
|
-
workspaceId,
|
|
1425
|
-
predictedCollection: resolved.collection,
|
|
1426
|
-
confidence: resolved.confidence,
|
|
1427
|
-
autoRouted: false,
|
|
1428
|
-
reasonCategory: "low-confidence",
|
|
1429
|
-
explicitCollectionProvided,
|
|
1430
|
-
outcome: "fallback"
|
|
1431
|
-
});
|
|
1432
|
-
return {
|
|
1433
|
-
classifierMeta,
|
|
1434
|
-
earlyResult: buildLowConfidenceResult(resolved, classifierMeta)
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
trackClassifierTelemetry({
|
|
1438
|
-
workspaceId,
|
|
1439
|
-
predictedCollection: resolved.collection,
|
|
1440
|
-
confidence: resolved.confidence,
|
|
1441
|
-
autoRouted: true,
|
|
1442
|
-
reasonCategory: "auto-routed",
|
|
1443
|
-
explicitCollectionProvided,
|
|
1444
|
-
outcome: "auto-routed"
|
|
1445
|
-
});
|
|
1446
|
-
return {
|
|
1447
|
-
resolvedCollection: resolved.collection,
|
|
1448
|
-
classifierMeta
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
960
|
var captureSuccessOutputSchema = z4.object({
|
|
1452
961
|
entryId: z4.string(),
|
|
1453
962
|
collection: z4.string(),
|
|
@@ -1459,6 +968,7 @@ var captureSuccessOutputSchema = z4.object({
|
|
|
1459
968
|
groundingReport: captureSinglePreviewOutputSchema.shape.groundingReport.optional(),
|
|
1460
969
|
classifier: captureClassifierSchema.optional(),
|
|
1461
970
|
studioUrl: z4.string().optional(),
|
|
971
|
+
// never set since TASK-964 (the chains-only Cortex link left with the retired drawer); kept, never narrowed (DEC-1831)
|
|
1462
972
|
warnings: z4.array(z4.string()).optional(),
|
|
1463
973
|
normalization: z4.object({
|
|
1464
974
|
remapped: z4.record(z4.string()).optional(),
|
|
@@ -1478,24 +988,20 @@ var captureSuccessOutputSchema = z4.object({
|
|
|
1478
988
|
// TASK-897: the kernel's outcome per link, on a create and on a replay.
|
|
1479
989
|
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(),
|
|
1480
990
|
fallbackApplied: z4.boolean().optional()
|
|
1481
|
-
//
|
|
1482
|
-
}).strict();
|
|
1483
|
-
var captureClassifierOnlyOutputSchema = z4.object({
|
|
1484
|
-
classifier: captureClassifierSchema
|
|
991
|
+
// never set since TASK-964 (the kernel classifies among live collections only); kept, never narrowed (DEC-1831)
|
|
1485
992
|
}).strict();
|
|
1486
993
|
var captureOutputSchema = z4.union([
|
|
1487
994
|
captureSuccessOutputSchema,
|
|
1488
|
-
captureSinglePreviewOutputSchema
|
|
1489
|
-
captureClassifierOnlyOutputSchema
|
|
995
|
+
captureSinglePreviewOutputSchema
|
|
1490
996
|
]);
|
|
1491
997
|
var CAPTURE_ACTIONS = ["capture"];
|
|
1492
998
|
var SUGGEST_ONLY_REMOVED = "suggestOnly was removed: call capture with preview: true to check a capture without writing.";
|
|
1493
999
|
var captureCompoundSchema = z4.object({
|
|
1494
1000
|
action: z4.enum(CAPTURE_ACTIONS).optional().default("capture").describe("Create a single knowledge entry."),
|
|
1495
|
-
collection: z4.string().max(200).optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014
|
|
1001
|
+
collection: z4.string().max(200).optional().describe("Collection slug, e.g. 'tensions', 'rules', 'glossary', 'decisions'. Optional \u2014 the kernel classifies when omitted."),
|
|
1496
1002
|
name: z4.string().max(ENTRY_NAME_MAX_CHARS).optional().describe("Display name \u2014 required."),
|
|
1497
1003
|
description: z4.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Full context, in Markdown \u2014 required."),
|
|
1498
|
-
context: z4.string().max(5e3).optional().describe("Optional additional context."),
|
|
1004
|
+
context: z4.string().max(5e3).optional().describe("Optional additional context; appended to the description, so it is stored only when the description is (not when data carries the prose field)."),
|
|
1499
1005
|
canonicalKey: z4.string().max(200).optional().describe("Semantic type (e.g. 'decision', 'tension', 'vision'). Auto-assigned from collection if omitted."),
|
|
1500
1006
|
data: z4.record(z4.unknown()).optional().describe("Explicit field values; user-provided wins over inferred."),
|
|
1501
1007
|
why: z4.string().max(ENTRY_BODY_MAX_CHARS).optional().describe("Why this entry exists."),
|
|
@@ -1532,14 +1038,7 @@ function registerSmartCaptureTools(server) {
|
|
|
1532
1038
|
"capture",
|
|
1533
1039
|
{
|
|
1534
1040
|
title: "Capture",
|
|
1535
|
-
description:
|
|
1536
|
-
|
|
1537
|
-
Supported collections with smart profiles: ${SMART_PROFILE_COLLECTIONS.join(", ")}.
|
|
1538
|
-
All other collections get an ENT-{random} ID and sensible defaults.
|
|
1539
|
-
|
|
1540
|
-
**Explicit data:** When you know the schema, pass \`data: { field: value }\` to set fields directly. Top-level \`name\`/\`description\` win for those fields; \`data\` wins over inference for the rest. Governance entries also require a top-level \`why\`.
|
|
1541
|
-
|
|
1542
|
-
**Compound capture:** Pass \`links\` to create relations in the same call; a capture writes no other relations. A capture lands as a \`draft\` to accept with \`entries action=commit\`; a question is born open. The reply names possible duplicates; only an exact name in the collection is refused.`,
|
|
1041
|
+
description: "The tool for creating a single knowledge entry. Creates an entry and returns a quality scorecard \u2014 all in one call. Provide a name and description; `collection` is optional \u2014 the kernel classifies when omitted and files a low-confidence pick as a draft with one line saying so. An agent capturing several atoms from one text calls this once per atom.\n\n**Explicit data:** When you know the schema, pass `data: { field: value }` to set fields directly. A value in `data` is kept as sent; `description` is the entry's text and fills the collection's prose field only when `data` leaves it empty (the same rule as `pb capture`); when `data` carries the prose field, `description` and `context` are used for classification and judging only and are not stored. Governance entries also require a top-level `why`.\n\n**Compound capture:** Pass `links` to create relations in the same call; a capture writes no other relations. A capture lands as a `draft` to accept with `entries action=commit`; a question is born open. The reply names possible duplicates; only an exact name in the collection is refused.",
|
|
1543
1042
|
inputSchema: captureCompoundSchema,
|
|
1544
1043
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
1545
1044
|
},
|
|
@@ -1554,129 +1053,46 @@ All other collections get an ENT-{random} ID and sensible defaults.
|
|
|
1554
1053
|
})
|
|
1555
1054
|
);
|
|
1556
1055
|
}
|
|
1557
|
-
|
|
1056
|
+
function classifierFromReply(classification) {
|
|
1057
|
+
return {
|
|
1058
|
+
enabled: true,
|
|
1059
|
+
autoRouted: true,
|
|
1060
|
+
agrees: true,
|
|
1061
|
+
abstained: false,
|
|
1062
|
+
topConfidence: classification.confidence,
|
|
1063
|
+
confidence: classification.confidence,
|
|
1064
|
+
reasons: classification.reasoning ? [classification.reasoning] : [],
|
|
1065
|
+
candidates: [
|
|
1066
|
+
{ collection: classification.collection, confidence: classification.confidence },
|
|
1067
|
+
...classification.alternatives.map((a) => ({ collection: a.collection, confidence: a.confidence }))
|
|
1068
|
+
].slice(0, 3),
|
|
1069
|
+
classifiedBy: classification.classifiedBy,
|
|
1070
|
+
confidenceTier: classification.tier,
|
|
1071
|
+
reasoning: classification.reasoning
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
async function handleCapture(_server, { collection, name, description, context, canonicalKey, data: userData, why, links, sourceRef, sourceExcerpt, preview, ownerRoleEntryId, team }) {
|
|
1558
1075
|
requireWriteAccess();
|
|
1559
1076
|
const timingStart = Date.now();
|
|
1560
1077
|
const wsCtx = await getWorkspaceContext();
|
|
1561
|
-
const
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
});
|
|
1569
|
-
if (resolution.earlyResult) {
|
|
1570
|
-
return resolution.earlyResult;
|
|
1571
|
-
}
|
|
1572
|
-
let resolvedCollection = resolution.resolvedCollection;
|
|
1573
|
-
const classifierMeta = resolution.classifierMeta;
|
|
1574
|
-
if (!resolvedCollection) {
|
|
1575
|
-
return buildClassifierUnknownResult();
|
|
1576
|
-
}
|
|
1577
|
-
let canonicalKeyAutoInjected = false;
|
|
1578
|
-
if (resolvedCollection === "chains" && !canonicalKey && !explicitCollectionProvided && classifierMeta?.candidates?.some((c) => c.collection === "work-packages")) {
|
|
1579
|
-
canonicalKey = "work_package";
|
|
1580
|
-
canonicalKeyAutoInjected = true;
|
|
1078
|
+
const collectionSlug = typeof collection === "string" && collection.trim().length > 0 ? collection.trim() : void 0;
|
|
1079
|
+
const text = typeof context === "string" && context.trim().length > 0 ? `${description}
|
|
1080
|
+
|
|
1081
|
+
${context.trim()}` : description;
|
|
1082
|
+
if (text.length > ENTRY_BODY_MAX_CHARS) {
|
|
1083
|
+
const msg = `description plus context is ${text.length} characters; the entry text is capped at ${ENTRY_BODY_MAX_CHARS}. Shorten one of them.`;
|
|
1084
|
+
return { content: [{ type: "text", text: msg }], structuredContent: failure("VALIDATION_ERROR", msg, "Shorten description or context.") };
|
|
1581
1085
|
}
|
|
1582
1086
|
const tAfterClassify = Date.now();
|
|
1583
|
-
let fallbackApplied = false;
|
|
1584
|
-
let col = await kernelQuery("chain.getCollection", { slug: resolvedCollection });
|
|
1585
|
-
if (!col && !explicitCollectionProvided) {
|
|
1586
|
-
const allCollections = await getCollections();
|
|
1587
|
-
const fallback = allCollections.find((c) => c.slug === "insights") ?? allCollections.find((c) => c.stratum === "system");
|
|
1588
|
-
if (fallback && canonicalKey === "work_package" && !canonicalKeyAutoInjected && fallback.slug !== "chains") {
|
|
1589
|
-
const msg = `\`work_package\` entries must be in the \`chains\` collection, not \`${fallback.slug}\`. \`${resolvedCollection}\` doesn't exist here, so there's no chains-equivalent drawer to fall back into.`;
|
|
1590
|
-
return { content: [{ type: "text", text: msg }], structuredContent: failure("VALIDATION_FAILED", `canonicalKey 'work_package' requires 'chains'; fallback target '${fallback.slug}' doesn't qualify.`, msg) };
|
|
1591
|
-
}
|
|
1592
|
-
if (fallback) {
|
|
1593
|
-
resolvedCollection = fallback.slug;
|
|
1594
|
-
col = fallback;
|
|
1595
|
-
fallbackApplied = true;
|
|
1596
|
-
if (canonicalKeyAutoInjected) canonicalKey = void 0;
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
if (!col) {
|
|
1600
|
-
const msg = `Collection \`${resolvedCollection}\` not found. Creating a collection is not self-serve \u2014 ask a platform admin to provision it, or enable the module ontology pack that owns it. Use \`collections action=list\` to see what exists.`;
|
|
1601
|
-
return {
|
|
1602
|
-
content: [{ type: "text", text: msg }],
|
|
1603
|
-
structuredContent: failure(
|
|
1604
|
-
"NOT_FOUND",
|
|
1605
|
-
`Collection '${resolvedCollection}' not found.`,
|
|
1606
|
-
msg,
|
|
1607
|
-
[{ tool: "collections", description: "List collections", parameters: { action: "list" } }]
|
|
1608
|
-
)
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
const profile = await getProfile(resolvedCollection, col.fields);
|
|
1612
1087
|
const groundingReport = { scanTruncated: null, duplicates: [] };
|
|
1613
1088
|
let groundingCaveat = "";
|
|
1614
|
-
const built = buildDataFromFieldsOrRefuse(col.fields ?? [], profile.descriptionField, description, resolvedCollection);
|
|
1615
|
-
if ("refusal" in built) return built.refusal;
|
|
1616
|
-
const { data, descriptionField } = built;
|
|
1617
|
-
for (const def of profile.defaults) {
|
|
1618
|
-
if (def.value !== "infer" && def.value !== "today") {
|
|
1619
|
-
data[def.key] = def.value;
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
if (profile.inferField) {
|
|
1623
|
-
const inferred = profile.inferField({
|
|
1624
|
-
collection: resolvedCollection,
|
|
1625
|
-
name,
|
|
1626
|
-
description,
|
|
1627
|
-
context,
|
|
1628
|
-
data,
|
|
1629
|
-
entryId: "",
|
|
1630
|
-
collectionFields: col.fields ?? []
|
|
1631
|
-
});
|
|
1632
|
-
for (const [key, val] of Object.entries(inferred)) {
|
|
1633
|
-
if (val !== void 0 && val !== "") {
|
|
1634
|
-
data[key] = val;
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
if (isEmptyValue(data.date)) {
|
|
1639
|
-
const inferredSourceDate = inferSourceDate(name, description, context, sourceRef, sourceExcerpt);
|
|
1640
|
-
if (inferredSourceDate && (col.fields ?? []).some((field) => field.key === "date")) {
|
|
1641
|
-
data.date = inferredSourceDate;
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
if (userData && typeof userData === "object") Object.assign(data, userData);
|
|
1645
|
-
data[descriptionField] = description;
|
|
1646
|
-
const colMeta = await getCollectionBySlug(resolvedCollection);
|
|
1647
|
-
const isWorkPackageCapture = canonicalKey === "work_package" || colMeta?.defaultCanonicalKey === "work_package";
|
|
1648
|
-
let entryWarnings = [];
|
|
1649
|
-
const isStrategyCapture = colMeta?.thinkingLayer === "strategy";
|
|
1650
|
-
const shouldDecompose = (isStrategyCapture || isWorkPackageCapture) && description.trim().length > 0;
|
|
1651
|
-
if (shouldDecompose) {
|
|
1652
|
-
try {
|
|
1653
|
-
const decomposed = await kernelCall(
|
|
1654
|
-
"chain.decomposeContent",
|
|
1655
|
-
{
|
|
1656
|
-
collectionSlug: resolvedCollection,
|
|
1657
|
-
entryName: name,
|
|
1658
|
-
description,
|
|
1659
|
-
existingData: data,
|
|
1660
|
-
...isWorkPackageCapture ? { canonicalKey: "work_package" } : {},
|
|
1661
|
-
...colMeta?.thinkingLayer ? { thinkingLayer: colMeta.thinkingLayer } : {}
|
|
1662
|
-
}
|
|
1663
|
-
);
|
|
1664
|
-
if (decomposed?.decomposed) {
|
|
1665
|
-
for (const [key, val] of Object.entries(decomposed.decomposed)) {
|
|
1666
|
-
if (isEmptyValue(data[key]) && isNonEmptyValue(val)) {
|
|
1667
|
-
data[key] = val;
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
} catch {
|
|
1672
|
-
entryWarnings.push("Content decomposition skipped (transient error). Entry saved with description only.");
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
canonicalizeSelects(data, col.fields ?? [], isWorkPackageCapture);
|
|
1676
1089
|
const agentId = getAgentSessionId();
|
|
1677
1090
|
let finalEntryId;
|
|
1678
|
-
let
|
|
1091
|
+
let landedCollection;
|
|
1092
|
+
let replyFields = [];
|
|
1093
|
+
let classifierMeta;
|
|
1679
1094
|
let normalizationMeta;
|
|
1095
|
+
const entryWarnings = [];
|
|
1680
1096
|
let formativeHints = [];
|
|
1681
1097
|
let coachingFromCreate;
|
|
1682
1098
|
let wasAutoCommittedServerSide = false;
|
|
@@ -1684,11 +1100,16 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1684
1100
|
let linkOutcomes = [];
|
|
1685
1101
|
try {
|
|
1686
1102
|
const result2 = await kernelMutation("chain.createEntry", {
|
|
1687
|
-
|
|
1103
|
+
// TASK-964: the slug only when the caller named one; otherwise the kernel classifies (entries.ts, the auto branch).
|
|
1104
|
+
...collectionSlug ? { collectionSlug } : {},
|
|
1688
1105
|
name,
|
|
1106
|
+
description: text,
|
|
1107
|
+
// the classifier's, the judge's and the shaper's text; the kernel files it in the collection's prose field when that is empty
|
|
1689
1108
|
// DEC-896 / BR-76: omit status — server resolves from governanceMode (Open→active, else draft).
|
|
1690
|
-
data,
|
|
1691
|
-
|
|
1109
|
+
data: userData ?? {},
|
|
1110
|
+
// as sent (WP-574/TEN-2916: no key is filtered); the kernel shapes it (DEC-2172)
|
|
1111
|
+
...canonicalKey ? { canonicalKey } : {},
|
|
1112
|
+
// the type hint (entries.ts: typeHint ?? canonicalKey)
|
|
1692
1113
|
...typeof why === "string" && why.trim() ? { why } : {},
|
|
1693
1114
|
// TASK-894: the kernel files it under the collection's why field
|
|
1694
1115
|
createdBy: agentId ? `agent:${agentId}` : "capture",
|
|
@@ -1706,22 +1127,20 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1706
1127
|
if (result2 && "replayed" in result2) return resumeReplayedCapture(result2, name);
|
|
1707
1128
|
if (result2 && isPreviewCreateEntryResult(result2)) {
|
|
1708
1129
|
const preflight = await resolvePreviewPreflight(result2, links);
|
|
1130
|
+
const previewCollection = resolveEffectiveCollection(result2, preview, collectionSlug ?? "");
|
|
1709
1131
|
const dupCount = groundingReport.duplicates.length;
|
|
1710
1132
|
const groundingSummary = dupCount > 0 ? `
|
|
1711
1133
|
|
|
1712
1134
|
**Possible duplicates:** ${dupCount}.` : "";
|
|
1713
1135
|
const wouldBlock = result2.wouldBlock === true;
|
|
1714
|
-
const headline = wouldBlock ? `Preview: would be BLOCKED \u2014 "${name}" [${
|
|
1136
|
+
const headline = wouldBlock ? `Preview: would be BLOCKED \u2014 "${name}" [${previewCollection}]` : `Preview: would capture "${name}" [${previewCollection}]`;
|
|
1715
1137
|
const previewEnvelope = success(
|
|
1716
1138
|
wouldBlock ? `Preview: would be BLOCKED \u2014 "${name}" \u2014 no DB writes` : `Preview: would capture "${name}" \u2014 no DB writes`,
|
|
1717
1139
|
{
|
|
1718
1140
|
entryId: result2.entryId,
|
|
1719
1141
|
preflight,
|
|
1720
1142
|
name,
|
|
1721
|
-
|
|
1722
|
-
// (lib/createEntryPreviewResult.ts) for why this must be the server's EFFECTIVE
|
|
1723
|
-
// post-routing collection, never the caller's pre-routing `resolvedCollection`.
|
|
1724
|
-
collection: resolveEffectiveCollection(result2, preview, resolvedCollection),
|
|
1143
|
+
collection: previewCollection,
|
|
1725
1144
|
outcome: wouldBlock ? "blocked" : "preview",
|
|
1726
1145
|
...wouldBlock && result2.blockingReason ? { blockingReason: result2.blockingReason } : {},
|
|
1727
1146
|
warnings: result2.warnings ?? [],
|
|
@@ -1735,7 +1154,7 @@ async function handleCapture(server, { collection, name, description, context, c
|
|
|
1735
1154
|
// this closes (a bare {collection,name,description} dropped `data`/`links`/etc.,
|
|
1736
1155
|
// so a required WHY supplied via `data` silently vanished on retry).
|
|
1737
1156
|
buildCaptureForRealNextAction({
|
|
1738
|
-
collection:
|
|
1157
|
+
collection: previewCollection,
|
|
1739
1158
|
name,
|
|
1740
1159
|
description,
|
|
1741
1160
|
context,
|
|
@@ -1767,14 +1186,15 @@ No DB writes \u2014 call without \`preview:true\` to capture for real.${result2.
|
|
|
1767
1186
|
};
|
|
1768
1187
|
}
|
|
1769
1188
|
const created = result2;
|
|
1770
|
-
internalId = created.docId;
|
|
1771
1189
|
finalEntryId = created.entryId;
|
|
1190
|
+
landedCollection = created.collection ?? collectionSlug ?? "";
|
|
1191
|
+
replyFields = created.fields ?? [];
|
|
1192
|
+
classifierMeta = created.classification ? classifierFromReply(created.classification) : void 0;
|
|
1772
1193
|
entryWarnings.push(...created.warnings ?? []);
|
|
1773
1194
|
normalizationMeta = created.normalization;
|
|
1774
1195
|
formativeHints = created.qualityHints ?? [];
|
|
1775
1196
|
coachingFromCreate = created.coaching;
|
|
1776
1197
|
linkOutcomes = created.links ?? [];
|
|
1777
|
-
resolveGapsForEntry(name, created.entryId);
|
|
1778
1198
|
const createdStatus = created.status;
|
|
1779
1199
|
if (createdStatus !== void 0 && isTrustAccepted(createdStatus)) wasAutoCommittedServerSide = true;
|
|
1780
1200
|
spineFloor = created.spineFloor;
|
|
@@ -1817,12 +1237,11 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1817
1237
|
}
|
|
1818
1238
|
if (verdictResult?.verdict) {
|
|
1819
1239
|
try {
|
|
1820
|
-
const wsForTracking = await getWorkspaceContext();
|
|
1821
1240
|
const v = verdictResult.verdict;
|
|
1822
1241
|
const failedCount = (v.criteria ?? []).filter((c) => !c.passed).length;
|
|
1823
|
-
trackQualityVerdict(
|
|
1242
|
+
trackQualityVerdict(wsCtx.workspaceId, {
|
|
1824
1243
|
entry_id: finalEntryId,
|
|
1825
|
-
entry_type: v.canonicalKey ??
|
|
1244
|
+
entry_type: v.canonicalKey ?? landedCollection,
|
|
1826
1245
|
tier: v.tier,
|
|
1827
1246
|
context: "capture",
|
|
1828
1247
|
passed: v.passed,
|
|
@@ -1838,50 +1257,26 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1838
1257
|
let finalStatus = "draft";
|
|
1839
1258
|
if (wasAutoCommittedServerSide) {
|
|
1840
1259
|
finalStatus = "committed";
|
|
1841
|
-
await recordSessionActivity({ entryModified: internalId });
|
|
1842
1260
|
trackChainEntryCommitted(wsCtx.workspaceId, {
|
|
1843
1261
|
entry_id: finalEntryId,
|
|
1844
|
-
collection:
|
|
1262
|
+
collection: landedCollection,
|
|
1845
1263
|
commit_method: "auto",
|
|
1846
1264
|
surface: "mcp_capture"
|
|
1847
1265
|
});
|
|
1848
1266
|
}
|
|
1849
1267
|
const lines = [
|
|
1850
1268
|
`# Captured: ${finalEntryId || name}`,
|
|
1851
|
-
`**${name}** added to \`${
|
|
1269
|
+
`**${name}** added to \`${landedCollection}\` as \`${finalStatus}\``,
|
|
1852
1270
|
`**Workspace:** ${wsCtx.workspaceSlug} (${wsCtx.workspaceId})`
|
|
1853
1271
|
];
|
|
1854
1272
|
if (classifierMeta) {
|
|
1855
1273
|
lines.push("");
|
|
1856
1274
|
lines.push("## Classification");
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
lines.push(`No classifier coverage for \`${resolvedCollection}\` \u2014 entry accepted as-is.`);
|
|
1861
|
-
} else if (classifierMeta.autoRouted) {
|
|
1862
|
-
lines.push(`Auto-routed to \`${resolvedCollection}\` (${classifierMeta.topConfidence}% confidence).`);
|
|
1863
|
-
if (classifierMeta.reasons.length > 0) {
|
|
1864
|
-
lines.push(`Reason: ${classifierMeta.reasons.join("; ")}.`);
|
|
1865
|
-
}
|
|
1866
|
-
lines.push(`Override: rerun with explicit \`collection\` if wrong.`);
|
|
1867
|
-
} else if (classifierMeta.agrees) {
|
|
1868
|
-
lines.push(`Classifier confirms \`${resolvedCollection}\` (${classifierMeta.topConfidence}% confidence).`);
|
|
1869
|
-
} else {
|
|
1870
|
-
const suggested = classifierMeta.candidates[0]?.collection ?? "unknown";
|
|
1871
|
-
lines.push(`Classifier suggests \`${suggested}\` (${classifierMeta.topConfidence}% confidence) \u2014 review classification.`);
|
|
1872
|
-
if (classifierMeta.reasons.length > 0) {
|
|
1873
|
-
lines.push(`Reason: ${classifierMeta.reasons.join("; ")}.`);
|
|
1874
|
-
}
|
|
1875
|
-
if (classifierMeta.overrideCommand) {
|
|
1876
|
-
lines.push(`Override: \`${classifierMeta.overrideCommand}\``);
|
|
1877
|
-
}
|
|
1275
|
+
lines.push(`Auto-routed to \`${landedCollection}\` (${classifierMeta.topConfidence}% confidence, ${classifierMeta.classifiedBy ?? "classifier"}).`);
|
|
1276
|
+
if (classifierMeta.reasons.length > 0) {
|
|
1277
|
+
lines.push(`Reason: ${classifierMeta.reasons.join("; ")}.`);
|
|
1878
1278
|
}
|
|
1879
1279
|
}
|
|
1880
|
-
const studioUrl = resolvedCollection === "chains" ? `${appUrl()}/w/${wsCtx.workspaceSlug}/chain?entry=${encodeURIComponent(internalId)}&railTab=context` : void 0;
|
|
1881
|
-
if (studioUrl) {
|
|
1882
|
-
lines.push("");
|
|
1883
|
-
lines.push(`**View in Cortex:** ${studioUrl}`);
|
|
1884
|
-
}
|
|
1885
1280
|
if (linkOutcomes.length > 0) {
|
|
1886
1281
|
lines.push("");
|
|
1887
1282
|
lines.push("## Requested links");
|
|
@@ -1891,9 +1286,6 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1891
1286
|
lines.push("");
|
|
1892
1287
|
lines.push(`## Accepted: ${finalEntryId}`);
|
|
1893
1288
|
lines.push(`**${name}** added to your knowledge base.`);
|
|
1894
|
-
if (await isGoverned(resolvedCollection)) {
|
|
1895
|
-
lines.push(`_Note: \`${resolvedCollection}\` is a governed collection \u2014 ensure this entry has been reviewed._`);
|
|
1896
|
-
}
|
|
1897
1289
|
} else if (spineFloor) {
|
|
1898
1290
|
lines.push("");
|
|
1899
1291
|
lines.push("## Saved as draft");
|
|
@@ -1913,28 +1305,11 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1913
1305
|
}
|
|
1914
1306
|
if (formativeHints.length > 0) {
|
|
1915
1307
|
trackCaptureQualityHints(wsCtx.workspaceId, {
|
|
1916
|
-
collection:
|
|
1308
|
+
collection: landedCollection,
|
|
1917
1309
|
hint_count: formativeHints.length,
|
|
1918
1310
|
hint_fields: formativeHints.map((h) => h.field)
|
|
1919
1311
|
});
|
|
1920
1312
|
}
|
|
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
|
-
}
|
|
1931
|
-
if (isBetOrGoal && !hasStrategyLink) {
|
|
1932
|
-
lines.push("");
|
|
1933
|
-
lines.push(
|
|
1934
|
-
`**Strategy link:** This ${isWorkPackageOrBetCapture ? "work package" : "goal"} doesn't connect to any strategy entry. Consider linking before accepting. Find one with \`context action=search\` and link it with \`relations action=create\`.`
|
|
1935
|
-
);
|
|
1936
|
-
await recordSessionActivity({ strategyLinkWarnedForEntryId: internalId });
|
|
1937
|
-
}
|
|
1938
1313
|
if (entryWarnings.length > 0) {
|
|
1939
1314
|
lines.push("");
|
|
1940
1315
|
lines.push("## Validation Warnings");
|
|
@@ -1964,20 +1339,6 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1964
1339
|
lines.push(`${linkOutcomes.length === 0 ? "3" : "2"}. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 fill missing fields`);
|
|
1965
1340
|
}
|
|
1966
1341
|
}
|
|
1967
|
-
try {
|
|
1968
|
-
const readiness = await kernelQuery("chain.workspaceReadiness");
|
|
1969
|
-
if (readiness?.sampleTruncated) {
|
|
1970
|
-
lines.push("", `## Workspace Readiness (provisional sample)`, READINESS_PROVISIONAL_LINE);
|
|
1971
|
-
} else if (readiness && readiness.gaps && readiness.gaps.length > 0) {
|
|
1972
|
-
const topGaps = readiness.gaps.slice(0, 2);
|
|
1973
|
-
lines.push("");
|
|
1974
|
-
lines.push(`## Workspace Readiness`);
|
|
1975
|
-
for (const gap of topGaps) {
|
|
1976
|
-
lines.push(`- _${gap.label}:_ ${gap.guidance}`);
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
} catch {
|
|
1980
|
-
}
|
|
1981
1342
|
const next = [];
|
|
1982
1343
|
if (finalStatus === "committed") {
|
|
1983
1344
|
next.push({ tool: "context", description: "See its connections", parameters: { action: "find", entryId: finalEntryId } });
|
|
@@ -1986,19 +1347,11 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
1986
1347
|
next.push({ tool: "entries", description: "Accept onto Chain", parameters: { action: "commit", entryId: finalEntryId } });
|
|
1987
1348
|
}
|
|
1988
1349
|
const qualityPhrase = verdictQualityPhrase(verdictResult);
|
|
1989
|
-
const summary = finalStatus === "committed" ? `Captured and accepted ${finalEntryId} (${name}) onto ${
|
|
1990
|
-
const expectedFields =
|
|
1991
|
-
key: f.key,
|
|
1992
|
-
type: f.type,
|
|
1993
|
-
...f.required && { required: true }
|
|
1994
|
-
}));
|
|
1350
|
+
const summary = finalStatus === "committed" ? `Captured and accepted ${finalEntryId} (${name}) onto ${landedCollection}. ${qualityPhrase}` : `Captured ${finalEntryId} (${name}) as draft in ${landedCollection}. ${qualityPhrase}`;
|
|
1351
|
+
const expectedFields = replyFields.map((f) => ({ key: f.key, type: f.type, ...f.required && { required: true } }));
|
|
1995
1352
|
const captureContractForEnvelope = {
|
|
1996
|
-
requiredFields:
|
|
1997
|
-
allFields:
|
|
1998
|
-
key: f.key,
|
|
1999
|
-
required: f.required === true,
|
|
2000
|
-
type: f.type
|
|
2001
|
-
}))
|
|
1353
|
+
requiredFields: replyFields.filter((f) => f.required === true).map((f) => f.key),
|
|
1354
|
+
allFields: replyFields.map((f) => ({ key: f.key, required: f.required === true, type: f.type }))
|
|
2002
1355
|
};
|
|
2003
1356
|
const totalMs = Date.now() - timingStart;
|
|
2004
1357
|
const timing = {
|
|
@@ -2014,15 +1367,13 @@ Use \`context action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2014
1367
|
summary,
|
|
2015
1368
|
{
|
|
2016
1369
|
entryId: finalEntryId,
|
|
2017
|
-
collection:
|
|
1370
|
+
collection: landedCollection,
|
|
2018
1371
|
name,
|
|
2019
1372
|
status: finalStatus,
|
|
2020
1373
|
// WP-480 S1: the server verdict rides `qualityVerdict` (named breaking
|
|
2021
1374
|
// output-contract change, pre-launch).
|
|
2022
1375
|
qualityVerdict: verdictResult?.verdict ? { ...verdictResult.verdict, source: verdictResult.source ?? "heuristic" } : void 0,
|
|
2023
1376
|
...classifierMeta && { classifier: classifierMeta },
|
|
2024
|
-
...fallbackApplied && { fallbackApplied: true },
|
|
2025
|
-
...studioUrl && { studioUrl },
|
|
2026
1377
|
...entryWarnings.length > 0 && { warnings: entryWarnings },
|
|
2027
1378
|
...normalizationMeta && (Object.keys(normalizationMeta.remapped).length > 0 || normalizationMeta.rejected.length > 0) && {
|
|
2028
1379
|
normalization: {
|
|
@@ -4013,6 +3364,9 @@ async function handleDelete(from, to, type) {
|
|
|
4013
3364
|
// src/tools/context.ts
|
|
4014
3365
|
import { z as z18 } from "zod/v3";
|
|
4015
3366
|
|
|
3367
|
+
// src/lib/readinessNotices.ts
|
|
3368
|
+
var READINESS_PROVISIONAL_LINE = "Readiness is provisional: its sample omitted accepted entries. Counts are lower bounds, apparent gaps may already be covered.";
|
|
3369
|
+
|
|
4016
3370
|
// src/tools/documents.ts
|
|
4017
3371
|
import { z as z13 } from "zod/v3";
|
|
4018
3372
|
var DOCUMENTS_ACTIONS = ["get-last-verified-brief"];
|
|
@@ -5819,6 +5173,58 @@ function formatTimeAgo(ms) {
|
|
|
5819
5173
|
// src/tools/collections.ts
|
|
5820
5174
|
import { z as z20 } from "zod/v3";
|
|
5821
5175
|
|
|
5176
|
+
// src/lib/scopedCache.ts
|
|
5177
|
+
var ScopedCache = class {
|
|
5178
|
+
constructor(ttlMs, maxScopes = 100) {
|
|
5179
|
+
this.ttlMs = ttlMs;
|
|
5180
|
+
this.maxScopes = maxScopes;
|
|
5181
|
+
}
|
|
5182
|
+
store = /* @__PURE__ */ new Map();
|
|
5183
|
+
get(scope) {
|
|
5184
|
+
const e = this.store.get(scope);
|
|
5185
|
+
if (!e) return void 0;
|
|
5186
|
+
if (Date.now() > e.expiresAt) {
|
|
5187
|
+
this.store.delete(scope);
|
|
5188
|
+
return void 0;
|
|
5189
|
+
}
|
|
5190
|
+
return e.value;
|
|
5191
|
+
}
|
|
5192
|
+
set(scope, value) {
|
|
5193
|
+
this.store.set(scope, { value, expiresAt: Date.now() + this.ttlMs });
|
|
5194
|
+
if (this.store.size > this.maxScopes) this.evict();
|
|
5195
|
+
}
|
|
5196
|
+
delete(scope) {
|
|
5197
|
+
this.store.delete(scope);
|
|
5198
|
+
}
|
|
5199
|
+
clear() {
|
|
5200
|
+
this.store.clear();
|
|
5201
|
+
}
|
|
5202
|
+
size() {
|
|
5203
|
+
return this.store.size;
|
|
5204
|
+
}
|
|
5205
|
+
/** Drop expired entries first; if still over budget, drop the oldest-expiring. */
|
|
5206
|
+
evict() {
|
|
5207
|
+
const now = Date.now();
|
|
5208
|
+
for (const [k, e] of this.store) {
|
|
5209
|
+
if (now > e.expiresAt) this.store.delete(k);
|
|
5210
|
+
}
|
|
5211
|
+
if (this.store.size > this.maxScopes) {
|
|
5212
|
+
const sorted = [...this.store.entries()].sort((a, b) => a[1].expiresAt - b[1].expiresAt);
|
|
5213
|
+
const overBy = sorted.length - this.maxScopes;
|
|
5214
|
+
for (let i = 0; i < overBy; i++) {
|
|
5215
|
+
this.store.delete(sorted[i][0]);
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
};
|
|
5220
|
+
var COLLECTION_CACHE_TTL_MS = 6e4;
|
|
5221
|
+
|
|
5222
|
+
// src/lib/collectionCache.ts
|
|
5223
|
+
var collectionCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
|
|
5224
|
+
function invalidateCollectionCacheForScope() {
|
|
5225
|
+
collectionCache.delete(cacheScope());
|
|
5226
|
+
}
|
|
5227
|
+
|
|
5822
5228
|
// src/lib/bodyTemplate.ts
|
|
5823
5229
|
function readBodyTemplateGuidance(bodyTemplate) {
|
|
5824
5230
|
if (!bodyTemplate || bodyTemplate.length === 0) return void 0;
|
|
@@ -6285,7 +5691,6 @@ async function handleUpdate(slug, name, description, purpose, icon, navGroup, fi
|
|
|
6285
5691
|
...metaFields(meta)
|
|
6286
5692
|
});
|
|
6287
5693
|
invalidateCollectionCacheForScope();
|
|
6288
|
-
invalidateProfileCacheForScope();
|
|
6289
5694
|
const changes = [name && "name", description && "description", purpose && "purpose", icon && "icon", navGroup && "navGroup", fields && "fields", meta.axis !== void 0 && "axis", meta.stageLabelMap !== void 0 && "stageLabelMap", meta.defaultCanonicalKey !== void 0 && "defaultCanonicalKey", meta.defaultWorkflowStatus !== void 0 && "defaultWorkflowStatus", meta.validWorkflowStatuses !== void 0 && "validWorkflowStatuses", meta.classificationCheck !== void 0 && "classificationCheck", meta.classificationPriority !== void 0 && "classificationPriority", meta.qualityCriteria !== void 0 && "qualityCriteria", meta.statusList !== void 0 && "statusList"].filter(Boolean).join(", ");
|
|
6290
5695
|
return {
|
|
6291
5696
|
content: [{
|
|
@@ -7923,6 +7328,20 @@ async function _handleOrient({ mode: mode2 = "full", tier, task, startupSignals,
|
|
|
7923
7328
|
// src/tools/workflows.ts
|
|
7924
7329
|
import { z as z22 } from "zod/v3";
|
|
7925
7330
|
|
|
7331
|
+
// src/lib/resolveCollection.ts
|
|
7332
|
+
async function resolveCollection(params) {
|
|
7333
|
+
const { name, description, typeHint, allowReviewRouting } = params;
|
|
7334
|
+
const sessionId = getAgentSessionId();
|
|
7335
|
+
const result2 = await kernelCall("chain.resolveCollection", {
|
|
7336
|
+
entryName: name,
|
|
7337
|
+
entryDescription: description,
|
|
7338
|
+
...typeHint ? { typeHint } : {},
|
|
7339
|
+
...allowReviewRouting ? { allowReviewRouting } : {},
|
|
7340
|
+
...sessionId ? { agentSessionId: sessionId } : {}
|
|
7341
|
+
});
|
|
7342
|
+
return result2 ?? null;
|
|
7343
|
+
}
|
|
7344
|
+
|
|
7926
7345
|
// src/workflows/descriptor.ts
|
|
7927
7346
|
function cloneWorkflowQuestion(question) {
|
|
7928
7347
|
return {
|
|
@@ -8867,7 +8286,7 @@ function validateFinalWorkflowCheckpoint(workflow, roundId) {
|
|
|
8867
8286
|
}
|
|
8868
8287
|
return null;
|
|
8869
8288
|
}
|
|
8870
|
-
function
|
|
8289
|
+
function levenshtein(a, b) {
|
|
8871
8290
|
const m = a.length;
|
|
8872
8291
|
const n = b.length;
|
|
8873
8292
|
const dp = Array.from(
|
|
@@ -8888,9 +8307,9 @@ function findClosestRound(input, rounds) {
|
|
|
8888
8307
|
);
|
|
8889
8308
|
if (substringMatch) return substringMatch;
|
|
8890
8309
|
let best = rounds[0];
|
|
8891
|
-
let bestDist =
|
|
8310
|
+
let bestDist = levenshtein(input, best.id);
|
|
8892
8311
|
for (const r of rounds.slice(1)) {
|
|
8893
|
-
const dist =
|
|
8312
|
+
const dist = levenshtein(input, r.id);
|
|
8894
8313
|
if (dist < bestDist) {
|
|
8895
8314
|
bestDist = dist;
|
|
8896
8315
|
best = r;
|
|
@@ -12836,4 +12255,4 @@ export {
|
|
|
12836
12255
|
createProductBrainServer,
|
|
12837
12256
|
initFeatureFlags
|
|
12838
12257
|
};
|
|
12839
|
-
//# sourceMappingURL=chunk-
|
|
12258
|
+
//# sourceMappingURL=chunk-RMVLWNVO.js.map
|