@sema-agent/core 7.4.0 → 7.5.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/agents/cascade.d.ts +2 -1
  3. package/dist/agents/peer-notices.d.ts +11 -1
  4. package/dist/agents/peer-session-drain.js +2 -0
  5. package/dist/agents/verify.d.ts +2 -1
  6. package/dist/core/ask-origin.d.ts +130 -0
  7. package/dist/core/ask-origin.js +35 -0
  8. package/dist/core/auto-mode-arming.d.ts +40 -1
  9. package/dist/core/auto-mode-arming.js +51 -3
  10. package/dist/core/auto-mode.d.ts +87 -10
  11. package/dist/core/auto-mode.js +34 -6
  12. package/dist/core/checkpoint-store.d.ts +41 -6
  13. package/dist/core/checkpoint-store.js +8 -0
  14. package/dist/core/hooks.d.ts +63 -19
  15. package/dist/core/hooks.js +37 -16
  16. package/dist/core/mcp.d.ts +47 -55
  17. package/dist/core/mcp.js +89 -31
  18. package/dist/core/park-selfcheck.js +3 -0
  19. package/dist/core/permission-rule-consent.d.ts +2 -11
  20. package/dist/core/permission-rule-consent.js +13 -62
  21. package/dist/core/permission-rule-org.d.ts +70 -54
  22. package/dist/core/permission-rule-org.js +47 -61
  23. package/dist/core/permission-rule-provider.d.ts +162 -0
  24. package/dist/core/permission-rule-provider.js +148 -0
  25. package/dist/core/permission-rule-session.d.ts +23 -19
  26. package/dist/core/permission-rule-session.js +5 -5
  27. package/dist/core/permission-rule-store.d.ts +46 -21
  28. package/dist/core/permission-rule-store.js +13 -6
  29. package/dist/core/permission-rule-sync.d.ts +2 -1
  30. package/dist/core/permission-rule-sync.js +11 -0
  31. package/dist/core/runner/assemble-result.d.ts +3 -2
  32. package/dist/core/runner/checkpoint-scope.d.ts +32 -0
  33. package/dist/core/runner/checkpoint-scope.js +4 -0
  34. package/dist/core/runner/contracts.d.ts +1878 -0
  35. package/dist/core/runner/contracts.js +1 -0
  36. package/dist/core/runner/denial-limit-arms.d.ts +57 -31
  37. package/dist/core/runner/denial-limit-arms.js +42 -17
  38. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  39. package/dist/core/runner/derived-route-fallback.js +16 -0
  40. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  41. package/dist/core/runner/prepare-announce-once.d.ts +83 -0
  42. package/dist/core/runner/prepare-announce-once.js +105 -0
  43. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  44. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  45. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  46. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  47. package/dist/core/runner/prepare-defer-classify.js +107 -0
  48. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  49. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  50. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  51. package/dist/core/runner/prepare-execution-env.js +86 -0
  52. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  53. package/dist/core/runner/prepare-file-history.js +383 -0
  54. package/dist/core/runner/prepare-hands-readface.d.ts +6 -8
  55. package/dist/core/runner/prepare-hands-readface.js +3 -3
  56. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  57. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  58. package/dist/core/runner/prepare-listings.d.ts +77 -0
  59. package/dist/core/runner/prepare-listings.js +76 -0
  60. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  61. package/dist/core/runner/prepare-lsp.js +27 -0
  62. package/dist/core/runner/prepare-memory.d.ts +1 -1
  63. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  64. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  65. package/dist/core/runner/prepare-permission-rules.d.ts +132 -0
  66. package/dist/core/runner/prepare-permission-rules.js +140 -0
  67. package/dist/core/runner/prepare-project-context.d.ts +131 -0
  68. package/dist/core/runner/prepare-project-context.js +150 -0
  69. package/dist/core/runner/prepare-prompt-inputs.d.ts +138 -0
  70. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  71. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  72. package/dist/core/runner/prepare-protocol-tools.js +182 -0
  73. package/dist/core/runner/prepare-question-face.d.ts +119 -0
  74. package/dist/core/runner/prepare-question-face.js +83 -0
  75. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  76. package/dist/core/runner/prepare-run-refs.js +39 -0
  77. package/dist/core/runner/prepare-safety-scan.d.ts +3 -2
  78. package/dist/core/runner/prepare-task.d.ts +11 -1815
  79. package/dist/core/runner/prepare-task.js +138 -2542
  80. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  81. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  82. package/dist/core/runner/prepare-wiring-manifest.d.ts +184 -0
  83. package/dist/core/runner/prepare-wiring-manifest.js +240 -0
  84. package/dist/core/runner/prepare-workspace-restore.d.ts +1 -27
  85. package/dist/core/runner/prepare-workspace-restore.js +1 -22
  86. package/dist/core/runner/rollback-stack.d.ts +32 -0
  87. package/dist/core/runner/rollback-stack.js +30 -0
  88. package/dist/core/runner/runtask.d.ts +11 -2
  89. package/dist/core/runner/runtask.js +27 -9
  90. package/dist/core/runner/workspace-path.d.ts +33 -0
  91. package/dist/core/runner/workspace-path.js +22 -0
  92. package/dist/core/sensitive-path-policy.d.ts +16 -0
  93. package/dist/core/sensitive-path-policy.js +1 -1
  94. package/dist/core/tool-policy.d.ts +57 -9
  95. package/dist/core/tool-policy.js +11 -0
  96. package/dist/core/types.d.ts +63 -51
  97. package/dist/core/wiring-manifest.d.ts +40 -3
  98. package/dist/core/wiring-manifest.js +4 -3
  99. package/dist/core/write-protect.d.ts +13 -2
  100. package/dist/core/write-protect.js +58 -29
  101. package/dist/engine/harness/types.d.ts +38 -16
  102. package/dist/engine/harness/types.js +25 -1
  103. package/dist/engine/session/session.d.ts +3 -11
  104. package/dist/index.d.ts +10 -6
  105. package/dist/index.js +9 -5
  106. package/dist/internal/harness.d.ts +1 -0
  107. package/dist/stores/file/adoption/adopt.d.ts +1 -1
  108. package/dist/stores/file/adoption/marker.d.ts +1 -1
  109. package/dist/stores/file/permission-rule-adopt.js +4 -3
  110. package/dist/stores/file/permission-rule-store.d.ts +65 -25
  111. package/dist/stores/file/permission-rule-store.js +215 -37
  112. package/dist/stores/file/task-list-store.d.ts +1 -1
  113. package/dist/tools/fs/read-face.d.ts +1 -1
  114. package/package.json +8 -2
  115. package/test/export-surface.snapshot.json +76 -28
