@sanctuary-framework/mcp-server 1.2.3 → 1.2.5
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/cli.cjs +2329 -136
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2329 -136
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2009 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +689 -7
- package/dist/index.d.ts +689 -7
- package/dist/index.js +2009 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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;
|
|
@@ -263,6 +300,20 @@ interface PrincipalPolicy {
|
|
|
263
300
|
tier3_always_allow: string[];
|
|
264
301
|
/** How approval requests reach the human */
|
|
265
302
|
approval_channel: ApprovalChannelConfig;
|
|
303
|
+
/**
|
|
304
|
+
* WP-V1.3-9 Tau-1: operator-tunable retention window (days) for the
|
|
305
|
+
* concierge memory store. Per-turn `retention_until` is stamped at
|
|
306
|
+
* append time; cocoon-unlock pruning drops expired turns. Default 30
|
|
307
|
+
* days when absent; values <= 0 fall back to the default.
|
|
308
|
+
*/
|
|
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;
|
|
266
317
|
}
|
|
267
318
|
/** Approval request sent to the human */
|
|
268
319
|
interface ApprovalRequest {
|
|
@@ -276,7 +327,7 @@ interface ApprovalRequest {
|
|
|
276
327
|
interface ApprovalResponse {
|
|
277
328
|
decision: "approve" | "deny";
|
|
278
329
|
decided_at: string;
|
|
279
|
-
decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
|
|
330
|
+
decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive" | "channel_failure";
|
|
280
331
|
}
|
|
281
332
|
/** Result of the approval gate evaluation */
|
|
282
333
|
interface GateResult {
|
|
@@ -658,6 +709,31 @@ type InjectionAlertCallback = (alert: {
|
|
|
658
709
|
}) => void;
|
|
659
710
|
/** Resolver for proxy tool tiers — provided by the ProxyRouter */
|
|
660
711
|
type ProxyTierResolver = (toolName: string) => (1 | 2 | 3) | null;
|
|
712
|
+
/**
|
|
713
|
+
* Approval-lifecycle callback. Wired in v1.3 Upsilon-1 by the Cross-
|
|
714
|
+
* Harness Approval Inbox aggregator. The gate fires this callback before
|
|
715
|
+
* `channel.requestApproval()` (phase = "requested") and after the channel
|
|
716
|
+
* resolves or fails (phase = "resolved"). The callback is fire-and-forget;
|
|
717
|
+
* exceptions are swallowed so a broken aggregator never blocks the gate.
|
|
718
|
+
*
|
|
719
|
+
* The callback shape is import-free here so the principal-policy module
|
|
720
|
+
* keeps no dependency on the aggregator (the aggregator imports the gate,
|
|
721
|
+
* not vice versa).
|
|
722
|
+
*/
|
|
723
|
+
type ApprovalEventCallback = (event: {
|
|
724
|
+
phase: "requested" | "resolved";
|
|
725
|
+
operation: string;
|
|
726
|
+
tier: 1 | 2;
|
|
727
|
+
reason: string;
|
|
728
|
+
context: Record<string, unknown>;
|
|
729
|
+
request_timestamp: string;
|
|
730
|
+
resolution?: {
|
|
731
|
+
decision: "approve" | "deny";
|
|
732
|
+
decided_at: string;
|
|
733
|
+
decided_by: string;
|
|
734
|
+
};
|
|
735
|
+
correlation_id: string;
|
|
736
|
+
}) => void;
|
|
661
737
|
declare class ApprovalGate {
|
|
662
738
|
private policy;
|
|
663
739
|
private baseline;
|
|
@@ -665,8 +741,16 @@ declare class ApprovalGate {
|
|
|
665
741
|
private auditLog;
|
|
666
742
|
private injectionDetector;
|
|
667
743
|
private onInjectionAlert?;
|
|
744
|
+
private onApprovalEvent?;
|
|
668
745
|
private proxyTierResolver?;
|
|
669
|
-
constructor(policy: PrincipalPolicy, baseline: BaselineTracker, channel: ApprovalChannel, auditLog: AuditLog, injectionDetector?: InjectionDetector, onInjectionAlert?: InjectionAlertCallback);
|
|
746
|
+
constructor(policy: PrincipalPolicy, baseline: BaselineTracker, channel: ApprovalChannel, auditLog: AuditLog, injectionDetector?: InjectionDetector, onInjectionAlert?: InjectionAlertCallback, onApprovalEvent?: ApprovalEventCallback);
|
|
747
|
+
/**
|
|
748
|
+
* Set the approval-event callback after construction. Used by the
|
|
749
|
+
* Upsilon-1 wire-up when the aggregator is constructed alongside the
|
|
750
|
+
* gate. The aggregator subscribes through this setter rather than the
|
|
751
|
+
* constructor so existing call sites continue to work unchanged.
|
|
752
|
+
*/
|
|
753
|
+
setApprovalEventCallback(cb: ApprovalEventCallback | undefined): void;
|
|
670
754
|
/**
|
|
671
755
|
* Set the proxy tier resolver. Called after the proxy router is initialized.
|
|
672
756
|
*/
|
|
@@ -685,6 +769,13 @@ declare class ApprovalGate {
|
|
|
685
769
|
private detectAnomaly;
|
|
686
770
|
/**
|
|
687
771
|
* Request approval from the human principal.
|
|
772
|
+
*
|
|
773
|
+
* Fail-closed contract (full-sweep #49): if the channel throws (network
|
|
774
|
+
* down, callback unreachable, dashboard SSE peer dropped, webhook DNS
|
|
775
|
+
* failure, etc.), the gate denies the operation and audit-logs the cause.
|
|
776
|
+
* Channel-internal timeouts already resolve with decision: "deny" per
|
|
777
|
+
* SEC-002; this catch covers the remaining "channel raised" path so an
|
|
778
|
+
* unhandled rejection cannot turn into an indeterminate state at the gate.
|
|
688
779
|
*/
|
|
689
780
|
private requestApproval;
|
|
690
781
|
/**
|
|
@@ -3537,7 +3628,7 @@ interface ExitBundleVerifierResult {
|
|
|
3537
3628
|
* Coarse failure-class enum, when `passed` is false. Distinct from a free-text
|
|
3538
3629
|
* error message so import commands can branch.
|
|
3539
3630
|
*/
|
|
3540
|
-
failure_class?: "manifest_signature_invalid" | "manifest_unknown_version" | "manifest_signature_scheme_invalid" | "artifact_hash_mismatch" | "artifact_missing" | "artifact_size_mismatch" | "aggregate_hash_mismatch" | "artifact_path_unsafe" | "artifact_path_duplicate" | "artifact_path_escapes_root" | "archive_contains_symlink" | "private_material_present" | "other";
|
|
3631
|
+
failure_class?: "manifest_signature_invalid" | "manifest_unknown_version" | "manifest_signature_scheme_invalid" | "artifact_hash_mismatch" | "artifact_missing" | "artifact_size_mismatch" | "aggregate_hash_mismatch" | "artifact_path_unsafe" | "artifact_path_duplicate" | "artifact_path_escapes_root" | "archive_contains_symlink" | "private_material_present" | "identity_signature_invalid" | "reputation_bundle_signature_invalid" | "reputation_attestation_signature_invalid" | "reputation_unverifiable_attestations" | "other";
|
|
3541
3632
|
}
|
|
3542
3633
|
|
|
3543
3634
|
/**
|
|
@@ -4104,21 +4195,35 @@ interface ExitCommandArgs {
|
|
|
4104
4195
|
declare function runExitCommand(args: ExitCommandArgs): Promise<number>;
|
|
4105
4196
|
|
|
4106
4197
|
/**
|
|
4107
|
-
* Sanctuary MCP Server
|
|
4198
|
+
* Sanctuary MCP Server -- Principal Policy Loader
|
|
4108
4199
|
*
|
|
4109
4200
|
* Loads the Principal Policy from a YAML file at server startup.
|
|
4110
|
-
* The policy is immutable at runtime
|
|
4201
|
+
* The policy is immutable at runtime -- no MCP tool can modify it.
|
|
4111
4202
|
*
|
|
4112
4203
|
* Security invariant:
|
|
4113
4204
|
* - The policy is loaded ONCE at startup and frozen.
|
|
4114
4205
|
* - No code path exists to modify the policy during a session.
|
|
4115
4206
|
* - If no policy file exists, a sensible default is generated and saved.
|
|
4207
|
+
* - If the policy file exists but is malformed, the server refuses to start
|
|
4208
|
+
* rather than silently substituting a default (operator intent preservation).
|
|
4116
4209
|
*/
|
|
4117
4210
|
|
|
4211
|
+
/**
|
|
4212
|
+
* Thrown when a principal-policy.yaml file exists on disk but cannot be
|
|
4213
|
+
* parsed or validated. Sanctuary refuses to substitute a default policy
|
|
4214
|
+
* when an existing file is present, to avoid silently overriding operator
|
|
4215
|
+
* intent.
|
|
4216
|
+
*/
|
|
4217
|
+
declare class MalformedPrincipalPolicyError extends Error {
|
|
4218
|
+
readonly policyPath: string;
|
|
4219
|
+
readonly reason: string;
|
|
4220
|
+
constructor(policyPath: string, reason: string);
|
|
4221
|
+
}
|
|
4118
4222
|
/**
|
|
4119
4223
|
* Load the Principal Policy from disk.
|
|
4120
4224
|
* If no policy file exists, generate the default and save it.
|
|
4121
|
-
*
|
|
4225
|
+
* If the file exists but is malformed, throw MalformedPrincipalPolicyError.
|
|
4226
|
+
* The returned policy is frozen -- immutable at runtime.
|
|
4122
4227
|
*/
|
|
4123
4228
|
declare function loadPrincipalPolicy(storagePath: string): Promise<PrincipalPolicy>;
|
|
4124
4229
|
|
|
@@ -5045,6 +5150,171 @@ declare class OperatorChatStore {
|
|
|
5045
5150
|
deleteThread(surface: OperatorChatSurface, threadKey: string): Promise<void>;
|
|
5046
5151
|
}
|
|
5047
5152
|
|
|
5153
|
+
/**
|
|
5154
|
+
* Sanctuary MCP Server — Concierge Memory Store (WP-V1.3-9 Tau-1)
|
|
5155
|
+
*
|
|
5156
|
+
* Per-fortress, encrypted-at-rest, cocoon-bound persistence for
|
|
5157
|
+
* concierge conversation turns. Distinct from the v1.2 OperatorChatStore
|
|
5158
|
+
* (single fortress-scoped thread, capped FIFO, no retention) — this
|
|
5159
|
+
* store is the foundation for v1.3 conversational sovereignty depth:
|
|
5160
|
+
* multi-thread enumeration, per-turn retention, scrollable history.
|
|
5161
|
+
*
|
|
5162
|
+
* Storage layout:
|
|
5163
|
+
* namespace: `_chat` (existing reserved L1 namespace)
|
|
5164
|
+
* key: `concierge_memory.{thread_id}` (one record per thread)
|
|
5165
|
+
* payload: AES-256-GCM ciphertext of the JSON-serialised turns bundle.
|
|
5166
|
+
* key: `concierge-memory-store-v1` HKDF subkey of fortress master.
|
|
5167
|
+
* AAD: UTF-8 bytes of `thread_id` — swapping records across
|
|
5168
|
+
* threads breaks the auth tag. Castle-walking discipline:
|
|
5169
|
+
* the encryption boundary holds even against on-disk shuffle.
|
|
5170
|
+
*
|
|
5171
|
+
* Concurrency:
|
|
5172
|
+
* Per-thread async lock serializes appendTurn calls within the same
|
|
5173
|
+
* store instance. Cross-process locking is out of scope (single-process
|
|
5174
|
+
* Sanctuary server).
|
|
5175
|
+
*
|
|
5176
|
+
* Retention:
|
|
5177
|
+
* Each turn carries an ISO-8601 `retention_until`. `pruneExpired()`
|
|
5178
|
+
* iterates threads and drops turns whose retention_until is in the
|
|
5179
|
+
* past. Empty bundles are deleted entirely. Caller wires this into
|
|
5180
|
+
* the cocoon-unlock initialization path (the wiring layer fires it
|
|
5181
|
+
* once the store is constructed).
|
|
5182
|
+
*
|
|
5183
|
+
* Multi-fortress isolation:
|
|
5184
|
+
* The HKDF subkey is derived from the fortress master key. Two
|
|
5185
|
+
* fortresses never produce identical encryption keys for identical
|
|
5186
|
+
* thread_ids. The `fortress_id` field on each turn is metadata for
|
|
5187
|
+
* audit clarity; isolation is enforced cryptographically.
|
|
5188
|
+
*/
|
|
5189
|
+
|
|
5190
|
+
/** One turn in a concierge thread. */
|
|
5191
|
+
interface ConciergeTurn {
|
|
5192
|
+
/** UUID of the thread this turn belongs to. */
|
|
5193
|
+
thread_id: string;
|
|
5194
|
+
/** Stable fortress id (audit metadata; isolation is keyed). */
|
|
5195
|
+
fortress_id: string;
|
|
5196
|
+
/** Monotonic turn number within thread, starting at 1. */
|
|
5197
|
+
turn_id: number;
|
|
5198
|
+
/** Sender role. */
|
|
5199
|
+
role: "user" | "assistant";
|
|
5200
|
+
/** Cleartext at runtime; AES-256-GCM at rest. */
|
|
5201
|
+
content: string;
|
|
5202
|
+
/** ISO-8601 timestamp of turn creation. */
|
|
5203
|
+
created_at: string;
|
|
5204
|
+
/** ISO-8601 timestamp after which prune drops this turn. */
|
|
5205
|
+
retention_until: string;
|
|
5206
|
+
}
|
|
5207
|
+
/** Operator-facing summary surfaced by `listThreads`. */
|
|
5208
|
+
interface ConciergeThreadSummary {
|
|
5209
|
+
thread_id: string;
|
|
5210
|
+
created_at: string;
|
|
5211
|
+
last_turn_at: string;
|
|
5212
|
+
turn_count: number;
|
|
5213
|
+
}
|
|
5214
|
+
interface ConciergeMemoryStoreOptions {
|
|
5215
|
+
/** Storage backend that persists the encrypted bundles. */
|
|
5216
|
+
storage: StorageBackend;
|
|
5217
|
+
/** 32-byte fortress master key. */
|
|
5218
|
+
masterKey: Uint8Array;
|
|
5219
|
+
/** Stable fortress id stamped on every turn for audit clarity. */
|
|
5220
|
+
fortressId: string;
|
|
5221
|
+
/** Operator-tunable retention window. Default 30 days. */
|
|
5222
|
+
retentionDays?: number;
|
|
5223
|
+
}
|
|
5224
|
+
interface ReadThreadOptions {
|
|
5225
|
+
/** Return only turns with turn_id strictly greater than this. */
|
|
5226
|
+
sinceTurnId?: number;
|
|
5227
|
+
/** Cap the number of turns returned (oldest-first within the bundle). */
|
|
5228
|
+
limit?: number;
|
|
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
|
+
};
|
|
5248
|
+
interface ListThreadsOptions {
|
|
5249
|
+
/** Cap the number of summaries returned. */
|
|
5250
|
+
limit?: number;
|
|
5251
|
+
}
|
|
5252
|
+
/**
|
|
5253
|
+
* Encrypted, AAD-bound, retention-aware concierge memory persistence.
|
|
5254
|
+
*/
|
|
5255
|
+
declare class ConciergeMemoryStore {
|
|
5256
|
+
private storage;
|
|
5257
|
+
private encryptionKey;
|
|
5258
|
+
private fortressId;
|
|
5259
|
+
private retentionDays;
|
|
5260
|
+
private locks;
|
|
5261
|
+
constructor(opts: ConciergeMemoryStoreOptions);
|
|
5262
|
+
/**
|
|
5263
|
+
* Append a turn to the named thread, creating the bundle if no record
|
|
5264
|
+
* exists. Returns the persisted turn (with assigned turn_id +
|
|
5265
|
+
* retention_until). Per-thread serialisation guarantees turn_id
|
|
5266
|
+
* monotonicity even under concurrent callers.
|
|
5267
|
+
*/
|
|
5268
|
+
appendTurn(threadId: string, role: "user" | "assistant", content: string): Promise<ConciergeTurn>;
|
|
5269
|
+
/**
|
|
5270
|
+
* Read turns from a thread, oldest-first. Returns an empty array if
|
|
5271
|
+
* the thread does not exist or its bundle is corrupt. Does not emit
|
|
5272
|
+
* audit events; the caller (HTTP route handler) owns audit semantics.
|
|
5273
|
+
*/
|
|
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>;
|
|
5290
|
+
/**
|
|
5291
|
+
* Enumerate concierge threads in this fortress with summary metadata.
|
|
5292
|
+
* Sorted newest-first by last_turn_at.
|
|
5293
|
+
*/
|
|
5294
|
+
listThreads(opts?: ListThreadsOptions): Promise<ConciergeThreadSummary[]>;
|
|
5295
|
+
/**
|
|
5296
|
+
* Delete a thread's bundle. Returns true if the bundle existed and
|
|
5297
|
+
* was removed; false if no bundle was present. Audit emission is the
|
|
5298
|
+
* caller's responsibility.
|
|
5299
|
+
*/
|
|
5300
|
+
deleteThread(threadId: string): Promise<boolean>;
|
|
5301
|
+
/**
|
|
5302
|
+
* Drop expired turns across all threads. Threads emptied by pruning
|
|
5303
|
+
* are removed entirely. Returns the count of turns pruned.
|
|
5304
|
+
*/
|
|
5305
|
+
pruneExpired(now?: Date): Promise<{
|
|
5306
|
+
pruned: number;
|
|
5307
|
+
}>;
|
|
5308
|
+
private loadBundle;
|
|
5309
|
+
private saveBundle;
|
|
5310
|
+
/**
|
|
5311
|
+
* Run `task` while holding the per-thread async lock. Lock is released
|
|
5312
|
+
* once the task settles (success or failure). Generic helper so
|
|
5313
|
+
* appendTurn / deleteThread / pruneExpired share serialisation.
|
|
5314
|
+
*/
|
|
5315
|
+
private withLock;
|
|
5316
|
+
}
|
|
5317
|
+
|
|
5048
5318
|
/**
|
|
5049
5319
|
* Sanctuary MCP Server — Operator Chat Service
|
|
5050
5320
|
*
|
|
@@ -5146,6 +5416,51 @@ interface OperatorChatServiceDeps {
|
|
|
5146
5416
|
* summary). Operator-tunable via dashboard config.
|
|
5147
5417
|
*/
|
|
5148
5418
|
conciergeMaxTokens?: number;
|
|
5419
|
+
/**
|
|
5420
|
+
* WP-V1.3-9 Tau-1: optional foundation memory store. When wired,
|
|
5421
|
+
* `sendConcierge` dual-writes each operator+concierge turn pair into
|
|
5422
|
+
* the memory store under a session-scoped thread_id. Tau-2 adds a
|
|
5423
|
+
* read-fold path that surfaces the prior turns to the substrate so
|
|
5424
|
+
* the concierge maintains coherence across a multi-turn session.
|
|
5425
|
+
*
|
|
5426
|
+
* Omit at construction time to disable memory-side persistence; the
|
|
5427
|
+
* existing `OperatorChatStore` write keeps working unchanged.
|
|
5428
|
+
*/
|
|
5429
|
+
conciergeMemory?: ConciergeMemoryStore;
|
|
5430
|
+
/**
|
|
5431
|
+
* WP-V1.3-9 Tau-2: maximum prior turns folded into the substrate
|
|
5432
|
+
* context per round-trip. Defaults to
|
|
5433
|
+
* `DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS` (10). The current operator
|
|
5434
|
+
* turn is always present; this caps history only.
|
|
5435
|
+
*/
|
|
5436
|
+
conciergeHistoryWindowTurns?: number;
|
|
5437
|
+
/**
|
|
5438
|
+
* WP-V1.3-9 Tau-2: prior turns older than this many milliseconds are
|
|
5439
|
+
* excluded from the active fold even when they remain on disk.
|
|
5440
|
+
* Defaults to `DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS` (24h).
|
|
5441
|
+
*/
|
|
5442
|
+
conciergeHistoryFreshnessMs?: number;
|
|
5443
|
+
/**
|
|
5444
|
+
* WP-V1.3-9 Tau-2: rough token budget for the prior-conversation
|
|
5445
|
+
* portion of the substrate context. Estimated as ~4 chars per token.
|
|
5446
|
+
* Defaults to `DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET` (500). The
|
|
5447
|
+
* static reference block + fortress sections are NOT subject to this
|
|
5448
|
+
* budget; only the prior-conversation fold is pruned oldest-first.
|
|
5449
|
+
*/
|
|
5450
|
+
conciergeHistoryTokenBudget?: number;
|
|
5451
|
+
/**
|
|
5452
|
+
* WP-V1.3-9 Tau-2: how long an active session may stay quiet before
|
|
5453
|
+
* the next `sendConcierge` allocates a fresh thread_id. Defaults to
|
|
5454
|
+
* `DEFAULT_CONCIERGE_SESSION_TTL_MS` (24h). Does not delete the prior
|
|
5455
|
+
* thread; it remains readable through the memory store.
|
|
5456
|
+
*/
|
|
5457
|
+
conciergeSessionTtlMs?: number;
|
|
5458
|
+
/**
|
|
5459
|
+
* Optional clock for the session-TTL + freshness checks. Tests inject
|
|
5460
|
+
* a deterministic clock; production lets the default `Date.now`-based
|
|
5461
|
+
* implementation run.
|
|
5462
|
+
*/
|
|
5463
|
+
conciergeClock?: () => number;
|
|
5149
5464
|
}
|
|
5150
5465
|
declare class OperatorChatService {
|
|
5151
5466
|
private store;
|
|
@@ -5155,6 +5470,27 @@ declare class OperatorChatService {
|
|
|
5155
5470
|
private contextProviders?;
|
|
5156
5471
|
private piiFilter?;
|
|
5157
5472
|
private conciergeMaxTokens;
|
|
5473
|
+
private memory?;
|
|
5474
|
+
private historyWindowTurns;
|
|
5475
|
+
private historyFreshnessMs;
|
|
5476
|
+
private historyTokenBudget;
|
|
5477
|
+
private sessionTtlMs;
|
|
5478
|
+
private clock;
|
|
5479
|
+
/**
|
|
5480
|
+
* In-memory thread_id assigned to the active concierge session.
|
|
5481
|
+
* The first sendConcierge call after construction allocates a fresh
|
|
5482
|
+
* UUID; subsequent calls reuse it so multi-turn coherence (Tau-2)
|
|
5483
|
+
* folds the prior turns into context. Cleared by `resetConciergeMemoryThread`.
|
|
5484
|
+
*/
|
|
5485
|
+
private activeMemoryThreadId?;
|
|
5486
|
+
/**
|
|
5487
|
+
* Wall-clock ms of the most recent sendConcierge that touched the
|
|
5488
|
+
* active session thread. Drives the WP-V1.3-9 Tau-2 session-TTL
|
|
5489
|
+
* check: a fresh sendConcierge after `sessionTtlMs` of quiet
|
|
5490
|
+
* allocates a new thread_id even though the prior one is still
|
|
5491
|
+
* readable from the memory store.
|
|
5492
|
+
*/
|
|
5493
|
+
private lastInteractionAt?;
|
|
5158
5494
|
constructor(deps: OperatorChatServiceDeps);
|
|
5159
5495
|
/**
|
|
5160
5496
|
* Operator submit on the concierge surface. Persists the operator's
|
|
@@ -5167,11 +5503,47 @@ declare class OperatorChatService {
|
|
|
5167
5503
|
* operator sees on the page (no silent dropping).
|
|
5168
5504
|
*/
|
|
5169
5505
|
sendConcierge(query: string): Promise<ConciergeResponse>;
|
|
5506
|
+
/**
|
|
5507
|
+
* Emit the WP-V1.3-9 Tau-2 graceful-degradation audit event. Pulled
|
|
5508
|
+
* out of `sendConcierge` so the read-fold path stays readable. Emits
|
|
5509
|
+
* with `result: "failure"` since the concierge fell back to
|
|
5510
|
+
* single-turn mode for this round-trip.
|
|
5511
|
+
*/
|
|
5512
|
+
private emitMemoryReadFailed;
|
|
5170
5513
|
/**
|
|
5171
5514
|
* Read the persisted concierge thread, oldest message first. Returns
|
|
5172
5515
|
* an empty array when no thread exists yet.
|
|
5173
5516
|
*/
|
|
5174
5517
|
getConciergeHistory(): Promise<OperatorChatMessage[]>;
|
|
5518
|
+
/**
|
|
5519
|
+
* Whether the foundation memory store is wired. Routes use this to
|
|
5520
|
+
* 503 cleanly when called against an unwired service.
|
|
5521
|
+
*/
|
|
5522
|
+
hasConciergeMemory(): boolean;
|
|
5523
|
+
/**
|
|
5524
|
+
* List concierge memory threads, newest-first. Emits the
|
|
5525
|
+
* `operator_concierge_history_read` audit event with `thread_id="*"`.
|
|
5526
|
+
*/
|
|
5527
|
+
listConciergeMemoryThreads(opts?: ListThreadsOptions): Promise<ConciergeThreadSummary[]>;
|
|
5528
|
+
/**
|
|
5529
|
+
* Read a concierge memory thread, oldest turn first. Emits the
|
|
5530
|
+
* `operator_concierge_history_read` audit event with the named
|
|
5531
|
+
* thread_id and the count of turns surfaced.
|
|
5532
|
+
*/
|
|
5533
|
+
readConciergeMemoryThread(threadId: string, opts?: ReadThreadOptions): Promise<ConciergeTurn[]>;
|
|
5534
|
+
/**
|
|
5535
|
+
* Delete a concierge memory thread. Emits
|
|
5536
|
+
* `operator_concierge_thread_deleted` only when a bundle was actually
|
|
5537
|
+
* removed; absent threads return false without an audit event.
|
|
5538
|
+
*/
|
|
5539
|
+
deleteConciergeMemoryThread(threadId: string): Promise<boolean>;
|
|
5540
|
+
/**
|
|
5541
|
+
* Reset the active session memory thread. Subsequent sendConcierge
|
|
5542
|
+
* calls allocate a fresh thread_id. Surfaced for tests + future "new
|
|
5543
|
+
* conversation" affordance; not currently called by the dashboard.
|
|
5544
|
+
*/
|
|
5545
|
+
resetConciergeMemoryThread(): void;
|
|
5546
|
+
private ensureActiveMemoryThread;
|
|
5175
5547
|
/**
|
|
5176
5548
|
* Stitch fortress state into a single context blob the substrate
|
|
5177
5549
|
* folds into its summarization prompt.
|
|
@@ -5184,6 +5556,11 @@ declare class OperatorChatService {
|
|
|
5184
5556
|
* ## Sanctuary reference
|
|
5185
5557
|
* <static domain reference block>
|
|
5186
5558
|
*
|
|
5559
|
+
* ## Prior conversation ← WP-V1.3-9 Tau-2, when present
|
|
5560
|
+
* OPERATOR: ...
|
|
5561
|
+
* CONCIERGE: ...
|
|
5562
|
+
* ---
|
|
5563
|
+
*
|
|
5187
5564
|
* ## Recent activity
|
|
5188
5565
|
* <recentActivity output>
|
|
5189
5566
|
*
|
|
@@ -5193,8 +5570,22 @@ declare class OperatorChatService {
|
|
|
5193
5570
|
* ## Open inbox
|
|
5194
5571
|
* <openInbox output>
|
|
5195
5572
|
* ```
|
|
5573
|
+
*
|
|
5574
|
+
* The substrate selector ships a `context: string` shape (not a
|
|
5575
|
+
* messages array), so multi-turn coherence is folded as a structured
|
|
5576
|
+
* prior-conversation section with explicit OPERATOR / CONCIERGE
|
|
5577
|
+
* boundaries. Coordinator-CTO guidance: prefer messages-array shape
|
|
5578
|
+
* if available; the v1.2 selector does not expose one, so structured
|
|
5579
|
+
* serialization is the canonical path for v1.3.
|
|
5196
5580
|
*/
|
|
5197
5581
|
private assembleConciergeContext;
|
|
5582
|
+
/**
|
|
5583
|
+
* Render the prior-conversation section with token-budget enforcement
|
|
5584
|
+
* (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
|
|
5585
|
+
* section exceeds `historyTokenBudget`. Returns an empty string when
|
|
5586
|
+
* the input is empty or when the budget excludes every turn.
|
|
5587
|
+
*/
|
|
5588
|
+
private formatPriorTurnsSection;
|
|
5198
5589
|
private emit;
|
|
5199
5590
|
}
|
|
5200
5591
|
|
|
@@ -5603,6 +5994,15 @@ declare class HubService {
|
|
|
5603
5994
|
* Read the persisted concierge thread.
|
|
5604
5995
|
*/
|
|
5605
5996
|
getConciergeHistory(): Promise<OperatorChatMessage[]>;
|
|
5997
|
+
/**
|
|
5998
|
+
* Whether the operator-chat service has the WP-V1.3-9 memory store
|
|
5999
|
+
* wired. Routes use this to 503 cleanly when the foundation memory
|
|
6000
|
+
* surface is unavailable on a given fortress.
|
|
6001
|
+
*/
|
|
6002
|
+
hasConciergeMemory(): boolean;
|
|
6003
|
+
listConciergeMemoryThreads(opts?: ListThreadsOptions): Promise<ConciergeThreadSummary[]>;
|
|
6004
|
+
readConciergeMemoryThread(threadId: string, opts?: ReadThreadOptions): Promise<ConciergeTurn[]>;
|
|
6005
|
+
deleteConciergeMemoryThread(threadId: string): Promise<boolean>;
|
|
5606
6006
|
/**
|
|
5607
6007
|
* Open the click-to-inspect/approve panel for a wrapped agent. The
|
|
5608
6008
|
* panel surfaces recent activity routed through this agent, pending
|
|
@@ -5669,6 +6069,268 @@ interface V11Bindings {
|
|
|
5669
6069
|
operatorChatService?: OperatorChatService;
|
|
5670
6070
|
}
|
|
5671
6071
|
|
|
6072
|
+
/**
|
|
6073
|
+
* Sanctuary v1.3 WP-V1.3-10 Cross-Harness Approval Inbox Upsilon-1
|
|
6074
|
+
*
|
|
6075
|
+
* The aggregator is a passive subscriber to the existing ApprovalGate
|
|
6076
|
+
* lifecycle. On each `gate-request` event it normalizes the payload into a
|
|
6077
|
+
* stable `AggregatedApproval` record, dedupes against prior emissions of
|
|
6078
|
+
* the same `(source_harness, source_agent_id, audit_log_entry_id)` tuple,
|
|
6079
|
+
* persists the record under the reserved `_approval_aggregator` namespace,
|
|
6080
|
+
* and exposes a query + resolve API.
|
|
6081
|
+
*
|
|
6082
|
+
* Scope of Upsilon-1:
|
|
6083
|
+
* - Aggregator module + storage + resolve API.
|
|
6084
|
+
* - Wired into the gate via an additive callback. The gate's blocking
|
|
6085
|
+
* deny/accept logic is unchanged; the aggregator never fails the gate
|
|
6086
|
+
* (callback exceptions are swallowed and audit-logged).
|
|
6087
|
+
* - HTTP route surface and three audit event names are owned by the
|
|
6088
|
+
* sibling `approval-aggregator-routes.ts` module + the gate wire-up.
|
|
6089
|
+
*
|
|
6090
|
+
* Out of scope (carries to Upsilon-2..4):
|
|
6091
|
+
* - Per-harness wrapped-agent approval-redirect mode.
|
|
6092
|
+
* - Operator decisions through the aggregator's HTTP `approve`/`deny`
|
|
6093
|
+
* routes flowing back to a blocked `channel.requestApproval()` call;
|
|
6094
|
+
* Upsilon-1 records the decision on the aggregator entry only.
|
|
6095
|
+
* - Provenance + replay UX.
|
|
6096
|
+
* - Mobile companion preview hook.
|
|
6097
|
+
*/
|
|
6098
|
+
|
|
6099
|
+
/**
|
|
6100
|
+
* Lifecycle status of an aggregated approval entry.
|
|
6101
|
+
* - `pending`: ingest fired, gate is awaiting a channel decision.
|
|
6102
|
+
* - `approved` / `denied`: gate's channel returned a decision OR the
|
|
6103
|
+
* operator resolved through the HTTP surface.
|
|
6104
|
+
* - `timeout`: the channel reported a timeout (decided_by ===
|
|
6105
|
+
* 'channel_failure' OR explicit timeout reason).
|
|
6106
|
+
* - `expired`: pending past TTL on a `list()` poll without a resolution.
|
|
6107
|
+
*/
|
|
6108
|
+
type AggregatedApprovalStatus = "pending" | "approved" | "denied" | "timeout" | "expired";
|
|
6109
|
+
/**
|
|
6110
|
+
* Normalized record the aggregator stores per approval. Field set is
|
|
6111
|
+
* additive-stable; new fields go behind `?` so existing dashboards keep
|
|
6112
|
+
* rendering unchanged. Raw request payloads are not stored on the record;
|
|
6113
|
+
* only a SHA-256 hash and the original payload pulled separately via
|
|
6114
|
+
* `getFullPayload()`.
|
|
6115
|
+
*/
|
|
6116
|
+
interface AggregatedApproval {
|
|
6117
|
+
/** Stable per-fortress aggregator id (UUID v4). */
|
|
6118
|
+
aggregator_id: string;
|
|
6119
|
+
/** Harness the source approval came from (claude-code, cline, cursor, etc). */
|
|
6120
|
+
source_harness: string;
|
|
6121
|
+
/** Wrapped-agent identifier. Ed25519 pubkey hex or label. */
|
|
6122
|
+
source_agent_id: string;
|
|
6123
|
+
/**
|
|
6124
|
+
* Audit-log entry id this approval correlates to. Used as the
|
|
6125
|
+
* deduplication key alongside source_harness and source_agent_id.
|
|
6126
|
+
* Format: `<iso-timestamp>:<operation>` so two different requests on the
|
|
6127
|
+
* same operation in the same millisecond are still distinguishable.
|
|
6128
|
+
*/
|
|
6129
|
+
audit_log_entry_id: string;
|
|
6130
|
+
/** Policy rule that fired (`tier1_<operation>`, `tier2_<anomaly>`, etc.). */
|
|
6131
|
+
policy_rule_id: string;
|
|
6132
|
+
/** Operator-friendly one-line summary, derived from gate context. */
|
|
6133
|
+
action_summary: string;
|
|
6134
|
+
/** SHA-256 hex of the canonicalized request payload. */
|
|
6135
|
+
request_payload_hash: string;
|
|
6136
|
+
/** Lifecycle status. */
|
|
6137
|
+
status: AggregatedApprovalStatus;
|
|
6138
|
+
/** ISO 8601 timestamp the aggregator first ingested the request. */
|
|
6139
|
+
created_at: string;
|
|
6140
|
+
/** ISO 8601 timestamp the status left `pending`. */
|
|
6141
|
+
resolved_at?: string;
|
|
6142
|
+
/** Operator identity that resolved the entry, if applicable. */
|
|
6143
|
+
resolved_by?: string;
|
|
6144
|
+
/** ISO 8601 deadline. Defaults to created_at + DEFAULT_PENDING_TTL_MS. */
|
|
6145
|
+
expires_at: string;
|
|
6146
|
+
/**
|
|
6147
|
+
* Optional cross-link to a v1.1 hub inbox item id. When set, the
|
|
6148
|
+
* dashboard renderer SHOULD suppress the duplicate hub card so the
|
|
6149
|
+
* operator sees one card per approval. Hub inbox surface is unchanged
|
|
6150
|
+
* (the cross-link signal lives on the aggregator side only).
|
|
6151
|
+
*/
|
|
6152
|
+
hub_inbox_item_id?: string;
|
|
6153
|
+
}
|
|
6154
|
+
/**
|
|
6155
|
+
* Source-context resolver. Called once per ingest to map gate context to
|
|
6156
|
+
* the aggregator's harness + agent_id fields. Default impl reads the
|
|
6157
|
+
* fortress identity (single-agent fortresses) and returns `unknown` when
|
|
6158
|
+
* the gate context does not name an agent.
|
|
6159
|
+
*/
|
|
6160
|
+
interface ApprovalSourceContext {
|
|
6161
|
+
source_harness: string;
|
|
6162
|
+
source_agent_id: string;
|
|
6163
|
+
}
|
|
6164
|
+
/**
|
|
6165
|
+
* Event the gate emits per approval lifecycle transition.
|
|
6166
|
+
*/
|
|
6167
|
+
interface ApprovalGateEvent {
|
|
6168
|
+
/** `requested` on initial gate entry; `resolved` when channel returns. */
|
|
6169
|
+
phase: "requested" | "resolved";
|
|
6170
|
+
/** Operation name from the policy gate. */
|
|
6171
|
+
operation: string;
|
|
6172
|
+
/** Tier the gate decided on. */
|
|
6173
|
+
tier: 1 | 2;
|
|
6174
|
+
/** Human-readable reason from the gate (audit log only; not user-facing). */
|
|
6175
|
+
reason: string;
|
|
6176
|
+
/** Sanitized context map the gate captured. */
|
|
6177
|
+
context: Record<string, unknown>;
|
|
6178
|
+
/** ISO 8601 timestamp the gate entered `requestApproval()`. */
|
|
6179
|
+
request_timestamp: string;
|
|
6180
|
+
/**
|
|
6181
|
+
* On `resolved` only. Channel decision plus decided_by metadata. When
|
|
6182
|
+
* `decision === 'deny'` and `decided_by === 'channel_failure'`, the
|
|
6183
|
+
* aggregator records `status: 'timeout'` (Castle-walking discipline:
|
|
6184
|
+
* fail-closed at the gate flows through to the inbox).
|
|
6185
|
+
*/
|
|
6186
|
+
resolution?: {
|
|
6187
|
+
decision: "approve" | "deny";
|
|
6188
|
+
decided_at: string;
|
|
6189
|
+
decided_by: string;
|
|
6190
|
+
};
|
|
6191
|
+
/**
|
|
6192
|
+
* Stable correlation id the gate emits BOTH on `requested` and on
|
|
6193
|
+
* `resolved`. The aggregator uses this to update the same entry on the
|
|
6194
|
+
* resolution event. Format: `<iso-ms-timestamp>:<operation>:<random4>`.
|
|
6195
|
+
*/
|
|
6196
|
+
correlation_id: string;
|
|
6197
|
+
}
|
|
6198
|
+
/**
|
|
6199
|
+
* Listener subscription returned by `onEvent`. Call to unsubscribe.
|
|
6200
|
+
*/
|
|
6201
|
+
type ApprovalAggregatorUnsubscribe = () => void;
|
|
6202
|
+
/**
|
|
6203
|
+
* Event emitted to subscribers. Mirrors the lifecycle transitions the
|
|
6204
|
+
* aggregator records. SSE listeners surface these to the dashboard.
|
|
6205
|
+
*/
|
|
6206
|
+
interface ApprovalAggregatorEmit {
|
|
6207
|
+
/**
|
|
6208
|
+
* `aggregated` on first ingest, `resolved` on a status leaving pending,
|
|
6209
|
+
* `deduped` when an ingest dropped because the same dedup key was seen.
|
|
6210
|
+
*/
|
|
6211
|
+
type: "aggregated" | "resolved" | "deduped";
|
|
6212
|
+
entry: AggregatedApproval;
|
|
6213
|
+
}
|
|
6214
|
+
/**
|
|
6215
|
+
* Constructor dependencies. `pendingTtlMs` and `maxListLimit` default to
|
|
6216
|
+
* coordinator-CTO defaults; tests pass overrides for deterministic timing.
|
|
6217
|
+
*/
|
|
6218
|
+
interface ApprovalAggregatorDeps {
|
|
6219
|
+
storage: StorageBackend;
|
|
6220
|
+
masterKey: Uint8Array;
|
|
6221
|
+
auditLog: AuditLog;
|
|
6222
|
+
/** Operator identity id. Recorded on the audit entries. */
|
|
6223
|
+
identityId: string;
|
|
6224
|
+
/** Stable fortress id. Drives the source-context default values. */
|
|
6225
|
+
fortressId: string;
|
|
6226
|
+
/** Optional override for the pending TTL. */
|
|
6227
|
+
pendingTtlMs?: number;
|
|
6228
|
+
/** Optional override for the maximum list limit. */
|
|
6229
|
+
maxListLimit?: number;
|
|
6230
|
+
/** Optional clock override for deterministic tests. Defaults to `() => new Date()`. */
|
|
6231
|
+
now?: () => Date;
|
|
6232
|
+
/** Optional source-context resolver. Defaults to fortress identifiers. */
|
|
6233
|
+
resolveSourceContext?: (event: ApprovalGateEvent) => ApprovalSourceContext;
|
|
6234
|
+
/**
|
|
6235
|
+
* Optional cross-link resolver. Returns the v1.1 hub inbox item id that
|
|
6236
|
+
* shadows the same approval, when known. Default returns undefined and
|
|
6237
|
+
* the dashboard renders both surfaces. Upsilon-2 wires this through the
|
|
6238
|
+
* hub inbox store.
|
|
6239
|
+
*/
|
|
6240
|
+
resolveHubInboxItemId?: (event: ApprovalGateEvent) => string | undefined;
|
|
6241
|
+
}
|
|
6242
|
+
/**
|
|
6243
|
+
* Aggregator state. The map is hydrated lazily from the encrypted
|
|
6244
|
+
* namespace on first read; subsequent reads are served from memory.
|
|
6245
|
+
*/
|
|
6246
|
+
declare class ApprovalAggregator {
|
|
6247
|
+
private readonly storage;
|
|
6248
|
+
private readonly encryptionKey;
|
|
6249
|
+
private readonly auditLog;
|
|
6250
|
+
private readonly identityId;
|
|
6251
|
+
private readonly fortressId;
|
|
6252
|
+
private readonly pendingTtlMs;
|
|
6253
|
+
private readonly maxListLimit;
|
|
6254
|
+
private readonly now;
|
|
6255
|
+
private readonly resolveSourceContext;
|
|
6256
|
+
private readonly resolveHubInboxItemId;
|
|
6257
|
+
/** Cached entries by `aggregator_id`. */
|
|
6258
|
+
private readonly entries;
|
|
6259
|
+
/** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
|
|
6260
|
+
private readonly dedupIndex;
|
|
6261
|
+
/** Correlation index: gate `correlation_id` -> aggregator_id. */
|
|
6262
|
+
private readonly correlationIndex;
|
|
6263
|
+
/** Original request payloads kept in-memory for `getFullPayload()`. */
|
|
6264
|
+
private readonly fullPayloads;
|
|
6265
|
+
/** Has the aggregator hydrated persisted entries on this process? */
|
|
6266
|
+
private hydrated;
|
|
6267
|
+
/** Active SSE listeners. */
|
|
6268
|
+
private readonly listeners;
|
|
6269
|
+
constructor(deps: ApprovalAggregatorDeps);
|
|
6270
|
+
/**
|
|
6271
|
+
* Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
|
|
6272
|
+
* use this to forward aggregator emissions to the dashboard.
|
|
6273
|
+
*/
|
|
6274
|
+
onEvent(listener: (event: ApprovalAggregatorEmit) => void): ApprovalAggregatorUnsubscribe;
|
|
6275
|
+
/**
|
|
6276
|
+
* Ingest a gate event. Returns the aggregator entry on first sight,
|
|
6277
|
+
* `null` when deduped. Resolution events update the existing record;
|
|
6278
|
+
* unmatched resolutions are dropped silently (caller's gate emitted a
|
|
6279
|
+
* resolved-without-requested pair, which the aggregator does not invent
|
|
6280
|
+
* a record for).
|
|
6281
|
+
*/
|
|
6282
|
+
ingest(event: ApprovalGateEvent): Promise<AggregatedApproval | null>;
|
|
6283
|
+
/**
|
|
6284
|
+
* List pending or recently resolved entries. Pending entries past TTL
|
|
6285
|
+
* are lazily transitioned to `expired` and persisted before the list
|
|
6286
|
+
* snapshot is returned.
|
|
6287
|
+
*/
|
|
6288
|
+
list(opts?: {
|
|
6289
|
+
status?: AggregatedApprovalStatus;
|
|
6290
|
+
sinceTs?: string;
|
|
6291
|
+
limit?: number;
|
|
6292
|
+
}): Promise<AggregatedApproval[]>;
|
|
6293
|
+
/**
|
|
6294
|
+
* Return the original (unhashed) request payload for the entry. Returns
|
|
6295
|
+
* `null` when the entry is unknown or the payload was evicted (e.g. the
|
|
6296
|
+
* process restarted; payloads are in-memory only at v1.3 Upsilon-1).
|
|
6297
|
+
*/
|
|
6298
|
+
getFullPayload(aggregatorId: string): Promise<unknown>;
|
|
6299
|
+
/**
|
|
6300
|
+
* Resolve an entry. Used by both:
|
|
6301
|
+
* 1. The gate wire-up on channel-decision return.
|
|
6302
|
+
* 2. The HTTP `approve`/`deny` routes when an operator clicks.
|
|
6303
|
+
*
|
|
6304
|
+
* Idempotent: resolving an already-resolved entry is a no-op (the record
|
|
6305
|
+
* keeps its first decision and the audit log is not double-fired).
|
|
6306
|
+
* Unknown ids throw `Error("approval-aggregator: not_found")` so HTTP
|
|
6307
|
+
* routes return 404.
|
|
6308
|
+
*/
|
|
6309
|
+
resolve(aggregatorId: string, decision: "approved" | "denied", operatorId: string): Promise<AggregatedApproval>;
|
|
6310
|
+
private ingestRequested;
|
|
6311
|
+
private ingestResolved;
|
|
6312
|
+
/**
|
|
6313
|
+
* Audit-log entry id for the dedup tuple. The audit log itself does not
|
|
6314
|
+
* surface a stable per-entry id (counter-prefixed keys are internal); the
|
|
6315
|
+
* aggregator uses the request timestamp + operation, which together pin
|
|
6316
|
+
* the audit entry the gate appended on the same call.
|
|
6317
|
+
*/
|
|
6318
|
+
private auditEntryIdForEvent;
|
|
6319
|
+
private derivePolicyRuleId;
|
|
6320
|
+
private deriveActionSummary;
|
|
6321
|
+
/**
|
|
6322
|
+
* Canonical SHA-256 of the request context. Sorted-keys serialization so
|
|
6323
|
+
* identical payloads always hash the same, even when key insertion order
|
|
6324
|
+
* varies. Defends against payload-replay smuggling (the aggregator can
|
|
6325
|
+
* tell the same payload was seen twice without storing it cleartext).
|
|
6326
|
+
*/
|
|
6327
|
+
private hashPayload;
|
|
6328
|
+
private emit;
|
|
6329
|
+
private expireStale;
|
|
6330
|
+
private persist;
|
|
6331
|
+
private hydrate;
|
|
6332
|
+
}
|
|
6333
|
+
|
|
5672
6334
|
/**
|
|
5673
6335
|
* Sanctuary MCP Server — Principal Dashboard
|
|
5674
6336
|
*
|
|
@@ -5758,6 +6420,14 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
5758
6420
|
* regardless. Default route flip is deferred to v1.2.
|
|
5759
6421
|
*/
|
|
5760
6422
|
private v11Bindings;
|
|
6423
|
+
/**
|
|
6424
|
+
* v1.3 WP-V1.3-10 Cross-Harness Approval Inbox aggregator. Mounted
|
|
6425
|
+
* additively at `/api/approval-inbox/*` when set. Legacy approval
|
|
6426
|
+
* routes at `/api/approvals/:id/(allow|deny)` continue to serve. The
|
|
6427
|
+
* aggregator is a passive subscriber to the gate; the routes here are
|
|
6428
|
+
* the operator-facing query / decision surface.
|
|
6429
|
+
*/
|
|
6430
|
+
private approvalAggregator;
|
|
5761
6431
|
constructor(config: DashboardConfig);
|
|
5762
6432
|
/**
|
|
5763
6433
|
* Inject dependencies after construction.
|
|
@@ -5788,6 +6458,18 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
5788
6458
|
* Pass `null` to detach the bindings (used by tests and during shutdown).
|
|
5789
6459
|
*/
|
|
5790
6460
|
setV11Bindings(bindings: V11Bindings | null): void;
|
|
6461
|
+
/**
|
|
6462
|
+
* v1.3 WP-V1.3-10 Upsilon-1: bind the cross-harness approval inbox
|
|
6463
|
+
* aggregator. Once set, requests to `/api/approval-inbox/*` route
|
|
6464
|
+
* through `handleApprovalInboxRoute`. Pass `null` to detach (used by
|
|
6465
|
+
* tests + during shutdown).
|
|
6466
|
+
*/
|
|
6467
|
+
setApprovalAggregator(aggregator: ApprovalAggregator | null): void;
|
|
6468
|
+
/**
|
|
6469
|
+
* v1.3 WP-V1.3-10 dispatch entry point. Called from `handleRequest`
|
|
6470
|
+
* before the legacy approval route table. Returns true when served.
|
|
6471
|
+
*/
|
|
6472
|
+
private dispatchApprovalInbox;
|
|
5791
6473
|
/**
|
|
5792
6474
|
* v1.1 dispatch entry point. Called from `handleRequest` before the
|
|
5793
6475
|
* legacy route table. Returns true when the request was served by v1.1
|
|
@@ -6772,4 +7454,4 @@ declare function createSanctuaryServer(options?: {
|
|
|
6772
7454
|
storage?: StorageBackend;
|
|
6773
7455
|
}): Promise<SanctuaryServer>;
|
|
6774
7456
|
|
|
6775
|
-
export { ATTESTATION_VERSION, type ActivityEntry, type AggregatorSources, ApprovalGate, type ApprovalHandlers, type AttestationBody, type AttestationVerificationResult, AuditLog, AutoApproveChannel, BaselineTracker, type BridgeAttestationRequest, type BridgeAttestationResult, type BridgeCommitment, type BridgeVerificationResult, TEMPLATES as CONTEXT_GATE_TEMPLATES, CallbackApprovalChannel, ClientManager, CommitmentStore, type ConcordiaOutcome, type ConnectionState, type ContextAction, type ContextFilterResult, ContextGateEnforcer, type ContextGatePolicy, ContextGatePolicyStore, type ContextGateRule, type ContextGateTemplate, DashboardApprovalChannel, type DashboardConfig, type DashboardHandle, type DashboardServerOptions, type DetectionResult, type EnforcerConfig, type ExitAuditReceiptsArtifact, type ExitBundleDetailedVerifierResult, ExitBundleImportError, type ExitCommandArgs, type ExitCommitmentsArtifact, type ExitEncryptedStateBundle, type ExitPlaceholderVaultMetadataArtifact, type ExitPolicySetArtifact, type ExitPublicIdentityArtifact, type ExportExitBundleOptions, type ExportExitBundleResult, type FederationCapabilities, type FederationPeer, FederationRegistry, type FieldClassification, type FieldFilterResult, FilesystemStorage, type GateResult, HERO_COPY, type HandshakeChallenge, type HandshakeCompletion, type HandshakeResponse, type HandshakeResult, type ImportExitBundleOptions, type ImportExitBundleResult, InMemoryModelProvenanceStore, InjectionDetector, type InjectionDetectorConfig, type InjectionSignal, type L1Status, type L2Status, type L3Status, type L4Status, type LoadedExitArtifact, MODEL_PRESETS, MemoryStorage, type ModelProvenance, type ModelProvenanceStore, type PedersenCommitment, type PeerTrustEvaluation, type PendingApproval, type PolicyRecommendation, PolicyStore, type PrincipalPolicy, type ProtectionSnapshot, type ProviderCategory, ProxyRouter, type ProxyRouterOptions, type ReputationLookup, ReputationStore, type SHRBody, type SHRGeneratorOptions, type SHRVerificationResult, type SanctuaryConfig, type SanctuaryServer, type SignedAttestation, type SignedSHR, type SovereigntyProfile, SovereigntyProfileStore, type SovereigntyProfileUpdate, type SovereigntyTier, type StartDashboardOptions, StateStore, StderrApprovalChannel, type StreamEvent, TIER_WEIGHTS, type TierMetadata, type TieredAttestation, type UpstreamConnection, type UpstreamServer, type UpstreamTool, type VerifyExitBundleOptions, WebhookApprovalChannel, type WebhookCallbackPayload, type WebhookConfig, type WebhookPayload, type ZKProofOfKnowledge, type ZKRangeProof, canonicalize, classifyField, completeHandshake, computeWeightedScore, createBridgeCommitment, createDefaultProfile, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, evaluateField, exitBundleManifestShape, exportExitBundle, filterContext, generateAttestation, generateSHR, generateSystemPrompt, getProtectionSnapshot, getTemplate, importExitBundle, initiateHandshake, listTemplateIds, loadConfig, loadExitArtifact, loadPrincipalPolicy, readManifest, recommendPolicy, renderDashboardHTML, resolveTier, respondToHandshake, runExitCommand, signPayload, startDashboard, startDashboardServer, tierDistribution, verifyAttestation, verifyBridgeCommitment, verifyCompletion, verifyExitBundle, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };
|
|
7457
|
+
export { ATTESTATION_VERSION, type ActivityEntry, type AggregatorSources, ApprovalGate, type ApprovalHandlers, type AttestationBody, type AttestationVerificationResult, AuditLog, AutoApproveChannel, BaselineTracker, type BridgeAttestationRequest, type BridgeAttestationResult, type BridgeCommitment, type BridgeVerificationResult, TEMPLATES as CONTEXT_GATE_TEMPLATES, CallbackApprovalChannel, ClientManager, CommitmentStore, type ConcordiaOutcome, type ConnectionState, type ContextAction, type ContextFilterResult, ContextGateEnforcer, type ContextGatePolicy, ContextGatePolicyStore, type ContextGateRule, type ContextGateTemplate, DashboardApprovalChannel, type DashboardConfig, type DashboardHandle, type DashboardServerOptions, type DetectionResult, type EnforcerConfig, type ExitAuditReceiptsArtifact, type ExitBundleDetailedVerifierResult, ExitBundleImportError, type ExitCommandArgs, type ExitCommitmentsArtifact, type ExitEncryptedStateBundle, type ExitPlaceholderVaultMetadataArtifact, type ExitPolicySetArtifact, type ExitPublicIdentityArtifact, type ExportExitBundleOptions, type ExportExitBundleResult, type FederationCapabilities, type FederationPeer, FederationRegistry, type FieldClassification, type FieldFilterResult, FilesystemStorage, type GateResult, HERO_COPY, type HandshakeChallenge, type HandshakeCompletion, type HandshakeResponse, type HandshakeResult, type ImportExitBundleOptions, type ImportExitBundleResult, InMemoryModelProvenanceStore, InjectionDetector, type InjectionDetectorConfig, type InjectionSignal, type L1Status, type L2Status, type L3Status, type L4Status, type LoadedExitArtifact, MODEL_PRESETS, MalformedPrincipalPolicyError, MemoryStorage, type ModelProvenance, type ModelProvenanceStore, type PedersenCommitment, type PeerTrustEvaluation, type PendingApproval, type PolicyRecommendation, PolicyStore, type PrincipalPolicy, type ProtectionSnapshot, type ProviderCategory, ProxyRouter, type ProxyRouterOptions, type ReputationLookup, ReputationStore, type SHRBody, type SHRGeneratorOptions, type SHRVerificationResult, type SanctuaryConfig, type SanctuaryServer, type SignedAttestation, type SignedSHR, type SovereigntyProfile, SovereigntyProfileStore, type SovereigntyProfileUpdate, type SovereigntyTier, type StartDashboardOptions, StateStore, StderrApprovalChannel, type StreamEvent, TIER_WEIGHTS, type TierMetadata, type TieredAttestation, type UpstreamConnection, type UpstreamServer, type UpstreamTool, type VerifyExitBundleOptions, WebhookApprovalChannel, type WebhookCallbackPayload, type WebhookConfig, type WebhookPayload, type ZKProofOfKnowledge, type ZKRangeProof, canonicalize, classifyField, completeHandshake, computeWeightedScore, createBridgeCommitment, createDefaultProfile, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, evaluateField, exitBundleManifestShape, exportExitBundle, filterContext, generateAttestation, generateSHR, generateSystemPrompt, getProtectionSnapshot, getTemplate, importExitBundle, initiateHandshake, listTemplateIds, loadConfig, loadExitArtifact, loadPrincipalPolicy, readManifest, recommendPolicy, renderDashboardHTML, resolveTier, respondToHandshake, runExitCommand, signPayload, startDashboard, startDashboardServer, tierDistribution, verifyAttestation, verifyBridgeCommitment, verifyCompletion, verifyExitBundle, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };
|