@exaudeus/workrail 3.73.2 → 3.74.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/cli-worktrain.js +126 -1
- package/dist/console-ui/assets/{index-CfI4I3OX.js → index-BmDxs-a5.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/coordinators/pr-review.d.ts +11 -1
- package/dist/coordinators/types.d.ts +15 -0
- package/dist/coordinators/types.js +2 -0
- package/dist/manifest.json +17 -9
- package/dist/trigger/coordinator-deps.js +203 -36
- package/docs/authoring.md +23 -0
- package/docs/ideas/backlog.md +299 -60
- package/docs/planning/README.md +6 -9
- package/docs/roadmap/archive/README.md +8 -0
- package/docs/tickets/next-up.md +6 -1
- package/docs/vision.md +115 -0
- package/package.json +1 -1
- package/spec/authoring-spec.json +36 -1
- /package/docs/roadmap/{now-next-later.md → archive/now-next-later.md} +0 -0
- /package/docs/roadmap/{open-work-inventory.md → archive/open-work-inventory.md} +0 -0
package/dist/cli-worktrain.js
CHANGED
|
@@ -965,7 +965,7 @@ runCommand
|
|
|
965
965
|
joinPath: path_1.default.join,
|
|
966
966
|
}, options.port);
|
|
967
967
|
const deps = {
|
|
968
|
-
spawnSession: async (workflowId, goal, workspace, context) => {
|
|
968
|
+
spawnSession: async (workflowId, goal, workspace, context, _agentConfig, parentSessionId) => {
|
|
969
969
|
const url = `http://127.0.0.1:${port}/api/v2/auto/dispatch`;
|
|
970
970
|
try {
|
|
971
971
|
const response = await globalThis.fetch(url, {
|
|
@@ -976,6 +976,7 @@ runCommand
|
|
|
976
976
|
goal,
|
|
977
977
|
workspacePath: workspace,
|
|
978
978
|
...(context !== undefined ? { context } : {}),
|
|
979
|
+
...(parentSessionId !== undefined ? { parentSessionId } : {}),
|
|
979
980
|
}),
|
|
980
981
|
signal: AbortSignal.timeout(30000),
|
|
981
982
|
});
|
|
@@ -1147,6 +1148,130 @@ runCommand
|
|
|
1147
1148
|
return emptyResult;
|
|
1148
1149
|
}
|
|
1149
1150
|
},
|
|
1151
|
+
getChildSessionResult: async (handle, _coordinatorSessionId) => {
|
|
1152
|
+
const sessionUrl = `http://127.0.0.1:${port}/api/v2/sessions/${encodeURIComponent(handle)}`;
|
|
1153
|
+
try {
|
|
1154
|
+
const sessionRes = await globalThis.fetch(sessionUrl, { signal: AbortSignal.timeout(30000) });
|
|
1155
|
+
if (!sessionRes.ok) {
|
|
1156
|
+
return { kind: 'failed', reason: 'error', message: `Session fetch HTTP ${sessionRes.status}` };
|
|
1157
|
+
}
|
|
1158
|
+
const sessionBody = await sessionRes.json();
|
|
1159
|
+
const data = sessionBody['data'];
|
|
1160
|
+
const runs = (data?.['runs'] ?? sessionBody['runs']);
|
|
1161
|
+
const runStatus = runs?.[0]?.['status'];
|
|
1162
|
+
if (runStatus === 'complete' || runStatus === 'complete_with_gaps') {
|
|
1163
|
+
const agentResult = await (async () => {
|
|
1164
|
+
const empty = { recapMarkdown: null, artifacts: [] };
|
|
1165
|
+
try {
|
|
1166
|
+
const tipNodeId = runs?.[0]?.['preferredTipNodeId'] ?? null;
|
|
1167
|
+
if (!tipNodeId)
|
|
1168
|
+
return empty;
|
|
1169
|
+
const allNodes = runs?.[0]?.['nodes'] ?? [];
|
|
1170
|
+
const allNodeIds = allNodes
|
|
1171
|
+
.map((n) => n['nodeId'])
|
|
1172
|
+
.filter((id) => typeof id === 'string' && id !== '');
|
|
1173
|
+
const nodeIdsToFetch = allNodeIds.length > 0 ? allNodeIds : [tipNodeId];
|
|
1174
|
+
let recap = null;
|
|
1175
|
+
const collectedArtifacts = [];
|
|
1176
|
+
for (const nodeId of nodeIdsToFetch) {
|
|
1177
|
+
try {
|
|
1178
|
+
const nodeUrl = `http://127.0.0.1:${port}/api/v2/sessions/${encodeURIComponent(handle)}/nodes/${encodeURIComponent(nodeId)}`;
|
|
1179
|
+
const nodeRes = await globalThis.fetch(nodeUrl, { signal: AbortSignal.timeout(30000) });
|
|
1180
|
+
if (!nodeRes.ok)
|
|
1181
|
+
continue;
|
|
1182
|
+
const nodeBody = await nodeRes.json();
|
|
1183
|
+
const nodeData = nodeBody['data'];
|
|
1184
|
+
if (nodeId === tipNodeId)
|
|
1185
|
+
recap = nodeData?.['recapMarkdown'] ?? null;
|
|
1186
|
+
const artifacts = nodeData?.['artifacts'] ?? [];
|
|
1187
|
+
if (artifacts.length > 0)
|
|
1188
|
+
collectedArtifacts.push(...artifacts);
|
|
1189
|
+
}
|
|
1190
|
+
catch {
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return { recapMarkdown: recap, artifacts: collectedArtifacts };
|
|
1195
|
+
}
|
|
1196
|
+
catch {
|
|
1197
|
+
return empty;
|
|
1198
|
+
}
|
|
1199
|
+
})();
|
|
1200
|
+
return { kind: 'success', notes: agentResult.recapMarkdown, artifacts: agentResult.artifacts };
|
|
1201
|
+
}
|
|
1202
|
+
if (runStatus === 'blocked') {
|
|
1203
|
+
return { kind: 'failed', reason: 'stuck', message: `Child session ${handle.slice(0, 16)} reached blocked state` };
|
|
1204
|
+
}
|
|
1205
|
+
if (runStatus === null || runStatus === undefined) {
|
|
1206
|
+
return { kind: 'timed_out', message: `Child session ${handle.slice(0, 16)} has no terminal run status` };
|
|
1207
|
+
}
|
|
1208
|
+
return { kind: 'timed_out', message: `Child session ${handle.slice(0, 16)} is in state '${runStatus}'` };
|
|
1209
|
+
}
|
|
1210
|
+
catch (e) {
|
|
1211
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1212
|
+
return { kind: 'failed', reason: 'error', message: `Exception in getChildSessionResult: ${msg}` };
|
|
1213
|
+
}
|
|
1214
|
+
},
|
|
1215
|
+
spawnAndAwait: async (workflowId, goal, workspace, opts) => {
|
|
1216
|
+
const spawnUrl = `http://127.0.0.1:${port}/api/v2/auto/dispatch`;
|
|
1217
|
+
let handle;
|
|
1218
|
+
try {
|
|
1219
|
+
const response = await globalThis.fetch(spawnUrl, {
|
|
1220
|
+
method: 'POST',
|
|
1221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1222
|
+
body: JSON.stringify({
|
|
1223
|
+
workflowId,
|
|
1224
|
+
goal,
|
|
1225
|
+
workspacePath: workspace,
|
|
1226
|
+
...(opts?.coordinatorSessionId !== undefined ? { parentSessionId: opts.coordinatorSessionId } : {}),
|
|
1227
|
+
}),
|
|
1228
|
+
signal: AbortSignal.timeout(30000),
|
|
1229
|
+
});
|
|
1230
|
+
const body = await response.json();
|
|
1231
|
+
if (!response.ok) {
|
|
1232
|
+
const errMsg = typeof body['error'] === 'string' ? body['error'] : `HTTP ${response.status}`;
|
|
1233
|
+
return { kind: 'failed', reason: 'error', message: `Spawn HTTP error: ${errMsg}` };
|
|
1234
|
+
}
|
|
1235
|
+
const sessionId = body['data']?.['sessionId']
|
|
1236
|
+
?? body['sessionId'];
|
|
1237
|
+
if (!sessionId) {
|
|
1238
|
+
return { kind: 'failed', reason: 'error', message: 'Spawn response missing sessionId' };
|
|
1239
|
+
}
|
|
1240
|
+
handle = sessionId;
|
|
1241
|
+
}
|
|
1242
|
+
catch (e) {
|
|
1243
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1244
|
+
return { kind: 'failed', reason: 'error', message: `Exception spawning session: ${msg}` };
|
|
1245
|
+
}
|
|
1246
|
+
const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
1247
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1248
|
+
const POLL_INTERVAL_MS = 5000;
|
|
1249
|
+
const deadline = Date.now() + timeoutMs;
|
|
1250
|
+
let timedOut = false;
|
|
1251
|
+
while (Date.now() < deadline) {
|
|
1252
|
+
try {
|
|
1253
|
+
const sessionUrl = `http://127.0.0.1:${port}/api/v2/sessions/${encodeURIComponent(handle)}`;
|
|
1254
|
+
const sessionRes = await globalThis.fetch(sessionUrl, { signal: AbortSignal.timeout(10000) });
|
|
1255
|
+
if (sessionRes.ok) {
|
|
1256
|
+
const body = await sessionRes.json();
|
|
1257
|
+
const data = body['data'];
|
|
1258
|
+
const runs = (data?.['runs'] ?? body['runs']);
|
|
1259
|
+
const status = runs?.[0]?.['status'];
|
|
1260
|
+
if (status === 'complete' || status === 'complete_with_gaps' || status === 'blocked') {
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
catch { }
|
|
1266
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
1267
|
+
}
|
|
1268
|
+
if (Date.now() >= deadline)
|
|
1269
|
+
timedOut = true;
|
|
1270
|
+
if (timedOut) {
|
|
1271
|
+
return { kind: 'timed_out', message: `Session ${handle.slice(0, 16)} timed out after ${timeoutMs}ms` };
|
|
1272
|
+
}
|
|
1273
|
+
return deps.getChildSessionResult(handle, opts?.coordinatorSessionId);
|
|
1274
|
+
},
|
|
1150
1275
|
listOpenPRs: async (workspace) => {
|
|
1151
1276
|
try {
|
|
1152
1277
|
const { stdout } = await execFilePromise('gh', ['pr', 'list', '--json', 'number,title,headRefName'], {
|