@@ -585,12 +585,42 @@ export interface PromptEpochEntry extends SessionTreeEntryBase, PromptEpochDescr
585
585
  * replay remain as the second/third rungs of the resume-seed ladder for pre-entry sessions.
586
586
  * Present-only arms — an absent arm means "no announcement recorded for that face", same Q5
587
587
  * discipline as the checkpoint mirror.
588
+ *
589
+ * Two further arms ride the same snapshot for the prepare-time ONE-SHOT announcements, which are
590
+ * per-SESSION facts even though prepare runs once per run (a serving layer maps one user message to
591
+ * one run, so a per-run count re-announces them on every message):
592
+ * - `mcpFailed`: the MCP servers whose connect failure the model has been TOLD about, as the
593
+ * `{name, error}` pairs the reminder printed. The next run announces only the pairs that are not
594
+ * in this set (a changed error text is a new pair); a server that is no longer failing is dropped
595
+ * from the set without any "recovered" line, so a later failure of the same server announces again.
596
+ * - `advisories`: digests of the operator-lane advisory lines already emitted on this branch
597
+ * (the `(code, message)` of each `onError` warning, plus the model-facing dropped-tool entries)
598
+ * — the same `(code, text)` is emitted once per session. Grow-only, bounded (oldest dropped).
588
599
  */
