@exaudeus/workrail 3.73.1 → 3.74.0

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.
Files changed (41) hide show
  1. package/dist/cli-worktrain.js +126 -1
  2. package/dist/console-ui/assets/{index-txIYXGHx.js → index-CfU3va8H.js} +1 -1
  3. package/dist/console-ui/index.html +1 -1
  4. package/dist/coordinators/pr-review.d.ts +11 -1
  5. package/dist/coordinators/types.d.ts +15 -0
  6. package/dist/coordinators/types.js +2 -0
  7. package/dist/manifest.json +81 -57
  8. package/dist/mcp/handlers/v2-advance-core/index.d.ts +1 -0
  9. package/dist/mcp/handlers/v2-advance-core/index.js +3 -3
  10. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +4 -18
  11. package/dist/mcp/handlers/v2-advance-events.d.ts +1 -1
  12. package/dist/mcp/handlers/v2-advance-events.js +1 -1
  13. package/dist/mcp/handlers/v2-execution/advance.d.ts +1 -0
  14. package/dist/mcp/handlers/v2-execution/advance.js +3 -3
  15. package/dist/mcp/handlers/v2-execution/continue-advance.d.ts +1 -0
  16. package/dist/mcp/handlers/v2-execution/continue-advance.js +2 -1
  17. package/dist/mcp/handlers/v2-execution/index.js +3 -1
  18. package/dist/mcp/server.js +6 -4
  19. package/dist/mcp/types.d.ts +2 -0
  20. package/dist/trigger/coordinator-deps.js +203 -36
  21. package/dist/trigger/delivery-action.d.ts +1 -0
  22. package/dist/trigger/delivery-action.js +1 -1
  23. package/dist/trigger/delivery-pipeline.d.ts +13 -2
  24. package/dist/trigger/delivery-pipeline.js +58 -3
  25. package/dist/trigger/trigger-router.js +6 -3
  26. package/dist/v2/durable-core/constants.d.ts +1 -0
  27. package/dist/v2/durable-core/constants.js +1 -0
  28. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +202 -0
  29. package/dist/v2/durable-core/schemas/session/events.d.ts +56 -0
  30. package/dist/v2/durable-core/schemas/session/events.js +8 -0
  31. package/dist/v2/infra/local/git-snapshot/index.d.ts +6 -0
  32. package/dist/v2/infra/local/git-snapshot/index.js +39 -0
  33. package/dist/v2/ports/git-snapshot.port.d.ts +10 -0
  34. package/dist/v2/ports/git-snapshot.port.js +9 -0
  35. package/dist/v2/projections/session-metrics.js +17 -2
  36. package/docs/authoring.md +23 -0
  37. package/docs/design/engine-boundary-discovery.md +123 -0
  38. package/docs/design/engine-boundary-review-findings.md +72 -0
  39. package/docs/ideas/backlog.md +129 -48
  40. package/package.json +1 -1
  41. package/spec/authoring-spec.json +36 -1
@@ -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'], {