@productbrain/mcp 0.0.1-beta.8849 → 0.0.1-beta.8872
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-QV6OOBAC.js → chunk-OEONSR7Q.js} +64 -303
- package/dist/chunk-OEONSR7Q.js.map +1 -0
- package/dist/{chunk-NT3NE6V5.js → chunk-VZNA24SX.js} +1 -3
- package/dist/chunk-VZNA24SX.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-VUX6TU6Q.js → setup-X5BRRUTV.js} +4 -4
- package/dist/{setup-VUX6TU6Q.js.map → setup-X5BRRUTV.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-NT3NE6V5.js.map +0 -1
- package/dist/chunk-QV6OOBAC.js.map +0 -1
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
trackSessionCaptureRate,
|
|
43
43
|
trackZeroCaptureAuditFired,
|
|
44
44
|
validateCallerSession
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-VZNA24SX.js";
|
|
46
46
|
|
|
47
47
|
// src/server.ts
|
|
48
48
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -357,7 +357,6 @@ var TOOL_NAME_MIGRATIONS = /* @__PURE__ */ new Map([
|
|
|
357
357
|
["relate-entries", 'relations action="create"'],
|
|
358
358
|
["batch-relate", 'relations action="batch-create"'],
|
|
359
359
|
["find-related", 'relations action="find"'],
|
|
360
|
-
["suggest-links", 'relations action="suggest"'],
|
|
361
360
|
["gather-context", 'context action="gather"'],
|
|
362
361
|
["get-build-context", 'context action="build"'],
|
|
363
362
|
["list-collections", 'collections action="list"'],
|
|
@@ -405,7 +404,7 @@ var TOOL_NAME_MIGRATIONS = /* @__PURE__ */ new Map([
|
|
|
405
404
|
["move-entry", 'entries action="move"'],
|
|
406
405
|
["session-wrapup", 'session action="wrapup-review|wrapup-commit" (review \u2192 wrapup-review, commit-all \u2192 wrapup-commit)'],
|
|
407
406
|
["facilitate", 'session action="resume|commit-constellation" (same action names)'],
|
|
408
|
-
["graph", 'relations action="find
|
|
407
|
+
["graph", 'relations action="find" (same action name)'],
|
|
409
408
|
["documents", 'context action="last-verified-brief"'],
|
|
410
409
|
["labels", 'collections action="label-*" (list/create/update/delete/apply/remove \u2192 label-<same>)'],
|
|
411
410
|
["health", 'workspace action="check|whoami|status|audit|self-test" (same action names)'],
|
|
@@ -2302,7 +2301,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2302
2301
|
if (isBetOrGoal && !hasStrategyLink) {
|
|
2303
2302
|
lines.push("");
|
|
2304
2303
|
lines.push(
|
|
2305
|
-
`**Strategy link:** This ${isWorkPackageOrBetCapture ? "work package" : "goal"} doesn't connect to any strategy entry. Consider linking before accepting.
|
|
2304
|
+
`**Strategy link:** This ${isWorkPackageOrBetCapture ? "work package" : "goal"} doesn't connect to any strategy entry. Consider linking before accepting. Find one with \`entries action=search\` and link it with \`relations action=create\`.`
|
|
2306
2305
|
);
|
|
2307
2306
|
await recordSessionActivity({ strategyLinkWarnedForEntryId: internalId });
|
|
2308
2307
|
}
|
|
@@ -2334,13 +2333,13 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2334
2333
|
lines.push("## Next Steps");
|
|
2335
2334
|
const eid = finalEntryId || "(check entry ID)";
|
|
2336
2335
|
if (finalStatus === "committed") {
|
|
2337
|
-
lines.push(`1. **Connect it:** \`relations action=
|
|
2336
|
+
lines.push(`1. **Connect it:** find related entries with \`entries action=search\`, then \`relations action=create from="${eid}" to="<ID>" type="<verb>"\``);
|
|
2338
2337
|
if (hasQualityGaps) {
|
|
2339
2338
|
lines.push(`2. **Improve quality:** \`entries action=update entryId="${eid}"\` \u2014 fill missing fields`);
|
|
2340
2339
|
}
|
|
2341
2340
|
} else {
|
|
2342
2341
|
if (userLinkResults.length === 0) {
|
|
2343
|
-
lines.push(`1. **Connect it:** \`relations action=
|
|
2342
|
+
lines.push(`1. **Connect it:** find related entries with \`entries action=search\`, then \`relations action=create from="${eid}" to="<ID>" type="<verb>"\``);
|
|
2344
2343
|
}
|
|
2345
2344
|
lines.push(`${userLinkResults.length === 0 ? "2" : "1"}. **Review the possibility:** Keep this visible draft while it is an alternative. Accept only when commitment is intended and your role is authorized. Types reserved for human acceptance require human ratification. An audit advisory is not a reason to accept it.`);
|
|
2346
2345
|
if (hasQualityGaps) {
|
|
@@ -2363,11 +2362,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`entries action=up
|
|
|
2363
2362
|
}
|
|
2364
2363
|
const next = [];
|
|
2365
2364
|
if (finalStatus === "committed") {
|
|
2366
|
-
next.push({ tool: "relations", description: "
|
|
2365
|
+
next.push({ tool: "relations", description: "See its connections", parameters: { action: "find", entryId: finalEntryId } });
|
|
2367
2366
|
} else {
|
|
2368
|
-
if (userLinkResults.length === 0) {
|
|
2369
|
-
next.push({ tool: "relations", description: "Discover links", parameters: { action: "suggest", entryId: finalEntryId } });
|
|
2370
|
-
}
|
|
2371
2367
|
next.push({ tool: "entries", description: "Review the draft alternative", parameters: { action: "get", entryId: finalEntryId } });
|
|
2372
2368
|
next.push({ tool: "entries", description: "Accept onto Chain", parameters: { action: "commit", entryId: finalEntryId } });
|
|
2373
2369
|
}
|
|
@@ -2925,7 +2921,7 @@ No DB writes \u2014 call without \`preview:true\` to accept for real.` }],
|
|
|
2925
2921
|
} catch {
|
|
2926
2922
|
}
|
|
2927
2923
|
const next = [
|
|
2928
|
-
{ tool: "relations", description: "
|
|
2924
|
+
{ tool: "relations", description: "See its connections", parameters: { action: "find", entryId } },
|
|
2929
2925
|
{ tool: "entries", description: "View accepted entry", parameters: { action: "get", entryId } }
|
|
2930
2926
|
];
|
|
2931
2927
|
const summary = `Accepted ${entryId} (${entry.name}) onto the Chain.`;
|
|
@@ -3832,7 +3828,7 @@ async function handleGet(entryId) {
|
|
|
3832
3828
|
`Found ${entryId}: ${String(e.name)} [${String(e.collectionSlug ?? "")}].`,
|
|
3833
3829
|
entryData,
|
|
3834
3830
|
[
|
|
3835
|
-
{ tool: "relations", description: "
|
|
3831
|
+
{ tool: "relations", description: "See its connections", parameters: { action: "find", entryId } },
|
|
3836
3832
|
{ tool: "context", description: "Gather related context", parameters: { action: "gather", entryId } }
|
|
3837
3833
|
]
|
|
3838
3834
|
)
|
|
@@ -4080,15 +4076,13 @@ import { z as z13 } from "zod/v3";
|
|
|
4080
4076
|
|
|
4081
4077
|
// src/tools/graph.ts
|
|
4082
4078
|
import { z as z12 } from "zod/v3";
|
|
4083
|
-
var GRAPH_ACTIONS = ["find"
|
|
4079
|
+
var GRAPH_ACTIONS = ["find"];
|
|
4084
4080
|
var graphSchema = z12.object({
|
|
4085
4081
|
action: z12.enum(GRAPH_ACTIONS).describe(
|
|
4086
|
-
"'find': traverse relations from an entry (graph walk).
|
|
4082
|
+
"'find': traverse relations from an entry (graph walk)."
|
|
4087
4083
|
),
|
|
4088
4084
|
entryId: z12.string().max(200).describe("Entry ID, e.g. '<PREFIX>-<n>'"),
|
|
4089
|
-
direction: z12.enum(["incoming", "outgoing", "both"]).default("both").optional().describe("For find: 'incoming' = what references this, 'outgoing' = what this references")
|
|
4090
|
-
limit: z12.number().min(1).max(20).default(10).optional().describe("For suggest: max suggestions to return"),
|
|
4091
|
-
depth: z12.number().min(1).max(3).default(2).optional().describe("For suggest: graph traversal depth")
|
|
4085
|
+
direction: z12.enum(["incoming", "outgoing", "both"]).default("both").optional().describe("For find: 'incoming' = what references this, 'outgoing' = what this references")
|
|
4092
4086
|
});
|
|
4093
4087
|
var graphFindOutputSchema = z12.object({
|
|
4094
4088
|
entryId: z12.string(),
|
|
@@ -4100,18 +4094,6 @@ var graphFindOutputSchema = z12.object({
|
|
|
4100
4094
|
})),
|
|
4101
4095
|
total: z12.number()
|
|
4102
4096
|
});
|
|
4103
|
-
var graphSuggestOutputSchema = z12.object({
|
|
4104
|
-
entryId: z12.string(),
|
|
4105
|
-
suggestions: z12.array(z12.object({
|
|
4106
|
-
targetEntryId: z12.string().optional(),
|
|
4107
|
-
targetName: z12.string(),
|
|
4108
|
-
relationType: z12.string(),
|
|
4109
|
-
direction: z12.string(),
|
|
4110
|
-
confidence: z12.number(),
|
|
4111
|
-
reason: z12.string()
|
|
4112
|
-
})),
|
|
4113
|
-
total: z12.number()
|
|
4114
|
-
});
|
|
4115
4097
|
async function handleFind(entryId, direction) {
|
|
4116
4098
|
const relations = await kernelQuery("chain.listEntryRelations", { entryId });
|
|
4117
4099
|
if (relations.length === 0) {
|
|
@@ -4121,8 +4103,7 @@ async function handleFind(entryId, direction) {
|
|
|
4121
4103
|
`No relations found for '${entryId}'.`,
|
|
4122
4104
|
{ entryId, relations: [], nodes: [], edges: [], total: 0 },
|
|
4123
4105
|
[
|
|
4124
|
-
{ tool: "relations", description: "Create a relation", parameters: { action: "create", from: entryId } }
|
|
4125
|
-
{ tool: "relations", description: "Get suggestions", parameters: { action: "suggest", entryId } }
|
|
4106
|
+
{ tool: "relations", description: "Create a relation", parameters: { action: "create", from: entryId } }
|
|
4126
4107
|
]
|
|
4127
4108
|
)
|
|
4128
4109
|
};
|
|
@@ -4210,97 +4191,9 @@ async function handleFind(entryId, direction) {
|
|
|
4210
4191
|
)
|
|
4211
4192
|
};
|
|
4212
4193
|
}
|
|
4213
|
-
async function handleSuggest(entryId, limit, depth) {
|
|
4214
|
-
const result2 = await kernelQuery("chain.graphSuggestLinks", {
|
|
4215
|
-
entryId,
|
|
4216
|
-
maxHops: depth,
|
|
4217
|
-
limit
|
|
4218
|
-
});
|
|
4219
|
-
if (!result2?.suggestions || result2.suggestions.length === 0) {
|
|
4220
|
-
return failureResult(
|
|
4221
|
-
`No suggestions found for '${entryId}' \u2014 it may already be well-connected, or no similar entries exist.`,
|
|
4222
|
-
"NOT_FOUND",
|
|
4223
|
-
`No suggestions found for '${entryId}'.`,
|
|
4224
|
-
"Entry may already be well-connected.",
|
|
4225
|
-
[{ tool: "relations", description: "Check existing relations", parameters: { action: "find", entryId } }]
|
|
4226
|
-
);
|
|
4227
|
-
}
|
|
4228
|
-
const resolved = result2.resolvedEntry;
|
|
4229
|
-
const resolvedLabel = resolved ? `\`${resolved.entryId ?? resolved.name}\` (${resolved.name})` : `\`${entryId}\``;
|
|
4230
|
-
const suggestions = result2.suggestions;
|
|
4231
|
-
const graphCount = suggestions.filter((s) => s.graphDistance > 0).length;
|
|
4232
|
-
const textCount = suggestions.filter((s) => s.graphDistance === -1).length;
|
|
4233
|
-
const sourceId = resolved?.entryId ?? entryId;
|
|
4234
|
-
const lines = [
|
|
4235
|
-
`## Suggest Result`,
|
|
4236
|
-
"",
|
|
4237
|
-
`# Link Suggestions for ${resolvedLabel}`,
|
|
4238
|
-
`_${suggestions.length} potential connections (${graphCount} via graph, ${textCount} via text similarity)._`,
|
|
4239
|
-
""
|
|
4240
|
-
];
|
|
4241
|
-
const top3 = suggestions.slice(0, 3);
|
|
4242
|
-
lines.push("## Quick Wins (top 3)");
|
|
4243
|
-
const batchArgs = [];
|
|
4244
|
-
for (const s of top3) {
|
|
4245
|
-
const tid = s.entryId ?? "(no ID)";
|
|
4246
|
-
const relType = s.recommendedRelationType || "related_to";
|
|
4247
|
-
lines.push(`- **${tid}**: ${s.name} [${s.collectionSlug}] \u2014 ${s.score}/100 \u2192 \`${relType}\``);
|
|
4248
|
-
if (s.entryId) batchArgs.push(`{from:"${sourceId}",to:"${tid}",type:"${relType}"}`);
|
|
4249
|
-
}
|
|
4250
|
-
lines.push("");
|
|
4251
|
-
if (batchArgs.length > 0) {
|
|
4252
|
-
lines.push(`**Link all 3:** \`relations action=batch-create relations=[${batchArgs.join(",")}]\``);
|
|
4253
|
-
lines.push("");
|
|
4254
|
-
}
|
|
4255
|
-
if (suggestions.length > 3) {
|
|
4256
|
-
lines.push("## All Suggestions");
|
|
4257
|
-
for (let i = 0; i < suggestions.length; i++) {
|
|
4258
|
-
const s = suggestions[i];
|
|
4259
|
-
const scoreBar = "\u2588".repeat(Math.round(s.score / 10)) + "\u2591".repeat(10 - Math.round(s.score / 10));
|
|
4260
|
-
const hopLabel = s.graphDistance > 0 ? `${s.graphDistance}-hop` : "text";
|
|
4261
|
-
const recType = s.recommendedRelationType !== "related_to" ? ` \u2192 \`${s.recommendedRelationType}\`` : "";
|
|
4262
|
-
lines.push(`${i + 1}. **${s.entryId ?? "(no ID)"}**: ${s.name} [${s.collectionSlug}] (${hopLabel})`);
|
|
4263
|
-
lines.push(` ${scoreBar} ${s.score}/100${recType}`);
|
|
4264
|
-
if (s.preview) lines.push(` ${s.preview}`);
|
|
4265
|
-
lines.push(` _${s.reasoning}_`);
|
|
4266
|
-
}
|
|
4267
|
-
}
|
|
4268
|
-
lines.push("");
|
|
4269
|
-
lines.push(`**To link one:** \`relations action=create from="${sourceId}" to="{target_id}" type="{type}"\``);
|
|
4270
|
-
lines.push(`**To dismiss:** \`relations action=dismiss from="${sourceId}" to="{target_id}" type="{type}"\``);
|
|
4271
|
-
const structuredSuggestions = suggestions.map((s) => ({
|
|
4272
|
-
targetEntryId: s.entryId,
|
|
4273
|
-
targetName: s.name,
|
|
4274
|
-
relationType: s.recommendedRelationType || "related_to",
|
|
4275
|
-
direction: "outgoing",
|
|
4276
|
-
confidence: s.score,
|
|
4277
|
-
reason: s.reasoning
|
|
4278
|
-
}));
|
|
4279
|
-
const nodes = [
|
|
4280
|
-
{ id: sourceId, name: resolved?.name ?? entryId, collectionName: "" },
|
|
4281
|
-
...suggestions.map((s) => ({
|
|
4282
|
-
id: s.entryId ?? String(Math.random()),
|
|
4283
|
-
name: s.name,
|
|
4284
|
-
collectionName: s.collectionSlug
|
|
4285
|
-
}))
|
|
4286
|
-
];
|
|
4287
|
-
const edges = suggestions.filter((s) => s.entryId).map((s) => ({
|
|
4288
|
-
source: sourceId,
|
|
4289
|
-
target: s.entryId,
|
|
4290
|
-
type: s.recommendedRelationType || "suggested"
|
|
4291
|
-
}));
|
|
4292
|
-
return {
|
|
4293
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
4294
|
-
structuredContent: success(
|
|
4295
|
-
`Found ${suggestions.length} link suggestions for ${resolvedLabel}.`,
|
|
4296
|
-
{ entryId: sourceId, suggestions: structuredSuggestions, nodes, edges, total: suggestions.length },
|
|
4297
|
-
[{ tool: "relations", description: "Create links", parameters: { action: "batch-create" } }]
|
|
4298
|
-
)
|
|
4299
|
-
};
|
|
4300
|
-
}
|
|
4301
4194
|
|
|
4302
4195
|
// src/tools/relations.ts
|
|
4303
|
-
var RELATIONS_ACTIONS = ["create", "batch-create", "
|
|
4196
|
+
var RELATIONS_ACTIONS = ["create", "batch-create", "delete", "find"];
|
|
4304
4197
|
var relationItemSchema = z13.object({
|
|
4305
4198
|
from: z13.string().max(200),
|
|
4306
4199
|
to: z13.string().max(200),
|
|
@@ -4308,19 +4201,16 @@ var relationItemSchema = z13.object({
|
|
|
4308
4201
|
});
|
|
4309
4202
|
var relationsSchema = z13.object({
|
|
4310
4203
|
action: z13.enum(RELATIONS_ACTIONS).describe(
|
|
4311
|
-
"'create': link two entries. 'batch-create': create multiple relations (validates every item before writing any). '
|
|
4204
|
+
"'create': link two entries. 'batch-create': create multiple relations (validates every item before writing any). 'delete': remove a relation. 'find': traverse relations from an entry (graph walk, absorbs graph action=find)."
|
|
4312
4205
|
),
|
|
4313
|
-
from: z13.string().max(200).optional().describe("For 'create'/'
|
|
4314
|
-
to: z13.string().max(200).optional().describe("For 'create'/'
|
|
4315
|
-
type: z13.string().max(200).optional().describe("For 'create'/'
|
|
4316
|
-
score: z13.number().optional().describe("For 'dismiss': suggestion score from action=suggest."),
|
|
4206
|
+
from: z13.string().max(200).optional().describe("For 'create'/'delete': source entry ID."),
|
|
4207
|
+
to: z13.string().max(200).optional().describe("For 'create'/'delete': target entry ID."),
|
|
4208
|
+
type: z13.string().max(200).optional().describe("For 'create'/'delete': relation type."),
|
|
4317
4209
|
relations: z13.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX).optional().describe("For 'batch-create': array of {from, to, type}."),
|
|
4318
4210
|
// WP-316 S3: Preview gate — dry-run mode for action=create.
|
|
4319
4211
|
preview: z13.boolean().optional().describe("For 'create': if true, validates the relation without writing. Returns what would happen. Default false."),
|
|
4320
|
-
entryId: z13.string().max(200).optional().describe("For 'find'
|
|
4321
|
-
direction: z13.enum(["incoming", "outgoing", "both"]).optional().describe("For 'find': 'incoming' = what references this, 'outgoing' = what this references. Default 'both'.")
|
|
4322
|
-
limit: z13.number().min(1).max(20).optional().describe("For 'suggest': max suggestions to return. Default 10."),
|
|
4323
|
-
depth: z13.number().min(1).max(3).optional().describe("For 'suggest': graph traversal depth. Default 2.")
|
|
4212
|
+
entryId: z13.string().max(200).optional().describe("For 'find': entry ID, e.g. '<PREFIX>-<n>'."),
|
|
4213
|
+
direction: z13.enum(["incoming", "outgoing", "both"]).optional().describe("For 'find': 'incoming' = what references this, 'outgoing' = what this references. Default 'both'.")
|
|
4324
4214
|
});
|
|
4325
4215
|
var relationsCreateVariant = z13.object({
|
|
4326
4216
|
action: z13.literal("create"),
|
|
@@ -4333,13 +4223,6 @@ var relationsBatchCreateVariant = z13.object({
|
|
|
4333
4223
|
action: z13.literal("batch-create"),
|
|
4334
4224
|
relations: z13.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX)
|
|
4335
4225
|
});
|
|
4336
|
-
var relationsDismissVariant = z13.object({
|
|
4337
|
-
action: z13.literal("dismiss"),
|
|
4338
|
-
from: z13.string().max(200),
|
|
4339
|
-
to: z13.string().max(200),
|
|
4340
|
-
type: z13.string().max(200).optional(),
|
|
4341
|
-
score: z13.number().optional()
|
|
4342
|
-
});
|
|
4343
4226
|
var relationsDeleteVariant = z13.object({
|
|
4344
4227
|
action: z13.literal("delete"),
|
|
4345
4228
|
from: z13.string().max(200),
|
|
@@ -4351,44 +4234,32 @@ var relationsFindVariant = z13.object({
|
|
|
4351
4234
|
entryId: z13.string().max(200),
|
|
4352
4235
|
direction: z13.enum(["incoming", "outgoing", "both"]).optional().default("both")
|
|
4353
4236
|
});
|
|
4354
|
-
var relationsSuggestVariant = z13.object({
|
|
4355
|
-
action: z13.literal("suggest"),
|
|
4356
|
-
entryId: z13.string().max(200),
|
|
4357
|
-
limit: z13.number().min(1).max(20).optional().default(10),
|
|
4358
|
-
depth: z13.number().min(1).max(3).optional().default(2)
|
|
4359
|
-
});
|
|
4360
4237
|
var relationsActionUnion = z13.discriminatedUnion("action", [
|
|
4361
4238
|
relationsCreateVariant,
|
|
4362
4239
|
relationsBatchCreateVariant,
|
|
4363
|
-
relationsDismissVariant,
|
|
4364
4240
|
relationsDeleteVariant,
|
|
4365
|
-
relationsFindVariant
|
|
4366
|
-
relationsSuggestVariant
|
|
4241
|
+
relationsFindVariant
|
|
4367
4242
|
]);
|
|
4368
4243
|
var RELATIONS_ACTION_SPECS = {
|
|
4369
4244
|
create: { params: ["from", "to", "type", "preview"], description: "from, to, and type are required." },
|
|
4370
4245
|
"batch-create": { params: ["relations"], description: "relations (1-20 items) is required." },
|
|
4371
|
-
dismiss: { params: ["from", "to", "type", "score"], description: "from and to are required." },
|
|
4372
4246
|
delete: { params: ["from", "to", "type"], description: "from, to, and type are required." },
|
|
4373
|
-
find: { params: ["entryId", "direction"], description: "entryId is required." }
|
|
4374
|
-
suggest: { params: ["entryId", "limit", "depth"], description: "entryId is required." }
|
|
4247
|
+
find: { params: ["entryId", "direction"], description: "entryId is required." }
|
|
4375
4248
|
};
|
|
4376
4249
|
function registerRelationsTools(server) {
|
|
4377
4250
|
const relationsHandlers = {
|
|
4378
4251
|
create: (data) => handleCreate(data.from, data.to, data.type, data.preview),
|
|
4379
4252
|
"batch-create": (data) => handleBatchCreate(data.relations),
|
|
4380
|
-
dismiss: (data) => handleDismiss(data.from, data.to, data.type ?? "related_to", data.score),
|
|
4381
4253
|
delete: (data) => handleDelete(data.from, data.to, data.type),
|
|
4382
|
-
find: (data) => handleFind(data.entryId, data.direction ?? "both")
|
|
4383
|
-
suggest: (data) => handleSuggest(data.entryId, data.limit ?? 10, data.depth ?? 2)
|
|
4254
|
+
find: (data) => handleFind(data.entryId, data.direction ?? "both")
|
|
4384
4255
|
};
|
|
4385
4256
|
server.registerTool(
|
|
4386
4257
|
"relations",
|
|
4387
4258
|
{
|
|
4388
4259
|
title: "Relations",
|
|
4389
|
-
description: "Create, manage, and
|
|
4260
|
+
description: "Create, manage, and read relations between entries. A relation is written only when you ask for it, with exactly the verb you send.\n\n- **create**: Link two entries with a typed relation.\n- **batch-create**: Create multiple relations in one call (validates every item before writing any).\n- **delete**: Remove a relation by from, to, and type.\n- **find**: Traverse relations from a specific entry \u2014 shows incoming/outgoing connections. NOT full-text search (use entries action=search).",
|
|
4390
4261
|
inputSchema: relationsSchema,
|
|
4391
|
-
// Mixed read/write noun (§3 R3): find
|
|
4262
|
+
// Mixed read/write noun (§3 R3): find is read-only, create/batch-create/delete write.
|
|
4392
4263
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
4393
4264
|
},
|
|
4394
4265
|
thinWrapper(async (args) => {
|
|
@@ -4572,28 +4443,6 @@ async function handleBatchCreate(relations) {
|
|
|
4572
4443
|
)
|
|
4573
4444
|
};
|
|
4574
4445
|
}
|
|
4575
|
-
async function handleDismiss(from, to, type, score) {
|
|
4576
|
-
requireWriteAccess();
|
|
4577
|
-
const fromEntry = await kernelQuery("chain.getEntry", { entryId: from });
|
|
4578
|
-
if (!fromEntry) {
|
|
4579
|
-
return notFoundResult(from);
|
|
4580
|
-
}
|
|
4581
|
-
const toEntry = await kernelQuery("chain.getEntry", { entryId: to });
|
|
4582
|
-
if (!toEntry) {
|
|
4583
|
-
return notFoundResult(to);
|
|
4584
|
-
}
|
|
4585
|
-
await kernelMutation("chain.dismissSuggestion", {
|
|
4586
|
-
fromEntryId: fromEntry._id,
|
|
4587
|
-
suggestedEntryId: toEntry._id,
|
|
4588
|
-
recommendedType: type,
|
|
4589
|
-
score: score ?? 0
|
|
4590
|
-
});
|
|
4591
|
-
return successResult(
|
|
4592
|
-
`Dismissed suggestion: ${from} \u2192 ${to} (${type}). Feedback recorded for scoring improvements.`,
|
|
4593
|
-
`Dismissed suggestion ${from} \u2192 ${to} (${type}). Feedback recorded.`,
|
|
4594
|
-
{ from, to, type }
|
|
4595
|
-
);
|
|
4596
|
-
}
|
|
4597
4446
|
async function handleDelete(from, to, type) {
|
|
4598
4447
|
requireWriteAccess();
|
|
4599
4448
|
await kernelMutation("chain.removeEntryRelation", {
|
|
@@ -4877,12 +4726,12 @@ async function handleGather(server, entryId, task, mode, maxHops, maxResults, st
|
|
|
4877
4726
|
|
|
4878
4727
|
_No relations found._ This entry is not yet connected to the knowledge graph.
|
|
4879
4728
|
|
|
4880
|
-
Use \`relations action=
|
|
4729
|
+
Use \`relations action=create\` to link it to the entries it relates to.`
|
|
4881
4730
|
}],
|
|
4882
4731
|
structuredContent: success(
|
|
4883
4732
|
`No relations found for ${root2.entryId}. Entry not yet connected.`,
|
|
4884
4733
|
{ root: root2, context: [], totalFound: 0 },
|
|
4885
|
-
[{ tool: "relations", description: "
|
|
4734
|
+
[{ tool: "relations", description: "Link it", parameters: { action: "create", from: root2.entryId } }]
|
|
4886
4735
|
)
|
|
4887
4736
|
};
|
|
4888
4737
|
}
|
|
@@ -5109,12 +4958,12 @@ _This helps future sessions start with better constraints and context._`
|
|
|
5109
4958
|
|
|
5110
4959
|
_No relations found._ This entry is not yet connected to the knowledge graph.
|
|
5111
4960
|
|
|
5112
|
-
Use \`relations action=
|
|
4961
|
+
Use \`relations action=create\` to link it.`
|
|
5113
4962
|
}],
|
|
5114
4963
|
structuredContent: success(
|
|
5115
4964
|
`No relations found for ${root.entryId}. Entry not yet connected.`,
|
|
5116
4965
|
{ root, related: [], totalRelations: 0 },
|
|
5117
|
-
[{ tool: "relations", description: "
|
|
4966
|
+
[{ tool: "relations", description: "Link it", parameters: { action: "create", from: root.entryId } }]
|
|
5118
4967
|
)
|
|
5119
4968
|
};
|
|
5120
4969
|
}
|
|
@@ -5297,14 +5146,14 @@ async function handleNeighborhood(entryId) {
|
|
|
5297
5146
|
const isEmpty = result2.blockingChain.length === 0 && result2.dependencyChain.length === 0 && !result2.parentContext && result2.relatedTensions.length === 0;
|
|
5298
5147
|
if (isEmpty) {
|
|
5299
5148
|
lines.push(`_No neighborhood data found. Entry may not exist or has no graph connections._`);
|
|
5300
|
-
lines.push(`Use \`relations action=
|
|
5149
|
+
lines.push(`Use \`relations action=create\` to link it to the entries it relates to.`);
|
|
5301
5150
|
}
|
|
5302
5151
|
return {
|
|
5303
5152
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
5304
5153
|
structuredContent: success(
|
|
5305
5154
|
`Neighborhood for ${entryId}: ${result2.blockingChain.length} blocking, ${result2.dependencyChain.length} dependencies, ${result2.relatedTensions.length} tensions.`,
|
|
5306
5155
|
result2,
|
|
5307
|
-
isEmpty ? [{ tool: "relations", description: "
|
|
5156
|
+
isEmpty ? [{ tool: "relations", description: "Link it", parameters: { action: "create", from: entryId } }] : void 0
|
|
5308
5157
|
)
|
|
5309
5158
|
};
|
|
5310
5159
|
}
|
|
@@ -7274,11 +7123,11 @@ function buildOrientBriefingLines(view, opts) {
|
|
|
7274
7123
|
lines.splice(pull < 0 ? lines.length : pull, 0, RULE_LISTS_EVERYONE_ONLY_LABEL, "(none currently served)", "");
|
|
7275
7124
|
return lines;
|
|
7276
7125
|
}
|
|
7277
|
-
async function fetchAndRenderOpenerDirection(
|
|
7126
|
+
async function fetchAndRenderOpenerDirection(kernelQuery3, errors, session) {
|
|
7278
7127
|
let view = null;
|
|
7279
7128
|
let fetchFailed = false;
|
|
7280
7129
|
try {
|
|
7281
|
-
view = await
|
|
7130
|
+
view = await kernelQuery3("chain.getOrientView", {
|
|
7282
7131
|
tier: "standard",
|
|
7283
7132
|
invocationPath: session?.invocationPath ?? "session-start",
|
|
7284
7133
|
// Stamps this call onto the current session/conversation/task (same as every other
|
|
@@ -7293,9 +7142,9 @@ async function fetchAndRenderOpenerDirection(kernelQuery2, errors, session) {
|
|
|
7293
7142
|
}
|
|
7294
7143
|
return { lines: [...buildOrientBriefingLines(view), ...buildRitualsSurfaceLines(view, { fetchFailed: fetchFailed || view?.fetchFailed === true })], view };
|
|
7295
7144
|
}
|
|
7296
|
-
async function buildAlreadyOrientedResult(
|
|
7145
|
+
async function buildAlreadyOrientedResult(kernelQuery3, agentSessionId, taskGroundingWarningLines2) {
|
|
7297
7146
|
const errors = [];
|
|
7298
|
-
const { lines, view } = await fetchAndRenderOpenerDirection(
|
|
7147
|
+
const { lines, view } = await fetchAndRenderOpenerDirection(kernelQuery3, errors, { agentSessionId, invocationPath: "manual-orient" });
|
|
7299
7148
|
return {
|
|
7300
7149
|
content: [{
|
|
7301
7150
|
type: "text",
|
|
@@ -8964,7 +8813,7 @@ Use \`workflows action=checkpoint\` after each round to persist progress to the
|
|
|
8964
8813
|
label: "Capture & Accept",
|
|
8965
8814
|
type: "close",
|
|
8966
8815
|
instruction: "Capture the pitch to the Chain. Accept draft entries. Connect the constellation.",
|
|
8967
|
-
facilitatorGuidance: "List all draft entries created during shaping (elements, risks, decisions, glossary terms). Ask the user to confirm: 'Ready to accept these onto the Chain?' Call `session action=commit-constellation` to accept the work package and all linked entries \u2014 sequentially, best-effort per entry; check failedIds and conflictIds. Then
|
|
8816
|
+
facilitatorGuidance: "List all draft entries created during shaping (elements, risks, decisions, glossary terms). Ask the user to confirm: 'Ready to accept these onto the Chain?' Call `session action=commit-constellation` to accept the work package and all linked entries \u2014 sequentially, best-effort per entry; check failedIds and conflictIds. Then link the work package to the entries it relates to via `relations action=batch-create`. Thank the user. State: what was shaped, how many constellation entries captured, what the implementer inherits.",
|
|
8968
8817
|
outputSchema: {
|
|
8969
8818
|
field: "capture",
|
|
8970
8819
|
description: "Final capture summary \u2014 entries accepted, relations created",
|
|
@@ -9156,7 +9005,7 @@ This block is the final deliverable of the review. Everything else is supporting
|
|
|
9156
9005
|
label: "Capture & Close",
|
|
9157
9006
|
type: "close",
|
|
9158
9007
|
instruction: "Finalize the review as an insight draft. Connect new entries from learning extraction. Use session action=wrapup-review. Thank the participant.",
|
|
9159
|
-
facilitatorGuidance: "Summarize the review for finalization. Complete the terminal round through `workflows action=checkpoint` with `isFinal=true` so the workflow substrate creates the draft insight record. Include: BET/feature IDs, verdict, key findings, fixes applied, and the structured verdict block from Round 05. The insight description MUST include the 5-item verdict block (verdict Y/N, what shipped, why it matters, user benefit, how to validate). If learnings were captured in Round 04:
|
|
9008
|
+
facilitatorGuidance: "Summarize the review for finalization. Complete the terminal round through `workflows action=checkpoint` with `isFinal=true` so the workflow substrate creates the draft insight record. Include: BET/feature IDs, verdict, key findings, fixes applied, and the structured verdict block from Round 05. The insight description MUST include the 5-item verdict block (verdict Y/N, what shipped, why it matters, user benefit, how to validate). If learnings were captured in Round 04: link each new entry to what it relates to with relations action=batch-create. Run session action=wrapup-review before closing. Thank the participant. State what was fixed, what was captured, what was deferred.",
|
|
9160
9009
|
outputSchema: {
|
|
9161
9010
|
field: "capture",
|
|
9162
9011
|
description: "Insight entry created, WP IDs referenced, learnings connected",
|
|
@@ -9221,7 +9070,6 @@ var PROCESS_CHANGE_WORKFLOW_DESCRIPTOR = {
|
|
|
9221
9070
|
- \`orient\` \u2014 load workspace context (governance, active work packages, stale entries)
|
|
9222
9071
|
- \`entries action=search\` \u2014 find related entries
|
|
9223
9072
|
- \`capture\` \u2014 manual fallback only if workflow finalization cannot safely route the entry
|
|
9224
|
-
- \`relations action=suggest\` \u2014 discover connections
|
|
9225
9073
|
- \`relations action=create\` or \`relations action=batch-create\` \u2014 link to related entries
|
|
9226
9074
|
- \`entries action=commit\` \u2014 promote to SSOT (only on user confirmation)
|
|
9227
9075
|
- \`workflows action=checkpoint\` \u2014 save progress if needed
|
|
@@ -9281,7 +9129,7 @@ var PROCESS_CHANGE_WORKFLOW_DESCRIPTOR = {
|
|
|
9281
9129
|
label: "Capture & Close",
|
|
9282
9130
|
type: "close",
|
|
9283
9131
|
instruction: "Capture to the Chain. Link to related entries. Confirm with the user.",
|
|
9284
|
-
facilitatorGuidance: "Finalize through workflows action=checkpoint with isFinal=true, passing a summaryName when you have a clean title. The workflow substrate will create the primary record as a draft and attach it to the durable run. After finalization,
|
|
9132
|
+
facilitatorGuidance: "Finalize through workflows action=checkpoint with isFinal=true, passing a summaryName when you have a clean title. The workflow substrate will create the primary record as a draft and attach it to the durable run. After finalization, link the entry to the 2-3 entries it relates to with relations action=create. Show the user what was captured: entry ID, collection it landed in, relations created. Ask if they want to accept now or leave as draft. If they confirm, use entries action=commit. If there are stale entries to reconfirm, mention them. Thank the user briefly and exit \u2014 don't linger.",
|
|
9285
9133
|
outputSchema: {
|
|
9286
9134
|
field: "capture",
|
|
9287
9135
|
description: "Entry captured, relations created, accept status",
|
|
@@ -9731,8 +9579,8 @@ async function handleGetRun(runId, workflowId) {
|
|
|
9731
9579
|
run.summary ? [
|
|
9732
9580
|
{
|
|
9733
9581
|
tool: "relations",
|
|
9734
|
-
description: "
|
|
9735
|
-
parameters: { action: "
|
|
9582
|
+
description: "See the summary entry's connections",
|
|
9583
|
+
parameters: { action: "find", entryId: run.summary.entryId }
|
|
9736
9584
|
}
|
|
9737
9585
|
] : void 0
|
|
9738
9586
|
)
|
|
@@ -9913,7 +9761,7 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
|
|
|
9913
9761
|
`Name: ${result2.summary.name}`,
|
|
9914
9762
|
"",
|
|
9915
9763
|
`The summary is captured as a draft. Use \`entries action=commit entryId="${result2.summary.entryId}"\` to promote it to the Chain.`,
|
|
9916
|
-
`Use \`relations action=
|
|
9764
|
+
`Use \`relations action=create\` to link it to the entries it relates to.`
|
|
9917
9765
|
);
|
|
9918
9766
|
return {
|
|
9919
9767
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
@@ -9936,8 +9784,8 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
|
|
|
9936
9784
|
},
|
|
9937
9785
|
{
|
|
9938
9786
|
tool: "relations",
|
|
9939
|
-
description: "
|
|
9940
|
-
parameters: { action: "
|
|
9787
|
+
description: "See its connections",
|
|
9788
|
+
parameters: { action: "find", entryId: result2.summary.entryId }
|
|
9941
9789
|
}
|
|
9942
9790
|
]
|
|
9943
9791
|
)
|
|
@@ -10112,8 +9960,8 @@ async function handleFacilitatedFinalization(wf, descriptor, roundId, normalized
|
|
|
10112
9960
|
[
|
|
10113
9961
|
{
|
|
10114
9962
|
tool: "relations",
|
|
10115
|
-
description: "
|
|
10116
|
-
parameters: { action: "
|
|
9963
|
+
description: "See its connections",
|
|
9964
|
+
parameters: { action: "find", entryId: summaryEntryId }
|
|
10117
9965
|
}
|
|
10118
9966
|
]
|
|
10119
9967
|
)
|
|
@@ -10418,7 +10266,7 @@ function registerQualityTools(server) {
|
|
|
10418
10266
|
"quality",
|
|
10419
10267
|
{
|
|
10420
10268
|
title: "Quality",
|
|
10421
|
-
description: "Assess, re-evaluate, and verify entry/work package quality. Four actions:\n\n- **check**: Read the entry's server quality verdict (tier + criteria) \u2014 it does not re-score
|
|
10269
|
+
description: "Assess, re-evaluate, and verify entry/work package quality. Four actions:\n\n- **check**: Read the entry's server quality verdict (tier + criteria) \u2014 it does not re-score.\n- **re-evaluate**: Trigger a fresh quality evaluation. Returns heuristic verdict immediately, schedules LLM evaluation in background.\n- **verify-chain**: Verify knowledge entries against the actual codebase (codeMapping drift, cross-references) \u2014 absorbs `verify`. Produces a trust report with a trust score.\n- **spine-check**: Structural completeness of the purpose+vision+strategy spine \u2014 duplicate singletons, connectedness, per-strategy refusal heuristic; story:true also runs the on-demand one-story LLM judgment (never scheduled, result labelled [unvalidated]). Async: a plain call reads back the latest stored verdict and auto-schedules a new run ONLY when none is already in flight AND the stored one is absent/stale/missing the requested story pass \u2014 repeated polling never re-triggers work. Pass refresh:true to force a fresh run regardless.",
|
|
10422
10270
|
inputSchema: qualitySchema,
|
|
10423
10271
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
10424
10272
|
},
|
|
@@ -10433,30 +10281,6 @@ function registerQualityTools(server) {
|
|
|
10433
10281
|
})
|
|
10434
10282
|
);
|
|
10435
10283
|
}
|
|
10436
|
-
async function appendLinkSuggestions(entryId, lines) {
|
|
10437
|
-
let relationCount = -1;
|
|
10438
|
-
try {
|
|
10439
|
-
const relations = await kernelQuery("chain.listEntryRelations", { entryId });
|
|
10440
|
-
relationCount = relations?.length ?? 0;
|
|
10441
|
-
} catch {
|
|
10442
|
-
return;
|
|
10443
|
-
}
|
|
10444
|
-
if (relationCount !== 0) return;
|
|
10445
|
-
try {
|
|
10446
|
-
const suggestions = await kernelQuery("chain.graphSuggestLinks", {
|
|
10447
|
-
entryId,
|
|
10448
|
-
maxHops: 2,
|
|
10449
|
-
limit: 3
|
|
10450
|
-
});
|
|
10451
|
-
if ((suggestions?.suggestions?.length ?? 0) > 0) {
|
|
10452
|
-
const linkHints = (suggestions.suggestions ?? []).map((s) => ` \u2192 \`relations action=create from="${entryId}" to="${s.entryId}" type="${s.recommendedRelationType}"\` \u2014 ${s.name} [${s.collectionSlug}] (${s.score}/100)`).join("\n");
|
|
10453
|
-
lines.push(`
|
|
10454
|
-
## Suggested Links to Improve Quality
|
|
10455
|
-
${linkHints}`);
|
|
10456
|
-
}
|
|
10457
|
-
} catch {
|
|
10458
|
-
}
|
|
10459
|
-
}
|
|
10460
10284
|
async function handleCheck(entryId) {
|
|
10461
10285
|
const res = await kernelQuery("quality.getLatestVerdictForEntry", { entryId });
|
|
10462
10286
|
if (!res || !res.entryFound) {
|
|
@@ -10476,7 +10300,6 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
10476
10300
|
"",
|
|
10477
10301
|
`No quality verdict yet for \`${entryId}\`. Run \`quality action=re-evaluate entryId="${entryId}"\` to evaluate it.`
|
|
10478
10302
|
];
|
|
10479
|
-
await appendLinkSuggestions(entryId, lines2);
|
|
10480
10303
|
return successResult(
|
|
10481
10304
|
lines2.join("\n"),
|
|
10482
10305
|
`No quality verdict yet for ${entryId} \u2014 re-evaluate to produce one.`,
|
|
@@ -10497,7 +10320,6 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
10497
10320
|
const verdictSection = formatRubricVerdictSection(verdict);
|
|
10498
10321
|
lines.push(verdictSection || `Verdict tier \`${verdict.tier ?? "unknown"}\` for \`${entryId}\` \u2014 no rubric criteria to report.`);
|
|
10499
10322
|
}
|
|
10500
|
-
await appendLinkSuggestions(entryId, lines);
|
|
10501
10323
|
try {
|
|
10502
10324
|
const wsForTracking = await getWorkspaceContext();
|
|
10503
10325
|
trackQualityCheck(wsForTracking.workspaceId, {
|
|
@@ -10526,9 +10348,7 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
|
|
|
10526
10348
|
...res.stale ? {} : { passed: verdict.passed },
|
|
10527
10349
|
criteria: res.stale ? [] : criteria.map((c) => ({ id: c.id, passed: c.passed, hint: c.hint }))
|
|
10528
10350
|
},
|
|
10529
|
-
[
|
|
10530
|
-
{ tool: "relations", description: "Suggest connections", parameters: { action: "suggest", entryId } }
|
|
10531
|
-
]
|
|
10351
|
+
[]
|
|
10532
10352
|
);
|
|
10533
10353
|
}
|
|
10534
10354
|
async function handleReEvaluate(entryId, context) {
|
|
@@ -10617,25 +10437,6 @@ function composeWrapupView(raw) {
|
|
|
10617
10437
|
}
|
|
10618
10438
|
|
|
10619
10439
|
// src/tools/wrapup.ts
|
|
10620
|
-
async function mapWithConcurrency(items, mapper, concurrency = 3) {
|
|
10621
|
-
const results = new Array(items.length);
|
|
10622
|
-
let index = 0;
|
|
10623
|
-
async function runNext() {
|
|
10624
|
-
while (index < items.length) {
|
|
10625
|
-
const i = index++;
|
|
10626
|
-
try {
|
|
10627
|
-
const value = await mapper(items[i]);
|
|
10628
|
-
results[i] = { status: "fulfilled", value };
|
|
10629
|
-
} catch (reason) {
|
|
10630
|
-
results[i] = { status: "rejected", reason };
|
|
10631
|
-
}
|
|
10632
|
-
}
|
|
10633
|
-
}
|
|
10634
|
-
await Promise.all(
|
|
10635
|
-
Array.from({ length: Math.min(concurrency, items.length) }, () => runNext())
|
|
10636
|
-
);
|
|
10637
|
-
return results;
|
|
10638
|
-
}
|
|
10639
10440
|
function isValidTopGap(g) {
|
|
10640
10441
|
if (typeof g !== "object" || g === null) return false;
|
|
10641
10442
|
const obj = g;
|
|
@@ -10657,40 +10458,13 @@ async function fetchSessionCoherenceSnapshot(sessionId) {
|
|
|
10657
10458
|
}
|
|
10658
10459
|
return null;
|
|
10659
10460
|
}
|
|
10660
|
-
async function suggestLinksForEntries(entries) {
|
|
10661
|
-
const suggestions = [];
|
|
10662
|
-
const results = await mapWithConcurrency(
|
|
10663
|
-
entries,
|
|
10664
|
-
async (entry) => {
|
|
10665
|
-
const result2 = await kernelQuery("chain.graphSuggestLinks", {
|
|
10666
|
-
entryId: entry.entryId,
|
|
10667
|
-
limit: 3
|
|
10668
|
-
});
|
|
10669
|
-
return { entry, suggestions: result2.suggestions ?? [] };
|
|
10670
|
-
},
|
|
10671
|
-
3
|
|
10672
|
-
);
|
|
10673
|
-
for (const result2 of results) {
|
|
10674
|
-
if (result2.status !== "fulfilled") continue;
|
|
10675
|
-
const { entry, suggestions: entrySuggestions } = result2.value;
|
|
10676
|
-
for (const s of entrySuggestions.filter((x) => x.entryId != null).slice(0, 2)) {
|
|
10677
|
-
suggestions.push({
|
|
10678
|
-
fromEntryId: entry.entryId,
|
|
10679
|
-
toEntryId: s.entryId,
|
|
10680
|
-
type: s.recommendedRelationType,
|
|
10681
|
-
label: `${entry.entryId} \u2192 ${s.entryId} (${s.name}) [${s.recommendedRelationType}]`
|
|
10682
|
-
});
|
|
10683
|
-
}
|
|
10684
|
-
}
|
|
10685
|
-
return suggestions;
|
|
10686
|
-
}
|
|
10687
10461
|
async function runWrapupReview() {
|
|
10688
10462
|
const sessionId = getAgentSessionId();
|
|
10689
10463
|
if (!sessionId) {
|
|
10690
|
-
return { text: "No active agent session. Call `session action=start` first.", data: null,
|
|
10464
|
+
return { text: "No active agent session. Call `session action=start` first.", data: null, failureCode: "SESSION_REQUIRED" };
|
|
10691
10465
|
}
|
|
10692
10466
|
if (getApiKeyScope() === "read") {
|
|
10693
|
-
return { text: "Read-only session \u2014 nothing to review.", data: null,
|
|
10467
|
+
return { text: "Read-only session \u2014 nothing to review.", data: null, failureCode: "READONLY_SCOPE" };
|
|
10694
10468
|
}
|
|
10695
10469
|
const { workspaceId } = await getWorkspaceContext();
|
|
10696
10470
|
const kernelEnvelope = await kernelCallEnvelope("agentKnowledge.wrapupEnvelope", {
|
|
@@ -10703,10 +10477,10 @@ async function runWrapupReview() {
|
|
|
10703
10477
|
const overflowUnscanned = data.overflowUnscanned ?? 0;
|
|
10704
10478
|
const loopEmpty = data.drafts.length === 0 && overflowDraftCount === 0 && overflowUnscanned === 0;
|
|
10705
10479
|
if (data.wrapupCompletedAt && loopEmpty) {
|
|
10706
|
-
return { text: "Wrapup already completed this session.", data,
|
|
10480
|
+
return { text: "Wrapup already completed this session.", data, failureCode: "ALREADY_COMPLETED" };
|
|
10707
10481
|
}
|
|
10708
10482
|
if (data.drafts.length === 0 && data.committed.length === 0 && overflowDraftCount === 0 && (data.overflowCommitted ?? []).length === 0) {
|
|
10709
|
-
return { text: "Empty session \u2014 no entries to review.", data,
|
|
10483
|
+
return { text: "Empty session \u2014 no entries to review.", data, failureCode: "NOT_FOUND" };
|
|
10710
10484
|
}
|
|
10711
10485
|
const lines = [];
|
|
10712
10486
|
lines.push(
|
|
@@ -10751,17 +10525,6 @@ async function runWrapupReview() {
|
|
|
10751
10525
|
}
|
|
10752
10526
|
lines.push("");
|
|
10753
10527
|
}
|
|
10754
|
-
const lowConnEntries = [...data.drafts, ...data.committed].filter(
|
|
10755
|
-
(e) => (data.connectionCounts[e.entryId] ?? 0) < 2
|
|
10756
|
-
);
|
|
10757
|
-
const suggestions = lowConnEntries.length > 0 ? await suggestLinksForEntries(lowConnEntries.slice(0, 10)) : [];
|
|
10758
|
-
if (suggestions.length > 0) {
|
|
10759
|
-
lines.push("### Suggested Links (not written; wrapup-commit writes none \u2014 create the ones you want with `relations action=create`)", "");
|
|
10760
|
-
for (const s of suggestions) {
|
|
10761
|
-
lines.push(`- ${s.label}`);
|
|
10762
|
-
}
|
|
10763
|
-
lines.push("");
|
|
10764
|
-
}
|
|
10765
10528
|
const topGaps = getTopGaps(5);
|
|
10766
10529
|
if (topGaps.length > 0) {
|
|
10767
10530
|
lines.push("### Learning Opportunities", "");
|
|
@@ -10860,7 +10623,6 @@ async function runWrapupReview() {
|
|
|
10860
10623
|
return {
|
|
10861
10624
|
text: lines.join("\n"),
|
|
10862
10625
|
data,
|
|
10863
|
-
suggestions,
|
|
10864
10626
|
gapCount,
|
|
10865
10627
|
coherenceVerdict,
|
|
10866
10628
|
persistentGaps,
|
|
@@ -10993,7 +10755,7 @@ function createWrapupHandler() {
|
|
|
10993
10755
|
commitNext
|
|
10994
10756
|
);
|
|
10995
10757
|
}
|
|
10996
|
-
const { text, data,
|
|
10758
|
+
const { text, data, failureCode, gapCount, coherenceVerdict, persistentGaps, kernelSummary, kernelNext, kernelMeta } = await runWrapupReview();
|
|
10997
10759
|
lastReviewData = data;
|
|
10998
10760
|
lastReviewSessionId = getAgentSessionId();
|
|
10999
10761
|
const composed = data ? composeWrapupView(data) : null;
|
|
@@ -11010,7 +10772,7 @@ ${text}` : text;
|
|
|
11010
10772
|
const persistentSummary = persistentGaps?.length ? `, ${persistentGaps.length} persistent coherence gaps` : "";
|
|
11011
10773
|
const draftsCount = composed?.drafts.length ?? data.drafts.length;
|
|
11012
10774
|
const committedCount = composed?.committed.length ?? data.committed.length;
|
|
11013
|
-
const summaryText = `Session review: ${draftsCount} uncommitted, ${committedCount} committed
|
|
10775
|
+
const summaryText = `Session review: ${draftsCount} uncommitted, ${committedCount} committed${gapsSummary}${coherenceSummary}${persistentSummary}.`;
|
|
11014
10776
|
return {
|
|
11015
10777
|
content: [{ type: "text", text: fullText }],
|
|
11016
10778
|
structuredContent: {
|
|
@@ -11020,7 +10782,6 @@ ${text}` : text;
|
|
|
11020
10782
|
drafts: draftsCount,
|
|
11021
10783
|
committed: committedCount,
|
|
11022
10784
|
uncommitted: data.summary.uncommitted,
|
|
11023
|
-
suggestedLinks: suggestions.length,
|
|
11024
10785
|
knowledgeGaps: gapCount ?? 0,
|
|
11025
10786
|
...coherenceVerdict ? { coherenceVerdict } : {},
|
|
11026
10787
|
...persistentGaps?.length ? { persistentCoherenceGaps: persistentGaps.length } : {}
|
|
@@ -11393,7 +11154,7 @@ function registerSessionTools(server) {
|
|
|
11393
11154
|
"session",
|
|
11394
11155
|
{
|
|
11395
11156
|
title: "Session \u2014 Lifecycle & Facilitation",
|
|
11396
|
-
description: "Session lifecycle, wrapup review, and work package facilitation.\n\n- **start**: Begin a tracked write session for attribution. **This is the ONLY call that creates a session** \u2014 write tools (capture, entries action=update/commit, relations) require it. `orient action=start` orients an existing session and loads stage-aware context, but does NOT create one: call `session action=start` first, then `orient action=start`.\n- **close**: End the current session. Records structured data (entries created, modified, relations). Run `session action=wrapup-review` before closing.\n- **status**: Check current session \u2014 active or not, oriented, activity counts.\n- **wrapup-review**: Review uncommitted drafts with quality scores
|
|
11157
|
+
description: "Session lifecycle, wrapup review, and work package facilitation.\n\n- **start**: Begin a tracked write session for attribution. **This is the ONLY call that creates a session** \u2014 write tools (capture, entries action=update/commit, relations) require it. `orient action=start` orients an existing session and loads stage-aware context, but does NOT create one: call `session action=start` first, then `orient action=start`.\n- **close**: End the current session. Records structured data (entries created, modified, relations). Run `session action=wrapup-review` before closing.\n- **status**: Check current session \u2014 active or not, oriented, activity counts.\n- **wrapup-review**: Review uncommitted drafts with quality scores. Run before close.\n- **wrapup-commit**: Accept all uncommitted drafts. Writes no relations; create the ones you want with relations action=create.\n- **resume**: Load session state (work package data, constellation, drafts) from an existing work package entry.\n- **commit-constellation**: Atomically accept a work package and all its linked draft entries in one call.",
|
|
11397
11158
|
inputSchema: sessionSchema,
|
|
11398
11159
|
// Mixed read/write noun (§3 R3): wrapup-commit/resume/commit-constellation write.
|
|
11399
11160
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
@@ -13132,7 +12893,7 @@ var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
|
|
|
13132
12893
|
| \`orient\` | Workspace context, governance, active work packages, session start | \`action\`: start, task |
|
|
13133
12894
|
| \`capture\` | Create entry | \`action\`: capture |
|
|
13134
12895
|
| \`entries\` | Read and write entries | \`action\`: list, get, batch, search, update, commit, history, move, verify, preflight, still-true |
|
|
13135
|
-
| \`relations\` | Link entries,
|
|
12896
|
+
| \`relations\` | Link entries, delete links, find connections | \`action\`: create, batch-create, delete, find |
|
|
13136
12897
|
| \`context\` | Gather related knowledge, last-verified brief | \`action\`: gather, build, neighborhood, changes, chain, cross-cut, incremental, brief, last-verified-brief |
|
|
13137
12898
|
| \`collections\` | Browse, describe, audit and export collections; manage labels | \`action\`: list, update, describe, audit, export, label-* |
|
|
13138
12899
|
| \`quality\` | Score an entry, verify the Chain, spine check | \`action\`: check, re-evaluate, verify-chain, spine-check |
|
|
@@ -13169,7 +12930,7 @@ Write via \`update\`: \`draft\` | \`archived\` (\`accepted\` via \`commit\`, nev
|
|
|
13169
12930
|
- A rule on no list binds nobody; a full list refuses a new rule until one retires, and a list over its cap is served whole.
|
|
13170
12931
|
|
|
13171
12932
|
## Key Patterns
|
|
13172
|
-
- **Capture flow:** \`capture\` \u2192 \`relations action=
|
|
12933
|
+
- **Capture flow:** \`capture\` \u2192 \`relations action=batch-create\` \u2192 \`entries action=commit\`
|
|
13173
12934
|
- Use \`workflowStatus\` (not \`status\`) for domain workflow state
|
|
13174
12935
|
- \`data\` param is merged (not replaced) \u2014 safe for partial updates
|
|
13175
12936
|
`;
|
|
@@ -13211,13 +12972,13 @@ Tags for filtering (e.g. \`severity:high\`), relations via \`entryRelations\`, v
|
|
|
13211
12972
|
'## Rules\nThe rules that bind you are in the briefing above, each with its why. A rule on no list binds nobody.\nOpen one: `entries action=get entryId="<ID>"`. Find one: `entries action=search query="<noun>"`.\nDraft a new rule: use the `draft-rule-from-context` prompt.'
|
|
13212
12973
|
);
|
|
13213
12974
|
sections.push(
|
|
13214
|
-
"## Knowledge Graph\nEntries are connected via typed relations (`entryRelations` table). Relations are bidirectional and collection-agnostic \u2014 any entry can link to any other entry.\n\n**Recommended relation types** (extensible \u2014 any string accepted):\n- `governs` \u2014 a rule constrains behavior of a feature\n- `defines_term_for` \u2014 a glossary term is canonical vocabulary for a feature/area\n- `belongs_to` \u2014 a feature belongs to a product area or parent concept (typed successor: `anchored_to`)\n- `informs` \u2014 a decision or insight informs a feature\n- `surfaces_tension_in` \u2014 a tension exists within a feature area\n- `related_to`, `depends_on`, `replaces`, `conflicts_with`, `references`, `confused_with`\n\nEach relation type is defined as a glossary entry (prefix `GT-REL-*`) to prevent terminology drift.\n\n**Tools:**\n- `context action=gather` \u2014 get the full context around any entry (multi-hop graph traversal)\n- `relations action=
|
|
12975
|
+
"## Knowledge Graph\nEntries are connected via typed relations (`entryRelations` table). Relations are bidirectional and collection-agnostic \u2014 any entry can link to any other entry.\n\n**Recommended relation types** (extensible \u2014 any string accepted):\n- `governs` \u2014 a rule constrains behavior of a feature\n- `defines_term_for` \u2014 a glossary term is canonical vocabulary for a feature/area\n- `belongs_to` \u2014 a feature belongs to a product area or parent concept (typed successor: `anchored_to`)\n- `informs` \u2014 a decision or insight informs a feature\n- `surfaces_tension_in` \u2014 a tension exists within a feature area\n- `related_to`, `depends_on`, `replaces`, `conflicts_with`, `references`, `confused_with`\n\nEach relation type is defined as a glossary entry (prefix `GT-REL-*`) to prevent terminology drift.\n\n**Tools:**\n- `context action=gather` \u2014 get the full context around any entry (multi-hop graph traversal)\n- `relations action=create` \u2014 create a typed link between two entries\n- `relations action=find` \u2014 list direct relations for an entry\n\n**Convention:** A relation is written only when you ask for it, with exactly the verb you send \u2014 link the entries you know are related with `relations action=create`."
|
|
13215
12976
|
);
|
|
13216
12977
|
sections.push(
|
|
13217
12978
|
`## Creating Knowledge
|
|
13218
12979
|
**Entries:** Use \`capture\` as the primary tool for creating new entries. It handles the full workflow in one call:
|
|
13219
12980
|
1. Creates the entry with collection-aware defaults (auto-fills dates, infers domains, sets priority)
|
|
13220
|
-
2. Writes the relations you pass in \`links\` \u2014 and no others; use \`relations action=
|
|
12981
|
+
2. Writes the relations you pass in \`links\` \u2014 and no others; use \`relations action=create\` to add more
|
|
13221
12982
|
3. Returns the server's quality verdict with improvement hints
|
|
13222
12983
|
|
|
13223
12984
|
**Smart profiles** exist for: ${SMART_PROFILE_COLLECTIONS.map((c) => `\`${c}\``).join(", ")}.
|
|
@@ -13234,14 +12995,14 @@ Use \`quality action=check\` to score existing entries retroactively.
|
|
|
13234
12995
|
Use \`entries action=update\` for post-creation adjustments (status changes, field updates, deprecation).`
|
|
13235
12996
|
);
|
|
13236
12997
|
sections.push(
|
|
13237
|
-
"## Where to Go Next\n- **Create entry** \u2192 `capture` tool (entry, the links you pass, and a quality verdict in one call)\n- **Full context** \u2192 `context action=gather` (by entry ID or task description)\n- **
|
|
12998
|
+
"## Where to Go Next\n- **Create entry** \u2192 `capture` tool (entry, the links you pass, and a quality verdict in one call)\n- **Full context** \u2192 `context action=gather` (by entry ID or task description)\n- **Link entries** \u2192 `relations action=create`\n- **Quality audit** \u2192 `quality action=check`\n- **Terminology** \u2192 `name-check` prompt or `productbrain://terminology` resource\n- **Schema details** \u2192 `productbrain://collections` resource or `collections action=list`\n- **Labels** \u2192 `productbrain://labels` resource or `collections action=label-list`\n- **Any collection** \u2192 `productbrain://{slug}/entries` resource\n- **Log a decision** \u2192 `draft-decision-record` prompt\n- **Growth funnel** \u2192 `productbrain://growth-funnel` resource or `entries action=list collection=strategy status=draft`\n- **Session identity** \u2192 `workspace action=whoami`\n- **Health check** \u2192 `workspace action=check`\n- **Debug MCP calls** \u2192 `workspace action=audit`"
|
|
13238
12999
|
);
|
|
13239
13000
|
return sections.join("\n\n---\n\n");
|
|
13240
13001
|
}
|
|
13241
|
-
async function buildOrientationResourceText(
|
|
13002
|
+
async function buildOrientationResourceText(kernelQuery3, agentSessionId) {
|
|
13242
13003
|
const [collectionsResult, orientViewResult] = await Promise.allSettled([
|
|
13243
|
-
|
|
13244
|
-
|
|
13004
|
+
kernelQuery3("chain.listCollections"),
|
|
13005
|
+
kernelQuery3("chain.getOrientView", {
|
|
13245
13006
|
tier: "summary",
|
|
13246
13007
|
invocationPath: "unknown",
|
|
13247
13008
|
...agentSessionId ? { sessionId: agentSessionId } : {}
|
|
@@ -13822,10 +13583,10 @@ var INSTRUCTIONS = [
|
|
|
13822
13583
|
"",
|
|
13823
13584
|
"## Writing",
|
|
13824
13585
|
"",
|
|
13825
|
-
"Write actions (capture, entries update and commit, relations create, batch-create
|
|
13586
|
+
"Write actions (capture, entries update and commit, relations create, batch-create and delete) need an active session and a readwrite key.",
|
|
13826
13587
|
"Pass the `sessionId` that `session action=start` returned on every write: the connection does not remember it.",
|
|
13827
13588
|
"Use `capture` to create an entry and read its reply: an entry that came back accepted needs no `entries action=commit`.",
|
|
13828
|
-
"Link with `relations action=
|
|
13589
|
+
"Link entries with `relations action=create` \u2014 a relation is written only when you ask, with exactly the verb you send.",
|
|
13829
13590
|
"Finish with `session action=wrapup-review`, then `session action=close`.",
|
|
13830
13591
|
"If the work falls outside the active work packages, name the gap and ask the user before designing it.",
|
|
13831
13592
|
"",
|
|
@@ -13923,4 +13684,4 @@ export {
|
|
|
13923
13684
|
createProductBrainServer,
|
|
13924
13685
|
initFeatureFlags
|
|
13925
13686
|
};
|
|
13926
|
-
//# sourceMappingURL=chunk-
|
|
13687
|
+
//# sourceMappingURL=chunk-OEONSR7Q.js.map
|