589
- export interface AnnouncedListingEntry extends SessionTreeEntryBase {
600
+ export interface AnnouncedListingEntry extends SessionTreeEntryBase, AnnouncedListingSnapshot {
590
601
  type: "announced_listing";
602
+ }
603
+ /** One announced MCP connect failure — the pair the reminder line printed (both fields already
604
+ * neutralized + bounded at intake; compared field-wise, CC `deferred_tools_delta` form). */
605
+ export interface AnnouncedMcpFailure {
606
+ name: string;
607
+ error?: string;
608
+ }
609
+ /** The announced-listing snapshot's arms (see {@link AnnouncedListingEntry}). Present-only. */
610
+ export interface AnnouncedListingSnapshot {
591
611
  agents?: string[];
592
612
  skills?: string[];
593
613
  models?: string[];
614
+ mcpFailed?: AnnouncedMcpFailure[];
615
+ advisories?: string[];
616
+ }
617
+ /** The same snapshot as an APPEND input (borrowed arrays; the store copies through the shape gate). */
618
+ export interface AnnouncedListingSnapshotInput {
619
+ agents?: readonly string[];
620
+ skills?: readonly string[];
621
+ models?: readonly string[];
622
+ mcpFailed?: ReadonlyArray<AnnouncedMcpFailure>;
623
+ advisories?: readonly string[];
594
624
  }
595
625
  /**
596
626
  * REF-D3/D4 merge target: the active EnterWorktree session (M22 live-CC anchor —
@@ -732,6 +762,10 @@ export declare function normalizeWorkspaceState(value: unknown): WorkspaceState
732
762
  * a "name" is a short identifier, never a document. Oversize ⇒ structurally invalid (forgery signal). */
733
763
  export declare const ANNOUNCED_LISTING_MAX_NAMES = 500;
734
764
  export declare const ANNOUNCED_LISTING_MAX_NAME_CHARS = 200;
765
+ /** Bound (in CODE POINTS, the producer's unit) for an announced MCP failure's `error` text — intake
766
+ * caps it at 240 code points plus the truncation ellipsis; anything past this bound never came from
767
+ * the engine's own projection. The `name` rides the listing name bound, also counted in code points. */
768
+ export declare const ANNOUNCED_MCP_ERROR_MAX_CHARS = 400;
735
769
  export declare function normalizeCompactionStateCarrier(v: unknown): {
736
770
  thinkingLevel?: string;
737
771
  model?: {
@@ -790,11 +824,7 @@ export declare function isSyntheticApiErrorMessage(m: unknown): boolean;
790
824
  * the contamination lands on disk and every later `open()` throws a raw `TypeError` (not even a `SessionError`).
791
825
  * Unrecoverable through the API, which is why this belongs at the import door. */
792
826
  export declare function isOptionalDisplayString(v: unknown, max?: number): boolean;
793
- export declare function normalizeAnnouncedListing(v: unknown): {
794
- agents?: string[];
795
- skills?: string[];
796
- models?: string[];
797
- } | undefined;
827
+ export declare function normalizeAnnouncedListing(v: unknown): AnnouncedListingSnapshot | undefined;
798
828
  /** All persisted session tree entry variants. */
799
829
  export type SessionTreeEntry = MessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | CompactionEntry | CustomEntry | CustomMessageEntry | LabelEntry | SessionInfoEntry | LeafEntry | PromptEpochEntry | AnnouncedListingEntry | GitAnnouncementEntry | WorkspaceStateEntry | ReminderMarkEntry;
800
830
  export interface SessionContext {
@@ -916,18 +946,10 @@ export interface Session<TMetadata extends SessionMetadata = SessionMetadata> {
916
946
  appendSessionName(name: string): Promise<string>;
917
947
  /** design/150 (RB-25): persist the listing announced name-sets as a first-class
918
948
  * {@link AnnouncedListingEntry} (snapshot semantics; see `Session.appendAnnouncedListing`). */
919
- appendAnnouncedListing(snapshot: {
920
- agents?: readonly string[];
921
- skills?: readonly string[];
922
- models?: readonly string[];
923
- }): Promise<string>;
949
+ appendAnnouncedListing(snapshot: AnnouncedListingSnapshotInput): Promise<string>;
924
950
  /** design/150 (RB-25): nearest announced-listing snapshot on the active branch (first rung of the
925
951
  * resume-seed ladder); undefined ⇒ pre-entry session. */
926
- getAnnouncedListing(): Promise<{
927
- agents?: string[];
928
- skills?: string[];
929
- models?: string[];
930
- } | undefined>;
952
+ getAnnouncedListing(): Promise<AnnouncedListingSnapshot | undefined>;
931
953
  /** design/155: persist the settle-time workspace state (tracked cwd + active worktree) as a
932
954
  * first-class {@link WorkspaceStateEntry} (snapshot semantics; see `Session.appendWorkspaceState`). */
933
955
  appendWorkspaceState(state: WorkspaceState): Promise<string>;
@@ -127,6 +127,7 @@ export function normalizeWorkspaceState(value) {
127
127
  }
128
128
  export const ANNOUNCED_LISTING_MAX_NAMES = 500;
129
129
  export const ANNOUNCED_LISTING_MAX_NAME_CHARS = 200;
130
+ export const ANNOUNCED_MCP_ERROR_MAX_CHARS = 400;
130
131
  const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
131
132
  export function normalizeCompactionStateCarrier(v) {
132
133
  if (typeof v !== "object" || v === null)
@@ -180,14 +181,37 @@ export function normalizeAnnouncedListing(v) {
180
181
  }
181
182
  return out;
182
183
  };
184
+ const cps = (t) => [...t].length;
185
+ const failureArm = (a) => {
186
+ if (a === undefined)
187
+ return undefined;
188
+ if (!Array.isArray(a) || a.length > ANNOUNCED_LISTING_MAX_NAMES)
189
+ return null;
190
+ const out = [];
191
+ for (const e of a) {
192
+ if (typeof e !== "object" || e === null)
193
+ return null;
194
+ const { name, error } = e;
195
+ if (typeof name !== "string" || cps(name) > ANNOUNCED_LISTING_MAX_NAME_CHARS)
196
+ return null;
197
+ if (error !== undefined && (typeof error !== "string" || cps(error) > ANNOUNCED_MCP_ERROR_MAX_CHARS))
198
+ return null;
199
+ out.push({ name, ...(error !== undefined ? { error } : {}) });
200
+ }
201
+ return out;
202
+ };
183
203
  const agents = arm(src.agents);
184
204
  const skills = arm(src.skills);
185
205
  const models = arm(src.models);
186
- if (agents === null || skills === null || models === null)
206
+ const mcpFailed = failureArm(src.mcpFailed);
207
+ const advisories = arm(src.advisories);
208
+ if (agents === null || skills === null || models === null || mcpFailed === null || advisories === null)
187
209
  return undefined;
188
210
  return {
189
211
  ...(agents !== undefined ? { agents } : {}),
190
212
  ...(skills !== undefined ? { skills } : {}),
191
213
  ...(models !== undefined ? { models } : {}),
214
+ ...(mcpFailed !== undefined ? { mcpFailed } : {}),
215
+ ...(advisories !== undefined ? { advisories } : {}),
192
216
  };
193
217
  }
@@ -1,6 +1,6 @@
1
1
  import type { ImageContent, TextContent } from "../llm/index.js";
2
2
  import type { AgentMessage } from "../loop/types.js";
3
- import type { GitAnnouncementState, Session, SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry, WorkspaceState } from "../harness/types.js";
3
+ import type { AnnouncedListingSnapshot, AnnouncedListingSnapshotInput, GitAnnouncementState, Session, SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry, WorkspaceState } from "../harness/types.js";
4
4
  import type { BuildContextOptions } from "../harness/types.js";
5
5
  import type { PromptEpochDescriptor } from "../../prompt-assembly/epoch.js";
6
6
  /** Build model context from the active session branch and its latest state markers.
@@ -40,11 +40,7 @@ export declare class StoredSession<TMetadata extends SessionMetadata = SessionMe
40
40
  * Called best-effort at every listing COMMIT point (the run loop's announce mirror sites) —
41
41
  * an append failure degrades to the checkpoint-mirror/transcript-probe rungs, never fails the run.
42
42
  */
43
- appendAnnouncedListing(snapshot: {
44
- agents?: readonly string[];
45
- skills?: readonly string[];
46
- models?: readonly string[];
47
- }): Promise<string>;
43
+ appendAnnouncedListing(snapshot: AnnouncedListingSnapshotInput): Promise<string>;
48
44
  /**
49
45
  * Git-status frame announced state (branch-authority mirror): persist the `(kind, hash)` tuple —
50
46
  * announced form carries the frame's own entry id; a compaction restates the tuple as `pending`
@@ -121,11 +117,7 @@ export declare class StoredSession<TMetadata extends SessionMetadata = SessionMe
121
117
  * loss). A floor-anchor seam (the getEpochAnchor analog) is deliberately deferred until a real
122
118
  * deployment hits that window.
123
119
  */
124
- getAnnouncedListing(): Promise<{
125
- agents?: string[];
126
- skills?: string[];
127
- models?: string[];
128
- } | undefined>;
120
+ getAnnouncedListing(): Promise<AnnouncedListingSnapshot | undefined>;
129
121
  /**
130
122
  * Recover the pinned prompt epoch for the ACTIVE branch (stage S3). Walks the branch from the
131
123
  * leaf backwards: the nearest `compaction` entry's `details.promptEpoch` restatement wins (the
package/dist/index.d.ts CHANGED
@@ -50,6 +50,7 @@ export { decideAutoPromote, deriveTripwire, FROZEN_DENYLIST_FLOOR, type AutoProm
50
50
  export { runCascade, type CascadeRung, type CascadeConfig, type CascadeAttempt, type CascadeRunResult, type GateVerdict, } from "./agents/cascade.js";
51
51
  export { pgQuery, mysqlQuery, sqliteQuery } from "./tools/sql-adapters.js";
52
52
  export { materializeMcpTools, MCP_PREFIX, type MaterializedMcp, type McpServerStatus, type McpRefreshResult } from "./core/mcp.js";
53
+ export { MCP_FAILURE_CODES, mcpFailureCodeOf, type McpFailureCode } from "./core/mcp.js";
53
54
  export { materializeA2aTools, A2aRpcError, type MaterializedA2a, type A2aPeerStatus, type A2aRefreshResult, type A2aToolAxis } from "./core/a2a.js";
54
55
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf, type ProtocolNamespace, type ProtocolId } from "./core/protocol-table.js";
55
56
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, type SessionPolicyStore, type SessionPermissionRules, type StoredSessionRules, type SessionRulesRecord, type PutRulesOptions, } from "./core/session-policy-store.js";
@@ -119,7 +120,7 @@ export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/f
119
120
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
120
121
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
121
122
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, type SemaProvenance, } from "./core/task-notification.js";
122
- export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type WiringManifest, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
123
+ export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type WiringManifest, type WiringManifestMcpEntry, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
123
124
  export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, type ParkSelfCheckResult, type ParkProbeFinding, type ParkProbeFindingCode, } from "./core/park-selfcheck.js";
124
125
  export { type StoreDurability } from "./core/checkpoint-store.js";
125
126
  export { type StoreFidelity } from "./core/checkpoint-store.js";
@@ -145,7 +146,9 @@ export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_N
145
146
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
146
147
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
147
148
  export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, type AskDenyResolution, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
148
- export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
149
+ export { type AskOrigin, ASK_ORIGINS, isAskOrigin, classifierMayAnswer } from "./core/ask-origin.js";
150
+ export { type AskCarry } from "./core/hooks.js";
151
+ export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
149
152
  export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
150
153
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
151
154
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -168,13 +171,14 @@ export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRul
168
171
  * allowed and widening is not.
169
172
  */
170
173
  export { parseAllowRuleText, formatAllowRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, type UncoveredSegmentDetail, type EditedRuleBreadthWarning, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, type PersistedAllowRule, type RuleTombstone, type RuleScope, type RuleDot, type RuleAdd, type RuleAddOrigin, type RuleOffer, type RuleOfferBatchMember, type SegmentRuleSuggestion, type SegmentCoverage, type RuleReject, type RuleRejectCode, type ParsedAllowRule, type PersistedRuleTool, type PersistedRuleMatch, } from "./core/permission-rule-model.js";
171
- export { removePersistedRule, applyTombstones, sameScope, isValidConsentScope, isValidDurableScope, InMemoryPermissionRuleStore, EMPTY_RULE_STORE, type PermissionRuleStore, type PermissionRuleStoreProvider, type StoredAllowRules, type RemoveResult, type PutResult, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, type RuleSyncState, type RuleSyncFrontier, type RuleSyncDrop, type RuleSyncLandingReport, type RuleOwner, type QuarantinedRuleAdd, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, type PermissionRuleWriter, type WritablePermissionRuleStore, type RuleWriteDelta, type RuleAddDelta, type RuleDeleteDelta, type RuleSyncJoinDelta, type RawRuleSyncState, type RedemptionAuthorization, } from "./core/permission-rule-store.js";
174
+ export { removePersistedRule, applyTombstones, sameScope, isValidConsentScope, isValidDurableScope, InMemoryDurableRulePartition, EMPTY_DURABLE_RULE_PARTITION, type DurableRulePartition, type DurableRulePartitionProvider, type RuleWriteOutcome, type StoredAllowRules, type RemoveResult, type PutResult, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, type RuleSyncState, type RuleSyncFrontier, type RuleSyncDrop, type RuleSyncLandingReport, type RuleOwner, type QuarantinedRuleAdd, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, type PermissionRuleWriter, type WritableDurableRulePartition, type RuleWriteDelta, type RuleAddDelta, type RuleDeleteDelta, type RuleSyncJoinDelta, type RawRuleSyncState, type RedemptionAuthorization, } from "./core/permission-rule-store.js";
172
175
  export { syncPermissionRules, parseRuleSyncResponse, PERMISSION_RULE_SYNC_PATH, LOCAL_OWNER_UNSYNCABLE_CODE, type PermissionRuleSyncTransport, type PermissionRuleSyncResult, type RuleSyncRequestBody, type RuleSyncResponseBody, } from "./core/permission-rule-sync.js";
173
- export { InMemorySessionRuleOverlay, type SessionRuleOverlay, type SessionRuleOverlayAdd, type SessionRuleOverlayApplyResult, } from "./core/permission-rule-session.js";
174
- export { createOrgRuleOverlay, orgRuleVerdictFor, effectivePermissionRules, orgRuleStatePersistenceOf, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, type OrgPermissionRule, type OrgRuleSnapshot, type OrgRuleSnapshotProvider, type OrgRuleStatePersistence, type PersistedOrgRuleState, type OrgRuleOverlay, type OrgOverlayResolution, type OrgOverlayStatus, type EffectivePermissionRule, } from "./core/permission-rule-org.js";
176
+ export { createPermissionRuleStoreProvider, effectivePermissionRules, effectiveOrThrow, ruleSourceOf, type PermissionRuleStore, type PermissionRuleStoreProvider, type PermissionRuleStoreConfig, type EffectivePermissionRules, type EffectivePermissionRule, type RemovedPermissionRule, type RuleSource, } from "./core/permission-rule-provider.js";
177
+ export { InMemorySessionRulePartition, type SessionRulePartition, type SessionRuleAdd, type SessionRuleApplyResult, } from "./core/permission-rule-session.js";
178
+ export { orgRuleVerdictFor, orgRuleShadows, unenforceableOrgRules, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, type OrgPermissionRule, type OrgRuleSnapshot, type OrgRuleSnapshotProvider, type OrgRuleStatePersistence, type PersistedOrgRuleState, type OrgRulePartitionConfig, type OrgRuleResolution, type OrgRuleStatus, } from "./core/permission-rule-org.js";
175
179
  export { RULE_SYNC_DROP_CODES, type RuleSyncDropReason, type RuleQuarantineReason } from "./core/governance-codes.js";
176
180
  export { prepareCardApproval, confirmRuleApproval, type ConfirmResult, type ConfirmRefusalReason, precheckEditedRuleText, type EditedRuleTextPrecheck, redeemRuleTicket, redeemRuleBatch, prepareCcImport, prepareStarterBatch, mintRuleTicket, STARTER_RULES, InMemoryRuleApprovalRecordStore, ruleOffersOfRecord, type RuleTicket, type RuleCandidate, type RuleApprovalKind, type RuleApprovalRecord, type RuleApprovalRecordStore, type RuleOffer2, type StaleRuleApprovalRecord, type RuleConsentDeps, type RedeemResult, type RedeemedBatchMember, type CcImportLayer, type ImportedSettingsLayer, type ImportPreview, } from "./core/permission-rule-consent.js";
177
- export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-store.js";
181
+ export { FileDurableRulePartitionProvider, ORG_STATE_FILE } from "./stores/file/permission-rule-store.js";
178
182
  export { adoptFilePermissionRuleStore, type AdoptFileRuleStoreResult } from "./stores/file/permission-rule-adopt.js";
179
183
  export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, type AdoptionErrorCode, type AdoptionSource, type AdoptionReport, type AdoptionReceipt, type AdoptionLegReport, type AffectedDeploymentConfig, type RootAdoptionFile, } from "./stores/file/adoption/marker.js";
180
184
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, type AdoptionStatus, type AdoptLocalDataRootOptions, type AdoptLocalDataRootResult, type AdoptionCarriageLeg, type AdoptionCarriageLegContext, type AdoptionConfigWitnessReceipt, } from "./stores/file/adoption/adopt.js";
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ export { decideAutoPromote, deriveTripwire, FROZEN_DENYLIST_FLOOR, } from "./cor
38
38
  export { runCascade, } from "./agents/cascade.js";
39
39
  export { pgQuery, mysqlQuery, sqliteQuery } from "./tools/sql-adapters.js";
40
40
  export { materializeMcpTools, MCP_PREFIX } from "./core/mcp.js";
41
+ export { MCP_FAILURE_CODES, mcpFailureCodeOf } from "./core/mcp.js";
41
42
  export { materializeA2aTools, A2aRpcError } from "./core/a2a.js";
42
43
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf } from "./core/protocol-table.js";
43
44
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, } from "./core/session-policy-store.js";
@@ -120,7 +121,9 @@ export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTIN
120
121
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
121
122
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
122
123
  export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, ASK_DENY_RESOLUTION_VALUES, isAskDenyResolution, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
