@productbrain/mcp 0.0.1-beta.52 → 0.0.1-beta.54

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.
@@ -38,7 +38,7 @@ import {
38
38
  unknownAction,
39
39
  validationResult,
40
40
  withEnvelope
41
- } from "./chunk-ED3KCWZE.js";
41
+ } from "./chunk-WEH5CZJ7.js";
42
42
  import {
43
43
  trackQualityCheck,
44
44
  trackQualityVerdict
@@ -232,7 +232,7 @@ ${formatted}` }],
232
232
  },
233
233
  withEnvelope(async ({ entryId }) => {
234
234
  requireWriteAccess();
235
- const { runContradictionCheck } = await import("./smart-capture-2IM2565I.js");
235
+ const { runContradictionCheck } = await import("./smart-capture-3OXWEXWM.js");
236
236
  const entry = await mcpQuery("chain.getEntry", { entryId });
237
237
  if (!entry) {
238
238
  return notFoundResult(entryId, `Entry '${entryId}' not found. Try search to find the right ID.`);
@@ -2569,11 +2569,150 @@ async function handleReEvaluate(entryId, context) {
2569
2569
  // src/tools/workflows.ts
2570
2570
  import { z as z11 } from "zod";
2571
2571
 
2572
+ // src/workflows/descriptor.ts
2573
+ function cloneWorkflowQuestion(question) {
2574
+ return {
2575
+ ...question,
2576
+ options: question.options?.map((option) => ({ ...option }))
2577
+ };
2578
+ }
2579
+ function cloneWorkflowRound(round) {
2580
+ return {
2581
+ ...round,
2582
+ questions: round.questions?.map(cloneWorkflowQuestion),
2583
+ outputSchema: { ...round.outputSchema }
2584
+ };
2585
+ }
2586
+ function cloneWorkflowDescriptor(descriptor) {
2587
+ return {
2588
+ ...descriptor,
2589
+ rounds: descriptor.rounds.map(cloneWorkflowRound),
2590
+ template: { ...descriptor.template },
2591
+ output: {
2592
+ primaryRecord: {
2593
+ routing: descriptor.output.primaryRecord.routing.mode === "fixed" ? {
2594
+ mode: "fixed",
2595
+ collection: descriptor.output.primaryRecord.routing.collection
2596
+ } : { mode: "classifier" }
2597
+ },
2598
+ summaryCapture: descriptor.output.summaryCapture ? { ...descriptor.output.summaryCapture } : void 0,
2599
+ emits: descriptor.output.emits?.map((emitted) => ({ ...emitted }))
2600
+ },
2601
+ runtime: { ...descriptor.runtime }
2602
+ };
2603
+ }
2604
+ function cloneWorkflowDefinition(definition) {
2605
+ return {
2606
+ ...definition,
2607
+ rounds: definition.rounds.map(cloneWorkflowRound),
2608
+ kbOutputTemplate: definition.kbOutputTemplate ? { ...definition.kbOutputTemplate } : void 0
2609
+ };
2610
+ }
2611
+ function validateWorkflowDescriptor(descriptor) {
2612
+ if (descriptor.rounds.length === 0) {
2613
+ throw new Error(`Workflow '${descriptor.id}' must define at least one round.`);
2614
+ }
2615
+ const roundIds = /* @__PURE__ */ new Set();
2616
+ for (const [index, round] of descriptor.rounds.entries()) {
2617
+ if (roundIds.has(round.id)) {
2618
+ throw new Error(`Workflow '${descriptor.id}' has duplicate round id '${round.id}'.`);
2619
+ }
2620
+ roundIds.add(round.id);
2621
+ const expectedNum = String(index + 1).padStart(2, "0");
2622
+ if (round.num !== expectedNum) {
2623
+ throw new Error(
2624
+ `Workflow '${descriptor.id}' round '${round.id}' has num '${round.num}', expected '${expectedNum}'.`
2625
+ );
2626
+ }
2627
+ const questionIds = /* @__PURE__ */ new Set();
2628
+ for (const question of round.questions ?? []) {
2629
+ if (questionIds.has(question.id)) {
2630
+ throw new Error(
2631
+ `Workflow '${descriptor.id}' round '${round.id}' has duplicate question id '${question.id}'.`
2632
+ );
2633
+ }
2634
+ questionIds.add(question.id);
2635
+ }
2636
+ }
2637
+ if (descriptor.runtime.kind === "facilitated" && !descriptor.runtime.primaryTool) {
2638
+ throw new Error(
2639
+ `Workflow '${descriptor.id}' is facilitated and must define runtime.primaryTool.`
2640
+ );
2641
+ }
2642
+ if (descriptor.output.summaryCapture?.createsPrimaryRecord && descriptor.output.primaryRecord.routing.mode !== "fixed") {
2643
+ throw new Error(
2644
+ `Workflow '${descriptor.id}' cannot define summaryCapture when primaryRecord routing is classifier-based.`
2645
+ );
2646
+ }
2647
+ }
2648
+ function getWorkflowSummaryCaptureDefinition(descriptor) {
2649
+ const { summaryCapture } = descriptor.output;
2650
+ const { routing } = descriptor.output.primaryRecord;
2651
+ if (!summaryCapture || !summaryCapture.createsPrimaryRecord) {
2652
+ return null;
2653
+ }
2654
+ if (routing.mode !== "fixed") {
2655
+ return null;
2656
+ }
2657
+ return {
2658
+ collection: routing.collection,
2659
+ nameTemplate: summaryCapture.nameTemplate,
2660
+ descriptionField: summaryCapture.descriptionField ?? "description",
2661
+ descriptionSource: summaryCapture.descriptionSource ?? "final-output-text"
2662
+ };
2663
+ }
2664
+ function getWorkflowPrimaryRecordCollection(descriptor) {
2665
+ const { routing } = descriptor.output.primaryRecord;
2666
+ return routing.mode === "fixed" ? routing.collection : null;
2667
+ }
2668
+ function getWorkflowTemplateMetadata(descriptor) {
2669
+ return {
2670
+ slug: descriptor.template.slug,
2671
+ name: descriptor.template.name
2672
+ };
2673
+ }
2674
+ function getWorkflowContextCollection(descriptor) {
2675
+ return getWorkflowPrimaryRecordCollection(descriptor);
2676
+ }
2677
+ function describeWorkflowPrimaryOutput(descriptor) {
2678
+ const { routing } = descriptor.output.primaryRecord;
2679
+ if (routing.mode === "fixed") {
2680
+ return `Primary record routes to \`${routing.collection}\`.`;
2681
+ }
2682
+ return "Primary record is classifier-routed at runtime.";
2683
+ }
2684
+ function compileWorkflowDefinition(descriptor) {
2685
+ validateWorkflowDescriptor(descriptor);
2686
+ const clonedDescriptor = cloneWorkflowDescriptor(descriptor);
2687
+ const primaryRecordCollection = getWorkflowPrimaryRecordCollection(
2688
+ clonedDescriptor
2689
+ );
2690
+ const summaryCapture = getWorkflowSummaryCaptureDefinition(clonedDescriptor);
2691
+ return {
2692
+ id: clonedDescriptor.id,
2693
+ name: clonedDescriptor.name,
2694
+ shortDescription: clonedDescriptor.shortDescription,
2695
+ icon: clonedDescriptor.icon,
2696
+ facilitatorPreamble: clonedDescriptor.facilitatorPreamble,
2697
+ rounds: clonedDescriptor.rounds,
2698
+ kbOutputCollection: primaryRecordCollection ?? void 0,
2699
+ kbOutputTemplate: summaryCapture ? {
2700
+ nameTemplate: summaryCapture.nameTemplate,
2701
+ descriptionField: summaryCapture.descriptionField
2702
+ } : void 0,
2703
+ errorRecovery: clonedDescriptor.errorRecovery
2704
+ };
2705
+ }
2706
+
2572
2707
  // src/workflows/definitions.ts
