la-machina-engine 0.19.3 → 0.19.7
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/index.cjs +250 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -62
- package/dist/index.d.ts +122 -62
- package/dist/index.js +250 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2098,6 +2098,7 @@ var AnthropicClient = class {
|
|
|
2098
2098
|
if (betas.length > 0) {
|
|
2099
2099
|
requestOptions.headers = { "anthropic-beta": betas.join(",") };
|
|
2100
2100
|
}
|
|
2101
|
+
if (request.abortSignal !== void 0) requestOptions.signal = request.abortSignal;
|
|
2101
2102
|
let stream;
|
|
2102
2103
|
try {
|
|
2103
2104
|
stream = this.sdk.messages.stream(params, requestOptions);
|
|
@@ -2284,6 +2285,7 @@ var AISdkAdapter = class {
|
|
|
2284
2285
|
tools,
|
|
2285
2286
|
...request.maxTokens !== void 0 ? { maxOutputTokens: request.maxTokens } : {},
|
|
2286
2287
|
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
2288
|
+
...request.abortSignal !== void 0 ? { abortSignal: request.abortSignal } : {},
|
|
2287
2289
|
// Plan 025 — pass through `'required'` so the AI SDK forwards it
|
|
2288
2290
|
// to the provider as that provider's "force tool call" flag.
|
|
2289
2291
|
...request.toolChoice === "required" ? { toolChoice: "required" } : {},
|
|
@@ -3305,6 +3307,9 @@ async function emitInspectTurn(args) {
|
|
|
3305
3307
|
...cacheRead !== void 0 ? { cacheReadInput: cacheRead } : {}
|
|
3306
3308
|
});
|
|
3307
3309
|
}
|
|
3310
|
+
function isAbortSignalAborted(signal) {
|
|
3311
|
+
return signal?.aborted === true;
|
|
3312
|
+
}
|
|
3308
3313
|
var DEFAULT_COMPACTION = {
|
|
3309
3314
|
strategy: "drop-middle",
|
|
3310
3315
|
threshold: 0.85,
|
|
@@ -3457,6 +3462,7 @@ async function agentLoop(options) {
|
|
|
3457
3462
|
messages: normalizedMessages,
|
|
3458
3463
|
system,
|
|
3459
3464
|
tools: anthropicTools,
|
|
3465
|
+
...options.runSignal !== void 0 ? { abortSignal: options.runSignal } : {},
|
|
3460
3466
|
...escalatedMaxTokens !== void 0 ? { maxTokens: escalatedMaxTokens } : {},
|
|
3461
3467
|
...options.toolChoice !== void 0 ? { toolChoice: options.toolChoice } : {}
|
|
3462
3468
|
})) {
|
|
@@ -3470,6 +3476,9 @@ async function agentLoop(options) {
|
|
|
3470
3476
|
}
|
|
3471
3477
|
}
|
|
3472
3478
|
} catch (err) {
|
|
3479
|
+
if (isAbortSignalAborted(options.runSignal)) {
|
|
3480
|
+
return failed(new RunTimeoutError(options.runTimeoutMs ?? 0), transcript);
|
|
3481
|
+
}
|
|
3473
3482
|
if (isPromptTooLong(err) && !compactedThisTurn) {
|
|
3474
3483
|
compactedThisTurn = true;
|
|
3475
3484
|
const emergency = await compactIfNeeded({
|
|
@@ -10980,6 +10989,38 @@ var RunStateManager = class {
|
|
|
10980
10989
|
await this.write(next);
|
|
10981
10990
|
return next;
|
|
10982
10991
|
}
|
|
10992
|
+
/**
|
|
10993
|
+
* Merge async lifecycle timing fields into the durable state.
|
|
10994
|
+
* Existing timestamps are preserved unless explicitly overwritten
|
|
10995
|
+
* by the caller.
|
|
10996
|
+
*/
|
|
10997
|
+
async patchAsyncTiming(runId, nodeId, patch) {
|
|
10998
|
+
const current = await this.read(runId, nodeId);
|
|
10999
|
+
if (current === null) {
|
|
11000
|
+
throw new Error(`RunStateManager.patchAsyncTiming: no state found for ${runId}/${nodeId}`);
|
|
11001
|
+
}
|
|
11002
|
+
const next = {
|
|
11003
|
+
...current,
|
|
11004
|
+
asyncTiming: { ...current.asyncTiming ?? {}, ...patch }
|
|
11005
|
+
};
|
|
11006
|
+
await this.write(next);
|
|
11007
|
+
return next;
|
|
11008
|
+
}
|
|
11009
|
+
async appendManualWebhookRetry(runId, nodeId, row) {
|
|
11010
|
+
const current = await this.read(runId, nodeId);
|
|
11011
|
+
if (current === null) {
|
|
11012
|
+
throw new Error(
|
|
11013
|
+
`RunStateManager.appendManualWebhookRetry: no state found for ${runId}/${nodeId}`
|
|
11014
|
+
);
|
|
11015
|
+
}
|
|
11016
|
+
const retries = current.manualWebhookRetries ?? [];
|
|
11017
|
+
const next = {
|
|
11018
|
+
...current,
|
|
11019
|
+
manualWebhookRetries: [...retries, row]
|
|
11020
|
+
};
|
|
11021
|
+
await this.write(next);
|
|
11022
|
+
return next;
|
|
11023
|
+
}
|
|
10983
11024
|
/**
|
|
10984
11025
|
* Update just the heartbeat + progress (cheap, called every turn).
|
|
10985
11026
|
*/
|
|
@@ -11716,6 +11757,7 @@ ${inputJson}
|
|
|
11716
11757
|
* uses fire-and-forget Promises which won't survive Worker request exit.
|
|
11717
11758
|
*/
|
|
11718
11759
|
async start(options) {
|
|
11760
|
+
const startCalledAt = Date.now();
|
|
11719
11761
|
const runId = options.runId ?? `run_${randomUUID()}`;
|
|
11720
11762
|
const storage = await this.buildStorage();
|
|
11721
11763
|
const stateManager = new RunStateManager(storage.workspace);
|
|
@@ -11727,19 +11769,62 @@ ${inputJson}
|
|
|
11727
11769
|
deliveries: []
|
|
11728
11770
|
} : void 0;
|
|
11729
11771
|
const initial = RunStateManager.initial(runId, options.nodeId, webhook);
|
|
11730
|
-
await stateManager.write(
|
|
11772
|
+
await stateManager.write({
|
|
11773
|
+
...initial,
|
|
11774
|
+
asyncTiming: { startCalledAt }
|
|
11775
|
+
});
|
|
11776
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11777
|
+
initialStateWrittenAt: Date.now()
|
|
11778
|
+
});
|
|
11731
11779
|
const handoffEnabled = this.config.runner !== void 0;
|
|
11780
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11781
|
+
backgroundScheduledAt: Date.now()
|
|
11782
|
+
});
|
|
11732
11783
|
this.backgroundExecutor.schedule(runId, async (signal) => {
|
|
11784
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11785
|
+
backgroundStartedAt: Date.now()
|
|
11786
|
+
});
|
|
11787
|
+
if (signal.aborted) return;
|
|
11733
11788
|
await stateManager.update(runId, options.nodeId, { status: "running" });
|
|
11734
11789
|
try {
|
|
11790
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11791
|
+
runCallStartedAt: Date.now()
|
|
11792
|
+
});
|
|
11735
11793
|
const response = await this.run({ ...options, runId }, { handoffToRunner: handoffEnabled });
|
|
11794
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11795
|
+
runCallCompletedAt: Date.now()
|
|
11796
|
+
});
|
|
11736
11797
|
if (signal.aborted) return;
|
|
11798
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11799
|
+
handoffStartedAt: Date.now()
|
|
11800
|
+
});
|
|
11737
11801
|
const postHandoff = await this.maybeHandoffToRunner(runId, options.nodeId, response);
|
|
11802
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11803
|
+
handoffCompletedAt: Date.now(),
|
|
11804
|
+
finalizeStartedAt: Date.now()
|
|
11805
|
+
});
|
|
11806
|
+
if (await this.runnerAlreadyWroteTerminal(stateManager, runId, options.nodeId, postHandoff)) {
|
|
11807
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11808
|
+
finalizeCompletedAt: Date.now(),
|
|
11809
|
+
backgroundCompletedAt: Date.now()
|
|
11810
|
+
});
|
|
11811
|
+
return;
|
|
11812
|
+
}
|
|
11738
11813
|
await stateManager.finalize(runId, options.nodeId, postHandoff);
|
|
11814
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11815
|
+
finalizeCompletedAt: Date.now()
|
|
11816
|
+
});
|
|
11739
11817
|
await this.maybeFireWebhook(stateManager, runId, options.nodeId, postHandoff);
|
|
11818
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11819
|
+
backgroundCompletedAt: Date.now()
|
|
11820
|
+
});
|
|
11740
11821
|
} catch (err) {
|
|
11741
11822
|
if (signal.aborted) return;
|
|
11742
11823
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
11824
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11825
|
+
backgroundFailedAt: Date.now(),
|
|
11826
|
+
errorMessage: errorMsg
|
|
11827
|
+
});
|
|
11743
11828
|
const failResponse = {
|
|
11744
11829
|
runId,
|
|
11745
11830
|
status: "failed",
|
|
@@ -11748,8 +11833,17 @@ ${inputJson}
|
|
|
11748
11833
|
errors: [{ code: "RUN_FAILED", message: errorMsg }],
|
|
11749
11834
|
timestamp: Date.now()
|
|
11750
11835
|
};
|
|
11836
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11837
|
+
finalizeStartedAt: Date.now()
|
|
11838
|
+
});
|
|
11751
11839
|
await stateManager.finalize(runId, options.nodeId, failResponse);
|
|
11840
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11841
|
+
finalizeCompletedAt: Date.now()
|
|
11842
|
+
});
|
|
11752
11843
|
await this.maybeFireWebhook(stateManager, runId, options.nodeId, failResponse);
|
|
11844
|
+
await this.recordAsyncTiming(stateManager, runId, options.nodeId, {
|
|
11845
|
+
backgroundCompletedAt: Date.now()
|
|
11846
|
+
});
|
|
11753
11847
|
}
|
|
11754
11848
|
});
|
|
11755
11849
|
return { runId, nodeId: options.nodeId, status: "queued" };
|
|
@@ -11759,6 +11853,7 @@ ${inputJson}
|
|
|
11759
11853
|
* dispatched via the background executor. Returns immediately.
|
|
11760
11854
|
*/
|
|
11761
11855
|
async resumeAsync(options) {
|
|
11856
|
+
const startCalledAt = Date.now();
|
|
11762
11857
|
const storage = await this.buildStorage();
|
|
11763
11858
|
const stateManager = new RunStateManager(storage.workspace);
|
|
11764
11859
|
let nodeId = options.nodeId;
|
|
@@ -11780,21 +11875,74 @@ ${inputJson}
|
|
|
11780
11875
|
lastHeartbeat: Date.now(),
|
|
11781
11876
|
response: null,
|
|
11782
11877
|
// clear stale paused response so getStatus returns provisional
|
|
11878
|
+
asyncTiming: {
|
|
11879
|
+
...existing.asyncTiming ?? {},
|
|
11880
|
+
startCalledAt
|
|
11881
|
+
},
|
|
11783
11882
|
...webhook !== void 0 ? { webhook } : {}
|
|
11784
|
-
} : {
|
|
11883
|
+
} : {
|
|
11884
|
+
...RunStateManager.initial(options.runId, nodeId, webhook),
|
|
11885
|
+
status: "running",
|
|
11886
|
+
asyncTiming: { startCalledAt }
|
|
11887
|
+
};
|
|
11785
11888
|
await stateManager.write(next);
|
|
11889
|
+
await this.recordAsyncTiming(stateManager, options.runId, nodeId, {
|
|
11890
|
+
initialStateWrittenAt: Date.now()
|
|
11891
|
+
});
|
|
11786
11892
|
const resumeNodeId = nodeId;
|
|
11787
11893
|
const handoffEnabled = this.config.runner !== void 0;
|
|
11894
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11895
|
+
backgroundScheduledAt: Date.now()
|
|
11896
|
+
});
|
|
11788
11897
|
this.backgroundExecutor.schedule(options.runId, async (signal) => {
|
|
11898
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11899
|
+
backgroundStartedAt: Date.now()
|
|
11900
|
+
});
|
|
11901
|
+
if (signal.aborted) return;
|
|
11789
11902
|
try {
|
|
11903
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11904
|
+
runCallStartedAt: Date.now()
|
|
11905
|
+
});
|
|
11790
11906
|
const response = await this.resume(options, { handoffToRunner: handoffEnabled });
|
|
11907
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11908
|
+
runCallCompletedAt: Date.now()
|
|
11909
|
+
});
|
|
11791
11910
|
if (signal.aborted) return;
|
|
11911
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11912
|
+
handoffStartedAt: Date.now()
|
|
11913
|
+
});
|
|
11792
11914
|
const postHandoff = await this.maybeHandoffToRunner(options.runId, resumeNodeId, response);
|
|
11915
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11916
|
+
handoffCompletedAt: Date.now(),
|
|
11917
|
+
finalizeStartedAt: Date.now()
|
|
11918
|
+
});
|
|
11919
|
+
if (await this.runnerAlreadyWroteTerminal(
|
|
11920
|
+
stateManager,
|
|
11921
|
+
options.runId,
|
|
11922
|
+
resumeNodeId,
|
|
11923
|
+
postHandoff
|
|
11924
|
+
)) {
|
|
11925
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11926
|
+
finalizeCompletedAt: Date.now(),
|
|
11927
|
+
backgroundCompletedAt: Date.now()
|
|
11928
|
+
});
|
|
11929
|
+
return;
|
|
11930
|
+
}
|
|
11793
11931
|
await stateManager.finalize(options.runId, resumeNodeId, postHandoff);
|
|
11932
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11933
|
+
finalizeCompletedAt: Date.now()
|
|
11934
|
+
});
|
|
11794
11935
|
await this.maybeFireWebhook(stateManager, options.runId, resumeNodeId, postHandoff);
|
|
11936
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11937
|
+
backgroundCompletedAt: Date.now()
|
|
11938
|
+
});
|
|
11795
11939
|
} catch (err) {
|
|
11796
11940
|
if (signal.aborted) return;
|
|
11797
11941
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
11942
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11943
|
+
backgroundFailedAt: Date.now(),
|
|
11944
|
+
errorMessage: errorMsg
|
|
11945
|
+
});
|
|
11798
11946
|
const failResponse = {
|
|
11799
11947
|
runId: options.runId,
|
|
11800
11948
|
status: "failed",
|
|
@@ -11803,8 +11951,17 @@ ${inputJson}
|
|
|
11803
11951
|
errors: [{ code: "RESUME_FAILED", message: errorMsg }],
|
|
11804
11952
|
timestamp: Date.now()
|
|
11805
11953
|
};
|
|
11954
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11955
|
+
finalizeStartedAt: Date.now()
|
|
11956
|
+
});
|
|
11806
11957
|
await stateManager.finalize(options.runId, resumeNodeId, failResponse);
|
|
11958
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11959
|
+
finalizeCompletedAt: Date.now()
|
|
11960
|
+
});
|
|
11807
11961
|
await this.maybeFireWebhook(stateManager, options.runId, resumeNodeId, failResponse);
|
|
11962
|
+
await this.recordAsyncTiming(stateManager, options.runId, resumeNodeId, {
|
|
11963
|
+
backgroundCompletedAt: Date.now()
|
|
11964
|
+
});
|
|
11808
11965
|
}
|
|
11809
11966
|
});
|
|
11810
11967
|
return { runId: options.runId, nodeId, status: "running" };
|
|
@@ -11837,7 +11994,15 @@ ${inputJson}
|
|
|
11837
11994
|
timestamp: Date.now()
|
|
11838
11995
|
};
|
|
11839
11996
|
}
|
|
11840
|
-
if (state.response !== null)
|
|
11997
|
+
if (state.response !== null) {
|
|
11998
|
+
return state.asyncTiming === void 0 ? state.response : {
|
|
11999
|
+
...state.response,
|
|
12000
|
+
meta: {
|
|
12001
|
+
...state.response.meta,
|
|
12002
|
+
asyncTiming: state.asyncTiming
|
|
12003
|
+
}
|
|
12004
|
+
};
|
|
12005
|
+
}
|
|
11841
12006
|
return {
|
|
11842
12007
|
runId: state.runId,
|
|
11843
12008
|
status: state.status,
|
|
@@ -11847,7 +12012,8 @@ ${inputJson}
|
|
|
11847
12012
|
turns: state.progress.turns,
|
|
11848
12013
|
tokensUsed: state.progress.tokensUsed,
|
|
11849
12014
|
activity: state.progress.currentActivity,
|
|
11850
|
-
...state.progress.lastTool !== void 0 ? { lastTool: state.progress.lastTool } : {}
|
|
12015
|
+
...state.progress.lastTool !== void 0 ? { lastTool: state.progress.lastTool } : {},
|
|
12016
|
+
...state.asyncTiming !== void 0 ? { asyncTiming: state.asyncTiming } : {}
|
|
11851
12017
|
},
|
|
11852
12018
|
errors: [],
|
|
11853
12019
|
timestamp: state.lastHeartbeat
|
|
@@ -11856,6 +12022,11 @@ ${inputJson}
|
|
|
11856
12022
|
/**
|
|
11857
12023
|
* Poll until the run reaches a terminal state (done | failed | paused |
|
|
11858
12024
|
* cancelled) or the timeout expires. Returns the final EngineResponse.
|
|
12025
|
+
*
|
|
12026
|
+
* For async paused runs, wait until the background wrapper has completed
|
|
12027
|
+
* its post-finalize bookkeeping before returning. Otherwise a caller can
|
|
12028
|
+
* immediately resume while the previous wrapper is still writing timing
|
|
12029
|
+
* diagnostics, allowing a stale paused state to overwrite the resumed run.
|
|
11859
12030
|
*/
|
|
11860
12031
|
async waitFor(runId, opts = {}) {
|
|
11861
12032
|
const pollInterval = opts.pollIntervalMs ?? 1e3;
|
|
@@ -11863,9 +12034,10 @@ ${inputJson}
|
|
|
11863
12034
|
const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : Infinity;
|
|
11864
12035
|
for (; ; ) {
|
|
11865
12036
|
const resp = await this.getStatus(runId, opts.nodeId);
|
|
11866
|
-
if (resp.status === "done" || resp.status === "failed"
|
|
12037
|
+
if (resp.status === "done" || resp.status === "failed") {
|
|
11867
12038
|
return resp;
|
|
11868
12039
|
}
|
|
12040
|
+
if (resp.status === "paused" && this.pausedRunIsQuiesced(resp, opts)) return resp;
|
|
11869
12041
|
if (Date.now() >= deadline) {
|
|
11870
12042
|
return {
|
|
11871
12043
|
runId,
|
|
@@ -11881,6 +12053,12 @@ ${inputJson}
|
|
|
11881
12053
|
await new Promise((r) => setTimeout(r, pollInterval));
|
|
11882
12054
|
}
|
|
11883
12055
|
}
|
|
12056
|
+
pausedRunIsQuiesced(resp, opts) {
|
|
12057
|
+
if (resp.meta.pauseReason === "handoff_to_runner") return opts.waitForRunnerHandoff !== true;
|
|
12058
|
+
const timing = resp.meta.asyncTiming;
|
|
12059
|
+
if (timing === void 0) return true;
|
|
12060
|
+
return timing.backgroundCompletedAt !== void 0 || timing.backgroundFailedAt !== void 0;
|
|
12061
|
+
}
|
|
11884
12062
|
/**
|
|
11885
12063
|
* Cancel an async run. Aborts the background executor and marks the
|
|
11886
12064
|
* state as cancelled. Idempotent — safe to call on already-terminal runs.
|
|
@@ -11937,14 +12115,31 @@ ${inputJson}
|
|
|
11937
12115
|
if (original === void 0) {
|
|
11938
12116
|
throw new Error(`retryWebhook: delivery ${deliveryId} not found`);
|
|
11939
12117
|
}
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
12118
|
+
const retryStartedAt = Date.now();
|
|
12119
|
+
try {
|
|
12120
|
+
await this.dispatchWebhookWithRetries(
|
|
12121
|
+
stateManager,
|
|
12122
|
+
runId,
|
|
12123
|
+
targetNodeId,
|
|
12124
|
+
original.event,
|
|
12125
|
+
state.response,
|
|
12126
|
+
1
|
|
12127
|
+
);
|
|
12128
|
+
await this.recordManualWebhookRetry(stateManager, runId, targetNodeId, {
|
|
12129
|
+
deliveryId,
|
|
12130
|
+
startedAt: retryStartedAt,
|
|
12131
|
+
completedAt: Date.now()
|
|
12132
|
+
});
|
|
12133
|
+
} catch (err) {
|
|
12134
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
12135
|
+
await this.recordManualWebhookRetry(stateManager, runId, targetNodeId, {
|
|
12136
|
+
deliveryId,
|
|
12137
|
+
startedAt: retryStartedAt,
|
|
12138
|
+
completedAt: Date.now(),
|
|
12139
|
+
errorMessage
|
|
12140
|
+
});
|
|
12141
|
+
throw err;
|
|
12142
|
+
}
|
|
11948
12143
|
}
|
|
11949
12144
|
/**
|
|
11950
12145
|
* Scan all runs for stale heartbeats and mark them as failed. Clients
|
|
@@ -11981,6 +12176,12 @@ ${inputJson}
|
|
|
11981
12176
|
return orphaned;
|
|
11982
12177
|
}
|
|
11983
12178
|
// ---------- runner handoff (Plan 019) ----------
|
|
12179
|
+
async runnerAlreadyWroteTerminal(stateManager, runId, nodeId, response) {
|
|
12180
|
+
if (response.status !== "paused") return false;
|
|
12181
|
+
if (response.meta.pauseReason !== "handoff_to_runner") return false;
|
|
12182
|
+
const latest = await stateManager.read(runId, nodeId);
|
|
12183
|
+
return latest?.status === "done" || latest?.status === "failed" || latest?.status === "cancelled";
|
|
12184
|
+
}
|
|
11984
12185
|
/**
|
|
11985
12186
|
* When the response indicates the run paused for runner handoff, POST
|
|
11986
12187
|
* `{ runId }` to the configured runner URL. On success, return the
|
|
@@ -12046,7 +12247,22 @@ ${inputJson}
|
|
|
12046
12247
|
}
|
|
12047
12248
|
}
|
|
12048
12249
|
// ---------- webhook helpers ----------
|
|
12250
|
+
async recordAsyncTiming(stateManager, runId, nodeId, patch) {
|
|
12251
|
+
try {
|
|
12252
|
+
await stateManager.patchAsyncTiming(runId, nodeId, patch);
|
|
12253
|
+
} catch {
|
|
12254
|
+
}
|
|
12255
|
+
}
|
|
12256
|
+
async recordManualWebhookRetry(stateManager, runId, nodeId, row) {
|
|
12257
|
+
try {
|
|
12258
|
+
await stateManager.appendManualWebhookRetry(runId, nodeId, row);
|
|
12259
|
+
} catch {
|
|
12260
|
+
}
|
|
12261
|
+
}
|
|
12049
12262
|
async maybeFireWebhook(stateManager, runId, nodeId, response) {
|
|
12263
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12264
|
+
webhookCheckStartedAt: Date.now()
|
|
12265
|
+
});
|
|
12050
12266
|
const state = await stateManager.read(runId, nodeId);
|
|
12051
12267
|
if (state === null || state.webhook === void 0) return;
|
|
12052
12268
|
const event = response.status === "done" ? "done" : response.status === "paused" ? "paused" : "failed";
|
|
@@ -12057,12 +12273,18 @@ ${inputJson}
|
|
|
12057
12273
|
const state = await stateManager.read(runId, nodeId);
|
|
12058
12274
|
if (state === null || state.webhook === void 0) return;
|
|
12059
12275
|
const hook = state.webhook;
|
|
12276
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12277
|
+
webhookDispatchStartedAt: Date.now()
|
|
12278
|
+
});
|
|
12060
12279
|
let attempt = startAttempt;
|
|
12061
12280
|
while (attempt <= MAX_ATTEMPTS) {
|
|
12062
12281
|
const delay = RETRY_DELAYS_MS[attempt - 1] ?? 0;
|
|
12063
12282
|
if (delay > 0) {
|
|
12064
12283
|
await new Promise((r) => setTimeout(r, delay));
|
|
12065
12284
|
}
|
|
12285
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12286
|
+
webhookHttpStartedAt: Date.now()
|
|
12287
|
+
});
|
|
12066
12288
|
const result = await this.webhookDispatcher.deliver({
|
|
12067
12289
|
url: hook.url,
|
|
12068
12290
|
event,
|
|
@@ -12071,6 +12293,9 @@ ${inputJson}
|
|
|
12071
12293
|
...hook.headers !== void 0 ? { headers: hook.headers } : {},
|
|
12072
12294
|
attempt
|
|
12073
12295
|
});
|
|
12296
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12297
|
+
webhookHttpCompletedAt: Date.now()
|
|
12298
|
+
});
|
|
12074
12299
|
const latest = await stateManager.read(runId, nodeId);
|
|
12075
12300
|
if (latest !== null && latest.webhook !== void 0) {
|
|
12076
12301
|
const updated = {
|
|
@@ -12078,10 +12303,21 @@ ${inputJson}
|
|
|
12078
12303
|
deliveries: [...latest.webhook.deliveries, result.delivery]
|
|
12079
12304
|
};
|
|
12080
12305
|
await stateManager.update(runId, nodeId, { webhook: updated });
|
|
12306
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12307
|
+
webhookStatePersistedAt: Date.now()
|
|
12308
|
+
});
|
|
12309
|
+
}
|
|
12310
|
+
if (!result.shouldRetry) {
|
|
12311
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12312
|
+
webhookDispatchCompletedAt: Date.now()
|
|
12313
|
+
});
|
|
12314
|
+
return;
|
|
12081
12315
|
}
|
|
12082
|
-
if (!result.shouldRetry) return;
|
|
12083
12316
|
attempt += 1;
|
|
12084
12317
|
}
|
|
12318
|
+
await this.recordAsyncTiming(stateManager, runId, nodeId, {
|
|
12319
|
+
webhookDispatchCompletedAt: Date.now()
|
|
12320
|
+
});
|
|
12085
12321
|
}
|
|
12086
12322
|
/**
|
|
12087
12323
|
* Shut down engine-owned background resources — currently just the
|