123
- export { parseAutoModeResponse, createAutoModeDecider, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, } from "./core/auto-mode.js";
124
+ export { ASK_ORIGINS, isAskOrigin, classifierMayAnswer } from "./core/ask-origin.js";
125
+ export {} from "./core/hooks.js";
126
+ export { parseAutoModeResponse, createAutoModeDecider, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, } from "./core/auto-mode.js";
124
127
  export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
125
128
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
126
129
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -128,13 +131,14 @@ export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoMo
128
131
  export { rebuildAutoModeDecider, } from "./core/auto-mode-rebuild.js";
129
132
  export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRule, wildcardMatch, isNamespacedCoveringRuleName, namespacedRuleNameCovers, } from "./core/permission-rules.js";
130
133
  export { parseAllowRuleText, formatAllowRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, } from "./core/permission-rule-model.js";
131
- export { removePersistedRule, applyTombstones, sameScope, isValidConsentScope, isValidDurableScope, InMemoryPermissionRuleStore, EMPTY_RULE_STORE, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, } from "./core/permission-rule-store.js";
134
+ export { removePersistedRule, applyTombstones, sameScope, isValidConsentScope, isValidDurableScope, InMemoryDurableRulePartition, EMPTY_DURABLE_RULE_PARTITION, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, } from "./core/permission-rule-store.js";
132
135
  export { syncPermissionRules, parseRuleSyncResponse, PERMISSION_RULE_SYNC_PATH, LOCAL_OWNER_UNSYNCABLE_CODE, } from "./core/permission-rule-sync.js";
