@exaudeus/workrail 0.17.0 → 1.1.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.
- package/dist/application/services/output-normalizer.d.ts +9 -0
- package/dist/application/services/output-normalizer.js +38 -0
- package/dist/di/container.js +8 -0
- package/dist/di/tokens.d.ts +2 -0
- package/dist/di/tokens.js +2 -0
- package/dist/infrastructure/session/HttpServer.d.ts +2 -1
- package/dist/infrastructure/session/HttpServer.js +34 -10
- package/dist/infrastructure/session/SessionManager.js +19 -1
- package/dist/infrastructure/storage/enhanced-multi-source-workflow-storage.js +26 -2
- package/dist/infrastructure/storage/file-workflow-storage.js +4 -4
- package/dist/infrastructure/storage/git-workflow-storage.d.ts +0 -1
- package/dist/infrastructure/storage/git-workflow-storage.js +28 -29
- package/dist/infrastructure/storage/plugin-workflow-storage.js +11 -5
- package/dist/manifest.json +276 -92
- package/dist/mcp/handler-factory.d.ts +7 -0
- package/dist/mcp/handler-factory.js +70 -0
- package/dist/mcp/handlers/v2-execution-helpers.d.ts +4 -4
- package/dist/mcp/handlers/v2-execution-helpers.js +29 -0
- package/dist/mcp/handlers/v2-execution.js +460 -166
- package/dist/mcp/output-schemas.d.ts +350 -37
- package/dist/mcp/output-schemas.js +91 -18
- package/dist/mcp/server.js +32 -130
- package/dist/mcp/tool-descriptions.js +126 -18
- package/dist/mcp/types/workflow-tool-edition.d.ts +28 -0
- package/dist/mcp/types/workflow-tool-edition.js +10 -0
- package/dist/mcp/types.d.ts +2 -6
- package/dist/mcp/v1/tool-registry.d.ts +8 -0
- package/dist/mcp/v1/tool-registry.js +49 -0
- package/dist/mcp/v2/tool-registry.d.ts +2 -5
- package/dist/mcp/v2/tool-registry.js +33 -32
- package/dist/mcp/v2/tools.js +6 -6
- package/dist/mcp/workflow-tool-edition-selector.d.ts +4 -0
- package/dist/mcp/workflow-tool-edition-selector.js +13 -0
- package/dist/utils/storage-security.js +15 -1
- package/dist/v2/durable-core/constants.d.ts +1 -0
- package/dist/v2/durable-core/constants.js +2 -1
- package/dist/v2/durable-core/domain/ack-advance-append-plan.d.ts +14 -7
- package/dist/v2/durable-core/domain/ack-advance-append-plan.js +78 -23
- package/dist/v2/durable-core/domain/blocking-decision.d.ts +32 -0
- package/dist/v2/durable-core/domain/blocking-decision.js +41 -0
- package/dist/v2/durable-core/domain/context-merge.d.ts +8 -0
- package/dist/v2/durable-core/domain/context-merge.js +40 -0
- package/dist/v2/durable-core/domain/function-definition-expander.d.ts +14 -0
- package/dist/v2/durable-core/domain/function-definition-expander.js +66 -0
- package/dist/v2/durable-core/domain/gap-builder.d.ts +19 -0
- package/dist/v2/durable-core/domain/gap-builder.js +24 -0
- package/dist/v2/durable-core/domain/prompt-renderer.d.ts +24 -0
- package/dist/v2/durable-core/domain/prompt-renderer.js +167 -0
- package/dist/v2/durable-core/domain/reason-model.d.ts +94 -0
- package/dist/v2/durable-core/domain/reason-model.js +228 -0
- package/dist/v2/durable-core/domain/recap-recovery.d.ts +24 -0
- package/dist/v2/durable-core/domain/recap-recovery.js +71 -0
- package/dist/v2/durable-core/domain/validation-criteria-validator.d.ts +8 -0
- package/dist/v2/durable-core/domain/validation-criteria-validator.js +16 -0
- package/dist/v2/durable-core/domain/validation-requirements-extractor.d.ts +2 -0
- package/dist/v2/durable-core/domain/validation-requirements-extractor.js +58 -0
- package/dist/v2/durable-core/encoding/base32-lower.d.ts +1 -0
- package/dist/v2/durable-core/encoding/base32-lower.js +28 -0
- package/dist/v2/durable-core/ids/index.d.ts +4 -0
- package/dist/v2/durable-core/ids/index.js +7 -0
- package/dist/v2/durable-core/ids/workflow-hash-ref.d.ts +7 -0
- package/dist/v2/durable-core/ids/workflow-hash-ref.js +23 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +206 -0
- package/dist/v2/durable-core/schemas/session/events.d.ts +58 -0
- package/dist/v2/durable-core/schemas/session/events.js +9 -0
- package/dist/v2/durable-core/tokens/binary-payload.d.ts +35 -0
- package/dist/v2/durable-core/tokens/binary-payload.js +279 -0
- package/dist/v2/durable-core/tokens/index.d.ts +9 -4
- package/dist/v2/durable-core/tokens/index.js +17 -7
- package/dist/v2/durable-core/tokens/payloads.d.ts +12 -8
- package/dist/v2/durable-core/tokens/payloads.js +5 -3
- package/dist/v2/durable-core/tokens/token-codec-capabilities.d.ts +4 -0
- package/dist/v2/durable-core/tokens/token-codec-capabilities.js +2 -0
- package/dist/v2/durable-core/tokens/token-codec-ports.d.ts +42 -0
- package/dist/v2/durable-core/tokens/token-codec-ports.js +27 -0
- package/dist/v2/durable-core/tokens/token-codec.d.ts +18 -0
- package/dist/v2/durable-core/tokens/token-codec.js +108 -0
- package/dist/v2/durable-core/tokens/token-signer.d.ts +13 -1
- package/dist/v2/durable-core/tokens/token-signer.js +65 -0
- package/dist/v2/infra/local/base32/index.d.ts +6 -0
- package/dist/v2/infra/local/base32/index.js +44 -0
- package/dist/v2/infra/local/bech32m/index.d.ts +8 -0
- package/dist/v2/infra/local/bech32m/index.js +56 -0
- package/dist/v2/infra/local/data-dir/index.d.ts +1 -0
- package/dist/v2/infra/local/data-dir/index.js +5 -2
- package/dist/v2/infra/local/fs/index.js +3 -0
- package/dist/v2/infra/local/session-store/index.js +38 -4
- package/dist/v2/ports/base32.port.d.ts +16 -0
- package/dist/v2/ports/base32.port.js +2 -0
- package/dist/v2/ports/bech32m.port.d.ts +11 -0
- package/dist/v2/ports/bech32m.port.js +2 -0
- package/dist/v2/projections/run-context.d.ts +22 -0
- package/dist/v2/projections/run-context.js +33 -0
- package/package.json +20 -2
|
@@ -44,6 +44,7 @@ const index_js_1 = require("../../v2/durable-core/tokens/index.js");
|
|
|
44
44
|
const index_js_2 = require("../../v2/durable-core/tokens/index.js");
|
|
45
45
|
const workflow_js_1 = require("../../types/workflow.js");
|
|
46
46
|
const index_js_3 = require("../../v2/durable-core/ids/index.js");
|
|
47
|
+
const workflow_hash_ref_js_1 = require("../../v2/durable-core/ids/workflow-hash-ref.js");
|
|
47
48
|
const attempt_id_derivation_js_1 = require("../../v2/durable-core/ids/attempt-id-derivation.js");
|
|
48
49
|
const neverthrow_1 = require("neverthrow");
|
|
49
50
|
const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
|
|
@@ -53,10 +54,19 @@ const constants_js_1 = require("../../v2/durable-core/constants.js");
|
|
|
53
54
|
const notes_markdown_js_1 = require("../../v2/durable-core/domain/notes-markdown.js");
|
|
54
55
|
const outputs_js_1 = require("../../v2/durable-core/domain/outputs.js");
|
|
55
56
|
const ack_advance_append_plan_js_1 = require("../../v2/durable-core/domain/ack-advance-append-plan.js");
|
|
57
|
+
const blocking_decision_js_1 = require("../../v2/durable-core/domain/blocking-decision.js");
|
|
58
|
+
const reason_model_js_1 = require("../../v2/durable-core/domain/reason-model.js");
|
|
59
|
+
const validation_criteria_validator_js_1 = require("../../v2/durable-core/domain/validation-criteria-validator.js");
|
|
60
|
+
const preferences_js_1 = require("../../v2/projections/preferences.js");
|
|
61
|
+
const run_context_js_1 = require("../../v2/projections/run-context.js");
|
|
62
|
+
const context_merge_js_1 = require("../../v2/durable-core/domain/context-merge.js");
|
|
63
|
+
const prompt_renderer_js_1 = require("../../v2/durable-core/domain/prompt-renderer.js");
|
|
56
64
|
const workflow_source_js_1 = require("../../types/workflow-source.js");
|
|
57
65
|
const workflow_definition_js_1 = require("../../types/workflow-definition.js");
|
|
58
66
|
const workflow_compiler_js_1 = require("../../application/services/workflow-compiler.js");
|
|
59
67
|
const workflow_interpreter_js_1 = require("../../application/services/workflow-interpreter.js");
|
|
68
|
+
const validation_engine_js_1 = require("../../application/services/validation-engine.js");
|
|
69
|
+
const enhanced_loop_validator_js_1 = require("../../application/services/enhanced-loop-validator.js");
|
|
60
70
|
const v2_execution_helpers_js_1 = require("./v2-execution-helpers.js");
|
|
61
71
|
function normalizeTokenErrorMessage(message) {
|
|
62
72
|
return message.split(os.homedir()).join('~');
|
|
@@ -238,18 +248,7 @@ function mapInternalErrorToToolError(e) {
|
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
function replayFromRecordedAdvance(args) {
|
|
241
|
-
const { recordedEvent, truth, sessionId, runId, nodeId, workflowHash, attemptId, inputStateToken, inputAckToken, pinnedWorkflow, snapshotStore,
|
|
242
|
-
const checkpointTokenRes = signTokenOrErr({
|
|
243
|
-
unsignedPrefix: 'chk.v1.',
|
|
244
|
-
payload: { tokenVersion: 1, tokenKind: 'checkpoint', sessionId, runId, nodeId, attemptId },
|
|
245
|
-
keyring,
|
|
246
|
-
hmac,
|
|
247
|
-
base64url,
|
|
248
|
-
});
|
|
249
|
-
if (checkpointTokenRes.isErr()) {
|
|
250
|
-
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: checkpointTokenRes.error });
|
|
251
|
-
}
|
|
252
|
-
const checkpointToken = checkpointTokenRes.value;
|
|
251
|
+
const { recordedEvent, truth, sessionId, runId, nodeId, workflowHash, attemptId, inputStateToken, inputAckToken, pinnedWorkflow, snapshotStore, sha256, tokenCodecPorts, } = args;
|
|
253
252
|
if (recordedEvent.data.outcome.kind === 'blocked') {
|
|
254
253
|
const blockers = recordedEvent.data.outcome.blockers;
|
|
255
254
|
const snapNode = truth.events.find((e) => e.kind === 'node_created' && e.scope?.nodeId === String(nodeId));
|
|
@@ -259,15 +258,32 @@ function replayFromRecordedAdvance(args) {
|
|
|
259
258
|
return snapRA.map((snap) => {
|
|
260
259
|
const pendingNow = snap ? (0, snapshot_state_js_1.derivePendingStep)(snap.enginePayload.engineState) : null;
|
|
261
260
|
const isCompleteNow = snap ? (0, snapshot_state_js_1.deriveIsComplete)(snap.enginePayload.engineState) : false;
|
|
261
|
+
const meta = pendingNow
|
|
262
|
+
? (0, prompt_renderer_js_1.renderPendingPrompt)({
|
|
263
|
+
workflow: pinnedWorkflow,
|
|
264
|
+
stepId: String(pendingNow.stepId),
|
|
265
|
+
loopPath: pendingNow.loopPath,
|
|
266
|
+
truth,
|
|
267
|
+
runId: (0, index_js_3.asRunId)(String(runId)),
|
|
268
|
+
nodeId: (0, index_js_3.asNodeId)(String(nodeId)),
|
|
269
|
+
rehydrateOnly: false,
|
|
270
|
+
}).unwrapOr({
|
|
271
|
+
stepId: String(pendingNow.stepId),
|
|
272
|
+
title: String(pendingNow.stepId),
|
|
273
|
+
prompt: `Pending step: ${String(pendingNow.stepId)}`,
|
|
274
|
+
requireConfirmation: false,
|
|
275
|
+
})
|
|
276
|
+
: null;
|
|
277
|
+
const preferences = derivePreferencesForNode({ truth, runId, nodeId });
|
|
278
|
+
const nextIntent = deriveNextIntent({ rehydrateOnly: false, isComplete: isCompleteNow, pending: meta });
|
|
262
279
|
return output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
|
|
263
280
|
kind: 'blocked',
|
|
264
281
|
stateToken: inputStateToken,
|
|
265
282
|
ackToken: inputAckToken,
|
|
266
|
-
checkpointToken,
|
|
267
283
|
isComplete: isCompleteNow,
|
|
268
|
-
pending:
|
|
269
|
-
|
|
270
|
-
|
|
284
|
+
pending: meta ? { stepId: meta.stepId, title: meta.title, prompt: meta.prompt } : null,
|
|
285
|
+
preferences,
|
|
286
|
+
nextIntent,
|
|
271
287
|
blockers,
|
|
272
288
|
});
|
|
273
289
|
});
|
|
@@ -296,44 +312,52 @@ function replayFromRecordedAdvance(args) {
|
|
|
296
312
|
const pending = (0, snapshot_state_js_1.derivePendingStep)(snap.enginePayload.engineState);
|
|
297
313
|
const isComplete = (0, snapshot_state_js_1.deriveIsComplete)(snap.enginePayload.engineState);
|
|
298
314
|
const nextAttemptId = attemptIdForNextNode(attemptId, sha256);
|
|
299
|
-
const nextAckTokenRes =
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
});
|
|
315
|
+
const nextAckTokenRes = pending
|
|
316
|
+
? signTokenOrErr({
|
|
317
|
+
payload: { tokenVersion: 1, tokenKind: 'ack', sessionId, runId, nodeId: toNodeIdBranded, attemptId: nextAttemptId },
|
|
318
|
+
ports: tokenCodecPorts,
|
|
319
|
+
})
|
|
320
|
+
: (0, neverthrow_1.ok)(undefined);
|
|
306
321
|
if (nextAckTokenRes.isErr()) {
|
|
307
322
|
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: nextAckTokenRes.error });
|
|
308
323
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
keyring,
|
|
313
|
-
hmac,
|
|
314
|
-
base64url,
|
|
315
|
-
});
|
|
316
|
-
if (nextCheckpointTokenRes.isErr()) {
|
|
317
|
-
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: nextCheckpointTokenRes.error });
|
|
324
|
+
const wfRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
|
|
325
|
+
if (wfRefRes.isErr()) {
|
|
326
|
+
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: wfRefRes.error.message, suggestion: 'Ensure workflowHash is a valid sha256 digest.' });
|
|
318
327
|
}
|
|
319
328
|
const nextStateTokenRes = signTokenOrErr({
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
keyring,
|
|
323
|
-
hmac,
|
|
324
|
-
base64url,
|
|
329
|
+
payload: { tokenVersion: 1, tokenKind: 'state', sessionId, runId, nodeId: toNodeIdBranded, workflowHashRef: wfRefRes.value },
|
|
330
|
+
ports: tokenCodecPorts,
|
|
325
331
|
});
|
|
326
332
|
if (nextStateTokenRes.isErr()) {
|
|
327
333
|
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: nextStateTokenRes.error });
|
|
328
334
|
}
|
|
329
|
-
const
|
|
335
|
+
const meta = pending
|
|
336
|
+
? (0, prompt_renderer_js_1.renderPendingPrompt)({
|
|
337
|
+
workflow: pinnedWorkflow,
|
|
338
|
+
stepId: String(pending.stepId),
|
|
339
|
+
loopPath: pending.loopPath,
|
|
340
|
+
truth,
|
|
341
|
+
runId: (0, index_js_3.asRunId)(String(runId)),
|
|
342
|
+
nodeId: (0, index_js_3.asNodeId)(String(toNodeIdBranded)),
|
|
343
|
+
rehydrateOnly: false,
|
|
344
|
+
}).unwrapOr({
|
|
345
|
+
stepId: String(pending.stepId),
|
|
346
|
+
title: String(pending.stepId),
|
|
347
|
+
prompt: `Pending step: ${String(pending.stepId)}`,
|
|
348
|
+
requireConfirmation: false,
|
|
349
|
+
})
|
|
350
|
+
: { stepId: '', title: '', prompt: '', requireConfirmation: false };
|
|
351
|
+
const preferences = derivePreferencesForNode({ truth, runId, nodeId: toNodeIdBranded });
|
|
352
|
+
const nextIntent = deriveNextIntent({ rehydrateOnly: false, isComplete, pending: pending ? meta : null });
|
|
330
353
|
return (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
|
|
331
354
|
kind: 'ok',
|
|
332
355
|
stateToken: nextStateTokenRes.value,
|
|
333
|
-
ackToken: nextAckTokenRes.value,
|
|
334
|
-
checkpointToken: nextCheckpointTokenRes.value,
|
|
356
|
+
ackToken: pending ? nextAckTokenRes.value : undefined,
|
|
335
357
|
isComplete,
|
|
336
|
-
pending:
|
|
358
|
+
pending: pending ? { stepId: meta.stepId, title: meta.title, prompt: meta.prompt } : null,
|
|
359
|
+
preferences,
|
|
360
|
+
nextIntent,
|
|
337
361
|
}));
|
|
338
362
|
});
|
|
339
363
|
}
|
|
@@ -365,69 +389,171 @@ function advanceAndRecord(args) {
|
|
|
365
389
|
if (!pendingStep) {
|
|
366
390
|
return errAsync({ kind: 'no_pending_step' });
|
|
367
391
|
}
|
|
368
|
-
const
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
return errAsync({ kind: 'advance_apply_failed', message: advanced.error.message });
|
|
372
|
-
}
|
|
373
|
-
const ctxObj = inputContext && typeof inputContext === 'object' && inputContext !== null && !Array.isArray(inputContext)
|
|
392
|
+
const storedContextRes = (0, run_context_js_1.projectRunContextV2)(truth.events);
|
|
393
|
+
const storedContext = storedContextRes.isOk() ? storedContextRes.value.byRunId[String(runId)]?.context : undefined;
|
|
394
|
+
const inputContextObj = inputContext && typeof inputContext === 'object' && inputContext !== null && !Array.isArray(inputContext)
|
|
374
395
|
? inputContext
|
|
375
|
-
:
|
|
376
|
-
const
|
|
377
|
-
if (
|
|
378
|
-
return errAsync({
|
|
396
|
+
: undefined;
|
|
397
|
+
const mergedContextRes = (0, context_merge_js_1.mergeContext)(storedContext, inputContextObj);
|
|
398
|
+
if (mergedContextRes.isErr()) {
|
|
399
|
+
return errAsync({
|
|
400
|
+
kind: 'invariant_violation',
|
|
401
|
+
message: `Context merge failed: ${mergedContextRes.error.message}`,
|
|
402
|
+
});
|
|
379
403
|
}
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
404
|
+
const ctxObj = mergedContextRes.value;
|
|
405
|
+
const step = (0, workflow_js_1.getStepById)(pinnedWorkflow, String(pendingStep.stepId));
|
|
406
|
+
const validationCriteria = step && typeof step === 'object' && step !== null ? step.validationCriteria : undefined;
|
|
407
|
+
const notesMarkdown = inputOutput?.notesMarkdown;
|
|
408
|
+
const validator = validationCriteria ? new validation_engine_js_1.ValidationEngine(new enhanced_loop_validator_js_1.EnhancedLoopValidator()) : null;
|
|
409
|
+
const validationRes = validator && notesMarkdown
|
|
410
|
+
? neverthrow_1.ResultAsync.fromPromise(validator.validate(notesMarkdown, validationCriteria, ctxObj), (cause) => ({ kind: 'advance_apply_failed', message: String(cause) }))
|
|
411
|
+
: (0, neverthrow_1.okAsync)(undefined);
|
|
412
|
+
return validationRes.andThen((validation) => {
|
|
413
|
+
const outputRequirement = (0, validation_criteria_validator_js_1.getOutputRequirementStatusV1)({
|
|
414
|
+
validationCriteria,
|
|
415
|
+
notesMarkdown,
|
|
416
|
+
validation,
|
|
417
|
+
});
|
|
418
|
+
const reasonsRes = (0, blocking_decision_js_1.detectBlockingReasonsV1)({ outputRequirement });
|
|
419
|
+
if (reasonsRes.isErr()) {
|
|
420
|
+
return errAsync({ kind: 'invariant_violation', message: reasonsRes.error.message });
|
|
421
|
+
}
|
|
422
|
+
const reasons = reasonsRes.value;
|
|
423
|
+
const parentByNodeId = {};
|
|
424
|
+
for (const e of truth.events) {
|
|
425
|
+
if (e.kind !== 'node_created')
|
|
426
|
+
continue;
|
|
427
|
+
if (e.scope?.runId !== String(runId))
|
|
428
|
+
continue;
|
|
429
|
+
parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
|
|
430
|
+
}
|
|
431
|
+
const prefs = (0, preferences_js_1.projectPreferencesV2)(truth.events, parentByNodeId);
|
|
432
|
+
const autonomy = prefs.isOk() ? prefs.value.byNodeId[String(nodeId)]?.effective.autonomy ?? 'guided' : 'guided';
|
|
433
|
+
const shouldBlockNow = reasons.length > 0 && (0, reason_model_js_1.shouldBlock)(autonomy, reasons);
|
|
434
|
+
if (shouldBlockNow) {
|
|
435
|
+
const blockersRes = (0, reason_model_js_1.buildBlockerReport)(reasons);
|
|
436
|
+
if (blockersRes.isErr()) {
|
|
437
|
+
return errAsync({ kind: 'invariant_violation', message: blockersRes.error.message });
|
|
438
|
+
}
|
|
439
|
+
const nextEventIndex = truth.events.length === 0 ? 0 : truth.events[truth.events.length - 1].eventIndex + 1;
|
|
440
|
+
const evtAdvanceRecorded = idFactory.mintEventId();
|
|
441
|
+
const planRes = (0, ack_advance_append_plan_js_1.buildAckAdvanceAppendPlanV1)({
|
|
442
|
+
sessionId: String(sessionId),
|
|
443
|
+
runId: String(runId),
|
|
444
|
+
fromNodeId: String(nodeId),
|
|
445
|
+
workflowHash,
|
|
446
|
+
attemptId: String(attemptId),
|
|
447
|
+
nextEventIndex,
|
|
448
|
+
outcome: { kind: 'blocked', blockers: blockersRes.value },
|
|
449
|
+
minted: { advanceRecordedEventId: evtAdvanceRecorded },
|
|
450
|
+
});
|
|
451
|
+
if (planRes.isErr())
|
|
452
|
+
return errAsync({ kind: 'invariant_violation', message: planRes.error.message });
|
|
453
|
+
return sessionStore.append(lock, planRes.value);
|
|
454
|
+
}
|
|
455
|
+
const allowNotesAppend = validationCriteria
|
|
456
|
+
? Boolean(notesMarkdown && validation && validation.valid)
|
|
457
|
+
: Boolean(notesMarkdown);
|
|
458
|
+
const gapEventsToAppend = autonomy === 'full_auto_never_stop' && reasons.length > 0
|
|
459
|
+
? reasons.map((r, idx) => {
|
|
460
|
+
const g = (0, reason_model_js_1.reasonToGap)(r);
|
|
461
|
+
const gapId = `gap_${String(attemptId)}_${idx}`;
|
|
462
|
+
return {
|
|
463
|
+
v: 1,
|
|
464
|
+
eventId: idFactory.mintEventId(),
|
|
465
|
+
kind: 'gap_recorded',
|
|
466
|
+
dedupeKey: `gap_recorded:${String(sessionId)}:${gapId}`,
|
|
467
|
+
scope: { runId: String(runId), nodeId: String(nodeId) },
|
|
468
|
+
data: {
|
|
469
|
+
gapId,
|
|
470
|
+
severity: g.severity,
|
|
471
|
+
reason: g.reason,
|
|
472
|
+
summary: g.summary,
|
|
473
|
+
resolution: { kind: 'unresolved' },
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
})
|
|
477
|
+
: [];
|
|
478
|
+
const contextSetEvents = inputContextObj
|
|
397
479
|
? [
|
|
398
480
|
{
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
481
|
+
v: 1,
|
|
482
|
+
eventId: idFactory.mintEventId(),
|
|
483
|
+
kind: 'context_set',
|
|
484
|
+
dedupeKey: `context_set:${String(sessionId)}:${String(runId)}:${idFactory.mintEventId()}`,
|
|
485
|
+
scope: { runId: String(runId) },
|
|
486
|
+
data: {
|
|
487
|
+
contextId: idFactory.mintEventId(),
|
|
488
|
+
context: mergedContextRes.value,
|
|
489
|
+
source: 'agent_delta',
|
|
404
490
|
},
|
|
405
491
|
},
|
|
406
492
|
]
|
|
407
493
|
: [];
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
494
|
+
const extraEventsToAppend = [...gapEventsToAppend, ...contextSetEvents];
|
|
495
|
+
const event = { kind: 'step_completed', stepInstanceId: pendingStep };
|
|
496
|
+
const advanced = interpreter.applyEvent(currentState, event);
|
|
497
|
+
if (advanced.isErr()) {
|
|
498
|
+
return errAsync({ kind: 'advance_apply_failed', message: advanced.error.message });
|
|
499
|
+
}
|
|
500
|
+
const nextRes = interpreter.next(compiledWf.value, advanced.value, ctxObj);
|
|
501
|
+
if (nextRes.isErr()) {
|
|
502
|
+
return errAsync({ kind: 'advance_next_failed', message: nextRes.error.message });
|
|
503
|
+
}
|
|
504
|
+
const out = nextRes.value;
|
|
505
|
+
const newEngineState = fromV1ExecutionState(out.state);
|
|
506
|
+
const snapshotFile = {
|
|
507
|
+
v: 1,
|
|
508
|
+
kind: 'execution_snapshot',
|
|
509
|
+
enginePayload: { v: 1, engineState: newEngineState },
|
|
510
|
+
};
|
|
511
|
+
return snapshotStore.putExecutionSnapshotV1(snapshotFile).andThen((newSnapshotRef) => {
|
|
512
|
+
const toNodeId = String(idFactory.mintNodeId());
|
|
513
|
+
const nextEventIndex = truth.events.length === 0 ? 0 : truth.events[truth.events.length - 1].eventIndex + 1;
|
|
514
|
+
const evtAdvanceRecorded = idFactory.mintEventId();
|
|
515
|
+
const evtNodeCreated = idFactory.mintEventId();
|
|
516
|
+
const evtEdgeCreated = idFactory.mintEventId();
|
|
517
|
+
const hasChildren = truth.events.some((e) => e.kind === 'edge_created' && e.data.fromNodeId === String(nodeId));
|
|
518
|
+
const causeKind = hasChildren ? 'non_tip_advance' : 'intentional_fork';
|
|
519
|
+
const outputId = (0, index_js_1.asOutputId)(`out_recap_${String(attemptId)}`);
|
|
520
|
+
const outputsToAppend = allowNotesAppend && inputOutput?.notesMarkdown
|
|
521
|
+
? [
|
|
522
|
+
{
|
|
523
|
+
outputId: String(outputId),
|
|
524
|
+
outputChannel: 'recap',
|
|
525
|
+
payload: {
|
|
526
|
+
payloadKind: 'notes',
|
|
527
|
+
notesMarkdown: (0, notes_markdown_js_1.toNotesMarkdownV1)(inputOutput.notesMarkdown),
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
]
|
|
531
|
+
: [];
|
|
532
|
+
const normalizedOutputs = (0, outputs_js_1.normalizeOutputsForAppend)(outputsToAppend);
|
|
533
|
+
const outputEventIds = normalizedOutputs.map(() => idFactory.mintEventId());
|
|
534
|
+
const planRes = (0, ack_advance_append_plan_js_1.buildAckAdvanceAppendPlanV1)({
|
|
535
|
+
sessionId: String(sessionId),
|
|
536
|
+
runId: String(runId),
|
|
537
|
+
fromNodeId: String(nodeId),
|
|
538
|
+
workflowHash,
|
|
539
|
+
attemptId: String(attemptId),
|
|
540
|
+
nextEventIndex,
|
|
541
|
+
extraEventsToAppend,
|
|
542
|
+
toNodeId,
|
|
543
|
+
snapshotRef: newSnapshotRef,
|
|
544
|
+
causeKind,
|
|
545
|
+
minted: {
|
|
546
|
+
advanceRecordedEventId: evtAdvanceRecorded,
|
|
547
|
+
nodeCreatedEventId: evtNodeCreated,
|
|
548
|
+
edgeCreatedEventId: evtEdgeCreated,
|
|
549
|
+
outputEventIds,
|
|
550
|
+
},
|
|
551
|
+
outputsToAppend,
|
|
552
|
+
});
|
|
553
|
+
if (planRes.isErr())
|
|
554
|
+
return errAsync({ kind: 'invariant_violation', message: planRes.error.message });
|
|
555
|
+
return sessionStore.append(lock, planRes.value);
|
|
427
556
|
});
|
|
428
|
-
if (planRes.isErr())
|
|
429
|
-
return errAsync({ kind: 'invariant_violation', message: planRes.error.message });
|
|
430
|
-
return sessionStore.append(lock, planRes.value);
|
|
431
557
|
});
|
|
432
558
|
});
|
|
433
559
|
}
|
|
@@ -447,7 +573,37 @@ function extractStepMetadata(workflow, stepId, options) {
|
|
|
447
573
|
const prompt = hasStringProp(step, 'prompt')
|
|
448
574
|
? String(step.prompt)
|
|
449
575
|
: options?.defaultPrompt ?? (stepId ? `Pending step: ${stepId}` : '');
|
|
450
|
-
|
|
576
|
+
const requireConfirmation = typeof step === 'object' && step !== null && 'requireConfirmation' in step
|
|
577
|
+
? Boolean(step.requireConfirmation)
|
|
578
|
+
: false;
|
|
579
|
+
return { stepId: resolvedStepId, title, prompt, requireConfirmation };
|
|
580
|
+
}
|
|
581
|
+
const defaultPreferences = { autonomy: 'guided', riskPolicy: 'conservative' };
|
|
582
|
+
function derivePreferencesForNode(args) {
|
|
583
|
+
const parentByNodeId = {};
|
|
584
|
+
for (const e of args.truth.events) {
|
|
585
|
+
if (e.kind !== 'node_created')
|
|
586
|
+
continue;
|
|
587
|
+
if (e.scope?.runId !== String(args.runId))
|
|
588
|
+
continue;
|
|
589
|
+
parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
|
|
590
|
+
}
|
|
591
|
+
const prefs = (0, preferences_js_1.projectPreferencesV2)(args.truth.events, parentByNodeId);
|
|
592
|
+
if (prefs.isErr())
|
|
593
|
+
return defaultPreferences;
|
|
594
|
+
const p = prefs.value.byNodeId[String(args.nodeId)]?.effective;
|
|
595
|
+
if (!p)
|
|
596
|
+
return defaultPreferences;
|
|
597
|
+
return { autonomy: p.autonomy, riskPolicy: p.riskPolicy };
|
|
598
|
+
}
|
|
599
|
+
function deriveNextIntent(args) {
|
|
600
|
+
if (args.isComplete && !args.pending)
|
|
601
|
+
return 'complete';
|
|
602
|
+
if (args.rehydrateOnly)
|
|
603
|
+
return 'rehydrate_only';
|
|
604
|
+
if (!args.pending)
|
|
605
|
+
return 'complete';
|
|
606
|
+
return args.pending.requireConfirmation ? 'await_user_confirmation' : 'perform_pending_then_continue';
|
|
451
607
|
}
|
|
452
608
|
function internalError(message, suggestion) {
|
|
453
609
|
return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', normalizeTokenErrorMessage(message), suggestion ? { suggestion } : undefined);
|
|
@@ -496,38 +652,38 @@ function snapshotStoreErrorToToolError(e, suggestion) {
|
|
|
496
652
|
function pinnedWorkflowStoreErrorToToolError(e, suggestion) {
|
|
497
653
|
return internalError(`Pinned workflow store error: ${e.message}`, suggestion);
|
|
498
654
|
}
|
|
499
|
-
function parseStateTokenOrFail(raw,
|
|
500
|
-
const parsedRes = (0, index_js_1.
|
|
655
|
+
function parseStateTokenOrFail(raw, ports) {
|
|
656
|
+
const parsedRes = (0, index_js_1.parseTokenV1Binary)(raw, ports);
|
|
501
657
|
if (parsedRes.isErr()) {
|
|
502
658
|
return { ok: false, failure: (0, v2_execution_helpers_js_1.mapTokenDecodeErrorToToolError)(parsedRes.error) };
|
|
503
659
|
}
|
|
504
|
-
const verified = (0, index_js_1.
|
|
660
|
+
const verified = (0, index_js_1.verifyTokenSignatureV1Binary)(parsedRes.value, ports);
|
|
505
661
|
if (verified.isErr()) {
|
|
506
662
|
return { ok: false, failure: (0, v2_execution_helpers_js_1.mapTokenVerifyErrorToToolError)(verified.error) };
|
|
507
663
|
}
|
|
508
664
|
if (parsedRes.value.payload.tokenKind !== 'state') {
|
|
509
665
|
return {
|
|
510
666
|
ok: false,
|
|
511
|
-
failure: (0, types_js_1.errNotRetryable)('TOKEN_INVALID_FORMAT', 'Expected a state token (
|
|
667
|
+
failure: (0, types_js_1.errNotRetryable)('TOKEN_INVALID_FORMAT', 'Expected a state token (st1...).', {
|
|
512
668
|
suggestion: 'Use the stateToken returned by WorkRail.',
|
|
513
669
|
}),
|
|
514
670
|
};
|
|
515
671
|
}
|
|
516
672
|
return { ok: true, token: parsedRes.value };
|
|
517
673
|
}
|
|
518
|
-
function parseAckTokenOrFail(raw,
|
|
519
|
-
const parsedRes = (0, index_js_1.
|
|
674
|
+
function parseAckTokenOrFail(raw, ports) {
|
|
675
|
+
const parsedRes = (0, index_js_1.parseTokenV1Binary)(raw, ports);
|
|
520
676
|
if (parsedRes.isErr()) {
|
|
521
677
|
return { ok: false, failure: (0, v2_execution_helpers_js_1.mapTokenDecodeErrorToToolError)(parsedRes.error) };
|
|
522
678
|
}
|
|
523
|
-
const verified = (0, index_js_1.
|
|
679
|
+
const verified = (0, index_js_1.verifyTokenSignatureV1Binary)(parsedRes.value, ports);
|
|
524
680
|
if (verified.isErr()) {
|
|
525
681
|
return { ok: false, failure: (0, v2_execution_helpers_js_1.mapTokenVerifyErrorToToolError)(verified.error) };
|
|
526
682
|
}
|
|
527
683
|
if (parsedRes.value.payload.tokenKind !== 'ack') {
|
|
528
684
|
return {
|
|
529
685
|
ok: false,
|
|
530
|
-
failure: (0, types_js_1.errNotRetryable)('TOKEN_INVALID_FORMAT', 'Expected an ack token (
|
|
686
|
+
failure: (0, types_js_1.errNotRetryable)('TOKEN_INVALID_FORMAT', 'Expected an ack token (ack1...).', {
|
|
531
687
|
suggestion: 'Use the ackToken returned by WorkRail.',
|
|
532
688
|
}),
|
|
533
689
|
};
|
|
@@ -541,13 +697,10 @@ function attemptIdForNextNode(parentAttemptId, sha256) {
|
|
|
541
697
|
return (0, attempt_id_derivation_js_1.deriveChildAttemptId)(parentAttemptId, sha256);
|
|
542
698
|
}
|
|
543
699
|
function signTokenOrErr(args) {
|
|
544
|
-
const
|
|
545
|
-
if (bytes.isErr())
|
|
546
|
-
return (0, neverthrow_1.err)(bytes.error);
|
|
547
|
-
const token = (0, index_js_2.signTokenV1)(args.unsignedPrefix, bytes.value, args.keyring, args.hmac, args.base64url);
|
|
700
|
+
const token = (0, index_js_2.signTokenV1Binary)(args.payload, args.ports);
|
|
548
701
|
if (token.isErr())
|
|
549
702
|
return (0, neverthrow_1.err)(token.error);
|
|
550
|
-
return (0, neverthrow_1.ok)(
|
|
703
|
+
return (0, neverthrow_1.ok)(token.value);
|
|
551
704
|
}
|
|
552
705
|
function toV1ExecutionState(engineState) {
|
|
553
706
|
if (engineState.kind === 'init')
|
|
@@ -630,7 +783,7 @@ function executeStartWorkflow(input, ctx) {
|
|
|
630
783
|
if (!ctx.v2) {
|
|
631
784
|
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: 'v2 tools disabled', suggestion: 'Enable v2Tools flag' });
|
|
632
785
|
}
|
|
633
|
-
const { gate, sessionStore, snapshotStore, pinnedStore,
|
|
786
|
+
const { gate, sessionStore, snapshotStore, pinnedStore, crypto, tokenCodecPorts, idFactory } = ctx.v2;
|
|
634
787
|
if (!idFactory) {
|
|
635
788
|
return (0, neverthrow_1.errAsync)({
|
|
636
789
|
kind: 'precondition_failed',
|
|
@@ -638,6 +791,13 @@ function executeStartWorkflow(input, ctx) {
|
|
|
638
791
|
suggestion: 'Reinitialize v2 tool context (idFactory must be provided when v2Tools are enabled).',
|
|
639
792
|
});
|
|
640
793
|
}
|
|
794
|
+
if (!tokenCodecPorts) {
|
|
795
|
+
return (0, neverthrow_1.errAsync)({
|
|
796
|
+
kind: 'precondition_failed',
|
|
797
|
+
message: 'v2 context missing tokenCodecPorts dependency',
|
|
798
|
+
suggestion: 'Reinitialize v2 tool context (tokenCodecPorts must be provided when v2Tools are enabled).',
|
|
799
|
+
});
|
|
800
|
+
}
|
|
641
801
|
const ctxCheck = checkContextBudget({ tool: 'start_workflow', context: input.context });
|
|
642
802
|
if (!ctxCheck.ok)
|
|
643
803
|
return (0, neverthrow_1.errAsync)({ kind: 'validation_failed', failure: ctxCheck.error });
|
|
@@ -708,7 +868,11 @@ function executeStartWorkflow(input, ctx) {
|
|
|
708
868
|
const evtRunStarted = idFactory.mintEventId();
|
|
709
869
|
const evtNodeCreated = idFactory.mintEventId();
|
|
710
870
|
return gate.withHealthySessionLock(sessionId, (lock) => {
|
|
711
|
-
const
|
|
871
|
+
const evtPreferencesChanged = idFactory.mintEventId();
|
|
872
|
+
const changeId = idFactory.mintEventId();
|
|
873
|
+
const evtContextSet = idFactory.mintEventId();
|
|
874
|
+
const contextId = idFactory.mintEventId();
|
|
875
|
+
const baseEvents = [
|
|
712
876
|
{
|
|
713
877
|
v: 1,
|
|
714
878
|
eventId: evtSessionCreated,
|
|
@@ -756,7 +920,47 @@ function executeStartWorkflow(input, ctx) {
|
|
|
756
920
|
snapshotRef,
|
|
757
921
|
},
|
|
758
922
|
},
|
|
923
|
+
{
|
|
924
|
+
v: 1,
|
|
925
|
+
eventId: evtPreferencesChanged,
|
|
926
|
+
eventIndex: 3,
|
|
927
|
+
sessionId,
|
|
928
|
+
kind: 'preferences_changed',
|
|
929
|
+
dedupeKey: `preferences_changed:${sessionId}:${runId}:${nodeId}:${changeId}`,
|
|
930
|
+
scope: { runId, nodeId },
|
|
931
|
+
data: {
|
|
932
|
+
changeId,
|
|
933
|
+
source: 'system',
|
|
934
|
+
delta: [
|
|
935
|
+
{ key: 'autonomy', value: defaultPreferences.autonomy },
|
|
936
|
+
{ key: 'riskPolicy', value: defaultPreferences.riskPolicy },
|
|
937
|
+
],
|
|
938
|
+
effective: {
|
|
939
|
+
autonomy: defaultPreferences.autonomy,
|
|
940
|
+
riskPolicy: defaultPreferences.riskPolicy,
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
},
|
|
759
944
|
];
|
|
945
|
+
const eventsArray = input.context
|
|
946
|
+
? [
|
|
947
|
+
...baseEvents,
|
|
948
|
+
{
|
|
949
|
+
v: 1,
|
|
950
|
+
eventId: evtContextSet,
|
|
951
|
+
eventIndex: 4,
|
|
952
|
+
sessionId,
|
|
953
|
+
kind: 'context_set',
|
|
954
|
+
dedupeKey: `context_set:${sessionId}:${runId}:${contextId}`,
|
|
955
|
+
scope: { runId },
|
|
956
|
+
data: {
|
|
957
|
+
contextId,
|
|
958
|
+
context: input.context,
|
|
959
|
+
source: 'initial',
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
]
|
|
963
|
+
: baseEvents;
|
|
760
964
|
return sessionStore.append(lock, {
|
|
761
965
|
events: eventsArray,
|
|
762
966
|
snapshotPins: [{ snapshotRef, eventIndex: 2, createdByEventId: evtNodeCreated }],
|
|
@@ -767,13 +971,21 @@ function executeStartWorkflow(input, ctx) {
|
|
|
767
971
|
});
|
|
768
972
|
})
|
|
769
973
|
.andThen(({ pinnedWorkflow, firstStep, workflowHash, sessionId, runId, nodeId }) => {
|
|
974
|
+
const wfRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
|
|
975
|
+
if (wfRefRes.isErr()) {
|
|
976
|
+
return (0, neverthrow_1.errAsync)({
|
|
977
|
+
kind: 'precondition_failed',
|
|
978
|
+
message: wfRefRes.error.message,
|
|
979
|
+
suggestion: 'Ensure the pinned workflowHash is a valid sha256 digest.',
|
|
980
|
+
});
|
|
981
|
+
}
|
|
770
982
|
const statePayload = {
|
|
771
983
|
tokenVersion: 1,
|
|
772
984
|
tokenKind: 'state',
|
|
773
985
|
sessionId,
|
|
774
986
|
runId,
|
|
775
987
|
nodeId,
|
|
776
|
-
|
|
988
|
+
workflowHashRef: wfRefRes.value,
|
|
777
989
|
};
|
|
778
990
|
const attemptId = newAttemptId(idFactory);
|
|
779
991
|
const ackPayload = {
|
|
@@ -784,31 +996,37 @@ function executeStartWorkflow(input, ctx) {
|
|
|
784
996
|
nodeId,
|
|
785
997
|
attemptId,
|
|
786
998
|
};
|
|
787
|
-
const
|
|
788
|
-
tokenVersion: 1,
|
|
789
|
-
tokenKind: 'checkpoint',
|
|
790
|
-
sessionId,
|
|
791
|
-
runId,
|
|
792
|
-
nodeId,
|
|
793
|
-
attemptId,
|
|
794
|
-
};
|
|
795
|
-
const stateToken = signTokenOrErr({ unsignedPrefix: 'st.v1.', payload: statePayload, keyring, hmac, base64url });
|
|
999
|
+
const stateToken = signTokenOrErr({ payload: statePayload, ports: tokenCodecPorts });
|
|
796
1000
|
if (stateToken.isErr())
|
|
797
1001
|
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: stateToken.error });
|
|
798
|
-
const ackToken = signTokenOrErr({
|
|
1002
|
+
const ackToken = signTokenOrErr({ payload: ackPayload, ports: tokenCodecPorts });
|
|
799
1003
|
if (ackToken.isErr())
|
|
800
1004
|
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: ackToken.error });
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1005
|
+
const metaRes = (0, prompt_renderer_js_1.renderPendingPrompt)({
|
|
1006
|
+
workflow: pinnedWorkflow,
|
|
1007
|
+
stepId: firstStep.id,
|
|
1008
|
+
loopPath: [],
|
|
1009
|
+
truth: { events: [], manifest: [] },
|
|
1010
|
+
runId: (0, index_js_3.asRunId)(String(runId)),
|
|
1011
|
+
nodeId: (0, index_js_3.asNodeId)(String(nodeId)),
|
|
1012
|
+
rehydrateOnly: false,
|
|
1013
|
+
});
|
|
1014
|
+
const meta = metaRes.isOk() ? metaRes.value : {
|
|
1015
|
+
stepId: firstStep.id,
|
|
1016
|
+
title: firstStep.title,
|
|
1017
|
+
prompt: firstStep.prompt,
|
|
1018
|
+
requireConfirmation: Boolean(firstStep.requireConfirmation),
|
|
1019
|
+
};
|
|
1020
|
+
const pending = { stepId: meta.stepId, title: meta.title, prompt: meta.prompt };
|
|
1021
|
+
const preferences = defaultPreferences;
|
|
1022
|
+
const nextIntent = deriveNextIntent({ rehydrateOnly: false, isComplete: false, pending: meta });
|
|
806
1023
|
return (0, neverthrow_1.okAsync)(output_schemas_js_1.V2StartWorkflowOutputSchema.parse({
|
|
807
1024
|
stateToken: stateToken.value,
|
|
808
1025
|
ackToken: ackToken.value,
|
|
809
|
-
checkpointToken: checkpointToken.value,
|
|
810
1026
|
isComplete: false,
|
|
811
1027
|
pending,
|
|
1028
|
+
preferences,
|
|
1029
|
+
nextIntent,
|
|
812
1030
|
}));
|
|
813
1031
|
});
|
|
814
1032
|
}
|
|
@@ -819,7 +1037,7 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
819
1037
|
if (!ctx.v2) {
|
|
820
1038
|
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: 'v2 tools disabled', suggestion: 'Enable v2Tools flag' });
|
|
821
1039
|
}
|
|
822
|
-
const { gate, sessionStore, snapshotStore, pinnedStore,
|
|
1040
|
+
const { gate, sessionStore, snapshotStore, pinnedStore, sha256, tokenCodecPorts, idFactory } = ctx.v2;
|
|
823
1041
|
if (!sha256 || !idFactory) {
|
|
824
1042
|
return (0, neverthrow_1.errAsync)({
|
|
825
1043
|
kind: 'precondition_failed',
|
|
@@ -827,7 +1045,14 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
827
1045
|
suggestion: 'Reinitialize v2 tool context (sha256 and idFactory must be provided when v2Tools are enabled).',
|
|
828
1046
|
});
|
|
829
1047
|
}
|
|
830
|
-
|
|
1048
|
+
if (!tokenCodecPorts) {
|
|
1049
|
+
return (0, neverthrow_1.errAsync)({
|
|
1050
|
+
kind: 'precondition_failed',
|
|
1051
|
+
message: 'v2 context missing tokenCodecPorts dependency',
|
|
1052
|
+
suggestion: 'Reinitialize v2 tool context (tokenCodecPorts must be provided when v2Tools are enabled).',
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
const stateRes = parseStateTokenOrFail(input.stateToken, tokenCodecPorts);
|
|
831
1056
|
if (!stateRes.ok)
|
|
832
1057
|
return (0, neverthrow_1.errAsync)({ kind: 'validation_failed', failure: stateRes.failure });
|
|
833
1058
|
const state = stateRes.token;
|
|
@@ -837,7 +1062,7 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
837
1062
|
const sessionId = (0, index_js_3.asSessionId)(state.payload.sessionId);
|
|
838
1063
|
const runId = (0, index_js_3.asRunId)(state.payload.runId);
|
|
839
1064
|
const nodeId = (0, index_js_3.asNodeId)(state.payload.nodeId);
|
|
840
|
-
const
|
|
1065
|
+
const workflowHashRef = state.payload.workflowHashRef;
|
|
841
1066
|
if (!input.ackToken) {
|
|
842
1067
|
return sessionStore.load(sessionId)
|
|
843
1068
|
.mapErr((cause) => ({ kind: 'session_load_failed', cause }))
|
|
@@ -851,7 +1076,16 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
851
1076
|
suggestion: 'Use start_workflow to mint a new run, or use a stateToken returned by WorkRail for an existing run.',
|
|
852
1077
|
});
|
|
853
1078
|
}
|
|
854
|
-
|
|
1079
|
+
const workflowHash = runStarted.data.workflowHash;
|
|
1080
|
+
const expectedRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
|
|
1081
|
+
if (expectedRefRes.isErr()) {
|
|
1082
|
+
return (0, neverthrow_1.errAsync)({
|
|
1083
|
+
kind: 'precondition_failed',
|
|
1084
|
+
message: expectedRefRes.error.message,
|
|
1085
|
+
suggestion: 'Re-pin the workflow via start_workflow.',
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
if (String(expectedRefRes.value) !== String(workflowHashRef)) {
|
|
855
1089
|
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: 'workflowHash mismatch for this run.', suggestion: 'Use the stateToken returned by WorkRail for this run.' });
|
|
856
1090
|
}
|
|
857
1091
|
const nodeCreated = truth.events.find((e) => e.kind === 'node_created' && e.scope.nodeId === String(nodeId) && e.scope.runId === String(runId));
|
|
@@ -862,7 +1096,15 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
862
1096
|
suggestion: 'Use a stateToken returned by WorkRail for an existing node.',
|
|
863
1097
|
});
|
|
864
1098
|
}
|
|
865
|
-
|
|
1099
|
+
const expectedNodeRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(nodeCreated.data.workflowHash);
|
|
1100
|
+
if (expectedNodeRefRes.isErr()) {
|
|
1101
|
+
return (0, neverthrow_1.errAsync)({
|
|
1102
|
+
kind: 'precondition_failed',
|
|
1103
|
+
message: expectedNodeRefRes.error.message,
|
|
1104
|
+
suggestion: 'Re-pin the workflow via start_workflow.',
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
if (String(expectedNodeRefRes.value) !== String(workflowHashRef)) {
|
|
866
1108
|
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: 'workflowHash mismatch for this node.', suggestion: 'Use the stateToken returned by WorkRail for this node.' });
|
|
867
1109
|
}
|
|
868
1110
|
return snapshotStore.getExecutionSnapshotV1(nodeCreated.data.snapshotRef)
|
|
@@ -878,40 +1120,30 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
878
1120
|
const engineState = snapshot.enginePayload.engineState;
|
|
879
1121
|
const pending = (0, snapshot_state_js_1.derivePendingStep)(engineState);
|
|
880
1122
|
const isComplete = (0, snapshot_state_js_1.deriveIsComplete)(engineState);
|
|
881
|
-
const attemptId = newAttemptId(idFactory);
|
|
882
|
-
const ackTokenRes = signTokenOrErr({
|
|
883
|
-
unsignedPrefix: 'ack.v1.',
|
|
884
|
-
payload: { tokenVersion: 1, tokenKind: 'ack', sessionId, runId, nodeId, attemptId },
|
|
885
|
-
keyring,
|
|
886
|
-
hmac,
|
|
887
|
-
base64url,
|
|
888
|
-
});
|
|
889
|
-
if (ackTokenRes.isErr())
|
|
890
|
-
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: ackTokenRes.error });
|
|
891
|
-
const checkpointTokenRes = signTokenOrErr({
|
|
892
|
-
unsignedPrefix: 'chk.v1.',
|
|
893
|
-
payload: { tokenVersion: 1, tokenKind: 'checkpoint', sessionId, runId, nodeId, attemptId },
|
|
894
|
-
keyring,
|
|
895
|
-
hmac,
|
|
896
|
-
base64url,
|
|
897
|
-
});
|
|
898
|
-
if (checkpointTokenRes.isErr())
|
|
899
|
-
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: checkpointTokenRes.error });
|
|
900
1123
|
if (!pending) {
|
|
1124
|
+
const preferences = derivePreferencesForNode({ truth, runId, nodeId });
|
|
1125
|
+
const nextIntent = deriveNextIntent({ rehydrateOnly: true, isComplete, pending: null });
|
|
901
1126
|
return (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
|
|
902
1127
|
kind: 'ok',
|
|
903
1128
|
stateToken: input.stateToken,
|
|
904
|
-
ackToken: ackTokenRes.value,
|
|
905
|
-
checkpointToken: checkpointTokenRes.value,
|
|
906
1129
|
isComplete,
|
|
907
1130
|
pending: null,
|
|
1131
|
+
preferences,
|
|
1132
|
+
nextIntent,
|
|
908
1133
|
}));
|
|
909
1134
|
}
|
|
1135
|
+
const attemptId = newAttemptId(idFactory);
|
|
1136
|
+
const ackTokenRes = signTokenOrErr({
|
|
1137
|
+
payload: { tokenVersion: 1, tokenKind: 'ack', sessionId, runId, nodeId, attemptId },
|
|
1138
|
+
ports: tokenCodecPorts,
|
|
1139
|
+
});
|
|
1140
|
+
if (ackTokenRes.isErr())
|
|
1141
|
+
return (0, neverthrow_1.errAsync)({ kind: 'token_signing_failed', cause: ackTokenRes.error });
|
|
910
1142
|
return pinnedStore.get(workflowHash)
|
|
911
1143
|
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
|
|
912
1144
|
.andThen((pinned) => {
|
|
913
1145
|
if (!pinned)
|
|
914
|
-
return (0, neverthrow_1.errAsync)({ kind: 'pinned_workflow_missing', workflowHash
|
|
1146
|
+
return (0, neverthrow_1.errAsync)({ kind: 'pinned_workflow_missing', workflowHash });
|
|
915
1147
|
if (pinned.sourceKind !== 'v1_pinned')
|
|
916
1148
|
return (0, neverthrow_1.errAsync)({ kind: 'precondition_failed', message: 'Pinned workflow snapshot is read-only (v1_preview) and cannot be executed.' });
|
|
917
1149
|
if (!(0, workflow_definition_js_1.hasWorkflowDefinitionShape)(pinned.definition)) {
|
|
@@ -922,24 +1154,43 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
922
1154
|
});
|
|
923
1155
|
}
|
|
924
1156
|
const wf = (0, workflow_js_1.createWorkflow)(pinned.definition, (0, workflow_source_js_1.createBundledSource)());
|
|
925
|
-
const
|
|
1157
|
+
const metaRes = (0, prompt_renderer_js_1.renderPendingPrompt)({
|
|
1158
|
+
workflow: wf,
|
|
1159
|
+
stepId: String(pending.stepId),
|
|
1160
|
+
loopPath: pending.loopPath,
|
|
1161
|
+
truth,
|
|
1162
|
+
runId: (0, index_js_3.asRunId)(String(runId)),
|
|
1163
|
+
nodeId: (0, index_js_3.asNodeId)(String(nodeId)),
|
|
1164
|
+
rehydrateOnly: true,
|
|
1165
|
+
});
|
|
1166
|
+
if (metaRes.isErr()) {
|
|
1167
|
+
return (0, neverthrow_1.errAsync)({
|
|
1168
|
+
kind: 'invariant_violation',
|
|
1169
|
+
message: `Prompt rendering failed: ${metaRes.error.message}`,
|
|
1170
|
+
suggestion: 'Retry; if this persists, treat as invariant violation.',
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
const meta = metaRes.value;
|
|
1174
|
+
const preferences = derivePreferencesForNode({ truth, runId, nodeId });
|
|
1175
|
+
const nextIntent = deriveNextIntent({ rehydrateOnly: true, isComplete, pending: meta });
|
|
926
1176
|
return (0, neverthrow_1.okAsync)(output_schemas_js_1.V2ContinueWorkflowOutputSchema.parse({
|
|
927
1177
|
kind: 'ok',
|
|
928
1178
|
stateToken: input.stateToken,
|
|
929
1179
|
ackToken: ackTokenRes.value,
|
|
930
|
-
checkpointToken: checkpointTokenRes.value,
|
|
931
1180
|
isComplete,
|
|
932
|
-
pending: { stepId, title, prompt },
|
|
1181
|
+
pending: { stepId: meta.stepId, title: meta.title, prompt: meta.prompt },
|
|
1182
|
+
preferences,
|
|
1183
|
+
nextIntent,
|
|
933
1184
|
}));
|
|
934
1185
|
});
|
|
935
1186
|
});
|
|
936
1187
|
});
|
|
937
1188
|
}
|
|
938
|
-
const ackRes = parseAckTokenOrFail(input.ackToken,
|
|
1189
|
+
const ackRes = parseAckTokenOrFail(input.ackToken, tokenCodecPorts);
|
|
939
1190
|
if (!ackRes.ok)
|
|
940
1191
|
return (0, neverthrow_1.errAsync)({ kind: 'validation_failed', failure: ackRes.failure });
|
|
941
1192
|
const ack = ackRes.token;
|
|
942
|
-
const scopeRes = (0, index_js_1.
|
|
1193
|
+
const scopeRes = (0, index_js_1.assertTokenScopeMatchesStateBinary)(state, ack);
|
|
943
1194
|
if (scopeRes.isErr())
|
|
944
1195
|
return (0, neverthrow_1.errAsync)({ kind: 'validation_failed', failure: (0, v2_execution_helpers_js_1.mapTokenDecodeErrorToToolError)(scopeRes.error) });
|
|
945
1196
|
const attemptId = (0, index_js_1.asAttemptId)(ack.payload.attemptId);
|
|
@@ -947,6 +1198,53 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
947
1198
|
return sessionStore.load(sessionId)
|
|
948
1199
|
.mapErr((cause) => ({ kind: 'session_load_failed', cause }))
|
|
949
1200
|
.andThen((truth) => {
|
|
1201
|
+
const runStarted = truth.events.find((e) => e.kind === 'run_started' && e.scope.runId === String(runId));
|
|
1202
|
+
if (!runStarted) {
|
|
1203
|
+
return (0, neverthrow_1.errAsync)({
|
|
1204
|
+
kind: 'token_unknown_node',
|
|
1205
|
+
message: 'No durable run state was found for this token (missing run_started).',
|
|
1206
|
+
suggestion: 'Use start_workflow to mint a new run, or use tokens returned by WorkRail for an existing run.',
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
const workflowHash = runStarted.data.workflowHash;
|
|
1210
|
+
const refRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(workflowHash);
|
|
1211
|
+
if (refRes.isErr()) {
|
|
1212
|
+
return (0, neverthrow_1.errAsync)({
|
|
1213
|
+
kind: 'precondition_failed',
|
|
1214
|
+
message: refRes.error.message,
|
|
1215
|
+
suggestion: 'Re-pin the workflow via start_workflow.',
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
if (String(refRes.value) !== String(workflowHashRef)) {
|
|
1219
|
+
return (0, neverthrow_1.errAsync)({
|
|
1220
|
+
kind: 'precondition_failed',
|
|
1221
|
+
message: 'workflowHash mismatch for this run.',
|
|
1222
|
+
suggestion: 'Use the stateToken returned by WorkRail for this run.',
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
const nodeCreated = truth.events.find((e) => e.kind === 'node_created' && e.scope.nodeId === String(nodeId) && e.scope.runId === String(runId));
|
|
1226
|
+
if (!nodeCreated) {
|
|
1227
|
+
return (0, neverthrow_1.errAsync)({
|
|
1228
|
+
kind: 'token_unknown_node',
|
|
1229
|
+
message: 'No durable node state was found for this token (missing node_created).',
|
|
1230
|
+
suggestion: 'Use tokens returned by WorkRail for an existing node.',
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
const nodeRefRes = (0, workflow_hash_ref_js_1.deriveWorkflowHashRef)(nodeCreated.data.workflowHash);
|
|
1234
|
+
if (nodeRefRes.isErr()) {
|
|
1235
|
+
return (0, neverthrow_1.errAsync)({
|
|
1236
|
+
kind: 'precondition_failed',
|
|
1237
|
+
message: nodeRefRes.error.message,
|
|
1238
|
+
suggestion: 'Re-pin the workflow via start_workflow.',
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
if (String(nodeRefRes.value) !== String(workflowHashRef)) {
|
|
1242
|
+
return (0, neverthrow_1.errAsync)({
|
|
1243
|
+
kind: 'precondition_failed',
|
|
1244
|
+
message: 'workflowHash mismatch for this node.',
|
|
1245
|
+
suggestion: 'Use the stateToken returned by WorkRail for this node.',
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
950
1248
|
const existing = truth.events.find((e) => e.kind === 'advance_recorded' && e.dedupeKey === dedupeKey);
|
|
951
1249
|
return pinnedStore.get(workflowHash)
|
|
952
1250
|
.mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
|
|
@@ -976,10 +1274,8 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
976
1274
|
inputAckToken: input.ackToken,
|
|
977
1275
|
pinnedWorkflow,
|
|
978
1276
|
snapshotStore,
|
|
979
|
-
keyring,
|
|
980
1277
|
sha256,
|
|
981
|
-
|
|
982
|
-
base64url,
|
|
1278
|
+
tokenCodecPorts,
|
|
983
1279
|
});
|
|
984
1280
|
}
|
|
985
1281
|
return gate
|
|
@@ -1050,10 +1346,8 @@ function executeContinueWorkflow(input, ctx) {
|
|
|
1050
1346
|
inputAckToken: input.ackToken,
|
|
1051
1347
|
pinnedWorkflow,
|
|
1052
1348
|
snapshotStore,
|
|
1053
|
-
keyring,
|
|
1054
1349
|
sha256,
|
|
1055
|
-
|
|
1056
|
-
base64url,
|
|
1350
|
+
tokenCodecPorts,
|
|
1057
1351
|
});
|
|
1058
1352
|
});
|
|
1059
1353
|
});
|