@sanctuary-framework/mcp-server 1.2.4 → 1.2.6

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/dist/index.d.cts CHANGED
@@ -252,6 +252,43 @@ interface ApprovalChannelConfig {
252
252
  webhook_url?: string;
253
253
  webhook_secret?: string;
254
254
  }
255
+ /**
256
+ * WP-V1.3-10 Upsilon-2: cross-harness approval-redirect mode.
257
+ *
258
+ * `mode` selects how the underlying channel and the aggregator interact when
259
+ * a Tier 1/2 approval fires for a wrapped agent:
260
+ *
261
+ * - `replace`: the underlying channel is bypassed; the gate's
262
+ * `requestApproval()` blocks awaiting the operator's decision through
263
+ * the aggregator HTTP `approve`/`deny` routes (or any other surface that
264
+ * calls `aggregator.resolve()`). Default behavior when redirect is
265
+ * enabled.
266
+ * - `notify`: both the underlying channel AND the aggregator run; the
267
+ * first decision wins. Right shape for harnesses (e.g. Mastra) where
268
+ * full local-prompt suppression is not viable, so the operator still
269
+ * sees the prompt locally but ALSO can resolve from the unified inbox.
270
+ *
271
+ * `per_agent` is a stub for v1.4 multi-agent fortresses: a map of
272
+ * `agent_id -> {enabled, mode}` overrides. v1.x ignores this field; the
273
+ * fortress-level `enabled` + `mode` apply to every wrapped agent in the
274
+ * fortress (which is currently single-agent in practice). v1.4 adds
275
+ * agent-id propagation through the gate signature and consults this map.
276
+ */
277
+ interface ApprovalRedirectPerAgentOverride {
278
+ enabled?: boolean;
279
+ mode?: "replace" | "notify";
280
+ }
281
+ interface ApprovalRedirectConfig {
282
+ /** Master toggle. When false, redirect-mode is off across the fortress. */
283
+ enabled: boolean;
284
+ /** How the underlying channel and the aggregator coexist. */
285
+ mode: "replace" | "notify";
286
+ /**
287
+ * Per-agent overrides. v1.x stub — populated entries are validated and
288
+ * persisted but not enforced. Reserved for v1.4 multi-agent fortresses.
289
+ */
290
+ per_agent?: Record<string, ApprovalRedirectPerAgentOverride>;
291
+ }
255
292
  /** Complete Principal Policy */
