@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.
- package/dist/{chunk-SCSEEERT.js → chunk-QUWYOGHB.js} +660 -109
- package/dist/chunk-QUWYOGHB.js.map +1 -0
- package/dist/{chunk-ED3KCWZE.js → chunk-WEH5CZJ7.js} +2 -2
- package/dist/chunk-WEH5CZJ7.js.map +1 -0
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{smart-capture-2IM2565I.js → smart-capture-3OXWEXWM.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-ED3KCWZE.js.map +0 -1
- package/dist/chunk-SCSEEERT.js.map +0 -1
- /package/dist/{smart-capture-2IM2565I.js.map → smart-capture-3OXWEXWM.js.map} +0 -0
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
unknownAction,
|
|
39
39
|
validationResult,
|
|
40
40
|
withEnvelope
|
|
41
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
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
|
|
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
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
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
|
|
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
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
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
|
|
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
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
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
|
|
3165
|
-
["retro",
|
|
3166
|
-
["shape",
|
|
3167
|
-
["implementation-review",
|
|
3168
|
-
["process-change",
|
|
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
|
-
|
|
3389
|
+
const workflow = WORKFLOWS.get(id);
|
|
3390
|
+
return workflow ? cloneWorkflowDefinition(workflow) : void 0;
|
|
3172
3391
|
}
|
|
3173
3392
|
function listWorkflows() {
|
|
3174
|
-
return Array.from(
|
|
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(
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
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**:
|
|
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
|
|
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 {
|
|
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(
|
|
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
|
|
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 (
|
|
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("
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
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
|
-
|
|
3764
|
+
summaryDescription,
|
|
3765
|
+
summaryName
|
|
3323
3766
|
});
|
|
3324
3767
|
lines.push(
|
|
3325
|
-
`**Draft Created**: \`${result.entryId}\``,
|
|
3326
|
-
`Collection: \`${
|
|
3327
|
-
`Name: ${
|
|
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 '${
|
|
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:
|
|
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: \`${
|
|
3365
|
-
`- Name: ${summaryName}`,
|
|
3366
|
-
`- Description:
|
|
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
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
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
|
-
|
|
3391
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
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
|
-
|
|
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-
|
|
11599
|
+
//# sourceMappingURL=chunk-QUWYOGHB.js.map
|