@noetic-tools/core 0.0.1 → 0.1.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/README.md +31 -11
- package/dist/adapters/in-memory-fs-adapter.d.ts +1 -1
- package/dist/adapters/in-memory-fs-adapter.js +1 -1
- package/dist/adapters/in-memory-shell-adapter.d.ts +1 -1
- package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
- package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
- package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
- package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
- package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
- package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
- package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
- package/dist/adapters/in-memory-subprocess/types.js +2 -0
- package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
- package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
- package/dist/adapters/openrouter.d.ts +16 -7
- package/dist/adapters/openrouter.js +68 -7
- package/dist/builders/channel-builder.d.ts +1 -1
- package/dist/builders/control-flow-builders.d.ts +3 -3
- package/dist/builders/control-flow-builders.js +9 -4
- package/dist/builders/every.d.ts +3 -3
- package/dist/builders/every.js +5 -2
- package/dist/builders/layer-provides-builders.d.ts +1 -32
- package/dist/builders/layer-provides-builders.js +1 -33
- package/dist/builders/loop-builder.d.ts +2 -2
- package/dist/builders/loop-builder.js +5 -2
- package/dist/builders/memory-builder.d.ts +1 -1
- package/dist/builders/memory-builder.js +1 -1
- package/dist/builders/provide-builder.d.ts +2 -2
- package/dist/builders/provide-builder.js +5 -2
- package/dist/builders/spawn-builder.d.ts +22 -9
- package/dist/builders/spawn-builder.js +17 -3
- package/dist/builders/step-builders.d.ts +51 -30
- package/dist/builders/step-builders.js +31 -12
- package/dist/builders/tool-builder.d.ts +7 -3
- package/dist/builders/tool-builder.js +3 -1
- package/dist/builders/workflow-hydrator.d.ts +38 -0
- package/dist/builders/workflow-hydrator.js +336 -0
- package/dist/conditions/conditions.d.ts +4 -4
- package/dist/conditions/conditions.js +2 -2
- package/dist/errors/noetic-error.d.ts +1 -1
- package/dist/errors/noetic-error.js +2 -0
- package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
- package/dist/harness/agent-harness.js +576 -0
- package/dist/harness/deps/adapters.d.ts +4 -0
- package/dist/harness/deps/adapters.js +4 -0
- package/dist/harness/deps/interpreter.d.ts +5 -0
- package/dist/harness/deps/interpreter.js +4 -0
- package/dist/harness/deps/memory.d.ts +5 -0
- package/dist/harness/deps/memory.js +4 -0
- package/dist/harness/deps/runtime.d.ts +10 -0
- package/dist/harness/deps/runtime.js +7 -0
- package/dist/harness/deps/types.d.ts +17 -0
- package/dist/harness/deps/types.js +1 -0
- package/dist/harness/model-call.d.ts +50 -0
- package/dist/harness/model-call.js +694 -0
- package/dist/harness/model-schema.d.ts +19 -0
- package/dist/harness/model-schema.js +46 -0
- package/dist/harness/step-bootstrap.d.ts +25 -0
- package/dist/harness/step-bootstrap.js +170 -0
- package/dist/index.d.ts +114 -76
- package/dist/index.js +74 -50
- package/dist/internal-test.d.ts +4 -4
- package/dist/internal-test.js +4 -4
- package/dist/interpreter/action-deps.d.ts +12 -0
- package/dist/interpreter/action-deps.js +10 -0
- package/dist/interpreter/action-types.d.ts +8 -0
- package/dist/interpreter/action-types.js +1 -0
- package/dist/interpreter/collect-tools.d.ts +2 -2
- package/dist/interpreter/collect-tools.js +6 -2
- package/dist/interpreter/execute-action.d.ts +22 -0
- package/dist/interpreter/execute-action.js +527 -0
- package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +8 -2
- package/dist/interpreter/execute-control.js +530 -0
- package/dist/interpreter/execute.d.ts +16 -3
- package/dist/interpreter/execute.js +154 -51
- package/dist/interpreter/message-helpers.d.ts +1 -9
- package/dist/interpreter/message-helpers.js +1 -42
- package/dist/interpreter/step-dispatcher.d.ts +61 -0
- package/dist/interpreter/step-dispatcher.js +67 -0
- package/dist/interpreter/typeguards.d.ts +8 -15
- package/dist/interpreter/typeguards.js +4 -21
- package/dist/memory/budget.d.ts +2 -2
- package/dist/memory/budget.js +1 -1
- package/dist/{runtime → memory}/exec-context-factory.d.ts +2 -2
- package/dist/{runtime → memory}/exec-context-factory.js +1 -1
- package/dist/memory/flow-schema.d.ts +52 -0
- package/dist/memory/flow-schema.js +102 -0
- package/dist/memory/function-call-utils.d.ts +1 -1
- package/dist/memory/function-call-utils.js +4 -2
- package/dist/memory/layer-api.d.ts +4 -4
- package/dist/memory/layer-api.js +2 -2
- package/dist/memory/layer-lifecycle.d.ts +6 -6
- package/dist/memory/layer-lifecycle.js +6 -6
- package/dist/memory/layer-provides.d.ts +32 -0
- package/dist/memory/layer-provides.js +33 -0
- package/dist/memory/layer-usage.d.ts +4 -3
- package/dist/memory/layer-usage.js +9 -3
- package/dist/memory/layers/durable-task-state.d.ts +8 -8
- package/dist/memory/layers/durable-task-state.js +2 -2
- package/dist/memory/layers/file-reference.d.ts +1 -1
- package/dist/memory/layers/file-reference.js +303 -247
- package/dist/memory/layers/history-window.d.ts +1 -1
- package/dist/memory/layers/history-window.js +4 -4
- package/dist/memory/layers/observational-memory.d.ts +5 -5
- package/dist/memory/layers/observational-memory.js +2 -3
- package/dist/memory/layers/plan.d.ts +2 -2
- package/dist/memory/layers/plan.js +15 -10
- package/dist/memory/layers/static-content.d.ts +2 -2
- package/dist/memory/layers/static-content.js +1 -1
- package/dist/memory/layers/steering.d.ts +7 -7
- package/dist/memory/layers/steering.js +5 -5
- package/dist/memory/layers/tool-memory-layer.d.ts +2 -2
- package/dist/memory/layers/tool-memory-layer.js +1 -1
- package/dist/memory/layers/working-memory.d.ts +7 -7
- package/dist/memory/layers/working-memory.js +4 -4
- package/dist/memory/projector.d.ts +2 -2
- package/dist/memory/scope.d.ts +1 -1
- package/dist/memory/strip-unresolved.d.ts +1 -1
- package/dist/observability/span-impl.d.ts +1 -1
- package/dist/observability/trace-exporter.d.ts +2 -2
- package/dist/observability/trace-exporter.js +1 -1
- package/dist/patterns/dynamic-workflow.d.ts +55 -0
- package/dist/patterns/dynamic-workflow.js +194 -0
- package/dist/patterns/flow.d.ts +2 -52
- package/dist/patterns/flow.js +1 -103
- package/dist/patterns/interview.d.ts +2 -2
- package/dist/patterns/interview.js +5 -5
- package/dist/patterns/plans.d.ts +6 -6
- package/dist/patterns/plans.js +4 -4
- package/dist/patterns/ralph-wiggum.d.ts +4 -4
- package/dist/patterns/ralph-wiggum.js +5 -5
- package/dist/patterns/react.d.ts +3 -3
- package/dist/patterns/react.js +5 -5
- package/dist/portable/adapters.d.ts +6 -0
- package/dist/portable/adapters.js +6 -0
- package/dist/portable/ask-user.d.ts +4 -0
- package/dist/portable/ask-user.js +2 -0
- package/dist/portable/builders.d.ts +12 -0
- package/dist/portable/builders.js +12 -0
- package/dist/portable/memory-layers.d.ts +12 -0
- package/dist/portable/memory-layers.js +12 -0
- package/dist/portable/runtime.d.ts +4 -0
- package/dist/portable/runtime.js +4 -0
- package/dist/portable/until.d.ts +4 -0
- package/dist/portable/until.js +4 -0
- package/dist/portable.d.ts +8 -26
- package/dist/portable.js +8 -34
- package/dist/runtime/broadcaster-utils.d.ts +7 -3
- package/dist/runtime/broadcaster-utils.js +11 -6
- package/dist/runtime/channel-store.d.ts +1 -1
- package/dist/runtime/channel-store.js +2 -2
- package/dist/runtime/context-deps.d.ts +4 -0
- package/dist/runtime/context-deps.js +2 -0
- package/dist/runtime/context-impl.d.ts +42 -12
- package/dist/runtime/context-impl.js +52 -5
- package/dist/runtime/context-type-deps.d.ts +14 -0
- package/dist/runtime/context-type-deps.js +1 -0
- package/dist/runtime/cwd-helpers.d.ts +1 -1
- package/dist/runtime/detached-handle.d.ts +52 -4
- package/dist/runtime/detached-handle.js +218 -14
- package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
- package/dist/runtime/durable/checkpoint-store.js +101 -0
- package/dist/runtime/durable/detached-signal.d.ts +37 -0
- package/dist/runtime/durable/detached-signal.js +52 -0
- package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
- package/dist/runtime/durable/harness-checkpoints.js +104 -0
- package/dist/runtime/durable/index.d.ts +10 -0
- package/dist/runtime/durable/index.js +5 -0
- package/dist/runtime/durable/runnable-loop.d.ts +100 -0
- package/dist/runtime/durable/runnable-loop.js +90 -0
- package/dist/runtime/durable/session-seed.d.ts +28 -0
- package/dist/runtime/durable/session-seed.js +27 -0
- package/dist/runtime/durable/stall-nudge.d.ts +82 -0
- package/dist/runtime/durable/stall-nudge.js +87 -0
- package/dist/runtime/event-broadcaster.d.ts +1 -1
- package/dist/runtime/in-memory-storage.d.ts +1 -1
- package/dist/runtime/in-memory-storage.js +1 -1
- package/dist/runtime/item-log-impl.d.ts +3 -3
- package/dist/runtime/item-log-impl.js +1 -1
- package/dist/runtime/message-queue.d.ts +2 -2
- package/dist/runtime/session-runner.d.ts +7 -7
- package/dist/runtime/session-runner.js +3 -3
- package/dist/runtime/session-streams.d.ts +3 -3
- package/dist/runtime/session-streams.js +1 -1
- package/dist/runtime/step-registry.d.ts +42 -0
- package/dist/runtime/step-registry.js +77 -0
- package/dist/runtime/tool-memory.d.ts +3 -3
- package/dist/schemas/item.d.ts +1 -1
- package/dist/schemas/workflow.d.ts +139 -0
- package/dist/schemas/workflow.js +232 -0
- package/dist/types/ask-user-types.d.ts +57 -0
- package/dist/types/ask-user-types.js +74 -0
- package/dist/types/checkpoint.d.ts +98 -0
- package/dist/types/checkpoint.js +81 -0
- package/dist/types/common.d.ts +1 -50
- package/dist/types/context-parts/item-log.d.ts +6 -0
- package/dist/types/context-parts/item-log.js +1 -0
- package/dist/types/context-parts/layer-usage.d.ts +18 -0
- package/dist/types/context-parts/layer-usage.js +1 -0
- package/dist/types/context.d.ts +140 -31
- package/dist/types/error.d.ts +15 -0
- package/dist/types/harness-result.d.ts +2 -2
- package/dist/types/items.d.ts +18 -2
- package/dist/types/memory-context.d.ts +39 -0
- package/dist/types/memory-context.js +1 -0
- package/dist/types/memory.d.ts +38 -68
- package/dist/types/mutable-context.d.ts +3 -3
- package/dist/types/runtime.d.ts +14 -13
- package/dist/types/steering.d.ts +2 -2
- package/dist/types/step-registrar.d.ts +46 -0
- package/dist/types/step-registrar.js +56 -0
- package/dist/types/step.d.ts +34 -9
- package/dist/types/subprocess-adapter.d.ts +119 -3
- package/dist/types/subprocess-adapter.js +2 -0
- package/dist/types/tool-context.d.ts +6 -6
- package/dist/types/tool.d.ts +64 -0
- package/dist/types/tool.js +1 -0
- package/dist/unstable.d.ts +9 -9
- package/dist/unstable.js +7 -7
- package/dist/until/combinators.d.ts +1 -1
- package/dist/until/never.d.ts +1 -1
- package/dist/until/predicates.d.ts +13 -4
- package/dist/until/predicates.js +16 -2
- package/dist/util/message-helpers.d.ts +17 -0
- package/dist/util/message-helpers.js +64 -0
- package/package.json +6 -4
- package/dist/adapters/local-fs-adapter.d.ts +0 -3
- package/dist/adapters/local-fs-adapter.js +0 -43
- package/dist/adapters/local-shell-adapter.d.ts +0 -51
- package/dist/adapters/local-shell-adapter.js +0 -167
- package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
- package/dist/adapters/local-subprocess-adapter.js +0 -187
- package/dist/conditions/index.d.ts +0 -3
- package/dist/conditions/index.js +0 -2
- package/dist/interpreter/execute-branch.d.ts +0 -3
- package/dist/interpreter/execute-branch.js +0 -10
- package/dist/interpreter/execute-every.js +0 -143
- package/dist/interpreter/execute-fork.d.ts +0 -3
- package/dist/interpreter/execute-fork.js +0 -215
- package/dist/interpreter/execute-llm.d.ts +0 -4
- package/dist/interpreter/execute-llm.js +0 -217
- package/dist/interpreter/execute-loop.d.ts +0 -3
- package/dist/interpreter/execute-loop.js +0 -171
- package/dist/interpreter/execute-provide.d.ts +0 -10
- package/dist/interpreter/execute-provide.js +0 -64
- package/dist/interpreter/execute-run.d.ts +0 -3
- package/dist/interpreter/execute-run.js +0 -39
- package/dist/interpreter/execute-spawn.d.ts +0 -11
- package/dist/interpreter/execute-spawn.js +0 -113
- package/dist/interpreter/execute-tool.d.ts +0 -5
- package/dist/interpreter/execute-tool.js +0 -81
- package/dist/runtime/agent-harness.js +0 -1170
- /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
- /package/dist/{interpreter → util}/framework-cast.js +0 -0
|
@@ -1,14 +1,62 @@
|
|
|
1
|
-
import type { DetachedHandle } from '../types/detached';
|
|
2
|
-
import { DetachedStatus } from '../types/detached';
|
|
1
|
+
import type { DetachedHandle } from '../types/detached.js';
|
|
2
|
+
import { DetachedStatus } from '../types/detached.js';
|
|
3
|
+
import type { SubprocessAdapter, SubprocessHandle } from '../types/subprocess-adapter.js';
|
|
4
|
+
interface DetachedHandleOpts {
|
|
5
|
+
/** Id surfaced to callers — usually the execution id (childCtx.id in
|
|
6
|
+
* `detachedSpawn`) so consumers can correlate it with their own state. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Step id — used when the adapter surfaces a generic failure and we need
|
|
9
|
+
* to produce a `NoeticError` with a meaningful `stepId`. */
|
|
10
|
+
stepId: string;
|
|
11
|
+
/** Adapter that produced the handle. */
|
|
12
|
+
adapter: SubprocessAdapter;
|
|
13
|
+
/**
|
|
14
|
+
* Promise that resolves to the adapter-assigned handle. `detachedSpawn`
|
|
15
|
+
* passes the in-flight `adapter.spawn(request)` promise here so `.await()`
|
|
16
|
+
* is reachable even before the spawn itself has settled.
|
|
17
|
+
*/
|
|
18
|
+
spawnPromise: Promise<SubprocessHandle>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
* Thin wrapper over a `SubprocessHandle` that implements the `DetachedHandle`
|
|
23
|
+
* contract. Polls the adapter until the handle reaches a terminal status,
|
|
24
|
+
* then reads the result (or error) off `handle.metadata`.
|
|
25
|
+
*
|
|
26
|
+
* The polling cadence starts at 10ms and ramps to 100ms after the first
|
|
27
|
+
* second — responsive for short-lived in-memory steps without burning cycles
|
|
28
|
+
* on long-running ones.
|
|
29
|
+
*/
|
|
3
30
|
export declare class DetachedHandleImpl<O> implements DetachedHandle<O> {
|
|
4
31
|
readonly id: string;
|
|
32
|
+
private readonly _stepId;
|
|
33
|
+
private readonly _adapter;
|
|
34
|
+
private readonly _spawnPromise;
|
|
35
|
+
private readonly _startTime;
|
|
5
36
|
private _status;
|
|
6
37
|
private _result;
|
|
7
38
|
private _error;
|
|
8
|
-
private
|
|
9
|
-
constructor(
|
|
39
|
+
private _settlementPromise;
|
|
40
|
+
constructor(opts: DetachedHandleOpts);
|
|
10
41
|
get status(): DetachedStatus;
|
|
11
42
|
get result(): O | undefined;
|
|
12
43
|
get error(): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Wait for the underlying handle to settle, up to an optional timeout.
|
|
46
|
+
* On settlement, the cached status/result/error are updated before
|
|
47
|
+
* returning — so a caller that inspects `status` after `await()` resolves
|
|
48
|
+
* observes the final value.
|
|
49
|
+
*/
|
|
13
50
|
await(timeout?: number): Promise<O>;
|
|
51
|
+
/**
|
|
52
|
+
* Start (or return the cached) polling promise that resolves on the first
|
|
53
|
+
* terminal status the adapter reports for this handle.
|
|
54
|
+
*
|
|
55
|
+
* A single settlement promise is shared across all `.await()` callers so
|
|
56
|
+
* repeated calls don't spawn parallel polling loops.
|
|
57
|
+
*/
|
|
58
|
+
private getOrStartSettlement;
|
|
59
|
+
private pollUntilSettled;
|
|
60
|
+
private finalise;
|
|
14
61
|
}
|
|
62
|
+
export {};
|
|
@@ -1,19 +1,125 @@
|
|
|
1
|
-
import { NoeticErrorImpl } from '../errors/noetic-error';
|
|
2
|
-
import { DetachedStatus } from '../types/detached';
|
|
1
|
+
import { NoeticErrorImpl } from '../errors/noetic-error.js';
|
|
2
|
+
import { DetachedStatus } from '../types/detached.js';
|
|
3
|
+
import { frameworkCast } from '../util/framework-cast.js';
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region Helpers
|
|
6
|
+
const INITIAL_POLL_MS = 10;
|
|
7
|
+
const MAX_POLL_MS = 100;
|
|
8
|
+
const POLL_RAMP_AFTER_MS = 1_000;
|
|
9
|
+
/** Upper bound on microtask yields before falling back to timer-based
|
|
10
|
+
* polling. Chosen empirically: in-memory step bodies settle within a
|
|
11
|
+
* handful of microtasks, and 64 gives plenty of headroom for chained
|
|
12
|
+
* awaits inside complex step trees without busy-spinning. */
|
|
13
|
+
const MAX_MICROTASK_YIELDS = 64;
|
|
14
|
+
/** Grace period before treating persistent null `adapter.get(handleId)`
|
|
15
|
+
* results as an eviction. Tolerates transient nulls from adapters that
|
|
16
|
+
* register the handle asynchronously relative to the spawn promise
|
|
17
|
+
* settling, but bounds the worst case so a corrupt adapter doesn't
|
|
18
|
+
* produce an infinite poll. */
|
|
19
|
+
const HANDLE_EVICTED_GRACE_MS = 500;
|
|
20
|
+
/**
|
|
21
|
+
* Yield control to the timer queue so the adapter has a chance to
|
|
22
|
+
* transition the handle before we issue the next `adapter.get()` call.
|
|
23
|
+
* Using a timer (even 0ms) ensures we don't spin-lock when the adapter
|
|
24
|
+
* schedules its completion on a later microtask.
|
|
25
|
+
*/
|
|
26
|
+
function sleep(ms) {
|
|
27
|
+
if (ms <= 0) {
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
|
+
}
|
|
32
|
+
function isTerminalStatus(status) {
|
|
33
|
+
return (status === 'completed' || status === 'failed' || status === 'stopped' || status === 'stale');
|
|
34
|
+
}
|
|
35
|
+
function isRecord(value) {
|
|
36
|
+
return typeof value === 'object' && value !== null;
|
|
37
|
+
}
|
|
38
|
+
function isNoeticErrorPayload(value) {
|
|
39
|
+
return isRecord(value) && typeof value.kind === 'string';
|
|
40
|
+
}
|
|
41
|
+
/** Rehydrate a thrown error from the handle metadata. Preserves the
|
|
42
|
+
* structured NoeticError shape when present so type-guards like
|
|
43
|
+
* `isNoeticError` keep working on the consumer side. */
|
|
44
|
+
function rehydrateError(payload, stepId) {
|
|
45
|
+
if (!payload) {
|
|
46
|
+
return new NoeticErrorImpl({
|
|
47
|
+
kind: 'step_failed',
|
|
48
|
+
stepId,
|
|
49
|
+
cause: new Error('Detached spawn failed without an error payload'),
|
|
50
|
+
retriesExhausted: false,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (isNoeticErrorPayload(payload.noeticError)) {
|
|
54
|
+
return new NoeticErrorImpl(payload.noeticError);
|
|
55
|
+
}
|
|
56
|
+
const err = new Error(payload.message);
|
|
57
|
+
if (payload.name) {
|
|
58
|
+
err.name = payload.name;
|
|
59
|
+
}
|
|
60
|
+
if (payload.stack) {
|
|
61
|
+
err.stack = payload.stack;
|
|
62
|
+
}
|
|
63
|
+
return err;
|
|
64
|
+
}
|
|
65
|
+
function extractResult(metadata) {
|
|
66
|
+
if (!metadata || metadata.result === undefined) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return frameworkCast(metadata.result);
|
|
70
|
+
}
|
|
71
|
+
function extractError(metadata) {
|
|
72
|
+
if (!metadata || metadata.error === undefined) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const raw = metadata.error;
|
|
76
|
+
if (!isRecord(raw)) {
|
|
77
|
+
return {
|
|
78
|
+
message: String(raw),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const message = typeof raw.message === 'string' ? raw.message : 'unknown error';
|
|
82
|
+
const name = typeof raw.name === 'string' ? raw.name : undefined;
|
|
83
|
+
const stack = typeof raw.stack === 'string' ? raw.stack : undefined;
|
|
84
|
+
return {
|
|
85
|
+
message,
|
|
86
|
+
name,
|
|
87
|
+
stack,
|
|
88
|
+
noeticError: raw.noeticError,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
3
92
|
//#region DetachedHandleImpl
|
|
93
|
+
/**
|
|
94
|
+
* @public
|
|
95
|
+
* Thin wrapper over a `SubprocessHandle` that implements the `DetachedHandle`
|
|
96
|
+
* contract. Polls the adapter until the handle reaches a terminal status,
|
|
97
|
+
* then reads the result (or error) off `handle.metadata`.
|
|
98
|
+
*
|
|
99
|
+
* The polling cadence starts at 10ms and ramps to 100ms after the first
|
|
100
|
+
* second — responsive for short-lived in-memory steps without burning cycles
|
|
101
|
+
* on long-running ones.
|
|
102
|
+
*/
|
|
4
103
|
export class DetachedHandleImpl {
|
|
5
104
|
id;
|
|
105
|
+
_stepId;
|
|
106
|
+
_adapter;
|
|
107
|
+
_spawnPromise;
|
|
108
|
+
_startTime;
|
|
6
109
|
_status = DetachedStatus.Running;
|
|
7
110
|
_result;
|
|
8
111
|
_error;
|
|
9
|
-
|
|
10
|
-
constructor(
|
|
11
|
-
this.id = id;
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
112
|
+
_settlementPromise;
|
|
113
|
+
constructor(opts) {
|
|
114
|
+
this.id = opts.id;
|
|
115
|
+
this._stepId = opts.stepId;
|
|
116
|
+
this._adapter = opts.adapter;
|
|
117
|
+
this._spawnPromise = opts.spawnPromise;
|
|
118
|
+
this._startTime = Date.now();
|
|
119
|
+
// Observe spawn failure eagerly so `.error` is populated even if the
|
|
120
|
+
// caller never invokes `.await()`. We intentionally swallow the
|
|
121
|
+
// rejection here — `.await()` surfaces it to the caller.
|
|
122
|
+
this._spawnPromise.catch((err) => {
|
|
17
123
|
this._status = DetachedStatus.Failed;
|
|
18
124
|
this._error = err instanceof Error ? err.message : String(err);
|
|
19
125
|
});
|
|
@@ -27,16 +133,23 @@ export class DetachedHandleImpl {
|
|
|
27
133
|
get error() {
|
|
28
134
|
return this._error;
|
|
29
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Wait for the underlying handle to settle, up to an optional timeout.
|
|
138
|
+
* On settlement, the cached status/result/error are updated before
|
|
139
|
+
* returning — so a caller that inspects `status` after `await()` resolves
|
|
140
|
+
* observes the final value.
|
|
141
|
+
*/
|
|
30
142
|
async await(timeout) {
|
|
143
|
+
const settlement = this.getOrStartSettlement();
|
|
31
144
|
if (timeout === undefined || timeout <= 0) {
|
|
32
|
-
return
|
|
145
|
+
return settlement;
|
|
33
146
|
}
|
|
34
147
|
let timerId;
|
|
35
148
|
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
36
149
|
timerId = setTimeout(() => {
|
|
37
150
|
reject(new NoeticErrorImpl({
|
|
38
151
|
kind: 'step_failed',
|
|
39
|
-
stepId: this.
|
|
152
|
+
stepId: this._stepId,
|
|
40
153
|
cause: new Error(`Detached spawn timed out after ${timeout}ms`),
|
|
41
154
|
retriesExhausted: false,
|
|
42
155
|
}));
|
|
@@ -44,13 +157,104 @@ export class DetachedHandleImpl {
|
|
|
44
157
|
});
|
|
45
158
|
try {
|
|
46
159
|
return await Promise.race([
|
|
47
|
-
|
|
160
|
+
settlement,
|
|
48
161
|
timeoutPromise,
|
|
49
162
|
]);
|
|
50
163
|
}
|
|
51
164
|
finally {
|
|
52
|
-
|
|
165
|
+
if (timerId !== undefined) {
|
|
166
|
+
clearTimeout(timerId);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Start (or return the cached) polling promise that resolves on the first
|
|
172
|
+
* terminal status the adapter reports for this handle.
|
|
173
|
+
*
|
|
174
|
+
* A single settlement promise is shared across all `.await()` callers so
|
|
175
|
+
* repeated calls don't spawn parallel polling loops.
|
|
176
|
+
*/
|
|
177
|
+
getOrStartSettlement() {
|
|
178
|
+
if (this._settlementPromise) {
|
|
179
|
+
return this._settlementPromise;
|
|
180
|
+
}
|
|
181
|
+
this._settlementPromise = this.pollUntilSettled();
|
|
182
|
+
return this._settlementPromise;
|
|
183
|
+
}
|
|
184
|
+
async pollUntilSettled() {
|
|
185
|
+
// Wait for the spawn itself to succeed before we start polling —
|
|
186
|
+
// otherwise we'd be polling with an undefined handle id.
|
|
187
|
+
const initialHandle = await this._spawnPromise;
|
|
188
|
+
if (isTerminalStatus(initialHandle.status)) {
|
|
189
|
+
return this.finalise(initialHandle);
|
|
190
|
+
}
|
|
191
|
+
// Track when the adapter persistently returns null for this handle.
|
|
192
|
+
// Transient nulls are tolerated for `HANDLE_EVICTED_GRACE_MS`; after
|
|
193
|
+
// that we raise `handle_evicted` instead of looping forever. A
|
|
194
|
+
// non-null observation resets the grace window.
|
|
195
|
+
let firstNullAt = null;
|
|
196
|
+
const observeNull = () => {
|
|
197
|
+
if (firstNullAt === null) {
|
|
198
|
+
firstNullAt = Date.now();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (Date.now() - firstNullAt >= HANDLE_EVICTED_GRACE_MS) {
|
|
202
|
+
throw new NoeticErrorImpl({
|
|
203
|
+
kind: 'handle_evicted',
|
|
204
|
+
handleId: initialHandle.id,
|
|
205
|
+
stepId: this._stepId,
|
|
206
|
+
gracePeriodMs: HANDLE_EVICTED_GRACE_MS,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const observeHandle = (h) => {
|
|
211
|
+
if (h === null) {
|
|
212
|
+
observeNull();
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
firstNullAt = null;
|
|
216
|
+
return h;
|
|
217
|
+
};
|
|
218
|
+
// Fast path: yield through the microtask queue first. The in-memory
|
|
219
|
+
// adapter schedules completion on a microtask, so a handful of yields
|
|
220
|
+
// typically resolves the handle in sub-millisecond time. This keeps the
|
|
221
|
+
// interpreter's "route through adapter" overhead negligible.
|
|
222
|
+
for (let i = 0; i < MAX_MICROTASK_YIELDS; i++) {
|
|
223
|
+
const handle = observeHandle(await this._adapter.get(initialHandle.id));
|
|
224
|
+
if (handle && isTerminalStatus(handle.status)) {
|
|
225
|
+
return this.finalise(handle);
|
|
226
|
+
}
|
|
227
|
+
await Promise.resolve();
|
|
228
|
+
}
|
|
229
|
+
// Slow path: fall back to timer-based polling for genuinely long-running
|
|
230
|
+
// handles (out-of-process adapters, async work that doesn't resolve on
|
|
231
|
+
// the microtask queue).
|
|
232
|
+
let delay = INITIAL_POLL_MS;
|
|
233
|
+
for (;;) {
|
|
234
|
+
const handle = observeHandle(await this._adapter.get(initialHandle.id));
|
|
235
|
+
if (handle && isTerminalStatus(handle.status)) {
|
|
236
|
+
return this.finalise(handle);
|
|
237
|
+
}
|
|
238
|
+
await sleep(delay);
|
|
239
|
+
if (Date.now() - this._startTime >= POLL_RAMP_AFTER_MS) {
|
|
240
|
+
delay = MAX_POLL_MS;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
finalise(handle) {
|
|
245
|
+
const metadata = handle.metadata;
|
|
246
|
+
if (handle.status === 'completed') {
|
|
247
|
+
const value = extractResult(metadata);
|
|
248
|
+
this._status = DetachedStatus.Completed;
|
|
249
|
+
this._result = value;
|
|
250
|
+
return frameworkCast(value);
|
|
53
251
|
}
|
|
252
|
+
// Failed / stopped / stale — all map to Failed from the caller's view.
|
|
253
|
+
const errorPayload = extractError(metadata);
|
|
254
|
+
const err = rehydrateError(errorPayload, this._stepId);
|
|
255
|
+
this._status = DetachedStatus.Failed;
|
|
256
|
+
this._error = err.message;
|
|
257
|
+
throw err;
|
|
54
258
|
}
|
|
55
259
|
}
|
|
56
260
|
//#endregion
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CheckpointSnapshot } from '../../types/checkpoint.js';
|
|
2
|
+
import type { StorageAdapter } from '../../types/memory.js';
|
|
3
|
+
export declare const CheckpointKeys: {
|
|
4
|
+
readonly ExecPrefix: "execution:";
|
|
5
|
+
readonly SnapshotSuffix: ":snapshot";
|
|
6
|
+
readonly FrontierSuffix: ":frontier";
|
|
7
|
+
readonly LayersSuffix: ":layers:";
|
|
8
|
+
readonly CwdSuffix: ":cwd";
|
|
9
|
+
readonly AskUserSuffix: ":askUser";
|
|
10
|
+
readonly ItemLogSuffix: ":itemLog";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
* Typed wrapper around a `StorageAdapter` that owns the key layout for
|
|
15
|
+
* checkpoint snapshots and validates every read with the canonical Zod
|
|
16
|
+
* schema.
|
|
17
|
+
*
|
|
18
|
+
* Corruption or schema-version drift is surfaced as `NoeticConfigError`
|
|
19
|
+
* (`code === 'CHECKPOINT_SCHEMA_MISMATCH'`) so callers can discard the
|
|
20
|
+
* offending snapshot without crashing the host.
|
|
21
|
+
*/
|
|
22
|
+
export interface CheckpointStore {
|
|
23
|
+
/** Persist a snapshot. Later calls for the same `executionId` overwrite. */
|
|
24
|
+
save(snapshot: CheckpointSnapshot): Promise<void>;
|
|
25
|
+
/** Load the snapshot for an execution, or `null` if none is recorded. */
|
|
26
|
+
load(executionId: string): Promise<CheckpointSnapshot | null>;
|
|
27
|
+
/** List every `executionId` that has a persisted snapshot. */
|
|
28
|
+
list(): Promise<ReadonlyArray<{
|
|
29
|
+
executionId: string;
|
|
30
|
+
}>>;
|
|
31
|
+
/** Remove the snapshot for `executionId`. No-op when the key is absent. */
|
|
32
|
+
clear(executionId: string): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
/** @public Options for `createCheckpointStore`. */
|
|
35
|
+
export interface CreateCheckpointStoreOptions {
|
|
36
|
+
storage: StorageAdapter;
|
|
37
|
+
}
|
|
38
|
+
export declare function createCheckpointStore(options: CreateCheckpointStoreOptions): CheckpointStore;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { NoeticConfigError } from '../../errors/noetic-config-error.js';
|
|
2
|
+
import { CheckpointSnapshotSchema } from '../../types/checkpoint.js';
|
|
3
|
+
//#region Keys
|
|
4
|
+
/**
|
|
5
|
+
* Typed key constants for the checkpoint namespace. Every execution snapshot
|
|
6
|
+
* lives under a single key so `save`/`load` are cheap single `get`/`set`
|
|
7
|
+
* calls, while a `list` prefix enumeration exposes every known execution id
|
|
8
|
+
* to the restart/reattach path. Auxiliary suffixes exist so future sharded
|
|
9
|
+
* storage schemes can still co-locate snapshot fragments under a shared
|
|
10
|
+
* execution prefix without bumping schemaVersion.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
const EXEC_KEY_PREFIX = 'execution:';
|
|
15
|
+
const SNAPSHOT_SUFFIX = ':snapshot';
|
|
16
|
+
const FRONTIER_SUFFIX = ':frontier';
|
|
17
|
+
const LAYERS_SUFFIX = ':layers:';
|
|
18
|
+
const CWD_SUFFIX = ':cwd';
|
|
19
|
+
const ASK_USER_SUFFIX = ':askUser';
|
|
20
|
+
const ITEM_LOG_SUFFIX = ':itemLog';
|
|
21
|
+
export const CheckpointKeys = {
|
|
22
|
+
ExecPrefix: EXEC_KEY_PREFIX,
|
|
23
|
+
SnapshotSuffix: SNAPSHOT_SUFFIX,
|
|
24
|
+
FrontierSuffix: FRONTIER_SUFFIX,
|
|
25
|
+
LayersSuffix: LAYERS_SUFFIX,
|
|
26
|
+
CwdSuffix: CWD_SUFFIX,
|
|
27
|
+
AskUserSuffix: ASK_USER_SUFFIX,
|
|
28
|
+
ItemLogSuffix: ITEM_LOG_SUFFIX,
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region Helpers
|
|
32
|
+
function snapshotKey(executionId) {
|
|
33
|
+
return `${EXEC_KEY_PREFIX}${executionId}${SNAPSHOT_SUFFIX}`;
|
|
34
|
+
}
|
|
35
|
+
function executionIdFromSnapshotKey(key) {
|
|
36
|
+
if (!key.startsWith(EXEC_KEY_PREFIX) || !key.endsWith(SNAPSHOT_SUFFIX)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const start = EXEC_KEY_PREFIX.length;
|
|
40
|
+
const end = key.length - SNAPSHOT_SUFFIX.length;
|
|
41
|
+
if (end <= start) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return key.slice(start, end);
|
|
45
|
+
}
|
|
46
|
+
function isJsonRecord(value) {
|
|
47
|
+
return typeof value === 'object' && value !== null;
|
|
48
|
+
}
|
|
49
|
+
function parseSnapshot(raw, executionId) {
|
|
50
|
+
const parsed = CheckpointSnapshotSchema.safeParse(raw);
|
|
51
|
+
if (parsed.success) {
|
|
52
|
+
return parsed.data;
|
|
53
|
+
}
|
|
54
|
+
const observedVersion = isJsonRecord(raw) && 'schemaVersion' in raw ? raw.schemaVersion : 'unknown';
|
|
55
|
+
throw new NoeticConfigError({
|
|
56
|
+
code: 'CHECKPOINT_SCHEMA_MISMATCH',
|
|
57
|
+
message: `Checkpoint snapshot for execution "${executionId}" failed schema validation (observed schemaVersion=${String(observedVersion)}).`,
|
|
58
|
+
hint: 'The snapshot was produced by a different runtime version. Clear the snapshot via CheckpointStore.clear() and restart the execution.',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region Factory
|
|
63
|
+
export function createCheckpointStore(options) {
|
|
64
|
+
const { storage } = options;
|
|
65
|
+
async function save(snapshot) {
|
|
66
|
+
// Persisted shape is the validated snapshot. The adapter is free to
|
|
67
|
+
// JSON-encode it internally; `InMemoryStorage` stores by reference which
|
|
68
|
+
// is fine because `CheckpointSnapshotSchema.parse` is deep-immutable.
|
|
69
|
+
await storage.set(snapshotKey(snapshot.executionId), snapshot);
|
|
70
|
+
}
|
|
71
|
+
async function load(executionId) {
|
|
72
|
+
const raw = await storage.get(snapshotKey(executionId));
|
|
73
|
+
if (raw === null) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return parseSnapshot(raw, executionId);
|
|
77
|
+
}
|
|
78
|
+
async function list() {
|
|
79
|
+
const keys = await storage.list(EXEC_KEY_PREFIX);
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
const id = executionIdFromSnapshotKey(key);
|
|
83
|
+
if (id !== null) {
|
|
84
|
+
out.push({
|
|
85
|
+
executionId: id,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
async function clear(executionId) {
|
|
92
|
+
await storage.delete(snapshotKey(executionId));
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
save,
|
|
96
|
+
load,
|
|
97
|
+
list,
|
|
98
|
+
clear,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deferred-style, single-shot signal used to wire a detached child — a
|
|
3
|
+
* terminal tool, an IPC handler, an external watchdog — back to the
|
|
4
|
+
* parent that awaits its outcome.
|
|
5
|
+
*
|
|
6
|
+
* The pattern is intentionally minimal: the parent awaits `signal.done`
|
|
7
|
+
* while the child calls `signal.resolve(outcome)` (or `signal.reject(err)`)
|
|
8
|
+
* when it has produced the final value. Any subsequent `resolve` /
|
|
9
|
+
* `reject` calls are silently dropped so a late-arriving duplicate
|
|
10
|
+
* settlement never overwrites the first.
|
|
11
|
+
*
|
|
12
|
+
* This is the canonical primitive the `runnableLoop` (and the task
|
|
13
|
+
* runner scripts that use it) build on. Keeping it in core means every
|
|
14
|
+
* out-of-process worker / detached step shares one signal shape rather
|
|
15
|
+
* than each reinventing the single-shot-deferred.
|
|
16
|
+
*/
|
|
17
|
+
/** Single-shot deferred signal the caller awaits via `.done`. */
|
|
18
|
+
export interface DetachedSignal<TOutcome> {
|
|
19
|
+
readonly done: Promise<TOutcome>;
|
|
20
|
+
/**
|
|
21
|
+
* Settle the signal with a success outcome. Only the first call has
|
|
22
|
+
* effect; subsequent calls (from retry logic, duplicate terminal
|
|
23
|
+
* tool invocations, external signal handlers) are dropped silently.
|
|
24
|
+
*/
|
|
25
|
+
resolve(outcome: TOutcome): void;
|
|
26
|
+
/**
|
|
27
|
+
* Settle the signal with a failure. Only the first call has effect;
|
|
28
|
+
* a `resolve` that races with a `reject` wins by whichever runs
|
|
29
|
+
* first — both further attempts are dropped.
|
|
30
|
+
*/
|
|
31
|
+
reject(err: unknown): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Construct a fresh single-shot `DetachedSignal<TOutcome>`. The returned
|
|
35
|
+
* signal's `done` Promise resolves/rejects at most once.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createDetachedSignal<TOutcome>(): DetachedSignal<TOutcome>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deferred-style, single-shot signal used to wire a detached child — a
|
|
3
|
+
* terminal tool, an IPC handler, an external watchdog — back to the
|
|
4
|
+
* parent that awaits its outcome.
|
|
5
|
+
*
|
|
6
|
+
* The pattern is intentionally minimal: the parent awaits `signal.done`
|
|
7
|
+
* while the child calls `signal.resolve(outcome)` (or `signal.reject(err)`)
|
|
8
|
+
* when it has produced the final value. Any subsequent `resolve` /
|
|
9
|
+
* `reject` calls are silently dropped so a late-arriving duplicate
|
|
10
|
+
* settlement never overwrites the first.
|
|
11
|
+
*
|
|
12
|
+
* This is the canonical primitive the `runnableLoop` (and the task
|
|
13
|
+
* runner scripts that use it) build on. Keeping it in core means every
|
|
14
|
+
* out-of-process worker / detached step shares one signal shape rather
|
|
15
|
+
* than each reinventing the single-shot-deferred.
|
|
16
|
+
*/
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region Public API
|
|
19
|
+
/**
|
|
20
|
+
* Construct a fresh single-shot `DetachedSignal<TOutcome>`. The returned
|
|
21
|
+
* signal's `done` Promise resolves/rejects at most once.
|
|
22
|
+
*/
|
|
23
|
+
export function createDetachedSignal() {
|
|
24
|
+
let resolveFn = null;
|
|
25
|
+
let rejectFn = null;
|
|
26
|
+
const done = new Promise((resolve, reject) => {
|
|
27
|
+
resolveFn = resolve;
|
|
28
|
+
rejectFn = reject;
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
done,
|
|
32
|
+
resolve(outcome) {
|
|
33
|
+
if (resolveFn === null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const fn = resolveFn;
|
|
37
|
+
resolveFn = null;
|
|
38
|
+
rejectFn = null;
|
|
39
|
+
fn(outcome);
|
|
40
|
+
},
|
|
41
|
+
reject(err) {
|
|
42
|
+
if (rejectFn === null) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const fn = rejectFn;
|
|
46
|
+
resolveFn = null;
|
|
47
|
+
rejectFn = null;
|
|
48
|
+
fn(err);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { LayerStateStore } from '../../memory/layer-lifecycle.js';
|
|
2
|
+
import type { ItemSchemaRegistry } from '../../schemas/item.js';
|
|
3
|
+
import type { Context } from '../../types/context.js';
|
|
4
|
+
import type { Item } from '../../types/items.js';
|
|
5
|
+
import type { MemoryLayer } from '../../types/memory.js';
|
|
6
|
+
import type { CheckpointStore } from './checkpoint-store.js';
|
|
7
|
+
/**
|
|
8
|
+
* Minimum harness surface `captureCheckpoint` / `restoreFromCheckpoint`
|
|
9
|
+
* require. `AgentHarness` already satisfies this shape via its
|
|
10
|
+
* `@internal` readonly fields — defining it structurally keeps the free
|
|
11
|
+
* functions loosely coupled to the harness implementation.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export interface CheckpointHarnessHandle {
|
|
16
|
+
readonly checkpointStore?: CheckpointStore;
|
|
17
|
+
readonly layerStateStore: LayerStateStore;
|
|
18
|
+
readonly itemSchemas: ItemSchemaRegistry;
|
|
19
|
+
readonly _memory?: MemoryLayer[];
|
|
20
|
+
createContext(opts?: {
|
|
21
|
+
items?: Item[];
|
|
22
|
+
threadId?: string;
|
|
23
|
+
resourceId?: string;
|
|
24
|
+
cwdInit?: string;
|
|
25
|
+
memory?: MemoryLayer[];
|
|
26
|
+
}): Context;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Snapshot the execution state at a checkpoint boundary. No-op when no
|
|
30
|
+
* `CheckpointStore` is configured — zero-config harnesses preserve
|
|
31
|
+
* ephemeral semantics. Save failures are logged rather than thrown,
|
|
32
|
+
* because a checkpoint failing must never abort an otherwise-successful
|
|
33
|
+
* step.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export declare function captureCheckpoint(h: CheckpointHarnessHandle, ctx: Context): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Rebuild a `Context` from a previously-persisted snapshot. Returns
|
|
40
|
+
* `null` if no snapshot is recorded for `executionId`. Layer state is
|
|
41
|
+
* replayed into `layerStateStore` keyed by the original executionId so
|
|
42
|
+
* the restored context observes the pre-crash state through
|
|
43
|
+
* `readLayerState` and the memory projectors.
|
|
44
|
+
*
|
|
45
|
+
* Preserves the original executionId on the returned context via
|
|
46
|
+
* `Object.defineProperty` — adapter correlation across crash/restart
|
|
47
|
+
* requires a stable id.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export declare function restoreFromCheckpoint(h: CheckpointHarnessHandle, executionId: string): Promise<Context | null>;
|