@productbrain/mcp 0.0.1-beta.43 → 0.0.1-beta.44

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.
@@ -25,11 +25,11 @@ import {
25
25
  startAgentSession,
26
26
  trackWriteTool,
27
27
  translateStaleToolNames
28
- } from "./chunk-PQP27A3R.js";
28
+ } from "./chunk-E6ZTRA6N.js";
29
29
  import {
30
30
  trackQualityCheck,
31
31
  trackQualityVerdict
32
- } from "./chunk-BIDMZOLE.js";
32
+ } from "./chunk-MRIO53BY.js";
33
33
 
34
34
  // src/server.ts
35
35
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -203,7 +203,7 @@ ${formatted}` }]
203
203
  },
204
204
  async ({ entryId }) => {
205
205
  requireWriteAccess();
206
- const { runContradictionCheck } = await import("./smart-capture-W2IALMJ5.js");
206
+ const { runContradictionCheck } = await import("./smart-capture-3BWER4RY.js");
207
207
  const entry = await mcpQuery("chain.getEntry", { entryId });
208
208
  if (!entry) {
209
209
  return {
@@ -3724,10 +3724,11 @@ function computeCommitBlockers(opts) {
3724
3724
  }
3725
3725
  for (const draft of sessionDrafts) {
3726
3726
  if (!draft.name || draft.name.trim().length === 0) {
3727
+ const draftRef = draft.entryId && draft.entryId.trim().length > 0 ? draft.entryId : "<draftId>";
3727
3728
  blockers.push({
3728
3729
  entryId: betEntryId,
3729
3730
  blocker: `Draft constellation entry is missing a name`,
3730
- fix: `update-entry entryId="<draftId>" name="<name>"`
3731
+ fix: `update-entry entryId="${draftRef}" name="<name>"`
3731
3732
  });
3732
3733
  }
3733
3734
  }
@@ -3778,7 +3779,7 @@ var captureItemSchema = z12.object({
3778
3779
  });
3779
3780
  var facilitateSchema = z12.object({
3780
3781
  action: z12.enum(FACILITATE_ACTIONS).describe(
3781
- "'start': create draft bet and begin session. 'respond': process user input, score, capture, return coaching. 'score': return current scorecard without advancing. 'resume': reconstruct session from existing bet entry. 'commit-constellation': atomically commit a bet and all its linked draft entries in one call. Requires betEntryId."
3782
+ "'start': begin shaping session context (entry created on first respond). 'respond': process user input, score, capture, return coaching. 'score': return current scorecard without advancing. 'resume': reconstruct session from existing bet entry. 'commit-constellation': atomically commit a bet and all its linked draft entries in one call. Requires betEntryId."
3782
3783
  ),
3783
3784
  sessionType: z12.enum(["shape"]).default("shape").optional().describe("Session type. Only 'shape' in v1."),
3784
3785
  betEntryId: z12.string().optional().describe("Bet entry ID. Required for resume/score. For respond: omit on first call after start (entry created automatically); required for subsequent calls."),
@@ -3797,7 +3798,7 @@ function registerFacilitateTools(server) {
3797
3798
  "facilitate",
3798
3799
  {
3799
3800
  title: "Facilitate \u2014 Coached Shaping",
3800
- description: "Server-controlled coached shaping session with real-time Chain capture.\n\n- **start**: Create a draft bet on the Chain and begin a coached session. Returns Studio URL, initial context, and first coaching prompt.\n- **respond**: Process user input \u2014 scores against 7 shaping rubrics (problem, appetite, elements, architecture, risks, boundaries, done-when), searches for overlap, captures to Chain, returns structured coaching response with phase tracking.\n- **score**: Return the current scorecard without advancing the session.\n- **resume**: Reconstruct session state from an existing bet entry on the Chain.\n- **commit-constellation**: Atomically commit a bet and all its linked draft entries (features, tensions, decisions) in one call. Validates strategy link and required fields before committing anything. Replaces 9-13 sequential commit-entry calls.\n\nThe structured response separates judgment (server) from coaching (agent) per DEC-56. Read the phase, scorecard, and coaching fields to determine what to say next. When captureReady is true, buildContract is auto-generated from Chain governance.",
3801
+ description: "Server-controlled coached shaping session with real-time Chain capture.\n\n- **start**: Begin a coached session context only. No bet entry is created yet.\n- **first respond**: Call respond with `betName` to create the draft bet and capture initial problem context.\n- **respond**: Process user input \u2014 scores against 7 shaping rubrics (problem, appetite, elements, architecture, risks, boundaries, done-when), searches for overlap, captures to Chain, returns structured coaching response with phase tracking.\n- **score**: Return the current scorecard without advancing the session.\n- **resume**: Reconstruct session state from an existing bet entry on the Chain.\n- **commit-constellation**: Atomically commit a bet and all its linked draft entries (features, tensions, decisions) in one call. Validates strategy link and required fields before committing anything. Replaces 9-13 sequential commit-entry calls.\n\nThe structured response separates judgment (server) from coaching (agent) per DEC-56. Read the phase, scorecard, and coaching fields to determine what to say next. When captureReady is true, buildContract is auto-generated from Chain governance.",
3801
3802
  inputSchema: facilitateSchema,
3802
3803
  annotations: {
3803
3804
  readOnlyHint: false,
@@ -3989,8 +3990,12 @@ async function loadConstellationState(betEntryId, betInternalId) {
3989
3990
  const elementCount = relations.filter(
3990
3991
  (r) => r.type === "part_of" && r.toId === internalId
3991
3992
  ).length;
3992
- const riskCount = relations.filter((r) => r.type === "constrains").length;
3993
- const decisionCount = relations.filter((r) => r.type === "informs").length;
3993
+ const riskCount = relations.filter(
3994
+ (r) => r.type === "constrains" && r.toId === internalId
3995
+ ).length;
3996
+ const decisionCount = relations.filter(
3997
+ (r) => r.type === "informs" && r.toId === internalId
3998
+ ).length;
3994
3999
  return { relations, elementCount, riskCount, decisionCount };
3995
4000
  } catch {
3996
4001
  return { relations: [], elementCount: 0, riskCount: 0, decisionCount: 0 };
@@ -4173,208 +4178,166 @@ async function handleStart2(args) {
4173
4178
  structuredContent: response
4174
4179
  };
4175
4180
  }
4176
- async function handleRespond(args) {
4177
- requireWriteAccess();
4178
- const { userInput, betEntryId: argBetId, dimension: argDimension, capture: rawCapture, source: argSource, betName: argBetName } = args;
4179
- const source = argSource ?? "user";
4180
- const captureItems = rawCapture ? Array.isArray(rawCapture) ? rawCapture : [rawCapture] : [];
4181
- if (!userInput) {
4182
- return {
4183
- content: [{ type: "text", text: "`userInput` is required for respond action." }]
4184
- };
4185
- }
4181
+ async function processCaptures(opts) {
4182
+ const { captureItems, betEntryId, betDocId, betData } = opts;
4186
4183
  const captureErrors = [];
4187
4184
  const entriesCreated = [];
4188
4185
  let relationsCreated = 0;
4189
- let betId = argBetId;
4190
- if (!betId) {
4191
- const betName2 = argBetName ?? "Untitled Bet (Shaping)";
4192
- const agentId = getAgentSessionId();
4186
+ const capAgentId = getAgentSessionId();
4187
+ const collectionMap = {
4188
+ element: { slug: "features", dataField: "description", relationType: "part_of" },
4189
+ risk: { slug: "tensions", dataField: "description", relationType: "constrains" },
4190
+ decision: { slug: "decisions", dataField: "rationale", relationType: "informs" }
4191
+ };
4192
+ let runningBetData = { ...betData };
4193
+ for (const item of captureItems) {
4194
+ if (item.type === "noGo") {
4195
+ const updatedNoGos = appendNoGo(
4196
+ runningBetData.noGos,
4197
+ { title: item.name, explanation: item.description }
4198
+ );
4199
+ runningBetData.noGos = updatedNoGos;
4200
+ try {
4201
+ await mcpMutation("chain.updateEntry", {
4202
+ entryId: betEntryId,
4203
+ data: { noGos: updatedNoGos },
4204
+ changeNote: `Added no-go: ${item.name}`
4205
+ });
4206
+ await recordSessionActivity({ entryModified: betDocId });
4207
+ } catch (updErr) {
4208
+ captureErrors.push({
4209
+ operation: "update",
4210
+ detail: `noGos field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4211
+ });
4212
+ }
4213
+ continue;
4214
+ }
4215
+ const mapping = collectionMap[item.type];
4216
+ if (!mapping) continue;
4217
+ let capturedEntryId = null;
4193
4218
  try {
4194
4219
  const result = await mcpMutation(
4195
4220
  "chain.createEntry",
4196
4221
  {
4197
- collectionSlug: "bets",
4198
- name: betName2,
4222
+ collectionSlug: mapping.slug,
4223
+ name: item.name,
4199
4224
  status: "draft",
4200
- data: {
4201
- problem: userInput,
4202
- appetite: "",
4203
- elements: "",
4204
- rabbitHoles: "",
4205
- noGos: "",
4206
- architecture: "",
4207
- buildContract: "",
4208
- description: `Shaping session for: ${betName2}`,
4209
- status: "shaping",
4210
- shapingSessionActive: true
4211
- },
4212
- createdBy: agentId ? `agent:${agentId}` : "facilitate",
4213
- sessionId: agentId ?? void 0
4225
+ data: { [mapping.dataField]: item.description },
4226
+ createdBy: capAgentId ? `agent:${capAgentId}` : "facilitate",
4227
+ sessionId: capAgentId ?? void 0
4214
4228
  }
4215
4229
  );
4216
- betId = result.entryId;
4230
+ capturedEntryId = result.entryId;
4231
+ entriesCreated.push(result.entryId);
4217
4232
  await recordSessionActivity({ entryCreated: result.docId });
4218
- } catch (err) {
4219
- const msg = err instanceof Error ? err.message : String(err);
4220
- return {
4221
- content: [{ type: "text", text: `Failed to create bet entry: ${msg}` }],
4222
- isError: true
4223
- };
4224
- }
4225
- }
4226
- const [betEntry, constellation, chainSurfaced] = await Promise.all([
4227
- loadBetEntry(betId),
4228
- loadConstellationState(betId),
4229
- searchChain(userInput, { maxResults: 8 })
4230
- ]);
4231
- if (!betEntry) {
4232
- return {
4233
- content: [{ type: "text", text: `Bet \`${betId}\` not found. Use start to create a session.` }]
4234
- };
4235
- }
4236
- const betData = betEntry.data ?? {};
4237
- if (captureItems.length > 0) {
4238
- const capAgentId = getAgentSessionId();
4239
- const collectionMap = {
4240
- element: { slug: "features", dataField: "description", relationType: "part_of" },
4241
- risk: { slug: "tensions", dataField: "description", relationType: "constrains" },
4242
- decision: { slug: "decisions", dataField: "rationale", relationType: "informs" }
4243
- };
4244
- let runningBetData = { ...betData };
4245
- for (const item of captureItems) {
4246
- if (item.type === "noGo") {
4247
- const updatedNoGos = appendNoGo(
4248
- runningBetData.noGos,
4249
- { title: item.name, explanation: item.description }
4233
+ } catch (createErr) {
4234
+ const msg = createErr instanceof Error ? createErr.message : String(createErr);
4235
+ if (msg.includes("Duplicate entry") || msg.includes("already exists")) {
4236
+ const fallback = await findAndLinkExisting(
4237
+ item.name,
4238
+ mapping.slug,
4239
+ betEntryId,
4240
+ mapping.relationType
4250
4241
  );
4251
- runningBetData.noGos = updatedNoGos;
4252
- try {
4253
- await mcpMutation("chain.updateEntry", {
4254
- entryId: betId,
4255
- data: { noGos: updatedNoGos },
4256
- changeNote: `Added no-go: ${item.name}`
4257
- });
4258
- await recordSessionActivity({ entryModified: betEntry._id });
4259
- } catch (updErr) {
4242
+ if (fallback) {
4243
+ capturedEntryId = fallback.entryId;
4244
+ entriesCreated.push(fallback.entryId);
4245
+ if (fallback.linked) relationsCreated++;
4260
4246
  captureErrors.push({
4261
- operation: "update",
4262
- detail: `noGos field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4247
+ operation: "info",
4248
+ detail: `Linked existing ${mapping.slug} entry \`${fallback.entryId}\` instead of creating duplicate.`
4263
4249
  });
4264
- }
4265
- continue;
4266
- }
4267
- const mapping = collectionMap[item.type];
4268
- if (!mapping) continue;
4269
- let capturedEntryId = null;
4270
- try {
4271
- const result = await mcpMutation(
4272
- "chain.createEntry",
4273
- {
4274
- collectionSlug: mapping.slug,
4275
- name: item.name,
4276
- status: "draft",
4277
- data: { [mapping.dataField]: item.description },
4278
- createdBy: capAgentId ? `agent:${capAgentId}` : "facilitate",
4279
- sessionId: capAgentId ?? void 0
4280
- }
4281
- );
4282
- capturedEntryId = result.entryId;
4283
- entriesCreated.push(result.entryId);
4284
- await recordSessionActivity({ entryCreated: result.docId });
4285
- } catch (createErr) {
4286
- const msg = createErr instanceof Error ? createErr.message : String(createErr);
4287
- if (msg.includes("Duplicate entry") || msg.includes("already exists")) {
4288
- const fallback = await findAndLinkExisting(
4289
- item.name,
4290
- mapping.slug,
4291
- betId,
4292
- mapping.relationType
4293
- );
4294
- if (fallback) {
4295
- capturedEntryId = fallback.entryId;
4296
- entriesCreated.push(fallback.entryId);
4297
- if (fallback.linked) relationsCreated++;
4298
- captureErrors.push({
4299
- operation: "info",
4300
- detail: `Linked existing ${mapping.slug} entry \`${fallback.entryId}\` instead of creating duplicate.`
4301
- });
4302
- } else {
4303
- captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
4304
- }
4305
4250
  } else {
4306
4251
  captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
4307
4252
  }
4253
+ } else {
4254
+ captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
4255
+ }
4256
+ }
4257
+ if (!capturedEntryId) continue;
4258
+ try {
4259
+ await mcpMutation("chain.createEntryRelation", {
4260
+ fromEntryId: capturedEntryId,
4261
+ toEntryId: betEntryId,
4262
+ type: mapping.relationType
4263
+ });
4264
+ relationsCreated++;
4265
+ await recordSessionActivity({ relationCreated: true });
4266
+ } catch (relErr) {
4267
+ const msg = relErr instanceof Error ? relErr.message : String(relErr);
4268
+ if (!msg.includes("already exists") && !msg.includes("Duplicate")) {
4269
+ captureErrors.push({
4270
+ operation: "relation",
4271
+ detail: `${mapping.relationType} from ${capturedEntryId} to ${betEntryId}: ${msg}`
4272
+ });
4308
4273
  }
4309
- if (!capturedEntryId) continue;
4274
+ }
4275
+ if (item.type === "element") {
4276
+ const updatedElements = appendElement(
4277
+ runningBetData.elements,
4278
+ { name: item.name, description: item.description, entryId: capturedEntryId }
4279
+ );
4280
+ runningBetData.elements = updatedElements;
4310
4281
  try {
4311
- await mcpMutation("chain.createEntryRelation", {
4312
- fromEntryId: capturedEntryId,
4313
- toEntryId: betId,
4314
- type: mapping.relationType
4282
+ await mcpMutation("chain.updateEntry", {
4283
+ entryId: betEntryId,
4284
+ data: { elements: updatedElements },
4285
+ changeNote: `Added element: ${item.name}`
4286
+ });
4287
+ await recordSessionActivity({ entryModified: betDocId });
4288
+ } catch (updErr) {
4289
+ captureErrors.push({
4290
+ operation: "update",
4291
+ detail: `elements field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4315
4292
  });
4316
- relationsCreated++;
4317
- await recordSessionActivity({ relationCreated: true });
4318
- } catch (relErr) {
4319
- const msg = relErr instanceof Error ? relErr.message : String(relErr);
4320
- if (!msg.includes("already exists") && !msg.includes("Duplicate")) {
4321
- captureErrors.push({
4322
- operation: "relation",
4323
- detail: `${mapping.relationType} from ${capturedEntryId} to ${betId}: ${msg}`
4324
- });
4325
- }
4326
4293
  }
4327
- if (item.type === "element") {
4328
- const updatedElements = appendElement(
4329
- runningBetData.elements,
4330
- { name: item.name, description: item.description, entryId: capturedEntryId }
4331
- );
4332
- runningBetData.elements = updatedElements;
4333
- try {
4334
- await mcpMutation("chain.updateEntry", {
4335
- entryId: betId,
4336
- data: { elements: updatedElements },
4337
- changeNote: `Added element: ${item.name}`
4338
- });
4339
- await recordSessionActivity({ entryModified: betEntry._id });
4340
- } catch (updErr) {
4341
- captureErrors.push({
4342
- operation: "update",
4343
- detail: `elements field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4344
- });
4345
- }
4346
- } else if (item.type === "risk") {
4347
- const updatedRisks = appendRabbitHole(
4348
- runningBetData.rabbitHoles,
4349
- { name: item.name, description: item.description, theme: item.theme, entryId: capturedEntryId }
4350
- );
4351
- runningBetData.rabbitHoles = updatedRisks;
4352
- try {
4353
- await mcpMutation("chain.updateEntry", {
4354
- entryId: betId,
4355
- data: { rabbitHoles: updatedRisks },
4356
- changeNote: `Added risk: ${item.name}`
4357
- });
4358
- await recordSessionActivity({ entryModified: betEntry._id });
4359
- } catch (updErr) {
4360
- captureErrors.push({
4361
- operation: "update",
4362
- detail: `rabbitHoles field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4363
- });
4364
- }
4294
+ } else if (item.type === "risk") {
4295
+ const updatedRisks = appendRabbitHole(
4296
+ runningBetData.rabbitHoles,
4297
+ { name: item.name, description: item.description, theme: item.theme, entryId: capturedEntryId }
4298
+ );
4299
+ runningBetData.rabbitHoles = updatedRisks;
4300
+ try {
4301
+ await mcpMutation("chain.updateEntry", {
4302
+ entryId: betEntryId,
4303
+ data: { rabbitHoles: updatedRisks },
4304
+ changeNote: `Added risk: ${item.name}`
4305
+ });
4306
+ await recordSessionActivity({ entryModified: betDocId });
4307
+ } catch (updErr) {
4308
+ captureErrors.push({
4309
+ operation: "update",
4310
+ detail: `rabbitHoles field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
4311
+ });
4365
4312
  }
4366
4313
  }
4367
4314
  }
4368
- const refreshedBet = await loadBetEntry(betId);
4315
+ return { entriesCreated, relationsCreated, captureErrors };
4316
+ }
4317
+ async function computeAndUpdateScores(opts) {
4318
+ const {
4319
+ betEntryId,
4320
+ betDocId,
4321
+ betData,
4322
+ betEntry,
4323
+ userInput,
4324
+ argDimension,
4325
+ source,
4326
+ captureItems,
4327
+ chainSurfaced,
4328
+ constellation
4329
+ } = opts;
4330
+ const captureErrors = [];
4331
+ const refreshedBet = await loadBetEntry(betEntryId);
4369
4332
  const refreshedData = refreshedBet?.data ?? betData;
4370
- const refreshedConstellation = captureItems.length > 0 ? await loadConstellationState(betId, refreshedBet?._id ?? betEntry._id) : constellation;
4371
- const sessionDrafts = await loadSessionDrafts(betId, refreshedBet?._id ?? betEntry._id);
4333
+ const refreshedConstellation = captureItems.length > 0 ? await loadConstellationState(betEntryId, refreshedBet?._id ?? betEntry._id) : constellation;
4334
+ const sessionDrafts = await loadSessionDrafts(betEntryId, refreshedBet?._id ?? betEntry._id);
4372
4335
  const constellationEntryIds = sessionDrafts.map((d) => d.entryId);
4373
4336
  const alreadyCheckedOverlap = typeof refreshedData._overlapIds === "string";
4374
4337
  let overlap = [];
4375
4338
  if (!alreadyCheckedOverlap && captureItems.length === 0) {
4376
4339
  const problemText = refreshedData.problem ?? userInput;
4377
- const overlapResults = await searchChain(problemText, { maxResults: 5, excludeIds: [betId, ...constellationEntryIds] });
4340
+ const overlapResults = await searchChain(problemText, { maxResults: 5, excludeIds: [betEntryId, ...constellationEntryIds] });
4378
4341
  overlap = overlapResults.map((r) => ({
4379
4342
  entryId: r.entryId,
4380
4343
  similarity: "text_match",
@@ -4428,7 +4391,7 @@ async function handleRespond(args) {
4428
4391
  }
4429
4392
  if (Object.keys(fieldUpdates).length > 0) {
4430
4393
  await mcpMutation("chain.updateEntry", {
4431
- entryId: betId,
4394
+ entryId: betEntryId,
4432
4395
  data: fieldUpdates,
4433
4396
  changeNote: `Updated ${Object.keys(fieldUpdates).join(", ")} from shaping session`
4434
4397
  });
@@ -4451,12 +4414,10 @@ async function handleRespond(args) {
4451
4414
  const observation = dimensionResult.satisfied.length > 0 ? `${DIMENSION_LABELS[activeDimension]}: ${dimensionResult.satisfied.join("; ")}` : `${DIMENSION_LABELS[activeDimension]}: needs more detail`;
4452
4415
  const pushback = captureItems.length === 0 && overlap.length > 0 ? `${overlap[0].summary} already exists on the Chain. How does your bet differ?` : dimensionResult.missing.length > 0 ? dimensionResult.missing[0] : "";
4453
4416
  const suggestedQuestion = dimensionResult.missing.length > 1 ? dimensionResult.missing[1] : dimensionResult.missing.length > 0 ? dimensionResult.missing[0] : `${DIMENSION_LABELS[nextDimension]} is next \u2014 ready to move on?`;
4454
- const wsCtx = await getWorkspaceContext();
4455
- const studioUrl = buildStudioUrl(wsCtx.workspaceSlug, betId);
4456
4417
  let buildContract;
4457
4418
  if (captureReady) {
4458
4419
  const contractCtx = {
4459
- betEntryId: betId,
4420
+ betEntryId,
4460
4421
  governanceEntries: chainSurfaced.filter((e) => ["principles", "standards", "business-rules"].includes(e.collection)).map((e) => ({ entryId: e.entryId, name: e.name, collection: e.collection })),
4461
4422
  relatedDecisions: chainSurfaced.filter((e) => e.collection === "decisions").map((e) => ({ entryId: e.entryId, name: e.name })),
4462
4423
  relatedTensions: chainSurfaced.filter((e) => e.collection === "tensions").map((e) => ({ entryId: e.entryId, name: e.name }))
@@ -4465,7 +4426,7 @@ async function handleRespond(args) {
4465
4426
  if (refreshedData.buildContract !== buildContract) {
4466
4427
  try {
4467
4428
  await mcpMutation("chain.updateEntry", {
4468
- entryId: betId,
4429
+ entryId: betEntryId,
4469
4430
  data: { buildContract },
4470
4431
  changeNote: "Updated build contract from shaping session"
4471
4432
  });
@@ -4481,7 +4442,7 @@ async function handleRespond(args) {
4481
4442
  let commitBlockers;
4482
4443
  if (captureReady) {
4483
4444
  commitBlockers = computeCommitBlockers({
4484
- betEntryId: betId,
4445
+ betEntryId,
4485
4446
  betDocId: refreshedBet?._id ?? betEntry._id,
4486
4447
  relations: refreshedConstellation.relations,
4487
4448
  betData: refreshedData,
@@ -4489,11 +4450,59 @@ async function handleRespond(args) {
4489
4450
  });
4490
4451
  if (commitBlockers.length === 0) commitBlockers = void 0;
4491
4452
  }
4453
+ return {
4454
+ scorecard,
4455
+ scorecardCriteria,
4456
+ phase,
4457
+ activeDimension,
4458
+ nextDimension,
4459
+ dimensionResult,
4460
+ overlap,
4461
+ sessionDrafts,
4462
+ alignment,
4463
+ coaching: { observation, pushback, suggestedQuestion, captureReady },
4464
+ completed,
4465
+ isSmallBatch,
4466
+ refreshedData,
4467
+ scoringCtx,
4468
+ captureErrors,
4469
+ buildContract,
4470
+ commitBlockers
4471
+ };
4472
+ }
4473
+ function assembleResponse(opts) {
4474
+ const {
4475
+ betEntryId,
4476
+ betName,
4477
+ workspaceSlug,
4478
+ scorecard,
4479
+ scorecardCriteria,
4480
+ phase,
4481
+ activeDimension,
4482
+ nextDimension,
4483
+ dimensionResult,
4484
+ coaching,
4485
+ overlap,
4486
+ sessionDrafts,
4487
+ alignment,
4488
+ chainSurfaced,
4489
+ scoringCtx,
4490
+ isSmallBatch,
4491
+ completed,
4492
+ refreshedData,
4493
+ buildContract,
4494
+ commitBlockers,
4495
+ entriesCreated,
4496
+ relationsCreated,
4497
+ captureErrors,
4498
+ captureItems
4499
+ } = opts;
4500
+ const studioUrl = buildStudioUrl(workspaceSlug, betEntryId);
4492
4501
  const suggested = suggestCaptures(scoringCtx, activeDimension);
4493
4502
  const betProblem = refreshedData.problem ?? "";
4494
- const betName = refreshedData.description ?? betEntry.name;
4503
+ const responseBetName = refreshedData.description ?? betName;
4495
4504
  const elementNames = (refreshedData.elements ?? "").match(/###\s*Element\s*\d+:\s*(.+)/gi)?.map((h) => h.replace(/###\s*Element\s*\d+:\s*/i, "").trim()) ?? [];
4496
- const investigationBrief = buildInvestigationBrief(phase, betName, betId, betProblem, elementNames) ?? void 0;
4505
+ const investigationBrief = buildInvestigationBrief(phase, responseBetName, betEntryId, betProblem, elementNames) ?? void 0;
4497
4506
  const response = {
4498
4507
  version: 2,
4499
4508
  phase,
@@ -4505,15 +4514,15 @@ async function handleRespond(args) {
4505
4514
  suggestedCaptures: suggested,
4506
4515
  sessionDrafts,
4507
4516
  coaching: {
4508
- observation,
4517
+ observation: coaching.observation,
4509
4518
  missing: dimensionResult.missing,
4510
- pushback,
4511
- suggestedQuestion,
4512
- captureReady,
4519
+ pushback: coaching.pushback,
4520
+ suggestedQuestion: coaching.suggestedQuestion,
4521
+ captureReady: coaching.captureReady,
4513
4522
  nextDimension
4514
4523
  },
4515
4524
  captured: {
4516
- betEntryId: betId,
4525
+ betEntryId,
4517
4526
  studioUrl,
4518
4527
  entriesCreated,
4519
4528
  relationsCreated
@@ -4525,6 +4534,7 @@ async function handleRespond(args) {
4525
4534
  suggestedOrder: activeDimensions(isSmallBatch)
4526
4535
  },
4527
4536
  buildContract,
4537
+ directive: buildDirective(scorecard, phase, betEntryId, coaching.captureReady, activeDimension, nextDimension) || void 0,
4528
4538
  investigationBrief,
4529
4539
  commitBlockers,
4530
4540
  scorecardCriteria
@@ -4536,39 +4546,141 @@ async function handleRespond(args) {
4536
4546
  };
4537
4547
  const expectedCaptureType = STRUCTURED_DIMS[activeDimension];
4538
4548
  const dataLossWarning = expectedCaptureType && captureItems.length === 0 ? `**WARNING:** You discussed ${activeDimension} but did not use the \`capture\` parameter \u2014 nothing was saved to the bet. Re-send with \`capture={type:"${expectedCaptureType}", name:"...", description:"..."}\` to persist this.` : "";
4539
- const directive = buildDirective(scorecard, phase, betId, captureReady, activeDimension, nextDimension);
4540
4549
  const summaryParts = [
4541
4550
  `# ${PHASE_LABELS[phase]} \u2014 ${DIMENSION_LABELS[activeDimension]}`,
4542
4551
  "",
4543
4552
  dataLossWarning,
4544
4553
  `**Score:** ${DIMENSION_LABELS[activeDimension]}: ${scorecard[activeDimension]}/10 \u2014 ${formatCriteriaLine(dimensionResult.criteria)}`,
4545
4554
  `**Phase:** ${PHASE_LABELS[phase]}`,
4546
- observation,
4547
- pushback ? `**Pushback:** ${pushback}` : "",
4548
- `**Next:** ${suggestedQuestion}`,
4549
- captureReady ? "\n**Capture ready** \u2014 the bet has enough shape to finalize." : "",
4555
+ coaching.observation,
4556
+ coaching.pushback ? `**Pushback:** ${coaching.pushback}` : "",
4557
+ `**Next:** ${coaching.suggestedQuestion}`,
4558
+ coaching.captureReady ? "\n**Capture ready** \u2014 the bet has enough shape to finalize." : "",
4550
4559
  commitBlockers?.length ? `
4551
4560
  \u26A0 **Commit blockers (${commitBlockers.length}):** ${commitBlockers.map((b) => `${b.blocker} \u2192 \`${b.fix}\``).join("; ")}` : "",
4552
4561
  entriesCreated.length > 0 ? `**Captured:** ${entriesCreated.join(", ")}` : "",
4553
4562
  suggested.length > 0 ? `**Suggest capturing:** ${suggested.map((s) => `${s.type}: "${s.name}"`).join("; ")}` : "",
4554
4563
  sessionDrafts.length > 0 ? `**Session drafts (${sessionDrafts.length}):** ${sessionDrafts.map((d) => `\`${d.entryId}\` ${d.name}`).join(", ")}` : "",
4555
- captureErrors.length > 0 ? `**Warnings:** ${captureErrors.map((e) => e.detail).join("; ")}` : "",
4556
- directive ? `
4557
- ---
4558
- ## Agent Directive
4559
- ${directive}` : "",
4560
- investigationBrief ? `
4561
- ## Investigation Brief
4562
- ${investigationBrief.tasks.map((t) => `- **${t.target}:** ${t.query} \u2014 ${t.purpose}`).join("\n")}
4563
-
4564
- **Proposal guidance:** ${investigationBrief.proposalGuidance}
4565
- **Reaction prompt:** ${investigationBrief.reactionPrompt}` : ""
4564
+ captureErrors.length > 0 ? `**Warnings:** ${captureErrors.map((e) => e.detail).join("; ")}` : ""
4566
4565
  ];
4567
4566
  return {
4568
4567
  content: [{ type: "text", text: summaryParts.filter(Boolean).join("\n") }],
4569
4568
  structuredContent: response
4570
4569
  };
4571
4570
  }
4571
+ async function handleRespond(args) {
4572
+ requireWriteAccess();
4573
+ const { userInput, betEntryId: argBetId, dimension: argDimension, capture: rawCapture, source: argSource, betName: argBetName } = args;
4574
+ const source = argSource ?? "user";
4575
+ const captureItems = rawCapture ? Array.isArray(rawCapture) ? rawCapture : [rawCapture] : [];
4576
+ if (!userInput) {
4577
+ return {
4578
+ content: [{ type: "text", text: "`userInput` is required for respond action." }]
4579
+ };
4580
+ }
4581
+ let betId = argBetId;
4582
+ if (!betId) {
4583
+ const betName = argBetName ?? "Untitled Bet (Shaping)";
4584
+ const agentId = getAgentSessionId();
4585
+ try {
4586
+ const result = await mcpMutation(
4587
+ "chain.createEntry",
4588
+ {
4589
+ collectionSlug: "bets",
4590
+ name: betName,
4591
+ status: "draft",
4592
+ data: {
4593
+ problem: userInput,
4594
+ appetite: "",
4595
+ elements: "",
4596
+ rabbitHoles: "",
4597
+ noGos: "",
4598
+ architecture: "",
4599
+ buildContract: "",
4600
+ description: `Shaping session for: ${betName}`,
4601
+ status: "shaping",
4602
+ shapingSessionActive: true
4603
+ },
4604
+ createdBy: agentId ? `agent:${agentId}` : "facilitate",
4605
+ sessionId: agentId ?? void 0
4606
+ }
4607
+ );
4608
+ betId = result.entryId;
4609
+ await recordSessionActivity({ entryCreated: result.docId });
4610
+ } catch (err) {
4611
+ const msg = err instanceof Error ? err.message : String(err);
4612
+ return {
4613
+ content: [{ type: "text", text: `Failed to create bet entry: ${msg}` }],
4614
+ isError: true
4615
+ };
4616
+ }
4617
+ }
4618
+ const [betEntry, constellation, chainSurfaced] = await Promise.all([
4619
+ loadBetEntry(betId),
4620
+ loadConstellationState(betId),
4621
+ searchChain(userInput, { maxResults: 8 })
4622
+ ]);
4623
+ if (!betEntry) {
4624
+ return {
4625
+ content: [{ type: "text", text: `Bet \`${betId}\` not found. Use start to create a session.` }]
4626
+ };
4627
+ }
4628
+ const betData = betEntry.data ?? {};
4629
+ let entriesCreated = [];
4630
+ let relationsCreated = 0;
4631
+ const captureErrors = [];
4632
+ if (captureItems.length > 0) {
4633
+ const capResult = await processCaptures({
4634
+ captureItems,
4635
+ betEntryId: betId,
4636
+ betDocId: betEntry._id,
4637
+ betData
4638
+ });
4639
+ entriesCreated = capResult.entriesCreated;
4640
+ relationsCreated = capResult.relationsCreated;
4641
+ captureErrors.push(...capResult.captureErrors);
4642
+ }
4643
+ const scoreResult = await computeAndUpdateScores({
4644
+ betEntryId: betId,
4645
+ betDocId: betEntry._id,
4646
+ betData,
4647
+ betEntry,
4648
+ userInput,
4649
+ argDimension,
4650
+ source,
4651
+ captureItems,
4652
+ chainSurfaced,
4653
+ constellation
4654
+ });
4655
+ captureErrors.push(...scoreResult.captureErrors);
4656
+ const wsCtx = await getWorkspaceContext();
4657
+ return assembleResponse({
4658
+ betEntryId: betId,
4659
+ betName: betEntry.name,
4660
+ workspaceSlug: wsCtx.workspaceSlug,
4661
+ scorecard: scoreResult.scorecard,
4662
+ scorecardCriteria: scoreResult.scorecardCriteria,
4663
+ phase: scoreResult.phase,
4664
+ activeDimension: scoreResult.activeDimension,
4665
+ nextDimension: scoreResult.nextDimension,
4666
+ dimensionResult: scoreResult.dimensionResult,
4667
+ coaching: scoreResult.coaching,
4668
+ overlap: scoreResult.overlap,
4669
+ sessionDrafts: scoreResult.sessionDrafts,
4670
+ alignment: scoreResult.alignment,
4671
+ chainSurfaced,
4672
+ scoringCtx: scoreResult.scoringCtx,
4673
+ isSmallBatch: scoreResult.isSmallBatch,
4674
+ completed: scoreResult.completed,
4675
+ refreshedData: scoreResult.refreshedData,
4676
+ buildContract: scoreResult.buildContract,
4677
+ commitBlockers: scoreResult.commitBlockers,
4678
+ entriesCreated,
4679
+ relationsCreated,
4680
+ captureErrors,
4681
+ captureItems
4682
+ });
4683
+ }
4572
4684
  async function handleScore(args) {
4573
4685
  const betId = args.betEntryId;
4574
4686
  if (!betId) {
@@ -4833,7 +4945,7 @@ async function handleCommitConstellation(args) {
4833
4945
  }
4834
4946
  let contradictionWarnings = [];
4835
4947
  try {
4836
- const { runContradictionCheck } = await import("./smart-capture-W2IALMJ5.js");
4948
+ const { runContradictionCheck } = await import("./smart-capture-3BWER4RY.js");
4837
4949
  const descField = betData.problem ?? betData.description ?? "";
4838
4950
  contradictionWarnings = await runContradictionCheck(
4839
4951
  betEntry.name ?? betId,
@@ -5676,6 +5788,16 @@ function buildInterviewResponse(workspaceName) {
5676
5788
  }
5677
5789
 
5678
5790
  // src/tools/start.ts
5791
+ async function tryMarkOriented(agentSessionId) {
5792
+ if (!agentSessionId) return { oriented: false, orientationStatus: "no_session" };
5793
+ try {
5794
+ await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5795
+ setSessionOriented(true);
5796
+ return { oriented: true, orientationStatus: "complete" };
5797
+ } catch {
5798
+ return { oriented: false, orientationStatus: "failed" };
5799
+ }
5800
+ }
5679
5801
  var startSchema = z15.object({
5680
5802
  preset: z15.string().optional().describe(
5681
5803
  "Collection preset ID to seed (e.g. 'software-product', 'content-business', 'agency', 'saas-api', 'general'). Only used for fresh workspaces. If omitted on a fresh workspace, returns the preset menu."
@@ -5725,10 +5847,16 @@ function registerStartTools(server) {
5725
5847
  };
5726
5848
  }
5727
5849
  if (stage === "blank") {
5728
- const scanResult = buildProjectScanResponse(wsCtx);
5850
+ const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
5851
+ const blankResult = buildBlankResponse(wsCtx);
5729
5852
  return {
5730
- content: [{ type: "text", text: scanResult.text }],
5731
- structuredContent: scanResult.structuredContent
5853
+ content: [{ type: "text", text: blankResult.text }],
5854
+ structuredContent: {
5855
+ ...blankResult.structuredContent,
5856
+ oriented,
5857
+ orientationStatus,
5858
+ ...agentSessionId ? { sessionId: agentSessionId } : {}
5859
+ }
5732
5860
  };
5733
5861
  }
5734
5862
  if (stage === "seeded") {
@@ -5752,44 +5880,71 @@ function registerStartTools(server) {
5752
5880
  }
5753
5881
  );
5754
5882
  }
5755
- function buildProjectScanResponse(wsCtx) {
5883
+ function buildBlankResponse(wsCtx) {
5884
+ const instructions = getInterviewInstructions(wsCtx.workspaceName);
5756
5885
  const text = [
5757
5886
  `# Welcome to ${wsCtx.workspaceName}`,
5758
5887
  "",
5759
- "Your workspace is fresh. Let me get oriented in your codebase.",
5888
+ "Your workspace is fresh. Let's set it up.",
5889
+ "",
5890
+ "## How would you like to start?",
5891
+ "",
5892
+ "Present these options to the user:",
5893
+ "",
5894
+ "**1. Tell me about your product** _(recommended)_",
5895
+ "Ask the user:",
5896
+ `> ${instructions.question1}`,
5897
+ "",
5898
+ "_A sentence or two is enough. This becomes the foundation \u2014 vision and audience \u2014 that makes everything else more relevant._",
5899
+ "",
5900
+ "**2. Scan my codebase**",
5901
+ "Skip the questions and jump straight to reading the project files.",
5902
+ "",
5903
+ "**3. Use a preset**",
5904
+ `Seed collections for a domain. Available: software-product, content-business, agency, saas-api, general.`,
5905
+ "",
5906
+ "---",
5907
+ "",
5908
+ "## Path 1: Interview first (default if user just starts talking)",
5909
+ "",
5910
+ instructions.systemPrompt,
5911
+ "",
5912
+ "After the user answers Q1, extract:",
5913
+ instructions.extractionGuidance,
5914
+ "",
5915
+ instructions.captureInstructions,
5916
+ "Use `autoCommit: false` for all captures \u2014 user confirms before anything is committed.",
5917
+ "",
5918
+ "**Then offer the codebase scan with context:**",
5919
+ `"Now that I know what you're building, I can scan your project files and find technical decisions, features, and conventions that are relevant to **your** product. Want me to?"`,
5920
+ "",
5921
+ "If they say yes, scan README.md, package.json/pyproject.toml/Cargo.toml, and top-level source dirs.",
5922
+ "Infer entries WITH the product context you now have. Present as a numbered list for confirmation.",
5923
+ "Capture confirmed entries with `autoCommit: false`.",
5924
+ "",
5925
+ "## Path 2: Scan first",
5760
5926
  "",
5761
- "**Please read these files and tell me what you find:**",
5762
- "1. `README.md` (or equivalent \u2014 top-level docs)",
5763
- "2. `package.json` / `pyproject.toml` / `Cargo.toml` (project manifest)",
5927
+ "Read these files:",
5928
+ "1. README.md (or equivalent top-level docs)",
5929
+ "2. package.json / pyproject.toml / Cargo.toml (project manifest)",
5764
5930
  "3. Top-level source directories and their purpose",
5765
5931
  "",
5766
- "From those, infer 5\u20138 product knowledge entries \u2014 things like:",
5767
- "- **Product name and what it does** (1\u20132 sentences)",
5768
- "- **Key technical decisions** already made (framework, DB, architecture)",
5769
- "- **Core features** the codebase reveals",
5770
- "- **Conventions** you notice (naming, patterns, standards)",
5932
+ "Infer 5\u20138 product knowledge entries (product name, tech decisions, features, conventions).",
5933
+ "Present as a numbered list. Ask which to keep. Capture confirmed entries with `autoCommit: false`.",
5771
5934
  "",
5772
- "Present them as a numbered list:",
5773
- "```",
5774
- "1. [Entry name]: [1-sentence description]",
5775
- "2. ...",
5776
- "```",
5935
+ "**If the codebase is sparse** (no README, empty project): fall back to Path 1 (ask the user about their product).",
5777
5936
  "",
5778
- `Then ask: **"Which of these are accurate? Reply with numbers to skip (e.g. 'skip 2, 4') or say 'all good' to capture everything."**`,
5937
+ "_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._",
5779
5938
  "",
5780
- "Once confirmed, call `capture` for each entry with `autoCommit: false` \u2014 the user will review before anything hits the Chain.",
5939
+ "## Path 3: Preset",
5781
5940
  "",
5782
- '**If the codebase is sparse** (no README, empty project, monorepo root): say so and ask "Tell me about your product in a sentence \u2014 what does it do and who is it for?" instead.',
5941
+ "If the user picks a preset, call `start` again with `preset: '<chosen-preset>'`.",
5783
5942
  "",
5784
- "_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._"
5943
+ instructions.qualityNote
5785
5944
  ].join("\n");
5786
5945
  return {
5787
5946
  text,
5788
- structuredContent: {
5789
- stage: "blank",
5790
- oriented: false,
5791
- orientationStatus: "no_session"
5792
- }
5947
+ structuredContent: { stage: "blank" }
5793
5948
  };
5794
5949
  }
5795
5950
  async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
@@ -5815,18 +5970,7 @@ async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
5815
5970
  } else {
5816
5971
  lines.push("No gaps detected \u2014 your workspace is filling up nicely. What would you like to work on?");
5817
5972
  }
5818
- let oriented = false;
5819
- let orientationStatus = "no_session";
5820
- if (agentSessionId) {
5821
- try {
5822
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5823
- setSessionOriented(true);
5824
- oriented = true;
5825
- orientationStatus = "complete";
5826
- } catch {
5827
- orientationStatus = "failed";
5828
- }
5829
- }
5973
+ const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
5830
5974
  return {
5831
5975
  text: lines.join("\n"),
5832
5976
  structuredContent: {
@@ -5868,18 +6012,7 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`,
5868
6012
  skipped.push(`${col.name} (already exists)`);
5869
6013
  }
5870
6014
  }
5871
- let oriented = false;
5872
- let orientationStatus = "no_session";
5873
- if (agentSessionId) {
5874
- try {
5875
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5876
- setSessionOriented(true);
5877
- oriented = true;
5878
- orientationStatus = "complete";
5879
- } catch {
5880
- orientationStatus = "failed";
5881
- }
5882
- }
6015
+ const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
5883
6016
  const lines = [
5884
6017
  `# ${wsCtx.workspaceName} is ready`,
5885
6018
  "",
@@ -6118,18 +6251,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6118
6251
  for (const err of errors) lines.push(`- ${err}`);
6119
6252
  lines.push("");
6120
6253
  }
6121
- let oriented = false;
6122
- let orientationStatus = "no_session";
6123
- if (agentSessionId) {
6124
- try {
6125
- await mcpCall("agent.markOriented", { sessionId: agentSessionId });
6126
- setSessionOriented(true);
6127
- oriented = true;
6128
- orientationStatus = "complete";
6129
- } catch {
6130
- orientationStatus = "failed";
6131
- }
6132
- }
6254
+ const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
6133
6255
  return {
6134
6256
  text: lines.join("\n"),
6135
6257
  structuredContent: {
@@ -8167,15 +8289,20 @@ function registerHealthTools(server) {
8167
8289
  "Your workspace is fresh \u2014 let's set it up.",
8168
8290
  "",
8169
8291
  "**Recommended:** call the `start` tool instead of `orient` for the best first-run experience.",
8170
- "It will guide you through scanning your codebase and capturing initial knowledge.",
8292
+ "It will guide you through setting up your workspace \u2014 you can tell me about your product, scan your codebase, or use a preset.",
8171
8293
  "",
8172
8294
  "Or tell me about your product and I'll start capturing knowledge directly."
8173
8295
  ];
8296
+ let oriented = false;
8297
+ let orientationStatus = "no_session";
8174
8298
  if (agentSessionId) {
8175
8299
  try {
8176
8300
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
8177
8301
  setSessionOriented(true);
8302
+ oriented = true;
8303
+ orientationStatus = "complete";
8178
8304
  } catch {
8305
+ orientationStatus = "failed";
8179
8306
  }
8180
8307
  }
8181
8308
  return {
@@ -8183,7 +8310,8 @@ function registerHealthTools(server) {
8183
8310
  structuredContent: {
8184
8311
  stage: "blank",
8185
8312
  readinessScore: readiness?.score ?? 0,
8186
- oriented: true,
8313
+ oriented,
8314
+ orientationStatus,
8187
8315
  redirectHint: "Use the `start` tool for the full guided setup experience."
8188
8316
  }
8189
8317
  };
@@ -9320,9 +9448,14 @@ Each scored 0-10 by the server:
9320
9448
 
9321
9449
  ### Session Flow
9322
9450
  1. **Start:** \`facilitate action=start betName="${idea}"\`
9323
- Creates a draft bet on the Chain. Returns Studio URL and initial coaching prompt.
9451
+ Starts shaping context only (no entry is created yet).
9452
+
9453
+ 2. **First respond:** \`facilitate action=respond betName="${idea}" userInput="<user's input>"\`
9454
+ This creates the draft bet and captures the initial problem context.
9455
+
9456
+ Save \`captured.betEntryId\` from the response and reuse it for every subsequent call.
9324
9457
 
9325
- 2. **Respond (repeat):** \`facilitate action=respond betEntryId="..." userInput="<user's input>"\`
9458
+ 3. **Respond (repeat):** \`facilitate action=respond betEntryId="..." userInput="<user's input>"\`
9326
9459
  Optionally add \`dimension="problem_clarity"\` to target a specific dimension.
9327
9460
 
9328
9461
  **CRITICAL \u2014 Capture is REQUIRED for structured items:**
@@ -9332,10 +9465,10 @@ Each scored 0-10 by the server:
9332
9465
  - No-gos: \`capture={type:"noGo", name:"...", description:"..."}\`
9333
9466
  Each element, risk, and no-go needs its own \`respond\` call with \`capture\`.
9334
9467
 
9335
- 3. **Score (anytime):** \`facilitate action=score betEntryId="..."\`
9468
+ 4. **Score (anytime):** \`facilitate action=score betEntryId="..."\`
9336
9469
  Check the scorecard without advancing.
9337
9470
 
9338
- 4. **Resume:** \`facilitate action=resume betEntryId="..."\`
9471
+ 5. **Resume:** \`facilitate action=resume betEntryId="..."\`
9339
9472
  Reconstruct session from Chain state if returning to an existing bet.
9340
9473
 
9341
9474
  ### Reading the Response
@@ -9819,4 +9952,4 @@ export {
9819
9952
  SERVER_VERSION,
9820
9953
  createProductBrainServer
9821
9954
  };
9822
- //# sourceMappingURL=chunk-AWDD44CN.js.map
9955
+ //# sourceMappingURL=chunk-FKOP3WC7.js.map