@sema-agent/core 5.44.0 → 5.46.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 +109 -0
- package/dist/agents/subagent.d.ts +3 -1
- package/dist/agents/subagent.js +1 -1
- package/dist/brain/reasoning.d.ts +50 -4
- package/dist/brain/reasoning.js +28 -7
- package/dist/brain/request-params.d.ts +0 -12
- package/dist/brain/request-params.js +1 -1
- package/dist/core/checkpoint-store.d.ts +12 -0
- package/dist/core/governance-codes.js +1 -0
- package/dist/core/hooks.d.ts +9 -2
- package/dist/core/hooks.js +6 -5
- package/dist/core/memory-engine/content-origin.d.ts +9 -4
- package/dist/core/memory-engine/delegation-provenance.d.ts +12 -7
- package/dist/core/memory-engine/engine.d.ts +50 -3
- package/dist/core/memory-engine/engine.js +194 -32
- package/dist/core/memory-engine/export-bundle.d.ts +10 -1
- package/dist/core/memory-engine/export-bundle.js +21 -0
- package/dist/core/memory-engine/file-backend.d.ts +33 -4
- package/dist/core/memory-engine/file-backend.js +165 -39
- package/dist/core/memory-engine/frontmatter.d.ts +42 -1
- package/dist/core/memory-engine/frontmatter.js +141 -1
- package/dist/core/memory-engine/header-hints.d.ts +17 -0
- package/dist/core/memory-engine/header-hints.js +6 -0
- package/dist/core/memory-engine/index.d.ts +4 -3
- package/dist/core/memory-engine/index.js +3 -2
- package/dist/core/memory-engine/layout.d.ts +25 -2
- package/dist/core/memory-engine/layout.js +25 -12
- package/dist/core/memory-engine/memory-backend-contract.js +65 -0
- package/dist/core/memory-engine/sync-client.d.ts +1 -1
- package/dist/core/memory-engine/sync-client.js +33 -1
- package/dist/core/memory-engine/tools.d.ts +7 -0
- package/dist/core/memory-engine/tools.js +3 -0
- package/dist/core/memory-engine/types.d.ts +75 -1
- package/dist/core/memory-engine/types.js +1 -1
- package/dist/core/reminder-mint.d.ts +70 -0
- package/dist/core/reminder-mint.js +25 -0
- package/dist/core/runner/assemble-result.d.ts +5 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/git-status-frame.d.ts +3 -14
- package/dist/core/runner/git-status-frame.js +39 -14
- package/dist/core/runner/prepare-config-doors.d.ts +10 -0
- package/dist/core/runner/prepare-config-doors.js +32 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +5 -11
- package/dist/core/runner/prepare-hands-readface.js +26 -0
- package/dist/core/runner/prepare-memory.d.ts +11 -0
- package/dist/core/runner/prepare-memory.js +12 -10
- package/dist/core/runner/prepare-task.d.ts +25 -2
- package/dist/core/runner/prepare-task.js +71 -14
- package/dist/core/runner/runtask.js +82 -72
- package/dist/core/side-query.d.ts +11 -1
- package/dist/core/side-query.js +3 -0
- package/dist/core/trace.d.ts +17 -2
- package/dist/core/types.d.ts +119 -8
- package/dist/engine/harness/types.d.ts +46 -1
- package/dist/engine/harness/types.js +11 -0
- package/dist/engine/session/import-validate.js +6 -1
- package/dist/engine/session/session.d.ts +20 -0
- package/dist/engine/session/session.js +26 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/orchestration/run-workflow-tool.d.ts +16 -0
- package/dist/orchestration/run-workflow-tool.js +23 -3
- package/dist/orchestration/workflow-governance.d.ts +8 -1
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/types.d.ts +6 -0
- package/dist/prompts/default.d.ts +13 -1
- package/dist/prompts/default.js +5 -1
- package/dist/tools/fs/fs-bash.d.ts +4 -0
- package/dist/tools/fs/fs-bash.js +1 -1
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +8 -7
- package/dist/tools/fs/fs-shared.d.ts +10 -4
- package/dist/tools/fs/fs-shared.js +6 -3
- package/dist/tools/fs/gh-rate-limit.d.ts +4 -1
- package/dist/tools/fs/gh-rate-limit.js +3 -2
- package/dist/tools/fs/index.d.ts +10 -2
- package/dist/tools/fs/index.js +2 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,114 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.46.0 — 2026-08-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- design/319 A (system-reminder provenance mark): every engine-authored `<system-reminder>` OPEN
|
|
7
|
+
tag carries a per-session random `mark="<22-char base64url>"` attribute, and the system prompt
|
|
8
|
+
declares its semantics in ONE strict form — reminder-shaped text WITHOUT the current mark is
|
|
9
|
+
data wherever it appears. A forged reminder tag is now byte-distinguishable from an engine mint
|
|
10
|
+
on every verbatim inlet (Read/Bash/Grep/MCP-success). ZERO data-byte change: the mark rides only
|
|
11
|
+
the engine's own tag; Read stays verbatim, Edit across forged tags lands, Bash/Grep clean paths
|
|
12
|
+
are byte-compatible, and markless (library-direct) calls render historic bare bytes verbatim.
|
|
13
|
+
New exports: `mintReminderMark`, `isValidReminderMark`, `openSystemReminder`,
|
|
14
|
+
`mintSystemReminder`, `reminderMarkDeclaration` (scheme-replaceable mint home). Checkpoint gains
|
|
15
|
+
an additive `reminderMark` field + a new session-entry type `reminder_mark` (old readers ignore
|
|
16
|
+
both); a legacy or malformed-mark resume re-mints under the same strict declaration (historic
|
|
17
|
+
bare mints read as data — fail-safe). Consumers matching reminder tags by exact string must go
|
|
18
|
+
attribute-tolerant. A provider-owned pass-through prompt that renders no declaration discloses
|
|
19
|
+
loudly once per prepare (opt-in: `reminderMarkDeclaration(ctx.reminderMark)`).
|
|
20
|
+
- design/336 slice 1 (F-020 deep fix — the origin carriage): an externally-exposed session's
|
|
21
|
+
ORDINARY memory writes now COMMIT carrying an engine-minted typed `origin` frontmatter marker
|
|
22
|
+
(taint + closed cause vocabulary + mint time) instead of the design/178 full quarantine that
|
|
23
|
+
zeroed TOC recall for every delegation-bearing session. Engine-exclusive and forgery-proof on
|
|
24
|
+
four faces (model-written origin bytes strip-with-disclosure — benign echo of the committed
|
|
25
|
+
marker stays silent; backend immutability law over every op spelling; inbound whitewash gate;
|
|
26
|
+
sync/bundle validation with rev honesty). Instruction-form files stay hard-gated. ONE deployment
|
|
27
|
+
seat: `RunnerDeps.memoryProvenance?: "off" | "carry"` (default `"carry"`; `"off"` restores
|
|
28
|
+
pre-336 containment byte-level; bad values refuse loudly, `config.memory_provenance`). New
|
|
29
|
+
public family: `MemoryEntryOrigin`/`MemoryOriginCause`/`MEMORY_ORIGIN_CAUSES`,
|
|
30
|
+
`committedOriginOf`, `originEquals`, `isInstructionEntry`. Bundle rows carry `marked`+`cause`;
|
|
31
|
+
lineage merge upgrades (never drops) a content-verified marked bit.
|
|
32
|
+
- #342: the `run_workflow` lane gains the onAsk pass-through arm — every workflow-spawned agent's
|
|
33
|
+
own ask reaches the same operator as the host's (provenance-stamped, `parentToolCallId` +
|
|
34
|
+
`depth:1`) instead of the headless auto-deny. `RunWorkflowToolDeps.parentOnAsk` mount dep; a
|
|
35
|
+
deployment-pinned `base.onAsk` always wins; with no approver anywhere the fail-closed deny is
|
|
36
|
+
byte-identical. `AskRequest.delegation.parentToolCallId` is now reliable on the workflow lane.
|
|
37
|
+
- #341: `SideQuerySpec.getApiKeyAndHeaders` — side-queries resolve per-model auth exactly like
|
|
38
|
+
the main lane (a gateway key no longer rides an external model's URL when a resolver is wired).
|
|
39
|
+
- Memory-engine fencing (#262/#270/#191): journal recovery carries a fence token asserted at every
|
|
40
|
+
destructive write (redo/sweep/heal/commit-boundary/identity-close/migration); the chain-degraded
|
|
41
|
+
marker's directory entry is fsynced (POSIX name durability); txn-lock contention is typed
|
|
42
|
+
(EACCES vs EEXIST). #339 (stale-steal residual window) consolidated into design/186 v2 §3.1.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- The read-face LIST seats (`additionalDirectories`, `additionalReadDirectories`,
|
|
46
|
+
`readDenyPatterns` deps+spec) refuse non-array values loudly (`config.read_face_seat`) instead
|
|
47
|
+
of iterating a bare string character by character — `"/tmp/x"` no longer admits the
|
|
48
|
+
one-character root `"/"` into the write-capable allowlist, and a bare deny-"pattern" string no
|
|
49
|
+
longer dissolves into meaningless entries with zero refusal (#344, merged-code review). The
|
|
50
|
+
deny seats' ENTRY law stays with `compileReadDeny` (the `ReadDenyEntry` union's object form —
|
|
51
|
+
verify's frozen-posture carry — still completes).
|
|
52
|
+
|
|
53
|
+
### Errata (5.44.0 changelog)
|
|
54
|
+
- The "Tool contract revisions move: search 1→2, get 2→3" line overstated the rule — revisions
|
|
55
|
+
bump on execute-visible text changes, per the in-code note.
|
|
56
|
+
- The #318 scope line ("single explicit FILE target's own spelling") was narrower than the shipped
|
|
57
|
+
behavior; see the path-prefix handling in `search.ts` (`relOut`).
|
|
58
|
+
|
|
59
|
+
## 5.45.0 — 2026-08-19
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
- `RunnerDeps.memoryDelegationEvidence?: "static-face" | "attested-only"` (design/324, ruling-1
|
|
63
|
+
stopgap): the deployment's evidence standard for delegation-driven memory pollution.
|
|
64
|
+
`"attested-only"` retires ONLY the static capability-face mark (the "a child COULD reach
|
|
65
|
+
external content" over-approximation); the attestation-observed mark (`att === "external"`),
|
|
66
|
+
the chain's `incomplete` honesty, `recordExternal`, non-delegation classification and the
|
|
67
|
+
pre-call direct-class mark all stay. Key absent ≡ `"static-face"` ≡ byte-identical behavior.
|
|
68
|
+
A malformed value is refused loudly (`config.memory_delegation_evidence`, exact-spelling gate).
|
|
69
|
+
ACCEPTED COST (the deployment's to own): with `"attested-only"`, a BACKGROUND child's real
|
|
70
|
+
external contact does not mark this session — its content re-enters via TaskOutput /
|
|
71
|
+
task-notification / AgentTranscript, none of which carries an attestation.
|
|
72
|
+
- `memory.delegation_static_mark_waived` EngineNotice — mandatory companion: minted at most once
|
|
73
|
+
per prepared leg when the knob actually waives a static-face mark on a pollution-armed chain
|
|
74
|
+
(`detail: {subagentType?, sessionId?}`; reason carries the waived mark's own sentence, bounded).
|
|
75
|
+
- `TaskResult.effectiveReasoning?: ResolvedReasoning` (#327b): the leg's effective reasoning
|
|
76
|
+
resolution echoed on the result — same object the `reasoning.resolved` trace event carries
|
|
77
|
+
(two faces, one mint); rides the backstop/`resume.tool_unavailable` carrier like its
|
|
78
|
+
precedents. `ReasoningFormat` gains `"responses"` (dispatched by api family, single-source
|
|
79
|
+
`RESPONSES_APIS`).
|
|
80
|
+
- Cross-session recall pins (#260 verdict: engine clean): the consumer contract is pinned —
|
|
81
|
+
the model's final answer text exists BEFORE the terminal harvest commits, `TaskStream.result()`
|
|
82
|
+
is the commit boundary, and a consumer that prints the answer and kills the worker without
|
|
83
|
+
awaiting `result()` strands the entry id-less on disk (the incident's exact evidence shape).
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- The reasoning resolver mirrors `thinkingLevelMap`'s null arm (5.45 rescan): a clamped tier the
|
|
87
|
+
model maps to `null` is reported tier-not-honored (`graded:false`, intent echo) — matching what
|
|
88
|
+
the wire appliers actually send (nothing) instead of claiming "honored exactly"; the r3/r4
|
|
89
|
+
mirror comment now names all three arms. `resolveEffort` reads a malformed non-array
|
|
90
|
+
`reasoningEffortLevels` as undeclared (member-level filtering: garbage/sparse/"off" entries can
|
|
91
|
+
no longer become clamp targets) — a catalog typo no longer fails the whole thinking-on leg with
|
|
92
|
+
a raw TypeError (5.44.0 regression window closed before release).
|
|
93
|
+
- The #260 pins tell the bounded-teardown truth: `result()` settles after the harvest LEG settled
|
|
94
|
+
(committed, or its 15s timeout REPORTED — never silent), the discriminative arm polls 50 ticks
|
|
95
|
+
(the abandon arm sat inside the old 1-tick slack), and the incident-scale argument is stated.
|
|
96
|
+
- `reasoning.resolved` now fires a LOUD DROP frame (`dropped: true`, `effective: "off"`) on
|
|
97
|
+
models whose `Model.reasoning` is falsy — a user-requested effort tier no longer evaporates
|
|
98
|
+
silently (#325). The public `resolveReasoning` reports the drop arm for such models; the
|
|
99
|
+
capable arm is byte-identical. The capability predicate is truthiness (matching every adapter),
|
|
100
|
+
and `supportsReasoningEffort === false` mirrors the ungraded intent-only arm.
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
- The two 5.44.0 pollution notices are DISPOSITIONED in the governance registry and the
|
|
104
|
+
error-surface cap vocabulary gained `inlineUntrusted`/`pollutionSentence` (both bounded by
|
|
105
|
+
contract) — landed as gate-family debts caught by the full-suite leg.
|
|
106
|
+
|
|
107
|
+
### Notes
|
|
108
|
+
- Commit `2f78edb7` (the delegation-evidence knob) carries the `TaskResult.effectiveReasoning`
|
|
109
|
+
declaration hunk of the parallel effort car (shared-checkout staging race; HEAD is consistent).
|
|
110
|
+
Do not cherry-pick that commit alone.
|
|
111
|
+
|
|
3
112
|
## 5.44.0 — 2026-08-19
|
|
4
113
|
|
|
5
114
|
### Added
|
|
@@ -297,7 +297,9 @@ export declare function completedAgentCard(child: {
|
|
|
297
297
|
* recorder aggregate at delivery. Present only when the parent chain armed the recorder; the
|
|
298
298
|
* parent-side judgment (the content-origin wrap) reads it off this card — `clean` retires the
|
|
299
299
|
* static face verdict for this call, `external` marks with the observed-event reason, `unknown`
|
|
300
|
-
* (or absence) keeps the static floor
|
|
300
|
+
* (or absence) keeps the static floor, whose mark action follows the parent run's deployment
|
|
301
|
+
* evidence standard (design/324, `RunnerDeps.memoryDelegationEvidence`). Deliberate whitelist
|
|
302
|
+
* admission (see the boundary note). */
|
|
301
303
|
attestation?: import("../core/memory-engine/delegation-provenance.js").DelegationAttestation;
|
|
302
304
|
}): Record<string, unknown>;
|
|
303
305
|
/**
|
package/dist/agents/subagent.js
CHANGED
|
@@ -1915,7 +1915,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1915
1915
|
const wt = await finishWorktree();
|
|
1916
1916
|
return { isError: true, content: `Sub-agent not started: source session not found or fork unavailable.${wt ? `\n${wt}` : ""}`, details: { error: "source not found", forkLane: ctx.hostSessionFork ? "host" : "child-runner", ...(ctx.sessionId !== undefined ? { sourceSessionId: ctx.sessionId } : {}) } };
|
|
1917
1917
|
}
|
|
1918
|
-
const forkInternals = { ...childInternals, insideFork: true };
|
|
1918
|
+
const forkInternals = { ...childInternals, insideFork: true, ...(ctx.reminderMark !== undefined ? { reminderMark: ctx.reminderMark } : {}) };
|
|
1919
1919
|
stepRecorder.lockTo(forkedId);
|
|
1920
1920
|
const forkWorktreeNote = worktreeDir !== undefined ? forkWorktreeTranslationNote(ctx.parentCwd, worktreeDir) : undefined;
|
|
1921
1921
|
const forkObjective = `${FORK_DIRECTIVE_FRAME}${prompt}${forkWorktreeNote !== undefined ? `\n\n${forkWorktreeNote}` : ""}`;
|
|
@@ -34,7 +34,20 @@ export type ReasoningFormat = "openai" | "openrouter" | "deepseek" | "together"
|
|
|
34
34
|
/** Anthropic `output_config.effort` (CC 2.1.198 / Fable-5 class, design/119): the tier is a named
|
|
35
35
|
* effort string, clamped to `compat.effortLevels` — the anthropic-side twin of the OpenAI
|
|
36
36
|
* `reasoning_effort` path (thinking FORM is `thinkingMode`; effort carries the intensity). */
|
|
37
|
-
| "effort"
|
|
37
|
+
| "effort"
|
|
38
|
+
/** OpenAI Responses API (`openai-responses` / `azure-openai-responses`, design/163): the tier rides
|
|
39
|
+
* the NESTED `reasoning: { effort }` request field (open-responses.ts `resolveWireEffort`), clamped
|
|
40
|
+
* by the same {@link resolveEffort} against `compat.reasoningEffortLevels` — NOT the completions
|
|
41
|
+
* top-level `reasoning_effort` the `"openai"` tag names. #325 review r3: before this member the
|
|
42
|
+
* resolver fell through to the completions default and labeled Responses resolutions `"openai"`. */
|
|
43
|
+
| "responses";
|
|
44
|
+
/**
|
|
45
|
+
* The Open Responses API family — every `Api` value `createOpenResponsesBrain` serves (design/163).
|
|
46
|
+
* The SINGLE family discriminant: request-params.ts (reserved-key set selection) and
|
|
47
|
+
* {@link resolveReasoning} (format dispatch, #325 review r4) both key off THIS set, so a new
|
|
48
|
+
* Responses api value added here reaches every family-scoped behavior instead of one of two twins.
|
|
49
|
+
*/
|
|
50
|
+
export declare const RESPONSES_APIS: ReadonlySet<string>;
|
|
38
51
|
/** How a requested intensity resolved against one endpoint's real capability. */
|
|
39
52
|
export interface ReasoningResolution {
|
|
40
53
|
/** The level the caller asked for. */
|
|
@@ -43,9 +56,10 @@ export interface ReasoningResolution {
|
|
|
43
56
|
effective: ThinkingLevel;
|
|
44
57
|
/**
|
|
45
58
|
* True when the endpoint varies behavior by intensity (effort tiers / token budget). False = binary
|
|
46
|
-
* enable-only (qwen / zai / qwen-chat-template,
|
|
47
|
-
* `supportsReasoningEffort:false
|
|
48
|
-
*
|
|
59
|
+
* enable-only (qwen / zai / qwen-chat-template, an effort endpoint that declares
|
|
60
|
+
* `supportsReasoningEffort:false`, or a tier `Model.thinkingLevelMap` maps to `null` — the wire then
|
|
61
|
+
* carries NO effort value for it): thinking is on or off and the tier is NOT honored — read this
|
|
62
|
+
* alongside {@link effective}, which then echoes the request as intent only.
|
|
49
63
|
*/
|
|
50
64
|
graded: boolean;
|
|
51
65
|
/** True when {@link effective} differs from {@link requested} (the request couldn't be honored exactly). */
|
|
@@ -62,6 +76,23 @@ export interface ResolvedReasoning extends ReasoningResolution {
|
|
|
62
76
|
format: ReasoningFormat;
|
|
63
77
|
/** A coarse endpoint label for the trace (`model.api` — e.g. `openai-completions`, `anthropic-messages`). */
|
|
64
78
|
endpoint: string;
|
|
79
|
+
/**
|
|
80
|
+
* Present (true) only when the model declares NO reasoning capability (`Model.reasoning` falsy): every
|
|
81
|
+
* brain early-returns on that flag (openai.ts applyThinking / anthropic.ts / open-responses.ts
|
|
82
|
+
* resolveWireEffort), so NO thinking
|
|
83
|
+
* parameter reaches the wire at all — the requested tier is DROPPED entirely, not clamped or
|
|
84
|
+
* downgraded-to-binary. `effective:"off"` here states the ENGINE side of that fact (nothing was
|
|
85
|
+
* requested), NOT a measured gateway state: on the binary enable-only formats (qwen / zai /
|
|
86
|
+
* qwen-chat-template) parameter ABSENCE is provider-default, and a default-on gateway may still
|
|
87
|
+
* reason at its own tier — the brains deliberately send no disable key for a model declared
|
|
88
|
+
* non-reasoning, so the engine cannot claim a measured off there; `format` tells a consumer which
|
|
89
|
+
* family applies (on every other format, absence is the closest the wire has to off — see
|
|
90
|
+
* applyThinking's own contract). `format`/`endpoint` report the family that WOULD have carried the
|
|
91
|
+
* tier (the why-shape a consumer diagnoses with, not a claim about the request). Absent on every
|
|
92
|
+
* reasoning-capable resolution, so existing consumers see byte-identical objects. Loud-bad-values
|
|
93
|
+
* rule: the one arm where the request evaporates must not be the one arm with no report.
|
|
94
|
+
*/
|
|
95
|
+
dropped?: true;
|
|
65
96
|
}
|
|
66
97
|
/**
|
|
67
98
|
* Resolve a requested intensity against a model END-TO-END, dispatching on its wire format the SAME way the
|
|
@@ -74,6 +105,17 @@ export interface ResolvedReasoning extends ReasoningResolution {
|
|
|
74
105
|
* - Binary enable-only formats (qwen / zai / qwen-chat-template) → `graded:false` (tier not honored).
|
|
75
106
|
* - An effort endpoint with `supportsReasoningEffort:false` → `graded:false` (enable key only, no effort tier).
|
|
76
107
|
* - Otherwise effort-based → clamp DOWN to the endpoint's `reasoningEffortLevels` (default minimal|low|medium|high).
|
|
108
|
+
* - A CLAMPED tier that `model.thinkingLevelMap` maps to `null` (declared unsupported on this model) →
|
|
109
|
+
* `graded:false`: both effort-wire appliers key the map by the post-clamp tier and then carry NO effort
|
|
110
|
+
* value (thinking still enables via the format's own enable key where one exists), so the tier is not
|
|
111
|
+
* honored. Previously this resolver never read the map and reported such a request as exactly honored
|
|
112
|
+
* (`graded:true`, `clamped:false`) while the wire dropped the value — trace/result-face drift.
|
|
113
|
+
*
|
|
114
|
+
* - A model whose `reasoning` capability flag is FALSY drops the request ENTIRELY (neither brain emits any
|
|
115
|
+
* thinking parameter, whatever the format) → `effective:"off"`, `graded:false`, `clamped:true`,
|
|
116
|
+
* `dropped:true` — the loud-drop arm. Previously this resolver described the capability dispatch for such
|
|
117
|
+
* a model (a resolution the request never carried), and the runner's trace guard skipped the frame — the
|
|
118
|
+
* one arm where the request evaporates was the one arm with no disclosure.
|
|
77
119
|
*
|
|
78
120
|
* `off`/falsy never enables thinking, so it resolves trivially (no clamp, graded:true) — the caller decides
|
|
79
121
|
* whether to emit at all.
|
|
@@ -82,12 +124,16 @@ export declare function resolveReasoning(requested: ThinkingLevel, model: {
|
|
|
82
124
|
api?: string;
|
|
83
125
|
reasoning?: boolean;
|
|
84
126
|
compat?: unknown;
|
|
127
|
+
thinkingLevelMap?: Readonly<Partial<Record<ThinkingLevel, string | null>>>;
|
|
85
128
|
}): ResolvedReasoning;
|
|
86
129
|
/**
|
|
87
130
|
* Resolve a requested intensity for an effort-based endpoint (`reasoning_effort` / `reasoning.effort`). Picks
|
|
88
131
|
* the requested tier when supported; otherwise the highest supported tier ≤ requested (clamp DOWN, never
|
|
89
132
|
* silently up); if none is ≤ requested (the endpoint's set is entirely higher — unusual), the lowest supported
|
|
90
133
|
* tier. Never returns a value outside `allowed`, so the brain can't emit a tier the endpoint would 422.
|
|
134
|
+
* A malformed `allowed` reads as UNDECLARED → the conservative default set (non-array, empty, or no
|
|
135
|
+
* valid member); individually invalid MEMBERS (garbage strings, sparse holes, `"off"` — not a legal
|
|
136
|
+
* member of the 6-tier compat effort sets) are dropped, so they can never become a clamp target.
|
|
91
137
|
*/
|
|
92
138
|
export declare function resolveEffort(requested: ThinkingLevel, allowed?: readonly ThinkingLevel[]): ReasoningResolution;
|
|
93
139
|
/**
|
package/dist/brain/reasoning.js
CHANGED
|
@@ -15,7 +15,18 @@ export function rankOf(level) {
|
|
|
15
15
|
}
|
|
16
16
|
export const DEFAULT_EFFORT_LEVELS = ["minimal", "low", "medium", "high"];
|
|
17
17
|
const BINARY_FORMATS = new Set(["qwen", "zai", "qwen-chat-template"]);
|
|
18
|
+
export const RESPONSES_APIS = new Set(["openai-responses", "azure-openai-responses", "openai-chatgpt-responses"]);
|
|
18
19
|
export function resolveReasoning(requested, model) {
|
|
20
|
+
const resolved = dispatchReasoning(requested, model);
|
|
21
|
+
if (requested !== "off" && !model.reasoning) {
|
|
22
|
+
return { requested, effective: "off", graded: false, clamped: true, format: resolved.format, endpoint: resolved.endpoint, dropped: true };
|
|
23
|
+
}
|
|
24
|
+
return resolved;
|
|
25
|
+
}
|
|
26
|
+
function effortTierUnmapped(requested, effective, model) {
|
|
27
|
+
return requested !== "off" && model.thinkingLevelMap?.[effective] === null;
|
|
28
|
+
}
|
|
29
|
+
function dispatchReasoning(requested, model) {
|
|
19
30
|
const endpoint = model.api ?? "unknown";
|
|
20
31
|
const compat = (model.compat ?? {});
|
|
21
32
|
if (model.api === "anthropic-messages") {
|
|
@@ -24,6 +35,16 @@ export function resolveReasoning(requested, model) {
|
|
|
24
35
|
}
|
|
25
36
|
return { requested, effective: requested, graded: true, clamped: false, format: "budget", endpoint };
|
|
26
37
|
}
|
|
38
|
+
if (model.api !== undefined && RESPONSES_APIS.has(model.api)) {
|
|
39
|
+
if (compat.supportsReasoningEffort === false) {
|
|
40
|
+
return { requested, effective: requested, graded: false, clamped: false, format: "responses", endpoint };
|
|
41
|
+
}
|
|
42
|
+
const responsesResolved = resolveEffort(requested, compat.reasoningEffortLevels ?? DEFAULT_EFFORT_LEVELS);
|
|
43
|
+
if (effortTierUnmapped(requested, responsesResolved.effective, model)) {
|
|
44
|
+
return { requested, effective: requested, graded: false, clamped: false, format: "responses", endpoint };
|
|
45
|
+
}
|
|
46
|
+
return { ...responsesResolved, format: "responses", endpoint };
|
|
47
|
+
}
|
|
27
48
|
const format = compat.thinkingFormat ?? "openai";
|
|
28
49
|
if (BINARY_FORMATS.has(format)) {
|
|
29
50
|
return { ...resolveBinary(requested), format, endpoint };
|
|
@@ -32,15 +53,15 @@ export function resolveReasoning(requested, model) {
|
|
|
32
53
|
if (!supportsEffort && format !== "openrouter") {
|
|
33
54
|
return { requested, effective: requested, graded: false, clamped: false, format, endpoint };
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
format,
|
|
38
|
-
|
|
39
|
-
};
|
|
56
|
+
const resolved = resolveEffort(requested, compat.reasoningEffortLevels ?? DEFAULT_EFFORT_LEVELS);
|
|
57
|
+
if (effortTierUnmapped(requested, resolved.effective, model)) {
|
|
58
|
+
return { requested, effective: requested, graded: false, clamped: false, format, endpoint };
|
|
59
|
+
}
|
|
60
|
+
return { ...resolved, format, endpoint };
|
|
40
61
|
}
|
|
41
62
|
export function resolveEffort(requested, allowed = DEFAULT_EFFORT_LEVELS) {
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
const declared = Array.isArray(allowed) ? allowed.filter((lvl) => isThinkingLevel(lvl) && lvl !== "off") : [];
|
|
64
|
+
allowed = declared.length > 0 ? declared : DEFAULT_EFFORT_LEVELS;
|
|
44
65
|
if (requested === "off" || allowed.includes(requested)) {
|
|
45
66
|
return { requested, effective: requested, graded: true, clamped: false };
|
|
46
67
|
}
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Request-parameter passthrough (design/40). A deployment can set official provider params it wants but
|
|
3
|
-
* core doesn't model as first-class fields (OpenAI `frequency_penalty`/`presence_penalty`/`top_p`/`seed`/
|
|
4
|
-
* `logit_bias`; Anthropic `top_k`/`top_p`/`stop_sequences`; …) via `Model.extraBody`, and core merges them
|
|
5
|
-
* into the brain's request body. Anchored at the **Model** level (not per-turn) so the serialized body
|
|
6
|
-
* stays byte-stable across turns → the prefix cache holds (design/9/31).
|
|
7
|
-
*
|
|
8
|
-
* Symmetric: each brain owns its structural + tuning keys (`reserved`) and ALWAYS wins — `extraBody` can
|
|
9
|
-
* only supply the long tail, never clobber `messages`/`tools`/`temperature`/etc. Stripping is the safety
|
|
10
|
-
* enforcement (the brains are standalone-usable); `prepareTask` separately warns on a collision (it has
|
|
11
|
-
* `onError`, the brain doesn't — council design/40).
|
|
12
|
-
*/
|
|
13
1
|
/** OpenAI request-body keys core owns; `extraBody` cannot override these. */
|
|
14
2
|
export declare const OPENAI_RESERVED: ReadonlySet<string>;
|
|
15
3
|
/** Anthropic request-body keys core owns; `extraBody` cannot override these. (Note the asymmetry vs
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RESPONSES_APIS } from "./reasoning.js";
|
|
1
2
|
export const OPENAI_RESERVED = new Set([
|
|
2
3
|
"model",
|
|
3
4
|
"messages",
|
|
@@ -38,7 +39,6 @@ export const RESPONSES_RESERVED = new Set([
|
|
|
38
39
|
"previous_response_id",
|
|
39
40
|
"conversation",
|
|
40
41
|
]);
|
|
41
|
-
const RESPONSES_APIS = new Set(["openai-responses", "azure-openai-responses", "openai-chatgpt-responses"]);
|
|
42
42
|
export function reservedFor(api) {
|
|
43
43
|
if (RESPONSES_APIS.has(api))
|
|
44
44
|
return RESPONSES_RESERVED;
|
|
@@ -807,6 +807,18 @@ export interface CheckpointState {
|
|
|
807
807
|
* scope for any in-row invariant). Absent = the row was minted ungoverned. */
|
|
808
808
|
realApproval?: true;
|
|
809
809
|
};
|
|
810
|
+
/**
|
|
811
|
+
* design/319 (A ticket) — the session's reminder provenance mark
|
|
812
|
+
* ({@link import("./reminder-mint.js").mintReminderMark}), persisted so a durable resume keeps
|
|
813
|
+
* minting the SAME mark its transcript's engine reminders and system-prompt declaration carry
|
|
814
|
+
* ("resume reuses" — one declaration, one mark). Additive on the existing checkpoint version —
|
|
815
|
+
* a reader that ignores it is unchanged (the :646 precedent; no version bump). Resume goes
|
|
816
|
+
* through the VERIFY port ({@link import("./reminder-mint.js").isValidReminderMark}): a missing
|
|
817
|
+
* field (pre-319 row) or an unrecognized value-shape (corrupt / foreign / old-scheme after a
|
|
818
|
+
* scheme change) RE-MINTS instead of adopting — the declaration stays strict, and the historic
|
|
819
|
+
* legs' reminders simply read as data (fail-safe downgrade, design/319 a-2).
|
|
820
|
+
*/
|
|
821
|
+
reminderMark?: string;
|
|
810
822
|
/**
|
|
811
823
|
* design/78 Slice-1: the SAFE-tier self-repair loop's durable state (`failureTrace`/`diagnostics`/
|
|
812
824
|
* `rejectedHypotheses`/`attemptCount`/`oracleTier` — all JSON/`structuredClone`-safe, no fn/Date). Set ONLY
|
|
@@ -16,6 +16,7 @@ export const NON_GOVERNANCE_MEMORY_CODES = new Set([
|
|
|
16
16
|
"memory.pollution_mark_failed",
|
|
17
17
|
"memory.session_polluted",
|
|
18
18
|
"memory.harvest_quarantined",
|
|
19
|
+
"memory.delegation_static_mark_waived",
|
|
19
20
|
"memory.challenge_sweep_failed",
|
|
20
21
|
"memory.lineage_settle_failed",
|
|
21
22
|
"memory.challenge_ledger_corrupt",
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -583,8 +583,11 @@ export interface UserPromptSubmitResult {
|
|
|
583
583
|
* NOTE (council design/74 #6): this does NOT escape a literal `</system-reminder>` in `text` — callers MUST
|
|
584
584
|
* pass trusted, first-party strings (every current caller does: fixed gate/limit messages). If a future
|
|
585
585
|
* caller needs to relay UNTRUSTED content (tool output, user data), it must sanitize the close tag first
|
|
586
|
-
* (or use the `delimitUntrusted` fence), or a crafted payload could break out of the reminder framing.
|
|
587
|
-
|
|
586
|
+
* (or use the `delimitUntrusted` fence), or a crafted payload could break out of the reminder framing.
|
|
587
|
+
* design/319 (A ticket): `mark` is the session's reminder provenance mark — run-scoped callers thread it
|
|
588
|
+
* so the open tag carries the value the system-prompt declaration names (rendered by the mint home; the
|
|
589
|
+
* body is byte-untouched). Absent ⇒ the historic bare open tag (a caller outside a run). */
|
|
590
|
+
export declare function formatHookFeedback(text: string, mark?: string): string;
|
|
588
591
|
/** The outcome of the two-phase tool gate, mapped onto the harness `tool_call` hook return shape. */
|
|
589
592
|
export interface ToolGateResult {
|
|
590
593
|
/** Block execution (the loop emits an error tool result with `reason`). */
|
|
@@ -799,6 +802,10 @@ export interface ToolGateInput {
|
|
|
799
802
|
* per-leg mint; a host driving the gate directly may omit it, and the contexts then carry no
|
|
800
803
|
* identity (honest absence — this layer never fabricates a leg). */
|
|
801
804
|
identity?: HookInvocationIdentity;
|
|
805
|
+
/** design/319 (A ticket) — the run's reminder provenance mark: the gate's own model-facing deny
|
|
806
|
+
* reasons ride `formatHookFeedback`, so their open tags must carry the same mark the run's
|
|
807
|
+
* system-prompt declaration names. Absent (a host driving the gate directly) ⇒ bare tags. */
|
|
808
|
+
reminderMark?: string;
|
|
802
809
|
preToolUse?: Hooks["preToolUse"];
|
|
803
810
|
/** The read-only env capability face put on every {@link HookToolContext} this gate call
|
|
804
811
|
* builds ({@link HookEnvCapabilities}). Built ONCE per task by the runner (after the env is minted) and
|
package/dist/core/hooks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { decisionText, describeThrown, refuseOutOfContractDecision } from "./tool-policy.js";
|
|
2
2
|
import { brandPolicyAskClass } from "./ask-class.js";
|
|
3
3
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
4
|
+
import { mintSystemReminder } from "./reminder-mint.js";
|
|
4
5
|
import { PROBE_REASON_MAX, normalizeProbeCause } from "./checkpoint-store.js";
|
|
5
6
|
import { ASK_USER_QUESTION_TOOL_NAME } from "./ask-question.js";
|
|
6
7
|
import { createSafeNotifier } from "./safe-notify.js";
|
|
@@ -90,8 +91,8 @@ export function mintHookInvocationIdentity(facts) {
|
|
|
90
91
|
...(facts.parentToolCallId !== undefined ? { parentToolCallId: facts.parentToolCallId } : {}),
|
|
91
92
|
}));
|
|
92
93
|
}
|
|
93
|
-
export function formatHookFeedback(text) {
|
|
94
|
-
return
|
|
94
|
+
export function formatHookFeedback(text, mark) {
|
|
95
|
+
return mintSystemReminder(text, mark);
|
|
95
96
|
}
|
|
96
97
|
const PARK_FAILURE_CAUSE_MAX = 600;
|
|
97
98
|
function withParkFailureCause(reason, parkFailed) {
|
|
@@ -304,7 +305,7 @@ export async function runToolGate(input) {
|
|
|
304
305
|
if (input.permissionDenied) {
|
|
305
306
|
await notifier.notifyAsync(() => input.permissionDenied?.({ toolName, input: cloneObserverInput(currentInput), toolCallId, reason, source: "hook", ...(input.identity !== undefined ? { identity: input.identity } : {}) }), "toolGate.permissionDenied");
|
|
306
307
|
}
|
|
307
|
-
return { block: true, reason: formatHookFeedback(reason), preToolContext };
|
|
308
|
+
return { block: true, reason: formatHookFeedback(reason, input.reminderMark), preToolContext };
|
|
308
309
|
}
|
|
309
310
|
if (r) {
|
|
310
311
|
if (r.additionalContext) {
|
|
@@ -313,7 +314,7 @@ export async function runToolGate(input) {
|
|
|
313
314
|
if (r.action === "deny") {
|
|
314
315
|
return {
|
|
315
316
|
block: true,
|
|
316
|
-
reason: formatHookFeedback(decisionText(r) ?? `tool "${toolName}" blocked by a PreToolUse hook
|
|
317
|
+
reason: formatHookFeedback(decisionText(r) ?? `tool "${toolName}" blocked by a PreToolUse hook`, input.reminderMark),
|
|
317
318
|
preToolContext,
|
|
318
319
|
};
|
|
319
320
|
}
|
|
@@ -813,7 +814,7 @@ export async function runToolGate(input) {
|
|
|
813
814
|
const denyApprover = denySettledBy !== undefined ? resolvedApprover : undefined;
|
|
814
815
|
return {
|
|
815
816
|
block: true,
|
|
816
|
-
reason: formatHookFeedback(denyReason),
|
|
817
|
+
reason: formatHookFeedback(denyReason, input.reminderMark),
|
|
817
818
|
...(denySettledBy !== undefined ? { settledBy: denySettledBy } : {}),
|
|
818
819
|
...(denyApprover !== undefined ? { approver: denyApprover } : {}),
|
|
819
820
|
preToolContext,
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* into long-term memory? Three tiers:
|
|
7
7
|
*
|
|
8
8
|
* - "external" — network-retrieval families and external protocol channels. Invocation marks the
|
|
9
|
-
* session's memory
|
|
9
|
+
* session's memory EXTERNALLY EXPOSED (one-way, durable; what the mark does at
|
|
10
|
+
* harvest is the provenance mode's question — design/336: tag-admit with an origin
|
|
11
|
+
* marker under "carry", full quarantine under "off").
|
|
10
12
|
* - "execution" — general execution (shell/exec). A NAMED residual channel: it can reach external
|
|
11
13
|
* content indirectly, but the two industry anchors this tier is calibrated against
|
|
12
14
|
* gate on network/protocol families only, and excluding every shell session would
|
|
@@ -66,9 +68,12 @@ export interface AgentPoolTool {
|
|
|
66
68
|
* A child that can reach a network or protocol tool returns content from outside the trust boundary
|
|
67
69
|
* just as surely as calling that tool in this session would, and the parent's transcript is where it
|
|
68
70
|
* lands. The judgment is STATIC — the child's face is known from its definition before the call — so
|
|
69
|
-
* nothing here tracks what the child actually did (runtime provenance is
|
|
70
|
-
* it conservative in the safe direction: a child that COULD reach external content counts as
|
|
71
|
-
* done so.
|
|
71
|
+
* nothing here tracks what the child actually did (runtime provenance is design/180 half A). That
|
|
72
|
+
* makes it conservative in the safe direction: a child that COULD reach external content counts as
|
|
73
|
+
* having done so. Whether this verdict MARKS the judging session's memory when no attestation
|
|
74
|
+
* arrives is the deployment's evidence standard (design/324,
|
|
75
|
+
* `RunnerDeps.memoryDelegationEvidence`) — the classification itself is value-independent, and the
|
|
76
|
+
* wrap computes it on every standard (the waiver announcement keys on it).
|
|
72
77
|
*
|
|
73
78
|
* `pool` is the CHILD'S pool, not the parent's roster: a delegation tool carries its own tool set, and
|
|
74
79
|
* the two are explicitly allowed to differ — a parent mounting nothing but the delegation tool can
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* design/180 half A — delegation RUNTIME provenance: the three-value attestation and its monotonic
|
|
3
3
|
* aggregate. The static tool-face judgment ({@link import("./content-origin.js").delegationCallIsExternal})
|
|
4
|
-
* stays the
|
|
5
|
-
* clean proof is in hand; any evidence gap falls back to the floor. "We could not
|
|
6
|
-
* and "it is clean" are different answers — replacing the floor outright would fail
|
|
7
|
-
* evidence gap (old child vintages, crashes, un-armed sessions).
|
|
4
|
+
* stays the FLOOR of the attestation reduction: runtime evidence may only retire the static verdict
|
|
5
|
+
* when a COMPLETE clean proof is in hand; any evidence gap falls back to the floor. "We could not
|
|
6
|
+
* prove it clean" and "it is clean" are different answers — replacing the floor outright would fail
|
|
7
|
+
* OPEN on every evidence gap (old child vintages, crashes, un-armed sessions). Whether the floor's
|
|
8
|
+
* verdict MARKS the judging session's memory is a separate, deployment-governed question
|
|
9
|
+
* (design/324, `RunnerDeps.memoryDelegationEvidence`): the reduction semantics here never change
|
|
10
|
+
* with that key — only the mark action at the judging run's wrap does.
|
|
8
11
|
*
|
|
9
12
|
* The aggregate is two MONOTONIC bits plus versioning:
|
|
10
13
|
* - `sawExternal` — an actually-observed polluting-class invocation (own recorder, or a delivered
|
|
@@ -17,8 +20,9 @@
|
|
|
17
20
|
* "clean" unprovable; it can never make an observed event disappear).
|
|
18
21
|
*/
|
|
19
22
|
/** The three-value delegation attestation. Only `clean` changes behavior (the parent skips the
|
|
20
|
-
* static mark); `unknown` is byte-equivalent to absence (static floor governs
|
|
21
|
-
*
|
|
23
|
+
* static mark); `unknown` is byte-equivalent to absence (the static floor governs — its mark
|
|
24
|
+
* action subject to the judging run's deployment evidence standard, design/324); `external` marks
|
|
25
|
+
* unconditionally on every evidence standard, with the observed-event reason. */
|
|
22
26
|
export type DelegationAttestation = "clean" | "external" | "unknown";
|
|
23
27
|
/** The monotonic per-run provenance aggregate (in-process this slice; the durable-checkpoint
|
|
24
28
|
* persistence leg rides the background-face slice — absence after a resume reads `incomplete`,
|
|
@@ -58,5 +62,6 @@ export declare function reduceDelegationAttestation(agg: DelegationProvenanceAgg
|
|
|
58
62
|
completed: boolean;
|
|
59
63
|
}): DelegationAttestation;
|
|
60
64
|
/** Read a delivered card's attestation field (the explicit-whitelist handback seat). Anything but
|
|
61
|
-
* the three spellings — including absence — reads as undefined, i.e. the static floor governs
|
|
65
|
+
* the three spellings — including absence — reads as undefined, i.e. the static floor governs
|
|
66
|
+
* (whether that floor MARKS is the judging run's deployment evidence standard, design/324). */
|
|
62
67
|
export declare function readCardAttestation(details: unknown): DelegationAttestation | undefined;
|
|
@@ -114,6 +114,29 @@ export interface MemoryEngineOptions {
|
|
|
114
114
|
maxFiles?: number;
|
|
115
115
|
/** Max nesting depth under the writable root (default {@link DEFAULT_MAX_ENTRY_DEPTH}). */
|
|
116
116
|
maxDepth?: number;
|
|
117
|
+
/**
|
|
118
|
+
* design/336 §13-3 — the memory-provenance master switch (the deployment threads
|
|
119
|
+
* {@link import("../types.js").RunnerDeps.memoryProvenance} here; engine-direct hosts set it
|
|
120
|
+
* directly). Two exact spellings:
|
|
121
|
+
* - `"carry"` (absent ≡ this; the default) — the 336 posture: an externally-exposed session's
|
|
122
|
+
* ORDINARY memory writes commit WITH an engine-minted `origin` marker (tag-admit; the content
|
|
123
|
+
* scan gates still run in full — a marker is not an exemption), instruction-form files
|
|
124
|
+
* (type: feedback / v2 injection hints) are withheld and quarantined (the narrow `polluted`
|
|
125
|
+
* rejection), the derived index's session prose still rolls back to its materialize-time
|
|
126
|
+
* baseline, and exposure carries the SAME file-count cap and harvest budget as a clean
|
|
127
|
+
* session;
|
|
128
|
+
* - `"off"` — the pre-336 behavior: an exposed session's harvest admits nothing (every changed/
|
|
129
|
+
* new file quarantines, host-auditable), no origin marker is ever MINTED, and the read side
|
|
130
|
+
* mounts no origin decorations. One stated deviation from the byte level: origin-form
|
|
131
|
+
* frontmatter written by the model is STILL stripped-with-warning from what commits (the
|
|
132
|
+
* origin seat is engine-authored in both modes — preserving model-authored marker bytes under
|
|
133
|
+
* "off" would hand a forgeable typed field to the next "carry" period), and an EXISTING
|
|
134
|
+
* committed origin still carries forward on edits (the backend's immutability law is
|
|
135
|
+
* structural and mode-independent — "off" stops minting, it never strips standing facts).
|
|
136
|
+
* Any other value refuses loudly at construction (`config.memory_provenance`, #123 — exact
|
|
137
|
+
* spellings, never truthiness).
|
|
138
|
+
*/
|
|
139
|
+
provenance?: "off" | "carry";
|
|
117
140
|
/** M7 — harvest deadline in ms (default {@link DEFAULT_HARVEST_DEADLINE_MS}); over-deadline files
|
|
118
141
|
* are deferred to the next harvest (report.degraded), never dropped. */
|
|
119
142
|
harvestDeadlineMs?: number;
|
|
@@ -264,8 +287,13 @@ export interface EntryProvenanceAccount {
|
|
|
264
287
|
* writing memory files, the harvest quarantined every one of them, and nothing said so while a host
|
|
265
288
|
* could still act on it.
|
|
266
289
|
*
|
|
290
|
+
* MODE-AWARE MESSAGE (design/336): the wording follows the engine's provenance mode — under
|
|
291
|
+
* `"carry"` (default) it states the tag-admit semantics (ordinary writes commit with an origin
|
|
292
|
+
* marker; instruction-form files are withheld); the `"off"`/absent spelling keeps the pre-336 text
|
|
293
|
+
* byte-identical. The caller passes the mode it constructed the engine with.
|
|
294
|
+
*
|
|
267
295
|
* SCOPE OF THE CLAIM (adversarial-review round 1, P1 — the first cut PROMISED quarantine on every mark).
|
|
268
|
-
* This notice states the VERDICT, not the mechanism's outcome: "no longer eligible", plus what a harvest
|
|
296
|
+
* This notice states the VERDICT, not the mechanism's outcome: "no longer eligible" (its "off" form), plus what a harvest
|
|
269
297
|
* does WHEN IT COLLECTS. That distinction is load-bearing, because several real paths reach a polluted
|
|
270
298
|
* harvest that quarantines nothing — a `writeScope`-null layering and a declared-unavailable session
|
|
271
299
|
* both take `harvestCore`'s zero-admission return (nothing is collected, so nothing is captured), and a
|
|
@@ -290,6 +318,7 @@ export interface EntryProvenanceAccount {
|
|
|
290
318
|
export declare function memorySessionPollutedNotice(input: {
|
|
291
319
|
reason: string;
|
|
292
320
|
sessionId?: string;
|
|
321
|
+
provenance?: "off" | "carry";
|
|
293
322
|
}): EngineNotice;
|
|
294
323
|
/**
|
|
295
324
|
* The polluted harvest's loud half ({@link EngineNotice}): what the containment actually did, minted
|
|
@@ -340,6 +369,7 @@ export declare function memoryHarvestQuarantinedNotice(input: {
|
|
|
340
369
|
escalated: number;
|
|
341
370
|
reason?: string;
|
|
342
371
|
sessionId?: string;
|
|
372
|
+
provenance?: "off" | "carry";
|
|
343
373
|
}): EngineNotice;
|
|
344
374
|
export declare class MemoryEngine {
|
|
345
375
|
private readonly backend;
|
|
@@ -352,6 +382,8 @@ export declare class MemoryEngine {
|
|
|
352
382
|
private readonly maxDepth;
|
|
353
383
|
private readonly harvestDeadlineMs;
|
|
354
384
|
private readonly harvestFileBudget;
|
|
385
|
+
/** {@link MemoryEngineOptions.provenance} — the design/336 master switch (screened at construction). */
|
|
386
|
+
private readonly provenance;
|
|
355
387
|
/** {@link MemoryEngineOptions.onIncident} — the advisory disclosure seat (partition + announce codes). */
|
|
356
388
|
private readonly onIncident;
|
|
357
389
|
/** A backend-pinned directory (FileBackend zero-copy detection). */
|
|
@@ -394,7 +426,7 @@ export declare class MemoryEngine {
|
|
|
394
426
|
* markers of one session would both read clean and both announce). Callers that do not disclose
|
|
395
427
|
* ignore it exactly as before.
|
|
396
428
|
*/
|
|
397
|
-
markSessionPolluted(sessionId: string, reason: string): SessionPollutionMarkOutcome;
|
|
429
|
+
markSessionPolluted(sessionId: string, reason: string, cause?: import("./types.js").MemoryOriginCause): SessionPollutionMarkOutcome;
|
|
398
430
|
/** The session's pollution record (in-process mark first, then the durable marker) — undefined =
|
|
399
431
|
* clean. Reads are side-effect-free. */
|
|
400
432
|
sessionPollution(sessionId: string): SessionPollutionRecord | undefined;
|
|
@@ -587,6 +619,7 @@ export declare class MemoryEngine {
|
|
|
587
619
|
}): Promise<MemorySessionHandle>;
|
|
588
620
|
inject(handle: MemorySessionHandle, opts?: {
|
|
589
621
|
writeToolMounted?: boolean;
|
|
622
|
+
reminderMark?: string;
|
|
590
623
|
}): MemoryInjection;
|
|
591
624
|
/**
|
|
592
625
|
* design/138 S2-C — gate ONE tool write (Write/Edit/NotebookEdit) against the memory domain.
|
|
@@ -617,6 +650,7 @@ export declare class MemoryEngine {
|
|
|
617
650
|
harvest(handle: MemorySessionHandle, opts?: {
|
|
618
651
|
polluted?: {
|
|
619
652
|
reason: string;
|
|
653
|
+
cause?: import("./types.js").MemoryOriginCause;
|
|
620
654
|
};
|
|
621
655
|
sessionId?: string;
|
|
622
656
|
admitNothing?: {
|
|
@@ -652,6 +686,19 @@ export declare class MemoryEngine {
|
|
|
652
686
|
* the deleted disk file WAS the backend's storage). Zero-copy skips getByIds: its read-side scan
|
|
653
687
|
* cannot see a deleted file, and calling it mid-harvest would sync-adopt in-session edits. */
|
|
654
688
|
private committedContentFor;
|
|
689
|
+
/**
|
|
690
|
+
* design/336 §2.2 (r4-9) — the COMMITTED frontmatter an origin carry-forward is computed against.
|
|
691
|
+
* Deliberately NOT {@link committedContentFor}'s non-zero-copy leg: that one calls the backend's
|
|
692
|
+
* plain `getByIds`, and the File backend's plain read path ADOPTS out-of-band disk changes (a
|
|
693
|
+
* read-side sync that advances revs and books retrievals) — a carry-forward read must be
|
|
694
|
+
* side-effect-free, or assembling one harvest mutates the store it is reading. Reading order:
|
|
695
|
+
* - zero-copy File: the control-plane shadow IS the committed state (the disk file is the
|
|
696
|
+
* session's own working copy — exactly what must NOT serve as the committed baseline);
|
|
697
|
+
* - copy-out with a `retrievalView` face (File): the lock-free non-adopting view;
|
|
698
|
+
* - twins (Pg/TiDB shapes): plain `getByIds` — naturally committed-state and side-effect-free
|
|
699
|
+
* (the contract clause on {@link MemoryBackend.getByIds}).
|
|
700
|
+
*/
|
|
701
|
+
private committedFrontmatterFor;
|
|
655
702
|
/** L4/C-F9 (S2-0) — capture a gate-rejected file into control-plane quarantine FROM THE SCANNED
|
|
656
703
|
* CONTENT (never re-reading the path: the disk bytes may have been swapped since the scan —
|
|
657
704
|
* TOCTOU), then remove the suspect from the model-visible plane (delete, falling back to clearing
|
|
@@ -712,6 +759,6 @@ export declare class MemoryEngine {
|
|
|
712
759
|
}
|
|
713
760
|
/** Render drained announcements as ONE engine-owned system-reminder block (the lead/frame is
|
|
714
761
|
* trusted; item identifiers were neutralized at enqueue — see {@link MemoryAnnouncement.items}). */
|
|
715
|
-
export declare function renderAnnouncements(queue: readonly MemoryAnnouncement[], folded: number): string;
|
|
762
|
+
export declare function renderAnnouncements(queue: readonly MemoryAnnouncement[], folded: number, reminderMark?: string): string;
|
|
716
763
|
/** Truncate the derived index to the CC injection parameters (200 lines / 25KB). */
|
|
717
764
|
export declare function truncateIndex(text: string): string;
|