2573
- var RETRO_WORKFLOW = {
2708
+ var RETRO_WORKFLOW_DESCRIPTOR = {
2574
2709
  id: "retro",
2575
2710
  name: "Retrospective",
2576
2711
  shortDescription: "Structured team retrospective \u2014 reflect, surface patterns, commit to actions. Commits a decision entry to the Chain.",
2712
+ template: {
2713
+ slug: "retro",
2714
+ name: "Retrospective"
2715
+ },
2577
2716
  icon: "\u25CE",
2578
2717
  facilitatorPreamble: `You are now in **Facilitator Mode**. You are not a coding assistant \u2014 you are a facilitator running a structured retrospective.
2579
2718
 
@@ -2719,10 +2858,21 @@ Create a Cursor Plan with these rounds as tasks. Mark each in_progress as you en
2719
2858
  maxDurationHint: "2 min"
2720
2859
  }
2721
2860
  ],
2722
- kbOutputCollection: "decisions",
2723
- kbOutputTemplate: {
2724
- nameTemplate: "Retro: {scope} \u2014 {date}",
2725
- descriptionField: "rationale"
2861
+ output: {
2862
+ primaryRecord: {
2863
+ routing: {
2864
+ mode: "fixed",
2865
+ collection: "decisions"
2866
+ }
2867
+ },
2868
+ summaryCapture: {
2869
+ createsPrimaryRecord: true,
2870
+ nameTemplate: "Retro: {scope} \u2014 {date}",
2871
+ descriptionField: "rationale"
2872
+ }
2873
+ },
2874
+ runtime: {
2875
+ kind: "guided"
2726
2876
  },
2727
2877
  errorRecovery: `If anything goes wrong during the retro:
2728
2878
 
@@ -2734,10 +2884,14 @@ Create a Cursor Plan with these rounds as tasks. Mark each in_progress as you en
2734
2884
 
2735
2885
  The retro must never fail silently. Always communicate state.`
2736
2886
  };
2737
- var SHAPE_WORKFLOW = {
2887
+ var SHAPE_WORKFLOW_DESCRIPTOR = {
2738
2888
  id: "shape",
2739
2889
  name: "Shape a Bet",
2740
2890
  shortDescription: "Coached shaping session \u2014 problem \u2192 appetite \u2192 elements \u2192 architecture \u2192 de-risk \u2192 pitch. Uses the `facilitate` tool for scoring and Chain capture.",
2891
+ template: {
2892
+ slug: "shape-a-bet",
2893
+ name: "Shape a Bet"
2894
+ },
2741
2895
  icon: "\u25C6",
2742
2896
  facilitatorPreamble: `You are now in **Shaping Mode**. You are an **investigating** shaping facilitator powered by the \`facilitate\` tool and Cursor sub-agents.
2743
2897
 
@@ -2910,10 +3064,44 @@ If any facilitate call fails:
2910
3064
  maxDurationHint: "5 min"
2911
3065
  }
2912
3066
  ],
2913
- kbOutputCollection: "bets",
2914
- kbOutputTemplate: {
2915
- nameTemplate: "{betName}",
2916
- descriptionField: "problem"
3067
+ output: {
3068
+ primaryRecord: {
3069
+ routing: {
3070
+ mode: "fixed",
3071
+ collection: "bets"
3072
+ }
3073
+ },
3074
+ emits: [
3075
+ {
3076
+ kind: "entry",
3077
+ collection: "features",
3078
+ description: "Solution elements captured during shaping."
3079
+ },
3080
+ {
3081
+ kind: "entry",
3082
+ collection: "tensions",
3083
+ description: "Risks and rabbit holes captured during shaping."
3084
+ },
3085
+ {
3086
+ kind: "entry",
3087
+ collection: "decisions",
3088
+ description: "Decisions captured inline when they surface."
3089
+ },
3090
+ {
3091
+ kind: "entry",
3092
+ collection: "glossary",
3093
+ description: "Glossary terms captured inline when they surface."
3094
+ },
3095
+ {
3096
+ kind: "update",
3097
+ collection: "bets",
3098
+ description: "Bet status and no-go updates during finalize."
3099
+ }
3100
+ ]
3101
+ },
3102
+ runtime: {
3103
+ kind: "facilitated",
3104
+ primaryTool: "facilitate"
2917
3105
  },
2918
3106
  errorRecovery: `If anything goes wrong during shaping:
2919
3107
 
@@ -2925,10 +3113,14 @@ If any facilitate call fails:
2925
3113
 
2926
3114
  The shaping must never fail silently. Always communicate state.`
2927
3115
  };
2928
- var IMPLEMENTATION_REVIEW_WORKFLOW = {
3116
+ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
2929
3117
  id: "implementation-review",
2930
3118
  name: "Implementation Review",
2931
3119
  shortDescription: "Review completed implementation against standards, Chain coherence, and test honesty. Spawns sub-agents, uses Context7 for library docs, validates chain is up to date. Ends with BET/chain IDs. Commits an insight to the Chain.",
3120
+ template: {
3121
+ slug: "implementation-review",
3122
+ name: "Implementation Review"
3123
+ },
2932
3124
  icon: "\u2713",
2933
3125
  facilitatorPreamble: `You are now in **Implementation Review Mode**. You orchestrate a structured review of work that was just built \u2014 not a conversation, but a tool-driven audit with sub-agent support.
2934
3126
 
@@ -3042,10 +3234,21 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
3042
3234
  maxDurationHint: "3 min"
3043
3235
  }
3044
3236
  ],