133
- export { InMemorySessionRuleOverlay, } from "./core/permission-rule-session.js";
134
- export { createOrgRuleOverlay, orgRuleVerdictFor, effectivePermissionRules, orgRuleStatePersistenceOf, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, } from "./core/permission-rule-org.js";
136
+ export { createPermissionRuleStoreProvider, effectivePermissionRules, effectiveOrThrow, ruleSourceOf, } from "./core/permission-rule-provider.js";
137
+ export { InMemorySessionRulePartition, } from "./core/permission-rule-session.js";
138
+ export { orgRuleVerdictFor, orgRuleShadows, unenforceableOrgRules, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, } from "./core/permission-rule-org.js";
135
139
  export { RULE_SYNC_DROP_CODES } from "./core/governance-codes.js";
136
140
  export { prepareCardApproval, confirmRuleApproval, precheckEditedRuleText, redeemRuleTicket, redeemRuleBatch, prepareCcImport, prepareStarterBatch, mintRuleTicket, STARTER_RULES, InMemoryRuleApprovalRecordStore, ruleOffersOfRecord, } from "./core/permission-rule-consent.js";
137
- export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-store.js";
141
+ export { FileDurableRulePartitionProvider, ORG_STATE_FILE } from "./stores/file/permission-rule-store.js";
138
142
  export { adoptFilePermissionRuleStore } from "./stores/file/permission-rule-adopt.js";
