@productbrain/mcp 0.0.1-beta.8845 → 0.0.1-beta.8855

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.
@@ -42,7 +42,7 @@ import {
42
42
  trackSessionCaptureRate,
43
43
  trackZeroCaptureAuditFired,
44
44
  validateCallerSession
45
- } from "./chunk-NT3NE6V5.js";
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|suggest" (same action names)'],
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. Use \`relations action=suggest entryId="${finalEntryId}"\` to find strategy entries to connect to.`
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=suggest entryId="${eid}"\` \u2014 discover additional links`);
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=suggest entryId="${eid}"\` \u2014 discover what this should link to`);
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: "Discover connections", parameters: { action: "suggest", entryId: finalEntryId } });
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: "Discover connections", parameters: { action: "suggest", entryId } },
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: "Explore connections", parameters: { action: "suggest", entryId } },
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", "suggest"];
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). 'suggest': discover potential connections for an entry."
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", "dismiss", "delete", "find", "suggest"];
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,39 +4201,28 @@ 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). 'dismiss': record that a suggestion was not relevant. 'delete': remove a relation. 'find': traverse relations from an entry (graph walk, absorbs graph action=find). 'suggest': discover potential connections for an entry (absorbs graph action=suggest)."
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'/'dismiss'/'delete': source entry ID."),
4314
- to: z13.string().max(200).optional().describe("For 'create'/'dismiss'/'delete': target entry ID."),
4315
- type: z13.string().max(200).optional().describe("For 'create'/'dismiss'/'delete': relation type."),
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'/'suggest': entry ID, e.g. '<PREFIX>-<n>'."),
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"),
4327
4217
  from: z13.string().max(200),
4328
4218
  to: z13.string().max(200),
4329
4219
  type: z13.string().max(200),
4330
- score: z13.number().optional(),
4331
4220
  preview: z13.boolean().optional()
4332
4221
  });
4333
4222
  var relationsBatchCreateVariant = z13.object({
4334
4223
  action: z13.literal("batch-create"),
4335
4224
  relations: z13.array(relationItemSchema).min(1).max(RELATION_BATCH_MAX)
4336
4225
  });
