@sema-agent/core 5.59.0 → 5.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/dist/brain/anthropic.js +15 -5
- package/dist/brain/errors.d.ts +18 -1
- package/dist/brain/errors.js +7 -1
- package/dist/brain/input-too-long.d.ts +57 -0
- package/dist/brain/input-too-long.js +35 -0
- package/dist/brain/stream-engine.js +9 -1
- package/dist/core/auto-compaction.js +2 -2
- package/dist/core/checkpoint-store.d.ts +172 -25
- package/dist/core/checkpoint-store.js +15 -8
- package/dist/core/context-edit.d.ts +243 -41
- package/dist/core/context-edit.js +247 -32
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +5 -0
- package/dist/core/locked-config.d.ts +36 -4
- package/dist/core/locked-config.js +34 -1
- package/dist/core/mcp.d.ts +4 -5
- package/dist/core/mcp.js +10 -6
- package/dist/core/memory-engine/content-origin.d.ts +24 -2
- package/dist/core/memory-engine/content-origin.js +6 -1
- package/dist/core/memory-engine/engine.d.ts +5 -6
- package/dist/core/memory.d.ts +10 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/permission-rule-consent.js +9 -5
- package/dist/core/runner/prepare-config-doors.d.ts +22 -1
- package/dist/core/runner/prepare-config-doors.js +36 -0
- package/dist/core/runner/prepare-task.d.ts +28 -1
- package/dist/core/runner/prepare-task.js +107 -8
- package/dist/core/runner/runtask.js +97 -9
- package/dist/core/store-contracts/checkpoint-store-contract.js +32 -0
- package/dist/core/tool-policy.d.ts +32 -10
- package/dist/core/tool-policy.js +3 -3
- package/dist/core/tools.js +1 -1
- package/dist/core/trace.d.ts +36 -0
- package/dist/core/types.d.ts +127 -2
- package/dist/core/untrusted-text.d.ts +11 -0
- package/dist/core/untrusted-text.js +1 -0
- package/dist/engine/llm/types.d.ts +21 -2
- package/dist/engine/loop/agent-loop.js +7 -1
- package/dist/engine/loop/types.d.ts +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/fs/fs-bash.js +1 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +5 -1
|
@@ -406,12 +406,11 @@ export declare function memorySessionPollutedNotice(input: {
|
|
|
406
406
|
* arithmetic would report a clean containment while a known failure sat in the report. Every
|
|
407
407
|
* not-moved file does have an escalation row, so `escalated` is the superset and the honest clause.
|
|
408
408
|
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
* The wording therefore says "entry file(s)" rather than implying the whole containment.
|
|
409
|
+
* The derived index is contained by a different path (`restorePollutedIndex` captures and restores
|
|
410
|
+
* `MEMORY.md`) that mints no rejection row; that gap was CLOSED by the structured containment signal
|
|
411
|
+
* on `HarvestReport.containment` (indexRolledBack — see the containment block below), so an
|
|
412
|
+
* index-only polluted session now signals and notices instead of hiding in warnings prose. The
|
|
413
|
+
* wording says "entry file(s)" because index containment reports through its own signal.
|
|
415
414
|
*
|
|
416
415
|
* `reason` is optional because the mark record is read separately from the report: a record that cannot
|
|
417
416
|
* be read must not turn a true "N files were withheld" into a lie about why.
|
package/dist/core/memory.d.ts
CHANGED
|
@@ -477,6 +477,16 @@ export interface NormalizedMemorySpec {
|
|
|
477
477
|
* of the fail-closed `"external"` default. The explicit-config channel for a deployment whose custom
|
|
478
478
|
* tools predate the {@link import("./types.js").ToolSpec.contentOrigin} declaration; it never
|
|
479
479
|
* overrides a tool's OWN declaration. Normalized: trimmed, deduped, empties dropped.
|
|
480
|
+
*
|
|
481
|
+
* NOT the channel for MCP servers (design/378). A per-name entry here keys on the MINTED name — the
|
|
482
|
+
* host has to predict the namespacing and charset normalization — covers nothing a mid-task refresh
|
|
483
|
+
* adds, and says "exempt this undeclared tool" where the fact is "this whole server is mine".
|
|
484
|
+
* {@link import("./types.js").McpServerSpec.contentOrigin} states that fact at the entry, and being
|
|
485
|
+
* a DECLARATION it wins over this allowlist in both directions: a declared server's tools leave this
|
|
486
|
+
* list's reach entirely, an explicit `"external"` declaration included (that is the one way a
|
|
487
|
+
* deployment can pin a server's tools BEYOND this exemption). This list is not retired — it remains
|
|
488
|
+
* the in-register channel for undeclared HOST tools and for a deployment not ready to touch its
|
|
489
|
+
* server entries.
|
|
480
490
|
*/
|
|
481
491
|
trustedTools?: string[];
|
|
482
492
|
/**
|
|
@@ -70,6 +70,7 @@ function syntheticCheckpoint(scope) {
|
|
|
70
70
|
toolName: "SelfCheck",
|
|
71
71
|
args: { probe: true, nested: { depth: 2, list: [1, 2, 3] } },
|
|
72
72
|
boundInputHash: boundInputHashOf({ probe: true, nested: { depth: 2, list: [1, 2, 3] } }),
|
|
73
|
+
hasBidiControls: true,
|
|
73
74
|
batchToolCallIds: [`${scope}:call`],
|
|
74
75
|
completedCallIds: [],
|
|
75
76
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { hasUnrenderableCharacters, parseAllowRuleText, ruleAdmitsCommand, segmentCoverageOf, suggestRulesForCommand, } from "./permission-rule-model.js";
|
|
2
|
+
import { escapeForDisclosure, hasUnrenderableCharacters, parseAllowRuleText, ruleAdmitsCommand, segmentCoverageOf, suggestRulesForCommand, } from "./permission-rule-model.js";
|
|
3
3
|
import { errText, sameRuleOwner, sameScope, writerOf } from "./permission-rule-store.js";
|
|
4
4
|
export class InMemoryRuleApprovalRecordStore {
|
|
5
5
|
rows = new Map();
|
|
@@ -115,7 +115,7 @@ function approvalRecordDamageOf(rec) {
|
|
|
115
115
|
const seen = new Set();
|
|
116
116
|
for (const member of members) {
|
|
117
117
|
if (!Number.isInteger(member) || member < 0 || rec.candidates[member] === undefined) {
|
|
118
|
-
return `offer ${at} references candidate ${member}, which the record does not carry`;
|
|
118
|
+
return `offer ${at} references candidate ${escapeForDisclosure(member)}, which the record does not carry`;
|
|
119
119
|
}
|
|
120
120
|
const cand = rec.candidates[member];
|
|
121
121
|
if (typeof cand !== "object" || cand === null || typeof cand.rule !== "string") {
|
|
@@ -138,10 +138,14 @@ function approvalRecordDamageOf(rec) {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
else if (offer.segments !== undefined || offer.uncoveredSegments !== undefined) {
|
|
141
|
-
return `${rec.kind} batch offer ${at} must not carry segment metadata — there is no compound command behind it`;
|
|
141
|
+
return `${escapeForDisclosure(rec.kind)} batch offer ${at} must not carry segment metadata — there is no compound command behind it`;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
for (const [at] of rec.candidates.entries()) {
|
|
146
|
+
if (!claimed.has(at))
|
|
147
|
+
return `candidate ${at} belongs to no offer — every candidate row is claimed by exactly one offer`;
|
|
148
|
+
}
|
|
145
149
|
return undefined;
|
|
146
150
|
}
|
|
147
151
|
export function ruleOffersOfRecord(rec) {
|
|
@@ -325,7 +329,7 @@ function checkEditedRuleText(text, command) {
|
|
|
325
329
|
if (!ruleAdmitsCommand(parsed.rule, command)) {
|
|
326
330
|
return {
|
|
327
331
|
ok: false,
|
|
328
|
-
message: `the edited rule "${parsed.rule.rule}" does not admit the command that was decided ("${command}") — a card's edit may widen how much the rule covers, never move it to a different grant`,
|
|
332
|
+
message: `the edited rule "${parsed.rule.rule}" does not admit the command that was decided ("${escapeForDisclosure(command)}") — a card's edit may widen how much the rule covers, never move it to a different grant`,
|
|
329
333
|
};
|
|
330
334
|
}
|
|
331
335
|
return { ok: true, canonicalRule: parsed.rule.rule };
|
|
@@ -342,7 +346,7 @@ export function precheckEditedRuleText(text, command) {
|
|
|
342
346
|
throw e;
|
|
343
347
|
}
|
|
344
348
|
if (hasUnrenderableCharacters(command)) {
|
|
345
|
-
const e = new Error("precheckEditedRuleText takes a command this lane can read — one carrying control or format characters is
|
|
349
|
+
const e = new Error("precheckEditedRuleText takes a command this lane can read — one carrying control or format characters is refused at this preflight face (likelier a caller wiring fault than a card's command; the in-engine confirmation arm still judges such a card, with the command escaped on its refusal line)");
|
|
346
350
|
e.code = "config.invalid_argument";
|
|
347
351
|
throw e;
|
|
348
352
|
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { type BrainCallGuardrailRef } from "../../brain/timeout.js";
|
|
23
23
|
import type { Model } from "../../internal/llm.js";
|
|
24
24
|
import type { ThinkingLevel } from "../../internal/harness.js";
|
|
25
|
+
import type { ContextEditMachine } from "../context-edit.js";
|
|
25
26
|
import { type LockedPreflight } from "../locked-config.js";
|
|
26
27
|
import { type ResolvedRole } from "../roles.js";
|
|
27
28
|
import type { SessionStore } from "../session.js";
|
|
@@ -119,6 +120,13 @@ export interface PrepareConfigDoorsResult {
|
|
|
119
120
|
/** owned — the resolved interaction posture (spec > spawning run > deployment); the
|
|
120
121
|
* AskUserQuestion mount and the child-ctx injection read this same value. */
|
|
121
122
|
resolvedInteractionPosture: "interactive" | "headless" | undefined;
|
|
123
|
+
/** owned — the validated/resolved microCompact knob (design/374; {@link resolveMicroCompactKnob}
|
|
124
|
+
* for the door + placement reasoning). The per-run state builder consumes exactly these values —
|
|
125
|
+
* the deps bag is never re-read after this door. */
|
|
126
|
+
microCompactKnob: {
|
|
127
|
+
machine: ContextEditMachine;
|
|
128
|
+
clearOnRejection: boolean;
|
|
129
|
+
};
|
|
122
130
|
/** owned — the resolved role; its `systemPrompt` seat is still read at prompt-input time. */
|
|
123
131
|
resolvedRole: ResolvedRole;
|
|
124
132
|
/** owned — the resolved main model (`resolvedRole.model`, re-exposed as the name every later
|
|
@@ -163,5 +171,18 @@ export interface PrepareConfigDoorsResult {
|
|
|
163
171
|
* the resolver's off states). */
|
|
164
172
|
brainCallGuardrailMs: number | undefined;
|
|
165
173
|
}
|
|
166
|
-
/**
|
|
174
|
+
/** design/374 — the microCompact knob door (坏值响亮度默认律, the sibling knob doors' form:
|
|
175
|
+
* interactionPosture / toolModelGate / staleToolResultOffload). Placement is load-bearing (review
|
|
176
|
+
* r3): it runs in THIS pre-first-await config-doors phase so a malformed declaration refuses
|
|
177
|
+
* BEFORE any resource-bearing preparation — session acquisition, execution-env creation, the MCP
|
|
178
|
+
* dial — whose external effects a late refusal's teardown cannot undo. SINGLE READ per seat, and
|
|
179
|
+
* the CONTAINER is judged first (reviews r1/r2): an accessor-backed bag answering a legal value at
|
|
180
|
+
* one read and junk at another must be caught by whichever read this door takes, and a
|
|
181
|
+
* `microCompact: "cc"` / null / number / array bag makes both property reads answer undefined —
|
|
182
|
+
* it must refuse exactly like a malformed field, never fold to the defaults. The RESOLVED values
|
|
183
|
+
* returned here are immutable and are the state builder's ONLY source — nothing re-reads the bag. */
|
|
184
|
+
export declare function resolveMicroCompactKnob(bag: RunnerDeps["microCompact"]): {
|
|
185
|
+
machine: ContextEditMachine;
|
|
186
|
+
clearOnRejection: boolean;
|
|
187
|
+
};
|
|
167
188
|
export declare function prepareConfigDoors(input: PrepareConfigDoorsInput): PrepareConfigDoorsResult;
|
|
@@ -83,10 +83,45 @@ export function resolveModelPromptTraits(model, spec, internals) {
|
|
|
83
83
|
fableMitigations: isFableFamilyModelId(model.id),
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
+
function microCompactConfigError(field, value, legal) {
|
|
87
|
+
let shown;
|
|
88
|
+
try {
|
|
89
|
+
shown = JSON.stringify(value) ?? String(value);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
try {
|
|
93
|
+
shown = String(value);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
shown = `[unrepresentable ${typeof value}]`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const seat = field === undefined ? "microCompact" : `microCompact.${field}`;
|
|
100
|
+
const e = new Error(`${seat} ${shown} is not ${legal} — an unevaluable declaration is refused loudly, ` +
|
|
101
|
+
`never folded to the default: a silently-ignored opt-in would run the pre-374 machine while the ` +
|
|
102
|
+
`deployment believes it opted in.`);
|
|
103
|
+
e.code = "config.microcompact_invalid";
|
|
104
|
+
return e;
|
|
105
|
+
}
|
|
106
|
+
export function resolveMicroCompactKnob(bag) {
|
|
107
|
+
if (bag !== undefined && (typeof bag !== "object" || bag === null || Array.isArray(bag))) {
|
|
108
|
+
throw microCompactConfigError(undefined, bag, "an object carrying optional machine/clearOnRejection keys");
|
|
109
|
+
}
|
|
110
|
+
const declaredMachine = bag?.machine;
|
|
111
|
+
if (declaredMachine !== undefined && declaredMachine !== "legacy" && declaredMachine !== "cc") {
|
|
112
|
+
throw microCompactConfigError("machine", declaredMachine, `"legacy" | "cc"`);
|
|
113
|
+
}
|
|
114
|
+
const declaredClearOnRejection = bag?.clearOnRejection;
|
|
115
|
+
if (declaredClearOnRejection !== undefined && typeof declaredClearOnRejection !== "boolean") {
|
|
116
|
+
throw microCompactConfigError("clearOnRejection", declaredClearOnRejection, "a boolean");
|
|
117
|
+
}
|
|
118
|
+
return { machine: declaredMachine ?? "legacy", clearOnRejection: declaredClearOnRejection === true };
|
|
119
|
+
}
|
|
86
120
|
export function prepareConfigDoors(input) {
|
|
87
121
|
const { deps, sessions, resume, internals } = input;
|
|
88
122
|
let spec = input.spec;
|
|
89
123
|
assertRestoreGatedToolsValue(spec.restoreGatedTools);
|
|
124
|
+
const microCompactKnob = resolveMicroCompactKnob(deps.microCompact);
|
|
90
125
|
const assertToolNameListValue = (value, seat) => {
|
|
91
126
|
if (value === undefined)
|
|
92
127
|
return;
|
|
@@ -318,6 +353,7 @@ export function prepareConfigDoors(input) {
|
|
|
318
353
|
promptProfile,
|
|
319
354
|
lockedPreflight,
|
|
320
355
|
resolvedInteractionPosture,
|
|
356
|
+
microCompactKnob,
|
|
321
357
|
resolvedRole,
|
|
322
358
|
model,
|
|
323
359
|
thinking,
|
|
@@ -20,6 +20,7 @@ import type { MemoryEngine } from "../memory-engine/engine.js";
|
|
|
20
20
|
import { type GitStatusLaneRef } from "./git-status-frame.js";
|
|
21
21
|
import { type ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
22
22
|
import type { ToolDisclosureManifest } from "../trace.js";
|
|
23
|
+
import { type ClearedProjectionLedger, type ContextEditMachine, type OccurrenceIndex } from "../context-edit.js";
|
|
23
24
|
import type { TaskNotificationPayload } from "../task-notification.js";
|
|
24
25
|
import { type CwdRef, type ReadFace } from "../../tools/fs/index.js";
|
|
25
26
|
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
@@ -134,7 +135,6 @@ export declare function checkpointScopeOf(spec: {
|
|
|
134
135
|
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
135
136
|
export { isFableFamilyModelId, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
|
|
136
137
|
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./prepare-workspace-restore.js";
|
|
137
|
-
/** Everything the run loop needs, built once by {@link prepareTask} (task setup, isolated from the loop). */
|
|
138
138
|
export interface Prepared {
|
|
139
139
|
harness: AgentHarness;
|
|
140
140
|
/** The CONCRETE built-in session (engine-internal: prepare constructs/acquires `StoredSession` itself,
|
|
@@ -876,6 +876,33 @@ export interface Prepared {
|
|
|
876
876
|
trimPressureRef: {
|
|
877
877
|
droppedMessages: boolean;
|
|
878
878
|
};
|
|
879
|
+
/** design/374 slices 1b/2 — the microCompact machine state this run: the selected clearing
|
|
880
|
+
* machine, the cleared-projection ledger (request-view application, durable decisions — see
|
|
881
|
+
* `context-edit.ts`'s ledger note; per-run in-memory, so durable resume / `resumeAt` rebuilds
|
|
882
|
+
* start EMPTY by construction), the last request's projection seat (what the provider actually
|
|
883
|
+
* saw — the MC-R rejection arm computes its candidates and savings on THIS view, never on the
|
|
884
|
+
* raw session rebuild), and the MC-R knob. Default machine "legacy" + MC-R off ⇒ the ledger
|
|
885
|
+
* never gains an entry and every replay is a same-reference no-op (default bytes unchanged). */
|
|
886
|
+
microCompact: PreparedMicroCompact;
|
|
887
|
+
}
|
|
888
|
+
/** See {@link Prepared.microCompact}. */
|
|
889
|
+
export interface PreparedMicroCompact {
|
|
890
|
+
machine: ContextEditMachine;
|
|
891
|
+
/** MC-R (design/374 §3.2): one-shot clear-and-retry on a provider input-too-long rejection.
|
|
892
|
+
* Default false (X2: lands with the machinery, flips with slice 3). */
|
|
893
|
+
clearOnRejection: boolean;
|
|
894
|
+
ledger: ClearedProjectionLedger;
|
|
895
|
+
projectionRef: {
|
|
896
|
+
current?: {
|
|
897
|
+
/** The FINAL projected view of the last provider request (post trim/sweep). */
|
|
898
|
+
messages: AgentMessage[];
|
|
899
|
+
/** Occurrence coordinates of that view (object-identity first, unambiguous-group fallback). */
|
|
900
|
+
keyOf: OccurrenceIndex["keyOf"];
|
|
901
|
+
};
|
|
902
|
+
};
|
|
903
|
+
/** The same offload persist seat the frontier machine uses (write-once, idempotent), so MC-R
|
|
904
|
+
* clears compose identical markers. Absent when no offload store is configured. */
|
|
905
|
+
offloadPersist?: (toolCallId: string, fullText: string) => string;
|
|
879
906
|
}
|
|
880
907
|
/**
|
|
881
908
|
* WHICH tool call a committed durable park is holding this run — `undefined` when nothing parked, or
|
|
@@ -20,7 +20,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial, resolveDelegationEn
|
|
|
20
20
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
21
21
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
22
22
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
23
|
-
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
23
|
+
import { askApproverIdentity, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
24
24
|
const PERSISTED_RULE_TOOL = "Bash";
|
|
25
25
|
import { findAdmittingRule, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
26
26
|
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
@@ -70,7 +70,7 @@ import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
|
|
|
70
70
|
import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
|
|
71
71
|
import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
|
|
72
72
|
import { SEMA_DEFAULT_PACK } from "../../prompt-assembly/packs/sema-default.js";
|
|
73
|
-
import { clearStaleToolResults, dropEmptyFailureAssistants, editBudget } from "../context-edit.js";
|
|
73
|
+
import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureAssistants, editBudget, replayClearedProjection, resolveTriggerWindow, } from "../context-edit.js";
|
|
74
74
|
import { capAggregateToolResults } from "../tool-result-budget.js";
|
|
75
75
|
import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
|
|
76
76
|
import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
|
|
@@ -124,7 +124,7 @@ function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId)
|
|
|
124
124
|
if (compModel === undefined)
|
|
125
125
|
return;
|
|
126
126
|
const compWindow = compModel.contextTokens ?? compModel.contextWindow;
|
|
127
|
-
const mainWindow = model.
|
|
127
|
+
const mainWindow = resolveTriggerWindow(model).window;
|
|
128
128
|
if (compWindow > 0 && mainWindow > 0 && compWindow < mainWindow) {
|
|
129
129
|
const merged = { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction };
|
|
130
130
|
const sanitized = sanitizeCompactionSettings(merged, mainWindow);
|
|
@@ -195,6 +195,33 @@ function announceToolModelGate(onNotice, modelId, gate) {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
function announceDeclaredMcpContentClasses(args) {
|
|
199
|
+
for (const entry of args.entries ?? []) {
|
|
200
|
+
const declaredClass = entry.contentOrigin;
|
|
201
|
+
if (declaredClass === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
const server = inlineUntrusted(String(entry.name), 160);
|
|
204
|
+
const toolCount = args.statuses?.find((s) => s.name === entry.name)?.toolNames?.length ?? 0;
|
|
205
|
+
const resourceFaceCaveat = " (Not covered: the cross-server resource faces — ListMcpResourcesTool/ReadMcpResourceTool/ReadMcpResourceDirTool — stay externally classified and still mark.)";
|
|
206
|
+
const posture = declaredClass === "local"
|
|
207
|
+
? `this server's mounted tools' invocations will not mark this session's memory as externally exposed.${resourceFaceCaveat}`
|
|
208
|
+
: declaredClass === "execution"
|
|
209
|
+
? `this server's mounted tools' invocations will not mark this session's memory unless the execIsExternalContent strict upgrade is armed (currently: ${args.execIsExternalContent ? "armed" : "off"}).${resourceFaceCaveat}`
|
|
210
|
+
: "its tools stay externally classified and this declaration PINS that — the trustedTools allowlist no longer exempts them, and invocations mark this session's memory.";
|
|
211
|
+
deliverEngineNotice(args.onNotice, {
|
|
212
|
+
code: "memory.content_class_declared",
|
|
213
|
+
message: `MCP server "${server}" (${toolCount} tool(s) mounted) is declared ` +
|
|
214
|
+
`contentOrigin "${declaredClass}" by this deployment's configuration — ${posture}`,
|
|
215
|
+
detail: {
|
|
216
|
+
server,
|
|
217
|
+
contentOrigin: declaredClass,
|
|
218
|
+
toolCount,
|
|
219
|
+
...(declaredClass === "execution" ? { execIsExternalContent: args.execIsExternalContent } : {}),
|
|
220
|
+
sessionId: args.sessionId,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
198
225
|
export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
|
|
199
226
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
200
227
|
const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
|
|
@@ -242,6 +269,49 @@ class ParkRefusal extends Error {
|
|
|
242
269
|
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
243
270
|
export { isFableFamilyModelId, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
|
|
244
271
|
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./prepare-workspace-restore.js";
|
|
272
|
+
function buildMicroCompactState(deps, model, sessionId, offloadStore, knob) {
|
|
273
|
+
const triggerWindow = resolveTriggerWindow(model);
|
|
274
|
+
if (triggerWindow.clamped) {
|
|
275
|
+
deliverEngineNotice(deps.onNotice, {
|
|
276
|
+
code: "config.autocompact_window_clamped",
|
|
277
|
+
message: `model "${model.id}" declares autoCompactTokens=${triggerWindow.declaredAutoCompactTokens} ABOVE its physical window ` +
|
|
278
|
+
`${triggerWindow.physicalWindow}; the autocompact window only ever LOWERS the trigger-side geometry, so the value was ` +
|
|
279
|
+
`clamped to the physical window — fix the model config (declare a value at or below the physical window, or omit it)`,
|
|
280
|
+
detail: {
|
|
281
|
+
modelId: model.id,
|
|
282
|
+
declaredAutoCompactTokens: triggerWindow.declaredAutoCompactTokens,
|
|
283
|
+
physicalWindow: triggerWindow.physicalWindow,
|
|
284
|
+
sessionId,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
machine: knob.machine,
|
|
290
|
+
clearOnRejection: knob.clearOnRejection,
|
|
291
|
+
ledger: createClearedProjectionLedger(),
|
|
292
|
+
projectionRef: {},
|
|
293
|
+
...(offloadStore ? { offloadPersist: createOffloadPersist(offloadStore, sessionId, deps.onNotice) } : {}),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function recordFrontierClears(args) {
|
|
297
|
+
for (const { index } of args.pass.clears) {
|
|
298
|
+
const at = args.index.keyAt(index);
|
|
299
|
+
const clearedMsg = args.edited[index];
|
|
300
|
+
const markerText = clearedMsg.content?.[0]?.text;
|
|
301
|
+
if (at !== undefined && typeof markerText === "string") {
|
|
302
|
+
args.ledger.entries.set(at.key, { marker: markerText, groupCount: at.groupCount, fp: at.fp });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
emitTrace(args.tracer, () => ({
|
|
306
|
+
kind: "context.mc_clear",
|
|
307
|
+
version: 1,
|
|
308
|
+
taskId: args.taskId,
|
|
309
|
+
clearedCount: args.pass.clears.length,
|
|
310
|
+
tokensSavedEstimate: args.pass.tokensSavedEstimate,
|
|
311
|
+
trigger: "frontier",
|
|
312
|
+
ts: Date.now(),
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
245
315
|
export function gatedCallIdOf(p) {
|
|
246
316
|
if (p.suspendRef.token !== undefined)
|
|
247
317
|
return p.suspendRef.gatedCallId;
|
|
@@ -492,7 +562,7 @@ async function derivedRouteFallsBack(args) {
|
|
|
492
562
|
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf) {
|
|
493
563
|
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
494
564
|
spec = doors.spec;
|
|
495
|
-
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
565
|
+
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
496
566
|
announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
|
|
497
567
|
const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes, ownToolNames } = prepareSafetyScan({ spec, deps });
|
|
498
568
|
let shellGatedBash = false;
|
|
@@ -2079,6 +2149,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2079
2149
|
const trustedTools = effectiveSafety.trustedTools;
|
|
2080
2150
|
const execIsExternalContent = effectiveSafety.execIsExternalContent;
|
|
2081
2151
|
provenanceForChildrenRef.current = { trustedTools: [...trustedTools], execIsExternalContent };
|
|
2152
|
+
announceDeclaredMcpContentClasses({
|
|
2153
|
+
entries: lockedPreflight.mcp,
|
|
2154
|
+
statuses: mcp?.statuses,
|
|
2155
|
+
execIsExternalContent,
|
|
2156
|
+
sessionId,
|
|
2157
|
+
onNotice: deps.onNotice,
|
|
2158
|
+
});
|
|
2082
2159
|
if (memoryEngineSession?.settlement !== undefined) {
|
|
2083
2160
|
delegationSettlementRef.current = { controlDir: memoryEngineSession.settlement.controlDir, sessionId: memoryEngineSession.settlement.sessionId };
|
|
2084
2161
|
}
|
|
@@ -3888,6 +3965,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3888
3965
|
: undefined,
|
|
3889
3966
|
resourceLedger: resourceLedgerOut,
|
|
3890
3967
|
...(spec.durableApproval !== undefined ? { durableApproval: { ...spec.durableApproval } } : {}),
|
|
3968
|
+
...(spec.principal ? { principal: spec.principal } : {}),
|
|
3891
3969
|
};
|
|
3892
3970
|
if (!(await commitSuspendSaga(token, cp, suspendableEnv, remoteHandle)).ok)
|
|
3893
3971
|
return false;
|
|
@@ -4229,7 +4307,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4229
4307
|
args: parkedArgs,
|
|
4230
4308
|
...(() => {
|
|
4231
4309
|
const preview = approvalPreviewOf(req.toolName, parkedArgs);
|
|
4232
|
-
|
|
4310
|
+
const bidi = carriesBidiControls(parkedArgs) || carriesBidiControls(preview);
|
|
4311
|
+
return {
|
|
4312
|
+
...(preview !== undefined ? { preview } : {}),
|
|
4313
|
+
...(bidi ? { hasBidiControls: true } : {}),
|
|
4314
|
+
};
|
|
4233
4315
|
})(),
|
|
4234
4316
|
...ruleOffersOf(req.toolName, parkedArgs, {
|
|
4235
4317
|
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
@@ -4502,11 +4584,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4502
4584
|
}
|
|
4503
4585
|
const editAt = editBudget(model);
|
|
4504
4586
|
const guardAt = guardBudget(model);
|
|
4587
|
+
const microCompact = buildMicroCompactState(deps, model, sessionId, offloadStore, microCompactKnob);
|
|
4505
4588
|
const charsPerToken = model.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
|
|
4506
4589
|
harness.on("context", async ({ messages }) => {
|
|
4507
4590
|
batchHaltRef.current = undefined;
|
|
4508
4591
|
const healed = dropEmptyFailureAssistants(messages);
|
|
4509
|
-
const
|
|
4592
|
+
const replay = replayClearedProjection(healed, microCompact.ledger);
|
|
4593
|
+
const replayed = replay.messages;
|
|
4594
|
+
const capped = await capAggregateToolResults(replayed, {
|
|
4510
4595
|
store: offloadStore,
|
|
4511
4596
|
sessionId,
|
|
4512
4597
|
onCapped: (info) => emitTrace(deps.tracer, () => ({
|
|
@@ -4523,8 +4608,17 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4523
4608
|
limitBytes: deps.mediaByteCapBytes ?? AGGREGATE_MEDIA_BUDGET_BYTES,
|
|
4524
4609
|
onStripped: deps.onMediaStripped,
|
|
4525
4610
|
});
|
|
4611
|
+
let ccPass;
|
|
4526
4612
|
const edited = clearStaleToolResults(mediaCapped, {
|
|
4527
4613
|
budgetTokens: editAt,
|
|
4614
|
+
...(microCompact.machine === "cc" || microCompact.clearOnRejection ? { recognizeCcMarkers: true } : {}),
|
|
4615
|
+
...(microCompact.machine === "cc"
|
|
4616
|
+
? {
|
|
4617
|
+
machine: "cc",
|
|
4618
|
+
clearSource: replayed,
|
|
4619
|
+
onCleared: (pass) => { ccPass = pass; },
|
|
4620
|
+
}
|
|
4621
|
+
: {}),
|
|
4528
4622
|
anchoredTotalTokens: estimateContextTokens(mediaCapped, charsPerToken).tokens,
|
|
4529
4623
|
charsPerToken,
|
|
4530
4624
|
...(offloadStore
|
|
@@ -4535,6 +4629,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4535
4629
|
}
|
|
4536
4630
|
: {}),
|
|
4537
4631
|
});
|
|
4632
|
+
if (ccPass !== undefined) {
|
|
4633
|
+
recordFrontierClears({ pass: ccPass, index: replay.index, edited, ledger: microCompact.ledger, tracer: deps.tracer, taskId: spec.taskId ?? sessionId });
|
|
4634
|
+
}
|
|
4538
4635
|
let trimmed = trimToBudget(edited, guardAt, estimateContextTokens(edited, charsPerToken).tokens, charsPerToken);
|
|
4539
4636
|
const trimDroppedMessages = trimmed.length < edited.length;
|
|
4540
4637
|
if (trimDroppedMessages) {
|
|
@@ -4575,12 +4672,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4575
4672
|
}
|
|
4576
4673
|
}
|
|
4577
4674
|
requestLossyRef.current =
|
|
4578
|
-
|
|
4675
|
+
replayed !== healed ||
|
|
4676
|
+
capped !== replayed ||
|
|
4579
4677
|
mediaCapped !== capped ||
|
|
4580
4678
|
edited !== mediaCapped ||
|
|
4581
4679
|
trimDroppedMessages ||
|
|
4582
4680
|
swept.dropped.length > 0 ||
|
|
4583
4681
|
gitStatusRef.overBudgetShrunk === true;
|
|
4682
|
+
microCompact.projectionRef.current = { messages: swept.messages, keyOf: replay.index.keyOf };
|
|
4584
4683
|
return { messages: swept.messages };
|
|
4585
4684
|
});
|
|
4586
4685
|
const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
|
|
@@ -4769,7 +4868,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4769
4868
|
const effectiveReadFaceObserved = carrierReadFace();
|
|
4770
4869
|
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
4771
4870
|
const preparedHolder = {};
|
|
4772
|
-
const buildPrepared = () => ({ harness, session, sessionId, reminderMark, reminderDisclosureCounts, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4871
|
+
const buildPrepared = () => ({ harness, session, sessionId, reminderMark, reminderDisclosureCounts, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4773
4872
|
const prepared = buildPrepared();
|
|
4774
4873
|
preparedHolder.current = prepared;
|
|
4775
4874
|
return prepared;
|