@sema-agent/core 5.31.0 → 5.32.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 +33 -0
- package/dist/core/runner/prepare-task.d.ts +2 -0
- package/dist/core/runner/prepare-task.js +20 -1
- package/dist/core/types.d.ts +10 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/fs/index.d.ts +7 -3
- package/dist/tools/fs/index.js +3 -1
- package/dist/tools/fs/read-face.d.ts +21 -0
- package/dist/tools/fs/read-face.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.32.0 — 2026-08-14
|
|
4
|
+
|
|
5
|
+
No BREAKING changes. One additive disclosure surface; zero resolution-result changes (the read-face
|
|
6
|
+
clamp VERDICT is unchanged since 5.30.0 — only its occurrence is now announced).
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `config.read_face_deployment_clamped` EngineNotice family (#237): a deployment-wide
|
|
11
|
+
`readFace: "open"` beside a read-only (verifier) mount clamps to `"roots"` (stricter-wins);
|
|
12
|
+
the clamp's occurrence is now announced instead of silent. The trigger fires only when the
|
|
13
|
+
deployment seat was the deciding one — an explicit task-level `"roots"` reaches the same value
|
|
14
|
+
with or without the mount (no false-cause claim), and the task-seat `"open"` contradiction still
|
|
15
|
+
refuses loudly. Delivery: the prepare station announces once per `onNotice` SINK (the listener,
|
|
16
|
+
not the deps object — the Runner rebuilds its deps and `runTask` builds a Runner per call, so a
|
|
17
|
+
deps-keyed ledger would repeat per task; unwired console arm latches once per process); a
|
|
18
|
+
library-direct `createHandsToolkit({ readFace: "open", readOnly: true })` announces at toolkit
|
|
19
|
+
creation through its own `HandsToolkitOptions.onNotice` seat (absent ⇒ the same line via
|
|
20
|
+
`console.warn`). `detail: { seat, declared, inForce, cause }`. New export:
|
|
21
|
+
`deploymentReadFaceClampNotice` (the one shared notice builder).
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `HandsToolkitOptions.readFace` JSDoc (ships in the `.d.ts`) stopped promising a loud refusal
|
|
26
|
+
beside `readOnly: true` — stale since the 96ef89d seat distinction (5.30.0): the deps-shaped
|
|
27
|
+
seat clamps and announces; the loud refusal belongs to `TaskSpec.readFace`. Wording only, two
|
|
28
|
+
sites; the runtime behavior was already as now documented.
|
|
29
|
+
|
|
30
|
+
### Docs
|
|
31
|
+
|
|
32
|
+
- The recursive-verb table (`RECURSIVE_READ_FORMS`) declares its KNOWN OPEN SET with the standing
|
|
33
|
+
sweep obligation, and `StripSurvey` states consumer guidance for truncated samples (the two
|
|
34
|
+
5.31.0 review deductions; wording only).
|
|
35
|
+
|
|
3
36
|
## 5.31.0 — 2026-08-14
|
|
4
37
|
|
|
5
38
|
No API-BREAKING changes. Behavior narrowings are all tighten-direction (auto-allow → ask, or
|
|
@@ -30,6 +30,8 @@ import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskLimits, TaskResult, T
|
|
|
30
30
|
import type { RepairBundle } from "../../agents/repair-loop.js";
|
|
31
31
|
/** Test seam (mirrors `__resetBashTimeoutAnnouncements`): never called by production code. */
|
|
32
32
|
export declare function __resetMaterializeEnvAnnouncements(): void;
|
|
33
|
+
/** Test seam (mirrors `__resetMalformedNoticeSeatAnnouncement`): never called by production code. */
|
|
34
|
+
export declare function __resetReadFaceClampAnnouncement(): void;
|
|
33
35
|
/**
|
|
34
36
|
* design/164 — validate `TaskSpec.limits` at the door and return it unchanged.
|
|
35
37
|
*
|
|
@@ -74,7 +74,7 @@ import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.j
|
|
|
74
74
|
import { createTaskOutputTool, createTaskStopTool, defaultTaskRegistry } from "../task-registry.js";
|
|
75
75
|
import { createMonitorTool } from "../../tools/monitor.js";
|
|
76
76
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
77
|
-
import { applyCompactionToReadFileState, assertReadFaceValue, bashReversibilityProbe, compileReadDeny, createHandsToolkit, isReadDedupStubResult, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
77
|
+
import { applyCompactionToReadFileState, assertReadFaceValue, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, isReadDedupStubResult, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
78
78
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
79
79
|
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
|
|
80
80
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
@@ -97,6 +97,11 @@ export function __resetMaterializeEnvAnnouncements() {
|
|
|
97
97
|
function emitMaterializeEnvNotice(onNotice, message, detail) {
|
|
98
98
|
deliverEngineNotice(onNotice, { code: "config.materialize_env_discarded", message, detail });
|
|
99
99
|
}
|
|
100
|
+
const readFaceClampAnnouncedSinks = new WeakSet();
|
|
101
|
+
let readFaceClampConsoleAnnounced = false;
|
|
102
|
+
export function __resetReadFaceClampAnnouncement() {
|
|
103
|
+
readFaceClampConsoleAnnounced = false;
|
|
104
|
+
}
|
|
100
105
|
const RECONCILE_MAX_RETRIES = 3;
|
|
101
106
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
102
107
|
const TASK_LIMIT_KEY_DICT = {
|
|
@@ -1692,6 +1697,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1692
1697
|
readOnlyMount: handsReadOnly,
|
|
1693
1698
|
orgGoverned: deps.permissionRuleOrg !== undefined,
|
|
1694
1699
|
fullShellReachable,
|
|
1700
|
+
onDeploymentClamp: () => {
|
|
1701
|
+
const sink = deps.onNotice;
|
|
1702
|
+
if (typeof sink === "function") {
|
|
1703
|
+
if (readFaceClampAnnouncedSinks.has(sink))
|
|
1704
|
+
return;
|
|
1705
|
+
readFaceClampAnnouncedSinks.add(sink);
|
|
1706
|
+
}
|
|
1707
|
+
else {
|
|
1708
|
+
if (readFaceClampConsoleAnnounced)
|
|
1709
|
+
return;
|
|
1710
|
+
readFaceClampConsoleAnnounced = true;
|
|
1711
|
+
}
|
|
1712
|
+
deliverEngineNotice(sink, deploymentReadFaceClampNotice());
|
|
1713
|
+
},
|
|
1695
1714
|
});
|
|
1696
1715
|
if (resume !== undefined && (seedReadFaceSection === undefined || seedReadFaceSection.face === "roots"))
|
|
1697
1716
|
liveReadFace = "roots";
|
package/dist/core/types.d.ts
CHANGED
|
@@ -4230,7 +4230,16 @@ export interface EngineNotice {
|
|
|
4230
4230
|
* baseline (the strip is not a refusal in this window), which is why the drop is announced;
|
|
4231
4231
|
* `detail: { total, stripped: [{ key, reason }], omitted? }`, the rendered key list bounded in count
|
|
4232
4232
|
* and length because the names come from the untrusted script. One aggregated notice per governed
|
|
4233
|
-
* child build, not de-duplicated across builds: each spec is a distinct fact.
|
|
4233
|
+
* child build, not de-duplicated across builds: each spec is a distinct fact.
|
|
4234
|
+
* - `"config.read_face_deployment_clamped"` (#237) — a deployment-wide `readFace: "open"` is not
|
|
4235
|
+
* in force beside a read-only (verifier) mount: it clamps to "roots" without throwing
|
|
4236
|
+
* (stricter-wins; the clamp verdict stands, only its occurrence was undisclosed). Announced
|
|
4237
|
+
* once per `onNotice` SINK at the prepare station (the verifier lane would otherwise repeat it
|
|
4238
|
+
* every spawn; the sink — not the deps object — is the unit because the Runner rebuilds its
|
|
4239
|
+
* deps and `runTask` builds a Runner per call), unwired console arm once per process;
|
|
4240
|
+
* a library-direct `createHandsToolkit` mount announces at toolkit creation (one per mount,
|
|
4241
|
+
* through the band-local `HandsToolkitOptions.onNotice` seat, absent ⇒ `console.warn`);
|
|
4242
|
+
* `detail: { seat, declared, inForce, cause }`. */
|
|
4234
4243
|
code: string;
|
|
4235
4244
|
/** The exact human-readable line the unwired build prints via `console.warn` — same words, one text. */
|
|
4236
4245
|
message: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW,
|
|
|
88
88
|
export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
|
|
89
89
|
export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
|
|
90
90
|
export { READ_FACE_DEFAULT_DENY_ENTRIES, compileReadDeny, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, } from "./tools/fs/index.js";
|
|
91
|
-
export { resolveReadFace, type ReadFace, type ReadFaceInputs } from "./tools/fs/index.js";
|
|
91
|
+
export { deploymentReadFaceClampNotice, resolveReadFace, type ReadFace, type ReadFaceInputs } from "./tools/fs/index.js";
|
|
92
92
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, type ToolResultDeletionReport, } from "./core/tool-result-store.js";
|
|
93
93
|
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, } from "./core/checkpoint-store.js";
|
|
94
94
|
export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW,
|
|
|
69
69
|
export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
|
|
70
70
|
export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
|
|
71
71
|
export { READ_FACE_DEFAULT_DENY_ENTRIES, compileReadDeny, } from "./tools/fs/index.js";
|
|
72
|
-
export { resolveReadFace } from "./tools/fs/index.js";
|
|
72
|
+
export { deploymentReadFaceClampNotice, resolveReadFace } from "./tools/fs/index.js";
|
|
73
73
|
export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
|
|
74
74
|
export { InMemoryCheckpointStore, CheckpointError, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, } from "./core/checkpoint-store.js";
|
|
75
75
|
export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type BeforeWriteHook } from "../../core/types.js";
|
|
3
3
|
import { type TaskRegistry } from "../../core/task-registry.js";
|
|
4
4
|
import { type ReadFileState } from "./safety.js";
|
|
5
5
|
import type { PdfModelCapabilities } from "./pdf.js";
|
|
@@ -130,8 +130,12 @@ export interface HandsToolkitOptions {
|
|
|
130
130
|
* {@link import("./read-face.js").resolveReadFace} order prepare-task uses). Absent ⇒ the
|
|
131
131
|
* resolution order's default = "roots" (D-1b: the engine never opens implicitly). "open" skips
|
|
132
132
|
* ONLY the roots containment judgment — the deny set, the UNC out-of-set refusal and the
|
|
133
|
-
* special-file type gates run in both faces (§2.0).
|
|
134
|
-
* (
|
|
133
|
+
* special-file type gates run in both faces (§2.0). Beside `readOnly: true` the mount wins:
|
|
134
|
+
* this seat is deps-shaped (a deployment default, not a per-call assertion), so its "open"
|
|
135
|
+
* CLAMPS to "roots" and the clamp is ANNOUNCED (`config.read_face_deployment_clamped`, through
|
|
136
|
+
* `onNotice` / `console.warn`) — the loud refusal belongs to the TASK seat (`TaskSpec.readFace`),
|
|
137
|
+
* which is the one genuine per-call contradiction (#237; stale "refused loudly" wording here
|
|
138
|
+
* predated the 96ef89d seat distinction). Never affects the write faces. */
|
|
135
139
|
readFace?: "open" | "roots";
|
|
136
140
|
}
|
|
137
141
|
/**
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deliverEngineNotice } from "../../core/types.js";
|
|
1
2
|
import { createTaskOutputTool, createTaskStopTool } from "../../core/task-registry.js";
|
|
2
3
|
import { hasBackgroundShell } from "../../core/background-shell.js";
|
|
3
4
|
import {} from "./safety.js";
|
|
@@ -12,7 +13,7 @@ export * from "./fs-bash.js";
|
|
|
12
13
|
export * from "./read-deny.js";
|
|
13
14
|
import { BASH_READONLY_DEFAULT_ALLOW, } from "./bash-readonly-classifier.js";
|
|
14
15
|
import { compileReadDeny } from "./read-deny.js";
|
|
15
|
-
import { resolveReadFace } from "./read-face.js";
|
|
16
|
+
import { deploymentReadFaceClampNotice, resolveReadFace } from "./read-face.js";
|
|
16
17
|
export * from "./read-face.js";
|
|
17
18
|
import {} from "./fs-shared.js";
|
|
18
19
|
import { createReadFileTool } from "./fs-read.js";
|
|
@@ -39,6 +40,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
39
40
|
readOnlyMount: readOnly,
|
|
40
41
|
orgGoverned: false,
|
|
41
42
|
fullShellReachable: includeShell && !readOnly,
|
|
43
|
+
onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentReadFaceClampNotice()),
|
|
42
44
|
});
|
|
43
45
|
const tools = [
|
|
44
46
|
createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder, readDeny, readFace),
|
|
@@ -29,6 +29,13 @@ export interface ReadFaceInputs {
|
|
|
29
29
|
* "open"… no — see the resolution order: an EXPLICIT open still wins (row 4 is a default, not a
|
|
30
30
|
* clamp); what it changes is that NOTHING implicit opens a bash-less mount. */
|
|
31
31
|
fullShellReachable: boolean;
|
|
32
|
+
/** #237 — observation hook for the ONE silent branch of the order: row 1's deployment-seat clamp
|
|
33
|
+
* (readOnly mount + deps "open" + NO task seat). Fired exactly when the deps declaration was the
|
|
34
|
+
* deciding seat and the mount overrode it — not when a task-level "roots" pinned the same value
|
|
35
|
+
* anyway (the mount changed nothing there, and announcing it would claim a false cause), and
|
|
36
|
+
* never on the throw rows (those are already loud). The resolver stays the single owner of the
|
|
37
|
+
* clamp predicate; stations own delivery/dedup ({@link deploymentReadFaceClampNotice}). */
|
|
38
|
+
onDeploymentClamp?: () => void;
|
|
32
39
|
}
|
|
33
40
|
/**
|
|
34
41
|
* The ONE resolution order (§2.2), first hit wins. prepare-task AND createHandsToolkit both call
|
|
@@ -47,3 +54,17 @@ export interface ReadFaceInputs {
|
|
|
47
54
|
* 5. default: ROOTS (D-1b — the engine never opens implicitly).
|
|
48
55
|
*/
|
|
49
56
|
export declare function resolveReadFace(i: ReadFaceInputs): ReadFace;
|
|
57
|
+
/**
|
|
58
|
+
* #237 — the ONE text of the deployment-clamp announcement (`config.read_face_deployment_clamped`).
|
|
59
|
+
* Both stations deliver THIS object through their own `deliverEngineNotice` call (prepare-task with
|
|
60
|
+
* the `RunnerDeps.onNotice` seat, once per notice sink — the sink, not the deps object, because the
|
|
61
|
+
* Runner rebuilds its deps; `createHandsToolkit` with its band-local `HandsToolkitOptions.onNotice`
|
|
62
|
+
* seat — toolkit creation is itself the one-per-mount boot moment, and an absent seat prints the
|
|
63
|
+
* same line via `console.warn`). Built here, import-free, so the message can never fork between
|
|
64
|
+
* the stations.
|
|
65
|
+
*/
|
|
66
|
+
export declare function deploymentReadFaceClampNotice(): {
|
|
67
|
+
code: string;
|
|
68
|
+
message: string;
|
|
69
|
+
detail: Record<string, unknown>;
|
|
70
|
+
};
|
|
@@ -16,6 +16,8 @@ export function resolveReadFace(i) {
|
|
|
16
16
|
e.code = "config.read_face_readonly_conflict";
|
|
17
17
|
throw e;
|
|
18
18
|
}
|
|
19
|
+
if (deps === "open" && spec === undefined)
|
|
20
|
+
i.onDeploymentClamp?.();
|
|
19
21
|
return "roots";
|
|
20
22
|
}
|
|
21
23
|
if (i.orgGoverned) {
|
|
@@ -36,3 +38,10 @@ export function resolveReadFace(i) {
|
|
|
36
38
|
return "roots";
|
|
37
39
|
return "roots";
|
|
38
40
|
}
|
|
41
|
+
export function deploymentReadFaceClampNotice() {
|
|
42
|
+
return {
|
|
43
|
+
code: "config.read_face_deployment_clamped",
|
|
44
|
+
message: `readFace (deployment seat): the deployment-wide "open" declaration is not in force on this read-only (verifier) mount — clamped to "roots" (stricter-wins: the mount's containment is load-bearing and never openable). Other mounts still honor "open".`,
|
|
45
|
+
detail: { seat: "readFace (deployment seat)", declared: "open", inForce: "roots", cause: "read_only_mount" },
|
|
46
|
+
};
|
|
47
|
+
}
|