4337
- var relationsDismissVariant = z13.object({
4338
- action: z13.literal("dismiss"),
4339
- from: z13.string().max(200),
4340
- to: z13.string().max(200),
4341
- type: z13.string().max(200).optional(),
4342
- score: z13.number().optional()
4343
- });
4344
4226
  var relationsDeleteVariant = z13.object({
4345
4227
  action: z13.literal("delete"),
4346
4228
  from: z13.string().max(200),
@@ -4352,44 +4234,32 @@ var relationsFindVariant = z13.object({
4352
4234
  entryId: z13.string().max(200),
4353
4235
  direction: z13.enum(["incoming", "outgoing", "both"]).optional().default("both")
4354
4236
  });
4355
- var relationsSuggestVariant = z13.object({
4356
- action: z13.literal("suggest"),
4357
- entryId: z13.string().max(200),
4358
- limit: z13.number().min(1).max(20).optional().default(10),
4359
- depth: z13.number().min(1).max(3).optional().default(2)
4360
- });
4361
4237
  var relationsActionUnion = z13.discriminatedUnion("action", [
4362
4238
  relationsCreateVariant,
4363
4239
  relationsBatchCreateVariant,
4364
- relationsDismissVariant,
4365
4240
  relationsDeleteVariant,
4366
- relationsFindVariant,
4367
- relationsSuggestVariant
4241
+ relationsFindVariant
4368
4242
  ]);
4369
4243
  var RELATIONS_ACTION_SPECS = {
4370
- create: { params: ["from", "to", "type", "score", "preview"], description: "from, to, and type are required." },
4244
+ create: { params: ["from", "to", "type", "preview"], description: "from, to, and type are required." },
4371
4245
  "batch-create": { params: ["relations"], description: "relations (1-20 items) is required." },
4372
- dismiss: { params: ["from", "to", "type", "score"], description: "from and to are required." },
4373
4246
  delete: { params: ["from", "to", "type"], description: "from, to, and type are required." },
4374
- find: { params: ["entryId", "direction"], description: "entryId is required." },
4375
- suggest: { params: ["entryId", "limit", "depth"], description: "entryId is required." }
4247
+ find: { params: ["entryId", "direction"], description: "entryId is required." }
4376
4248
  };
4377
4249
  function registerRelationsTools(server) {
4378
4250
  const relationsHandlers = {
4379
- create: (data) => handleCreate(data.from, data.to, data.type, data.score, data.preview),
4251
+ create: (data) => handleCreate(data.from, data.to, data.type, data.preview),
4380
4252
  "batch-create": (data) => handleBatchCreate(data.relations),
4381
- dismiss: (data) => handleDismiss(data.from, data.to, data.type ?? "related_to", data.score),
4382
4253
  delete: (data) => handleDelete(data.from, data.to, data.type),
4383
- find: (data) => handleFind(data.entryId, data.direction ?? "both"),
4384
- suggest: (data) => handleSuggest(data.entryId, data.limit ?? 10, data.depth ?? 2)
4254
+ find: (data) => handleFind(data.entryId, data.direction ?? "both")
4385
4255
  };
4386
4256
  server.registerTool(
4387
4257
  "relations",
4388
4258
  {
4389
4259
  title: "Relations",
4390
- description: "Create, manage, and discover relations between entries.\n\n- **create**: Link two entries with a typed relation. Use action=suggest to discover connections.\n- **batch-create**: Create multiple relations in one call (validates every item before writing any). Use after action=suggest.\n- **dismiss**: Record that a suggested link was not relevant. Improves future suggestion quality.\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).\n- **suggest**: Discover potential relations for an entry using graph-aware intelligence. Returns ranked suggestions with confidence scores.",
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).",
4391
4261
  inputSchema: relationsSchema,
4392
- // Mixed read/write noun (§3 R3): find/suggest are read-only, create/batch-create/dismiss/delete write.
4262
+ // Mixed read/write noun (§3 R3): find is read-only, create/batch-create/delete write.
4393
4263
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
4394
4264
  },
4395
4265
  thinWrapper(async (args) => {
@@ -4403,16 +4273,14 @@ function registerRelationsTools(server) {
4403
4273
  })
4404
4274
  );
4405
4275
  }
4406
- async function handleCreate(from, to, type, score, preview) {
4276
+ async function handleCreate(from, to, type, preview) {
4407
4277
  requireWriteAccess();
4408
4278
  const agentSessionId = getAgentSessionId();
4409
4279
  const result2 = await kernelMutation("chain.createEntryRelation", {
4410
4280
  fromEntryId: from,
4411
4281
  toEntryId: to,
4412
4282
  type,
4413
- suggestionScore: score ?? void 0,
4414
- // WP-474 E4-REWIRE: explicit relate = human/agent-CHOSEN type → mark "user"
4415
- // (DEC-1399: out of gate scope, but positively marked so the judge never keys off absence).
4283
+ // Explicit relate = the caller chose the verb; DEC-1915 / TASK-601: it is written exactly as sent.
4416
4284
  proposedBy: "user",
4417
4285
  sessionId: agentSessionId ?? void 0,
4418
4286
  ...preview ? { preview: true } : {}
@@ -4478,16 +4346,13 @@ ${result2.blockingReason ?? "This relation would be rejected."}` }],
4478
4346
  )
4479
4347
  };
4480
4348
  }
4481
- const suggested2 = result2.suggestedType;
4482
4349
  const advisoryText = (result2.advisories ?? []).map((a) => `
4483
4350
 
4484
4351
  \u26A0 **${a.ruleId}** (org stage: ${a.stage}): ${a.ruleText}`).join("");
4485
4352
  return {
4486
4353
  content: [{ type: "text", text: `# Preview: would relate ${from} \u2192 ${to} (${type})
4487
4354
 
4488
- No DB writes \u2014 call without \`preview:true\` to create for real.${suggested2 ? `
4489
-
4490
- **Type suggestion:** \`${suggested2.type}\` may be more precise (${suggested2.confidence}% confidence).` : ""}${advisoryText}` }],
4355
+ No DB writes \u2014 call without \`preview:true\` to create for real.${advisoryText}` }],
4491
4356
  structuredContent: success(
4492
4357
  `Preview: would relate ${from} \u2192 ${to} (${type}) \u2014 no DB writes`,
4493
4358
  {
@@ -4495,7 +4360,6 @@ No DB writes \u2014 call without \`preview:true\` to create for real.${suggested
4495
4360
  from,
4496
4361
  to,
4497
4362
  type,
4498
- ...suggested2 && { suggestedType: suggested2 },
4499
4363
  ...result2.advisories && result2.advisories.length > 0 && { advisories: result2.advisories }
4500
4364
  },
4501
4365
  [{ tool: "relations", description: "Create for real", parameters: { action: "create", from, to, type } }]
@@ -4509,13 +4373,6 @@ No DB writes \u2014 call without \`preview:true\` to create for real.${suggested
4509
4373
  `**${from}** \u2014[${type}]\u2192 **${to}**`,
4510
4374
  `**Workspace:** ${wsCtx.workspaceSlug} (${wsCtx.workspaceId})`
4511
4375
  ];
4512
- const suggested = result2?.suggestedType;
4513
- if (suggested) {
4514
- lines.push("");
4515
- lines.push(`**Type suggestion:** \`${suggested.type}\` may be more precise (${suggested.confidence}% confidence).`);
4516
- lines.push(`_${suggested.reasoning}_`);
4517
- lines.push(`To use instead: \`relations action=create from="${from}" to="${to}" type="${suggested.type}"\``);
4518
- }
4519
4376
  for (const a of result2?.advisories ?? []) {
4520
4377
  lines.push("");
4521
4378
  lines.push(`\u26A0 Written anyway (org stage: ${a.stage}): ${a.ruleText}`);
@@ -4529,7 +4386,6 @@ No DB writes \u2014 call without \`preview:true\` to create for real.${suggested
4529
4386
  from,
4530
4387
  to,
4531
4388
  type,
4532
- ...suggested && { suggestedType: suggested },
4533
4389
  ...result2?.advisories && result2.advisories.length > 0 && { advisories: result2.advisories }
4534
4390
  },
4535
4391
  [{ tool: "relations", description: "See connections", parameters: { action: "find", entryId: from } }]
@@ -4587,28 +4443,6 @@ async function handleBatchCreate(relations) {
4587
4443
  )
4588
4444
  };
4589
4445
  }
4590
- async function handleDismiss(from, to, type, score) {
4591
- requireWriteAccess();
4592
- const fromEntry = await kernelQuery("chain.getEntry", { entryId: from });
4593
- if (!fromEntry) {
4594
- return notFoundResult(from);
4595
- }
4596
- const toEntry = await kernelQuery("chain.getEntry", { entryId: to });
4597
- if (!toEntry) {
4598
- return notFoundResult(to);
4599
- }
4600
- await kernelMutation("chain.dismissSuggestion", {
4601
- fromEntryId: fromEntry._id,
4602
- suggestedEntryId: toEntry._id,
4603
- recommendedType: type,
4604
- score: score ?? 0
4605
- });
4606
- return successResult(
4607
- `Dismissed suggestion: ${from} \u2192 ${to} (${type}). Feedback recorded for scoring improvements.`,
4608
- `Dismissed suggestion ${from} \u2192 ${to} (${type}). Feedback recorded.`,
4609
- { from, to, type }
4610
- );
4611
- }
4612
4446
  async function handleDelete(from, to, type) {
4613
4447
  requireWriteAccess();
4614
4448
  await kernelMutation("chain.removeEntryRelation", {
@@ -4892,12 +4726,12 @@ async function handleGather(server, entryId, task, mode, maxHops, maxResults, st
4892
4726
 
4893
4727
  _No relations found._ This entry is not yet connected to the knowledge graph.
4894
4728
 
4895
- Use \`relations action=suggest\` to discover potential connections.`
4729
+ Use \`relations action=create\` to link it to the entries it relates to.`
4896
4730
  }],
4897
4731
  structuredContent: success(
4898
4732
  `No relations found for ${root2.entryId}. Entry not yet connected.`,
4899
4733
  { root: root2, context: [], totalFound: 0 },
4900
- [{ tool: "relations", description: "Discover connections", parameters: { action: "suggest", entryId: root2.entryId } }]
4734
+ [{ tool: "relations", description: "Link it", parameters: { action: "create", from: root2.entryId } }]
4901
4735
  )
4902
4736
  };
4903
4737
  }
