@sema-agent/core 5.47.0 → 5.49.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 +115 -0
- package/dist/agents/agent-transcript-tool.d.ts +4 -0
- package/dist/agents/agent-transcript-tool.js +10 -3
- package/dist/agents/send-message-tool.d.ts +43 -1
- package/dist/agents/send-message-tool.js +50 -11
- package/dist/agents/subagent.d.ts +18 -0
- package/dist/agents/subagent.js +102 -2
- package/dist/agents/teacher.d.ts +25 -1
- package/dist/agents/teacher.js +85 -12
- package/dist/config/defaults.d.ts +20 -0
- package/dist/config/defaults.js +5 -0
- package/dist/core/background-agent-store.d.ts +1 -0
- package/dist/core/background-agent-store.js +14 -0
- package/dist/core/mcp.d.ts +6 -1
- package/dist/core/mcp.js +34 -7
- package/dist/core/memory-engine/delegation-settlement.d.ts +27 -0
- package/dist/core/memory-engine/delegation-settlement.js +31 -4
- package/dist/core/memory-engine/dual-root.js +11 -0
- package/dist/core/memory-engine/engine.d.ts +6 -1
- package/dist/core/memory-engine/engine.js +136 -21
- package/dist/core/memory-engine/memory-backend-contract.js +33 -0
- package/dist/core/memory-engine/origin-clearance.d.ts +19 -0
- package/dist/core/memory-engine/origin-clearance.js +10 -0
- package/dist/core/memory-engine/provenance-wording.d.ts +15 -1
- package/dist/core/memory-engine/provenance-wording.js +1 -0
- package/dist/core/memory-engine/tools.js +6 -4
- package/dist/core/reminder-disclosure.d.ts +90 -0
- package/dist/core/reminder-disclosure.js +64 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +6 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-task.d.ts +15 -0
- package/dist/core/runner/prepare-task.js +51 -33
- package/dist/core/runner/runtask.d.ts +26 -1
- package/dist/core/runner/runtask.js +21 -3
- package/dist/core/session-store.d.ts +59 -1
- package/dist/core/session-store.js +82 -14
- package/dist/core/session.d.ts +83 -1
- package/dist/core/strategy-store.d.ts +180 -3
- package/dist/core/strategy-store.js +172 -23
- package/dist/core/task-registry-agent.d.ts +28 -0
- package/dist/core/task-registry-agent.js +63 -2
- package/dist/core/task-registry.d.ts +21 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/types.d.ts +66 -0
- package/dist/core/untrusted-text.d.ts +63 -0
- package/dist/core/untrusted-text.js +48 -0
- package/dist/core/wiring-manifest.d.ts +35 -0
- package/dist/core/wiring-manifest.js +21 -1
- package/dist/engine/harness/types.d.ts +36 -1
- package/dist/index.d.ts +7 -6
- package/dist/index.js +6 -5
- package/dist/internal/harness-types.d.ts +1 -0
- package/dist/stores/file/file-snapshot-store.js +7 -1
- package/dist/stores/file/index.d.ts +27 -3
- package/dist/stores/file/index.js +36 -1
- package/dist/stores/file/session-policy-store.d.ts +0 -13
- package/dist/stores/file/session-policy-store.js +7 -1
- package/dist/stores/file/session-store.d.ts +22 -5
- package/dist/stores/file/session-store.js +80 -13
- package/dist/stores/file/strategy-store.d.ts +97 -0
- package/dist/stores/file/strategy-store.js +340 -0
- package/dist/tools/fs/fs-pdf.d.ts +12 -1
- package/dist/tools/fs/fs-pdf.js +17 -3
- package/dist/tools/fs/fs-read.d.ts +2 -1
- package/dist/tools/fs/fs-read.js +33 -5
- package/dist/tools/fs/fs-shared.d.ts +6 -2
- package/dist/tools/fs/index.d.ts +7 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/web.js +21 -2
- package/package.json +3 -2
- package/test/export-surface.snapshot.json +22 -1
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ export { looksDegenerate, inspectDegenerate, trimDegenerateTail } from "./brain/
|
|
|
28
28
|
export { computeCostMicroUsd, modelCostToPricing, } from "./core/pricing.js";
|
|
29
29
|
export { cacheFamilyOf, promptTokensOf, uncachedInputTokensOf } from "./core/runner/usage-accounting.js";
|
|
30
30
|
export { emitTrace } from "./core/trace.js";
|
|
31
|
-
export { InMemoryStrategyStore } from "./core/strategy-store.js";
|
|
31
|
+
export { InMemoryStrategyStore, seedStrategies, } from "./core/strategy-store.js";
|
|
32
32
|
export { createSqlTool, validateReadOnlySql } from "./tools/sql.js";
|
|
33
33
|
export { runWithTeacher, parseTeacherAdvice, TEACHER_PROMPT, } from "./agents/teacher.js";
|
|
34
34
|
export { runWithVerification, resumeWithVerification, verifyCompleted, runDeveloperTask, VERIFICATION_PROMPT, STATIC_VERIFICATION_PROMPT, VerdictSchema, } from "./agents/verify.js";
|
|
@@ -81,7 +81,7 @@ export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
|
|
|
81
81
|
export { ENV_LIFETIME_SUSPEND_MARGIN_MS, USAGE_WINDOW_REAP_MARGIN_MS } from "./core/runner/prepare-task.js";
|
|
82
82
|
export { InMemoryFileSnapshotStore, DEFAULT_SNAPSHOT_BOUNDS } from "./core/file-snapshot-store.js";
|
|
83
83
|
export { captureManifest, applyManifest } from "./core/file-snapshot-store.js";
|
|
84
|
-
export { FileStorageBackend, FileSessionRepo, FileCheckpointStore, FileMemoryStore, FileToolResultStore, FileSessionPolicyStore, FileFileSnapshotStore, FileWorkflowJournalStore, MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult, resolveDataRoot, sanitizeScope, sanitizePathComponent, createFileConsolidationLock, atomicWriteFile, writeThenLink, ensureDir, readJsonlRecords, AppendLog, } from "./stores/file/index.js";
|
|
84
|
+
export { FileStorageBackend, FileSessionRepo, FileCheckpointStore, FileMemoryStore, FileToolResultStore, FileSessionPolicyStore, FileFileSnapshotStore, FileStrategyStore, FileWorkflowJournalStore, MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult, resolveDataRoot, sanitizeScope, sanitizePathComponent, createFileConsolidationLock, atomicWriteFile, writeThenLink, ensureDir, readJsonlRecords, AppendLog, } from "./stores/file/index.js";
|
|
85
85
|
export { CacheBreakDetector } from "./core/cache-break-detector.js";
|
|
86
86
|
export { maybeCompact } from "./core/auto-compaction.js";
|
|
87
87
|
export { brainToRuntime } from "./core/runtime.js";
|
|
@@ -90,7 +90,7 @@ export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
|
|
|
90
90
|
export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
|
|
91
91
|
export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
|
|
92
92
|
export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, SystemInjectionQueue, } from "./core/task-notification.js";
|
|
93
|
-
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, } from "./core/wiring-manifest.js";
|
|
93
|
+
export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, } from "./core/wiring-manifest.js";
|
|
94
94
|
export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, } from "./core/park-selfcheck.js";
|
|
95
95
|
export {} from "./core/checkpoint-store.js";
|
|
96
96
|
export {} from "./core/checkpoint-store.js";
|
|
@@ -193,10 +193,11 @@ export { summarizeRedactions } from "./core/untrusted-egress.js";
|
|
|
193
193
|
export { MemoryRosterStore, FileRosterStore } from "./agents/roster-store.js";
|
|
194
194
|
export { CONFIG_CATALOG_VERSION, describeConfigCatalog, resolveEffectiveConfig, } from "./config/catalog.js";
|
|
195
195
|
export { normalizeAgentName } from "./core/task-registry.js";
|
|
196
|
+
export { DELEGATION_MAX_CONCURRENT_DEFAULT, DELEGATION_MAX_PER_SESSION_DEFAULT, ORPHAN_ADOPT_WINDOW_MS_DEFAULT, ORPHAN_ADOPT_MAX_DEFAULT, SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT, } from "./config/defaults.js";
|
|
196
197
|
export { COORDINATOR_ROLE_PROMPT, TEAMMATE_COMMUNICATION_ADDENDUM, TEAMMATE_TASK_LIST_ADDENDUM } from "./prompts/coordinator.js";
|
|
197
|
-
export { createSubagentTool, FORK_SUBAGENT_TYPE, GENERAL_PURPOSE_SUBAGENT_TYPE, agentWhenToUseText, FORK_DIRECTIVE_FRAME, SUBAGENT_SYSTEM_NOTE, } from "./agents/subagent.js";
|
|
198
|
+
export { createSubagentTool, FORK_SUBAGENT_TYPE, GENERAL_PURPOSE_SUBAGENT_TYPE, agentWhenToUseText, FORK_DIRECTIVE_FRAME, SUBAGENT_SYSTEM_NOTE, resolveDelegationEntryCaps, } from "./agents/subagent.js";
|
|
198
199
|
export { getSessionRetainLedger, releaseSessionRetainLedger, } from "./agents/retain-ledger.js";
|
|
199
|
-
export { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "./agents/send-message-tool.js";
|
|
200
|
+
export { createSendMessageTool, createAgentContinuationVerb, SEND_MESSAGE_TOOL_NAME } from "./agents/send-message-tool.js";
|
|
200
201
|
export { createPeerAdmission, peerAdmissionFor, judgePeerAdmission, resolvePeerAdmissionConfig, PEER_ADMISSION_DEFAULTS, PEER_HOP_CHAIN_WINDOW, PEER_MESSAGE_NOTICE, createPeerSelfRef, createPeerInboundChainRef, peerAxisToken, appendHopToken, } from "./agents/peer-admission.js";
|
|
201
202
|
export { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME, } from "./agents/agent-transcript-tool.js";
|
|
202
203
|
export { defineAgent } from "./agents/agent-definition.js";
|
|
@@ -14,3 +14,4 @@ export type { SessionWriteOptions, CompactionEntry } from "../engine/harness/typ
|
|
|
14
14
|
export type { ActiveWorktreeSession, WorkspaceState } from "../engine/harness/types.js";
|
|
15
15
|
export type { GitAnnouncementKind, GitAnnouncementState } from "../engine/harness/types.js";
|
|
16
16
|
export type { SessionForkOptions } from "../engine/harness/types.js";
|
|
17
|
+
export type { SessionPlacementRecord, SessionCreateOptions } from "../engine/harness/types.js";
|
|
@@ -5,6 +5,12 @@ import { applyManifest, captureManifest, DEFAULT_SNAPSHOT_BOUNDS, } from "../../
|
|
|
5
5
|
import { canonicalStoreKey, ensureDir, sanitizePathComponent, sanitizeScope, writeThenLink } from "./fs-atomic.js";
|
|
6
6
|
import { assertAdoptionBootGate } from "./adoption/marker.js";
|
|
7
7
|
const sharedInFlight = new Map();
|
|
8
|
+
function containSinkThenable(r) {
|
|
9
|
+
if (typeof r?.then === "function") {
|
|
10
|
+
r.then(undefined, () => {
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
8
14
|
export class FileFileSnapshotStore {
|
|
9
15
|
base;
|
|
10
16
|
blobsDir;
|
|
@@ -37,7 +43,7 @@ export class FileFileSnapshotStore {
|
|
|
37
43
|
}
|
|
38
44
|
disclose(path, reason) {
|
|
39
45
|
try {
|
|
40
|
-
this.onCorruptRead?.({ path, reason });
|
|
46
|
+
containSinkThenable(this.onCorruptRead?.({ path, reason }));
|
|
41
47
|
}
|
|
42
48
|
catch {
|
|
43
49
|
}
|
|
@@ -4,6 +4,10 @@ import type { ToolResultStore } from "../../core/tool-result-store.js";
|
|
|
4
4
|
import { type EvictPolicy } from "../../core/session-store.js";
|
|
5
5
|
import type { SessionStore } from "../../core/session.js";
|
|
6
6
|
import { type FileCheckpointStoreOptions } from "./checkpoint-store.js";
|
|
7
|
+
import type { StrategyStore } from "../../core/strategy-store.js";
|
|
8
|
+
import type { BackgroundAgentStore } from "../../core/background-agent-store.js";
|
|
9
|
+
import type { MailboxStore } from "../../core/mailbox-store.js";
|
|
10
|
+
import type { RosterStore } from "../../agents/roster-store.js";
|
|
7
11
|
import type { SessionPolicyStore } from "../../core/session-policy-store.js";
|
|
8
12
|
import type { FileSnapshotStore } from "../../core/file-snapshot-store.js";
|
|
9
13
|
import type { WorkflowJournalStore } from "../../core/workflow-journal-store.js";
|
|
@@ -16,6 +20,7 @@ export { FileSessionPolicyStore, type FileSessionPolicyStoreOptions, type Sessio
|
|
|
16
20
|
export { FileFileSnapshotStore, type FileFileSnapshotStoreOptions } from "./file-snapshot-store.js";
|
|
17
21
|
export { FileWorkflowJournalStore, MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult } from "./workflow-journal-store.js";
|
|
18
22
|
export { FileUsageWindowStore } from "./usage-window-store.js";
|
|
23
|
+
export { FileStrategyStore, type FileStrategyStoreOptions } from "./strategy-store.js";
|
|
19
24
|
export { resolveDataRoot, sanitizeScope, sanitizePathComponent, createFileConsolidationLock } from "./fs-atomic.js";
|
|
20
25
|
export { atomicWriteFile, writeThenLink, ensureDir, readJsonlRecords, AppendLog } from "./fs-atomic.js";
|
|
21
26
|
export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, type AdoptionErrorCode, type AdoptionSource, type AdoptionReport, type AdoptionReceipt, type AdoptionLegReport, type AffectedDeploymentConfig, type RootAdoptionFile, } from "./adoption/marker.js";
|
|
@@ -76,9 +81,8 @@ export interface FileStorageBackendOptions {
|
|
|
76
81
|
* Reach TODAY: {@link FileStorageBackend.sessionPolicyStore} (rules read as ABSENT ⇒ an unconstrained
|
|
77
82
|
* task), {@link FileStorageBackend.sessionStore}'s repo (a session listing that silently omits what it
|
|
78
83
|
* could not read) and {@link FileStorageBackend.fileSnapshotStore} (a snapshot/scope read as missing,
|
|
79
|
-
* and a blob GC that aborted). The roster and
|
|
80
|
-
*
|
|
81
|
-
* the same constructor.
|
|
84
|
+
* and a blob GC that aborted). The aggregated roster and mailbox stores receive the same sink. A
|
|
85
|
+
* store that grows the seat later is wired here in the same constructor.
|
|
82
86
|
*
|
|
83
87
|
* The payload is the UNION shape ({@link FileStorageCorruptReadInfo}): `path` + `reason` always,
|
|
84
88
|
* with the session-policy face's `sessionId`/`principal` present only when the reading store keys by
|
|
@@ -125,6 +129,24 @@ export declare class FileStorageBackend {
|
|
|
125
129
|
* `RunnerDeps.usageWindowStore` so a deployment's `usageWindows` survive a restart (an in-memory
|
|
126
130
|
* ledger silently re-grants every allowance on every boot). */
|
|
127
131
|
readonly usageWindowStore: UsageWindowStore;
|
|
132
|
+
/** design/151 S1a / subagent transcript persistence — the durable background-agent execution
|
|
133
|
+
* ledger over the same data root. Wire into BOTH `RunnerDeps.backgroundAgentStore` AND (for a
|
|
134
|
+
* deployment-composed Agent tool) `SubagentToolOptions.background.agentStore` — SAME instance
|
|
135
|
+
* (the RB-37 pairing discipline). Aggregated here so the TOC full-persistence assembly is one
|
|
136
|
+
* constructor, the same reason the session/checkpoint/tool-result trio are. */
|
|
137
|
+
readonly backgroundAgentStore: BackgroundAgentStore;
|
|
138
|
+
/** design/151 §7 S3c — the durable SendMessage mailbox (tier-3 parking lane), same data root.
|
|
139
|
+
* Wire into `RunnerDeps.mailboxStore` (single-instance pairing, same as above). */
|
|
140
|
+
readonly mailboxStore: MailboxStore;
|
|
141
|
+
/** File-backed teacher-mode strategy repository (`root/strategies`) — pass as
|
|
142
|
+
* `TeacherConfig.strategyStore` (with a `scope`) so escalation strategies survive a restart;
|
|
143
|
+
* the in-memory default forgets everything each process, which zeroes the cross-session reuse
|
|
144
|
+
* the repository exists for. Aggregated here so the TOC full-persistence assembly stays one
|
|
145
|
+
* constructor (the same half-wiring hazard as the delegation trio). */
|
|
146
|
+
readonly strategyStore: StrategyStore;
|
|
147
|
+
/** design/147 S1c — the durable name→agent roster (layer 0.5 resolution), `root/roster.json`.
|
|
148
|
+
* Wire into `RunnerDeps.rosterStore` so name-addressing survives a restart alongside the rows. */
|
|
149
|
+
readonly rosterStore: RosterStore;
|
|
128
150
|
/**
|
|
129
151
|
* design/84 Seam B (TOC) — the per-scope consolidation lock for `consolidateScope`'s `acquire` injection
|
|
130
152
|
* point: `consolidateScope(scope, { store: backend.memoryStore, llm, acquire: backend.consolidationLock })`.
|
|
@@ -133,6 +155,8 @@ export declare class FileStorageBackend {
|
|
|
133
155
|
*/
|
|
134
156
|
readonly consolidationLock: (scope: string) => (() => void) | undefined;
|
|
135
157
|
private readonly lock;
|
|
158
|
+
private readonly fileAgentRows;
|
|
159
|
+
private readonly fileMailbox;
|
|
136
160
|
private readonly fileCheckpoints;
|
|
137
161
|
private readonly fileMemory;
|
|
138
162
|
private readonly ttl;
|
|
@@ -11,6 +11,10 @@ import { FileSessionPolicyStore } from "./session-policy-store.js";
|
|
|
11
11
|
import { FileFileSnapshotStore } from "./file-snapshot-store.js";
|
|
12
12
|
import { FileWorkflowJournalStore } from "./workflow-journal-store.js";
|
|
13
13
|
import { FileUsageWindowStore } from "./usage-window-store.js";
|
|
14
|
+
import { FileBackgroundAgentStore } from "./background-agent-store.js";
|
|
15
|
+
import { FileMailboxStore } from "./mailbox-store.js";
|
|
16
|
+
import { FileRosterStore } from "../../agents/roster-store.js";
|
|
17
|
+
import { FileStrategyStore } from "./strategy-store.js";
|
|
14
18
|
export { FileCheckpointStore } from "./checkpoint-store.js";
|
|
15
19
|
export { FileMemoryStore } from "./memory-store.js";
|
|
16
20
|
export { FileSessionRepo } from "./session-store.js";
|
|
@@ -19,6 +23,7 @@ export { FileSessionPolicyStore } from "./session-policy-store.js";
|
|
|
19
23
|
export { FileFileSnapshotStore } from "./file-snapshot-store.js";
|
|
20
24
|
export { FileWorkflowJournalStore, MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult } from "./workflow-journal-store.js";
|
|
21
25
|
export { FileUsageWindowStore } from "./usage-window-store.js";
|
|
26
|
+
export { FileStrategyStore } from "./strategy-store.js";
|
|
22
27
|
export { resolveDataRoot, sanitizeScope, sanitizePathComponent, createFileConsolidationLock } from "./fs-atomic.js";
|
|
23
28
|
export { atomicWriteFile, writeThenLink, ensureDir, readJsonlRecords, AppendLog } from "./fs-atomic.js";
|
|
24
29
|
export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootAdoptionFile, ROOT_ADOPTION_FILE, } from "./adoption/marker.js";
|
|
@@ -33,8 +38,14 @@ export class FileStorageBackend {
|
|
|
33
38
|
fileSnapshotStore;
|
|
34
39
|
workflowJournalStore;
|
|
35
40
|
usageWindowStore;
|
|
41
|
+
backgroundAgentStore;
|
|
42
|
+
mailboxStore;
|
|
43
|
+
strategyStore;
|
|
44
|
+
rosterStore;
|
|
36
45
|
consolidationLock;
|
|
37
46
|
lock;
|
|
47
|
+
fileAgentRows;
|
|
48
|
+
fileMailbox;
|
|
38
49
|
fileCheckpoints;
|
|
39
50
|
fileMemory;
|
|
40
51
|
ttl;
|
|
@@ -49,7 +60,7 @@ export class FileStorageBackend {
|
|
|
49
60
|
const corruptRead = opts.onCorruptRead !== undefined ? { onCorruptRead: opts.onCorruptRead } : undefined;
|
|
50
61
|
const repo = new FileSessionRepo(this.root, corruptRead);
|
|
51
62
|
this.fileSessions = repo;
|
|
52
|
-
this.ttl = new TtlSessionStore({ repo, evict: opts.evict ?? "forget", durability: "durable" });
|
|
63
|
+
this.ttl = new TtlSessionStore({ repo, evict: opts.evict ?? "forget", durability: "durable", placements: { subagent: { durability: "durable" } } });
|
|
53
64
|
this.sessionStore = this.ttl;
|
|
54
65
|
this.fileCheckpoints = new FileCheckpointStore(this.root, opts.checkpoint);
|
|
55
66
|
this.checkpointStore = this.fileCheckpoints;
|
|
@@ -61,6 +72,20 @@ export class FileStorageBackend {
|
|
|
61
72
|
this.fileWorkflowJournal = new FileWorkflowJournalStore(this.root);
|
|
62
73
|
this.workflowJournalStore = this.fileWorkflowJournal;
|
|
63
74
|
this.usageWindowStore = new FileUsageWindowStore(this.root);
|
|
75
|
+
this.fileAgentRows = new FileBackgroundAgentStore(this.root);
|
|
76
|
+
this.backgroundAgentStore = this.fileAgentRows;
|
|
77
|
+
this.fileMailbox = new FileMailboxStore(this.root, corruptRead ?? {});
|
|
78
|
+
this.mailboxStore = this.fileMailbox;
|
|
79
|
+
this.rosterStore = new FileRosterStore(join(this.root, "roster.json"), corruptRead ?? {});
|
|
80
|
+
const strategiesRoot = join(this.root, "strategies");
|
|
81
|
+
this.strategyStore = new FileStrategyStore({
|
|
82
|
+
root: strategiesRoot,
|
|
83
|
+
...(opts.onCorruptRead !== undefined
|
|
84
|
+
? {
|
|
85
|
+
onIncident: (i) => opts.onCorruptRead?.({ path: i.path ?? strategiesRoot, reason: `strategy ${i.op}: ${i.error}` }),
|
|
86
|
+
}
|
|
87
|
+
: {}),
|
|
88
|
+
});
|
|
64
89
|
this.consolidationLock = createFileConsolidationLock(join(this.root, "consolidation-locks"));
|
|
65
90
|
}
|
|
66
91
|
catch (err) {
|
|
@@ -87,6 +112,16 @@ export class FileStorageBackend {
|
|
|
87
112
|
}
|
|
88
113
|
this.fileMemory.close();
|
|
89
114
|
this.fileWorkflowJournal.dispose();
|
|
115
|
+
try {
|
|
116
|
+
this.fileAgentRows.close();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
this.fileMailbox.close();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
}
|
|
90
125
|
this.lock.release();
|
|
91
126
|
}
|
|
92
127
|
}
|
|
@@ -1,17 +1,4 @@
|
|
|
1
1
|
import { type PutRulesOptions, type SessionPermissionRules, type SessionPolicyStore, type SessionRulesRecord, type StoredSessionRules } from "../../core/session-policy-store.js";
|
|
2
|
-
/**
|
|
3
|
-
* design/99 §E6 — file-backed {@link SessionPolicyStore} for the local (TOC) backend: ONE JSON file per
|
|
4
|
-
* `(sessionId, principal)`, semantics **byte-for-byte identical to `InMemorySessionPolicyStore`** (the
|
|
5
|
-
* cross-backend equivalence contract). CAS-rev OCC + tighten-only reuse the SAME pure helpers
|
|
6
|
-
* (`loosenReasons`/`normalizeRules`/`stripRev`) as core — no re-implemented rule logic that could drift.
|
|
7
|
-
*
|
|
8
|
-
* Atomicity: the file backend is single-process (the `FileStorageBackend` boot lock guarantees ONE writer per
|
|
9
|
-
* data dir), and `getRules`/`putRules` read-check-write SYNCHRONOUSLY (no await between the rev read and the
|
|
10
|
-
* atomic write), so the read-modify-write is atomic in the one event loop — exactly the InMemory store's premise.
|
|
11
|
-
* Cross-process CORRECT concurrency is the Pg/TiDB backend's job (a CAS WHERE clause), by design.
|
|
12
|
-
*/
|
|
13
|
-
/** Coordinates of one corrupt-treated-as-absent policy read (the {@link FileSessionPolicyStoreOptions.onCorruptRead}
|
|
14
|
-
* payload). Named rather than inline so the backend option that forwards it names the SAME shape. */
|
|
15
2
|
export interface SessionPolicyCorruptReadInfo {
|
|
16
3
|
/** The session whose read observed the corrupt row. On the enumeration face this is the session being
|
|
17
4
|
* ENUMERATED — a corrupt row's own `__sid` is by definition unreadable, so it cannot be attributed. */
|
|
@@ -3,6 +3,12 @@ import { join } from "node:path";
|
|
|
3
3
|
import { loosenReasons, normalizeRules, stripRev, SessionPolicyError, } from "../../core/session-policy-store.js";
|
|
4
4
|
import { atomicWriteFile, ensureDir, sanitizeScope } from "./fs-atomic.js";
|
|
5
5
|
import { assertAdoptionBootGate, readRootAdoptionFile } from "./adoption/marker.js";
|
|
6
|
+
function containSinkThenable(r) {
|
|
7
|
+
if (typeof r?.then === "function") {
|
|
8
|
+
r.then(undefined, () => {
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
6
12
|
export class FileSessionPolicyStore {
|
|
7
13
|
dir;
|
|
8
14
|
onCorruptRead;
|
|
@@ -18,7 +24,7 @@ export class FileSessionPolicyStore {
|
|
|
18
24
|
}
|
|
19
25
|
disclose(info) {
|
|
20
26
|
try {
|
|
21
|
-
this.onCorruptRead?.(info);
|
|
27
|
+
containSinkThenable(this.onCorruptRead?.(info));
|
|
22
28
|
}
|
|
23
29
|
catch {
|
|
24
30
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Session, SessionForkOptions, SessionMetadata, SessionRepo, SessionTreeEntry } from "../../internal/harness.js";
|
|
1
|
+
import type { Session, SessionCreateOptions, SessionForkOptions, SessionMetadata, SessionPlacementRecord, SessionRepo, SessionTreeEntry } from "../../internal/harness.js";
|
|
2
|
+
import type { PlacedSessionRow } from "../../core/session.js";
|
|
2
3
|
export interface FileSessionRepoOptions {
|
|
3
4
|
/**
|
|
4
5
|
* Disclosure sink for a durable read this repo treats as ABSENT (ruled 2026-08-03). Same name and
|
|
@@ -28,7 +29,10 @@ export declare class FileSessionRepo implements SessionRepo {
|
|
|
28
29
|
private readonly joined;
|
|
29
30
|
constructor(root: string, opts?: FileSessionRepoOptions);
|
|
30
31
|
/** The one delivery point for {@link FileSessionRepoOptions.onCorruptRead}; swallow-guarded here so
|
|
31
|
-
* no call site has to remember.
|
|
32
|
+
* no call site has to remember. The sink seat is void-typed but a host may hand it an async
|
|
33
|
+
* function — an async sink's rejection is observed off its returned thenable (same containment
|
|
34
|
+
* as the strategy store's incident sink), so neither a sync throw nor an async rejection can
|
|
35
|
+
* re-introduce the failure mode the fail-open avoids. */
|
|
32
36
|
private disclose;
|
|
33
37
|
private pathFor;
|
|
34
38
|
/** Read + torn-tail-recover a session file into (meta, entries); a missing file → not_found. */
|
|
@@ -47,11 +51,24 @@ export declare class FileSessionRepo implements SessionRepo {
|
|
|
47
51
|
* means a stale holder can never revoke the rebuilt entry.
|
|
48
52
|
*/
|
|
49
53
|
dispose(): Promise<void>;
|
|
50
|
-
create(options?:
|
|
51
|
-
id?: string;
|
|
52
|
-
}): Promise<Session>;
|
|
54
|
+
create(options?: SessionCreateOptions): Promise<Session>;
|
|
53
55
|
open(metadata: SessionMetadata): Promise<Session>;
|
|
54
56
|
list(): Promise<SessionMetadata[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Subagent transcript persistence — the PLACED partition's own enumeration (the joint reap's
|
|
59
|
+
* partition-leg input; `TtlSessionStore` re-exposes it verbatim). Age (`olderThanMs`) is judged on
|
|
60
|
+
* the session FILE's mtime — the transcript's last append — falling back to `placedAt` when the
|
|
61
|
+
* stat fails (mtime is also the CC orphan-adoption window's basis, so the two age reads agree).
|
|
62
|
+
* Rows missing either `(scope, handle)` join half return the honest `tupleIncomplete` variant
|
|
63
|
+
* (consumers fail closed on it — never age-reaped, see the SessionStore contract).
|
|
64
|
+
*/
|
|
65
|
+
listPlaced(kind: "subagent", opts?: {
|
|
66
|
+
olderThanMs?: number;
|
|
67
|
+
scope?: string;
|
|
68
|
+
}): Promise<PlacedSessionRow[]>;
|
|
69
|
+
/** Cheap placement probe for one id (the TtlSessionStore's cold-cache release consults it so the
|
|
70
|
+
* placed real-deletion obligation holds after a restart). `undefined` = missing file OR unplaced. */
|
|
71
|
+
placementOf(sessionId: string): Promise<SessionPlacementRecord | undefined>;
|
|
55
72
|
delete(metadata: SessionMetadata): Promise<void>;
|
|
56
73
|
fork(sourceMetadata: SessionMetadata, options?: SessionForkOptions): Promise<Session>;
|
|
57
74
|
/** 2c session-sync ([275], the FILE half of [266]②): the FULL oldest-first log for export. The file backend
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { BaseSessionStorage, StoredSession, SessionError, getEntriesToFork, uuidv7, validateEntriesForImport, } from "../../internal/harness.js";
|
|
4
4
|
import { canonicalStoreKey, AppendLog, atomicWriteFile, ensureDir, readJsonlRecords, sanitizePathComponent } from "./fs-atomic.js";
|
|
5
5
|
import { assertAdoptionBootGate } from "./adoption/marker.js";
|
|
6
|
+
function containSinkThenable(r) {
|
|
7
|
+
if (typeof r?.then === "function") {
|
|
8
|
+
r.then(undefined, () => {
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
6
12
|
const SUFFIX = ".jsonl";
|
|
7
13
|
const sharedSessionStorages = new Map();
|
|
8
14
|
const sessionStorageFinalizer = new FinalizationRegistry(({ canonical, log }) => {
|
|
@@ -67,7 +73,7 @@ export class FileSessionRepo {
|
|
|
67
73
|
}
|
|
68
74
|
disclose(path, reason) {
|
|
69
75
|
try {
|
|
70
|
-
this.onCorruptRead?.({ path, reason });
|
|
76
|
+
containSinkThenable(this.onCorruptRead?.({ path, reason }));
|
|
71
77
|
}
|
|
72
78
|
catch {
|
|
73
79
|
}
|
|
@@ -83,18 +89,20 @@ export class FileSessionRepo {
|
|
|
83
89
|
const lines = readJsonlRecords(path, (info) => this.disclose(info.path, info.reason));
|
|
84
90
|
let createdAt = "";
|
|
85
91
|
let forkedFrom;
|
|
92
|
+
let placement;
|
|
86
93
|
const entries = [];
|
|
87
94
|
for (const line of lines) {
|
|
88
95
|
if (line.kind === "meta") {
|
|
89
96
|
createdAt = line.createdAt;
|
|
90
97
|
forkedFrom = line.forkedFrom;
|
|
98
|
+
placement = line.placement;
|
|
91
99
|
}
|
|
92
100
|
else
|
|
93
101
|
entries.push(line.entry);
|
|
94
102
|
}
|
|
95
|
-
return { createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), entries };
|
|
103
|
+
return { createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}), entries };
|
|
96
104
|
}
|
|
97
|
-
storage(id, createdAt, entries, forkedFrom) {
|
|
105
|
+
storage(id, createdAt, entries, forkedFrom, placement) {
|
|
98
106
|
const canonical = canonicalStoreKey(this.pathFor(id));
|
|
99
107
|
const live = sharedSessionStorages.get(canonical)?.deref();
|
|
100
108
|
if (live !== undefined) {
|
|
@@ -103,7 +111,7 @@ export class FileSessionRepo {
|
|
|
103
111
|
return live;
|
|
104
112
|
}
|
|
105
113
|
const log = new AppendLog(this.pathFor(id));
|
|
106
|
-
const created = new FileSessionStorage(log, { id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}) }, entries, canonical);
|
|
114
|
+
const created = new FileSessionStorage(log, { id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}) }, entries, canonical);
|
|
107
115
|
sharedSessionStorages.set(canonical, new WeakRef(created));
|
|
108
116
|
created.addHolder(this);
|
|
109
117
|
this.joined.set(canonical, new WeakRef(created));
|
|
@@ -128,15 +136,16 @@ export class FileSessionRepo {
|
|
|
128
136
|
const path = this.pathFor(id);
|
|
129
137
|
if (!existsSync(path)) {
|
|
130
138
|
evictSharedSessionStorage(canonicalStoreKey(path));
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
const placement = options.placement !== undefined ? { ...options.placement, placedAt: options.placement.placedAt ?? Date.now() } : undefined;
|
|
140
|
+
atomicWriteFile(this.tmpDir, path, `${JSON.stringify({ kind: "meta", id, createdAt, ...(placement !== undefined ? { placement } : {}) })}\n`);
|
|
141
|
+
return new StoredSession(this.storage(id, createdAt, [], undefined, placement));
|
|
133
142
|
}
|
|
134
|
-
const { createdAt: existingCreatedAt, forkedFrom, entries } = this.read(id);
|
|
135
|
-
return new StoredSession(this.storage(id, existingCreatedAt || createdAt, entries, forkedFrom));
|
|
143
|
+
const { createdAt: existingCreatedAt, forkedFrom, placement, entries } = this.read(id);
|
|
144
|
+
return new StoredSession(this.storage(id, existingCreatedAt || createdAt, entries, forkedFrom, placement));
|
|
136
145
|
}
|
|
137
146
|
async open(metadata) {
|
|
138
|
-
const { createdAt, forkedFrom, entries } = this.read(metadata.id);
|
|
139
|
-
return new StoredSession(this.storage(metadata.id, createdAt, entries, forkedFrom));
|
|
147
|
+
const { createdAt, forkedFrom, placement, entries } = this.read(metadata.id);
|
|
148
|
+
return new StoredSession(this.storage(metadata.id, createdAt, entries, forkedFrom, placement));
|
|
140
149
|
}
|
|
141
150
|
async list() {
|
|
142
151
|
let names;
|
|
@@ -155,7 +164,9 @@ export class FileSessionRepo {
|
|
|
155
164
|
continue;
|
|
156
165
|
const id = name.slice(0, -SUFFIX.length);
|
|
157
166
|
try {
|
|
158
|
-
const { createdAt, forkedFrom } = this.read(id);
|
|
167
|
+
const { createdAt, forkedFrom, placement } = this.read(id);
|
|
168
|
+
if (placement !== undefined)
|
|
169
|
+
continue;
|
|
159
170
|
out.push({ id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}) });
|
|
160
171
|
}
|
|
161
172
|
catch (err) {
|
|
@@ -166,12 +177,68 @@ export class FileSessionRepo {
|
|
|
166
177
|
}
|
|
167
178
|
return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
168
179
|
}
|
|
180
|
+
async listPlaced(kind, opts) {
|
|
181
|
+
let names;
|
|
182
|
+
try {
|
|
183
|
+
names = readdirSync(this.dir);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (err.code !== "ENOENT") {
|
|
187
|
+
this.disclose(this.dir, `session directory unreadable (${err.code ?? "unknown"}) — placed listing degraded to empty`);
|
|
188
|
+
}
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const name of names) {
|
|
194
|
+
if (!name.endsWith(SUFFIX))
|
|
195
|
+
continue;
|
|
196
|
+
const id = name.slice(0, -SUFFIX.length);
|
|
197
|
+
let placement;
|
|
198
|
+
try {
|
|
199
|
+
placement = this.read(id).placement;
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
if (!(err instanceof SessionError && err.code === "not_found")) {
|
|
203
|
+
this.disclose(this.pathFor(id), `session log unreadable (${err instanceof Error ? err.message : String(err)}) — skipped from the placed listing`);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (placement === undefined || placement.kind !== kind)
|
|
208
|
+
continue;
|
|
209
|
+
if (opts?.scope !== undefined && placement.scope !== opts.scope)
|
|
210
|
+
continue;
|
|
211
|
+
if (opts?.olderThanMs !== undefined) {
|
|
212
|
+
let ageAnchor = placement.placedAt;
|
|
213
|
+
try {
|
|
214
|
+
ageAnchor = statSync(this.pathFor(id)).mtimeMs;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
}
|
|
218
|
+
if (now - ageAnchor <= opts.olderThanMs)
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
out.push(placement.scope !== undefined && placement.handle !== undefined
|
|
222
|
+
? { sessionId: id, placedAt: placement.placedAt, scope: placement.scope, handle: placement.handle }
|
|
223
|
+
: { sessionId: id, placedAt: placement.placedAt, tupleIncomplete: true });
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
async placementOf(sessionId) {
|
|
228
|
+
try {
|
|
229
|
+
return this.read(sessionId).placement;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
169
235
|
async delete(metadata) {
|
|
170
236
|
evictSharedSessionStorage(canonicalStoreKey(this.pathFor(metadata.id)));
|
|
171
237
|
try {
|
|
172
238
|
rmSync(this.pathFor(metadata.id), { force: true });
|
|
173
239
|
}
|
|
174
|
-
catch {
|
|
240
|
+
catch (err) {
|
|
241
|
+
throw new SessionError("storage", `session ${metadata.id} could not be deleted: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
|
|
175
242
|
}
|
|
176
243
|
}
|
|
177
244
|
async fork(sourceMetadata, options = {}) {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type StoredStrategy, type StrategyStore, type StrategyStoreIncident } from "../../core/strategy-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* File-backed {@link StrategyStore} — the persistence twin of `InMemoryStrategyStore`, so the teacher
|
|
4
|
+
* mode's cross-session reuse premise actually holds on a local (TOC) deployment where every session is
|
|
5
|
+
* a fresh process (an in-memory strategy repository there re-learns everything, every time).
|
|
6
|
+
*
|
|
7
|
+
* DESIGN STANCE — this is a **cache, not an authority store**: every entry is regenerable (worst case:
|
|
8
|
+
* ask the teacher again), so losing one never loses correctness. It therefore deliberately skips the
|
|
9
|
+
* journal/shadow armor of the memory file backend and does NOT fsync-harden beyond the shared
|
|
10
|
+
* atomic-write primitive, does NOT take a cross-process lock, and accepts a narrow multi-process race
|
|
11
|
+
* on save-side dedup (two processes can each write one copy of the same normalized entry — the read
|
|
12
|
+
* side dedups, and a lost max-confidence merge is cache-grade). What it does NOT relax: scope
|
|
13
|
+
* isolation and loud-refusal discipline, which are held to the same standard as the durable stores.
|
|
14
|
+
*
|
|
15
|
+
* Layout: `<root>/<scopeDir>/<id>.json`, one strategy per file, whole-file atomic replace on write.
|
|
16
|
+
* `scopeDir = slug(scope) + "-" + sha256(scope).slice(0,24)` — the slug is a strict `[a-z0-9-]`
|
|
17
|
+
* whitelist purely for readability; IDENTITY lives in the 96-bit hash, so no scope value, however
|
|
18
|
+
* hostile, can traverse out of the root or collide another scope's directory. Ids are gated to
|
|
19
|
+
* `[A-Za-z0-9_-]{1,64}` at save (the escalation loop mints UUIDs; the interface is public, so a
|
|
20
|
+
* hand-rolled id must not be able to name a path).
|
|
21
|
+
*
|
|
22
|
+
* `scope` is a NAMESPACE, not an authorization boundary — the store cannot authenticate its caller;
|
|
23
|
+
* whoever holds the instance can address any scope (same posture as the memory store). Authorization
|
|
24
|
+
* is the host's obligation.
|
|
25
|
+
*/
|
|
26
|
+
export interface FileStrategyStoreOptions {
|
|
27
|
+
/** Directory to keep strategies under (created `0o700` if absent; a non-directory or unwritable
|
|
28
|
+
* path is refused loudly at construction — a store that cannot persist must not pretend to). */
|
|
29
|
+
root: string;
|
|
30
|
+
/** Per-scope capacity cap (entries), evicting the lowest-scoring on overflow. Default 100. */
|
|
31
|
+
maxPerScope?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Disclosure sink for contained store incidents (a corrupt entry quarantined, an eviction that
|
|
34
|
+
* failed after a successful save, a degraded read). Absent ⇒ `console.warn`, once per op kind —
|
|
35
|
+
* a contained fault must be loud somewhere, but must never flood.
|
|
36
|
+
*/
|
|
37
|
+
onIncident?: (i: StrategyStoreIncident) => void;
|
|
38
|
+
}
|
|
39
|
+
export declare class FileStrategyStore implements StrategyStore {
|
|
40
|
+
private readonly root;
|
|
41
|
+
private readonly maxPerScope;
|
|
42
|
+
private readonly onIncident?;
|
|
43
|
+
/** Ops that already warned on the absent-sink fallback (bounded disclosure — never flood). */
|
|
44
|
+
private readonly warnedOps;
|
|
45
|
+
/** Isolation scope for the host incident sink (sync throw AND async rejection contained). */
|
|
46
|
+
private readonly sinkNotifier;
|
|
47
|
+
/** Scope dirs whose stale temps this instance already swept (once per scope per instance). */
|
|
48
|
+
private readonly sweptTemps;
|
|
49
|
+
constructor(opts: FileStrategyStoreOptions);
|
|
50
|
+
/** `slug(scope)-sha256(scope)[0..24]` — slug is readability only; the hash is the injective key. */
|
|
51
|
+
private scopeDirName;
|
|
52
|
+
private scopeDirPath;
|
|
53
|
+
/** The scope dir must be a REAL directory — a symlink here means the store's namespace was tampered
|
|
54
|
+
* with (a link could point retrieval at another scope's data, or writes out of the root). The
|
|
55
|
+
* check-then-use window against a same-uid local attacker is out of the threat model: such an
|
|
56
|
+
* attacker already holds the same rights as the store itself. */
|
|
57
|
+
private assertScopeDirSafe;
|
|
58
|
+
private incident;
|
|
59
|
+
/** Sweep crashed writers' stale temps (once per scope per instance). Fresh temps are left alone —
|
|
60
|
+
* they may belong to a live concurrent writer. */
|
|
61
|
+
private sweepStaleTemps;
|
|
62
|
+
/** Load every VALID entry of a scope dir. Corrupt files are quarantined (`.bad` rename) with one
|
|
63
|
+
* disclosure; entries that violate the read-side invariants (foreign scope / id≠filename) are
|
|
64
|
+
* skipped with disclosure but NOT quarantined (a copied-in file may be someone's valid data);
|
|
65
|
+
* normalized duplicates collapse to the best-scoring copy. */
|
|
66
|
+
private loadScope;
|
|
67
|
+
/** Rename a corrupt file to `<name>.bad` — one disclosure now, zero parse cost on every later read
|
|
68
|
+
* (the suffix no longer matches the entry-file grammar). Never faults the calling operation. */
|
|
69
|
+
private quarantine;
|
|
70
|
+
/** Serialize + write one entry, refusing a record whose SERIALIZED form exceeds the read bound.
|
|
71
|
+
* The field caps bound RAW string bytes, but JSON escaping expands control characters up to 6x —
|
|
72
|
+
* without this door a save could succeed and then self-quarantine on the very next read (the
|
|
73
|
+
* worst possible shape: an accepted write the store itself later refuses to serve). */
|
|
74
|
+
private writeEntry;
|
|
75
|
+
save(s: StoredStrategy): void;
|
|
76
|
+
private evict;
|
|
77
|
+
find(scope: string, query: string, limit: number): StoredStrategy[];
|
|
78
|
+
/** Bounded candidate view for the RETRIEVAL-adjacent paths (find/hasStrategy/save): an externally
|
|
79
|
+
* inflated directory must not make a hot operation unbounded. MAINTENANCE passes Infinity — see
|
|
80
|
+
* {@link prune}. */
|
|
81
|
+
private retrievalReadCap;
|
|
82
|
+
/**
|
|
83
|
+
* Trims to `maxSize` over an UNCAPPED enumeration: prune is the reconciliation verb, so it must see
|
|
84
|
+
* every entry file — under the bounded retrieval view, a capacity SHRINK across restarts (or
|
|
85
|
+
* `maxPerScope: 0`) left files beyond the window untouched, reporting success while the "removed"
|
|
86
|
+
* strategies sat on disk ready to resurrect under a later, larger capacity. Host-invoked
|
|
87
|
+
* maintenance accepts the O(all files) cost the retrieval path refuses.
|
|
88
|
+
*/
|
|
89
|
+
prune(scope: string, maxSize: number): void;
|
|
90
|
+
/** Uncapped for the same reason as {@link prune}: this face feeds the seed capacity door, and an
|
|
91
|
+
* under-count there turns "refuse what cannot fit" into silent eviction of fresh seeds. */
|
|
92
|
+
scopeUsage(scope: string): {
|
|
93
|
+
used: number;
|
|
94
|
+
capacity: number;
|
|
95
|
+
};
|
|
96
|
+
hasStrategy(scope: string, entry: Pick<StoredStrategy, "problem" | "strategy">): boolean;
|
|
97
|
+
}
|