@productbrain/mcp 0.0.1-beta.7317 → 0.0.1-beta.7330
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-CPX4DW3V.js → chunk-CPOQXPOQ.js} +51 -152
- package/dist/chunk-CPOQXPOQ.js.map +1 -0
- package/dist/{chunk-2RHVPN24.js → chunk-SU5XEDCF.js} +1 -4
- package/dist/chunk-SU5XEDCF.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-XUZMANEX.js → setup-UNAVGLP5.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-2RHVPN24.js.map +0 -1
- package/dist/chunk-CPX4DW3V.js.map +0 -1
- /package/dist/{setup-XUZMANEX.js.map → setup-UNAVGLP5.js.map} +0 -0
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
trackSessionCaptureRate,
|
|
42
42
|
trackWriteBackHintServed,
|
|
43
43
|
trackZeroCaptureAuditFired
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-SU5XEDCF.js";
|
|
45
45
|
|
|
46
46
|
// src/server.ts
|
|
47
47
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -6312,6 +6312,8 @@ var COLLECTIONS_ACTIONS = [
|
|
|
6312
6312
|
"label-apply",
|
|
6313
6313
|
"label-remove"
|
|
6314
6314
|
];
|
|
6315
|
+
var STATUS_CATEGORIES = ["draft", "current", "past", "discarded"];
|
|
6316
|
+
var statusListEntrySchema = z16.object({ label: z16.string().max(200), category: z16.enum(STATUS_CATEGORIES) });
|
|
6315
6317
|
var qualityCriterionSchema = z16.object({
|
|
6316
6318
|
field: z16.string().max(200).describe("Entry data field key this criterion applies to, e.g. 'description', 'owner'"),
|
|
6317
6319
|
// WP-480 S1: `max_length` — must mirror the Convex rule union, or the tool rejects a
|
|
@@ -6329,17 +6331,12 @@ var fieldSchema = z16.object({
|
|
|
6329
6331
|
displayHint: z16.enum(["hero", "badge", "meta", "section", "hidden", "inline-meta"]).optional().describe("V2 rendering hint: how the field should be displayed in Cortex"),
|
|
6330
6332
|
zone: z16.enum(["header", "body", "meta"]).optional().describe("V2 layout zone: where the field appears in the entry view"),
|
|
6331
6333
|
colorMap: z16.record(z16.string().max(50)).optional().describe("V2 value-to-semantic-color mapping, e.g. { critical: 'danger', low: 'success' }"),
|
|
6332
|
-
// ENT-61
|
|
6333
6334
|
accentSource: z16.boolean().optional().describe("When true, this field's colorMap value drives the card-level accent styling"),
|
|
6334
|
-
// ENT-61
|
|
6335
6335
|
iconMap: z16.record(z16.string(), z16.string().max(50)).optional().describe("Maps field values to icons (emoji/symbol), prepended to badge text"),
|
|
6336
6336
|
helpText: z16.string().max(2e3).optional().describe("Help text shown in editors and describe output"),
|
|
6337
6337
|
optionDescriptions: z16.record(z16.string().max(500)).optional().describe("Per-option guidance for select fields"),
|
|
6338
|
-
// BET-136
|
|
6339
6338
|
semanticRole: z16.enum(["problem", "appetite", "elements", "architecture", "done_when", "risks", "exclusions"]).optional().describe("Semantic role for schema-driven consumers \u2014 enables field-key-independent validation and rendering"),
|
|
6340
|
-
// BET-196
|
|
6341
6339
|
maxLength: z16.number().optional().describe("Maximum character length for field values. Three-tier resolution: explicit > displayHint > type fallback."),
|
|
6342
|
-
// BET-196
|
|
6343
6340
|
minLength: z16.number().optional().describe("Minimum character length for field values. Only explicit \u2014 no defaults.")
|
|
6344
6341
|
});
|
|
6345
6342
|
var collectionsSchema = z16.object({
|
|
@@ -6355,20 +6352,15 @@ var collectionsSchema = z16.object({
|
|
|
6355
6352
|
fields: z16.array(fieldSchema).max(200).optional().describe("Field definitions for create, or replacement schema for update (replaces all fields)"),
|
|
6356
6353
|
axis: z16.enum(["work", "none", "pending"]).optional().describe("REQUIRED for create: 'work' (needs stageLabelMap + validWorkflowStatuses), 'none', or 'pending'. For update: only settable on a non-system-stratum (custom) collection \u2014 the repair path for a legacy collection that predates the axis contract."),
|
|
6357
6354
|
stageLabelMap: z16.record(z16.string(), z16.enum(["open", "in_progress", "done", "stopped", "tag"])).optional().describe("Required when axis is 'work': maps each valid workflowStatus to a canonical stage."),
|
|
6358
|
-
// ENT-62
|
|
6359
6355
|
defaultCanonicalKey: z16.string().max(200).optional().describe("The canonical_key entries in this collection default to (e.g. 'decision', 'insight'). Consumers read from collection doc; code map is fallback."),
|
|
6360
|
-
// ENT-67
|
|
6361
6356
|
defaultWorkflowStatus: z16.string().max(200).optional().describe("Default workflowStatus for new entries. Must be in validWorkflowStatuses when set (e.g. 'hypothesis' for insights)."),
|
|
6362
|
-
// ENT-65
|
|
6363
6357
|
validWorkflowStatuses: z16.array(z16.string().max(200)).max(50).optional().describe("The allowed workflowStatus values for entries in this collection. New entries are validated against this list. Empty array means no constraint."),
|
|
6364
6358
|
// ENT-65, FEAT-200
|
|
6365
6359
|
classificationCheck: z16.string().max(500).optional().describe("LLM decision-tree check for this collection (3\u2013500 chars). Guides the classifier in routing entries here."),
|
|
6366
|
-
// ENT-65, FEAT-200
|
|
6367
6360
|
classificationPriority: z16.number().optional().describe("Classifier priority (1\u20139, lower = higher priority). Used with classificationCheck to order the decision tree."),
|
|
6368
|
-
// FEAT-301 Slice 2: quality gate criteria and usage guidance.
|
|
6369
|
-
// FEAT-257
|
|
6370
6361
|
qualityCriteria: z16.array(qualityCriterionSchema).max(50).optional().describe("Per-collection accept gate rules. 'required' rule hard-blocks accepts on empty fields; 'min_length'/'pattern' rules warn. Pass an empty array to clear all criteria."),
|
|
6371
6362
|
usageGuidance: z16.string().max(2e4).optional().describe("Plain-text guidance shown to agents and users: when to use this collection, when not to, and what makes a good entry."),
|
|
6363
|
+
statusList: z16.array(statusListEntrySchema).max(50).optional().describe(`Ordered status vocabulary: {label, category}[] (draft/current/past/discarded); draft first, exactly one 'discarded' labeled "discarded", labels unique. Re-validated server-side.`),
|
|
6372
6364
|
// For label-*
|
|
6373
6365
|
color: z16.string().max(50).optional().describe("For label-create/label-update: hex color, e.g. '#ef4444'."),
|
|
6374
6366
|
parentSlug: z16.string().max(200).optional().describe("For label-create: parent group slug for label hierarchy."),
|
|
@@ -6396,7 +6388,8 @@ var collectionsUpdateVariant = z16.object({
|
|
|
6396
6388
|
classificationCheck: z16.string().max(500).optional(),
|
|
6397
6389
|
classificationPriority: z16.number().optional(),
|
|
6398
6390
|
qualityCriteria: z16.array(qualityCriterionSchema).optional(),
|
|
6399
|
-
usageGuidance: z16.string().max(2e4).optional()
|
|
6391
|
+
usageGuidance: z16.string().max(2e4).optional(),
|
|
6392
|
+
statusList: z16.array(statusListEntrySchema).optional()
|
|
6400
6393
|
});
|
|
6401
6394
|
var collectionsAuditVariant = z16.object({ action: z16.literal("audit") });
|
|
6402
6395
|
var collectionsExportVariant = z16.object({ action: z16.literal("export") });
|
|
@@ -6439,7 +6432,7 @@ var collectionsActionUnion = z16.discriminatedUnion("action", [
|
|
|
6439
6432
|
var COLLECTIONS_ACTION_SPECS = {
|
|
6440
6433
|
list: { params: [], description: "Browse all collections; no params." },
|
|
6441
6434
|
describe: { params: ["slug"], description: "slug is required." },
|
|
6442
|
-
update: { params: ["slug", "name", "description", "purpose", "icon", "navGroup", "fields", "axis", "stageLabelMap", "defaultCanonicalKey", "defaultWorkflowStatus", "validWorkflowStatuses", "classificationCheck", "classificationPriority", "qualityCriteria", "usageGuidance"], description: "slug is required; at least one other field must be provided. axis/stageLabelMap are only settable on a non-system-stratum collection (the repair path for a legacy custom collection missing its axis declaration)." },
|
|
6435
|
+
update: { params: ["slug", "name", "description", "purpose", "icon", "navGroup", "fields", "axis", "stageLabelMap", "defaultCanonicalKey", "defaultWorkflowStatus", "validWorkflowStatuses", "classificationCheck", "classificationPriority", "qualityCriteria", "usageGuidance", "statusList"], description: "slug is required; at least one other field must be provided. axis/stageLabelMap are only settable on a non-system-stratum collection (the repair path for a legacy custom collection missing its axis declaration)." },
|
|
6443
6436
|
audit: { params: [], description: "Health report for all collections; no params." },
|
|
6444
6437
|
export: { params: [], description: "Full system_collection_definitions export; no params. Admin only." },
|
|
6445
6438
|
"label-list": { params: [], description: "List all labels; no params." },
|
|
@@ -6454,9 +6447,9 @@ function registerCollectionsTools(server) {
|
|
|
6454
6447
|
list: () => handleList2(),
|
|
6455
6448
|
describe: (data) => handleDescribe(data.slug),
|
|
6456
6449
|
update: (data) => {
|
|
6457
|
-
const hasChanges = data.name || data.description || data.purpose || data.icon || data.navGroup || data.fields || data.axis !== void 0 || data.stageLabelMap !== void 0 || data.defaultCanonicalKey !== void 0 || data.defaultWorkflowStatus !== void 0 || data.validWorkflowStatuses !== void 0 || data.classificationCheck !== void 0 || data.classificationPriority !== void 0 || data.qualityCriteria !== void 0 || data.usageGuidance !== void 0;
|
|
6450
|
+
const hasChanges = data.name || data.description || data.purpose || data.icon || data.navGroup || data.fields || data.axis !== void 0 || data.stageLabelMap !== void 0 || data.defaultCanonicalKey !== void 0 || data.defaultWorkflowStatus !== void 0 || data.validWorkflowStatuses !== void 0 || data.classificationCheck !== void 0 || data.classificationPriority !== void 0 || data.qualityCriteria !== void 0 || data.usageGuidance !== void 0 || data.statusList;
|
|
6458
6451
|
if (!hasChanges) {
|
|
6459
|
-
return Promise.resolve(validationResult("Provide at least one field to update (name, description, purpose, icon, navGroup, fields, axis, stageLabelMap, defaultCanonicalKey, defaultWorkflowStatus, validWorkflowStatuses, classificationCheck, classificationPriority, qualityCriteria, or
|
|
6452
|
+
return Promise.resolve(validationResult("Provide at least one field to update (name, description, purpose, icon, navGroup, fields, axis, stageLabelMap, defaultCanonicalKey, defaultWorkflowStatus, validWorkflowStatuses, classificationCheck, classificationPriority, qualityCriteria, usageGuidance, or statusList)."));
|
|
6460
6453
|
}
|
|
6461
6454
|
return handleUpdate(
|
|
6462
6455
|
data.slug,
|
|
@@ -6466,7 +6459,7 @@ function registerCollectionsTools(server) {
|
|
|
6466
6459
|
data.icon,
|
|
6467
6460
|
data.navGroup,
|
|
6468
6461
|
data.fields,
|
|
6469
|
-
{ axis: data.axis, stageLabelMap: data.stageLabelMap, defaultCanonicalKey: data.defaultCanonicalKey, defaultWorkflowStatus: data.defaultWorkflowStatus, validWorkflowStatuses: data.validWorkflowStatuses, classificationCheck: data.classificationCheck, classificationPriority: data.classificationPriority, qualityCriteria: data.qualityCriteria, usageGuidance: data.usageGuidance }
|
|
6462
|
+
{ axis: data.axis, stageLabelMap: data.stageLabelMap, defaultCanonicalKey: data.defaultCanonicalKey, defaultWorkflowStatus: data.defaultWorkflowStatus, validWorkflowStatuses: data.validWorkflowStatuses, classificationCheck: data.classificationCheck, classificationPriority: data.classificationPriority, qualityCriteria: data.qualityCriteria, usageGuidance: data.usageGuidance, statusList: data.statusList }
|
|
6470
6463
|
);
|
|
6471
6464
|
},
|
|
6472
6465
|
audit: () => handleAudit(),
|
|
@@ -6482,7 +6475,7 @@ function registerCollectionsTools(server) {
|
|
|
6482
6475
|
"collections",
|
|
6483
6476
|
{
|
|
6484
6477
|
title: "Collections",
|
|
6485
|
-
description: "Manage knowledge collections and workspace labels. Eleven actions:\n\n- **list**: Browse all collections \u2014 glossary, business rules, tracking events, etc. Returns slug, name, description, and field schema. Use before capture to see what exists.\n- **describe**: Full documentation for a single collection. Returns field help text, option decision guides, usage guidance, examples, and cross-references. Use when you need to understand a collection's purpose, field semantics, or option values.\n- **update**: Update an existing collection's name, description, purpose, icon, navGroup, fields, or (non-system collections only) axis/stageLabelMap. Only provide the fields you want to change. Creating a NEW collection is not offered here \u2014 ask a platform admin to provision it, or enable the module ontology pack that owns the drawer.\n- **audit**: Health report for all workspace collections. Checks classification metadata, icon presence, displayHint coverage per field, and field schema gaps vs system definitions. Returns total collections, count with issues, and per-collection issue list.\n- **export**: Full system_collection_definitions export as JSON. Includes all classification metadata (thinkingLayer, classificationPriority, classificationCheck, timelineRole, governanceRole, governanceFunction, etc.). Admin only.\n- **label-list/label-create/label-update/label-delete/label-apply/label-remove**: manage workspace labels (absorbs the `labels` tool) \u2014 list all labels, create/update/delete a label, or apply/remove a label on an entry. Labels can be applied to any entry across any collection for cross-domain filtering.",
|
|
6478
|
+
description: "Manage knowledge collections and workspace labels. Eleven actions:\n\n- **list**: Browse all collections \u2014 glossary, business rules, tracking events, etc. Returns slug, name, description, and field schema. Use before capture to see what exists.\n- **describe**: Full documentation for a single collection. Returns field help text, option decision guides, usage guidance, examples, and cross-references. Use when you need to understand a collection's purpose, field semantics, or option values.\n- **update**: Update an existing collection's name, description, purpose, icon, navGroup, fields, statusList, or (non-system collections only) axis/stageLabelMap. Only provide the fields you want to change. Creating a NEW collection is not offered here \u2014 ask a platform admin to provision it, or enable the module ontology pack that owns the drawer.\n- **audit**: Health report for all workspace collections. Checks classification metadata, icon presence, displayHint coverage per field, and field schema gaps vs system definitions. Returns total collections, count with issues, and per-collection issue list.\n- **export**: Full system_collection_definitions export as JSON. Includes all classification metadata (thinkingLayer, classificationPriority, classificationCheck, timelineRole, governanceRole, governanceFunction, etc.). Admin only.\n- **label-list/label-create/label-update/label-delete/label-apply/label-remove**: manage workspace labels (absorbs the `labels` tool) \u2014 list all labels, create/update/delete a label, or apply/remove a label on an entry. Labels can be applied to any entry across any collection for cross-domain filtering.",
|
|
6486
6479
|
inputSchema: collectionsSchema,
|
|
6487
6480
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
6488
6481
|
},
|
|
@@ -6537,6 +6530,12 @@ Valid \`workflowStatus\` values for entries in this collection:
|
|
|
6537
6530
|
${col.validWorkflowStatuses.map((s) => `- ${s}`).join("\n")}`
|
|
6538
6531
|
);
|
|
6539
6532
|
}
|
|
6533
|
+
if (col.statusList && col.statusList.length > 0) {
|
|
6534
|
+
sections.push(`
|
|
6535
|
+
## Status Vocabulary
|
|
6536
|
+
|
|
6537
|
+
${col.statusList.map((s) => `- \`${s.label}\` \u2192 ${s.category}`).join("\n")}`);
|
|
6538
|
+
}
|
|
6540
6539
|
if (col.fields.length > 0) {
|
|
6541
6540
|
const fieldDocs = col.fields.map((f) => {
|
|
6542
6541
|
const parts = [];
|
|
@@ -6586,6 +6585,8 @@ ${refList}`);
|
|
|
6586
6585
|
usageGuidance: col.usageGuidance ?? null,
|
|
6587
6586
|
// WP-480 S2: null = unconstrained by design (STD-34), not "unknown".
|
|
6588
6587
|
validWorkflowStatuses: col.validWorkflowStatuses && col.validWorkflowStatuses.length > 0 ? col.validWorkflowStatuses : null,
|
|
6588
|
+
statusList: col.statusList && col.statusList.length > 0 ? col.statusList : null,
|
|
6589
|
+
// T2: same null-means-undeclared posture
|
|
6589
6590
|
fieldCount: col.fields.length,
|
|
6590
6591
|
fields: col.fields.map((f) => ({
|
|
6591
6592
|
key: f.key,
|
|
@@ -6659,7 +6660,7 @@ async function handleUpdate(slug, name, description, purpose, icon, navGroup, fi
|
|
|
6659
6660
|
invalidateCollectionCacheForScope();
|
|
6660
6661
|
invalidateProfileCacheForScope();
|
|
6661
6662
|
invalidateHubCacheForScope();
|
|
6662
|
-
const changes = [name && "name", description && "description", purpose && "purpose", icon && "icon", navGroup && "navGroup", fields && "fields", meta.axis !== void 0 && "axis", meta.stageLabelMap !== void 0 && "stageLabelMap", meta.defaultCanonicalKey !== void 0 && "defaultCanonicalKey", meta.defaultWorkflowStatus !== void 0 && "defaultWorkflowStatus", meta.validWorkflowStatuses !== void 0 && "validWorkflowStatuses", meta.classificationCheck !== void 0 && "classificationCheck", meta.classificationPriority !== void 0 && "classificationPriority", meta.qualityCriteria !== void 0 && "qualityCriteria", meta.usageGuidance !== void 0 && "usageGuidance"].filter(Boolean).join(", ");
|
|
6663
|
+
const changes = [name && "name", description && "description", purpose && "purpose", icon && "icon", navGroup && "navGroup", fields && "fields", meta.axis !== void 0 && "axis", meta.stageLabelMap !== void 0 && "stageLabelMap", meta.defaultCanonicalKey !== void 0 && "defaultCanonicalKey", meta.defaultWorkflowStatus !== void 0 && "defaultWorkflowStatus", meta.validWorkflowStatuses !== void 0 && "validWorkflowStatuses", meta.classificationCheck !== void 0 && "classificationCheck", meta.classificationPriority !== void 0 && "classificationPriority", meta.qualityCriteria !== void 0 && "qualityCriteria", meta.usageGuidance !== void 0 && "usageGuidance", meta.statusList !== void 0 && "statusList"].filter(Boolean).join(", ");
|
|
6663
6664
|
return {
|
|
6664
6665
|
content: [{
|
|
6665
6666
|
type: "text",
|
|
@@ -7910,10 +7911,6 @@ function formatNoticeLines(notices) {
|
|
|
7910
7911
|
(n) => n.source === QUESTION_ACK_GATE_SOURCE_ID && n.payload ? formatQuestionAckGateBlock(n) : [`- ${safe(n.subject)} (${safe(n.change)})`]
|
|
7911
7912
|
);
|
|
7912
7913
|
}
|
|
7913
|
-
function formatAdoptDiscoveryLine(display) {
|
|
7914
|
-
if (!display) return [];
|
|
7915
|
-
return [`- ${display} open question${display === "1" ? "" : "s"} you raised have no owner yet \u2014 use \`question action=adopt\` to route one.`];
|
|
7916
|
-
}
|
|
7917
7914
|
|
|
7918
7915
|
// src/lib/terminalStatuses.ts
|
|
7919
7916
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -12317,10 +12314,6 @@ async function handleStart2(data) {
|
|
|
12317
12314
|
if (noticeLines.length > 0) {
|
|
12318
12315
|
lines.push("", "Notices:", ...noticeLines);
|
|
12319
12316
|
}
|
|
12320
|
-
const adoptLines = formatAdoptDiscoveryLine(result2.adoptDiscoveryHint);
|
|
12321
|
-
if (adoptLines.length > 0) {
|
|
12322
|
-
lines.push("", ...adoptLines);
|
|
12323
|
-
}
|
|
12324
12317
|
const summary = `Session ${result2.sessionId} active. Workspace ${result2.workspaceName}. Write tools available after orient.`;
|
|
12325
12318
|
return {
|
|
12326
12319
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
@@ -12333,8 +12326,7 @@ async function handleStart2(data) {
|
|
|
12333
12326
|
roleName: result2.roleName,
|
|
12334
12327
|
feedbackQueueNew: result2.feedbackQueueNew,
|
|
12335
12328
|
feedbackOldestNewAt: result2.feedbackOldestNewAt,
|
|
12336
|
-
notices: result2.notices ?? []
|
|
12337
|
-
adoptDiscoveryHint: result2.adoptDiscoveryHint
|
|
12329
|
+
notices: result2.notices ?? []
|
|
12338
12330
|
// Finding #16: bare `parameters: {}` lets orient's schema default kick in
|
|
12339
12331
|
// (action defaults to "task", which requires a `task` string) — the suggested
|
|
12340
12332
|
// next-action call failed validation instead of running. `action: "start"` is
|
|
@@ -13570,45 +13562,34 @@ function registerShapeTools(server) {
|
|
|
13570
13562
|
|
|
13571
13563
|
// src/tools/question.ts
|
|
13572
13564
|
import { z as z32 } from "zod/v3";
|
|
13573
|
-
var QUESTION_ACTIONS = ["create", "
|
|
13565
|
+
var QUESTION_ACTIONS = ["create", "assign", "answer", "force-close", "list"];
|
|
13574
13566
|
var questionSchema = z32.object({
|
|
13575
13567
|
action: z32.enum(QUESTION_ACTIONS).describe(
|
|
13576
|
-
"'create': create a new routed question from text
|
|
13568
|
+
"'create': create a new routed question from text \u2014 the only birth path now; every question is born escalated to the team lead. 'assign': reassign ownership to a role (toRole required \u2014 no bare self-confirm, no toPerson). 'answer': close the loop \u2014 creates the answering entry itself unless --entry is given. 'force-close': Team-Lead-only escape hatch \u2014 reason required. 'list': the ack-gate read model \u2014 what you owe (or, with mine:true, every question you asked, including closed ones)."
|
|
13577
13569
|
),
|
|
13578
13570
|
entryId: z32.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
|
|
13579
13571
|
text: z32.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
|
|
13583
|
-
toPerson: z32.string().max(200).optional().describe("'assign': reassign to this person (a raw person id)."),
|
|
13584
|
-
reason: z32.string().max(2e3).optional().describe("'decline'/'force-close': required reason."),
|
|
13572
|
+
blocks: z32.array(z32.string().max(200)).max(50).optional().describe("'create': entry ids this question is blocking (repeatable)."),
|
|
13573
|
+
toRole: z32.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id) \u2014 required."),
|
|
13574
|
+
reason: z32.string().max(2e3).optional().describe("'force-close': required reason."),
|
|
13585
13575
|
answerEntryId: z32.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
|
|
13586
13576
|
mine: z32.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
|
|
13587
13577
|
});
|
|
13588
|
-
var createVariant = z32.object({ action: z32.literal("create"), text: z32.string().min(1).max(5e3),
|
|
13589
|
-
var
|
|
13590
|
-
var assignVariant = z32.object({ action: z32.literal("assign"), entryId: z32.string().max(200), toRole: z32.string().max(200).optional(), toPerson: z32.string().max(200).optional() });
|
|
13591
|
-
var snoozeVariant = z32.object({ action: z32.literal("snooze"), entryId: z32.string().max(200) });
|
|
13592
|
-
var declineVariant = z32.object({ action: z32.literal("decline"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
|
|
13578
|
+
var createVariant = z32.object({ action: z32.literal("create"), text: z32.string().min(1).max(5e3), blocks: z32.array(z32.string().max(200)).max(50).optional() });
|
|
13579
|
+
var assignVariant = z32.object({ action: z32.literal("assign"), entryId: z32.string().max(200), toRole: z32.string().max(200).min(1) });
|
|
13593
13580
|
var answerVariant = z32.object({ action: z32.literal("answer"), entryId: z32.string().max(200), text: z32.string().max(2e3).optional(), answerEntryId: z32.string().max(200).optional() });
|
|
13594
13581
|
var forceCloseVariant = z32.object({ action: z32.literal("force-close"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
|
|
13595
13582
|
var listVariant = z32.object({ action: z32.literal("list"), mine: z32.boolean().optional() });
|
|
13596
13583
|
var questionActionUnion = z32.discriminatedUnion("action", [
|
|
13597
13584
|
createVariant,
|
|
13598
|
-
adoptVariant,
|
|
13599
13585
|
assignVariant,
|
|
13600
|
-
snoozeVariant,
|
|
13601
|
-
declineVariant,
|
|
13602
13586
|
answerVariant,
|
|
13603
13587
|
forceCloseVariant,
|
|
13604
13588
|
listVariant
|
|
13605
13589
|
]);
|
|
13606
13590
|
var QUESTION_ACTION_SPECS = {
|
|
13607
|
-
create: { params: ["text", "
|
|
13608
|
-
|
|
13609
|
-
assign: { params: ["entryId", "toRole", "toPerson"], description: "entryId is required; omit toRole/toPerson to self-confirm, or supply one/both to reassign." },
|
|
13610
|
-
snooze: { params: ["entryId"], description: "entryId is required." },
|
|
13611
|
-
decline: { params: ["entryId", "reason"], description: "entryId and reason are both required." },
|
|
13591
|
+
create: { params: ["text", "blocks"], description: "text is required; blocks is optional." },
|
|
13592
|
+
assign: { params: ["entryId", "toRole"], description: "entryId and toRole are both required \u2014 assign is reassign-to-a-role only." },
|
|
13612
13593
|
answer: { params: ["entryId", "text", "answerEntryId"], description: "entryId is required; supply exactly one of text or answerEntryId." },
|
|
13613
13594
|
"force-close": { params: ["entryId", "reason"], description: "entryId and reason are both required." },
|
|
13614
13595
|
list: { params: ["mine"], description: "mine is optional \u2014 omit/false for the ack-gate view, true for every question you asked." }
|
|
@@ -13629,114 +13610,35 @@ async function displayName(id) {
|
|
|
13629
13610
|
return void 0;
|
|
13630
13611
|
}
|
|
13631
13612
|
}
|
|
13632
|
-
async function
|
|
13633
|
-
|
|
13634
|
-
}
|
|
13635
|
-
function domainListSuffix(domainSlugs) {
|
|
13636
|
-
if (!domainSlugs) return "";
|
|
13637
|
-
return domainSlugs.length > 0 ? ` Domains in this workspace: ${domainSlugs.join(", ")}.` : " No domains exist in this workspace yet.";
|
|
13638
|
-
}
|
|
13639
|
-
async function renderCreateAdoptConfirmation(result2, isAdopt) {
|
|
13640
|
-
const lead = isAdopt ? `${result2.entryId} is now a routed question,` : "Question created and";
|
|
13641
|
-
let base;
|
|
13642
|
-
if (result2.status === "unrouted") {
|
|
13643
|
-
const roleName = result2.suggestedRoleId ? await unresolvableRoleFallback(result2.suggestedRoleId) : result2.suggestedRoleId;
|
|
13644
|
-
base = isAdopt ? `${lead} suggested to ${roleName}.` : `${lead} suggested to ${roleName}. Confirm with: pb question assign ${result2.entryId}`;
|
|
13645
|
-
} else {
|
|
13646
|
-
const leadName = result2.ownerRoleId ? await unresolvableRoleFallback(result2.ownerRoleId) : result2.ownerRoleId;
|
|
13647
|
-
base = isAdopt ? `${result2.entryId} is now a routed question. Nothing here says who owns it, so it has gone to ${leadName}.` : `Question created. Nothing here says who owns it, so it has gone to ${leadName}.`;
|
|
13648
|
-
}
|
|
13613
|
+
async function renderCreateConfirmation(result2) {
|
|
13614
|
+
const leadName = result2.ownerRoleId ? await displayName(result2.ownerRoleId) ?? `an unresolved role (\`${result2.ownerRoleId}\` \u2014 reassign it)` : result2.ownerRoleId;
|
|
13615
|
+
const base = `Question created. Nothing here says who owns it, so it has gone to ${leadName}.`;
|
|
13649
13616
|
const blocksHint = " If something is blocked on this, add blocks:[<entry-id>...].";
|
|
13650
13617
|
const nudge = result2.nudge ? ` ${result2.nudge}` : "";
|
|
13651
|
-
return `${base}${
|
|
13618
|
+
return `${base}${blocksHint}${nudge}`;
|
|
13652
13619
|
}
|
|
13653
|
-
async function handleQuestionCreate(text,
|
|
13620
|
+
async function handleQuestionCreate(text, blocks) {
|
|
13654
13621
|
requireWriteAccess();
|
|
13655
13622
|
try {
|
|
13656
13623
|
const resolvedBlocks = blocks ? await Promise.all(blocks.map((b) => resolveEntryRef(b, "Blocked entry"))) : void 0;
|
|
13657
|
-
const result2 = await kernelMutation("question.create", { text,
|
|
13658
|
-
const message = await
|
|
13659
|
-
const next = result2.status === "unrouted" ? [{ tool: "question", description: "Confirm or reassign the suggestion", parameters: { action: "assign", entryId: result2.entryId } }] : void 0;
|
|
13660
|
-
return successResult(message, message, result2, next);
|
|
13661
|
-
} catch (err) {
|
|
13662
|
-
return questionErrorResult(err, "create", { text, domain, blocks });
|
|
13663
|
-
}
|
|
13664
|
-
}
|
|
13665
|
-
async function handleQuestionAdopt(entryId, domain, blocks) {
|
|
13666
|
-
requireWriteAccess();
|
|
13667
|
-
try {
|
|
13668
|
-
const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
|
|
13669
|
-
const resolvedBlocks = blocks ? await Promise.all(blocks.map((b) => resolveEntryRef(b, "Blocked entry"))) : void 0;
|
|
13670
|
-
const result2 = await kernelMutation("question.adopt", { entryId: resolvedEntryId, domainSlug: domain, blocks: resolvedBlocks, sessionId: getAgentSessionId() ?? void 0 });
|
|
13671
|
-
const message = await renderCreateAdoptConfirmation(result2, true);
|
|
13624
|
+
const result2 = await kernelMutation("question.create", { text, blocks: resolvedBlocks, sessionId: getAgentSessionId() ?? void 0 });
|
|
13625
|
+
const message = await renderCreateConfirmation(result2);
|
|
13672
13626
|
return successResult(message, message, result2);
|
|
13673
13627
|
} catch (err) {
|
|
13674
|
-
return questionErrorResult(err, "
|
|
13628
|
+
return questionErrorResult(err, "create", { text, blocks });
|
|
13675
13629
|
}
|
|
13676
13630
|
}
|
|
13677
|
-
async function handleQuestionAssign(entryId, toRole
|
|
13631
|
+
async function handleQuestionAssign(entryId, toRole) {
|
|
13678
13632
|
requireWriteAccess();
|
|
13679
13633
|
try {
|
|
13680
13634
|
const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
|
|
13681
|
-
const resolvedToRoleId =
|
|
13682
|
-
const result2 = await kernelMutation("question.assign", { entryId: resolvedEntryId, toRoleId: resolvedToRoleId
|
|
13683
|
-
const
|
|
13684
|
-
|
|
13685
|
-
if (isReassign) {
|
|
13686
|
-
const who = toPerson ? stripControlChars(result2.ownerPersonName ?? "") || result2.ownerPersonId || "the target" : await displayName(result2.ownerRoleId) ?? result2.ownerRoleId ?? "the target";
|
|
13687
|
-
message = `${entryId} reassigned to ${who}. They'll see it at their next session.`;
|
|
13688
|
-
} else {
|
|
13689
|
-
message = `${entryId} is yours now.`;
|
|
13690
|
-
}
|
|
13691
|
-
return successResult(message, message, result2);
|
|
13692
|
-
} catch (err) {
|
|
13693
|
-
return questionErrorResult(err, "assign", { entryId, toRole, toPerson });
|
|
13694
|
-
}
|
|
13695
|
-
}
|
|
13696
|
-
async function handleQuestionSnooze(entryId) {
|
|
13697
|
-
requireWriteAccess();
|
|
13698
|
-
try {
|
|
13699
|
-
const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
|
|
13700
|
-
const result2 = await kernelMutation("question.snooze", { entryId: resolvedEntryId });
|
|
13701
|
-
let message;
|
|
13702
|
-
if (result2.snoozeUntil) {
|
|
13703
|
-
message = `Snoozed until ${new Date(result2.snoozeUntil).toISOString().slice(0, 10)}. ${result2.snoozeCount ?? "?"} of 2 snoozes used.`;
|
|
13704
|
-
} else if (result2.status === "escalated") {
|
|
13705
|
-
const leadName = await displayName(result2.ownerRoleId) ?? "the team lead";
|
|
13706
|
-
message = `You have used both snoozes on this question \u2014 it has been escalated to ${leadName}.`;
|
|
13707
|
-
} else {
|
|
13708
|
-
message = "No snoozes left \u2014 answer it or force-close it.";
|
|
13709
|
-
}
|
|
13710
|
-
return successResult(message, message, result2);
|
|
13711
|
-
} catch (err) {
|
|
13712
|
-
return questionErrorResult(err, "snooze", { entryId });
|
|
13713
|
-
}
|
|
13714
|
-
}
|
|
13715
|
-
async function handleQuestionDecline(entryId, reason) {
|
|
13716
|
-
requireWriteAccess();
|
|
13717
|
-
try {
|
|
13718
|
-
const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
|
|
13719
|
-
const result2 = await kernelMutation("question.decline", { entryId: resolvedEntryId, reason });
|
|
13720
|
-
let message;
|
|
13721
|
-
if (result2.status === "processed") {
|
|
13722
|
-
message = "Declined and closed.";
|
|
13723
|
-
} else if (result2.isRoot === true) {
|
|
13724
|
-
message = `Declined \u2014 but ${result2.dependentsCount ?? 0} item(s) still depend on it, so it stays with you. Answer it or force-close it.`;
|
|
13725
|
-
} else if (result2.isRoot === false) {
|
|
13726
|
-
const leadName = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId;
|
|
13727
|
-
message = `Declined and escalated to ${leadName}.`;
|
|
13728
|
-
} else if (result2.suggestedRoleId) {
|
|
13729
|
-
const roleName = await displayName(result2.suggestedRoleId) ?? result2.suggestedRoleId;
|
|
13730
|
-
message = `Passed. ${entryId} is now suggested to ${roleName}.`;
|
|
13731
|
-
} else if (result2.ownerRoleId) {
|
|
13732
|
-
const leadName = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId;
|
|
13733
|
-
message = `Passed. Nobody else here owns this, so it has gone to ${leadName}.`;
|
|
13734
|
-
} else {
|
|
13735
|
-
message = "Declined.";
|
|
13736
|
-
}
|
|
13635
|
+
const resolvedToRoleId = await resolveEntryRef(toRole, "Role");
|
|
13636
|
+
const result2 = await kernelMutation("question.assign", { entryId: resolvedEntryId, toRoleId: resolvedToRoleId });
|
|
13637
|
+
const who = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId ?? "the target";
|
|
13638
|
+
const message = `${entryId} reassigned to ${who}. They'll see it at their next session.`;
|
|
13737
13639
|
return successResult(message, message, result2);
|
|
13738
13640
|
} catch (err) {
|
|
13739
|
-
return questionErrorResult(err, "
|
|
13641
|
+
return questionErrorResult(err, "assign", { entryId, toRole });
|
|
13740
13642
|
}
|
|
13741
13643
|
}
|
|
13742
13644
|
async function handleQuestionAnswer(entryId, text, answerEntryId) {
|
|
@@ -13753,7 +13655,7 @@ async function handleQuestionAnswer(entryId, text, answerEntryId) {
|
|
|
13753
13655
|
const result2 = await kernelMutation("question.answer", { entryId: resolvedEntryId, text, answerEntryId: resolvedAnswerEntryId });
|
|
13754
13656
|
const askerName = (result2.askerName ? stripControlChars(result2.askerName) : "") || "The asker";
|
|
13755
13657
|
const pendingNote = result2.answerPending ? " The answer is a draft awaiting accept." : "";
|
|
13756
|
-
const message = result2.idempotent ? result2.processedVia === "
|
|
13658
|
+
const message = result2.idempotent ? result2.processedVia === "force_closed" ? `${entryId} is already processed (force-closed) \u2014 no change.` : result2.processedVia === "declined" ? `${entryId} is already processed (declined) \u2014 no change.` : `${entryId} is already answered \u2014 no change.` : `Answered. ${askerName} will see the outcome at their next session (and in pb question list --mine).${pendingNote}`;
|
|
13757
13659
|
return successResult(message, message, result2);
|
|
13758
13660
|
} catch (err) {
|
|
13759
13661
|
return questionErrorResult(err, "answer", { entryId, text, answerEntryId });
|
|
@@ -13799,18 +13701,15 @@ function questionErrorResult(err, action, params = {}) {
|
|
|
13799
13701
|
classified.message,
|
|
13800
13702
|
classified.code,
|
|
13801
13703
|
classified.message,
|
|
13802
|
-
classified.recovery ?? "Use action='create'
|
|
13704
|
+
classified.recovery ?? "Use action='create', or `pb question --help` / this tool's description, to find valid next steps.",
|
|
13803
13705
|
classified.availableActions ?? fallbackActions,
|
|
13804
13706
|
classified.diagnostics
|
|
13805
13707
|
);
|
|
13806
13708
|
}
|
|
13807
13709
|
function registerQuestionTools(server) {
|
|
13808
13710
|
const questionHandlers = {
|
|
13809
|
-
create: (data) => handleQuestionCreate(data.text, data.
|
|
13810
|
-
|
|
13811
|
-
assign: (data) => handleQuestionAssign(data.entryId, data.toRole, data.toPerson),
|
|
13812
|
-
snooze: (data) => handleQuestionSnooze(data.entryId),
|
|
13813
|
-
decline: (data) => handleQuestionDecline(data.entryId, data.reason),
|
|
13711
|
+
create: (data) => handleQuestionCreate(data.text, data.blocks),
|
|
13712
|
+
assign: (data) => handleQuestionAssign(data.entryId, data.toRole),
|
|
13814
13713
|
answer: (data) => handleQuestionAnswer(data.entryId, data.text, data.answerEntryId),
|
|
13815
13714
|
"force-close": (data) => handleQuestionForceClose(data.entryId, data.reason),
|
|
13816
13715
|
list: (data) => handleQuestionList(data.mine)
|
|
@@ -13819,7 +13718,7 @@ function registerQuestionTools(server) {
|
|
|
13819
13718
|
"question",
|
|
13820
13719
|
{
|
|
13821
13720
|
title: "Question",
|
|
13822
|
-
description: "Routed questions \u2014 a question with no owner
|
|
13721
|
+
description: "Routed questions \u2014 a question is born with no owner and escalates straight to the team lead; the team lead reassigns it to a role. Five actions:\n\n- **create**: create a new routed question from text. Always born escalated to the team lead \u2014 there is no domain-owner suggestion any more.\n- **assign**: reassign ownership to a role (`toRole` required \u2014 no self-confirm, no person-only reassignment).\n- **answer**: close the loop. Creates the answering entry itself from `text`, or points at an existing one via `answerEntryId` \u2014 never both.\n- **force-close**: Team-Lead-only escape hatch (reason required) for a question that cannot otherwise be resolved.\n- **list**: the ack-gate read model \u2014 what you owe (`mine:false`, the default), or (`mine:true`) every question you asked, including closed ones, with its outcome.\n\n`blocks` (repeatable, on create) records what a question is holding up \u2014 pass entry ids it blocks.",
|
|
13823
13722
|
inputSchema: questionSchema,
|
|
13824
13723
|
annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false }
|
|
13825
13724
|
},
|
|
@@ -14189,7 +14088,7 @@ var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
|
|
|
14189
14088
|
| \`workspace\` | Check / whoami / status / self-test, usage, governance proposals | \`action\`: check, whoami, status, audit, self-test, usage, proposals-* |
|
|
14190
14089
|
| \`feedback\` | Submit product feedback (any key, no session required); \`list\` reads back your own workspace's feedback (no gate); vendor triage: queue / note / group / status | \`action\`: submit, list, queue, note, group, status |
|
|
14191
14090
|
| \`shape\` | Review / disposition write-shape (single-concern split) advisories | \`action\`: list, show, agree, dismiss |
|
|
14192
|
-
| \`question\` | Routed questions \u2014 create
|
|
14091
|
+
| \`question\` | Routed questions \u2014 create, reassign, answer, force-close, list | \`action\`: create, assign, answer, force-close, list |
|
|
14193
14092
|
| \`role\` | What a role is accountable for \u2014 owns, reports on, and (not yet computed) inherits via Team-Lead vacancy coverage | \`action\`: accountable |
|
|
14194
14093
|
| \`outcome\` | Record an outcome measurement and read the ones already recorded | \`action\`: record, list, show |
|
|
14195
14094
|
|
|
@@ -14989,4 +14888,4 @@ export {
|
|
|
14989
14888
|
createProductBrainServer,
|
|
14990
14889
|
initFeatureFlags
|
|
14991
14890
|
};
|
|
14992
|
-
//# sourceMappingURL=chunk-
|
|
14891
|
+
//# sourceMappingURL=chunk-CPOQXPOQ.js.map
|