@@ -5124,12 +4958,12 @@ _This helps future sessions start with better constraints and context._`
5124
4958
 
5125
4959
  _No relations found._ This entry is not yet connected to the knowledge graph.
5126
4960
 
5127
- Use \`relations action=suggest\` to discover potential connections, or \`relations action=create\` to link manually.`
4961
+ Use \`relations action=create\` to link it.`
5128
4962
  }],
5129
4963
  structuredContent: success(
5130
4964
  `No relations found for ${root.entryId}. Entry not yet connected.`,
5131
4965
  { root, related: [], totalRelations: 0 },
5132
- [{ tool: "relations", description: "Discover connections", parameters: { action: "suggest", entryId: root.entryId } }]
4966
+ [{ tool: "relations", description: "Link it", parameters: { action: "create", from: root.entryId } }]
5133
4967
  )
5134
4968
  };
5135
4969
  }
@@ -5312,14 +5146,14 @@ async function handleNeighborhood(entryId) {
5312
5146
  const isEmpty = result2.blockingChain.length === 0 && result2.dependencyChain.length === 0 && !result2.parentContext && result2.relatedTensions.length === 0;
5313
5147
  if (isEmpty) {
5314
5148
  lines.push(`_No neighborhood data found. Entry may not exist or has no graph connections._`);
5315
- lines.push(`Use \`relations action=suggest\` to discover potential connections.`);
5149
+ lines.push(`Use \`relations action=create\` to link it to the entries it relates to.`);
5316
5150
  }
5317
5151
  return {
5318
5152
  content: [{ type: "text", text: lines.join("\n") }],
5319
5153
  structuredContent: success(
5320
5154
  `Neighborhood for ${entryId}: ${result2.blockingChain.length} blocking, ${result2.dependencyChain.length} dependencies, ${result2.relatedTensions.length} tensions.`,
5321
5155
  result2,
5322
- isEmpty ? [{ tool: "relations", description: "Discover connections", parameters: { action: "suggest", entryId } }] : void 0
5156
+ isEmpty ? [{ tool: "relations", description: "Link it", parameters: { action: "create", from: entryId } }] : void 0
5323
5157
  )
5324
5158
  };
5325
5159
  }
