@sema-agent/core 7.25.0 → 7.26.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 +81 -0
- package/dist/core/arg-summary.d.ts +0 -7
- package/dist/core/arg-summary.js +2 -1
- package/dist/core/ask-origin.d.ts +84 -0
- package/dist/core/ask-origin.js +31 -3
- package/dist/core/auto-compaction.d.ts +5 -0
- package/dist/core/auto-compaction.js +7 -0
- package/dist/core/auto-mode-directive.d.ts +45 -0
- package/dist/core/auto-mode-directive.js +47 -0
- package/dist/core/checkpoint-store.d.ts +10 -1
- package/dist/core/engine-notice.d.ts +10 -0
- package/dist/core/gate-lanes.js +18 -10
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +3 -0
- package/dist/core/mcp-probe.d.ts +122 -0
- package/dist/core/mcp-probe.js +132 -0
- package/dist/core/memory-engine/engine.d.ts +6 -1
- package/dist/core/memory-engine/engine.js +30 -2
- package/dist/core/memory-engine/index.d.ts +1 -0
- package/dist/core/memory-engine/index.js +1 -0
- package/dist/core/memory-engine/shadowing.d.ts +58 -0
- package/dist/core/memory-engine/shadowing.js +40 -0
- package/dist/core/memory-engine/tools.d.ts +33 -1
- package/dist/core/memory-engine/tools.js +136 -31
- package/dist/core/memory-engine/types.d.ts +10 -0
- package/dist/core/memory.d.ts +60 -0
- package/dist/core/memory.js +47 -2
- package/dist/core/permission-rule-store.d.ts +51 -8
- package/dist/core/permission-rule-store.js +112 -65
- package/dist/core/reminder-mint.d.ts +41 -0
- package/dist/core/reminder-mint.js +55 -0
- package/dist/core/runner/compaction-restate.d.ts +9 -0
- package/dist/core/runner/compaction-restate.js +20 -0
- package/dist/core/runner/contracts.d.ts +18 -1
- package/dist/core/runner/denial-limit-arms.js +1 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +5 -17
- package/dist/core/runner/permission-rule-lanes.js +5 -23
- package/dist/core/runner/prepare-ask-lane.js +2 -0
- package/dist/core/runner/prepare-context-lane.d.ts +4 -0
- package/dist/core/runner/prepare-context-lane.js +7 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -1
- package/dist/core/runner/prepare-inherited-gate.js +4 -2
- package/dist/core/runner/prepare-memory.js +47 -6
- package/dist/core/runner/prepare-park-ask.js +1 -0
- package/dist/core/runner/prepare-policy-chain.js +8 -4
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
- package/dist/core/runner/prepare-suspend-saga.js +3 -1
- package/dist/core/runner/prepare-task.js +7 -4
- package/dist/core/runner/run-leg.js +9 -1
- package/dist/core/runner/run-recovery-lanes.js +2 -2
- package/dist/core/runner/run-terminal-adoption.js +2 -2
- package/dist/core/runner/run-turn-boundary.js +2 -2
- package/dist/core/store-contracts/permission-rule-write-contract.d.ts +20 -0
- package/dist/core/store-contracts/permission-rule-write-contract.js +77 -0
- package/dist/core/task-result.d.ts +11 -0
- package/dist/core/task-spec.d.ts +31 -0
- package/dist/core/tool-catalog-entries.js +3 -3
- package/dist/core/tool-policy.d.ts +38 -0
- package/dist/core/tool-policy.js +35 -24
- package/dist/core/types.d.ts +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -1
- package/dist/stores/file/permission-rule-store.js +48 -29
- package/dist/tools/fs/bash-lexer.d.ts +13 -0
- package/dist/tools/fs/bash-lexer.js +61 -0
- package/dist/tools/fs/bash-program-position.d.ts +32 -3
- package/dist/tools/fs/bash-program-position.js +33 -2
- package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -4
- package/dist/tools/fs/bash-readonly-classifier.js +55 -18
- package/dist/tools/fs/shell-read-plan.js +9 -11
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +29 -1
- package/wire-changes/core@7.25.1.json +64 -0
- package/wire-changes/core@7.26.0.json +695 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_CHARS_PER_TOKEN, estimateContextTokens } from "../../internal/harness.js";
|
|
2
2
|
import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureAssistants, editBudget, replayClearedProjection, resolveTriggerWindow } from "../context-edit.js";
|
|
3
|
-
import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
|
|
3
|
+
import { dropOrphanToolResults, guardBudget, insertTrimNotice, protectGitFrame, trimToBudget } from "../context-guard.js";
|
|
4
4
|
import { AGGREGATE_MEDIA_BUDGET_BYTES, capAggregateMediaBytes } from "../media-byte-cap.js";
|
|
5
5
|
import { capAggregateToolResults } from "../tool-result-budget.js";
|
|
6
6
|
import { createOffloadPersist } from "../tool-result-store.js";
|
|
@@ -9,7 +9,7 @@ import { deliverEngineNotice } from "../types.js";
|
|
|
9
9
|
import { applyGitFrameGuard } from "./git-status-frame.js";
|
|
10
10
|
import { applyContextShrinkToReadFileState } from "../../tools/fs/index.js";
|
|
11
11
|
export function prepareContextLane(input) {
|
|
12
|
-
const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, readFileState } = input;
|
|
12
|
+
const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, readFileState } = input;
|
|
13
13
|
const editAt = editBudget(model);
|
|
14
14
|
const guardAt = guardBudget(model);
|
|
15
15
|
const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
|
|
@@ -85,6 +85,11 @@ export function prepareContextLane(input) {
|
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
});
|
|
88
|
+
if (postureFrameRef.protectedText !== undefined) {
|
|
89
|
+
const kept = protectGitFrame(working, trimmed, guardAt, { protectedText: postureFrameRef.protectedText }, charsPerToken);
|
|
90
|
+
if (kept.action === "kept" || kept.action === "reinserted")
|
|
91
|
+
trimmed = kept.messages;
|
|
92
|
+
}
|
|
88
93
|
const swept = dropOrphanToolResults(trimmed);
|
|
89
94
|
if (swept.dropped.length > 0) {
|
|
90
95
|
try {
|
|
@@ -44,7 +44,7 @@ import type { DelegationContentSafety } from "../memory-engine/delegation-proven
|
|
|
44
44
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
45
45
|
import type { SessionStore } from "../session.js";
|
|
46
46
|
import { type OnAsk } from "../tool-policy.js";
|
|
47
|
-
import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
47
|
+
import type { ApproverPosture, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
48
48
|
import type { ActiveSkillScope } from "./active-skill-scope.js";
|
|
49
49
|
import type { InheritedGate, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
50
50
|
import { type StopForDenialLimit } from "./denial-limit-arms.js";
|
|
@@ -96,6 +96,8 @@ export interface PrepareInheritedGateInput {
|
|
|
96
96
|
resolvedInteractionPosture: ToolExecuteContext["interactionPosture"];
|
|
97
97
|
/** borrowed-readonly — the task's own auto-mode intent seat, screened and snapshotted before the first await. */
|
|
98
98
|
autoModeSeat: boolean;
|
|
99
|
+
/** borrowed-readonly — the task's own approver-posture seat, screened and snapshotted before the first await. */
|
|
100
|
+
approverPostureSeat: ApproverPosture | undefined;
|
|
99
101
|
/** borrowed-readonly — whether an execution env is wired at all (the full-shell predicate's first term). */
|
|
100
102
|
handsEnabled: boolean;
|
|
101
103
|
/** borrowed-readonly — the nested-usage sink the ctx forwards. */
|
|
@@ -192,6 +194,8 @@ export interface PrepareInheritedGateResult {
|
|
|
192
194
|
inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
|
|
193
195
|
/** owned — the auto-mode INTENT (own seat ∨ live chain ∨ checkpoint bit); the arming site judges the other two arms. */
|
|
194
196
|
autoModeIntent: boolean;
|
|
197
|
+
/** owned — the resolved approver POSTURE (own seat, else the live chain's, else the checkpoint's); rendered, never enforced. */
|
|
198
|
+
approverPosture: ApproverPosture | undefined;
|
|
195
199
|
/** owned — the seed ∩ live admitted org scopes (one side absent ⇒ the other; both absent ⇒ undefined). */
|
|
196
200
|
inheritedAdmittedOrgScopes: readonly string[] | undefined;
|
|
197
201
|
/** owned — the prior own org verdict: checkpoint ∩ same-session ref (either alone stands; neither ⇒ undefined). */
|
|
@@ -7,7 +7,7 @@ import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
|
|
|
7
7
|
import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
|
|
8
8
|
import { asThrownError } from "../thrown-value.js";
|
|
9
9
|
export function prepareInheritedGate(input) {
|
|
10
|
-
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
10
|
+
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
11
11
|
const liveInheritedGate = internals?.inheritedGate;
|
|
12
12
|
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
13
13
|
const inheritedAncestorRules = (() => {
|
|
@@ -39,6 +39,7 @@ export function prepareInheritedGate(input) {
|
|
|
39
39
|
: seedShellGate;
|
|
40
40
|
const inheritedParentConstraints = attachRebuiltDenialTrackers(liveInheritedGate?.parentConstraints, deps.autoMode?.denialLimit);
|
|
41
41
|
const autoModeIntent = autoModeSeat || liveInheritedGate?.autoModeRequested === true || seedInheritedGate?.autoModeRequested === true;
|
|
42
|
+
const approverPosture = [approverPostureSeat, liveInheritedGate?.approverPosture, seedInheritedGate?.approverPosture].find((w) => w !== undefined);
|
|
42
43
|
const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
|
|
43
44
|
const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
|
|
44
45
|
const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
|
|
@@ -166,6 +167,7 @@ export function prepareInheritedGate(input) {
|
|
|
166
167
|
...(ancestorRules.length > 0 ? { ancestorRules } : {}),
|
|
167
168
|
...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
|
|
168
169
|
...(autoModeIntent ? { autoModeRequested: true } : {}),
|
|
170
|
+
...(approverPosture !== undefined ? { approverPosture } : {}),
|
|
169
171
|
...(parentConstraints.length > 0 ? { parentConstraints } : {}),
|
|
170
172
|
admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
|
|
171
173
|
...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
|
|
@@ -276,5 +278,5 @@ export function prepareInheritedGate(input) {
|
|
|
276
278
|
...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
|
|
277
279
|
};
|
|
278
280
|
};
|
|
279
|
-
return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
|
|
281
|
+
return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
|
|
280
282
|
}
|
|
@@ -3,7 +3,9 @@ import { deliverEngineNotice } from "../types.js";
|
|
|
3
3
|
import { admitMemoryScopes } from "../memory-admission.js";
|
|
4
4
|
import { adoptLegacyRepoDirs, canonicalize, deriveRepoControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, isContainedIn, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
5
5
|
import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
|
|
6
|
-
import { normalizeMemorySpec } from "../memory.js";
|
|
6
|
+
import { memoryProjectScopeAbsentNotice, normalizeMemorySpec } from "../memory.js";
|
|
7
|
+
import { deriveSupersededSet } from "../memory-engine/consolidation.js";
|
|
8
|
+
import { shadowNamesOf } from "../memory-engine/shadowing.js";
|
|
7
9
|
import { MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA, MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_CAPTURE_INDETERMINATE_NOTICE, MEMORY_CAPTURE_OPTOUT_NOTICE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MemoryEngine, entryFileHeadCarriesOrigin, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, memoryConsolidationRecommendedNotice, memoryHarvestQuarantinedNotice, memoryHoldNotices, memoryRecallDisciplineSegment, memoryRestoreWithheldNotice, memorySessionPollutedNotice, pollutionContainmentCounts, } from "../memory-engine/engine.js";
|
|
8
10
|
import { adjudicateCaptureOptOut, captureOptOutDeniedError, captureOptOutUnpersistedError } from "./memory-capture-optout.js";
|
|
9
11
|
import { readSessionCaptureOptOut } from "../memory-engine/layout.js";
|
|
@@ -46,6 +48,9 @@ export async function prepareMemory(input) {
|
|
|
46
48
|
memorySpec.scopes = outcome.scopes;
|
|
47
49
|
memorySpec.writeScope = outcome.writeScope;
|
|
48
50
|
}
|
|
51
|
+
if (memorySpec?.enabled === true && memorySpec.projectKey?.absent !== undefined) {
|
|
52
|
+
deliverEngineNotice(deps.onNotice, memoryProjectScopeAbsentNotice({ reason: memorySpec.projectKey.absent, taskRoot: taskRootPath, sessionId, writeScope: memorySpec.writeScope }));
|
|
53
|
+
}
|
|
49
54
|
const useMemoryEngine = Boolean(memorySpec && memorySpec.enabled && deps.memoryBackend);
|
|
50
55
|
const capturePosture = input.memoryCapturePolicy ?? "open";
|
|
51
56
|
const captureEntitlement = input.captureEntitlement ?? { value: undefined, faulted: false };
|
|
@@ -367,11 +372,35 @@ export async function prepareMemory(input) {
|
|
|
367
372
|
const personalEngine = personal.engine;
|
|
368
373
|
const p = planes;
|
|
369
374
|
await armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
|
|
370
|
-
const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
371
|
-
materializedResidue.push(...planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null));
|
|
372
|
-
const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
373
|
-
materializedResidue.push(...planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null));
|
|
374
375
|
const writeIsPersonal = p.writePlane === "personal";
|
|
376
|
+
const mounts = [
|
|
377
|
+
{ engine: writeIsPersonal ? personalEngine : projectEngine, scopes: writeIsPersonal ? p.personal : p.project, write: p.writePlane === null ? null : memorySpec.writeScope },
|
|
378
|
+
{ engine: writeIsPersonal ? projectEngine : personalEngine, scopes: writeIsPersonal ? p.project : p.personal, write: null },
|
|
379
|
+
];
|
|
380
|
+
const mounted = [];
|
|
381
|
+
let shadowNames;
|
|
382
|
+
for (const m of mounts) {
|
|
383
|
+
const handle = await m.engine.materialize(m.scopes, m.write, {
|
|
384
|
+
adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate,
|
|
385
|
+
sessionId,
|
|
386
|
+
...(m.write === null && shadowNames !== undefined && shadowNames.size > 0 ? { shadowNames } : {}),
|
|
387
|
+
});
|
|
388
|
+
mounted.push(handle);
|
|
389
|
+
materializedResidue.push(...planeScopes(m.scopes, m.write));
|
|
390
|
+
if (m.write !== null) {
|
|
391
|
+
try {
|
|
392
|
+
const withheld = new Set(m.engine.readChallengeExclusions().keys());
|
|
393
|
+
const committed = await m.engine.listCommittedHeaders([m.write]);
|
|
394
|
+
const superseded = deriveSupersededSet(committed, { excluded: (id) => withheld.has(id) });
|
|
395
|
+
shadowNames = shadowNamesOf(committed, m.write, { excluded: (id) => withheld.has(id) || superseded.has(id) });
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
shadowNames = undefined;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const projectHandle = writeIsPersonal ? mounted[1] : mounted[0];
|
|
403
|
+
const personalHandle = writeIsPersonal ? mounted[0] : mounted[1];
|
|
375
404
|
memoryMarkedEntriesServed = projectHandle.markedEntriesPresent === true || personalHandle.markedEntriesPresent === true;
|
|
376
405
|
writeEngine = writeIsPersonal ? personalEngine : projectEngine;
|
|
377
406
|
writeHandle = writeIsPersonal ? personalHandle : projectHandle;
|
|
@@ -385,6 +414,7 @@ export async function prepareMemory(input) {
|
|
|
385
414
|
recordRetrieved: (ids) => projectEngine.recordRetrieved(ids),
|
|
386
415
|
challengeExclusions: () => projectEngine.readChallengeExclusions(),
|
|
387
416
|
enginePlaneProvenance: projectEngine.provenanceMode,
|
|
417
|
+
committedHeaders: () => projectEngine.listCommittedHeaders(planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null)),
|
|
388
418
|
},
|
|
389
419
|
{
|
|
390
420
|
backend: retrievalBackend(personal.backend, adoptionRestricted || captureOptedOut || captureIndeterminate || p.writePlane !== "personal"),
|
|
@@ -392,6 +422,7 @@ export async function prepareMemory(input) {
|
|
|
392
422
|
recordRetrieved: (ids) => personalEngine.recordRetrieved(ids),
|
|
393
423
|
challengeExclusions: () => personalEngine.readChallengeExclusions(),
|
|
394
424
|
enginePlaneProvenance: personalEngine.provenanceMode,
|
|
425
|
+
committedHeaders: () => personalEngine.listCommittedHeaders(planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null)),
|
|
395
426
|
},
|
|
396
427
|
];
|
|
397
428
|
harvestBoth = async () => {
|
|
@@ -430,6 +461,7 @@ export async function prepareMemory(input) {
|
|
|
430
461
|
recordRetrieved: (ids) => personalEngine.recordRetrieved(ids),
|
|
431
462
|
challengeExclusions: () => personalEngine.readChallengeExclusions(),
|
|
432
463
|
enginePlaneProvenance: personalEngine.provenanceMode,
|
|
464
|
+
committedHeaders: () => personalEngine.listCommittedHeaders(planeScopes(memorySpec.scopes, memorySpec.writeScope)),
|
|
433
465
|
},
|
|
434
466
|
];
|
|
435
467
|
}
|
|
@@ -458,9 +490,17 @@ export async function prepareMemory(input) {
|
|
|
458
490
|
recordRetrieved: (ids) => engine.recordRetrieved(ids),
|
|
459
491
|
challengeExclusions: () => engine.readChallengeExclusions(),
|
|
460
492
|
enginePlaneProvenance: engine.provenanceMode,
|
|
493
|
+
committedHeaders: () => engine.listCommittedHeaders(planeScopes(memorySpec.scopes, memorySpec.writeScope)),
|
|
461
494
|
},
|
|
462
495
|
];
|
|
463
496
|
}
|
|
497
|
+
for (const [scope, count] of [...(writeHandle.shadowedByScope ?? []), ...(readOnlyHandle?.shadowedByScope ?? [])]) {
|
|
498
|
+
if (count <= 0 || scope === memorySpec.writeScope)
|
|
499
|
+
continue;
|
|
500
|
+
const row = mountedScopeRows.find((r) => r.scope === scope);
|
|
501
|
+
if (row !== undefined)
|
|
502
|
+
row.shadowed = (row.shadowed ?? 0) + count;
|
|
503
|
+
}
|
|
464
504
|
const foreignUnderRoot = [];
|
|
465
505
|
if (input.writeToolsMounted && writeHandle.writeScope !== null && input.advertisedWritableDirs !== undefined) {
|
|
466
506
|
const controlPlane = canonicalize(writeEngine.controlPlaneDir);
|
|
@@ -678,6 +718,7 @@ export async function prepareMemory(input) {
|
|
|
678
718
|
contract: memorySpec.scopeContract === "v2" ? "v2" : "legacy",
|
|
679
719
|
scopes: mountedScopeRows,
|
|
680
720
|
writeScope: memorySpec.writeScope,
|
|
721
|
+
...(memorySpec.projectKey !== undefined ? { projectKey: memorySpec.projectKey } : {}),
|
|
681
722
|
};
|
|
682
723
|
if (input.memoryProvenance !== "off") {
|
|
683
724
|
const session = memoryEngineSession;
|
|
@@ -689,7 +730,7 @@ export async function prepareMemory(input) {
|
|
|
689
730
|
plane.onTaintedDelivery = markDerived;
|
|
690
731
|
}
|
|
691
732
|
if (input.memorySearchToolsPlanned)
|
|
692
|
-
memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance });
|
|
733
|
+
memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance, writeScope: memorySpec.writeScope });
|
|
693
734
|
}
|
|
694
735
|
catch (err) {
|
|
695
736
|
if (typeof err.code === "string" &&
|
|
@@ -294,6 +294,7 @@ export function prepareParkAsk(input) {
|
|
|
294
294
|
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
295
295
|
...askCarryRowMembers(carry),
|
|
296
296
|
...ruleOffersOf(req.toolName, parkedArgs, {
|
|
297
|
+
...(carry?.mandated === true ? { mandated: true } : {}),
|
|
297
298
|
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
298
299
|
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
299
300
|
...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
|
|
@@ -10,7 +10,8 @@ import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
|
10
10
|
import { NAMESPACED_NAME_SHAPES, protocolOf } from "../protocol-table.js";
|
|
11
11
|
import { emitTrace } from "../trace.js";
|
|
12
12
|
import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
13
|
-
import { probeMandatedAsk } from "../ask-origin.js";
|
|
13
|
+
import { askMandateMember, probeMandatedAsk } from "../ask-origin.js";
|
|
14
|
+
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
|
|
14
15
|
import { headlessDenyAtFold, headlessDenyAtRecheck, inheritedAskCarry, judgeInheritedClassifier, lateAskSettlementObserver, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
15
16
|
import { askGrantShapeOf } from "./inherited-ask-grants.js";
|
|
16
17
|
import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
@@ -349,11 +350,12 @@ export async function preparePolicyChain(input) {
|
|
|
349
350
|
toolName: creq.toolName,
|
|
350
351
|
toolCallId: creq.toolCallId,
|
|
351
352
|
args: editArgs,
|
|
352
|
-
...ruleOffersOf(creq.toolName, editArgs,
|
|
353
|
+
...ruleOffersOf(creq.toolName, editArgs, ancestorFacts(re)),
|
|
353
354
|
message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
354
355
|
...askSourceIdentity(),
|
|
355
356
|
...riskAxesOf(creq.toolName),
|
|
356
357
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
358
|
+
...askMandateMember(ancestorFacts(re)),
|
|
357
359
|
...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
|
|
358
360
|
...inheritedAskCarry(judged, isLiveApproverSeat(onAskOf), ancestorTracker),
|
|
359
361
|
ruleEvidence: inheritedAskEvidence,
|
|
@@ -424,11 +426,12 @@ export async function preparePolicyChain(input) {
|
|
|
424
426
|
toolName: creq.toolName,
|
|
425
427
|
toolCallId: creq.toolCallId,
|
|
426
428
|
args: presentedArgs,
|
|
427
|
-
...ruleOffersOf(creq.toolName, presentedArgs,
|
|
429
|
+
...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
|
|
428
430
|
message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
429
431
|
...askSourceIdentity(),
|
|
430
432
|
...riskAxesOf(creq.toolName),
|
|
431
433
|
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
434
|
+
...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
|
|
432
435
|
...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
|
|
433
436
|
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
434
437
|
ruleEvidence: inheritedAskEvidence,
|
|
@@ -493,11 +496,12 @@ export async function preparePolicyChain(input) {
|
|
|
493
496
|
toolName: creq.toolName,
|
|
494
497
|
toolCallId: creq.toolCallId,
|
|
495
498
|
args: presentedArgs,
|
|
496
|
-
...ruleOffersOf(creq.toolName, presentedArgs,
|
|
499
|
+
...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
|
|
497
500
|
message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
498
501
|
...askSourceIdentity(),
|
|
499
502
|
...riskAxesOf(creq.toolName),
|
|
500
503
|
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
504
|
+
...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
|
|
501
505
|
...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
|
|
502
506
|
...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
503
507
|
ruleEvidence: inheritedAskEvidence,
|
|
@@ -134,6 +134,8 @@ export interface PrepareSuspendSagaInput {
|
|
|
134
134
|
inheritedShellGate: InheritedGate["shellGate"];
|
|
135
135
|
/** borrowed-readonly — the auto-mode intent (seat ∨ live ∨ seed), carried while the latch is healthy. */
|
|
136
136
|
autoModeIntent: boolean;
|
|
137
|
+
/** borrowed-readonly — the resolved approver posture (seat, else live, else seed), persisted verbatim. */
|
|
138
|
+
approverPosture: InheritedGate["approverPosture"];
|
|
137
139
|
/** borrowed-readonly — the auto-mode decider, or undefined (the latch-health reading only). */
|
|
138
140
|
/** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
|
|
139
141
|
inheritedAdmittedOrgScopes: readonly string[] | undefined;
|
|
@@ -79,7 +79,7 @@ function stampPlacementRootSessionId(placementRootResolved) {
|
|
|
79
79
|
return placementValueOrAbsent(placementRootResolved);
|
|
80
80
|
}
|
|
81
81
|
export function prepareSuspendSaga(input) {
|
|
82
|
-
const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
|
|
82
|
+
const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
|
|
83
83
|
if (!gateMachineryActive)
|
|
84
84
|
return { saga: undefined };
|
|
85
85
|
const inFlightSpendMicroUsd = () => {
|
|
@@ -139,6 +139,7 @@ export function prepareSuspendSaga(input) {
|
|
|
139
139
|
return ancestorRulesAtMint !== undefined ||
|
|
140
140
|
inheritedShellGate !== undefined ||
|
|
141
141
|
autoModeIntentCarried ||
|
|
142
|
+
approverPosture !== undefined ||
|
|
142
143
|
inheritedAdmittedOrgScopes !== undefined ||
|
|
143
144
|
ownOrgVerdictRef.current !== undefined ||
|
|
144
145
|
orgGovernedProvenance ||
|
|
@@ -147,6 +148,7 @@ export function prepareSuspendSaga(input) {
|
|
|
147
148
|
...(ancestorRulesAtMint !== undefined ? { ancestorRules: structuredClone(ancestorRulesAtMint) } : {}),
|
|
148
149
|
...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
|
|
149
150
|
...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
|
|
151
|
+
...(approverPosture !== undefined ? { approverPosture } : {}),
|
|
150
152
|
...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
|
|
151
153
|
...(ownOrgVerdictRef.current !== undefined
|
|
152
154
|
? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
|
|
@@ -9,6 +9,7 @@ import { hasSessionFork } from "../session.js";
|
|
|
9
9
|
import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
10
10
|
import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
|
|
11
11
|
import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
|
|
12
|
+
import { refuseInvalidApproverPosture } from "../auto-mode-directive.js";
|
|
12
13
|
import { emitTrace } from "../trace.js";
|
|
13
14
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
14
15
|
import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
|
|
@@ -265,6 +266,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
265
266
|
spec = doors.spec;
|
|
266
267
|
const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
|
|
267
268
|
const autoModeSeat = refuseInvalidAutoModeSeat(spec);
|
|
269
|
+
const approverPostureSeat = refuseInvalidApproverPosture(spec);
|
|
268
270
|
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
269
271
|
announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
|
|
270
272
|
const modelGateManifest = toolModelGateManifest(doors.modelGate);
|
|
@@ -376,7 +378,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
376
378
|
let readDenyAdditionsNormalized = [];
|
|
377
379
|
let handsLessResolvedFace;
|
|
378
380
|
let handsLessResolvedWriteFace;
|
|
379
|
-
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
381
|
+
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
380
382
|
const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
381
383
|
const tools = roster.tools;
|
|
382
384
|
const pausedRef = {};
|
|
@@ -700,15 +702,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
700
702
|
});
|
|
701
703
|
}
|
|
702
704
|
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash });
|
|
703
|
-
const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
|
|
705
|
+
const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
|
|
704
706
|
const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
|
|
705
707
|
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
|
|
706
708
|
prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
|
|
707
|
-
const
|
|
709
|
+
const postureFrameRef = {};
|
|
710
|
+
const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
|
|
708
711
|
const preparedHolder = {};
|
|
709
712
|
const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
|
|
710
713
|
await onceLedger.settle(session, announcedListingsRef);
|
|
711
|
-
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, 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, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
|
|
714
|
+
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, approvalPosture: { autoMode: autoModeIntent, ...(approverPostureResolved !== undefined ? { approverPosture: approverPostureResolved } : {}) }, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(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, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, 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, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
|
|
712
715
|
rollback.commit();
|
|
713
716
|
const prepared = buildPrepared();
|
|
714
717
|
preparedHolder.current = prepared;
|
|
@@ -8,7 +8,8 @@ import { buildHumanInputEvent, projectHumanInput } from "../human-input-projecti
|
|
|
8
8
|
import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
9
9
|
import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
|
|
10
10
|
import { computeCostMicroUsd } from "../pricing.js";
|
|
11
|
-
import {
|
|
11
|
+
import { composeApprovalPostureDirective } from "../auto-mode-directive.js";
|
|
12
|
+
import { mintSystemReminder, statedReminderBodiesOf } from "../reminder-mint.js";
|
|
12
13
|
import { hasDestroy } from "../remote-env.js";
|
|
13
14
|
import { advertisedWritableRootsSeat, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
14
15
|
import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
|
|
@@ -375,6 +376,13 @@ export async function runLeg(input) {
|
|
|
375
376
|
effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
|
|
376
377
|
}
|
|
377
378
|
}
|
|
379
|
+
if (composeApprovalPostureDirective(prepared.approvalPosture) !== undefined) {
|
|
380
|
+
const postureDirective = composeApprovalPostureDirective(prepared.approvalPosture, await statedReminderBodiesOf(prepared.session, prepared.reminderMark));
|
|
381
|
+
if (postureDirective !== undefined) {
|
|
382
|
+
prepared.postureFrameRef.protectedText = mintSystemReminder(postureDirective, prepared.reminderMark);
|
|
383
|
+
effectiveObjective = `${prepared.postureFrameRef.protectedText}\n${effectiveObjective}`;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
378
386
|
const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
|
|
379
387
|
const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
|
|
380
388
|
? 0
|
|
@@ -6,7 +6,7 @@ import { emitTrace } from "../trace.js";
|
|
|
6
6
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
7
7
|
import { rebaseCadenceWindows } from "./turn-attachments.js";
|
|
8
8
|
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
9
|
-
import {
|
|
9
|
+
import { compactionRestateOptions } from "./compaction-restate.js";
|
|
10
10
|
import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
11
11
|
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
|
|
12
12
|
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
@@ -41,7 +41,7 @@ export function runRecoveryLanes(input) {
|
|
|
41
41
|
...contextInstructionFilesOption(prepared),
|
|
42
42
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
43
43
|
...seamCCompactionOptions(runner, prepared),
|
|
44
|
-
...
|
|
44
|
+
...compactionRestateOptions(prepared),
|
|
45
45
|
...windowSafetyOptions(prepared.harness.getModel()),
|
|
46
46
|
...compactionHookOptions(runner, spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
|
|
47
47
|
});
|
|
@@ -7,7 +7,7 @@ import { answerSettled, errorCodeOf } from "./assemble-result.js";
|
|
|
7
7
|
import { awaitChargeWithSlowDisclosure, discloseUnevaluableWindow } from "./clock-and-limits.js";
|
|
8
8
|
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
|
|
9
9
|
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
|
|
10
|
-
import {
|
|
10
|
+
import { compactionRestateOptions } from "./compaction-restate.js";
|
|
11
11
|
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
12
12
|
import { reconciledToolEndBody } from "./tool-end-body.js";
|
|
13
13
|
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
@@ -149,7 +149,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
149
149
|
...contextInstructionFilesOption(prepared),
|
|
150
150
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
151
151
|
...seamCCompactionOptions(runner, prepared),
|
|
152
|
-
...
|
|
152
|
+
...compactionRestateOptions(prepared),
|
|
153
153
|
...compactionHookOptions(runner, spec, prepared.sessionId, "auto", prepared.hookIdentity, {
|
|
154
154
|
timeoutMs: prepared.hookTimeoutMs,
|
|
155
155
|
...(opts?.cancelled === true ? { signal: prepared.abortController.signal } : {}),
|
|
@@ -13,7 +13,7 @@ import { droppedToolDigest, withAnnounceDigests } from "./announce-once-ledger.j
|
|
|
13
13
|
import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
|
|
14
14
|
import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
|
|
15
15
|
import { awaitChargeWithSlowDisclosure, discloseUnevaluableWindow, ENV_DUE_GOVERNANCE_READ_BUDGET_MS, GOVERNANCE_READ_STALLED, LIMIT_APPROACH_DEFAULT_THRESHOLDS, limitApproachFrames, platformLimitTerminal, raceUntilDeadline } from "./clock-and-limits.js";
|
|
16
|
-
import {
|
|
16
|
+
import { compactionRestateOptions } from "./compaction-restate.js";
|
|
17
17
|
import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
18
18
|
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
|
|
19
19
|
import { contextUsageSections } from "../../prompt-assembly/section-usage.js";
|
|
@@ -583,7 +583,7 @@ export function createTurnBoundary(input) {
|
|
|
583
583
|
...contextInstructionFilesOption(prepared),
|
|
584
584
|
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
585
585
|
...seamCCompactionOptions(runner, prepared),
|
|
586
|
-
...
|
|
586
|
+
...compactionRestateOptions(prepared),
|
|
587
587
|
...windowSafetyOptions(event.model),
|
|
588
588
|
...compactionHookOptions(runner, spec, prepared.sessionId, passTrigger, prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal }),
|
|
589
589
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The permission-rule WRITE-FACE contract: what every backend behind `PermissionRuleWriter.apply` owes
|
|
3
|
+
* its caller. The sibling kit (`permission-rule-sync-contract.ts`) pins the pure JOIN; this one pins the
|
|
4
|
+
* DOOR it arrives through. A delta is DATA — a foreign caller, a replayed record, a deserialized join —
|
|
5
|
+
* so "the type system says it cannot happen" is not an answer a door may give, and a kind outside the
|
|
6
|
+
* union must be refused rather than folded as whichever arm a dispatch chain happens to end with. The
|
|
7
|
+
* positive control rides the same run on purpose: a backend that throws at everything refuses the
|
|
8
|
+
* unnamed kind too, so a suite without the control proves nothing.
|
|
9
|
+
*
|
|
10
|
+
* KIT form: vitest-free (`node:assert`), runner seam via {@link ContractAssertionRunner}, no skip hook.
|
|
11
|
+
*/
|
|
12
|
+
import type { WritableDurableRulePartition } from "../permission-rule-store.js";
|
|
13
|
+
import { type ContractAssertionRunner } from "./contract-harness.js";
|
|
14
|
+
export interface PermissionRuleWriteContractHooks {
|
|
15
|
+
/** Open ONE fresh, EMPTY partition with a write face — once per case; teardown stays with the caller. */
|
|
16
|
+
make: () => WritableDurableRulePartition | Promise<WritableDurableRulePartition>;
|
|
17
|
+
runAssertion?: ContractAssertionRunner;
|
|
18
|
+
}
|
|
19
|
+
/** Run the full conformance suite. Throws (assert) on the first violated clause. */
|
|
20
|
+
export declare function permissionRuleWriteContract(hooks: PermissionRuleWriteContractHooks): Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { PERMISSION_RULE_WRITER } from "../permission-rule-store.js";
|
|
3
|
+
import { beginContract } from "./contract-harness.js";
|
|
4
|
+
const GLOBAL = { kind: "global" };
|
|
5
|
+
function add(actor, counter) {
|
|
6
|
+
return { dot: { actor, counter }, origin: "user", createdAt: "2026-01-01T00:00:00.000Z" };
|
|
7
|
+
}
|
|
8
|
+
const IDENTITY = { behavior: "deny", rule: "Bash(curl:*)", scope: GLOBAL, tool: "Bash", match: "prefix", command: "curl" };
|
|
9
|
+
function tombstoneFor(removed, deletedBy) {
|
|
10
|
+
return { behavior: IDENTITY.behavior, rule: IDENTITY.rule, scope: GLOBAL, removedDots: removed.map((a) => a.dot), deletedBy };
|
|
11
|
+
}
|
|
12
|
+
function liveDots(rules) {
|
|
13
|
+
return new Set(rules.flatMap((r) => r.adds.map((a) => `${r.behavior}|${r.rule}|${a.dot.actor}#${a.dot.counter}`)));
|
|
14
|
+
}
|
|
15
|
+
async function rawShape(part) {
|
|
16
|
+
const raw = await part[PERMISSION_RULE_WRITER].readRaw();
|
|
17
|
+
const dots = [...liveDots(raw.rules)].sort();
|
|
18
|
+
const tombs = raw.tombstones.map((t) => `${t.behavior}|${t.rule}|${t.deletedBy.actor}#${t.deletedBy.counter}`).sort();
|
|
19
|
+
return JSON.stringify({ rev: raw.rev, dots, tombs });
|
|
20
|
+
}
|
|
21
|
+
export async function permissionRuleWriteContract(hooks) {
|
|
22
|
+
const { run, settle } = beginContract(hooks.runAssertion);
|
|
23
|
+
run("the named arms land, a landing ADVANCES the revision, and a spent revision is ANSWERED (positive control)", async () => {
|
|
24
|
+
const part = await hooks.make();
|
|
25
|
+
const writer = part[PERMISSION_RULE_WRITER];
|
|
26
|
+
const a1 = add("kit", 1);
|
|
27
|
+
const held = (await part.list()).rev;
|
|
28
|
+
const first = await writer.apply({ kind: "tighten-add", ...IDENTITY, add: a1 }, { expectedRev: held });
|
|
29
|
+
assert.ok(!("conflict" in first) && !("refused" in first), "a well-formed tighten-add must land");
|
|
30
|
+
const afterAdd = await part.list();
|
|
31
|
+
assert.deepEqual(liveDots(afterAdd.rules), new Set([`deny|${IDENTITY.rule}|kit#1`]), "the added dot must be visible through list()");
|
|
32
|
+
assert.ok(afterAdd.rev > held, "a landed write must ADVANCE the revision — a constant one is not a fence, it is a number");
|
|
33
|
+
assert.equal((await part[PERMISSION_RULE_WRITER].readRaw()).rev, afterAdd.rev, "every face must report ONE revision");
|
|
34
|
+
const rawBefore = await rawShape(part);
|
|
35
|
+
const stale = await writer.apply({ kind: "tighten-add", ...IDENTITY, add: add("kit", 2) }, { expectedRev: held });
|
|
36
|
+
assert.ok("conflict" in stale, "a spent expectedRev must answer `conflict`, not throw and not land");
|
|
37
|
+
const staleDelete = await writer.apply({ kind: "tighten-delete", tombstone: tombstoneFor([a1], { actor: "kit", counter: 8 }) }, { expectedRev: held });
|
|
38
|
+
assert.ok("conflict" in staleDelete, "a spent expectedRev must answer `conflict` on the delete arm too");
|
|
39
|
+
assert.equal(await rawShape(part), rawBefore, "a declined write must move nothing — rows, tombstones or revision");
|
|
40
|
+
const beforeDelete = (await part.list()).rev;
|
|
41
|
+
const removed = await writer.apply({ kind: "tighten-delete", tombstone: tombstoneFor([a1], { actor: "kit", counter: 9 }) }, { expectedRev: beforeDelete });
|
|
42
|
+
assert.ok(!("conflict" in removed) && !("refused" in removed), "a well-formed tighten-delete must land");
|
|
43
|
+
assert.deepEqual(liveDots((await part.list()).rules), new Set(), "the tombstoned dot must leave the live view");
|
|
44
|
+
assert.ok((await part.list()).rev > beforeDelete, "a landed delete must advance the revision too");
|
|
45
|
+
const raw = await part[PERMISSION_RULE_WRITER].readRaw();
|
|
46
|
+
assert.ok(liveDots(raw.rules).has(`deny|${IDENTITY.rule}|kit#1`), "the raw face must RETAIN a tombstoned add (list() filters; the evidence may not be filtered too)");
|
|
47
|
+
const tomb = raw.tombstones.find((t) => t.deletedBy.actor === "kit" && t.deletedBy.counter === 9);
|
|
48
|
+
assert.ok(tomb !== undefined, "the raw face must retain the tombstone a landed delete wrote");
|
|
49
|
+
assert.deepEqual({ behavior: tomb.behavior, rule: tomb.rule, scope: tomb.scope, removedDots: tomb.removedDots }, { behavior: IDENTITY.behavior, rule: IDENTITY.rule, scope: GLOBAL, removedDots: [a1.dot] }, "the retained tombstone must be the WHOLE one — a partial one identifies nothing");
|
|
50
|
+
});
|
|
51
|
+
run("a delta whose kind is outside the union is REFUSED — not folded as the arm it resembles", async () => {
|
|
52
|
+
for (const [label, payload] of [
|
|
53
|
+
["① a legal tombstone under an unnamed kind", { kind: "tighten-purge", tombstone: tombstoneFor([add("kit", 1)], { actor: "kit", counter: 9 }) }],
|
|
54
|
+
["② a legal add payload under an unnamed kind", { kind: "import-add", ...IDENTITY, add: add("kit", 2) }],
|
|
55
|
+
]) {
|
|
56
|
+
const part = await hooks.make();
|
|
57
|
+
const writer = part[PERMISSION_RULE_WRITER];
|
|
58
|
+
const seeded = add("kit", 7);
|
|
59
|
+
await writer.apply({ kind: "tighten-add", ...IDENTITY, add: seeded }, { expectedRev: (await part.list()).rev });
|
|
60
|
+
const before = await part.list();
|
|
61
|
+
const rawShapeBefore = await rawShape(part);
|
|
62
|
+
let threw = false;
|
|
63
|
+
try {
|
|
64
|
+
await writer.apply(payload, { expectedRev: before.rev });
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
threw = true;
|
|
68
|
+
}
|
|
69
|
+
assert.equal(threw, true, `${label}: the write face must refuse an unnamed delta kind`);
|
|
70
|
+
const after = await part.list();
|
|
71
|
+
assert.deepEqual(liveDots(after.rules), liveDots(before.rules), `${label}: a refused delta must not change the rows`);
|
|
72
|
+
assert.equal(after.rev, before.rev, `${label}: a refused delta must not advance the revision`);
|
|
73
|
+
assert.equal(await rawShape(part), rawShapeBefore, `${label}: a refused delta must move nothing in the RAW state either`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
await settle();
|
|
77
|
+
}
|
|
@@ -121,11 +121,22 @@ export type EffectiveMemoryScopes = {
|
|
|
121
121
|
state: "mounted";
|
|
122
122
|
reason?: never;
|
|
123
123
|
contract: "v2" | "legacy";
|
|
124
|
+
/** `shadowed` = how many of this scope's entries the session's write home retires from the
|
|
125
|
+
* DEFAULT read face by name (they stay reachable by id and through `includeShadowed`).
|
|
126
|
+
* PRESENT-IFF non-zero, and never on the write home's own row — absence is "none", which is
|
|
127
|
+
* why zero is not spelled. A mount-time snapshot: the tool faces re-derive it per call, so a
|
|
128
|
+
* later challenge or tombstone can make the live answer smaller than this number. */
|
|
124
129
|
scopes: Array<{
|
|
125
130
|
scope: string;
|
|
126
131
|
origin: "deployment" | "request";
|
|
132
|
+
shadowed?: number;
|
|
127
133
|
}>;
|
|
128
134
|
writeScope: string | null;
|
|
135
|
+
/** The project identity the assembly point DECLARED for this task, echoed verbatim
|
|
136
|
+
* ({@link import("./memory.js").MemoryProjectKey}). PRESENT-IFF the spec carried one:
|
|
137
|
+
* absence means the deployment said nothing, which is not the same as `{ absent }`, which
|
|
138
|
+
* means it looked and found none. */
|
|
139
|
+
projectKey?: import("./memory.js").MemoryProjectKey;
|
|
129
140
|
materializedResidue?: never;
|
|
130
141
|
} | {
|
|
131
142
|
state: "memoryless";
|