3045
- kbOutputCollection: "insights",
3046
- kbOutputTemplate: {
3047
- nameTemplate: "Implementation Review: {scope} \u2014 {date}",
3048
- descriptionField: "description"
3237
+ output: {
3238
+ primaryRecord: {
3239
+ routing: {
3240
+ mode: "fixed",
3241
+ collection: "insights"
3242
+ }
3243
+ },
3244
+ summaryCapture: {
3245
+ createsPrimaryRecord: true,
3246
+ nameTemplate: "Implementation Review \u2014 {date}",
3247
+ descriptionField: "description"
3248
+ }
3249
+ },
3250
+ runtime: {
3251
+ kind: "guided"
3049
3252
  },
3050
3253
  errorRecovery: `If anything goes wrong during implementation review:
3051
3254
 
@@ -3056,10 +3259,14 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
3056
3259
 
3057
3260
  The review must never fail silently. Always end with BET/chain IDs.`
3058
3261
  };
3059
- var PROCESS_CHANGE_WORKFLOW = {
3262
+ var PROCESS_CHANGE_WORKFLOW_DESCRIPTOR = {
3060
3263
  id: "process-change",
3061
3264
  name: "Process a Change",
3062
3265
  shortDescription: "Lightweight workflow for everyday changes \u2014 bug fixes, improvements, change requests. Captures what changed, why, and what's affected without a full shaping ceremony. Routes output to the right collection via the classifier.",
3266
+ template: {
3267
+ slug: "process-change",
3268
+ name: "Process a Change"
3269
+ },
3063
3270
  icon: "\u21BB",
3064
3271
  facilitatorPreamble: `You are now in **Change Processing Mode**. You are a lightweight facilitator helping the user capture the context around a change they're making or just made \u2014 a bug fix, improvement, change request, or prerequisite task.
3065
3272
 
@@ -3147,10 +3354,15 @@ var PROCESS_CHANGE_WORKFLOW = {
3147
3354
  maxDurationHint: "1 min"
3148
3355
  }
3149
3356
  ],
3150
- kbOutputCollection: "decisions",
3151
- kbOutputTemplate: {
3152
- nameTemplate: "{changeName} \u2014 {date}",
3153
- descriptionField: "rationale"
3357
+ output: {
3358
+ primaryRecord: {
3359
+ routing: {
3360
+ mode: "classifier"
3361
+ }
3362
+ }
3363
+ },
3364
+ runtime: {
3365
+ kind: "guided"
3154
3366
  },
3155
3367
  errorRecovery: `If anything goes wrong during change processing:
3156
3368
 
@@ -3161,34 +3373,102 @@ var PROCESS_CHANGE_WORKFLOW = {
3161
3373
 
3162
3374
  Never block the user's real work. This workflow serves the work \u2014 it doesn't replace it.`
3163
3375
  };
3164
- var WORKFLOWS = /* @__PURE__ */ new Map([
3165
- ["retro", RETRO_WORKFLOW],
3166
- ["shape", SHAPE_WORKFLOW],
3167
- ["implementation-review", IMPLEMENTATION_REVIEW_WORKFLOW],
3168
- ["process-change", PROCESS_CHANGE_WORKFLOW]
3376
+ var WORKFLOW_DESCRIPTORS = /* @__PURE__ */ new Map([
3377
+ ["retro", RETRO_WORKFLOW_DESCRIPTOR],
3378
+ ["shape", SHAPE_WORKFLOW_DESCRIPTOR],
3379
+ ["implementation-review", IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR],
3380
+ ["process-change", PROCESS_CHANGE_WORKFLOW_DESCRIPTOR]
3169
3381
  ]);
3382
+ var WORKFLOWS = new Map(
3383
+ Array.from(WORKFLOW_DESCRIPTORS.entries(), ([id, descriptor]) => [
3384
+ id,
3385
+ compileWorkflowDefinition(descriptor)
3386
+ ])
3387
+ );
3170
3388
  function getWorkflow(id) {
3171
- return WORKFLOWS.get(id);
3389
+ const workflow = WORKFLOWS.get(id);
3390
+ return workflow ? cloneWorkflowDefinition(workflow) : void 0;
3172
3391
  }
3173
3392
  function listWorkflows() {
3174
- return Array.from(WORKFLOWS.values());
3393
+ return Array.from(
3394
+ WORKFLOWS.values(),
3395
+ (workflow) => cloneWorkflowDefinition(workflow)
3396
+ );
3397
+ }
3398
+ function getWorkflowDescriptor(id) {
3399
+ const descriptor = WORKFLOW_DESCRIPTORS.get(id);
3400
+ return descriptor ? cloneWorkflowDescriptor(descriptor) : void 0;
3401
+ }
3402
+
3403
+ // ../../convex/lib/workflowRun.ts
3404
+ function workflowRunOutputToText(output) {
3405
+ switch (output.format) {
3406
+ case "freetext":
3407
+ return output.value;
3408
+ case "list":
3409
+ return output.value.join(", ");
3410
+ case "choice":
3411
+ return Array.isArray(output.value) ? output.value.join(", ") : output.value;
3412
+ case "structured":
3413
+ if (output.value && typeof output.value === "object" && "text" in output.value) {
3414
+ const textValue = output.value.text;
3415
+ if (typeof textValue === "string") {
3416
+ return textValue;
3417
+ }
3418
+ }
3419
+ return JSON.stringify(output.value);
3420
+ }
3175
3421
  }
3176
3422
 
3177
3423
  // src/tools/workflows.ts
3178
- var WORKFLOWS_ACTIONS = ["list", "checkpoint"];
3424
+ var WORKFLOWS_ACTIONS = ["list", "checkpoint", "get-run"];
3425
+ var jsonValueSchema = z11.lazy(
3426
+ () => z11.union([
3427
+ z11.string(),
3428
+ z11.number(),
3429
+ z11.boolean(),
3430
+ z11.null(),
3431
+ z11.array(jsonValueSchema),
3432
+ z11.record(jsonValueSchema)
3433
+ ])
3434
+ );
3435
+ var workflowRunOutputInputSchema = z11.union([
3436
+ z11.object({
3437
+ format: z11.literal("freetext"),
3438
+ value: z11.string()
3439
+ }),
3440
+ z11.object({
3441
+ format: z11.literal("list"),
3442
+ value: z11.array(z11.string())
3443
+ }),
3444
+ z11.object({
3445
+ format: z11.literal("choice"),
3446
+ value: z11.union([z11.string(), z11.array(z11.string())])
3447
+ }),
3448
+ z11.object({
3449
+ format: z11.literal("structured"),
3450
+ value: jsonValueSchema
3451
+ })
3452
+ ]);
3179
3453
  var workflowsSchema = z11.object({
3180
3454
  action: z11.enum(WORKFLOWS_ACTIONS).describe(
3181
- "'list': browse available workflows. 'checkpoint': record round output or final summary."
3455
+ "'list': browse available workflows. 'checkpoint': record round output or final summary. 'get-run': inspect a persisted workflow run."
3182
3456
  ),
3183
3457
  workflowId: z11.string().optional().describe("Workflow ID for checkpoint, e.g. 'retro'"),
3458
+ runId: z11.string().optional().describe("Workflow run ID to inspect. Optional for action='get-run' when workflowId is provided."),
3184
3459
  roundId: z11.string().optional().describe("Round ID for checkpoint, e.g. 'what-went-well'"),
3185
- output: z11.string().optional().describe("The round's output \u2014 synthesized by the facilitator"),
3186
- isFinal: z11.boolean().optional().describe("If true, final checkpoint \u2014 triggers summary chain entry"),
3187
- summaryName: z11.string().optional().describe("Name for final chain entry (required when isFinal=true)"),
3188
- summaryDescription: z11.string().optional().describe("Description for final chain entry (required when isFinal=true)")
3460
+ output: z11.union([z11.string(), workflowRunOutputInputSchema]).optional().describe(
3461
+ "The round's output \u2014 either legacy synthesized text or a typed workflow run payload."
3462
+ ),
3463
+ isFinal: z11.boolean().optional().describe("If true, finalize an existing durable workflow run from its terminal round and create the summary chain entry."),
3464
+ restart: z11.boolean().optional().describe("If true, start a new durable run from the workflow's first round in the current session."),
3465
+ summaryName: z11.string().optional().describe("Optional name for final chain entry. If omitted, the workflow summary template is used."),
3466
+ summaryDescription: z11.string().optional().describe("Optional override for the final chain entry description. Defaults to the final round output text.")
3189
3467
  });
3190
3468
  function formatWorkflowCard(wf) {
3191
3469
  const roundList = wf.rounds.map((r) => ` ${r.num}. ${r.label} (${r.type}, ~${r.maxDurationHint ?? "?"})`).join("\n");
3470
+ const descriptor = getWorkflowDescriptor(wf.id);
3471
+ const outputDescription = descriptor ? describeWorkflowPrimaryOutput(descriptor) : wf.kbOutputCollection ? `Primary record routes to \`${wf.kbOutputCollection}\`.` : "Primary output routing is resolved at runtime.";
3192
3472
  return `## ${wf.icon} ${wf.name}
3193
3473
  **ID**: \`${wf.id}\`
3194
3474
  ${wf.shortDescription}
@@ -3196,33 +3476,69 @@ ${wf.shortDescription}
3196
3476
  **Rounds** (${wf.rounds.length}):
3197
3477
  ${roundList}
3198
3478
 
3199
- **Output**: Creates entries in \`${wf.kbOutputCollection}\` collection.
3479
+ **Output**: ${outputDescription}
3200
3480
  _Use the \`run-workflow\` prompt with workflow="${wf.id}" to start._`;
3201
3481
  }
3482
+ function validateFinalWorkflowCheckpoint(workflow, roundId) {
3483
+ const finalRound = workflow.rounds.at(-1);
3484
+ if (!finalRound) {
3485
+ return `Workflow '${workflow.id}' has no rounds and cannot create a final summary.`;
3486
+ }
3487
+ if (roundId !== finalRound.id) {
3488
+ return `Workflow '${workflow.id}' can only create a final summary from its terminal round '${finalRound.id}' (${finalRound.label}).`;
3489
+ }
3490
+ return null;
3491
+ }
3202
3492
  function registerWorkflowTools(server) {
3203
3493
  const workflowsTool = server.registerTool(
3204
3494
  "workflows",
3205
3495
  {
3206
3496
  title: "Workflows",
3207
- description: "Chainwork workflow discovery and checkpointing. Two actions:\n\n- **list**: Browse available workflows \u2014 retro, shape-a-bet, IDM, etc. Use the `run-workflow` prompt to launch one.\n- **checkpoint**: Record round output during Facilitator Mode. Persists progress. At completion, use isFinal=true to create the summary chain entry.",
3497
+ description: "Chainwork workflow discovery, checkpointing, and run inspection. Three actions:\n\n- **list**: Browse available workflows \u2014 retro, shape-a-bet, IDM, etc. Use the `run-workflow` prompt to launch one.\n- **checkpoint**: Record round output during Facilitator Mode. Persists progress. At completion, some workflows support isFinal=true to create the summary chain entry.\n- **get-run**: Inspect a persisted workflow run by run ID or by workflow ID in the current session.",
3208
3498
  inputSchema: workflowsSchema,
3209
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
3499
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
3210
3500
  },
3211
3501
  withEnvelope(async (args) => {
3212
3502
  const parsed = parseOrFail(workflowsSchema, args);
3213
3503
  if (!parsed.ok) return parsed.result;
3214
- const { action, workflowId, roundId, output, isFinal, summaryName, summaryDescription } = parsed.data;
3504
+ const {
3505
+ action,
3506
+ workflowId,
3507
+ runId,
3508
+ roundId,
3509
+ output,
3510
+ isFinal,
3511
+ restart,
3512
+ summaryName,
3513
+ summaryDescription
3514
+ } = parsed.data;
3215
3515
  return runWithToolContext({ tool: "workflows", action }, async () => {
3216
3516
  if (action === "list") {
3217
3517
  return handleList3();
3218
3518
  }
3519
+ if (action === "get-run") {
3520
+ if (!runId && !workflowId) {
3521
+ return validationResult(
3522
+ "runId or workflowId is required when action is 'get-run'."
3523
+ );
3524
+ }
3525
+ return handleGetRun(runId, workflowId);
3526
+ }
3219
3527
  if (action === "checkpoint") {
3220
3528
  if (!workflowId || !roundId || output === void 0) {
3221
3529
  return validationResult(
3222
3530
  "workflowId, roundId, and output are required when action is 'checkpoint'."
3223
3531
  );
3224
3532
  }
3225
- return handleCheckpoint(workflowId, roundId, output, isFinal, summaryName, summaryDescription);
3533
+ return handleCheckpoint(
3534
+ workflowId,
3535
+ roundId,
3536
+ output,
3537
+ isFinal,
3538
+ restart,
3539
+ summaryName,
3540
+ summaryDescription
3541
+ );
3226
3542
  }
3227
3543
  return unknownAction(action, WORKFLOWS_ACTIONS);
3228
3544
  });
@@ -3258,14 +3574,78 @@ ${cards}`
3258
3574
  id: w.id,
3259
3575
  name: w.name,
3260
3576
  rounds: w.rounds.length,
3261
- outputCollection: w.kbOutputCollection
3577
+ outputCollection: w.kbOutputCollection ?? null
3262
3578
  })),
3263
3579
  total: workflows.length
3264
3580
  }
3265
3581
  )
3266
3582
  };
3267
3583
  }
3268
- async function handleCheckpoint(workflowId, roundId, output, isFinal, summaryName, summaryDescription) {
3584
+ async function handleGetRun(runId, workflowId) {
3585
+ let run;
3586
+ if (runId) {
3587
+ run = await mcpQuery("chainwork.getWorkflowRun", {
3588
+ runId
3589
+ });
3590
+ } else {
3591
+ requireActiveSession();
3592
+ const agentSessionId = getAgentSessionId();
3593
+ if (!agentSessionId) {
3594
+ return validationResult(
3595
+ "Active session required for workflow run inspection by workflowId."
3596
+ );
3597
+ }
3598
+ run = await mcpQuery("chainwork.getLatestWorkflowRun", {
3599
+ agentSessionId,
3600
+ workflowId
3601
+ });
3602
+ }
3603
+ if (!run) {
3604
+ const target = runId ? `workflow run '${runId}'` : `workflow '${workflowId}' in the current session`;
3605
+ return validationResult(`No persisted run found for ${target}.`);
3606
+ }
3607
+ if (workflowId && run.workflowId !== workflowId) {
3608
+ return validationResult(
3609
+ `Workflow run '${run.runId}' belongs to workflow '${run.workflowId}', not '${workflowId}'.`
3610
+ );
3611
+ }
3612
+ const stepLines = run.projection.steps.map((step) => {
3613
+ const status = step.status.toUpperCase();
3614
+ const outputSummary = step.output ? ` \u2014 ${workflowRunOutputToText(step.output)}` : "";
3615
+ return `- ${step.num}. ${step.label} [${status}]${outputSummary}`;
3616
+ });
3617
+ const lines = [
3618
+ `# Workflow Run: ${run.workflowName}`,
3619
+ "",
3620
+ `- Run ID: \`${run.runId}\``,
3621
+ `- Workflow ID: \`${run.workflowId}\``,
3622
+ `- Status: ${run.status}`,
3623
+ `- Progress: ${run.projection.completedCount}/${run.projection.totalCount}`,
3624
+ `- Current round: ${run.currentRoundId ? `\`${run.currentRoundId}\`` : "complete"}`,
3625
+ `- Renderable sections: ${Object.keys(run.projection.render.links).length}/${run.projection.render.sections.length}`,
3626
+ `- Started: ${run.startedAt}`,
3627
+ ...run.completedAt ? [`- Completed: ${run.completedAt}`] : [],
3628
+ ...run.summary ? [`- Summary entry: \`${run.summary.entryId}\``] : [],
3629
+ "",
3630
+ "## Steps",
3631
+ ...stepLines
3632
+ ];
3633
+ return {
3634
+ content: [{ type: "text", text: lines.join("\n") }],
3635
+ structuredContent: success(
3636
+ `Loaded workflow run ${run.runId} (${run.workflowName}).`,
3637
+ run,
3638
+ run.summary ? [
3639
+ {
3640
+ tool: "graph",
3641
+ description: "Discover connections for the summary entry",
3642
+ parameters: { action: "suggest", entryId: run.summary.entryId }
3643
+ }
3644
+ ] : void 0
3645
+ )
3646
+ };
3647
+ }
3648
+ async function handleCheckpoint(workflowId, roundId, output, isFinal, restart, summaryName, summaryDescription) {
3269
3649
  const wf = getWorkflow(workflowId);
3270
3650
  if (!wf) {
3271
3651
  return {
@@ -3306,50 +3686,115 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3306
3686
  `Workflow: ${wf.name} (\`${wf.id}\`)`,
3307
3687
  ""
3308
3688
  ];
3309
- if (isFinal && summaryName && summaryDescription) {
3689
+ if (restart && isFinal) {
3690
+ return validationResult(
3691
+ "restart cannot be combined with isFinal. Restart from the first round, then continue the workflow normally."
3692
+ );
3693
+ }
3694
+ let normalizedOutput;
3695
+ try {
3696
+ normalizedOutput = coerceWorkflowRunOutput(round, output);
3697
+ } catch (error) {
3698
+ const message = error instanceof Error ? error.message : String(error);
3699
+ return validationResult(message);
3700
+ }
3701
+ const outputPreview = workflowRunOutputToText(normalizedOutput);
3702
+ if (isFinal) {
3703
+ const finalCheckpointValidationError = validateFinalWorkflowCheckpoint(
3704
+ wf,
3705
+ roundId
3706
+ );
3707
+ if (finalCheckpointValidationError) {
3708
+ return validationResult(finalCheckpointValidationError);
3709
+ }
3710
+ const descriptor = getWorkflowDescriptor(workflowId);
3711
+ const template = descriptor ? getWorkflowTemplateMetadata(descriptor) : { slug: wf.id, name: wf.name };
3712
+ const summaryCapture = descriptor ? getWorkflowSummaryCaptureDefinition(descriptor) : wf.kbOutputCollection && wf.kbOutputTemplate ? {
3713
+ collection: wf.kbOutputCollection,
3714
+ nameTemplate: wf.kbOutputTemplate.nameTemplate,
3715
+ descriptionField: wf.kbOutputTemplate.descriptionField
3716
+ } : null;
3717
+ if (!summaryCapture) {
3718
+ return validationResult(
3719
+ `Workflow '${workflowId}' does not support final summary capture through workflows.checkpoint. Follow the workflow guidance and use capture directly for its primary record.`
3720
+ );
3721
+ }
3310
3722
  requireWriteAccess();
3723
+ const agentSessionId = getAgentSessionId();
3724
+ if (!agentSessionId) {
3725
+ return validationResult(
3726
+ "Active agent session required for workflow checkpoint persistence."
3727
+ );
3728
+ }
3729
+ const existingRun = await mcpQuery(
3730
+ "chainwork.getLatestWorkflowRun",
3731
+ {
3732
+ agentSessionId,
3733
+ workflowId: wf.id
3734
+ }
3735
+ );
3736
+ if (!existingRun) {
3737
+ return validationResult(
3738
+ `Workflow '${workflowId}' has no durable run to finalize yet. Record checkpoints for the earlier rounds first.`
3739
+ );
3740
+ }
3311
3741
  try {
3312
- const result = await mcpMutation("chain.createEntry", {
3313
- collectionSlug: wf.kbOutputCollection,
3314
- name: summaryName,
3315
- status: "draft",
3316
- data: {
3317
- [wf.kbOutputTemplate.descriptionField]: summaryDescription,
3318
- workflowType: wf.id,
3319
- completedRound: roundId,
3320
- date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
3742
+ const result = await mcpMutation("chainwork.finalizeWorkflowRun", {
3743
+ agentSessionId,
3744
+ workflowId: wf.id,
3745
+ workflowName: wf.name,
3746
+ templateSlug: template.slug,
3747
+ templateName: template.name,
3748
+ steps: wf.rounds.map((workflowRound) => ({
3749
+ roundId: workflowRound.id,
3750
+ num: workflowRound.num,
3751
+ label: workflowRound.label,
3752
+ type: workflowRound.type,
3753
+ outputField: workflowRound.outputSchema.field,
3754
+ outputFormat: workflowRound.outputSchema.format
3755
+ })),
3756
+ finalRoundId: roundId,
3757
+ finalOutput: normalizedOutput,
3758
+ summaryCapture: {
3759
+ collection: summaryCapture.collection,
3760
+ nameTemplate: summaryCapture.nameTemplate,
3761
+ descriptionField: summaryCapture.descriptionField,
3762
+ descriptionSource: summaryCapture.descriptionSource
3321
3763
  },
3322
- createdBy: `workflow:${wf.id}`
3764
+ summaryDescription,
3765
+ summaryName
3323
3766
  });
3324
3767
  lines.push(
3325
- `**Draft Created**: \`${result.entryId}\``,
3326
- `Collection: \`${wf.kbOutputCollection}\``,
3327
- `Name: ${summaryName}`,
3768
+ result.summary.created ? `**Draft Created**: \`${result.summary.entryId}\`` : `**Draft Already Exists**: \`${result.summary.entryId}\``,
3769
+ `Collection: \`${summaryCapture.collection}\``,
3770
+ `Name: ${result.summary.name}`,
3328
3771
  "",
3329
- `The summary is captured as a draft. Use \`commit-entry entryId="${result.entryId}"\` to promote it to the Chain.`,
3330
- `Use \`graph action=suggest entryId="${result.entryId}"\` to discover connections.`
3772
+ `The summary is captured as a draft. Use \`commit-entry entryId="${result.summary.entryId}"\` to promote it to the Chain.`,
3773
+ `Use \`graph action=suggest entryId="${result.summary.entryId}"\` to discover connections.`
3331
3774
  );
3332
3775
  return {
3333
3776
  content: [{ type: "text", text: lines.join("\n") }],
3334
3777
  structuredContent: success(
3335
- `Draft '${summaryName}' created (${result.entryId}) for workflow ${workflowId}.`,
3778
+ `Draft '${result.summary.name}' ${result.summary.created ? "created" : "reused"} (${result.summary.entryId}) for workflow ${workflowId}.`,
3336
3779
  {
3337
- entryId: result.entryId,
3780
+ entryId: result.summary.entryId,
3338
3781
  workflowId,
3339
3782
  roundId,
3340
- collection: wf.kbOutputCollection,
3341
- isFinal: true
3783
+ collection: summaryCapture.collection,
3784
+ isFinal: true,
3785
+ runId: result.runId,
3786
+ created: result.summary.created
3342
3787
  },
3343
3788
  [
3344
3789
  {
3345
3790
  tool: "commit-entry",
3346
3791
  description: "Promote draft to Chain",
3347
- parameters: { entryId: result.entryId }
3792
+ parameters: { entryId: result.summary.entryId }
3348
3793
  },
3349
3794
  {
3350
3795
  tool: "graph",
3351
3796
  description: "Discover connections",
3352
- parameters: { action: "suggest", entryId: result.entryId }
3797
+ parameters: { action: "suggest", entryId: result.summary.entryId }
3353
3798
  }
3354
3799
  ]
3355
3800
  )
@@ -3361,9 +3806,9 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3361
3806
  "",
3362
3807
  `The retro output is preserved in this conversation. `,
3363
3808
  `You can manually create the entry later using \`capture\` with:`,
3364
- `- Collection: \`${wf.kbOutputCollection}\``,
3365
- `- Name: ${summaryName}`,
3366
- `- Description: (copy from the conversation summary above)`
3809
+ `- Collection: \`${summaryCapture.collection}\``,
3810
+ `- Name: ${summaryName ?? summaryCapture.nameTemplate}`,
3811
+ `- Description: ${summaryDescription ?? outputPreview}`
3367
3812
  );
3368
3813
  return {
3369
3814
  content: [{ type: "text", text: lines.join("\n") }],
@@ -3376,42 +3821,122 @@ This checkpoint was NOT saved. The conversation context is preserved \u2014 cont
3376
3821
  };
3377
3822
  }
3378
3823
  } else {
3379
- lines.push(
3380
- `Round ${round.num} output recorded.`,
3381
- `Output: ${output.substring(0, 200)}${output.length > 200 ? "..." : ""}`
3382
- );
3383
- const currentIdx = wf.rounds.findIndex((r) => r.id === roundId);
3384
- let nextRound;
3385
- if (currentIdx < wf.rounds.length - 1) {
3386
- const next = wf.rounds[currentIdx + 1];
3387
- nextRound = { id: next.id, num: next.num, label: next.label };
3824
+ requireWriteAccess();
3825
+ const agentSessionId = getAgentSessionId();
3826
+ if (!agentSessionId) {
3827
+ return validationResult(
3828
+ "Active agent session required for workflow checkpoint persistence."
3829
+ );
3830
+ }
3831
+ try {
3832
+ const descriptor = getWorkflowDescriptor(workflowId);
3833
+ const template = descriptor ? getWorkflowTemplateMetadata(descriptor) : { slug: wf.id, name: wf.name };
3834
+ const checkpointResult = await mcpMutation("chainwork.recordWorkflowCheckpoint", {
3835
+ agentSessionId,
3836
+ workflowId: wf.id,
3837
+ workflowName: wf.name,
3838
+ templateSlug: template.slug,
3839
+ templateName: template.name,
3840
+ steps: wf.rounds.map((workflowRound) => ({
3841
+ roundId: workflowRound.id,
3842
+ num: workflowRound.num,
3843
+ label: workflowRound.label,
3844
+ type: workflowRound.type,
3845
+ outputField: workflowRound.outputSchema.field,
3846
+ outputFormat: workflowRound.outputSchema.format
3847
+ })),
3848
+ checkpoint: {
3849
+ roundId,
3850
+ output: normalizedOutput,
3851
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
3852
+ },
3853
+ restart
3854
+ });
3388
3855
  lines.push(
3389
- "",
3390
- `**Next**: Round ${next.num} \u2014 ${next.label}`,
3391
- `_${next.instruction}_`
3856
+ `Round ${round.num} output recorded.`,
3857
+ `Output: ${outputPreview.substring(0, 200)}${outputPreview.length > 200 ? "..." : ""}`,
3858
+ `Progress: ${checkpointResult.projection.completedCount}/${checkpointResult.projection.totalCount} rounds completed.`
3392
3859
  );
3393
- } else {
3860
+ const nextWorkflowRound = checkpointResult.projection.currentRoundId ? wf.rounds.find((workflowRound) => workflowRound.id === checkpointResult.projection.currentRoundId) : void 0;
3861
+ const nextRound = nextWorkflowRound ? { id: nextWorkflowRound.id, num: nextWorkflowRound.num, label: nextWorkflowRound.label } : void 0;
3862
+ if (nextWorkflowRound) {
3863
+ lines.push(
3864
+ "",
3865
+ `**Next**: Round ${nextWorkflowRound.num} \u2014 ${nextWorkflowRound.label}`,
3866
+ `_${nextWorkflowRound.instruction}_`
3867
+ );
3868
+ } else {
3869
+ lines.push(
3870
+ "",
3871
+ `**All rounds complete.** Call this tool again with \`isFinal: true\` to commit to the Chain.`
3872
+ );
3873
+ }
3874
+ return {
3875
+ content: [{ type: "text", text: lines.join("\n") }],
3876
+ structuredContent: success(
3877
+ `Round ${round.num} (${round.label}) output recorded for workflow ${workflowId}.`,
3878
+ {
3879
+ workflowId,
3880
+ roundId,
3881
+ roundNum: round.num,
3882
+ roundLabel: round.label,
3883
+ isFinal: false,
3884
+ nextRound,
3885
+ runId: checkpointResult.runId,
3886
+ runStatus: checkpointResult.run.status,
3887
+ progress: {
3888
+ completedCount: checkpointResult.projection.completedCount,
3889
+ totalCount: checkpointResult.projection.totalCount
3890
+ }
3891
+ }
3892
+ )
3893
+ };
3894
+ } catch (err) {
3895
+ const msg = err instanceof Error ? err.message : String(err);
3394
3896
  lines.push(
3897
+ `**Checkpoint persistence failed**: ${msg}`,
3395
3898
  "",
3396
- `**All rounds complete.** Call this tool again with \`isFinal: true\` to commit to the Chain.`
3899
+ `The conversation context is preserved, but this round was not saved as a durable workflow checkpoint.`
3397
3900
  );
3901
+ return {
3902
+ content: [{ type: "text", text: lines.join("\n") }],
3903
+ structuredContent: failure(
3904
+ "BACKEND_ERROR",
3905
+ `Workflow checkpoint persistence failed: ${msg}.`,
3906
+ "Continue the workflow in conversation or retry the checkpoint."
3907
+ )
3908
+ };
3398
3909
  }
3399
- return {
3400
- content: [{ type: "text", text: lines.join("\n") }],
3401
- structuredContent: success(
3402
- `Round ${round.num} (${round.label}) output recorded for workflow ${workflowId}.`,
3403
- {
3404
- workflowId,
3405
- roundId,
3406
- roundNum: round.num,
3407
- roundLabel: round.label,
3408
- isFinal: false,
3409
- nextRound
3410
- }
3411
- )
3412
- };
3413
3910
  }
3414
3911
  }
3912
+ function coerceWorkflowRunOutput(round, output) {
3913
+ if (typeof output !== "string") {
3914
+ assertWorkflowRunOutputMatchesRound(round, output);
3915
+ return output;
3916
+ }
3917
+ const trimmed = output.trim();
3918
+ switch (round.outputSchema.format) {
3919
+ case "freetext":
3920
+ return { format: "freetext", value: trimmed };
3921
+ case "list":
3922
+ return { format: "list", value: parseListOutput(trimmed) };
3923
+ case "choice":
3924
+ return { format: "choice", value: trimmed };
3925
+ case "structured":
3926
+ return { format: "structured", value: { text: trimmed } };
3927
+ }
3928
+ }
3929
+ function assertWorkflowRunOutputMatchesRound(round, output) {
3930
+ if (output.format !== round.outputSchema.format) {
3931
+ throw new Error(
3932
+ `Round '${round.id}' expects '${round.outputSchema.format}' output, received '${output.format}'.`
3933
+ );
3934
+ }
3935
+ }
3936
+ function parseListOutput(output) {
3937
+ const lines = output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => line.replace(/^[-*]\s+/, "").replace(/^\d+\.\s+/, "").trim()).filter(Boolean);
3938
+ return lines.length > 0 ? lines : [output];
3939
+ }
3415
3940
 
3416
3941
  // src/tools/facilitate.ts
3417
3942
  import { z as z12 } from "zod";
@@ -5533,7 +6058,7 @@ async function handleCommitConstellation(args) {
5533
6058
  }
5534
6059
  let contradictionWarnings = [];
5535
6060
  try {
5536
- const { runContradictionCheck } = await import("./smart-capture-2IM2565I.js");
6061
+ const { runContradictionCheck } = await import("./smart-capture-3OXWEXWM.js");
5537
6062
  const descField = betData.problem ?? betData.description ?? "";
5538
6063
  contradictionWarnings = await runContradictionCheck(
5539
6064
  betEntry.name ?? betId,
@@ -10363,6 +10888,7 @@ Make sure the rule is consistent with existing rules and doesn't contradict them
10363
10888
  },
10364
10889
  async ({ workflow: workflowId, context }) => {
10365
10890
  const wf = getWorkflow(workflowId);
10891
+ const descriptor = getWorkflowDescriptor(workflowId);
10366
10892
  if (!wf) {
10367
10893
  const available = listWorkflows().map((w) => `- **${w.id}**: ${w.name} \u2014 ${w.shortDescription}`).join("\n");
10368
10894
  return {
@@ -10383,15 +10909,18 @@ Use one of these IDs to run a workflow.`
10383
10909
  };
10384
10910
  }
10385
10911
  let kbContext = "";
10912
+ const contextCollection = descriptor ? getWorkflowContextCollection(descriptor) : wf.kbOutputCollection ?? null;
10386
10913
  try {
10387
- const recentDecisions = await mcpQuery("chain.listEntries", {
10388
- collectionSlug: wf.kbOutputCollection
10389
- });
10390
- const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
10391
- if (sorted.length > 0) {
10392
- kbContext = `
10393
- ## Recent ${wf.kbOutputCollection} entries (for context)
10914
+ if (contextCollection) {
10915
+ const recentDecisions = await mcpQuery("chain.listEntries", {
10916
+ collectionSlug: contextCollection
10917
+ });
10918
+ const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
10919
+ if (sorted.length > 0) {
10920
+ kbContext = `
10921
+ ## Recent ${contextCollection} entries (for context)
10394
10922
  ` + sorted.map((d) => `- ${d.entryId ?? ""}: ${d.name} [${d.status}]`).join("\n");
10923
+ }
10395
10924
  }
10396
10925
  } catch {
10397
10926
  kbContext = "\n_Could not load chain context \u2014 proceed without it._";
@@ -10405,12 +10934,34 @@ Use one of these IDs to run a workflow.`
10405
10934
  (q) => `**Question**: ${q.prompt}
10406
10935
  ` + (q.options ? q.options.map((o) => ` - ${o.id}: ${o.label}`).join("\n") : " _(open response)_")
10407
10936
  ).join("\n") : "") + `
10408
- **Output**: Capture to \`${r.outputSchema.field}\` (${r.outputSchema.format})`
10937
+ **Output**: Capture to \`${r.outputSchema.field}\` (${r.outputSchema.format})
10938
+ **Checkpoint**: Immediately persist this round with \`workflows action="checkpoint" workflowId="${wf.id}" roundId="${r.id}"\``
10409
10939
  ).join("\n\n---\n\n");
10410
10940
  const contextLine = context ? `
10411
10941
  The participant provided this context: "${context}"
10412
10942
  Use it \u2014 don't make them repeat themselves.
10413
10943
  ` : "";
10944
+ const outputDescription = descriptor ? describeWorkflowPrimaryOutput(descriptor) : wf.kbOutputCollection ? `Primary record routes to \`${wf.kbOutputCollection}\`.` : "Primary output routing is resolved at runtime.";
10945
+ const summaryCapture = descriptor ? getWorkflowSummaryCaptureDefinition(descriptor) : wf.kbOutputCollection && wf.kbOutputTemplate ? {
10946
+ collection: wf.kbOutputCollection,
10947
+ nameTemplate: wf.kbOutputTemplate.nameTemplate,
10948
+ descriptionField: wf.kbOutputTemplate.descriptionField
10949
+ } : null;
10950
+ const chainOutputBlock = summaryCapture ? `Primary record routes to \`${summaryCapture.collection}\`.
10951
+ Summary capture template: ${summaryCapture.nameTemplate}
10952
+ Description field: ${summaryCapture.descriptionField}
10953
+ ` : `Primary output: ${outputDescription}
10954
+ Use the workflow guidance to capture the primary record directly rather than relying on final summary auto-capture.
10955
+ `;
10956
+ const checkpointingBlock = `## Checkpointing & Resume
10957
+
10958
+ This workflow now supports durable workflow runs.
10959
+ 1. Before starting, check for an existing run in the current session with \`workflows action="get-run" workflowId="${wf.id}"\`.
10960
+ 2. If an active run exists, resume from its \`currentRoundId\` instead of restarting the workflow.
10961
+ 3. After completing EACH round, immediately call \`workflows action="checkpoint" workflowId="${wf.id}" roundId="<round-id>" output=<round-output>\`.
10962
+ 4. Only call \`isFinal: true\` from the terminal round when the workflow supports summary capture.
10963
+ 5. If a checkpoint fails, say so explicitly and continue the conversation as backup.
10964
+ `;
10414
10965
  return {
10415
10966
  messages: [
10416
10967
  {
@@ -10429,6 +10980,9 @@ ${wf.facilitatorPreamble}
10429
10980
 
10430
10981
  ---
10431
10982
 
10983
+ ${checkpointingBlock}
10984
+ ---
10985
+
10432
10986
  ## Rounds
10433
10987
 
10434
10988
  ${roundsPlan}
@@ -10443,10 +10997,7 @@ ${wf.errorRecovery}
10443
10997
 
10444
10998
  ## Chain Output
10445
10999
 
10446
- When complete, use \`capture\` to create a \`${wf.kbOutputCollection}\` entry.
10447
- Name template: ${wf.kbOutputTemplate.nameTemplate}
10448
- Description field: ${wf.kbOutputTemplate.descriptionField}
10449
- ` + kbContext + `
11000
+ ` + chainOutputBlock + kbContext + `
10450
11001
 
10451
11002
  ---
10452
11003
 
@@ -11045,4 +11596,4 @@ export {
11045
11596
  SERVER_VERSION,
11046
11597
  createProductBrainServer
11047
11598
  };
11048
- //# sourceMappingURL=chunk-SCSEEERT.js.map
11599
+ //# sourceMappingURL=chunk-QUWYOGHB.js.map