139
143
  export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, } from "./stores/file/adoption/marker.js";
140
144
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, } from "./stores/file/adoption/adopt.js";
@@ -23,6 +23,7 @@ export { InMemorySessionStorage } from "../engine/session/memory-storage.js";
23
23
  export { InMemorySessionRepo } from "../engine/session/memory-repo.js";
24
24
  export { uuidv7 } from "../engine/session/uuid.js";
25
25
  export { SessionError } from "../engine/harness/types.js";
26
+ export type { AnnouncedListingSnapshot, AnnouncedListingSnapshotInput, AnnouncedMcpFailure } from "../engine/harness/types.js";
26
27
  export { normalizeGitAnnouncement } from "../engine/harness/types.js";
27
28
  export { gitFrameContextVisible } from "../engine/session/session.js";
28
29
  export { isSyntheticApiErrorMessage } from "../engine/harness/types.js";
@@ -38,7 +38,7 @@
38
38
  * independent store faces — `new FileMemoryStore(root)`, the mailbox family). It keeps its open
39
39
  * handles, and I6 is a construction-time gate, not a write-time one;
40
40
  * · a NEW construction whose anchor is not the data root. Two faces take a directory that need not
41
- * BE the data root — `FilePermissionRuleStoreProvider` (the rule bucket) and
41
+ * BE the data root — `FileDurableRulePartitionProvider` (the rule bucket) and
42
42
  * `createFileTaskListStore` (the document's own directory) — so each takes its root anchor as an
43
43
  * explicit option, and a caller that omits it constructs unGATED during a freeze. That default is
44
44
  * what this arc's own nested rule leg rides (it must reach the bucket while the marker is in
@@ -197,7 +197,7 @@ export declare function writeRootAdoptionFile(root: string, content: RootAdoptio
197
197
  *
198
198
  * WHO calls it, precisely: every store constructor whose argument IS the data root (that is most of
199
199
  * the family — they derive their own subdirectory from it). The two faces whose argument is a
200
- * caller-chosen directory instead — `FilePermissionRuleStoreProvider` (a rule bucket) and
200
+ * caller-chosen directory instead — `FileDurableRulePartitionProvider` (a rule bucket) and
201
201
  * `createFileTaskListStore` (the document's directory) — cannot anchor on their argument, so each
202
202
  * takes the data root as an explicit option and is gated only when given one. `root` here is always
203
203
  * the ANCHOR, never "wherever this store keeps its bytes": passing a subdirectory reads a marker that
@@ -1,8 +1,9 @@
1
1
  import { existsSync, renameSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { writerOf } from "../../core/permission-rule-store.js";
4
+ import { createPermissionRuleStoreProvider } from "../../core/permission-rule-provider.js";
4
5
  import { syncPermissionRules } from "../../core/permission-rule-sync.js";
5
- import { FilePermissionRuleStoreProvider, LOCAL_OWNER_FILE, principalRuleFileName, readRuleAdoptionFile, writeRuleAdoptionFile, } from "./permission-rule-store.js";
6
+ import { FileDurableRulePartitionProvider, LOCAL_OWNER_FILE, principalRuleFileName, readRuleAdoptionFile, writeRuleAdoptionFile, } from "./permission-rule-store.js";
6
7
  export async function adoptFilePermissionRuleStore(opts) {
7
8
  if (typeof opts.toPrincipal !== "string" || opts.toPrincipal === "") {
8
9
  throw new Error("adoptFilePermissionRuleStore requires the adopting principal — adoption is the act of giving this bucket an identity");
@@ -26,7 +27,7 @@ export async function adoptFilePermissionRuleStore(opts) {
26
27
  else if (existsSync(targetFile)) {
27
28
  throw new Error(`the target bucket for "${opts.toPrincipal}" already exists — merging two existing buckets goes through an explicit sync join, not a rename`);
28
29
  }
29
- const provider = new FilePermissionRuleStoreProvider(opts.dir, opts.onError);
30
+ const provider = new FileDurableRulePartitionProvider(opts.dir, opts.onError);
30
31
  try {
31
32
  const lockProbe = writerOf(provider.forPrincipal(opts.toPrincipal));
32
33
  if (lockProbe === undefined)
@@ -55,7 +56,7 @@ export async function adoptFilePermissionRuleStore(opts) {
55
56
  if (phase < 5) {
56
57
  try {
57
58
  syncResult = await syncPermissionRules({
58
- provider,
59
+ provider: createPermissionRuleStoreProvider({ durable: provider }),
59
60
  principal: opts.toPrincipal,
60
61
  transport: opts.transport,
61
62
  ...(opts.now !== undefined ? { now: opts.now } : {}),
@@ -1,5 +1,18 @@
1
1
  /**
2
- * design/179 §5/§8 — the file backend for persisted allow rules.
2
+ * design/179 §5/§8 / design/389 — the file backend of the permission-rule store's DURABLE partition (one
3
+ * bucket file per owner), plus the file home of the ORG partition's durable state (`org.json`).
4
+ *
5
+ * ## Schema 3 and the one-time migration
6
+ *
7
+ * Pre-389 files (schema 1/2) kept the org last-known-good snapshot as an `org` block INSIDE each
8
+ * principal's bucket file — deployment-wide policy stored per person. Schema 3 drops that block; the org
9
+ * state lives in one `org.json` beside the buckets. `FileDurableRulePartitionProvider` migrates every
10
+ * legacy bucket file ONCE at construction (under the writer lock: the migration is a write, and a live
11
+ * older writer over the same directory is refused rather than raced), folding each file's `org` block
12
+ * into `org.json` by the anti-rollback rule (the highest high-water wins; an equal revision with
13
+ * different content keeps what is already installed and discloses). After that the reader accepts
14
+ * schema 3 ONLY — a 1/2/4 file is refused whole, loudly (zero rules + disclosure). There is no second
15
+ * read path for the old shapes: the migrator is the only code that reads them, and it runs once.
3
16
  *
4
17
  * ## Why this store builds its own symlink refusal
5
18
  *
@@ -43,23 +56,18 @@
43
56
  * For this backend the trust is host = user, exactly as for the settings file it mirrors.
44
57
  */
45
58
  import type { PersistedAllowRule, RuleTombstone } from "../../core/permission-rule-model.js";
46
- import type { PermissionRuleStore, PermissionRuleStoreProvider, PermissionRuleWriter, QuarantinedRuleAdd, RuleOwner, RuleSyncFrontier, StoredAllowRules, WritablePermissionRuleStore } from "../../core/permission-rule-store.js";
59
+ import type { DurableRulePartition, DurableRulePartitionProvider, PermissionRuleWriter, QuarantinedRuleAdd, RuleOwner, RuleSyncFrontier, StoredAllowRules, WritableDurableRulePartition } from "../../core/permission-rule-store.js";
47
60
  import { PERMISSION_RULE_WRITER } from "../../core/permission-rule-store.js";
48
- import type { PersistedOrgRuleState } from "../../core/permission-rule-org.js";
61
+ import type { OrgRuleStatePersistence, PersistedOrgRuleState } from "../../core/permission-rule-org.js";
49
62
  import type { StoreDurability, StoreFidelity } from "../../core/checkpoint-store.js";
50
63
  /**
51
- * The on-disk shape. `schemaVersion` is the only field a future reader may rely on before validating.
52
- *
53
- * v1 v2 (design/182 §8.3): three OPTIONAL blocks — `sync` (observation vector + round timestamp),
54
- * `org` (last-known-good org snapshot + anti-rollback high-water) and `quarantined` (rows moved out of
55
- * the live view, bytes preserved). The five v1 fields are byte-identical in meaning. A file only ever
56
- * ESCALATES to v2 when a write actually carries v2 content (a sync round, an org install, a
57
- * quarantine): a deployment that never syncs keeps writing v1 files forever, and a v1 reader handed a
58
- * v2 file refuses the whole file loudly (zero rules + disclosure — fail-closed, more asks, and the
59
- * documented cost of rolling the engine back under an already-synced store).
64
+ * The on-disk shape of one bucket. `schemaVersion` is the only field a future reader may rely on before
65
+ * validating. Schema 3 (design/389): the five core fields plus the OPTIONAL `sync` block (observation
66
+ * vector + round timestamp) and `quarantined` area (rows moved out of the live view, bytes preserved).
67
+ * The org state is NOT here see {@link OrgStateFile}.
60
68
  */
61
69
  interface RuleFile {
62
- schemaVersion: 1 | 2;
70
+ schemaVersion: 3;
63
71
  actor: string;
64
72
  counter: number;
65
73
  rev: number;
@@ -69,10 +77,27 @@ interface RuleFile {
69
77
  observedVector?: RuleSyncFrontier;
70
78
  lastRoundAtMs?: number;
71
79
  };
72
- org?: PersistedOrgRuleState;
73
80
  quarantined?: QuarantinedRuleAdd[];
74
81
  checksum: string;
75
82
  }
83
+ /** The deployment-wide org state file: last-known-good snapshot + anti-rollback high-water, one blob. */
84
+ interface OrgStateFile {
85
+ schemaVersion: 1;
86
+ state: PersistedOrgRuleState;
87
+ checksum: string;
88
+ }
89
+ /** The org partition's durable home in a rule directory. */
90
+ export declare const ORG_STATE_FILE = "org.json";
91
+ /** Written once the directory is known to hold only schema-3 buckets. Its presence ends the legacy
92
+ * scan for good: a schema 1/2 bucket that appears AFTERWARDS (an older engine writing after the
93
+ * cutover) is refused by the reader on every later construction too, never re-admitted by a restart. */
94
+ export declare const SCHEMA3_MARKER_FILE = ".rule-schema-3";
95
+ /** The anti-rollback contract's DESIGNED refusal (a lower high-water mark, or different content under
96
+ * the same revision) — distinguished by class from every other install failure, because the two are
97
+ * handled in opposite directions by the migration: a refusal means the block is superseded and may be
98
+ * dropped; anything else means the block has no second home yet and the legacy copy must be kept. */
99
+ export declare class OrgRevisionRefusal extends Error {
100
+ }
76
101
  /** The local-owner bucket's FIXED file name — never a principal-hash path (identity absence is a
77
102
  * structural position, not a name; design/182 §4.5). Cannot collide with `forPrincipal` names, which
78
103
  * are 64 hex characters. */
@@ -100,7 +125,7 @@ export declare function readRuleAdoptionFile(dir: string): RuleAdoptionFile | un
100
125
  /** Atomically publish the adoption marker (same write-temp → fsync → rename discipline as the store). */
101
126
  export declare function writeRuleAdoptionFile(dir: string, content: RuleAdoptionFile): void;
102
127
  /** One principal's rule file. */
103
- declare class FilePermissionRuleStore implements WritablePermissionRuleStore {
128
+ declare class FileDurableRulePartition implements WritableDurableRulePartition {
104
129
  private readonly dir;
105
130
  private readonly file;
106
131
  private readonly acquireWriteLock;
@@ -126,12 +151,6 @@ declare class FilePermissionRuleStore implements WritablePermissionRuleStore {
126
151
  list(): Promise<StoredAllowRules>;
127
152
  /** The quarantine area (design/182 §5.2/§8.3): introspection only — never part of `list()`. */
128
153
  quarantined(): Promise<QuarantinedRuleAdd[]>;
129
- /** design/182 §7.4 — the durable org block: last-known-good snapshot + anti-rollback high-water,
130
- * installed in ONE atomic publish, surviving restarts. Reading takes no lock (the gate reads);
131
- * installing takes the writer lock (it is a write) but does not move `rev` — nothing about the
132
- * PERSONAL rule set changed, and an OCC holder must not be disturbed (the `nextDot` precedent). */
133
- readOrgState(): Promise<PersistedOrgRuleState | undefined>;
134
- installOrgState(state: PersistedOrgRuleState): Promise<void>;
135
154
  /**
136
155
  * The current file, for a WRITE.
137
156
  *
@@ -180,17 +199,38 @@ declare class FilePermissionRuleStore implements WritablePermissionRuleStore {
180
199
  * provider without a `dataRoot` precisely because it runs while the marker is in flight, and the
181
200
  * bucket-level marker (read in `forPrincipal`/`forLocalOwner`) is what governs writes inside that arc.
182
201
  */
183
- export declare class FilePermissionRuleStoreProvider implements PermissionRuleStoreProvider {
202
+ export declare class FileDurableRulePartitionProvider implements DurableRulePartitionProvider {
184
203
  private readonly dir;
185
204
  private readonly onError?;
186
205
  private lock;
206
+ private orgPersistence;
187
207
  constructor(dir: string, onError?: ((message: string) => void) | undefined, opts?: {
188
208
  /** The data root whose adoption marker gates construction (design/183 I6). Absent ⇒ no root gate. */
189
209
  dataRoot?: string;
190
210
  });
211
+ /**
212
+ * Fold every legacy bucket file in `dir` into schema 3 and its `org` block into `org.json`. A no-op on a
213
+ * directory with no legacy file (zero writes, the lock untouched — the second construction over a
214
+ * migrated directory is byte-silent). A legacy file whose checksum or shape does not hold is NOT
215
+ * migrated: its bytes stay where they are for a person to look at, and the reader keeps refusing it
216
+ * whole. A live writer holding the directory (an older engine still running over it, or a directory
217
+ * this process cannot write) DEFERS the migration: the construction succeeds as a read face, the
218
+ * deferral is disclosed, and the marker stays unwritten so the next construction that can take the
219
+ * lock performs the cutover. Two engine versions writing one rule directory is still not a supported
220
+ * shape — the old engine's buckets are refused whole until they are migrated.
221
+ */
222
+ private migrateLegacyBuckets;
223
+ /** Publish the schema-3 completion marker (idempotent, atomic, no lock: its content never changes and
224
+ * a concurrent writer of the same bytes is harmless). A failure to write it is disclosed, not thrown —
225
+ * the next construction simply scans again, which is the safe direction. */
226
+ private writeSchema3Marker;
227
+ /** The org partition's durable home in this directory (design/182 §7.4 contract; `org.json`). Shares
228
+ * this provider's writer lock — an install is a write. */
229
+ orgStatePersistence(): OrgRuleStatePersistence;
230
+ private fileOrgPersistence;
191
231
  /** Take the writer lock, once, on first use of a write face. Reading never calls this. */
192
232
  private acquireWriteLock;
193
- forPrincipal(principal: string | undefined): PermissionRuleStore;
233
+ forPrincipal(principal: string | undefined): DurableRulePartition;
194
234
  /** The read-only resolution of an adoption SOURCE: post-flip (phase ≥ 4 / terminal) always the
195
235
  * target; pre-flip, whichever path currently holds the bytes (source first — it is the pre-flip
196
236
  * truth when both somehow exist, which the resume path refuses loudly anyway). */
@@ -202,7 +242,7 @@ export declare class FilePermissionRuleStoreProvider implements PermissionRuleSt
202
242
  * reads, not the other way round, so a crash between marker and host-config change cannot fork writes
203
243
  * into a retired bucket. A corrupt marker throws (fail-closed) rather than guessing an owner.
204
244
  */
205
- forLocalOwner(): PermissionRuleStore;
245
+ forLocalOwner(): DurableRulePartition;
206
246
  /** Release the writer lock. A long-lived deployment holds it for its lifetime; a test or a short-lived
207
247
  * tool releases so the next holder is not told a live process owns the directory. */
208
248
  dispose(): void;
@@ -211,4 +251,4 @@ export declare class FilePermissionRuleStoreProvider implements PermissionRuleSt
211
251
  * and the resolver can never disagree about where a principal lives. */
212
252
  export declare function principalRuleFileName(principal: string): string;
213
253
  export { LOCAL_OWNER_FILE };
214
- export type { FilePermissionRuleStore, RuleFile };
254
+ export type { FileDurableRulePartition, RuleFile, OrgStateFile };