256
293
  interface PrincipalPolicy {
257
294
  version: number;
@@ -270,6 +307,13 @@ interface PrincipalPolicy {
270
307
  * days when absent; values <= 0 fall back to the default.
271
308
  */
272
309
  concierge_memory_retention_days?: number;
310
+ /**
311
+ * WP-V1.3-10 Upsilon-2: cross-harness approval-redirect mode. Optional;
312
+ * defaults to `{ enabled: false, mode: "replace" }` (preserves legacy
313
+ * behavior). Operator-toggled via `sanctuary agents config <tenant>
314
+ * --approval-redirect=<bool>` or by editing principal-policy.yaml.
315
+ */
316
+ approval_redirect?: ApprovalRedirectConfig;
273
317
  }
274
318
  /** Approval request sent to the human */
275
319
  interface ApprovalRequest {
@@ -5183,6 +5227,24 @@ interface ReadThreadOptions {
5183
5227
  /** Cap the number of turns returned (oldest-first within the bundle). */
5184
5228
  limit?: number;
5185
5229
  }
5230
+ /**
5231
+ * Stable failure-reason enum for `readThreadStrict` (WP-V1.3-9 Tau-2).
5232
+ * Mirrors the shape carried by `operator_concierge_memory_read_failed`
5233
+ * audit payloads so the service emits the cause verbatim.
5234
+ */
5235
+ type ConciergeMemoryReadFailure = "decrypt_failed" | "schema_mismatch" | "oversize_bundle" | "io_failed" | "unknown";
5236
+ /**
5237
+ * Discriminated result for `readThreadStrict`. The fold-read path uses
5238
+ * this so an empty thread (no bundle) does not look the same as a
5239
+ * corrupted bundle (which must trigger graceful degradation).
5240
+ */
5241
+ type ReadThreadStrictResult = {
5242
+ ok: true;
5243
+ turns: ConciergeTurn[];
5244
+ } | {
5245
+ ok: false;
5246
+ reason: ConciergeMemoryReadFailure;
5247
+ };
5186
5248
  interface ListThreadsOptions {
5187
5249
  /** Cap the number of summaries returned. */
5188
5250
  limit?: number;
@@ -5210,6 +5272,21 @@ declare class ConciergeMemoryStore {
5210
5272
  * audit events; the caller (HTTP route handler) owns audit semantics.
5211
5273
  */
5212
5274
  readThread(threadId: string, opts?: ReadThreadOptions): Promise<ConciergeTurn[]>;
5275
+ /**
5276
+ * Read turns with explicit failure surfacing (WP-V1.3-9 Tau-2). Where
5277
+ * `readThread` collapses every failure mode to an empty array, this
5278
+ * variant returns a discriminated result so the multi-turn fold path
5279
+ * can degrade cleanly + emit `operator_concierge_memory_read_failed`
5280
+ * with a concrete cause.
5281
+ *
5282
+ * - No bundle on disk → `{ ok: true, turns: [] }` (a fresh thread).
5283
+ * - Bundle present, decode + decrypt + schema check pass → ok with turns.
5284
+ * - Bundle present, oversize → `{ ok: false, reason: "oversize_bundle" }`.
5285
+ * - Bundle present, decryption fails → `{ ok: false, reason: "decrypt_failed" }`.
5286
+ * - Bundle present, schema mismatch (version / thread_id) → `schema_mismatch`.
5287
+ * - Storage IO error → `io_failed`.
5288
+ */
5289
+ readThreadStrict(threadId: string, opts?: ReadThreadOptions): Promise<ReadThreadStrictResult>;
5213
5290
  /**
5214
5291
  * Enumerate concierge threads in this fortress with summary metadata.
5215
5292
  * Sorted newest-first by last_turn_at.
@@ -5238,6 +5315,35 @@ declare class ConciergeMemoryStore {
5238
5315
  private withLock;
5239
5316
  }
5240
5317
 
5318
+ /**
5319
+ * Closed enum of categories the router may classify a query into. Adding
5320
+ * a category requires updating: this enum, the keyword table, the
5321
+ * fetcher interface, every wiring fetcher, and the test surface.
5322
+ */
5323
+ type ContextCategory = "templates" | "agent_state" | "agent_activity" | "audit_log" | "sentinel_findings" | "anomaly_alerts" | "recent_receipts" | "verascore_deltas";
5324
+ /**
5325
+ * Caller-supplied data sources. Each fetcher returns plain text the
5326
+ * router stitches into the rendered section. Returning the empty string
5327
+ * (or whitespace only) is treated as "nothing to fold for this category"
5328
+ * and the category drops out of the final categoriesIncluded list.
5329
+ */
5330
+ interface ContextFetchers {
5331
+ templates: () => Promise<string>;
5332
+ agent_state: (agentNameHint: string | null) => Promise<string>;
5333
+ agent_activity: (agentNameHint: string | null) => Promise<string>;
5334
+ audit_log: () => Promise<string>;
5335
+ sentinel_findings: () => Promise<string>;
5336
+ anomaly_alerts: () => Promise<string>;
5337
+ recent_receipts: () => Promise<string>;
5338
+ verascore_deltas: () => Promise<string>;
5339
+ }
5340
+ /**
5341
+ * Auxiliary classifier the caller may supply for the LLM-assist fallback
5342
+ * path. Invoked only when keyword classification returns zero matches
5343
+ * AND the query is non-trivial (>= 8 chars, not a clear greeting).
5344
+ */
5345
+ type LlmAssistClassifier = (query: string, categories: readonly ContextCategory[]) => Promise<ContextCategory | "none">;
5346
+
5241
5347
  /**
5242
5348
  * Sanctuary MCP Server — Operator Chat Service
5243
5349
  *
@@ -5342,13 +5448,72 @@ interface OperatorChatServiceDeps {
5342
5448
  /**
5343
5449
  * WP-V1.3-9 Tau-1: optional foundation memory store. When wired,
5344
5450
  * `sendConcierge` dual-writes each operator+concierge turn pair into
5345
- * the memory store under a session-scoped thread_id. Read-side wiring
5346
- * (substrate-selector context-fold) lands in Tau-2.
5451
+ * the memory store under a session-scoped thread_id. Tau-2 adds a
5452
+ * read-fold path that surfaces the prior turns to the substrate so
5453
+ * the concierge maintains coherence across a multi-turn session.
5347
5454
  *
5348
5455
  * Omit at construction time to disable memory-side persistence; the
5349
5456
  * existing `OperatorChatStore` write keeps working unchanged.
5350
5457
  */
5351
5458
  conciergeMemory?: ConciergeMemoryStore;
5459
+ /**
5460
+ * WP-V1.3-9 Tau-2: maximum prior turns folded into the substrate
5461
+ * context per round-trip. Defaults to
5462
+ * `DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS` (10). The current operator
5463
+ * turn is always present; this caps history only.
5464
+ */
5465
+ conciergeHistoryWindowTurns?: number;
5466
+ /**
5467
+ * WP-V1.3-9 Tau-2: prior turns older than this many milliseconds are
5468
+ * excluded from the active fold even when they remain on disk.
5469
+ * Defaults to `DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS` (24h).
5470
+ */
5471
+ conciergeHistoryFreshnessMs?: number;
5472
+ /**
5473
+ * WP-V1.3-9 Tau-2: rough token budget for the prior-conversation
5474
+ * portion of the substrate context. Estimated as ~4 chars per token.
5475
+ * Defaults to `DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET` (500). The
5476
+ * static reference block + fortress sections are NOT subject to this
5477
+ * budget; only the prior-conversation fold is pruned oldest-first.
5478
+ */
5479
+ conciergeHistoryTokenBudget?: number;
5480
+ /**
5481
+ * WP-V1.3-9 Tau-2: how long an active session may stay quiet before
5482
+ * the next `sendConcierge` allocates a fresh thread_id. Defaults to
5483
+ * `DEFAULT_CONCIERGE_SESSION_TTL_MS` (24h). Does not delete the prior
5484
+ * thread; it remains readable through the memory store.
5485
+ */
5486
+ conciergeSessionTtlMs?: number;
5487
+ /**
5488
+ * Optional clock for the session-TTL + freshness checks. Tests inject
5489
+ * a deterministic clock; production lets the default `Date.now`-based
5490
+ * implementation run.
5491
+ */
5492
+ conciergeClock?: () => number;
5493
+ /**
5494
+ * WP-V1.3-9 Tau-3: caller-supplied fetchers for dynamic context
5495
+ * injection. When wired alongside the substrate selector, every
5496
+ * `sendConcierge` round-trip routes the operator's query through the
5497
+ * concierge-context-router and folds matching live data into the
5498
+ * substrate prompt between the static Sanctuary reference and the
5499
+ * prior-turns fold. Omit to disable dynamic context (the static
5500
+ * reference + prior-turns paths keep working unchanged).
5501
+ */
5502
+ conciergeContextFetchers?: ContextFetchers;
5503
+ /**
5504
+ * WP-V1.3-9 Tau-3: optional LLM-assist classifier for queries that
5505
+ * fail keyword classification. Coordinator-CTO bake: when wired, the
5506
+ * service routes the auxiliary classifier call through the substrate
5507
+ * selector at the same `concierge` surface, so the auxiliary call
5508
+ * shares the operator's substrate choice and never opens a new
5509
+ * outbound surface.
5510
+ */
5511
+ conciergeContextLlmAssist?: LlmAssistClassifier;
5512
+ /**
5513
+ * WP-V1.3-9 Tau-3: rough token budget for the dynamic-context fold.
5514
+ * Defaults to `DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET` (2000).
5515
+ */
5516
+ conciergeDynamicContextBudget?: number;
5352
5517
  }
5353
5518
  declare class OperatorChatService {
5354
5519
  private store;
@@ -5359,6 +5524,14 @@ declare class OperatorChatService {
5359
5524
  private piiFilter?;
5360
5525
  private conciergeMaxTokens;
5361
5526
  private memory?;
5527
+ private historyWindowTurns;
5528
+ private historyFreshnessMs;
5529
+ private historyTokenBudget;
5530
+ private sessionTtlMs;
5531
+ private clock;
5532
+ private contextFetchers?;
5533
+ private contextLlmAssist?;
5534
+ private dynamicContextBudget;
5362
5535
  /**
5363
5536
  * In-memory thread_id assigned to the active concierge session.
5364
5537
  * The first sendConcierge call after construction allocates a fresh
@@ -5366,6 +5539,14 @@ declare class OperatorChatService {
5366
5539
  * folds the prior turns into context. Cleared by `resetConciergeMemoryThread`.
5367
5540
  */
5368
5541
  private activeMemoryThreadId?;
5542
+ /**
5543
+ * Wall-clock ms of the most recent sendConcierge that touched the
5544
+ * active session thread. Drives the WP-V1.3-9 Tau-2 session-TTL
5545
+ * check: a fresh sendConcierge after `sessionTtlMs` of quiet
5546
+ * allocates a new thread_id even though the prior one is still
5547
+ * readable from the memory store.
5548
+ */
5549
+ private lastInteractionAt?;
5369
5550
  constructor(deps: OperatorChatServiceDeps);
5370
5551
  /**
5371
5552
  * Operator submit on the concierge surface. Persists the operator's
@@ -5378,6 +5559,13 @@ declare class OperatorChatService {
5378
5559
  * operator sees on the page (no silent dropping).
5379
5560
  */
5380
5561
  sendConcierge(query: string): Promise<ConciergeResponse>;
5562
+ /**
5563
+ * Emit the WP-V1.3-9 Tau-2 graceful-degradation audit event. Pulled
5564
+ * out of `sendConcierge` so the read-fold path stays readable. Emits
5565
+ * with `result: "failure"` since the concierge fell back to
5566
+ * single-turn mode for this round-trip.
5567
+ */
5568
+ private emitMemoryReadFailed;
5381
5569
  /**
5382
5570
  * Read the persisted concierge thread, oldest message first. Returns
5383
5571
  * an empty array when no thread exists yet.
@@ -5424,6 +5612,14 @@ declare class OperatorChatService {
5424
5612
  * ## Sanctuary reference
5425
5613
  * <static domain reference block>
5426
5614
  *
5615
+ * ## Live fortress context ← WP-V1.3-9 Tau-3, when present
5616
+ * ### <Category>
5617
+ * <fetcher payload>
5618
+ *
5619
+ * ## Prior conversation ← WP-V1.3-9 Tau-2, when present
5620
+ * OPERATOR: ...
5621
+ * CONCIERGE: ...
5622
+ *
5427
5623
  * ## Recent activity
5428
5624
  * <recentActivity output>
5429
5625
  *
@@ -5433,8 +5629,39 @@ declare class OperatorChatService {
5433
5629
  * ## Open inbox
5434
5630
  * <openInbox output>
5435
5631
  * ```
5632
+ *
5633
+ * The substrate selector ships a `context: string` shape (not a
5634
+ * messages array), so multi-turn coherence is folded as a structured
5635
+ * prior-conversation section with explicit OPERATOR / CONCIERGE
5636
+ * boundaries. Coordinator-CTO guidance: prefer messages-array shape
5637
+ * if available; the v1.2 selector does not expose one, so structured
5638
+ * serialization is the canonical path for v1.3.
5436
5639
  */
5437
5640
  private assembleConciergeContext;
5641
+ /**
5642
+ * Run the WP-V1.3-9 Tau-3 dynamic-context fold for a single round-
5643
+ * trip. Fail-soft on every axis: missing fetchers short-circuit to
5644
+ * an empty fold, fetcher failures emit a per-category audit event
5645
+ * and are omitted from the rendered section, an LLM-assist failure
5646
+ * proceeds with no fold. Returns the rendered section + the list of
5647
+ * categories whose data made it into the section (used for the
5648
+ * round-trip audit emission).
5649
+ */
5650
+ private runDynamicContextFold;
5651
+ /**
5652
+ * Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
5653
+ * of the fold path so the dynamic-context handler stays readable.
5654
+ * Emits with `result: "failure"` since the named category dropped
5655
+ * from the rendered section for this round-trip.
5656
+ */
5657
+ private emitContextFetcherFailed;
5658
+ /**
5659
+ * Render the prior-conversation section with token-budget enforcement
5660
+ * (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
5661
+ * section exceeds `historyTokenBudget`. Returns an empty string when
5662
+ * the input is empty or when the budget excludes every turn.
5663
+ */
5664
+ private formatPriorTurnsSection;
5438
5665
  private emit;
5439
5666
  }
5440
5667
 
@@ -5918,6 +6145,87 @@ interface V11Bindings {
5918
6145
  operatorChatService?: OperatorChatService;
5919
6146
  }
5920
6147
 
6148
+ /**
6149
+ * Sanctuary v1.3 WP-V1.3-10 Cross-Harness Approval Inbox Upsilon-3
6150
+ *
6151
+ * At-rest encrypted persistence for full request payloads tied to
6152
+ * AggregatedApproval entries. Sibling store to ApprovalAggregator: the
6153
+ * aggregator's own entry record (status, provenance, hashes) is persisted
6154
+ * by the aggregator class under `_approval_aggregator`; this store holds
6155
+ * the original request payload separately under
6156
+ * `_approval_aggregator_payloads` so the operator can replay the full
6157
+ * payload after a server restart.
6158
+ *
6159
+ * Storage layout:
6160
+ * namespace: `_approval_aggregator_payloads`
6161
+ * key: `payload.{aggregator_id}` (one record per approval)
6162
+ * payload: AES-256-GCM ciphertext of the JSON-serialised bundle.
6163
+ * key: `l2-approval-aggregator-payload-v1` HKDF subkey of fortress
6164
+ * master.
6165
+ * AAD: UTF-8 bytes of `aggregator_id`. Swapping records across
6166
+ * aggregator_ids breaks the auth tag. Castle-walking
6167
+ * discipline: the encryption boundary holds even against
6168
+ * on-disk shuffle.
6169
+ *
6170
+ * Retention:
6171
+ * Each bundle carries an ISO-8601 `retention_until`. `pruneExpired()`
6172
+ * iterates payloads and drops entries whose retention_until is in the
6173
+ * past. Default retention is 30 days, mirroring the audit-log retention
6174
+ * envelope. Operator override flows through the aggregator's deps.
6175
+ *
6176
+ * Multi-fortress isolation:
6177
+ * The HKDF subkey is derived from the fortress master key. Two
6178
+ * fortresses never produce identical encryption keys for identical
6179
+ * aggregator_ids. Isolation is enforced cryptographically; the AAD
6180
+ * binds the ciphertext to a specific aggregator_id within a fortress.
6181
+ */
6182
+
6183
+ interface AggregatorPayloadStoreOptions {
6184
+ /** Storage backend that persists the encrypted bundles. */
6185
+ storage: StorageBackend;
6186
+ /** 32-byte fortress master key. */
6187
+ masterKey: Uint8Array;
6188
+ /** Stable fortress id stamped on every bundle for audit clarity. */
6189
+ fortressId: string;
6190
+ /** Operator-tunable retention window. Default 30 days. */
6191
+ retentionDays?: number;
6192
+ }
6193
+ /**
6194
+ * Encrypted, AAD-bound, retention-aware payload persistence for the
6195
+ * cross-harness approval inbox.
6196
+ */
6197
+ declare class AggregatorPayloadStore {
6198
+ private readonly storage;
6199
+ private readonly encryptionKey;
6200
+ private readonly fortressId;
6201
+ private readonly retentionDays;
6202
+ constructor(opts: AggregatorPayloadStoreOptions);
6203
+ /**
6204
+ * Persist `payload` under the given aggregator_id. Idempotent; calling
6205
+ * twice with the same id rewrites the bundle (retention_until is
6206
+ * recomputed). Returns the bundle's retention_until ISO-8601 timestamp
6207
+ * so callers can log it.
6208
+ */
6209
+ savePayload(aggregatorId: string, payload: unknown): Promise<string>;
6210
+ /**
6211
+ * Read the persisted payload for the aggregator_id. Returns null if no
6212
+ * bundle exists, the bundle is corrupted, or AAD binding fails.
6213
+ */
6214
+ loadPayload(aggregatorId: string): Promise<unknown>;
6215
+ /**
6216
+ * Delete the persisted payload. Returns true when a bundle was removed,
6217
+ * false when none existed.
6218
+ */
6219
+ deletePayload(aggregatorId: string): Promise<boolean>;
6220
+ /**
6221
+ * Drop expired payload bundles. Returns the count of bundles pruned.
6222
+ * Caller wires this into the cocoon-unlock initialization path.
6223
+ */
6224
+ pruneExpired(now?: Date): Promise<{
6225
+ pruned: number;
6226
+ }>;
6227
+ }
6228
+
5921
6229
  /**
5922
6230
  * Sanctuary v1.3 WP-V1.3-10 Cross-Harness Approval Inbox Upsilon-1
5923
6231
  *
@@ -5955,6 +6263,19 @@ interface V11Bindings {
5955
6263
  * - `expired`: pending past TTL on a `list()` poll without a resolution.
5956
6264
  */
5957
6265
  type AggregatedApprovalStatus = "pending" | "approved" | "denied" | "timeout" | "expired";
6266
+ /**
6267
+ * One step in the Castle Architecture enforcement chain that led to this
6268
+ * approval. Layers are: l1 (Castle Wall, OS-level egress), l2 (Sentinel +
6269
+ * cooperative MCP gate), l3 (selective disclosure), l4 (reputation).
6270
+ * v1.3 Upsilon-3 ships the schema; default resolver populates a single
6271
+ * `l2` entry. Future Castle Wall wiring will extend the chain when a
6272
+ * payload's egress was first observed by the kernel filter.
6273
+ */
6274
+ interface EnforcementLayerEvent {
6275
+ layer: "l1" | "l2" | "l3" | "l4";
6276
+ event: string;
6277
+ timestamp: string;
6278
+ }
5958
6279
  /**
5959
6280
  * Normalized record the aggregator stores per approval. Field set is
5960
6281
  * additive-stable; new fields go behind `?` so existing dashboards keep
@@ -5999,6 +6320,15 @@ interface AggregatedApproval {
5999
6320
  * (the cross-link signal lives on the aggregator side only).
6000
6321
  */
6001
6322
  hub_inbox_item_id?: string;
6323
+ /**
6324
+ * Castle Architecture enforcement-layer chain that led to this
6325
+ * approval. Populated by the optional `resolveEnforcementChain` deps
6326
+ * hook; default returns a single `l2` step (cooperative MCP gate
6327
+ * fired). Persisted with the entry so the operator-replay surface can
6328
+ * render the enforcement context after a server restart. v1.3
6329
+ * Upsilon-3.
6330
+ */
6331
+ enforcement_chain?: EnforcementLayerEvent[];
6002
6332
  }
6003
6333
  /**
6004
6334
  * Source-context resolver. Called once per ingest to map gate context to
@@ -6087,6 +6417,23 @@ interface ApprovalAggregatorDeps {
6087
6417
  * hub inbox store.
6088
6418
  */
6089
6419
  resolveHubInboxItemId?: (event: ApprovalGateEvent) => string | undefined;
6420
+ /**
6421
+ * Optional at-rest payload store. When provided, the aggregator
6422
+ * persists each request payload via `savePayload` on ingest, and
6423
+ * rehydrates payloads on `getFullPayload` if the in-memory map lost
6424
+ * them (e.g. after a server restart). Upsilon-3 surface; absent in
6425
+ * Upsilon-1 / Upsilon-2 deployments, where payloads remain in-memory
6426
+ * only.
6427
+ */
6428
+ payloadStore?: AggregatorPayloadStore;
6429
+ /**
6430
+ * Optional resolver for the Castle Architecture enforcement chain
6431
+ * leading to this approval. Default returns a single `l2` step
6432
+ * (cooperative MCP gate fired). When the Castle Wall (Layer 1) ships,
6433
+ * its kernel-filter observer can populate richer chains by passing a
6434
+ * resolver here.
6435
+ */
6436
+ resolveEnforcementChain?: (event: ApprovalGateEvent) => EnforcementLayerEvent[];
6090
6437
  }
6091
6438
  /**
6092
6439
  * Aggregator state. The map is hydrated lazily from the encrypted
@@ -6103,6 +6450,8 @@ declare class ApprovalAggregator {
6103
6450
  private readonly now;
6104
6451
  private readonly resolveSourceContext;
6105
6452
  private readonly resolveHubInboxItemId;
6453
+ private readonly payloadStore;
6454
+ private readonly resolveEnforcementChain;
6106
6455
  /** Cached entries by `aggregator_id`. */
6107
6456
  private readonly entries;
6108
6457
  /** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
@@ -6141,10 +6490,48 @@ declare class ApprovalAggregator {
6141
6490
  }): Promise<AggregatedApproval[]>;
6142
6491
  /**
6143
6492
  * Return the original (unhashed) request payload for the entry. Returns
6144
- * `null` when the entry is unknown or the payload was evicted (e.g. the
6145
- * process restarted; payloads are in-memory only at v1.3 Upsilon-1).
6493
+ * `null` when the entry is unknown. When the in-memory payload map has
6494
+ * been evicted (e.g. after a server restart) and a `payloadStore` was
6495
+ * provided, the at-rest bundle is decrypted and the in-memory map is
6496
+ * refilled. Audit emission lives on the `*WithAudit` variant; this base
6497
+ * accessor is silent so internal callers can read without polluting the
6498
+ * audit trail.
6146
6499
  */
6147
6500
  getFullPayload(aggregatorId: string): Promise<unknown>;
6501
+ /**
6502
+ * Return the entry record for the given id, or null when unknown.
6503
+ * Idempotent. v1.3 Upsilon-3.
6504
+ */
6505
+ getEntry(aggregatorId: string): Promise<AggregatedApproval | null>;
6506
+ /**
6507
+ * Audited variant of `getFullPayload`. Emits the
6508
+ * `cross_harness_approval_payload_decrypted` audit event before
6509
+ * returning. Used by the operator-facing /payload replay route.
6510
+ * v1.3 Upsilon-3.
6511
+ */
6512
+ getFullPayloadWithAudit(aggregatorId: string, operatorId: string): Promise<unknown>;
6513
+ /**
6514
+ * Return the audit-log entries that led to and surround this approval.
6515
+ * Best-effort matching: aggregator-side emissions (AGGREGATED, RESOLVED,
6516
+ * DEDUPED, replay events) all carry `details.aggregator_id` and link
6517
+ * directly. Gate-side emissions (`gate_*:operation`) do not carry the
6518
+ * aggregator id at v1.3, so they are matched via timestamp window
6519
+ * (entry.created_at to entry.resolved_at + 1s, or expires_at + 1s while
6520
+ * pending) and operation suffix. Emits AUDIT_TRAIL_VIEWED on call.
6521
+ * v1.3 Upsilon-3.
6522
+ */
6523
+ getAuditTrail(aggregatorId: string, operatorId: string): Promise<AuditEntry[]>;
6524
+ /**
6525
+ * List historical (resolved) approvals. Excludes pending entries by
6526
+ * design: `list()` is the pending-inbox surface and `getHistory()` is
6527
+ * the resolved-replay surface. Emits REPLAYED on each call. v1.3
6528
+ * Upsilon-3.
6529
+ */
6530
+ getHistory(opts: {
6531
+ status?: AggregatedApprovalStatus;
6532
+ sinceTs?: string;
6533
+ limit?: number;
6534
+ } | undefined, operatorId: string): Promise<AggregatedApproval[]>;
6148
6535
  /**
6149
6536
  * Resolve an entry. Used by both:
6150
6537
  * 1. The gate wire-up on channel-decision return.