@exaudeus/workrail 1.16.1 → 1.17.1
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/application/services/workflow-interpreter.js +15 -7
- package/dist/manifest.json +54 -22
- package/dist/mcp/server.d.ts +18 -0
- package/dist/mcp/server.js +8 -1
- package/dist/mcp/transports/http-entry.d.ts +1 -0
- package/dist/mcp/transports/http-entry.js +87 -0
- package/dist/mcp/transports/http-listener.d.ts +9 -0
- package/dist/mcp/transports/http-listener.js +64 -0
- package/dist/mcp/transports/stdio-entry.d.ts +1 -0
- package/dist/mcp/transports/stdio-entry.js +92 -0
- package/dist/mcp/transports/transport-mode.d.ts +7 -0
- package/dist/mcp/transports/transport-mode.js +18 -0
- package/dist/mcp-server.js +21 -5
- package/dist/v2/durable-core/domain/decision-trace-builder.d.ts +2 -0
- package/dist/v2/durable-core/domain/decision-trace-builder.js +15 -0
- package/dist/v2/durable-core/domain/loop-control-evaluator.d.ts +1 -1
- package/dist/v2/durable-core/domain/loop-control-evaluator.js +16 -9
- package/dist/v2/durable-core/domain/prompt-renderer.js +49 -16
- package/dist/v2/durable-core/schemas/artifacts/loop-control.d.ts +1 -1
- package/dist/v2/durable-core/schemas/artifacts/loop-control.js +2 -5
- package/package.json +1 -1
- package/workflows/bug-investigation.agentic.v2.json +1 -1
- package/workflows/coding-task-workflow-agentic.v2.json +1 -1
- package/workflows/mr-review-workflow.agentic.v2.json +1 -1
- package/workflows/test-artifact-loop-control.json +2 -2
- package/workflows/workflow-for-workflows.json +2 -2
|
@@ -196,7 +196,7 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
196
196
|
return (0, neverthrow_1.ok)(shouldEnter);
|
|
197
197
|
}
|
|
198
198
|
case 'while': {
|
|
199
|
-
const res = this.evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, false);
|
|
199
|
+
const res = this.evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, false, trace);
|
|
200
200
|
if (res.isOk()) {
|
|
201
201
|
const source = loopCompiled.conditionSource?.kind === 'artifact_contract' ? 'artifact'
|
|
202
202
|
: loopCompiled.conditionSource?.kind === 'context_variable' ? 'context'
|
|
@@ -208,7 +208,7 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
208
208
|
return res;
|
|
209
209
|
}
|
|
210
210
|
case 'until': {
|
|
211
|
-
const res = this.evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, true);
|
|
211
|
+
const res = this.evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, true, trace);
|
|
212
212
|
if (res.isOk()) {
|
|
213
213
|
const source = loopCompiled.conditionSource?.kind === 'artifact_contract' ? 'artifact'
|
|
214
214
|
: loopCompiled.conditionSource?.kind === 'context_variable' ? 'context'
|
|
@@ -303,7 +303,7 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
303
303
|
return (0, neverthrow_1.err)(error_1.Err.invalidState('Non-exhaustive loop decision'));
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
|
-
evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, invertForUntil) {
|
|
306
|
+
evaluateWhileUntilCondition(loopCompiled, iteration, context, artifacts, frame, invertForUntil, traceEntries = []) {
|
|
307
307
|
const source = loopCompiled.conditionSource;
|
|
308
308
|
if (!source) {
|
|
309
309
|
if (!loopCompiled.loop.loop.condition) {
|
|
@@ -318,11 +318,19 @@ let WorkflowInterpreter = class WorkflowInterpreter {
|
|
|
318
318
|
}
|
|
319
319
|
switch (source.kind) {
|
|
320
320
|
case 'artifact_contract': {
|
|
321
|
-
const result = (0, loop_control_evaluator_1.evaluateLoopControlFromArtifacts)(artifacts
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
const result = (0, loop_control_evaluator_1.evaluateLoopControlFromArtifacts)(artifacts);
|
|
322
|
+
traceEntries.push((0, decision_trace_builder_1.traceArtifactMatchResult)(source.loopId, iteration, result));
|
|
323
|
+
switch (result.kind) {
|
|
324
|
+
case 'found':
|
|
325
|
+
return (0, neverthrow_1.ok)(result.decision === 'continue');
|
|
326
|
+
case 'not_found':
|
|
327
|
+
case 'invalid':
|
|
328
|
+
return (0, neverthrow_1.ok)(true);
|
|
329
|
+
default: {
|
|
330
|
+
const _exhaustive = result;
|
|
331
|
+
return (0, neverthrow_1.ok)(true);
|
|
332
|
+
}
|
|
324
333
|
}
|
|
325
|
-
return (0, neverthrow_1.ok)(true);
|
|
326
334
|
}
|
|
327
335
|
case 'context_variable': {
|
|
328
336
|
const raw = (0, condition_evaluator_1.evaluateCondition)(source.condition, this.projectLoopContextAtIteration(loopCompiled.loop, iteration, context));
|
package/dist/manifest.json
CHANGED
|
@@ -110,8 +110,8 @@
|
|
|
110
110
|
"bytes": 1507
|
|
111
111
|
},
|
|
112
112
|
"application/services/workflow-interpreter.js": {
|
|
113
|
-
"sha256": "
|
|
114
|
-
"bytes":
|
|
113
|
+
"sha256": "df7a04c0b2c578cf54e3bc818a08b6e1af18ea75f6ade4901b3e9ceeabf46814",
|
|
114
|
+
"bytes": 21430
|
|
115
115
|
},
|
|
116
116
|
"application/services/workflow-service.d.ts": {
|
|
117
117
|
"sha256": "b92da17c6d91c90758ec42b4ee3bc448e5d5b1dfe7351f2fe0f5e1d10a715ec6",
|
|
@@ -598,8 +598,8 @@
|
|
|
598
598
|
"bytes": 67
|
|
599
599
|
},
|
|
600
600
|
"mcp-server.js": {
|
|
601
|
-
"sha256": "
|
|
602
|
-
"bytes":
|
|
601
|
+
"sha256": "bf97a81d9bc5bb40d10ec8a06c73b322320e0a0afdaa88f19730efe781ace7cf",
|
|
602
|
+
"bytes": 1135
|
|
603
603
|
},
|
|
604
604
|
"mcp/error-mapper.d.ts": {
|
|
605
605
|
"sha256": "c6e9db65c565063726442b73a4a0cf1a1c07d54c778a85cf0122c4c04ea6a5a9",
|
|
@@ -842,12 +842,12 @@
|
|
|
842
842
|
"bytes": 12115
|
|
843
843
|
},
|
|
844
844
|
"mcp/server.d.ts": {
|
|
845
|
-
"sha256": "
|
|
846
|
-
"bytes":
|
|
845
|
+
"sha256": "782a9a50797cac9c5f30e79556f809351d7eb176a16d7c603c09a5133cc25303",
|
|
846
|
+
"bytes": 882
|
|
847
847
|
},
|
|
848
848
|
"mcp/server.js": {
|
|
849
|
-
"sha256": "
|
|
850
|
-
"bytes":
|
|
849
|
+
"sha256": "5086b482d2eb3c42caac637282a5361b4db6e5d14aaeeb7c5ffdf75d211af0d8",
|
|
850
|
+
"bytes": 13422
|
|
851
851
|
},
|
|
852
852
|
"mcp/tool-description-provider.d.ts": {
|
|
853
853
|
"sha256": "1d46abc3112e11b68e57197e846f5708293ec9b2281fa71a9124ee2aad71e41b",
|
|
@@ -881,6 +881,38 @@
|
|
|
881
881
|
"sha256": "aa364f3e613fc61a39fc7336a723231da06852d92ec7840a95327aa370e42e1c",
|
|
882
882
|
"bytes": 8360
|
|
883
883
|
},
|
|
884
|
+
"mcp/transports/http-entry.d.ts": {
|
|
885
|
+
"sha256": "35d313b120dcf38643de9462559163581b89943fe432706986252e8b698b9507",
|
|
886
|
+
"bytes": 70
|
|
887
|
+
},
|
|
888
|
+
"mcp/transports/http-entry.js": {
|
|
889
|
+
"sha256": "3ecf3a39d4eb2d4b844e83296cf04238acfc7c91e9eca0e10c1e8a0fbae71721",
|
|
890
|
+
"bytes": 4279
|
|
891
|
+
},
|
|
892
|
+
"mcp/transports/http-listener.d.ts": {
|
|
893
|
+
"sha256": "5c512d31f001bd65eaaca5421ca8f80e614896fdfe803773aa1f6c893257f20e",
|
|
894
|
+
"bytes": 316
|
|
895
|
+
},
|
|
896
|
+
"mcp/transports/http-listener.js": {
|
|
897
|
+
"sha256": "9e055636c281b5fe09623aa017d2e773462ddbe65e42c0938ed897cac606bb62",
|
|
898
|
+
"bytes": 2555
|
|
899
|
+
},
|
|
900
|
+
"mcp/transports/stdio-entry.d.ts": {
|
|
901
|
+
"sha256": "4ced3c9e00ef67555781dea74315290eea8a9dbffd38155bc00c3fb07b0c1794",
|
|
902
|
+
"bytes": 59
|
|
903
|
+
},
|
|
904
|
+
"mcp/transports/stdio-entry.js": {
|
|
905
|
+
"sha256": "a1f75205084e632b78e1ec28fb82498f04ecdb66fa4a9d9678d885268b0ed885",
|
|
906
|
+
"bytes": 4509
|
|
907
|
+
},
|
|
908
|
+
"mcp/transports/transport-mode.d.ts": {
|
|
909
|
+
"sha256": "1c59128ab0174bd2a113fff17521e6339ca367f2b8980c2f2c164ec393c10518",
|
|
910
|
+
"bytes": 206
|
|
911
|
+
},
|
|
912
|
+
"mcp/transports/transport-mode.js": {
|
|
913
|
+
"sha256": "6627303319e87e047caf99d910626a331742089b90db180f6031aa9a1afc98eb",
|
|
914
|
+
"bytes": 747
|
|
915
|
+
},
|
|
884
916
|
"mcp/types.d.ts": {
|
|
885
917
|
"sha256": "2ef7f9c1bffbe365b06469dfad43b45c4ae3b5292dd269b33423bf1c8c9c7c43",
|
|
886
918
|
"bytes": 4355
|
|
@@ -1330,12 +1362,12 @@
|
|
|
1330
1362
|
"bytes": 1734
|
|
1331
1363
|
},
|
|
1332
1364
|
"v2/durable-core/domain/decision-trace-builder.d.ts": {
|
|
1333
|
-
"sha256": "
|
|
1334
|
-
"bytes":
|
|
1365
|
+
"sha256": "bcc7892a25099be88ca92770d98f2e7e232eef007ff8f48001aefe9c08ee876c",
|
|
1366
|
+
"bytes": 1742
|
|
1335
1367
|
},
|
|
1336
1368
|
"v2/durable-core/domain/decision-trace-builder.js": {
|
|
1337
|
-
"sha256": "
|
|
1338
|
-
"bytes":
|
|
1369
|
+
"sha256": "411da4951de3603c811afe8f4fa93de724f162834522c84e99a219c28248bf73",
|
|
1370
|
+
"bytes": 4332
|
|
1339
1371
|
},
|
|
1340
1372
|
"v2/durable-core/domain/function-definition-expander.d.ts": {
|
|
1341
1373
|
"sha256": "2720e7c4dfbd9ec0cdf75eb4384fdbea18d4430aeae7f13d9215f0e5e4980468",
|
|
@@ -1354,12 +1386,12 @@
|
|
|
1354
1386
|
"bytes": 908
|
|
1355
1387
|
},
|
|
1356
1388
|
"v2/durable-core/domain/loop-control-evaluator.d.ts": {
|
|
1357
|
-
"sha256": "
|
|
1358
|
-
"bytes":
|
|
1389
|
+
"sha256": "fbda4d2cb49d71e066f1b9bc967d5b89f4e2d1c692b865b026cf9e0bad80ad99",
|
|
1390
|
+
"bytes": 517
|
|
1359
1391
|
},
|
|
1360
1392
|
"v2/durable-core/domain/loop-control-evaluator.js": {
|
|
1361
|
-
"sha256": "
|
|
1362
|
-
"bytes":
|
|
1393
|
+
"sha256": "0db4f39141a02c3a60c50955e71e5d925a473cdd01a6d6661b2d6df5d4ca2643",
|
|
1394
|
+
"bytes": 1071
|
|
1363
1395
|
},
|
|
1364
1396
|
"v2/durable-core/domain/loop-runtime.d.ts": {
|
|
1365
1397
|
"sha256": "ee4f8857d72bf0066bccc351a4e8fd8e4800f3d540e9851bb4978179b8ebfc04",
|
|
@@ -1398,8 +1430,8 @@
|
|
|
1398
1430
|
"bytes": 935
|
|
1399
1431
|
},
|
|
1400
1432
|
"v2/durable-core/domain/prompt-renderer.js": {
|
|
1401
|
-
"sha256": "
|
|
1402
|
-
"bytes":
|
|
1433
|
+
"sha256": "5021f3cc9e59db56c5aeddcc8ed6f69df7ea1b1f3011d685695021e039376e49",
|
|
1434
|
+
"bytes": 13910
|
|
1403
1435
|
},
|
|
1404
1436
|
"v2/durable-core/domain/reason-model.d.ts": {
|
|
1405
1437
|
"sha256": "650fcb2d9969a4e6123cccbd4913f4d57aeab21a19bb907aa1e11f95e5a95089",
|
|
@@ -1586,12 +1618,12 @@
|
|
|
1586
1618
|
"bytes": 1728
|
|
1587
1619
|
},
|
|
1588
1620
|
"v2/durable-core/schemas/artifacts/loop-control.d.ts": {
|
|
1589
|
-
"sha256": "
|
|
1590
|
-
"bytes":
|
|
1621
|
+
"sha256": "714e2980400da0aaf814c691d8915653393316e70bc9af9da72130bb07e959be",
|
|
1622
|
+
"bytes": 2768
|
|
1591
1623
|
},
|
|
1592
1624
|
"v2/durable-core/schemas/artifacts/loop-control.js": {
|
|
1593
|
-
"sha256": "
|
|
1594
|
-
"bytes":
|
|
1625
|
+
"sha256": "c4b2ed38f9fdda3fbe3e4b68b0a228890fd9a7c73117a6d202928acabe6fc0f9",
|
|
1626
|
+
"bytes": 2115
|
|
1595
1627
|
},
|
|
1596
1628
|
"v2/durable-core/schemas/compiled-workflow/index.d.ts": {
|
|
1597
1629
|
"sha256": "d13d2dbbcd0971f7c170788552f533c2605f26e0f352939bf5cd2348437d7519",
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import type { ToolContext } from './types.js';
|
|
2
|
+
import { WorkspaceRootsManager } from './workspace-roots-manager.js';
|
|
3
|
+
import { type ToolAnnotations } from './tool-factory.js';
|
|
4
|
+
import type { WrappedToolHandler } from './types/workflow-tool-edition.js';
|
|
5
|
+
interface Tool {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: Record<string, unknown>;
|
|
9
|
+
annotations?: ToolAnnotations;
|
|
10
|
+
}
|
|
2
11
|
export declare function createToolContext(): Promise<ToolContext>;
|
|
12
|
+
export interface ComposedServer {
|
|
13
|
+
readonly server: import('@modelcontextprotocol/sdk/server/index.js').Server;
|
|
14
|
+
readonly ctx: ToolContext;
|
|
15
|
+
readonly rootsManager: WorkspaceRootsManager;
|
|
16
|
+
readonly tools: readonly Tool[];
|
|
17
|
+
readonly handlers: Record<string, WrappedToolHandler>;
|
|
18
|
+
}
|
|
19
|
+
export declare function composeServer(): Promise<ComposedServer>;
|
|
3
20
|
export declare function startServer(): Promise<void>;
|
|
21
|
+
export {};
|
package/dist/mcp/server.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createToolContext = createToolContext;
|
|
37
|
+
exports.composeServer = composeServer;
|
|
37
38
|
exports.startServer = startServer;
|
|
38
39
|
const zod_to_json_schema_js_1 = require("./zod-to-json-schema.js");
|
|
39
40
|
const container_js_1 = require("../di/container.js");
|
|
@@ -134,7 +135,7 @@ function toMcpTool(tool) {
|
|
|
134
135
|
annotations: tool.annotations,
|
|
135
136
|
};
|
|
136
137
|
}
|
|
137
|
-
async function
|
|
138
|
+
async function composeServer() {
|
|
138
139
|
await (0, container_js_1.bootstrap)({ runtimeMode: { kind: 'production' } });
|
|
139
140
|
const ctx = await createToolContext();
|
|
140
141
|
if (ctx.v2 && ctx.httpServer && ctx.v2.dataDir && ctx.v2.directoryListing) {
|
|
@@ -204,6 +205,12 @@ async function startServer() {
|
|
|
204
205
|
: ctx;
|
|
205
206
|
return handler(args ?? {}, requestCtx);
|
|
206
207
|
});
|
|
208
|
+
return { server, ctx, rootsManager, tools, handlers };
|
|
209
|
+
}
|
|
210
|
+
async function startServer() {
|
|
211
|
+
const { server, ctx, rootsManager } = await composeServer();
|
|
212
|
+
const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
|
|
213
|
+
const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
|
|
207
214
|
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
|
|
208
215
|
try {
|
|
209
216
|
const result = await server.listRoots();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startHttpServer(port: number): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.startHttpServer = startHttpServer;
|
|
40
|
+
const server_js_1 = require("../server.js");
|
|
41
|
+
const http_listener_js_1 = require("./http-listener.js");
|
|
42
|
+
const container_js_1 = require("../../di/container.js");
|
|
43
|
+
const tokens_js_1 = require("../../di/tokens.js");
|
|
44
|
+
const crypto = __importStar(require("crypto"));
|
|
45
|
+
const express_1 = __importDefault(require("express"));
|
|
46
|
+
async function startHttpServer(port) {
|
|
47
|
+
const { server, ctx } = await (0, server_js_1.composeServer)();
|
|
48
|
+
const listener = (0, http_listener_js_1.createHttpListener)(port);
|
|
49
|
+
const { StreamableHTTPServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/streamableHttp.js')));
|
|
50
|
+
const transport = new StreamableHTTPServerTransport({
|
|
51
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
52
|
+
enableJsonResponse: true,
|
|
53
|
+
});
|
|
54
|
+
listener.app.use(express_1.default.json());
|
|
55
|
+
listener.app.post('/mcp', (req, res) => transport.handleRequest(req, res, req.body));
|
|
56
|
+
listener.app.get('/mcp', (req, res) => transport.handleRequest(req, res));
|
|
57
|
+
listener.app.delete('/mcp', (req, res) => transport.handleRequest(req, res));
|
|
58
|
+
await listener.start();
|
|
59
|
+
await server.connect(transport);
|
|
60
|
+
const boundPort = listener.getBoundPort();
|
|
61
|
+
console.error('[Transport] WorkRail MCP Server running on HTTP');
|
|
62
|
+
console.error(`[Transport] MCP endpoint: http://localhost:${boundPort}/mcp`);
|
|
63
|
+
const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
|
|
64
|
+
const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
|
|
65
|
+
const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
|
|
66
|
+
processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
|
|
67
|
+
processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
|
|
68
|
+
processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
|
|
69
|
+
let shutdownStarted = false;
|
|
70
|
+
shutdownEvents.onShutdown((event) => {
|
|
71
|
+
if (shutdownStarted)
|
|
72
|
+
return;
|
|
73
|
+
shutdownStarted = true;
|
|
74
|
+
void (async () => {
|
|
75
|
+
try {
|
|
76
|
+
console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
|
|
77
|
+
await listener.stop();
|
|
78
|
+
await ctx.httpServer?.stop();
|
|
79
|
+
terminator.terminate({ kind: 'success' });
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.error('[Shutdown] Error while stopping services:', err);
|
|
83
|
+
terminator.terminate({ kind: 'failure' });
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Application } from 'express';
|
|
2
|
+
export interface HttpListener {
|
|
3
|
+
readonly app: Application;
|
|
4
|
+
readonly requestedPort: number;
|
|
5
|
+
getBoundPort(): number | null;
|
|
6
|
+
start(): Promise<void>;
|
|
7
|
+
stop(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare function createHttpListener(requestedPort: number): HttpListener;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createHttpListener = createHttpListener;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const http_1 = require("http");
|
|
9
|
+
function createHttpListener(requestedPort) {
|
|
10
|
+
const app = (0, express_1.default)();
|
|
11
|
+
let state = { kind: 'not_started' };
|
|
12
|
+
return {
|
|
13
|
+
app,
|
|
14
|
+
requestedPort,
|
|
15
|
+
getBoundPort() {
|
|
16
|
+
return state.kind === 'running' ? state.boundPort : null;
|
|
17
|
+
},
|
|
18
|
+
async start() {
|
|
19
|
+
if (state.kind === 'running') {
|
|
20
|
+
throw new Error('[HttpListener] Already started');
|
|
21
|
+
}
|
|
22
|
+
if (state.kind === 'stopped') {
|
|
23
|
+
throw new Error('[HttpListener] Cannot restart a stopped listener');
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const server = (0, http_1.createServer)(app);
|
|
27
|
+
server.on('error', (err) => {
|
|
28
|
+
if (err.code === 'EADDRINUSE') {
|
|
29
|
+
reject(new Error(`[HttpListener] Port ${requestedPort} is already in use. ` +
|
|
30
|
+
`Set WORKRAIL_HTTP_PORT to a different port or stop the conflicting process.`));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
reject(err);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
server.listen(requestedPort, () => {
|
|
37
|
+
const addr = server.address();
|
|
38
|
+
const boundPort = addr && typeof addr === 'object' ? addr.port : requestedPort;
|
|
39
|
+
state = { kind: 'running', server, boundPort };
|
|
40
|
+
console.error(`[HttpListener] MCP HTTP transport listening on port ${boundPort}`);
|
|
41
|
+
resolve();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
async stop() {
|
|
46
|
+
if (state.kind !== 'running') {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const serverToClose = state.server;
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
serverToClose.close((err) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
reject(err);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
state = { kind: 'stopped' };
|
|
57
|
+
console.error(`[HttpListener] MCP HTTP transport stopped`);
|
|
58
|
+
resolve();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startStdioServer(): Promise<void>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.startStdioServer = startStdioServer;
|
|
37
|
+
const server_js_1 = require("../server.js");
|
|
38
|
+
const container_js_1 = require("../../di/container.js");
|
|
39
|
+
const tokens_js_1 = require("../../di/tokens.js");
|
|
40
|
+
async function startStdioServer() {
|
|
41
|
+
const { server, ctx, rootsManager } = await (0, server_js_1.composeServer)();
|
|
42
|
+
const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
|
|
43
|
+
const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
|
|
44
|
+
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
|
|
45
|
+
try {
|
|
46
|
+
const result = await server.listRoots();
|
|
47
|
+
rootsManager.updateRootUris(result.roots.map((r) => r.uri));
|
|
48
|
+
console.error(`[Roots] Updated workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
console.error('[Roots] Failed to fetch updated roots after change notification');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const transport = new StdioServerTransport();
|
|
55
|
+
await server.connect(transport);
|
|
56
|
+
try {
|
|
57
|
+
const result = await server.listRoots();
|
|
58
|
+
rootsManager.updateRootUris(result.roots.map((r) => r.uri));
|
|
59
|
+
console.error(`[Roots] Initial workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
console.error('[Roots] Client does not support roots/list; workspace context will use server CWD fallback');
|
|
63
|
+
}
|
|
64
|
+
console.error('[Transport] WorkRail MCP Server running on stdio');
|
|
65
|
+
const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
|
|
66
|
+
const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
|
|
67
|
+
const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
|
|
68
|
+
processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
|
|
69
|
+
processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
|
|
70
|
+
processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
|
|
71
|
+
process.stdin.on('end', () => {
|
|
72
|
+
console.error('[MCP] stdin closed, initiating shutdown');
|
|
73
|
+
shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' });
|
|
74
|
+
});
|
|
75
|
+
let shutdownStarted = false;
|
|
76
|
+
shutdownEvents.onShutdown((event) => {
|
|
77
|
+
if (shutdownStarted)
|
|
78
|
+
return;
|
|
79
|
+
shutdownStarted = true;
|
|
80
|
+
void (async () => {
|
|
81
|
+
try {
|
|
82
|
+
console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
|
|
83
|
+
await ctx.httpServer?.stop();
|
|
84
|
+
terminator.terminate({ kind: 'success' });
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error('[Shutdown] Error while stopping services:', err);
|
|
88
|
+
terminator.terminate({ kind: 'failure' });
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTransportMode = resolveTransportMode;
|
|
4
|
+
function resolveTransportMode(env) {
|
|
5
|
+
const transport = env.WORKRAIL_TRANSPORT ?? 'stdio';
|
|
6
|
+
if (transport === 'http') {
|
|
7
|
+
const portStr = env.WORKRAIL_HTTP_PORT ?? '3100';
|
|
8
|
+
const port = parseInt(portStr, 10);
|
|
9
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
10
|
+
throw new Error(`Invalid WORKRAIL_HTTP_PORT: "${portStr}". Must be a number between 1-65535.`);
|
|
11
|
+
}
|
|
12
|
+
return { kind: 'http', port };
|
|
13
|
+
}
|
|
14
|
+
if (transport !== 'stdio') {
|
|
15
|
+
throw new Error(`Invalid WORKRAIL_TRANSPORT: "${transport}". Must be 'stdio' or 'http'.`);
|
|
16
|
+
}
|
|
17
|
+
return { kind: 'stdio' };
|
|
18
|
+
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -2,10 +2,26 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.startServer = void 0;
|
|
5
|
+
const transport_mode_js_1 = require("./mcp/transports/transport-mode.js");
|
|
6
|
+
const stdio_entry_js_1 = require("./mcp/transports/stdio-entry.js");
|
|
7
|
+
const http_entry_js_1 = require("./mcp/transports/http-entry.js");
|
|
8
|
+
const assert_never_js_1 = require("./runtime/assert-never.js");
|
|
5
9
|
var server_js_1 = require("./mcp/server.js");
|
|
6
10
|
Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return server_js_1.startServer; } });
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const mode = (0, transport_mode_js_1.resolveTransportMode)(process.env);
|
|
12
|
+
switch (mode.kind) {
|
|
13
|
+
case 'stdio':
|
|
14
|
+
(0, stdio_entry_js_1.startStdioServer)().catch((error) => {
|
|
15
|
+
console.error('[stdio] Fatal error:', error);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
});
|
|
18
|
+
break;
|
|
19
|
+
case 'http':
|
|
20
|
+
(0, http_entry_js_1.startHttpServer)(mode.port).catch((error) => {
|
|
21
|
+
console.error('[http] Fatal error:', error);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
(0, assert_never_js_1.assertNever)(mode);
|
|
27
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Result } from 'neverthrow';
|
|
2
|
+
import type { LoopControlEvaluationResult } from './loop-control-evaluator.js';
|
|
2
3
|
export type DecisionTraceEntryKind = 'selected_next_step' | 'evaluated_condition' | 'entered_loop' | 'exited_loop' | 'detected_non_tip_advance';
|
|
3
4
|
export type DecisionTraceRef = {
|
|
4
5
|
readonly kind: 'step_id';
|
|
@@ -22,6 +23,7 @@ export declare function traceEnteredLoop(loopId: string, iteration: number): Dec
|
|
|
22
23
|
export declare function traceEvaluatedCondition(loopId: string, iteration: number, result: boolean, source: 'artifact' | 'context' | 'legacy'): DecisionTraceEntry;
|
|
23
24
|
export declare function traceExitedLoop(loopId: string, reason: string): DecisionTraceEntry;
|
|
24
25
|
export declare function traceSelectedNextStep(stepId: string): DecisionTraceEntry;
|
|
26
|
+
export declare function traceArtifactMatchResult(loopId: string, iteration: number, result: LoopControlEvaluationResult): DecisionTraceEntry;
|
|
25
27
|
export declare function applyTraceBudget(entries: readonly DecisionTraceEntry[]): readonly DecisionTraceEntry[];
|
|
26
28
|
export declare function buildDecisionTraceEventData(traceId: string, entries: readonly DecisionTraceEntry[]): Result<{
|
|
27
29
|
readonly traceId: string;
|
|
@@ -4,6 +4,7 @@ exports.traceEnteredLoop = traceEnteredLoop;
|
|
|
4
4
|
exports.traceEvaluatedCondition = traceEvaluatedCondition;
|
|
5
5
|
exports.traceExitedLoop = traceExitedLoop;
|
|
6
6
|
exports.traceSelectedNextStep = traceSelectedNextStep;
|
|
7
|
+
exports.traceArtifactMatchResult = traceArtifactMatchResult;
|
|
7
8
|
exports.applyTraceBudget = applyTraceBudget;
|
|
8
9
|
exports.buildDecisionTraceEventData = buildDecisionTraceEventData;
|
|
9
10
|
const neverthrow_1 = require("neverthrow");
|
|
@@ -38,6 +39,20 @@ function traceSelectedNextStep(stepId) {
|
|
|
38
39
|
refs: [{ kind: 'step_id', stepId }],
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
function traceArtifactMatchResult(loopId, iteration, result) {
|
|
43
|
+
const detail = (() => {
|
|
44
|
+
switch (result.kind) {
|
|
45
|
+
case 'found': return `decision="${result.decision}"`;
|
|
46
|
+
case 'not_found': return result.reason;
|
|
47
|
+
case 'invalid': return result.reason;
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
return {
|
|
51
|
+
kind: 'evaluated_condition',
|
|
52
|
+
summary: `Loop '${loopId}' iteration ${iteration}: artifact match=${result.kind} — ${detail}`,
|
|
53
|
+
refs: [{ kind: 'loop_id', loopId }, { kind: 'iteration', value: iteration }],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
41
56
|
function applyTraceBudget(entries) {
|
|
42
57
|
const capped = entries.slice(0, constants_js_1.MAX_DECISION_TRACE_ENTRIES);
|
|
43
58
|
let totalBytes = 0;
|
|
@@ -10,4 +10,4 @@ export type LoopControlEvaluationResult = {
|
|
|
10
10
|
readonly kind: 'invalid';
|
|
11
11
|
readonly reason: string;
|
|
12
12
|
};
|
|
13
|
-
export declare function evaluateLoopControlFromArtifacts(artifacts: readonly unknown[]
|
|
13
|
+
export declare function evaluateLoopControlFromArtifacts(artifacts: readonly unknown[]): LoopControlEvaluationResult;
|
|
@@ -2,23 +2,30 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.evaluateLoopControlFromArtifacts = evaluateLoopControlFromArtifacts;
|
|
4
4
|
const index_js_1 = require("../schemas/artifacts/index.js");
|
|
5
|
-
function evaluateLoopControlFromArtifacts(artifacts
|
|
5
|
+
function evaluateLoopControlFromArtifacts(artifacts) {
|
|
6
6
|
if (artifacts.length === 0) {
|
|
7
7
|
return {
|
|
8
8
|
kind: 'not_found',
|
|
9
|
-
reason:
|
|
9
|
+
reason: 'No artifacts provided for loop control evaluation',
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
const artifact = (0, index_js_1.findLoopControlArtifact)(artifacts
|
|
13
|
-
if (
|
|
12
|
+
const artifact = (0, index_js_1.findLoopControlArtifact)(artifacts);
|
|
13
|
+
if (artifact) {
|
|
14
14
|
return {
|
|
15
|
-
kind: '
|
|
16
|
-
|
|
15
|
+
kind: 'found',
|
|
16
|
+
decision: artifact.decision,
|
|
17
|
+
artifact,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const hasKindMatch = artifacts.some(index_js_1.isLoopControlArtifact);
|
|
21
|
+
if (hasKindMatch) {
|
|
22
|
+
return {
|
|
23
|
+
kind: 'invalid',
|
|
24
|
+
reason: 'Artifact with kind=wr.loop_control found but failed schema validation (bad loopId format, missing decision, or extra fields)',
|
|
17
25
|
};
|
|
18
26
|
}
|
|
19
27
|
return {
|
|
20
|
-
kind: '
|
|
21
|
-
|
|
22
|
-
artifact,
|
|
28
|
+
kind: 'not_found',
|
|
29
|
+
reason: 'No wr.loop_control artifact found',
|
|
23
30
|
};
|
|
24
31
|
}
|
|
@@ -114,24 +114,55 @@ function applyPromptBudget(combinedPrompt) {
|
|
|
114
114
|
const decoder = new TextDecoder('utf-8');
|
|
115
115
|
return decoder.decode(truncatedBytes) + markerText + omissionNote;
|
|
116
116
|
}
|
|
117
|
+
function resolveParentLoopMaxIterations(workflow, stepId) {
|
|
118
|
+
for (const step of workflow.definition.steps) {
|
|
119
|
+
if ((0, workflow_js_1.isLoopStepDefinition)(step) && Array.isArray(step.body)) {
|
|
120
|
+
for (const bodyStep of step.body) {
|
|
121
|
+
if (bodyStep.id === stepId) {
|
|
122
|
+
return step.loop.maxIterations;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
function buildScopeInstruction(iteration, maxIterations) {
|
|
130
|
+
if (iteration <= 1)
|
|
131
|
+
return 'Focus on what the first pass missed — do not re-litigate settled findings.';
|
|
132
|
+
if (maxIterations !== undefined && iteration + 1 >= maxIterations)
|
|
133
|
+
return 'FINAL PASS — verify prior amendments landed correctly. Only flag regressions or clearly missed issues.';
|
|
134
|
+
return 'Diminishing returns expected. Focus on gaps and regressions, not fresh territory.';
|
|
135
|
+
}
|
|
117
136
|
function buildLoopContextBanner(args) {
|
|
118
137
|
if (args.loopPath.length === 0 || args.isExitStep)
|
|
119
138
|
return '';
|
|
120
139
|
const current = args.loopPath[args.loopPath.length - 1];
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
const iterationNumber = current.iteration + 1;
|
|
141
|
+
const maxIter = args.maxIterations;
|
|
142
|
+
if (current.iteration === 0) {
|
|
143
|
+
const bound = maxIter !== undefined ? ` (up to ${maxIter} passes)` : '';
|
|
144
|
+
return [
|
|
145
|
+
`> This step is part of an iterative loop${bound}. After your work, a decision step determines whether another pass is needed.`,
|
|
146
|
+
``,
|
|
147
|
+
].join('\n');
|
|
148
|
+
}
|
|
149
|
+
const lines = ['---'];
|
|
150
|
+
if (maxIter !== undefined) {
|
|
151
|
+
const filled = Math.min(iterationNumber, maxIter);
|
|
152
|
+
const empty = Math.max(maxIter - filled, 0);
|
|
153
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
|
|
154
|
+
lines.push(`**Progress: [${bar}] Pass ${iterationNumber} of ${maxIter}**`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
lines.push(`**Pass ${iterationNumber}**`);
|
|
158
|
+
}
|
|
159
|
+
lines.push('');
|
|
160
|
+
lines.push(`**Scope**: ${buildScopeInstruction(current.iteration, maxIter)}`);
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push('Your previous work is in the **Ancestry Recap** below. Build on it — do not repeat work already done.');
|
|
163
|
+
lines.push('---');
|
|
164
|
+
lines.push('');
|
|
165
|
+
return lines.join('\n');
|
|
135
166
|
}
|
|
136
167
|
function formatOutputContractRequirements(outputContract) {
|
|
137
168
|
const contractRef = outputContract?.contractRef;
|
|
@@ -143,7 +174,8 @@ function formatOutputContractRequirements(outputContract) {
|
|
|
143
174
|
`Artifact contract: ${index_js_2.LOOP_CONTROL_CONTRACT_REF}`,
|
|
144
175
|
`Provide an artifact with kind: "wr.loop_control"`,
|
|
145
176
|
`Required field: decision ("continue" | "stop")`,
|
|
146
|
-
`
|
|
177
|
+
`Do NOT include loopId — the engine matches automatically`,
|
|
178
|
+
`Canonical format:\n\`\`\`json\n{ "artifacts": [{ "kind": "wr.loop_control", "decision": "stop" }] }\n\`\`\``,
|
|
147
179
|
];
|
|
148
180
|
default:
|
|
149
181
|
return [
|
|
@@ -184,7 +216,8 @@ function renderPendingPrompt(args) {
|
|
|
184
216
|
? step.outputContract
|
|
185
217
|
: undefined;
|
|
186
218
|
const isExitStep = outputContract?.contractRef === index_js_2.LOOP_CONTROL_CONTRACT_REF;
|
|
187
|
-
const
|
|
219
|
+
const maxIterations = resolveParentLoopMaxIterations(args.workflow, args.stepId);
|
|
220
|
+
const loopBanner = buildLoopContextBanner({ loopPath: args.loopPath, isExitStep, maxIterations });
|
|
188
221
|
const validationCriteria = step.validationCriteria;
|
|
189
222
|
const requirements = (0, validation_requirements_extractor_js_1.extractValidationRequirements)(validationCriteria);
|
|
190
223
|
const requirementsSection = requirements.length > 0
|
|
@@ -63,4 +63,4 @@ export declare const LoopControlArtifactV1Schema: z.ZodObject<{
|
|
|
63
63
|
export type LoopControlArtifactV1 = z.infer<typeof LoopControlArtifactV1Schema>;
|
|
64
64
|
export declare function isLoopControlArtifact(artifact: unknown): artifact is LoopControlArtifactV1;
|
|
65
65
|
export declare function parseLoopControlArtifact(artifact: unknown): LoopControlArtifactV1 | null;
|
|
66
|
-
export declare function findLoopControlArtifact(artifacts: readonly unknown[]
|
|
66
|
+
export declare function findLoopControlArtifact(artifacts: readonly unknown[]): LoopControlArtifactV1 | null;
|
|
@@ -35,17 +35,14 @@ function parseLoopControlArtifact(artifact) {
|
|
|
35
35
|
const result = exports.LoopControlArtifactV1Schema.safeParse(artifact);
|
|
36
36
|
return result.success ? result.data : null;
|
|
37
37
|
}
|
|
38
|
-
function findLoopControlArtifact(artifacts
|
|
38
|
+
function findLoopControlArtifact(artifacts) {
|
|
39
39
|
for (let i = artifacts.length - 1; i >= 0; i--) {
|
|
40
40
|
const artifact = artifacts[i];
|
|
41
41
|
if (!isLoopControlArtifact(artifact))
|
|
42
42
|
continue;
|
|
43
43
|
const parsed = parseLoopControlArtifact(artifact);
|
|
44
|
-
if (
|
|
45
|
-
continue;
|
|
46
|
-
if (parsed.loopId === undefined || parsed.loopId === expectedLoopId) {
|
|
44
|
+
if (parsed)
|
|
47
45
|
return parsed;
|
|
48
|
-
}
|
|
49
46
|
}
|
|
50
47
|
return null;
|
|
51
48
|
}
|
package/package.json
CHANGED
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
{
|
|
106
106
|
"id": "phase-4b-loop-decision",
|
|
107
107
|
"title": "Evidence Loop Decision",
|
|
108
|
-
"prompt": "Decide whether the evidence loop should continue.\n\nDecision rules:\n- if `contradictionCount > 0` → continue\n- else if `unresolvedEvidenceGapCount > 0` → continue\n- else if `hasStrongAlternative = true` and the alternative is not meaningfully weaker → continue\n- else if `diagnosisType = inconclusive_but_narrowed` and further evidence is not realistically available → stop with bounded uncertainty\n- else → stop\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
108
|
+
"prompt": "Decide whether the evidence loop should continue.\n\nDecision rules:\n- if `contradictionCount > 0` → continue\n- else if `unresolvedEvidenceGapCount > 0` → continue\n- else if `hasStrongAlternative = true` and the alternative is not meaningfully weaker → continue\n- else if `diagnosisType = inconclusive_but_narrowed` and further evidence is not realistically available → stop with bounded uncertainty\n- else → stop\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\"\n }]\n}\n```",
|
|
109
109
|
"requireConfirmation": true,
|
|
110
110
|
"outputContract": {
|
|
111
111
|
"contractRef": "wr.contracts.loop_control"
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
{
|
|
140
140
|
"id": "phase-4c-loop-decision",
|
|
141
141
|
"title": "Loop Exit Decision",
|
|
142
|
-
"prompt": "Provide a loop control artifact.\n\nDecision rules:\n- if `planFindings` is non-empty → continue\n- if `planFindings` is empty → stop, but enumerate what was checked to justify the clean pass\n- if max iterations reached → stop and document remaining concerns\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
142
|
+
"prompt": "Provide a loop control artifact.\n\nDecision rules:\n- if `planFindings` is non-empty → continue\n- if `planFindings` is empty → stop, but enumerate what was checked to justify the clean pass\n- if max iterations reached → stop and document remaining concerns\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\"\n }]\n}\n```",
|
|
143
143
|
"requireConfirmation": true,
|
|
144
144
|
"outputContract": {
|
|
145
145
|
"contractRef": "wr.contracts.loop_control"
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
{
|
|
189
189
|
"id": "phase-4c-loop-decision",
|
|
190
190
|
"title": "Synthesis Loop Decision",
|
|
191
|
-
"prompt": "Decide whether the synthesis loop should continue.\n\nDecision rules:\n- if `contradictionCount > 0` → continue\n- else if `coverageUncertainCount > 0` and the uncertainty materially affects the recommendation → continue\n- else if `falsePositiveRiskCount > 0` → continue\n- else if `recommendationDriftDetected = true` → continue\n- else → stop\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
191
|
+
"prompt": "Decide whether the synthesis loop should continue.\n\nDecision rules:\n- if `contradictionCount > 0` → continue\n- else if `coverageUncertainCount > 0` and the uncertainty materially affects the recommendation → continue\n- else if `falsePositiveRiskCount > 0` → continue\n- else if `recommendationDriftDetected = true` → continue\n- else → stop\n\nOutput exactly:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\"\n }]\n}\n```",
|
|
192
192
|
"requireConfirmation": true,
|
|
193
193
|
"outputContract": {
|
|
194
194
|
"contractRef": "wr.contracts.loop_control"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"id": "init-loop",
|
|
15
15
|
"title": "Initialize Loop",
|
|
16
|
-
"prompt": "Initialize the bounded iteration loop.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
16
|
+
"prompt": "Initialize the bounded iteration loop.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\",\n \"metadata\": {\n \"reason\": \"Starting iteration loop\"\n }\n }]\n}\n```\n\nThe artifact must have:\n- kind: 'wr.loop_control'\n- decision: 'continue' or 'stop'",
|
|
17
17
|
"requireConfirmation": false,
|
|
18
18
|
"outputContract": {
|
|
19
19
|
"contractRef": "wr.contracts.loop_control"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{
|
|
43
43
|
"id": "decide-continue",
|
|
44
44
|
"title": "Loop Decision",
|
|
45
|
-
"prompt": "Decide whether to continue or stop the loop.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
45
|
+
"prompt": "Decide whether to continue or stop the loop.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\",\n \"metadata\": {\n \"reason\": \"More work needed\",\n \"iterationIndex\": 1\n }\n }]\n}\n```\n\n**Decision logic:**\n- If more work is needed: decision = 'continue'\n- If work is complete: decision = 'stop'\n\nThe artifact must have:\n- kind: 'wr.loop_control'\n- decision: 'continue' or 'stop'",
|
|
46
46
|
"requireConfirmation": true,
|
|
47
47
|
"outputContract": {
|
|
48
48
|
"contractRef": "wr.contracts.loop_control"
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
{
|
|
90
90
|
"id": "discovery-loop-decision",
|
|
91
91
|
"title": "Discovery Loop Decision",
|
|
92
|
-
"prompt": "**Provide a loop control decision artifact.**\n\nBased on the discovery analysis above, decide whether to continue gathering requirements or proceed to synthesis.\n\n**Decision logic:**\n- If critical information is still missing (problem unclear, user needs undefined, success criteria absent): decision = 'continue'\n- If discovery is 80%+ complete (clear problem, user needs, success criteria, basic constraints): decision = 'stop'\n\n**Output (exact format):**\nProvide a loop control artifact:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
92
|
+
"prompt": "**Provide a loop control decision artifact.**\n\nBased on the discovery analysis above, decide whether to continue gathering requirements or proceed to synthesis.\n\n**Decision logic:**\n- If critical information is still missing (problem unclear, user needs undefined, success criteria absent): decision = 'continue'\n- If discovery is 80%+ complete (clear problem, user needs, success criteria, basic constraints): decision = 'stop'\n\n**Output (exact format):**\nProvide a loop control artifact:\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\",\n \"metadata\": {\n \"reason\": \"One sentence explaining why continuing or stopping\"\n }\n }]\n}\n```",
|
|
93
93
|
"agentRole": "You are a discovery analyst making an explicit go/no-go decision on whether enough requirements have been gathered.",
|
|
94
94
|
"requireConfirmation": false,
|
|
95
95
|
"outputContract": {
|
|
@@ -418,7 +418,7 @@
|
|
|
418
418
|
{
|
|
419
419
|
"id": "satisfaction-check",
|
|
420
420
|
"title": "Iteration Satisfaction Check & Loop Decision",
|
|
421
|
-
"prompt": "Let's assess your satisfaction with the workflow so far.\n\n**Rate your satisfaction (1-10):**\n- 10: Perfect! Ready to deploy\n- 8-9: Very good, minor tweaks only\n- 6-7: Good foundation, needs refinement\n- 4-5: Major improvements needed\n- 1-3: Significant rework required\n\n**Decision logic:**\n- Score 9+: decision = 'stop' (proceed to completion)\n- Score <9: decision = 'continue' (iterate again, up to 3 times total)\n\nAgent: Set context.satisfactionScore and increment context.iterationCount.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"
|
|
421
|
+
"prompt": "Let's assess your satisfaction with the workflow so far.\n\n**Rate your satisfaction (1-10):**\n- 10: Perfect! Ready to deploy\n- 8-9: Very good, minor tweaks only\n- 6-7: Good foundation, needs refinement\n- 4-5: Major improvements needed\n- 1-3: Significant rework required\n\n**Decision logic:**\n- Score 9+: decision = 'stop' (proceed to completion)\n- Score <9: decision = 'continue' (iterate again, up to 3 times total)\n\nAgent: Set context.satisfactionScore and increment context.iterationCount.\n\n**Provide a loop control artifact:**\n```json\n{\n \"artifacts\": [{\n \"kind\": \"wr.loop_control\",\n \"decision\": \"continue\",\n \"metadata\": {\n \"reason\": \"Satisfaction score X/10 - [brief reason]\"\n }\n }]\n}\n```",
|
|
422
422
|
"agentRole": "You are a quality assessment specialist. Guide the user through evaluating their workflow objectively. Provide a loop control artifact with the decision.",
|
|
423
423
|
"requireConfirmation": false,
|
|
424
424
|
"outputContract": {
|