@@ -7289,11 +7123,11 @@ function buildOrientBriefingLines(view, opts) {
7289
7123
  lines.splice(pull < 0 ? lines.length : pull, 0, RULE_LISTS_EVERYONE_ONLY_LABEL, "(none currently served)", "");
7290
7124
  return lines;
7291
7125
  }
7292
- async function fetchAndRenderOpenerDirection(kernelQuery2, errors, session) {
7126
+ async function fetchAndRenderOpenerDirection(kernelQuery3, errors, session) {
7293
7127
  let view = null;
7294
7128
  let fetchFailed = false;
7295
7129
  try {
7296
- view = await kernelQuery2("chain.getOrientView", {
7130
+ view = await kernelQuery3("chain.getOrientView", {
7297
7131
  tier: "standard",
7298
7132
  invocationPath: session?.invocationPath ?? "session-start",
7299
7133
  // Stamps this call onto the current session/conversation/task (same as every other
@@ -7308,9 +7142,9 @@ async function fetchAndRenderOpenerDirection(kernelQuery2, errors, session) {
7308
7142
  }
7309
7143
  return { lines: [...buildOrientBriefingLines(view), ...buildRitualsSurfaceLines(view, { fetchFailed: fetchFailed || view?.fetchFailed === true })], view };
7310
7144
  }
7311
- async function buildAlreadyOrientedResult(kernelQuery2, agentSessionId, taskGroundingWarningLines2) {
7145
+ async function buildAlreadyOrientedResult(kernelQuery3, agentSessionId, taskGroundingWarningLines2) {
7312
7146
  const errors = [];
7313
- const { lines, view } = await fetchAndRenderOpenerDirection(kernelQuery2, errors, { agentSessionId, invocationPath: "manual-orient" });
7147
+ const { lines, view } = await fetchAndRenderOpenerDirection(kernelQuery3, errors, { agentSessionId, invocationPath: "manual-orient" });
7314
7148
  return {
7315
7149
  content: [{
7316
7150
  type: "text",
@@ -8979,7 +8813,7 @@ Use \`workflows action=checkpoint\` after each round to persist progress to the
8979
8813
  label: "Capture & Accept",
8980
8814
  type: "close",
8981
8815
  instruction: "Capture the pitch to the Chain. Accept draft entries. Connect the constellation.",
8982
- 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 `relations action=suggest entryId='<betId>'` to discover connections. Create the top 3 suggested relations via `relations action=batch-create`. Thank the user. State: what was shaped, how many constellation entries captured, what the implementer inherits.",
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.",
8983
8817
  outputSchema: {
8984
8818
  field: "capture",
8985
8819
  description: "Final capture summary \u2014 entries accepted, relations created",
@@ -9171,7 +9005,7 @@ This block is the final deliverable of the review. Everything else is supporting
9171
9005
  label: "Capture & Close",
9172
9006
  type: "close",
9173
9007
  instruction: "Finalize the review as an insight draft. Connect new entries from learning extraction. Use session action=wrapup-review. Thank the participant.",
9174
- 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: run relations action=suggest for each new entry, then relations action=batch-create. Run session action=wrapup-review before closing. Thank the participant. State what was fixed, what was captured, what was deferred.",
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.",
9175
9009
  outputSchema: {
9176
9010
  field: "capture",
9177
9011
  description: "Insight entry created, WP IDs referenced, learnings connected",
@@ -9236,7 +9070,6 @@ var PROCESS_CHANGE_WORKFLOW_DESCRIPTOR = {
9236
9070
  - \`orient\` \u2014 load workspace context (governance, active work packages, stale entries)
9237
9071
  - \`entries action=search\` \u2014 find related entries
9238
9072
  - \`capture\` \u2014 manual fallback only if workflow finalization cannot safely route the entry
9239
- - \`relations action=suggest\` \u2014 discover connections
9240
9073
  - \`relations action=create\` or \`relations action=batch-create\` \u2014 link to related entries
9241
9074
  - \`entries action=commit\` \u2014 promote to SSOT (only on user confirmation)
9242
9075
  - \`workflows action=checkpoint\` \u2014 save progress if needed
@@ -9296,7 +9129,7 @@ var PROCESS_CHANGE_WORKFLOW_DESCRIPTOR = {
9296
9129
  label: "Capture & Close",
9297
9130
  type: "close",
9298
9131
  instruction: "Capture to the Chain. Link to related entries. Confirm with the user.",
9299
- 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, call relations action=suggest to find connections. Create the top 2-3 relevant relations. 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.",
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.",
9300
9133
  outputSchema: {
9301
9134
  field: "capture",
9302
9135
  description: "Entry captured, relations created, accept status",
@@ -9746,8 +9579,8 @@ async function handleGetRun(runId, workflowId) {
9746
9579
  run.summary ? [
9747
9580
  {
9748
9581
  tool: "relations",
9749
- description: "Discover connections for the summary entry",
9750
- parameters: { action: "suggest", entryId: run.summary.entryId }
9582
+ description: "See the summary entry's connections",
9583
+ parameters: { action: "find", entryId: run.summary.entryId }
9751
9584
  }
9752
9585
  ] : void 0
9753
9586
  )
@@ -9928,7 +9761,7 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
9928
9761
  `Name: ${result2.summary.name}`,
9929
9762
  "",
9930
9763
  `The summary is captured as a draft. Use \`entries action=commit entryId="${result2.summary.entryId}"\` to promote it to the Chain.`,
9931
- `Use \`relations action=suggest entryId="${result2.summary.entryId}"\` to discover connections.`
9764
+ `Use \`relations action=create\` to link it to the entries it relates to.`
9932
9765
  );
9933
9766
  return {
9934
9767
  content: [{ type: "text", text: lines.join("\n") }],
@@ -9951,8 +9784,8 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
9951
9784
  },
9952
9785
  {
9953
9786
  tool: "relations",
9954
- description: "Discover connections",
9955
- parameters: { action: "suggest", entryId: result2.summary.entryId }
9787
+ description: "See its connections",
9788
+ parameters: { action: "find", entryId: result2.summary.entryId }
9956
9789
  }
9957
9790
  ]
9958
9791
  )
@@ -10127,8 +9960,8 @@ async function handleFacilitatedFinalization(wf, descriptor, roundId, normalized
10127
9960
  [
10128
9961
  {
10129
9962
  tool: "relations",
10130
- description: "Discover connections",
10131
- parameters: { action: "suggest", entryId: summaryEntryId }
9963
+ description: "See its connections",
9964
+ parameters: { action: "find", entryId: summaryEntryId }
10132
9965
  }
10133
9966
  ]
10134
9967
  )
@@ -10433,7 +10266,7 @@ function registerQualityTools(server) {
10433
10266
  "quality",
10434
10267
  {
10435
10268
  title: "Quality",
10436
- 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. Suggests links when the entry has no relations.\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.",
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.",
10437
10270
  inputSchema: qualitySchema,
10438
10271
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
10439
10272
  },
@@ -10448,30 +10281,6 @@ function registerQualityTools(server) {
10448
10281
  })
10449
10282
  );
10450
10283
  }
10451
- async function appendLinkSuggestions(entryId, lines) {
10452
- let relationCount = -1;
10453
- try {
10454
- const relations = await kernelQuery("chain.listEntryRelations", { entryId });
10455
- relationCount = relations?.length ?? 0;
10456
- } catch {
10457
- return;
10458
- }
10459
- if (relationCount !== 0) return;
10460
- try {
10461
- const suggestions = await kernelQuery("chain.graphSuggestLinks", {
10462
- entryId,
10463
- maxHops: 2,
10464
- limit: 3
10465
- });
10466
- if ((suggestions?.suggestions?.length ?? 0) > 0) {
10467
- 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");
10468
- lines.push(`
10469
- ## Suggested Links to Improve Quality
10470
- ${linkHints}`);
10471
- }
10472
- } catch {
10473
- }
10474
- }
10475
10284
  async function handleCheck(entryId) {
10476
10285
  const res = await kernelQuery("quality.getLatestVerdictForEntry", { entryId });
10477
10286
  if (!res || !res.entryFound) {
@@ -10491,7 +10300,6 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10491
10300
  "",
10492
10301
  `No quality verdict yet for \`${entryId}\`. Run \`quality action=re-evaluate entryId="${entryId}"\` to evaluate it.`
10493
10302
  ];
10494
- await appendLinkSuggestions(entryId, lines2);
10495
10303
  return successResult(
10496
10304
  lines2.join("\n"),
10497
10305
  `No quality verdict yet for ${entryId} \u2014 re-evaluate to produce one.`,
@@ -10512,7 +10320,6 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10512
10320
  const verdictSection = formatRubricVerdictSection(verdict);
10513
10321
  lines.push(verdictSection || `Verdict tier \`${verdict.tier ?? "unknown"}\` for \`${entryId}\` \u2014 no rubric criteria to report.`);
10514
10322
  }
10515
- await appendLinkSuggestions(entryId, lines);
10516
10323
  try {
10517
10324
  const wsForTracking = await getWorkspaceContext();
10518
10325
  trackQualityCheck(wsForTracking.workspaceId, {
@@ -10541,9 +10348,7 @@ Entry \`${entryId}\` not found. Use \`search\` to find the right ID.`,
10541
10348
  ...res.stale ? {} : { passed: verdict.passed },
10542
10349
  criteria: res.stale ? [] : criteria.map((c) => ({ id: c.id, passed: c.passed, hint: c.hint }))
10543
10350
  },
10544
- [
10545
- { tool: "relations", description: "Suggest connections", parameters: { action: "suggest", entryId } }
10546
- ]
10351
+ []
10547
10352
  );
10548
10353
  }
10549
10354
  async function handleReEvaluate(entryId, context) {
@@ -10632,25 +10437,6 @@ function composeWrapupView(raw) {
10632
10437
  }
10633
10438
 
10634
10439
  // src/tools/wrapup.ts
10635
- async function mapWithConcurrency(items, mapper, concurrency = 3) {
10636
- const results = new Array(items.length);
10637
- let index = 0;
10638
- async function runNext() {
10639
- while (index < items.length) {
10640
- const i = index++;
10641
- try {
10642
- const value = await mapper(items[i]);
10643
- results[i] = { status: "fulfilled", value };
10644
- } catch (reason) {
10645
- results[i] = { status: "rejected", reason };
10646
- }
10647
- }
10648
- }
10649
- await Promise.all(
10650
- Array.from({ length: Math.min(concurrency, items.length) }, () => runNext())
10651
- );
10652
- return results;
10653
- }
10654
10440
  function isValidTopGap(g) {
10655
10441
  if (typeof g !== "object" || g === null) return false;
10656
10442
  const obj = g;
@@ -10672,40 +10458,13 @@ async function fetchSessionCoherenceSnapshot(sessionId) {
10672
10458
  }
10673
10459
  return null;
10674
10460
  }
10675
- async function suggestLinksForEntries(entries) {
10676
- const suggestions = [];
10677
- const results = await mapWithConcurrency(
10678
- entries,
10679
- async (entry) => {
10680
- const result2 = await kernelQuery("chain.graphSuggestLinks", {
10681
- entryId: entry.entryId,
10682
- limit: 3
10683
- });
10684
- return { entry, suggestions: result2.suggestions ?? [] };
10685
- },
10686
- 3
10687
- );
10688
- for (const result2 of results) {
10689
- if (result2.status !== "fulfilled") continue;
10690
- const { entry, suggestions: entrySuggestions } = result2.value;
10691
- for (const s of entrySuggestions.filter((x) => x.entryId != null).slice(0, 2)) {
10692
- suggestions.push({
10693
- fromEntryId: entry.entryId,
10694
- toEntryId: s.entryId,
10695
- type: s.recommendedRelationType,
10696
- label: `${entry.entryId} \u2192 ${s.entryId} (${s.name}) [${s.recommendedRelationType}]`
10697
- });
10698
- }
10699
- }
10700
- return suggestions;
10701
- }
10702
10461
  async function runWrapupReview() {
10703
10462
  const sessionId = getAgentSessionId();
10704
10463
  if (!sessionId) {
10705
- return { text: "No active agent session. Call `session action=start` first.", data: null, suggestions: [], failureCode: "SESSION_REQUIRED" };
10464
+ return { text: "No active agent session. Call `session action=start` first.", data: null, failureCode: "SESSION_REQUIRED" };
10706
10465
  }
10707
10466
  if (getApiKeyScope() === "read") {
10708
- return { text: "Read-only session \u2014 nothing to review.", data: null, suggestions: [], failureCode: "READONLY_SCOPE" };
10467
+ return { text: "Read-only session \u2014 nothing to review.", data: null, failureCode: "READONLY_SCOPE" };
10709
10468
  }
10710
10469
  const { workspaceId } = await getWorkspaceContext();
10711
10470
  const kernelEnvelope = await kernelCallEnvelope("agentKnowledge.wrapupEnvelope", {
@@ -10718,10 +10477,10 @@ async function runWrapupReview() {
10718
10477
  const overflowUnscanned = data.overflowUnscanned ?? 0;
10719
10478
  const loopEmpty = data.drafts.length === 0 && overflowDraftCount === 0 && overflowUnscanned === 0;
10720
10479
  if (data.wrapupCompletedAt && loopEmpty) {
10721
- return { text: "Wrapup already completed this session.", data, suggestions: [], failureCode: "ALREADY_COMPLETED" };
10480
+ return { text: "Wrapup already completed this session.", data, failureCode: "ALREADY_COMPLETED" };
10722
10481
  }
10723
10482
  if (data.drafts.length === 0 && data.committed.length === 0 && overflowDraftCount === 0 && (data.overflowCommitted ?? []).length === 0) {
10724
- return { text: "Empty session \u2014 no entries to review.", data, suggestions: [], failureCode: "NOT_FOUND" };
10483
+ return { text: "Empty session \u2014 no entries to review.", data, failureCode: "NOT_FOUND" };
10725
10484
  }
10726
10485
  const lines = [];
10727
10486
  lines.push(
@@ -10766,17 +10525,6 @@ async function runWrapupReview() {
10766
10525
  }
10767
10526
  lines.push("");
10768
10527
  }
10769
- const lowConnEntries = [...data.drafts, ...data.committed].filter(
10770
- (e) => (data.connectionCounts[e.entryId] ?? 0) < 2
10771
- );
10772
- const suggestions = lowConnEntries.length > 0 ? await suggestLinksForEntries(lowConnEntries.slice(0, 10)) : [];
10773
- if (suggestions.length > 0) {
10774
- lines.push("### Suggested Links (not written; wrapup-commit writes none \u2014 create the ones you want with `relations action=create`)", "");
10775
- for (const s of suggestions) {
10776
- lines.push(`- ${s.label}`);
10777
- }
10778
- lines.push("");
10779
- }
10780
10528
  const topGaps = getTopGaps(5);
10781
10529
  if (topGaps.length > 0) {
10782
10530
  lines.push("### Learning Opportunities", "");
@@ -10875,7 +10623,6 @@ async function runWrapupReview() {
10875
10623
  return {
10876
10624
  text: lines.join("\n"),
10877
10625
  data,
10878
- suggestions,
10879
10626
  gapCount,
10880
10627
  coherenceVerdict,
10881
10628
  persistentGaps,
@@ -11008,7 +10755,7 @@ function createWrapupHandler() {
11008
10755
  commitNext
11009
10756
  );
11010
10757
  }
11011
- const { text, data, suggestions, failureCode, gapCount, coherenceVerdict, persistentGaps, kernelSummary, kernelNext, kernelMeta } = await runWrapupReview();
10758
+ const { text, data, failureCode, gapCount, coherenceVerdict, persistentGaps, kernelSummary, kernelNext, kernelMeta } = await runWrapupReview();
11012
10759
  lastReviewData = data;
11013
10760
  lastReviewSessionId = getAgentSessionId();
11014
10761
  const composed = data ? composeWrapupView(data) : null;
@@ -11025,7 +10772,7 @@ ${text}` : text;
11025
10772
  const persistentSummary = persistentGaps?.length ? `, ${persistentGaps.length} persistent coherence gaps` : "";
11026
10773
  const draftsCount = composed?.drafts.length ?? data.drafts.length;
11027
10774
  const committedCount = composed?.committed.length ?? data.committed.length;
11028
- const summaryText = `Session review: ${draftsCount} uncommitted, ${committedCount} committed, ${suggestions.length} link suggestions${gapsSummary}${coherenceSummary}${persistentSummary}.`;
10775
+ const summaryText = `Session review: ${draftsCount} uncommitted, ${committedCount} committed${gapsSummary}${coherenceSummary}${persistentSummary}.`;
11029
10776
  return {
11030
10777
  content: [{ type: "text", text: fullText }],
11031
10778
  structuredContent: {
@@ -11035,7 +10782,6 @@ ${text}` : text;
11035
10782
  drafts: draftsCount,
11036
10783
  committed: committedCount,
11037
10784
  uncommitted: data.summary.uncommitted,
11038
- suggestedLinks: suggestions.length,
11039
10785
  knowledgeGaps: gapCount ?? 0,
11040
10786
  ...coherenceVerdict ? { coherenceVerdict } : {},
11041
10787
  ...persistentGaps?.length ? { persistentCoherenceGaps: persistentGaps.length } : {}
@@ -11408,7 +11154,7 @@ function registerSessionTools(server) {
11408
11154
  "session",
11409
11155
  {
11410
11156
  title: "Session \u2014 Lifecycle & Facilitation",
11411
- 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, suggested links. 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.",
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.",
11412
11158
  inputSchema: sessionSchema,
11413
11159
  // Mixed read/write noun (§3 R3): wrapup-commit/resume/commit-constellation write.
11414
11160
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
@@ -13147,7 +12893,7 @@ var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
13147
12893
  | \`orient\` | Workspace context, governance, active work packages, session start | \`action\`: start, task |
13148
12894
  | \`capture\` | Create entry | \`action\`: capture |
13149
12895
  | \`entries\` | Read and write entries | \`action\`: list, get, batch, search, update, commit, history, move, verify, preflight, still-true |
13150
- | \`relations\` | Link entries, dismiss suggestions and delete links, find and suggest | \`action\`: create, batch-create, dismiss, delete, find, suggest |
12896
+ | \`relations\` | Link entries, delete links, find connections | \`action\`: create, batch-create, delete, find |
13151
12897
  | \`context\` | Gather related knowledge, last-verified brief | \`action\`: gather, build, neighborhood, changes, chain, cross-cut, incremental, brief, last-verified-brief |
13152
12898
  | \`collections\` | Browse, describe, audit and export collections; manage labels | \`action\`: list, update, describe, audit, export, label-* |
13153
12899
  | \`quality\` | Score an entry, verify the Chain, spine check | \`action\`: check, re-evaluate, verify-chain, spine-check |
@@ -13184,7 +12930,7 @@ Write via \`update\`: \`draft\` | \`archived\` (\`accepted\` via \`commit\`, nev
13184
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.
13185
12931
 
13186
12932
  ## Key Patterns
13187
- - **Capture flow:** \`capture\` \u2192 \`relations action=suggest\` \u2192 \`relations action=batch-create\` \u2192 \`entries action=commit\`
12933
+ - **Capture flow:** \`capture\` \u2192 \`relations action=batch-create\` \u2192 \`entries action=commit\`
13188
12934
  - Use \`workflowStatus\` (not \`status\`) for domain workflow state
13189
12935
  - \`data\` param is merged (not replaced) \u2014 safe for partial updates
13190
12936
  `;
@@ -13226,13 +12972,13 @@ Tags for filtering (e.g. \`severity:high\`), relations via \`entryRelations\`, v
13226
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.'
13227
12973
  );
13228
12974
  sections.push(
13229
- "## 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=suggest` \u2014 discover potential connections for an entry\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:** When creating or updating entries in governed collections, always use `relations action=suggest` to discover and create relevant relations."
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`."
13230
12976
  );
13231
12977
  sections.push(
13232
12978
  `## Creating Knowledge
13233
12979
  **Entries:** Use \`capture\` as the primary tool for creating new entries. It handles the full workflow in one call:
13234
12980
  1. Creates the entry with collection-aware defaults (auto-fills dates, infers domains, sets priority)
13235
- 2. Writes the relations you pass in \`links\` \u2014 and no others; use \`relations action=suggest\` then \`relations action=create\` to add more
12981
+ 2. Writes the relations you pass in \`links\` \u2014 and no others; use \`relations action=create\` to add more
13236
12982
  3. Returns the server's quality verdict with improvement hints
13237
12983
 
13238
12984
  **Smart profiles** exist for: ${SMART_PROFILE_COLLECTIONS.map((c) => `\`${c}\``).join(", ")}.
@@ -13249,14 +12995,14 @@ Use \`quality action=check\` to score existing entries retroactively.
13249
12995
  Use \`entries action=update\` for post-creation adjustments (status changes, field updates, deprecation).`
13250
12996
  );
13251
12997
  sections.push(
13252
- "## 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- **Discover links** \u2192 `relations action=suggest`\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`"
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`"
13253
12999
  );
13254
13000
  return sections.join("\n\n---\n\n");
13255
13001
  }
13256
- async function buildOrientationResourceText(kernelQuery2, agentSessionId) {
13002
+ async function buildOrientationResourceText(kernelQuery3, agentSessionId) {
13257
13003
  const [collectionsResult, orientViewResult] = await Promise.allSettled([
13258
- kernelQuery2("chain.listCollections"),
13259
- kernelQuery2("chain.getOrientView", {
13004
+ kernelQuery3("chain.listCollections"),
13005
+ kernelQuery3("chain.getOrientView", {
13260
13006
  tier: "summary",
13261
13007
  invocationPath: "unknown",
13262
13008
  ...agentSessionId ? { sessionId: agentSessionId } : {}
@@ -13837,10 +13583,10 @@ var INSTRUCTIONS = [
13837
13583
  "",
13838
13584
  "## Writing",
13839
13585
  "",
13840
- "Write actions (capture, entries update and commit, relations create, batch-create, dismiss and delete) need an active session and a readwrite key.",
13586
+ "Write actions (capture, entries update and commit, relations create, batch-create and delete) need an active session and a readwrite key.",
13841
13587
  "Pass the `sessionId` that `session action=start` returned on every write: the connection does not remember it.",
13842
13588
  "Use `capture` to create an entry and read its reply: an entry that came back accepted needs no `entries action=commit`.",
13843
- "Link with `relations action=suggest`, then `relations action=create`.",
13589
+ "Link entries with `relations action=create` \u2014 a relation is written only when you ask, with exactly the verb you send.",
13844
13590
  "Finish with `session action=wrapup-review`, then `session action=close`.",
13845
13591
  "If the work falls outside the active work packages, name the gap and ask the user before designing it.",
13846
13592
  "",
@@ -13938,4 +13684,4 @@ export {
13938
13684
  createProductBrainServer,
13939
13685
  initFeatureFlags
13940
13686
  };
13941
- //# sourceMappingURL=chunk-LPG7TJRX.js.map
13687
+ //# sourceMappingURL=chunk-OEONSR7Q.js.map