@sema-agent/core 5.17.0-pre.0 → 5.18.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/CHANGELOG.md +186 -1
- package/dist/agents/subagent.d.ts +8 -0
- package/dist/agents/subagent.js +93 -2
- package/dist/core/ask-question.js +6 -1
- package/dist/core/auto-compaction.d.ts +6 -0
- package/dist/core/auto-compaction.js +15 -1
- package/dist/core/canonical-json.js +176 -14
- package/dist/core/checkpoint-store.d.ts +15 -0
- package/dist/core/checkpoint-store.js +73 -0
- package/dist/core/governance-codes.js +1 -0
- package/dist/core/hooks.d.ts +12 -1
- package/dist/core/hooks.js +41 -6
- package/dist/core/mcp.d.ts +1 -0
- package/dist/core/mcp.js +18 -3
- package/dist/core/memory-engine/content-origin.d.ts +27 -0
- package/dist/core/memory-engine/content-origin.js +38 -0
- package/dist/core/memory-engine/engine.d.ts +12 -2
- package/dist/core/memory-engine/engine.js +172 -12
- package/dist/core/memory-engine/file-backend.d.ts +4 -0
- package/dist/core/memory-engine/file-backend.js +25 -3
- package/dist/core/memory-engine/index.d.ts +2 -1
- package/dist/core/memory-engine/index.js +2 -1
- package/dist/core/memory-engine/layout.d.ts +16 -0
- package/dist/core/memory-engine/layout.js +90 -2
- package/dist/core/memory-engine/sync-client.d.ts +1 -0
- package/dist/core/memory-engine/sync-client.js +23 -5
- package/dist/core/memory-engine/tools.d.ts +55 -0
- package/dist/core/memory-engine/tools.js +307 -0
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/memory.d.ts +4 -0
- package/dist/core/memory.js +15 -2
- package/dist/core/permission-rule-consent.d.ts +131 -0
- package/dist/core/permission-rule-consent.js +307 -0
- package/dist/core/permission-rule-model.d.ts +66 -0
- package/dist/core/permission-rule-model.js +135 -0
- package/dist/core/permission-rule-store.d.ts +89 -0
- package/dist/core/permission-rule-store.js +145 -0
- package/dist/core/permission-rules.d.ts +3 -2
- package/dist/core/permission-rules.js +9 -4
- package/dist/core/runner/prepare-memory.d.ts +3 -1
- package/dist/core/runner/prepare-memory.js +54 -14
- package/dist/core/runner/prepare-task.d.ts +12 -0
- package/dist/core/runner/prepare-task.js +270 -32
- package/dist/core/runner/runtask.js +27 -1
- package/dist/core/runner/tool-output-projection.js +1 -1
- package/dist/core/runner/turn-attachments.d.ts +2 -1
- package/dist/core/runner/turn-attachments.js +9 -6
- package/dist/core/session-reconcile.js +19 -1
- package/dist/core/task-registry-agent.js +1 -0
- package/dist/core/tool-policy.d.ts +17 -1
- package/dist/core/tool-policy.js +64 -8
- package/dist/core/tools.js +1 -0
- package/dist/core/trace.d.ts +20 -0
- package/dist/core/types.d.ts +15 -0
- package/dist/core/wiring-manifest.d.ts +5 -1
- package/dist/core/wiring-manifest.js +4 -2
- package/dist/engine/llm/validation.js +121 -5
- package/dist/engine/loop/agent-loop.d.ts +2 -0
- package/dist/engine/loop/agent-loop.js +17 -4
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -1
- package/dist/prompts/supervisor.d.ts +1 -1
- package/dist/prompts/supervisor.js +1 -1
- package/dist/stores/file/checkpoint-store.d.ts +1 -0
- package/dist/stores/file/checkpoint-store.js +1 -0
- package/dist/stores/file/permission-rule-store.d.ts +32 -0
- package/dist/stores/file/permission-rule-store.js +213 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +11 -10
- package/dist/tools/fs/fs-bash.js +17 -10
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +4 -3
- package/dist/tools/fs/fs-shared.d.ts +15 -0
- package/dist/tools/fs/fs-shared.js +73 -2
- package/dist/tools/fs/fs-write.js +8 -8
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/safety.d.ts +1 -0
- package/dist/tools/fs/safety.js +9 -2
- package/dist/tools/web.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,191 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 5.
|
|
3
|
+
## 5.18.0 — 2026-08-10
|
|
4
|
+
|
|
5
|
+
> Two mainline features land together: design/178 personal-assistant memory v1 (the retrieval pair
|
|
6
|
+
> and its safety spine) and design/179 persisted permission rules v1 (the "don't ask again" lane).
|
|
7
|
+
> Plus the TOC field-report fixes and the compaction/timeout honesty batch that accumulated on main.
|
|
8
|
+
|
|
9
|
+
### design/178 — personal-assistant memory v1 (additive)
|
|
10
|
+
|
|
11
|
+
- **`memory_search` + `memory_get`** mount as an atomic pair when a deployment wires a memory spec
|
|
12
|
+
(no spec = no tools = byte-identical behavior). Search returns scored, scope-contained results
|
|
13
|
+
across planes; get returns full entries under an explicit byte budget with honest
|
|
14
|
+
remainder disclosure. Retrieval is recorded (a usage account, collect-only in v1).
|
|
15
|
+
- **Content-origin classification (fail-closed)**: every mounted tool is classified
|
|
16
|
+
local/protocol/external; unknown ⇒ external. External content entering the transcript flips the
|
|
17
|
+
session into a **one-way polluted state**; a polluted session's harvest is quarantined, not
|
|
18
|
+
ingested.
|
|
19
|
+
- **Delegation is judged per call** by the delegated child's own statically-knowable tool pool: a
|
|
20
|
+
child that can reach external tools (or re-delegate to one that can) marks the parent session on
|
|
21
|
+
result return; a purely local child leaves memory fully live. Unknown/unresolvable child ⇒
|
|
22
|
+
fail-closed external.
|
|
23
|
+
- **The retrieval face never writes**: `memory_search`/`memory_get` read through a no-ingest view
|
|
24
|
+
(`retrievalView()` on the file backend) — a model-triggered read can no longer launder
|
|
25
|
+
out-of-band file edits into the ledger (the polluted-session laundering hole found in review).
|
|
26
|
+
- Containment/restore writes go through `O_NOFOLLOW` + write-through-fd (leaf-segment symlink
|
|
27
|
+
refusal; the ancestor-directory window is honestly registered, not claimed).
|
|
28
|
+
- Sync push runs the shared secret/size scan set; `maxEntryBytes` is validated (NaN/Infinity no
|
|
29
|
+
longer silently remove the cap).
|
|
30
|
+
|
|
31
|
+
### design/179 — persisted permission rules v1
|
|
32
|
+
|
|
33
|
+
- **The allow-rule lane**: wire a `PermissionRuleStoreProvider` and a human-approved rule
|
|
34
|
+
(`Bash(git status)` exact / `Bash(git status:*)` word-boundary prefix) resolves the plain asks it
|
|
35
|
+
matches — post-fold, before the classifier, never inside the policy fold. Consumption predicate
|
|
36
|
+
(all three required): the decision is an `ask`, it does not carry `requiresRealApproval`
|
|
37
|
+
(the two integrity gates stay untouchable), and it is not hook-minted. Every rule-resolved allow
|
|
38
|
+
carries `decisionReason: "persisted_rule"`.
|
|
39
|
+
- **Consent protocol**: rules enter the store ONLY through redemption of a durable approval record
|
|
40
|
+
(ask-card single approvals, the CC settings importer, and the starter batch share one protocol);
|
|
41
|
+
one redemption mints exactly one dot; replay is idempotent. `removePersistedRule` is the
|
|
42
|
+
host-callable tighten-direction removal (no ceremony; tombstone-identity retries).
|
|
43
|
+
- **Shell constructs are never rule-resolved** (pipes, redirections, substitutions, compounds —
|
|
44
|
+
fail-closed to the existing chain); rules match single plain commands only, per tool.
|
|
45
|
+
- **CC settings importer** reads the three user-editable layers and structurally reports the
|
|
46
|
+
flag/policy layers as not imported (`uncovered`, always present). Starter batch is consent-gated.
|
|
47
|
+
- Defaults: storeless deployments are byte-identical to 5.17.0 (golden-pinned), and wiring a store
|
|
48
|
+
does not silence the ungated-write warning. The wiring manifest gains
|
|
49
|
+
`permissionRules.storeWired`.
|
|
50
|
+
|
|
51
|
+
### BREAKING
|
|
52
|
+
|
|
53
|
+
- **`decisionReason` closed set gains `"persisted_rule"`** — consumers pinning the enum must re-pin.
|
|
54
|
+
- **`confirmRuleApproval` requires `selectedCandidate`** (the card's rules come from the engine;
|
|
55
|
+
a host cannot submit free-text rules).
|
|
56
|
+
- **`prepareCardApproval` no longer accepts caller candidates** (engine-minted only).
|
|
57
|
+
- **`createAllowDenyPolicy` throws `config.invalid_tool_name_set` by default** on names that can
|
|
58
|
+
never match (retired names, CC content-form entries like `Bash(ps:*)`, bad MCP forms) instead of
|
|
59
|
+
silently narrowing the tool out of the whitelist — the silent-Bash-loss class the field report
|
|
60
|
+
found. `onInvalidName: "skip"` opts back into discard-with-disclosure.
|
|
61
|
+
- Behavior narrowed (was 5.17.x): generated prefix *suggestions* are withdrawn (exact-command
|
|
62
|
+
suggestions only); a rule speaks only for its own tool; the file store write face takes a
|
|
63
|
+
process-wide lock.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- **Compaction**: an unevaluable context window (unknown/zero/null `contextWindow`) is a disclosed
|
|
68
|
+
state on every lane — including the end-of-task-only lane — instead of a silent `false`; a null
|
|
69
|
+
window value now takes the nothing-declared remedy.
|
|
70
|
+
- **Bash timeout plateau**: the sub-1.5s collapse is stated on the card from the same caps the
|
|
71
|
+
runtime enforces, minted once for both shell legs; a cap pair inside the plateau states the floor
|
|
72
|
+
instead of promising "1500+ for longer"; the config leg's discard warning names the value.
|
|
73
|
+
- Canonical-JSON internals drop double-jump casts (hash-neutral, byte-pinned).
|
|
74
|
+
- Truncation-invariant surface: two review-sampling legs registered as exempt (layered sampling
|
|
75
|
+
with unconditional tail window).
|
|
76
|
+
|
|
77
|
+
### Docs / registry
|
|
78
|
+
|
|
79
|
+
- Backlog #99–#104 recorded (deadline verdicts, classifier-guard REFUTED with corpus anchors,
|
|
80
|
+
models-hint truth, memory_get cursor v2, BootLock takeover, containment write family).
|
|
81
|
+
|
|
82
|
+
## 5.17.0 — 2026-08-09
|
|
83
|
+
|
|
84
|
+
> One release, three campaigns: design/176 peer guard + design/177 shared memory stores (below, from
|
|
85
|
+
> the first RC), the error-surface re-anchor batch, and the durable-approval honesty tail
|
|
86
|
+
> (capture/fidelity/seat-criterion). The re-verified RC supersedes `c48e66d`.
|
|
87
|
+
|
|
88
|
+
### BREAKING — model-visible error text re-anchored
|
|
89
|
+
|
|
90
|
+
- **Validation failure is a synthesized sentence, not a dump.** A tool-argument validation error now
|
|
91
|
+
reads as classified one-line findings (missing / unexpected / type-mismatched parameters, other
|
|
92
|
+
issues listed with a 2,000-char cap) — and **the full argument echo is gone** (the failed call sits
|
|
93
|
+
immediately above its result in the transcript; the echo was a verbatim second copy, paid once per
|
|
94
|
+
retry). The schema re-teach section stays, capped at 4,000 chars (an intentional divergence: a BYOM
|
|
95
|
+
model benefits from re-teaching). Tests pinning the old `Received arguments:` form must re-pin.
|
|
96
|
+
- **Every loop-minted error text is bounded.** `createErrorToolResult` runs the same 10k head+tail
|
|
97
|
+
truncation the tool-throw path already had — the tool-not-found, abort, hook/policy-text,
|
|
98
|
+
harness-throw and after-tool-note arms are no longer unbounded.
|
|
99
|
+
- **The tool-not-found roster lists ≤25 names** (`… and N more`, with a ToolSearch pointer when
|
|
100
|
+
mounted) instead of the whole roster.
|
|
101
|
+
- **Repeated interruption stubs collapse**: within one reconcile batch, the 2nd..nth orphaned call of
|
|
102
|
+
the same class gets a one-line back-reference instead of the full boilerplate.
|
|
103
|
+
- An MCP error's `structuredContent` is no longer appended when its JSON already appears in the text
|
|
104
|
+
parts; an ambiguous-Edit refusal echoes at most 200 chars of the needle; four repeated guidance
|
|
105
|
+
sentences are single-sourced (model-visible bytes unchanged).
|
|
106
|
+
- **The per-read content-safety reminder is gated** (`readCyberReminder` on the hands band / toolkit
|
|
107
|
+
options): default ON (injected after every text read, as before), explicit `false` for deployments
|
|
108
|
+
whose model carries this mitigation natively.
|
|
109
|
+
|
|
110
|
+
### BREAKING — durable approval rows filed at backend width
|
|
111
|
+
|
|
112
|
+
- The park mint files arguments at the width its checkpoint backend can hold. `CheckpointStore` gains
|
|
113
|
+
the third honest-declaration axis `fidelity` (`"structured-clone" | "json"`; ABSENT reads
|
|
114
|
+
fail-closed as `"json"`); the row's args, preview, risk descriptor and opaque `boundInputHash` are
|
|
115
|
+
minted from the backend's projection, so what an approver sees, what is on disk and what a resume
|
|
116
|
+
executes are one value. Arguments with no JSON encoding (BigInt, a cycle) are refused at the mint,
|
|
117
|
+
naming the backend. A projection that moves the value is re-adjudicated against the deployment's
|
|
118
|
+
policy; a deny — or a rewrite naming anything else — refuses the park and falls back to the
|
|
119
|
+
synchronous gate. `StoreFidelity` is exported.
|
|
120
|
+
**Upgrade note:** rows minted by earlier versions keep their pre-projection hash and already-degraded
|
|
121
|
+
arguments; drain pending approvals (approve/deny them) before upgrading, or accept the old semantics
|
|
122
|
+
on those rows.
|
|
123
|
+
|
|
124
|
+
### Changed
|
|
125
|
+
|
|
126
|
+
- **Model-visible text (four faces) now states what this engine actually does.** Each is a string the
|
|
127
|
+
model reads, so a deployment pinning these bytes must re-pin:
|
|
128
|
+
1. `tool_search_usage_reminder` no longer ends with "Calling a tool before its schema is loaded will
|
|
129
|
+
fail." With the RB-403 direct-call lane mounted — the DEFAULT (`TaskSpec.deferSelfResolve` not
|
|
130
|
+
disabled) — a call whose arguments match the real schema executes, so the absolute claim was false
|
|
131
|
+
for that posture. The default posture now reads "You do not have these tools' parameters, so
|
|
132
|
+
activate one rather than guessing its arguments."; the absolute wording is retained only under
|
|
133
|
+
`deferSelfResolve: false`, where it is exactly true. Same amendment DD-5 already made to the
|
|
134
|
+
sibling ToolSearch description.
|
|
135
|
+
2. `tools_delta`'s static `added` arm attributes schemas to "the tool result that activated them"
|
|
136
|
+
instead of "the ToolSearch result". Under `toolMaterializeStrategy: "static"` the direct-call lane
|
|
137
|
+
is always mounted, so an announced tool may have been activated with no ToolSearch result in
|
|
138
|
+
existence at all.
|
|
139
|
+
3. `AskUserQuestion`'s description is posture-conditional. Under `interactionPosture: "interactive"`
|
|
140
|
+
without `interactiveQuestionFallback`, both no-answer arms are coded failures, so the description
|
|
141
|
+
states that instead of promising "you will be told to proceed with your best judgment".
|
|
142
|
+
4. The deferred-orchestration prompt block no longer promises "the full contract arrives with it" —
|
|
143
|
+
under the static face only the parameter schema materializes, never the tool's description.
|
|
144
|
+
|
|
145
|
+
### Fixed
|
|
146
|
+
|
|
147
|
+
- **A durable park that cannot capture its arguments now says so, instead of leaving the call to report
|
|
148
|
+
a missing approver.** Arguments a `structuredClone` cannot copy — a hook/policy rewrite handing over a
|
|
149
|
+
function, a symbol, a live handle; model arguments are JSON, so the deployment is the only source —
|
|
150
|
+
used to reach the checkpoint store and throw a `DataCloneError` from inside `put`. That failure went to
|
|
151
|
+
`onError` alone, the park reported "not suspended", and the fallback chain's headless auto-deny told
|
|
152
|
+
the model and the `permissionDenied` observer that no approver was wired: true, and about a different
|
|
153
|
+
subject than the one that stopped the call. The park leg now checks capturability before any side
|
|
154
|
+
effect (with the same helper the synchronous approval boundary uses), refuses with the runtime's own
|
|
155
|
+
sentence, and never enters the store. A park attempt that fails for any reason carries its cause to
|
|
156
|
+
the gate, which appends it to whatever deny the fallback produces — the fallback itself, and the
|
|
157
|
+
compensation contract behind it, are unchanged. Three narrowings ride along: arguments carrying shared
|
|
158
|
+
memory (`SharedArrayBuffer`) are refused at the mint rather than filed as a row another holder can
|
|
159
|
+
still mutate; the row stores the checked snapshot, so its arguments, preview and binding hash describe
|
|
160
|
+
one reading rather than one per consumer; and a park that already made its one real attempt is not
|
|
161
|
+
asked again when the approver then reports unavailable (a suspend and a checkpoint write are not
|
|
162
|
+
idempotent — the invariant the content-ask lane already states, now held on the permission lane too).
|
|
163
|
+
Refusal text quoting a thrown value is neutralized and bounded on its way to the model, since a clone
|
|
164
|
+
error renders the offending value's own source into its message; the exception itself still reaches
|
|
165
|
+
`onError` with its type and stack intact.
|
|
166
|
+
- **BREAKING (narrow) — the child-chain durable-approval mandate uses the same seat criterion as the
|
|
167
|
+
task's own gate, single-sourced.** A blanket `onAsk: "allow"`/`"deny"` is a policy setting, not a
|
|
168
|
+
live approver — the parent's gate already said so; the inherited-constraint leg still read a string
|
|
169
|
+
seat as occupied. One predicate now serves both sites. The reachable defect was in the storeless
|
|
170
|
+
rows: with `durableApproval` configured but no `CheckpointStore`, a child under a blanket `"allow"`
|
|
171
|
+
chain entry EXECUTED side-effecting calls the parent's own gate would not have resolved through that
|
|
172
|
+
blanket. Such a child now parks with its own facility or denies fail-closed. (The store remains the
|
|
173
|
+
park lane's arming condition — it is just no longer misread as part of the seat.)
|
|
174
|
+
- **`wiring-manifest` no longer reports an auto verdict for runs whose asks park.** `deriveAskEffective`
|
|
175
|
+
returns `park_only` for a blanket `"allow"`/`"deny"` seat on an armed park lane. Operator/governance
|
|
176
|
+
projection only; the interaction-posture door is unaffected (it tests `human_reachable` alone).
|
|
177
|
+
- **A revived background agent no longer inherits the previous cycle's stop attribution.**
|
|
178
|
+
`reviveBackgroundAgentLane` clears `stoppedBy` in lockstep with the durable row's cleared-field set:
|
|
179
|
+
cycle 2's running and completed polls were reporting cycle 1's stopper while the durable-row lane
|
|
180
|
+
reported none, and cycle 2's real stopper was discarded in favour of cycle 1's.
|
|
181
|
+
- **Three CI discipline gates that had been red on every push are green.** `gate:field-liveness` (the
|
|
182
|
+
managed-retention contract never got allowlist rows), `gate:message-branching` and
|
|
183
|
+
`gate:domain-lexicon` (one false positive each, both narrowed rather than allowlisted). The step
|
|
184
|
+
aborts on first failure, so the six gate test files after it had not been running either.
|
|
185
|
+
- **The `eval/` and `bench/memory-write-channel` harnesses run again.** Both failed at task start on
|
|
186
|
+
config keys retired in 5.8.0 (`limits.timeoutSec`) and 2.0.0 (`memory: { scope }`) and reported it
|
|
187
|
+
only as a score of zero. `eval/` now runs 5/5 offline as its README claims, and joins `tsconfig`'s
|
|
188
|
+
include so the class reds `tsc` instead of rotting silently (the build config is unaffected).
|
|
4
189
|
|
|
5
190
|
### Added
|
|
6
191
|
|
|
@@ -9,6 +9,14 @@ import { SubagentRetainLedger } from "./retain-ledger.js";
|
|
|
9
9
|
import { type PeerSelfRef } from "./peer-admission.js";
|
|
10
10
|
export type { SubagentStep, SubagentEditedFile } from "./subagent-steps.js";
|
|
11
11
|
export declare function notifyResultField(result: string | undefined): string | undefined;
|
|
12
|
+
interface ReviewSample {
|
|
13
|
+
text: string;
|
|
14
|
+
readChars: number;
|
|
15
|
+
totalChars: number;
|
|
16
|
+
sampled: boolean;
|
|
17
|
+
gaps: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function layeredReviewSample(text: string, budget?: number, windows?: number): ReviewSample;
|
|
12
20
|
export declare function inheritedManifestScopeFor(snapshot: readonly unknown[] | undefined): RunInternals["inheritedManifestScope"];
|
|
13
21
|
export declare const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
|
|
14
22
|
export declare const EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE = 64;
|
package/dist/agents/subagent.js
CHANGED
|
@@ -55,6 +55,66 @@ export function notifyResultField(result) {
|
|
|
55
55
|
? `${result.slice(0, BG_AGENT_NOTIFY_RESULT_MAX)}\n[result truncated: ${result.length} chars total — call TaskOutput for the full text]`
|
|
56
56
|
: result;
|
|
57
57
|
}
|
|
58
|
+
const HANDBACK_REVIEW_SAMPLE_BUDGET = 12_000;
|
|
59
|
+
const HANDBACK_REVIEW_SAMPLE_WINDOWS = 5;
|
|
60
|
+
const HANDBACK_REVIEW_SAMPLE_MAX_WINDOWS = 16;
|
|
61
|
+
const HANDBACK_REVIEW_MARKER_COST = 32;
|
|
62
|
+
const REVIEW_MARKER_SCAR = String.fromCharCode(0xff3b);
|
|
63
|
+
function defuseGapMarkers(slice) {
|
|
64
|
+
return slice.replace(/\[…/g, `${REVIEW_MARKER_SCAR}…`);
|
|
65
|
+
}
|
|
66
|
+
function surrogateSafeSlice(text, from, width, anchorEnd = false) {
|
|
67
|
+
const isLow = (i) => text.charCodeAt(i) >= 0xdc00 && text.charCodeAt(i) <= 0xdfff;
|
|
68
|
+
const isHigh = (i) => text.charCodeAt(i) >= 0xd800 && text.charCodeAt(i) <= 0xdbff;
|
|
69
|
+
let start = from;
|
|
70
|
+
if (start > 0 && start < text.length && isLow(start) && isHigh(start - 1))
|
|
71
|
+
start--;
|
|
72
|
+
if (anchorEnd)
|
|
73
|
+
return { text: text.slice(start), start, end: text.length };
|
|
74
|
+
let end = Math.min(start + width, text.length);
|
|
75
|
+
if (end > start && end < text.length && isHigh(end - 1) && isLow(end))
|
|
76
|
+
end--;
|
|
77
|
+
return { text: text.slice(start, end), start, end };
|
|
78
|
+
}
|
|
79
|
+
export function layeredReviewSample(text, budget = HANDBACK_REVIEW_SAMPLE_BUDGET, windows = HANDBACK_REVIEW_SAMPLE_WINDOWS) {
|
|
80
|
+
const total = text.length;
|
|
81
|
+
const budgetChars = Number.isFinite(budget) ? Math.max(4, Math.floor(budget)) : HANDBACK_REVIEW_SAMPLE_BUDGET;
|
|
82
|
+
if (total <= budgetChars)
|
|
83
|
+
return { text, readChars: total, totalChars: total, sampled: false, gaps: 0 };
|
|
84
|
+
const requested = Number.isFinite(windows) ? Math.floor(windows) : HANDBACK_REVIEW_SAMPLE_WINDOWS;
|
|
85
|
+
const count = Math.max(2, Math.min(Math.max(2, requested), HANDBACK_REVIEW_SAMPLE_MAX_WINDOWS, Math.floor(budgetChars / HANDBACK_REVIEW_MARKER_COST)));
|
|
86
|
+
const size = Math.floor(budgetChars / count);
|
|
87
|
+
const span = total - size;
|
|
88
|
+
const parts = [];
|
|
89
|
+
let readChars = 0;
|
|
90
|
+
let prevEnd = 0;
|
|
91
|
+
let gaps = 0;
|
|
92
|
+
for (let i = 0; i < count; i++) {
|
|
93
|
+
const start = Math.round((span * i) / (count - 1));
|
|
94
|
+
const slice = surrogateSafeSlice(text, start, size, i === count - 1);
|
|
95
|
+
if (i > 0 && slice.start > prevEnd) {
|
|
96
|
+
parts.push(`\n[… ${slice.start - prevEnd} chars not shown …]\n`);
|
|
97
|
+
gaps++;
|
|
98
|
+
}
|
|
99
|
+
parts.push(defuseGapMarkers(slice.text));
|
|
100
|
+
readChars += slice.text.length;
|
|
101
|
+
prevEnd = slice.end;
|
|
102
|
+
}
|
|
103
|
+
const out = parts.join("");
|
|
104
|
+
if (out.length >= total)
|
|
105
|
+
return { text, readChars: total, totalChars: total, sampled: false, gaps: 0 };
|
|
106
|
+
return { text: out, readChars, totalChars: total, sampled: true, gaps };
|
|
107
|
+
}
|
|
108
|
+
function reviewCoverageNote(fields) {
|
|
109
|
+
const sampled = fields.filter((f) => f.sample.sampled);
|
|
110
|
+
if (sampled.length === 0)
|
|
111
|
+
return undefined;
|
|
112
|
+
return (sampled
|
|
113
|
+
.map((f) => `${f.field}: the classifier was shown ${f.sample.readChars} of ${f.sample.totalChars} characters (UTF-16 code units) ` +
|
|
114
|
+
`(layered head/middle/tail sample; the ${f.sample.gaps} skipped span(s) are marked in place as ` +
|
|
115
|
+
`"[… N chars not shown …]" — any further such marker in the text is the child's own writing, not this sampler's)`)
|
|
116
|
+
.join("; ") + " — the unshown spans were NOT reviewed; treat them as unknown, not as benign.");
|
|
117
|
+
}
|
|
58
118
|
const HANDBACK_ASK_MESSAGE = "Subagent has finished and is handing back control to the main agent. Review the subagent's work and flag if any action may violate security policy.";
|
|
59
119
|
async function reviewHandback(opts) {
|
|
60
120
|
const { review, evidence, signal } = opts;
|
|
@@ -64,15 +124,22 @@ async function reviewHandback(opts) {
|
|
|
64
124
|
return undefined;
|
|
65
125
|
if (!evidence.result && !evidence.steps && !evidence.edits && !evidence.partialText)
|
|
66
126
|
return undefined;
|
|
127
|
+
const resultSample = evidence.result ? layeredReviewSample(evidence.result) : undefined;
|
|
128
|
+
const partialSample = evidence.partialText ? layeredReviewSample(evidence.partialText) : undefined;
|
|
129
|
+
const coverage = reviewCoverageNote([
|
|
130
|
+
...(resultSample ? [{ field: "result", sample: resultSample }] : []),
|
|
131
|
+
...(partialSample ? [{ field: "partialFindings", sample: partialSample }] : []),
|
|
132
|
+
]);
|
|
67
133
|
const verdict = await review.decider
|
|
68
134
|
.decide({
|
|
69
135
|
req: {
|
|
70
136
|
toolName: opts.toolName,
|
|
71
137
|
args: {
|
|
72
|
-
result:
|
|
138
|
+
result: resultSample?.text,
|
|
73
139
|
toolSteps: evidence.steps,
|
|
74
140
|
editedFiles: evidence.edits,
|
|
75
|
-
...(
|
|
141
|
+
...(partialSample ? { partialFindings: partialSample.text } : {}),
|
|
142
|
+
...(coverage ? { reviewCoverage: coverage } : {}),
|
|
76
143
|
},
|
|
77
144
|
toolCallId: opts.toolCallId,
|
|
78
145
|
},
|
|
@@ -933,9 +1000,33 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
933
1000
|
}]
|
|
934
1001
|
: []),
|
|
935
1002
|
];
|
|
1003
|
+
const delegationToolName = opts.name ?? DEFAULT_SUBAGENT_TOOL_NAME;
|
|
1004
|
+
const depthPermitsNesting = depth + 1 < maxDepth;
|
|
1005
|
+
const agentToolFaces = [
|
|
1006
|
+
...(generalPurposeShadowed ? [] : [{ name: GENERAL_PURPOSE_SUBAGENT_TYPE, ...(depthPermitsNesting ? { canRedelegate: true } : {}) }]),
|
|
1007
|
+
...available.map((a) => ({
|
|
1008
|
+
name: a.name,
|
|
1009
|
+
...(a.allowTools !== undefined ? { allowTools: a.allowTools } : {}),
|
|
1010
|
+
...(a.denyTools !== undefined ? { denyTools: a.denyTools } : {}),
|
|
1011
|
+
...(depthPermitsNesting && toolNameAllowed(delegationToolName, a.allowTools, a.denyTools) ? { canRedelegate: true } : {}),
|
|
1012
|
+
})),
|
|
1013
|
+
...(forkOffered ? [{ name: FORK_SUBAGENT_TYPE, ...(depthPermitsNesting ? { canRedelegate: true } : {}) }] : []),
|
|
1014
|
+
];
|
|
1015
|
+
const agentToolPool = opts.extraTools !== undefined
|
|
1016
|
+
? undefined
|
|
1017
|
+
: (opts.tools ?? []).map((t) => ({
|
|
1018
|
+
name: t.name,
|
|
1019
|
+
...(t.aliases !== undefined ? { aliases: t.aliases } : {}),
|
|
1020
|
+
...(t.contentOrigin !== undefined
|
|
1021
|
+
? { contentOrigin: t.contentOrigin }
|
|
1022
|
+
: {}),
|
|
1023
|
+
}));
|
|
936
1024
|
return {
|
|
937
1025
|
name: opts.name ?? DEFAULT_SUBAGENT_TOOL_NAME,
|
|
938
1026
|
agentListing,
|
|
1027
|
+
agentToolFaces,
|
|
1028
|
+
...(agentToolPool !== undefined ? { agentToolPool } : {}),
|
|
1029
|
+
contentOrigin: "local",
|
|
939
1030
|
...(rosterNames !== undefined ? { agentModels: rosterNames } : {}),
|
|
940
1031
|
executionMode: "parallel",
|
|
941
1032
|
contract: { contractId: "core.agent@1", implementationRevision: "1" },
|
|
@@ -174,7 +174,12 @@ export function createAskUserQuestionTool(onQuestion, source, opts) {
|
|
|
174
174
|
description: "Ask the user a structured multiple-choice question when you are genuinely stuck or a decision is " +
|
|
175
175
|
"the user's to make (which approach, which of these). Provide 1-4 questions, each with 2-4 options " +
|
|
176
176
|
"(label + description). Use sparingly — not for anything you can determine yourself from the code or " +
|
|
177
|
-
"the task.
|
|
177
|
+
"the task. " +
|
|
178
|
+
(onQuestion !== undefined && opts?.posture === "interactive" && opts.interactiveFallback !== true
|
|
179
|
+
? "If no human can be reached this call FAILS with a coded error — do not plan around being told to " +
|
|
180
|
+
"proceed with your best judgment."
|
|
181
|
+
: "If no human is available you will be told to proceed with your best judgment.") +
|
|
182
|
+
"\n" +
|
|
178
183
|
"\n" +
|
|
179
184
|
"Usage notes:\n" +
|
|
180
185
|
'- Users will always be able to select "Other" to provide custom text input\n' +
|
|
@@ -125,6 +125,12 @@ export declare function maybeCompact(opts: MaybeCompactOptions): Promise<{
|
|
|
125
125
|
estTokens: number;
|
|
126
126
|
floor: number;
|
|
127
127
|
};
|
|
128
|
+
unevaluableWindow?: {
|
|
129
|
+
estTokens: number;
|
|
130
|
+
windowField: "autoCompactTokens" | "contextTokens" | "contextWindow";
|
|
131
|
+
windowValue?: number;
|
|
132
|
+
modelId?: string;
|
|
133
|
+
};
|
|
128
134
|
contextUsage?: {
|
|
129
135
|
usedTokens: number;
|
|
130
136
|
windowTokens: number;
|
|
@@ -69,6 +69,7 @@ export async function maybeCompact(opts) {
|
|
|
69
69
|
const tokens = anchorStale || est.usageTokens === 0 ? structuralTokens + overhead : est.tokens;
|
|
70
70
|
const window = opts.model.autoCompactTokens ?? opts.model.contextTokens ?? opts.model.contextWindow;
|
|
71
71
|
const windowKnown = Number.isFinite(window) && window > 0;
|
|
72
|
+
const windowField = opts.model.autoCompactTokens != null ? "autoCompactTokens" : opts.model.contextTokens != null ? "contextTokens" : "contextWindow";
|
|
72
73
|
const settings = sanitizeCompactionSettings(rawSettings, window);
|
|
73
74
|
const contextUsage = windowKnown
|
|
74
75
|
? { usedTokens: tokens, windowTokens: window, compactAtTokens: window - settings.reserveTokens }
|
|
@@ -80,7 +81,20 @@ export async function maybeCompact(opts) {
|
|
|
80
81
|
if (wantsCompact) {
|
|
81
82
|
return { contextUsage, compacted: false, suppressedByFloor: { estTokens: tokens, floor: opts.minTokens ?? 0 } };
|
|
82
83
|
}
|
|
83
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
contextUsage,
|
|
86
|
+
compacted: false,
|
|
87
|
+
...(windowKnown
|
|
88
|
+
? {}
|
|
89
|
+
: {
|
|
90
|
+
unevaluableWindow: {
|
|
91
|
+
estTokens: tokens,
|
|
92
|
+
windowField,
|
|
93
|
+
...(typeof window === "number" ? { windowValue: window } : {}),
|
|
94
|
+
...(opts.model.id !== undefined ? { modelId: opts.model.id } : {}),
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
84
98
|
}
|
|
85
99
|
const branch = await opts.session.getBranch();
|
|
86
100
|
const prep = prepareCompaction(branch, settings, cpt, window);
|
|
@@ -39,20 +39,10 @@ function encode(value, depth = 0) {
|
|
|
39
39
|
out += encode(el, depth + 1);
|
|
40
40
|
return `${out}]`;
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let encoded;
|
|
47
|
-
try {
|
|
48
|
-
encoded = encode(obj[k], depth + 1);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
encoded = "<unreadable-property>";
|
|
52
|
-
}
|
|
53
|
-
out += `${Buffer.byteLength(k, "utf8")}:${k}=${encoded}`;
|
|
54
|
-
}
|
|
55
|
-
return `${out}}`;
|
|
42
|
+
const branded = encodeBranded(value, depth);
|
|
43
|
+
if (branded !== undefined)
|
|
44
|
+
return branded;
|
|
45
|
+
return encodeOwnKeys(value, depth);
|
|
56
46
|
}
|
|
57
47
|
catch {
|
|
58
48
|
return "<unserializable>";
|
|
@@ -62,6 +52,178 @@ function encode(value, depth = 0) {
|
|
|
62
52
|
return "U";
|
|
63
53
|
}
|
|
64
54
|
}
|
|
55
|
+
function encodeOwnKeys(source, depth, skipIndicesBelow = 0) {
|
|
56
|
+
const obj = source;
|
|
57
|
+
const all = Object.keys(obj);
|
|
58
|
+
const keys = (skipIndicesBelow > 0 ? all.filter((k) => !isIndexBelow(k, skipIndicesBelow)) : all).sort();
|
|
59
|
+
let out = `o${keys.length}:{`;
|
|
60
|
+
for (const k of keys) {
|
|
61
|
+
let encoded;
|
|
62
|
+
try {
|
|
63
|
+
encoded = encode(obj[k], depth + 1);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
encoded = "<unreadable-property>";
|
|
67
|
+
}
|
|
68
|
+
out += `${Buffer.byteLength(k, "utf8")}:${k}=${encoded}`;
|
|
69
|
+
}
|
|
70
|
+
return `${out}}`;
|
|
71
|
+
}
|
|
72
|
+
function isIndexBelow(key, limit) {
|
|
73
|
+
const n = Number(key);
|
|
74
|
+
return Number.isInteger(n) && n >= 0 && n < limit && String(n) === key;
|
|
75
|
+
}
|
|
76
|
+
function brandOf(value) {
|
|
77
|
+
try {
|
|
78
|
+
const s = Object.prototype.toString.call(value);
|
|
79
|
+
return s.startsWith("[object ") && s.endsWith("]") ? s.slice(8, -1) : "Object";
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return "Object";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function encodeBranded(value, depth) {
|
|
86
|
+
const brand = brandOf(value);
|
|
87
|
+
if (brand === "Object")
|
|
88
|
+
return undefined;
|
|
89
|
+
if (ArrayBuffer.isView(value)) {
|
|
90
|
+
try {
|
|
91
|
+
const { buffer, byteOffset, byteLength, elements, family } = viewRange(value);
|
|
92
|
+
const bytesToken = digestBytes(new Uint8Array(buffer, byteOffset, byteLength));
|
|
93
|
+
return `V:${family}:${brand}:${byteLength}:${bytesToken}${encodeOwnKeys(value, depth, elements)}`;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const own = encodeOwnKeys(value, depth);
|
|
99
|
+
try {
|
|
100
|
+
switch (brand) {
|
|
101
|
+
case "Date": {
|
|
102
|
+
const t = Date.prototype.getTime.call(value);
|
|
103
|
+
return `D:${Number.isNaN(t) ? "Invalid" : encodeNumber(t)}${own}`;
|
|
104
|
+
}
|
|
105
|
+
case "Map": {
|
|
106
|
+
const size = Number(mapSizeGetter.call(value));
|
|
107
|
+
let out = `m${size}:{`;
|
|
108
|
+
let seen = 0;
|
|
109
|
+
for (const [k, v] of mapEntries.call(value)) {
|
|
110
|
+
if (seen++ >= size)
|
|
111
|
+
break;
|
|
112
|
+
out += `${encode(k, depth + 1)}=${encode(v, depth + 1)}`;
|
|
113
|
+
}
|
|
114
|
+
return `${out}}${own}`;
|
|
115
|
+
}
|
|
116
|
+
case "Set": {
|
|
117
|
+
const size = Number(setSizeGetter.call(value));
|
|
118
|
+
let out = `t${size}:[`;
|
|
119
|
+
let seen = 0;
|
|
120
|
+
for (const v of setValues.call(value)) {
|
|
121
|
+
if (seen++ >= size)
|
|
122
|
+
break;
|
|
123
|
+
out += encode(v, depth + 1);
|
|
124
|
+
}
|
|
125
|
+
return `${out}]${own}`;
|
|
126
|
+
}
|
|
127
|
+
case "RegExp": {
|
|
128
|
+
const source = String(regexpSourceGetter.call(value));
|
|
129
|
+
const flags = String(regexpFlagsGetter.call(value));
|
|
130
|
+
return `r${Buffer.byteLength(source, "utf8")}:${source}/${flags}${own}`;
|
|
131
|
+
}
|
|
132
|
+
case "Error": {
|
|
133
|
+
const name = String(value.name);
|
|
134
|
+
const message = String(value.message);
|
|
135
|
+
return `E${Buffer.byteLength(name, "utf8")}:${name}:${Buffer.byteLength(message, "utf8")}:${message}${own}`;
|
|
136
|
+
}
|
|
137
|
+
case "URL": {
|
|
138
|
+
const href = String(urlHrefGetter.call(value));
|
|
139
|
+
return `L${Buffer.byteLength(href, "utf8")}:${href}${own}`;
|
|
140
|
+
}
|
|
141
|
+
case "Boolean":
|
|
142
|
+
case "Number":
|
|
143
|
+
case "String":
|
|
144
|
+
case "BigInt":
|
|
145
|
+
case "Symbol": {
|
|
146
|
+
const unboxed = boxedValueOf[brand].call(value);
|
|
147
|
+
return `B:${encode(unboxed, depth + 1)}${own}`;
|
|
148
|
+
}
|
|
149
|
+
case "ArrayBuffer":
|
|
150
|
+
case "SharedArrayBuffer": {
|
|
151
|
+
const byteLength = Number((brand === "ArrayBuffer" ? arrayBufferByteLengthGetter : sharedArrayBufferByteLengthGetter).call(value));
|
|
152
|
+
return `A:${brand}:${byteLength}:${digestBytes(new Uint8Array(value))}${own}`;
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
}
|
|
160
|
+
return `x${Buffer.byteLength(brand, "utf8")}:${brand}${own}`;
|
|
161
|
+
}
|
|
162
|
+
function primordialGetter(proto, key) {
|
|
163
|
+
const get = Object.getOwnPropertyDescriptor(proto, key)?.get;
|
|
164
|
+
if (!get) {
|
|
165
|
+
return () => {
|
|
166
|
+
throw new Error(`no primordial getter for ${key}`);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return get;
|
|
170
|
+
}
|
|
171
|
+
const mapEntries = Map.prototype.entries;
|
|
172
|
+
const setValues = Set.prototype.values;
|
|
173
|
+
const mapSizeGetter = primordialGetter(Map.prototype, "size");
|
|
174
|
+
const setSizeGetter = primordialGetter(Set.prototype, "size");
|
|
175
|
+
const regexpSourceGetter = primordialGetter(RegExp.prototype, "source");
|
|
176
|
+
const regexpFlagsGetter = primordialGetter(RegExp.prototype, "flags");
|
|
177
|
+
const urlHrefGetter = primordialGetter(URL.prototype, "href");
|
|
178
|
+
const arrayBufferByteLengthGetter = primordialGetter(ArrayBuffer.prototype, "byteLength");
|
|
179
|
+
const sharedArrayBufferByteLengthGetter = typeof SharedArrayBuffer !== "undefined"
|
|
180
|
+
? primordialGetter(SharedArrayBuffer.prototype, "byteLength")
|
|
181
|
+
: primordialGetter({}, "byteLength");
|
|
182
|
+
const boxedValueOf = {
|
|
183
|
+
Boolean: Boolean.prototype.valueOf,
|
|
184
|
+
Number: Number.prototype.valueOf,
|
|
185
|
+
String: String.prototype.valueOf,
|
|
186
|
+
BigInt: BigInt.prototype.valueOf,
|
|
187
|
+
Symbol: Symbol.prototype.valueOf,
|
|
188
|
+
};
|
|
189
|
+
const typedArrayProto = Object.getPrototypeOf(Uint8Array.prototype);
|
|
190
|
+
const typedArrayLengthGetter = primordialGetter(typedArrayProto, "length");
|
|
191
|
+
const viewGetters = {
|
|
192
|
+
typed: {
|
|
193
|
+
buffer: primordialGetter(typedArrayProto, "buffer"),
|
|
194
|
+
byteOffset: primordialGetter(typedArrayProto, "byteOffset"),
|
|
195
|
+
byteLength: primordialGetter(typedArrayProto, "byteLength"),
|
|
196
|
+
},
|
|
197
|
+
dataView: {
|
|
198
|
+
buffer: primordialGetter(DataView.prototype, "buffer"),
|
|
199
|
+
byteOffset: primordialGetter(DataView.prototype, "byteOffset"),
|
|
200
|
+
byteLength: primordialGetter(DataView.prototype, "byteLength"),
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
function viewRange(view) {
|
|
204
|
+
for (const family of ["typed", "dataView"]) {
|
|
205
|
+
const g = viewGetters[family];
|
|
206
|
+
try {
|
|
207
|
+
const range = {
|
|
208
|
+
buffer: g.buffer.call(view),
|
|
209
|
+
byteOffset: Number(g.byteOffset.call(view)),
|
|
210
|
+
byteLength: Number(g.byteLength.call(view)),
|
|
211
|
+
elements: 0,
|
|
212
|
+
family,
|
|
213
|
+
};
|
|
214
|
+
if (family === "typed")
|
|
215
|
+
range.elements = Number(typedArrayLengthGetter.call(view));
|
|
216
|
+
return range;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
throw new Error("not a readable ArrayBuffer view");
|
|
223
|
+
}
|
|
224
|
+
function digestBytes(bytes) {
|
|
225
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
226
|
+
}
|
|
65
227
|
function encodeNumber(n) {
|
|
66
228
|
if (Number.isNaN(n))
|
|
67
229
|
return "NaN";
|
|
@@ -123,6 +123,7 @@ export type PendingAction = {
|
|
|
123
123
|
toolName: string;
|
|
124
124
|
args: unknown;
|
|
125
125
|
preview?: unknown;
|
|
126
|
+
ruleSuggestions?: readonly import("./permission-rule-model.js").RuleSuggestion[];
|
|
126
127
|
boundInputHash: string;
|
|
127
128
|
batchToolCallIds: string[];
|
|
128
129
|
completedCallIds: string[];
|
|
@@ -283,9 +284,22 @@ export declare class CheckpointError extends Error {
|
|
|
283
284
|
} | undefined);
|
|
284
285
|
}
|
|
285
286
|
export type StoreDurability = "durable" | "process-local";
|
|
287
|
+
export type StoreFidelity = "structured-clone" | "json";
|
|
288
|
+
export declare function resolveDeclaredFidelity(store: {
|
|
289
|
+
readonly fidelity?: StoreFidelity;
|
|
290
|
+
} | undefined, storeName: string): StoreFidelity;
|
|
291
|
+
export declare function encodeAtFidelity(fidelity: StoreFidelity, value: unknown): {
|
|
292
|
+
ok: true;
|
|
293
|
+
value: unknown;
|
|
294
|
+
} | {
|
|
295
|
+
ok: false;
|
|
296
|
+
cause: unknown;
|
|
297
|
+
};
|
|
298
|
+
export declare function samePlainValue(a: unknown, b: unknown): boolean;
|
|
286
299
|
export interface CheckpointStore {
|
|
287
300
|
readonly retention?: import("./retention.js").RetentionDeclaration;
|
|
288
301
|
readonly durability?: StoreDurability;
|
|
302
|
+
readonly fidelity?: StoreFidelity;
|
|
289
303
|
put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
|
|
290
304
|
get(token: CheckpointToken): Promise<Checkpoint | null>;
|
|
291
305
|
resolve(token: CheckpointToken, scope: string, outcome: ResumeOutcome, expect?: ResolveExpectation): Promise<boolean>;
|
|
@@ -315,6 +329,7 @@ export type CheckpointFaultMode = "resolve-after-commit" | "resolve-before-commi
|
|
|
315
329
|
export declare class InMemoryCheckpointStore implements CheckpointStore {
|
|
316
330
|
readonly retention: "none";
|
|
317
331
|
readonly durability: "process-local";
|
|
332
|
+
readonly fidelity: "structured-clone";
|
|
318
333
|
private cps;
|
|
319
334
|
private fault;
|
|
320
335
|
put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
|