@exaudeus/workrail 3.18.1 → 3.19.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/console/assets/index-QhCFuxQV.js +28 -0
- package/dist/console/index.html +1 -1
- package/dist/manifest.json +36 -28
- package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +2 -0
- package/dist/mcp/handlers/shared/request-workflow-reader.js +20 -1
- package/dist/mcp/handlers/shared/workflow-source-visibility.js +2 -4
- package/dist/mcp/handlers/shared/workspace-path-utils.d.ts +2 -0
- package/dist/mcp/handlers/shared/workspace-path-utils.js +29 -0
- package/dist/mcp/server.js +25 -4
- package/dist/mcp/tool-call-timing.d.ts +4 -0
- package/dist/mcp/tool-call-timing.js +52 -0
- package/dist/v2/durable-core/domain/observation-builder.js +2 -0
- package/dist/v2/infra/local/data-dir/index.d.ts +1 -0
- package/dist/v2/infra/local/data-dir/index.js +3 -0
- package/dist/v2/ports/data-dir.port.d.ts +1 -0
- package/dist/v2/usecases/console-routes.d.ts +1 -1
- package/dist/v2/usecases/console-routes.js +61 -4
- package/package.json +1 -1
- package/dist/console/assets/index-DMaX2-CW.js +0 -28
package/dist/console/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>WorkRail Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/console/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/console/assets/index-QhCFuxQV.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/console/assets/index-ibLhWBmX.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/dist/manifest.json
CHANGED
|
@@ -369,16 +369,16 @@
|
|
|
369
369
|
"sha256": "5fe866e54f796975dec5d8ba9983aefd86074db212d3fccd64eed04bc9f0b3da",
|
|
370
370
|
"bytes": 8011
|
|
371
371
|
},
|
|
372
|
-
"console/assets/index-
|
|
373
|
-
"sha256": "
|
|
374
|
-
"bytes":
|
|
372
|
+
"console/assets/index-QhCFuxQV.js": {
|
|
373
|
+
"sha256": "0096a86267fdf969abc3e3976ca44b3d3447271c7394345c8d6c27c084e6412c",
|
|
374
|
+
"bytes": 719937
|
|
375
375
|
},
|
|
376
376
|
"console/assets/index-ibLhWBmX.css": {
|
|
377
377
|
"sha256": "346e55635d4d3dc2836dae83edb8563872721bf4b0e7e1ecf47fb9603424c206",
|
|
378
378
|
"bytes": 58826
|
|
379
379
|
},
|
|
380
380
|
"console/index.html": {
|
|
381
|
-
"sha256": "
|
|
381
|
+
"sha256": "4232baa5128b860125a649d8e135da858f07290bbb9da04861945649b53ac85d",
|
|
382
382
|
"bytes": 417
|
|
383
383
|
},
|
|
384
384
|
"core/error-handler.d.ts": {
|
|
@@ -734,12 +734,12 @@
|
|
|
734
734
|
"bytes": 2785
|
|
735
735
|
},
|
|
736
736
|
"mcp/handlers/shared/request-workflow-reader.d.ts": {
|
|
737
|
-
"sha256": "
|
|
738
|
-
"bytes":
|
|
737
|
+
"sha256": "94eae7516f1ca0aef0c9995d0fd41fc206abb001970cebcac35b912624341d0a",
|
|
738
|
+
"bytes": 2103
|
|
739
739
|
},
|
|
740
740
|
"mcp/handlers/shared/request-workflow-reader.js": {
|
|
741
|
-
"sha256": "
|
|
742
|
-
"bytes":
|
|
741
|
+
"sha256": "cfd2946ec9deb52c21611b21fc07859f35edaaf2c85d15743833380352dedf6f",
|
|
742
|
+
"bytes": 11702
|
|
743
743
|
},
|
|
744
744
|
"mcp/handlers/shared/with-timeout.d.ts": {
|
|
745
745
|
"sha256": "31ca3db008cb5cd439e0d1132bc4b29be0900c403c452931e3a24a50e45beb54",
|
|
@@ -754,8 +754,16 @@
|
|
|
754
754
|
"bytes": 1772
|
|
755
755
|
},
|
|
756
756
|
"mcp/handlers/shared/workflow-source-visibility.js": {
|
|
757
|
-
"sha256": "
|
|
758
|
-
"bytes":
|
|
757
|
+
"sha256": "59f5b6f50a46a708c19649ab6dc0c90f21d9d4f151b52f454dc38a2dad344717",
|
|
758
|
+
"bytes": 4526
|
|
759
|
+
},
|
|
760
|
+
"mcp/handlers/shared/workspace-path-utils.d.ts": {
|
|
761
|
+
"sha256": "c660cf0605f3d23c2826d69c5ba47aea4866ab929a94cb5cdde882cedde047ab",
|
|
762
|
+
"bytes": 169
|
|
763
|
+
},
|
|
764
|
+
"mcp/handlers/shared/workspace-path-utils.js": {
|
|
765
|
+
"sha256": "c375c508fb830dc87b930eaeb99a1443b075c2d3f4f323875c3fb56107f82a05",
|
|
766
|
+
"bytes": 1261
|
|
759
767
|
},
|
|
760
768
|
"mcp/handlers/v2-advance-core.d.ts": {
|
|
761
769
|
"sha256": "0a07ea15caf7b0a3123d3b0b44c2b85392d090dd140bf82370fd0e84d1aa5b0e",
|
|
@@ -1026,8 +1034,8 @@
|
|
|
1026
1034
|
"bytes": 960
|
|
1027
1035
|
},
|
|
1028
1036
|
"mcp/server.js": {
|
|
1029
|
-
"sha256": "
|
|
1030
|
-
"bytes":
|
|
1037
|
+
"sha256": "a54d39ce8d7282abe16d140abbe1011e67cd3f9d9353f40727c44c1c212a90c2",
|
|
1038
|
+
"bytes": 16152
|
|
1031
1039
|
},
|
|
1032
1040
|
"mcp/step-content-envelope.d.ts": {
|
|
1033
1041
|
"sha256": "19bd63c4d4de1d5d93393d346625d28ffd1bebdc320d4ba4e694cb740ec97d3b",
|
|
@@ -1038,12 +1046,12 @@
|
|
|
1038
1046
|
"bytes": 483
|
|
1039
1047
|
},
|
|
1040
1048
|
"mcp/tool-call-timing.d.ts": {
|
|
1041
|
-
"sha256": "
|
|
1042
|
-
"bytes":
|
|
1049
|
+
"sha256": "d775153fb5bb7c7b2bc896b2f9a57dc48ee0d0949307778dcdfea293bf0e394b",
|
|
1050
|
+
"bytes": 1320
|
|
1043
1051
|
},
|
|
1044
1052
|
"mcp/tool-call-timing.js": {
|
|
1045
|
-
"sha256": "
|
|
1046
|
-
"bytes":
|
|
1053
|
+
"sha256": "b3ae45196ab4f9d089a77703efaaa17e7e012e7019b1a5cfee9b128e67119c0c",
|
|
1054
|
+
"bytes": 4776
|
|
1047
1055
|
},
|
|
1048
1056
|
"mcp/tool-description-provider.d.ts": {
|
|
1049
1057
|
"sha256": "1d46abc3112e11b68e57197e846f5708293ec9b2281fa71a9124ee2aad71e41b",
|
|
@@ -1666,8 +1674,8 @@
|
|
|
1666
1674
|
"bytes": 619
|
|
1667
1675
|
},
|
|
1668
1676
|
"v2/durable-core/domain/observation-builder.js": {
|
|
1669
|
-
"sha256": "
|
|
1670
|
-
"bytes":
|
|
1677
|
+
"sha256": "df833dd1e0663acd4daf99cf27dc599b2c5e4a9e9c60ef88c1b0932892f9c62a",
|
|
1678
|
+
"bytes": 1951
|
|
1671
1679
|
},
|
|
1672
1680
|
"v2/durable-core/domain/outputs.d.ts": {
|
|
1673
1681
|
"sha256": "adc32e4b86c8036eac61096fe83371140c7de140db414227041a8854435f8f54",
|
|
@@ -2190,12 +2198,12 @@
|
|
|
2190
2198
|
"bytes": 457
|
|
2191
2199
|
},
|
|
2192
2200
|
"v2/infra/local/data-dir/index.d.ts": {
|
|
2193
|
-
"sha256": "
|
|
2194
|
-
"bytes":
|
|
2201
|
+
"sha256": "b51169bfbaabb5149f874dc7b545099bc67e3615d92ee1a11163b39f1d12e218",
|
|
2202
|
+
"bytes": 1016
|
|
2195
2203
|
},
|
|
2196
2204
|
"v2/infra/local/data-dir/index.js": {
|
|
2197
|
-
"sha256": "
|
|
2198
|
-
"bytes":
|
|
2205
|
+
"sha256": "9ed70045f305f8a2257cab5fd5c7c2b9d12b678d15a67dde177145d1524c0f6b",
|
|
2206
|
+
"bytes": 3671
|
|
2199
2207
|
},
|
|
2200
2208
|
"v2/infra/local/directory-listing/index.d.ts": {
|
|
2201
2209
|
"sha256": "3139014cb738db3b0f10beca01a3a4a35b9ab8e72c8889b3bbff204fdbcb6b6c",
|
|
@@ -2366,8 +2374,8 @@
|
|
|
2366
2374
|
"bytes": 77
|
|
2367
2375
|
},
|
|
2368
2376
|
"v2/ports/data-dir.port.d.ts": {
|
|
2369
|
-
"sha256": "
|
|
2370
|
-
"bytes":
|
|
2377
|
+
"sha256": "6713270d6e894e6ecf3cd6f0583dd16edfea771827ea2693f6ea54b970e97eb2",
|
|
2378
|
+
"bytes": 779
|
|
2371
2379
|
},
|
|
2372
2380
|
"v2/ports/data-dir.port.js": {
|
|
2373
2381
|
"sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
|
|
@@ -2646,12 +2654,12 @@
|
|
|
2646
2654
|
"bytes": 4795
|
|
2647
2655
|
},
|
|
2648
2656
|
"v2/usecases/console-routes.d.ts": {
|
|
2649
|
-
"sha256": "
|
|
2650
|
-
"bytes":
|
|
2657
|
+
"sha256": "1afd41269475fecd4a27ac1c5faccf1e369ec8bd8c9a9c36d38ed92c79e4ec51",
|
|
2658
|
+
"bytes": 508
|
|
2651
2659
|
},
|
|
2652
2660
|
"v2/usecases/console-routes.js": {
|
|
2653
|
-
"sha256": "
|
|
2654
|
-
"bytes":
|
|
2661
|
+
"sha256": "315a10a46cacd8bf279ecdc72b2cfac912f6d6ab5f606419d16fd734c9406718",
|
|
2662
|
+
"bytes": 15503
|
|
2655
2663
|
},
|
|
2656
2664
|
"v2/usecases/console-service.d.ts": {
|
|
2657
2665
|
"sha256": "489a44091dc1b15ff938a60f9cf3ac28363614b9f0446286f0914157528c7eb3",
|
|
@@ -31,6 +31,8 @@ export interface WorkflowReaderForRequestResult {
|
|
|
31
31
|
readonly stalePaths: readonly string[];
|
|
32
32
|
readonly managedSourceRecords: readonly ManagedSourceRecordV2[];
|
|
33
33
|
readonly staleManagedRecords: readonly ManagedSourceRecordV2[];
|
|
34
|
+
readonly excludedByScope: readonly string[];
|
|
34
35
|
readonly managedStoreError?: string;
|
|
35
36
|
}
|
|
37
|
+
export declare function filterRememberedRootsForWorkspace(allRoots: readonly string[], workspace: string): Promise<readonly string[]>;
|
|
36
38
|
export declare function createWorkflowReaderForRequest(options: RequestWorkflowReaderOptions): Promise<WorkflowReaderForRequestResult>;
|
|
@@ -8,6 +8,7 @@ exports.hasRequestWorkspaceSignal = hasRequestWorkspaceSignal;
|
|
|
8
8
|
exports.resolveRequestWorkspaceDirectory = resolveRequestWorkspaceDirectory;
|
|
9
9
|
exports.toProjectWorkflowDirectory = toProjectWorkflowDirectory;
|
|
10
10
|
exports.discoverRootedWorkflowDirectories = discoverRootedWorkflowDirectories;
|
|
11
|
+
exports.filterRememberedRootsForWorkspace = filterRememberedRootsForWorkspace;
|
|
11
12
|
exports.createWorkflowReaderForRequest = createWorkflowReaderForRequest;
|
|
12
13
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
13
14
|
const path_1 = __importDefault(require("path"));
|
|
@@ -15,6 +16,7 @@ const url_1 = require("url");
|
|
|
15
16
|
const enhanced_multi_source_workflow_storage_js_1 = require("../../../infrastructure/storage/enhanced-multi-source-workflow-storage.js");
|
|
16
17
|
const schema_validating_workflow_storage_js_1 = require("../../../infrastructure/storage/schema-validating-workflow-storage.js");
|
|
17
18
|
const with_timeout_js_1 = require("./with-timeout.js");
|
|
19
|
+
const workspace_path_utils_js_1 = require("./workspace-path-utils.js");
|
|
18
20
|
const SKIP_DIRS = new Set([
|
|
19
21
|
'.git', 'node_modules',
|
|
20
22
|
'build', 'dist', 'out', 'target',
|
|
@@ -99,10 +101,26 @@ async function _doWalk(cacheKey, roots, now) {
|
|
|
99
101
|
walkCache.set(cacheKey, { result, expiresAt: now + WALK_CACHE_TTL_MS });
|
|
100
102
|
return result;
|
|
101
103
|
}
|
|
104
|
+
async function filterRememberedRootsForWorkspace(allRoots, workspace) {
|
|
105
|
+
const ancestorRoots = allRoots.filter((r) => (0, workspace_path_utils_js_1.isWorkspaceAncestor)(r, workspace));
|
|
106
|
+
const nonAncestorRoots = allRoots.filter((r) => !(0, workspace_path_utils_js_1.isWorkspaceAncestor)(r, workspace));
|
|
107
|
+
let siblingRoots = [];
|
|
108
|
+
if (nonAncestorRoots.length > 0) {
|
|
109
|
+
const workspaceCommonDir = await (0, workspace_path_utils_js_1.getGitCommonDir)(workspace);
|
|
110
|
+
if (workspaceCommonDir !== null) {
|
|
111
|
+
const commonDirResults = await Promise.all(nonAncestorRoots.map((r) => (0, workspace_path_utils_js_1.getGitCommonDir)(r)));
|
|
112
|
+
siblingRoots = nonAncestorRoots.filter((_, i) => commonDirResults[i] === workspaceCommonDir);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return [...ancestorRoots, ...siblingRoots];
|
|
116
|
+
}
|
|
102
117
|
async function createWorkflowReaderForRequest(options) {
|
|
103
118
|
const workspaceDirectory = resolveRequestWorkspaceDirectory(options);
|
|
104
119
|
const projectWorkflowDirectory = toProjectWorkflowDirectory(workspaceDirectory);
|
|
105
|
-
const
|
|
120
|
+
const allRememberedRoots = await listRememberedRoots(options.rememberedRootsStore);
|
|
121
|
+
const resolvedWorkspace = path_1.default.resolve(workspaceDirectory);
|
|
122
|
+
const rememberedRoots = await filterRememberedRootsForWorkspace(allRememberedRoots, resolvedWorkspace);
|
|
123
|
+
const excludedByScope = allRememberedRoots.filter((root) => !rememberedRoots.includes(root));
|
|
106
124
|
let discoveryResult;
|
|
107
125
|
try {
|
|
108
126
|
discoveryResult = await (0, with_timeout_js_1.withTimeout)(discoverRootedWorkflowDirectories(rememberedRoots), DISCOVERY_TIMEOUT_MS, 'workflow_root_discovery');
|
|
@@ -159,6 +177,7 @@ async function createWorkflowReaderForRequest(options) {
|
|
|
159
177
|
stalePaths: allStalePaths,
|
|
160
178
|
managedSourceRecords: activeManagedRecords,
|
|
161
179
|
staleManagedRecords,
|
|
180
|
+
excludedByScope,
|
|
162
181
|
...(managedStoreError !== undefined ? { managedStoreError } : {}),
|
|
163
182
|
};
|
|
164
183
|
}
|
|
@@ -8,6 +8,7 @@ exports.detectWorkflowMigrationGuidance = detectWorkflowMigrationGuidance;
|
|
|
8
8
|
exports.isCompositeWorkflowReader = isCompositeWorkflowReader;
|
|
9
9
|
exports.deriveGroupLabel = deriveGroupLabel;
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const workspace_path_utils_js_1 = require("./workspace-path-utils.js");
|
|
11
12
|
function toWorkflowVisibility(workflow, rememberedRoots, options = {}) {
|
|
12
13
|
const source = {
|
|
13
14
|
kind: workflow.source.kind,
|
|
@@ -70,10 +71,7 @@ function deriveRootedSharingContext(workflow, rememberedRoots) {
|
|
|
70
71
|
const sourcePath = path_1.default.resolve(workflow.source.directoryPath);
|
|
71
72
|
for (const record of rememberedRoots) {
|
|
72
73
|
const rootPath = path_1.default.resolve(record.path);
|
|
73
|
-
|
|
74
|
-
const isUnderRoot = relative.length === 0 ||
|
|
75
|
-
(!relative.startsWith('..') && !path_1.default.isAbsolute(relative));
|
|
76
|
-
if (!isUnderRoot)
|
|
74
|
+
if (!(0, workspace_path_utils_js_1.isWorkspaceAncestor)(rootPath, sourcePath))
|
|
77
75
|
continue;
|
|
78
76
|
return {
|
|
79
77
|
kind: 'remembered_root',
|
|
@@ -0,0 +1,29 @@
|
|
|
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.isWorkspaceAncestor = isWorkspaceAncestor;
|
|
7
|
+
exports.getGitCommonDir = getGitCommonDir;
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const util_1 = require("util");
|
|
12
|
+
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
13
|
+
function isWorkspaceAncestor(root, workspace) {
|
|
14
|
+
const rel = path_1.default.relative(path_1.default.resolve(root), path_1.default.resolve(workspace));
|
|
15
|
+
return rel.length === 0 || (!rel.startsWith('..') && !path_1.default.isAbsolute(rel));
|
|
16
|
+
}
|
|
17
|
+
async function getGitCommonDir(dirPath) {
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execFileAsync('git', ['-C', dirPath, 'rev-parse', '--git-common-dir'], { encoding: 'utf-8', timeout: 500 });
|
|
20
|
+
const raw = stdout.trim();
|
|
21
|
+
if (!raw)
|
|
22
|
+
return null;
|
|
23
|
+
const resolved = path_1.default.resolve(dirPath, raw);
|
|
24
|
+
return await promises_1.default.realpath(resolved);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/mcp/server.js
CHANGED
|
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.createToolContext = createToolContext;
|
|
37
37
|
exports.composeServer = composeServer;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
38
40
|
const zod_to_json_schema_js_1 = require("./zod-to-json-schema.js");
|
|
39
41
|
const container_js_1 = require("../di/container.js");
|
|
40
42
|
const tokens_js_1 = require("../di/tokens.js");
|
|
@@ -164,6 +166,18 @@ async function composeServer() {
|
|
|
164
166
|
await (0, container_js_1.bootstrap)();
|
|
165
167
|
const ctx = await createToolContext();
|
|
166
168
|
const timingRingBuffer = new tool_call_timing_js_1.ToolCallTimingRingBuffer(tool_call_timing_js_1.DEFAULT_RING_BUFFER_CAPACITY);
|
|
169
|
+
let serverVersion = 'unknown';
|
|
170
|
+
try {
|
|
171
|
+
const pkgPath = path.resolve(__dirname, '../../package.json');
|
|
172
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
173
|
+
if (pkgJson.version)
|
|
174
|
+
serverVersion = pkgJson.version;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
}
|
|
178
|
+
const toolCallsPerfFile = ctx.v2?.dataDir
|
|
179
|
+
? path.join(ctx.v2.dataDir.perfDir(), 'tool-calls.jsonl')
|
|
180
|
+
: null;
|
|
167
181
|
if (ctx.v2 && ctx.httpServer && ctx.v2.dataDir && ctx.v2.directoryListing) {
|
|
168
182
|
const { ConsoleService } = await Promise.resolve().then(() => __importStar(require('../v2/usecases/console-service.js')));
|
|
169
183
|
const { mountConsoleRoutes } = await Promise.resolve().then(() => __importStar(require('../v2/usecases/console-routes.js')));
|
|
@@ -174,7 +188,7 @@ async function composeServer() {
|
|
|
174
188
|
snapshotStore: ctx.v2.snapshotStore,
|
|
175
189
|
pinnedWorkflowStore: ctx.v2.pinnedStore,
|
|
176
190
|
});
|
|
177
|
-
ctx.httpServer.mountRoutes((app) => mountConsoleRoutes(app, consoleService, ctx.workflowService, timingRingBuffer));
|
|
191
|
+
ctx.httpServer.mountRoutes((app) => mountConsoleRoutes(app, consoleService, ctx.workflowService, timingRingBuffer, toolCallsPerfFile ?? undefined, serverVersion));
|
|
178
192
|
console.error('[Console] v2 Console API routes mounted at /api/v2/');
|
|
179
193
|
}
|
|
180
194
|
ctx.httpServer?.finalize();
|
|
@@ -218,9 +232,16 @@ async function composeServer() {
|
|
|
218
232
|
tools,
|
|
219
233
|
}));
|
|
220
234
|
const devMode = (0, dev_mode_js_1.isDevMode)();
|
|
221
|
-
const
|
|
222
|
-
? (0, tool_call_timing_js_1.
|
|
223
|
-
:
|
|
235
|
+
const diskSink = toolCallsPerfFile
|
|
236
|
+
? (0, tool_call_timing_js_1.createDiskPersistSink)(toolCallsPerfFile, serverVersion)
|
|
237
|
+
: null;
|
|
238
|
+
const timingSink = diskSink
|
|
239
|
+
? devMode
|
|
240
|
+
? (0, tool_call_timing_js_1.composeSinks)((0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer), diskSink, (0, tool_call_timing_js_1.createDevPerfSink)())
|
|
241
|
+
: (0, tool_call_timing_js_1.composeSinks)((0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer), diskSink)
|
|
242
|
+
: devMode
|
|
243
|
+
? (0, tool_call_timing_js_1.composeSinks)((0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer), (0, tool_call_timing_js_1.createDevPerfSink)())
|
|
244
|
+
: (0, tool_call_timing_js_1.createRingBufferSink)(timingRingBuffer);
|
|
224
245
|
if (devMode) {
|
|
225
246
|
console.error('[PerfTrace] WORKRAIL_DEV=1 -- tool call timing active');
|
|
226
247
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export type ToolCallOutcome = 'success' | 'error' | 'unknown_tool';
|
|
2
|
+
export type ToolCallTimingEntry = ToolCallTiming & {
|
|
3
|
+
readonly serverVersion: string;
|
|
4
|
+
};
|
|
2
5
|
export interface ToolCallTiming {
|
|
3
6
|
readonly toolName: string;
|
|
4
7
|
readonly startedAtMs: number;
|
|
@@ -19,6 +22,7 @@ export declare class ToolCallTimingRingBuffer {
|
|
|
19
22
|
}
|
|
20
23
|
export declare const DEFAULT_RING_BUFFER_CAPACITY = 100;
|
|
21
24
|
export declare function createRingBufferSink(buffer: ToolCallTimingRingBuffer): ToolCallTimingSink;
|
|
25
|
+
export declare function createDiskPersistSink(perfFilePath: string, serverVersion: string): ToolCallTimingSink;
|
|
22
26
|
export declare function createDevPerfSink(): ToolCallTimingSink;
|
|
23
27
|
export declare function composeSinks(...sinks: ToolCallTimingSink[]): ToolCallTimingSink;
|
|
24
28
|
export declare function withToolCallTiming<T>(toolName: string, handler: () => Promise<T>, sink: ToolCallTimingSink): Promise<T>;
|
|
@@ -1,10 +1,46 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.DEFAULT_RING_BUFFER_CAPACITY = exports.ToolCallTimingRingBuffer = exports.noopToolCallTimingSink = void 0;
|
|
4
37
|
exports.createRingBufferSink = createRingBufferSink;
|
|
38
|
+
exports.createDiskPersistSink = createDiskPersistSink;
|
|
5
39
|
exports.createDevPerfSink = createDevPerfSink;
|
|
6
40
|
exports.composeSinks = composeSinks;
|
|
7
41
|
exports.withToolCallTiming = withToolCallTiming;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
8
44
|
const noopToolCallTimingSink = () => { };
|
|
9
45
|
exports.noopToolCallTimingSink = noopToolCallTimingSink;
|
|
10
46
|
class ToolCallTimingRingBuffer {
|
|
@@ -44,6 +80,22 @@ function createRingBufferSink(buffer) {
|
|
|
44
80
|
buffer.push(timing);
|
|
45
81
|
};
|
|
46
82
|
}
|
|
83
|
+
function createDiskPersistSink(perfFilePath, serverVersion) {
|
|
84
|
+
try {
|
|
85
|
+
fs.mkdirSync(path.dirname(perfFilePath), { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
let warnedOnce = false;
|
|
89
|
+
return (timing) => {
|
|
90
|
+
const entry = { ...timing, serverVersion };
|
|
91
|
+
fs.promises.appendFile(perfFilePath, JSON.stringify(entry) + '\n').catch((err) => {
|
|
92
|
+
if (!warnedOnce) {
|
|
93
|
+
warnedOnce = true;
|
|
94
|
+
process.stderr.write(`[PerfTrace] Failed to write timing to disk: ${String(err)}\n`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
}
|
|
47
99
|
function createDevPerfSink() {
|
|
48
100
|
return (timing) => {
|
|
49
101
|
const outcomeLabel = timing.outcome === 'success' ? 'OK' : timing.outcome.toUpperCase();
|
|
@@ -34,6 +34,8 @@ function anchorsToObservations(anchors) {
|
|
|
34
34
|
});
|
|
35
35
|
break;
|
|
36
36
|
case 'repo_root':
|
|
37
|
+
if (anchor.value.length > constants_js_1.MAX_OBSERVATION_SHORT_STRING_LENGTH)
|
|
38
|
+
break;
|
|
37
39
|
observations.push({
|
|
38
40
|
key: 'repo_root',
|
|
39
41
|
value: { type: 'short_string', value: anchor.value },
|
|
@@ -2,4 +2,4 @@ import type { Application } from 'express';
|
|
|
2
2
|
import type { ConsoleService } from './console-service.js';
|
|
3
3
|
import type { WorkflowService } from '../../application/services/workflow-service.js';
|
|
4
4
|
import type { ToolCallTimingRingBuffer } from '../../mcp/tool-call-timing.js';
|
|
5
|
-
export declare function mountConsoleRoutes(app: Application, consoleService: ConsoleService, workflowService?: WorkflowService, timingRingBuffer?: ToolCallTimingRingBuffer): () => void;
|
|
5
|
+
export declare function mountConsoleRoutes(app: Application, consoleService: ConsoleService, workflowService?: WorkflowService, timingRingBuffer?: ToolCallTimingRingBuffer, toolCallsPerfFile?: string, serverVersion?: string): () => void;
|
|
@@ -53,7 +53,7 @@ function loadWorkflowTags() {
|
|
|
53
53
|
return { version: 0, tags: [], workflows: {} };
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
function mountConsoleRoutes(app, consoleService, workflowService, timingRingBuffer) {
|
|
56
|
+
function mountConsoleRoutes(app, consoleService, workflowService, timingRingBuffer, toolCallsPerfFile, serverVersion) {
|
|
57
57
|
const sseClients = new Set();
|
|
58
58
|
let sseDebounceTimer = null;
|
|
59
59
|
function broadcastChange() {
|
|
@@ -99,14 +99,71 @@ function mountConsoleRoutes(app, consoleService, workflowService, timingRingBuff
|
|
|
99
99
|
req.on('close', () => { sseClients.delete(res); });
|
|
100
100
|
res.on('close', () => { sseClients.delete(res); });
|
|
101
101
|
});
|
|
102
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
103
|
+
const PERF_FILE_READ_LIMIT_BYTES = 5 * 1024 * 1024;
|
|
104
|
+
async function readDiskEntries(perfFile) {
|
|
105
|
+
try {
|
|
106
|
+
const stat = await fs_1.default.promises.stat(perfFile);
|
|
107
|
+
let raw;
|
|
108
|
+
if (stat.size > PERF_FILE_READ_LIMIT_BYTES) {
|
|
109
|
+
const fd = await fs_1.default.promises.open(perfFile, 'r');
|
|
110
|
+
const offset = stat.size - PERF_FILE_READ_LIMIT_BYTES;
|
|
111
|
+
const buf = Buffer.alloc(PERF_FILE_READ_LIMIT_BYTES);
|
|
112
|
+
await fd.read(buf, 0, PERF_FILE_READ_LIMIT_BYTES, offset);
|
|
113
|
+
await fd.close();
|
|
114
|
+
raw = buf.toString('utf8');
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
raw = await fs_1.default.promises.readFile(perfFile, 'utf8');
|
|
118
|
+
}
|
|
119
|
+
const cutoff = Date.now() - THIRTY_DAYS_MS;
|
|
120
|
+
return raw
|
|
121
|
+
.split('\n')
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.flatMap((line) => {
|
|
124
|
+
try {
|
|
125
|
+
const entry = JSON.parse(line);
|
|
126
|
+
if (typeof entry.toolName !== 'string' ||
|
|
127
|
+
typeof entry.startedAtMs !== 'number' ||
|
|
128
|
+
typeof entry.durationMs !== 'number' ||
|
|
129
|
+
(entry.outcome !== 'success' && entry.outcome !== 'error' && entry.outcome !== 'unknown_tool'))
|
|
130
|
+
return [];
|
|
131
|
+
const safeEntry = typeof entry.serverVersion === 'string'
|
|
132
|
+
? entry
|
|
133
|
+
: { ...entry, serverVersion: 'unknown' };
|
|
134
|
+
if (safeEntry.startedAtMs < cutoff)
|
|
135
|
+
return [];
|
|
136
|
+
return [safeEntry];
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
102
147
|
const devMode = (0, dev_mode_js_1.isDevMode)();
|
|
103
148
|
if (devMode) {
|
|
104
|
-
app.get('/api/v2/perf/tool-calls', (req, res) => {
|
|
149
|
+
app.get('/api/v2/perf/tool-calls', async (req, res) => {
|
|
105
150
|
const rawLimit = req.query['limit'];
|
|
106
151
|
const limit = typeof rawLimit === 'string' ? parseInt(rawLimit, 10) : undefined;
|
|
107
152
|
const safeLimit = (limit !== undefined && Number.isFinite(limit) && limit > 0) ? limit : undefined;
|
|
108
|
-
const
|
|
109
|
-
|
|
153
|
+
const diskEntries = toolCallsPerfFile ? await readDiskEntries(toolCallsPerfFile) : [];
|
|
154
|
+
const ringEntries = timingRingBuffer ? timingRingBuffer.recent(safeLimit) : [];
|
|
155
|
+
const version = serverVersion ?? 'unknown';
|
|
156
|
+
const ringEntriesWithVersion = ringEntries.map((t) => ({
|
|
157
|
+
...t,
|
|
158
|
+
serverVersion: version,
|
|
159
|
+
}));
|
|
160
|
+
const dedupeKey = (e) => `${e.toolName}:${e.startedAtMs}:${e.durationMs}`;
|
|
161
|
+
const inMemoryKeys = new Set(ringEntriesWithVersion.map(dedupeKey));
|
|
162
|
+
const diskOnlyEntries = diskEntries.filter((e) => !inMemoryKeys.has(dedupeKey(e)));
|
|
163
|
+
const allEntries = [...ringEntriesWithVersion, ...diskOnlyEntries]
|
|
164
|
+
.sort((a, b) => b.startedAtMs - a.startedAtMs)
|
|
165
|
+
.slice(0, safeLimit ?? undefined);
|
|
166
|
+
res.json({ success: true, data: { observations: allEntries, devMode } });
|
|
110
167
|
});
|
|
111
168
|
}
|
|
112
169
|
app.get('/api/v2/sessions', async (_req, res) => {
|