@sanctuary-framework/mcp-server 1.2.6 → 1.2.7

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
@@ -5315,27 +5315,204 @@ declare class ConciergeMemoryStore {
5315
5315
  private withLock;
5316
5316
  }
5317
5317
 
5318
+ /**
5319
+ * Sanctuary MCP Server, Operator Chat, Concierge Query Grammar
5320
+ *
5321
+ * WP-V1.3-9 Tau-4 (criterion (d) of the v1.3 scope-lock revision). Builds
5322
+ * on Tau-3 (PR #171, dynamic context router). The grammar layer parses
5323
+ * operator queries into structured parameters: time ranges, agent names,
5324
+ * event types, and a free-form intent residual. Tau-3 fetchers receive
5325
+ * the parsed hints alongside the query so the substrate sees the most
5326
+ * relevant data slice.
5327
+ *
5328
+ * The module is pure: rule-based extraction, no I/O, no clock side
5329
+ * effects beyond the optional `now` parameter for deterministic tests.
5330
+ * The optional LLM-assist fallback is invoked by the caller when the
5331
+ * rule-based parser produces low confidence.
5332
+ *
5333
+ * Castle-walking discipline:
5334
+ * - No new outbound surface; the LLM-assist fallback routes through the
5335
+ * substrate selector at the same `concierge` surface Tau-3 already
5336
+ * uses.
5337
+ * - Encryption boundaries hold: this module receives plain strings
5338
+ * already produced inside the fortress.
5339
+ * - Failure-mode handling is fail-soft: low-confidence parses surface a
5340
+ * structured fallback menu so the operator sees what the concierge
5341
+ * can answer, never a silent drop.
5342
+ *
5343
+ * Privacy invariant:
5344
+ * The runtime `ParsedQuery` carries `intent_phrase` (free-form residual
5345
+ * pulled from the operator's query) for the router's own consumption.
5346
+ * The audit emission MUST NOT include `intent_phrase`; consumers serialize
5347
+ * via `auditSafeSummary()` which strips it. See `OperatorConciergeChatPayload`
5348
+ * safe-metadata invariant in `contracts/v1.2/operator-chat-events.ts`.
5349
+ *
5350
+ * Out of scope here (Tau-5 + later):
5351
+ * - Agent-context awareness (proactive parsing without an explicit
5352
+ * query). Tau-4 parses off explicit query content only.
5353
+ * - Voice input or natural-language-as-API surface (defer to v1.5+).
5354
+ * - Cross-fortress concierge query (defer to v1.4 cross-tenant scope).
5355
+ * - Window markers like "around the time of <event>" that require an
5356
+ * audit-log lookup to resolve. Tau-4 ships static patterns only.
5357
+ */
5358
+
5359
+ /**
5360
+ * A resolved time range. `relative_label` carries the original token
5361
+ * ("yesterday", "past hour", etc.) when the range was derived from a
5362
+ * relative pattern, so consumers can render operator-readable copy.
5363
+ */
5364
+ interface ParsedTimeRange {
5365
+ start: Date;
5366
+ end: Date;
5367
+ relative_label?: string;
5368
+ }
5369
+ /**
5370
+ * Stable structured-failure flags surfaced when the parser cannot
5371
+ * resolve a token confidently. The strings are enum-like (no free text)
5372
+ * so the audit emission stays safe-metadata-only and the dashboard can
5373
+ * group by cause.
5374
+ */
5375
+ type AmbiguityFlag = "unknown_time_token" | "unknown_agent_token" | "unknown_event_token" | "ambiguous_time_range" | "no_signal_extracted";
5376
+ /**
5377
+ * Runtime parsed-query shape. Used by the router to thread parameters
5378
+ * into context fetchers, and by the structured-fallback menu builder.
5379
+ *
5380
+ * `intent_phrase` is the residual after time/agent/event tokens are
5381
+ * stripped, useful for router-level intent routing. It carries operator
5382
+ * query content and MUST NOT be serialized into the audit log.
5383
+ */
5384
+ interface ParsedQuery {
5385
+ time_range: ParsedTimeRange | null;
5386
+ agent_names: string[];
5387
+ event_types: string[];
5388
+ intent_phrase: string;
5389
+ ambiguity_flags: AmbiguityFlag[];
5390
+ /** 0..1; 1.0 = every component resolved cleanly. */
5391
+ parse_confidence: number;
5392
+ }
5393
+ /**
5394
+ * Read-only registry view consumed by `parseQuery` for agent-name
5395
+ * extraction. The grammar matches operator query tokens against the
5396
+ * `agent_id` field of each record (case-insensitive, prefix-or-exact).
5397
+ *
5398
+ * Wired from `HubAgentRegistrySource.list()` at the service layer; tests
5399
+ * can hand a literal array. Both shapes are accepted so callers do not
5400
+ * have to wrap a hub registry in an adapter just to feed the parser.
5401
+ */
5402
+ type AgentRegistryView = readonly {
5403
+ agent_id: string;
5404
+ }[] | {
5405
+ list: () => readonly {
5406
+ agent_id: string;
5407
+ }[];
5408
+ };
5409
+ /**
5410
+ * Optional LLM-assist completion. Invoked by `parseQueryWithLlmAssist`
5411
+ * when the rule-based parse confidence is below `LLM_ASSIST_THRESHOLD`.
5412
+ * The classifier sees the query plus the partial parse and may return
5413
+ * `null` (no completion) or a partial-shape patch the caller merges.
5414
+ *
5415
+ * The runtime caller (operator-chat-service) MUST route this through
5416
+ * the substrate selector at the `concierge` surface so no new outbound
5417
+ * channel opens. See Castle-walking discipline at the top of the file.
5418
+ */
5419
+ type LlmAssistGrammarCompletion = (query: string, partial: ParsedQuery) => Promise<Partial<{
5420
+ time_range: ParsedTimeRange;
5421
+ agent_names: string[];
5422
+ event_types: string[];
5423
+ }> | null>;
5424
+
5425
+ /**
5426
+ * Sanctuary MCP Server, Operator Chat, Concierge Dynamic Context Router
5427
+ *
5428
+ * WP-V1.3-9 Tau-3 (criterion (c) of the v1.3 scope-lock revision). Builds
5429
+ * on Tau-1 (PR #164, concierge memory store) and Tau-2 (PR #166, multi-turn
5430
+ * coherence read-fold path). At each operator turn the concierge fetches
5431
+ * live data relevant to the operator's query and folds it into context
5432
+ * between the static Sanctuary reference and the prior-turns fold.
5433
+ *
5434
+ * Tau-4 (PR #_, criterion (d)) layers an operator-query grammar on top:
5435
+ * the caller (`operator-chat-service`) parses the query into a
5436
+ * `ParsedQuery` via `concierge-query-grammar`, threads the result into
5437
+ * `classifyQuery` + `foldContext`, and the matches carry the parsed
5438
+ * grammar through to fetchers as `FetcherHints` (time range + agent
5439
+ * names + event types). Existing fetcher implementations that do not
5440
+ * read the second argument continue to work unchanged.
5441
+ *
5442
+ * The module is intentionally pure: keyword classification + a small
5443
+ * amount of hint extraction + a fold orchestrator that calls caller-
5444
+ * provided fetchers. The wiring layer assembles the concrete fetchers
5445
+ * (templates registry, agent registry, audit log, sentinel store, etc.)
5446
+ * so this module stays test-isolatable and composable.
5447
+ *
5448
+ * Castle-walking discipline:
5449
+ * - No new outbound surface; the optional auxiliary classifier is invoked
5450
+ * through the substrate selector by the caller (the service threads the
5451
+ * classifier callback through, this module never owns the channel).
5452
+ * - Encryption boundaries hold for the underlying data sources; this
5453
+ * module receives plain strings already produced inside the fortress.
5454
+ * - Failure-mode handling is fail-soft: a fetcher failure omits its
5455
+ * category from the fold and surfaces an audit event through the
5456
+ * caller's onFetcherFailure hook. The user-facing concierge query is
5457
+ * never broken by a context-assembly failure.
5458
+ *
5459
+ * Out of scope here (Tau-5):
5460
+ * - Agent-context awareness (proactive folding without an explicit
5461
+ * query). The router still routes off explicit query content only.
5462
+ */
5463
+
5318
5464
  /**
5319
5465
  * Closed enum of categories the router may classify a query into. Adding
5320
5466
  * a category requires updating: this enum, the keyword table, the
5321
5467
  * fetcher interface, every wiring fetcher, and the test surface.
5322
5468
  */
5323
5469
  type ContextCategory = "templates" | "agent_state" | "agent_activity" | "audit_log" | "sentinel_findings" | "anomaly_alerts" | "recent_receipts" | "verascore_deltas";
5470
+ /**
5471
+ * Structured hints passed to fetchers as the optional second argument
5472
+ * (Tau-4). Derived from `ParsedQuery` by the router; fetchers are free
5473
+ * to read or ignore individual fields. Tau-3 fetchers that take no
5474
+ * second argument continue to work because the slot is optional.
5475
+ */
5476
+ interface FetcherHints {
5477
+ /** Time range derived from the operator's query, when one resolved. */
5478
+ time_range?: {
5479
+ start: Date;
5480
+ end: Date;
5481
+ relative_label?: string;
5482
+ };
5483
+ /**
5484
+ * All agent names the parser matched against the registry, in the
5485
+ * order they surfaced. Distinct from the `agentNameHint` first arg
5486
+ * (which carries only the first match for back-compat).
5487
+ */
5488
+ agent_names?: readonly string[];
5489
+ /**
5490
+ * Event-type names the parser matched against the canonical audit-
5491
+ * event-class enumeration. Already filtered to the closed set.
5492
+ */
5493
+ event_types?: readonly string[];
5494
+ }
5324
5495
  /**
5325
5496
  * Caller-supplied data sources. Each fetcher returns plain text the
5326
5497
  * router stitches into the rendered section. Returning the empty string
5327
5498
  * (or whitespace only) is treated as "nothing to fold for this category"
5328
5499
  * and the category drops out of the final categoriesIncluded list.
5500
+ *
5501
+ * Tau-4 extends every fetcher with an optional `hints?: FetcherHints`
5502
+ * argument carrying the parsed-query attachments (time range, agent
5503
+ * names, event types). Fetchers that ignore the argument continue to
5504
+ * compile; fetchers that consume it can scope queries against the
5505
+ * audit log, activity feed, etc., to the operator's stated window.
5329
5506
  */
5330
5507
  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>;
5508
+ templates: (hints?: FetcherHints) => Promise<string>;
5509
+ agent_state: (agentNameHint: string | null, hints?: FetcherHints) => Promise<string>;
5510
+ agent_activity: (agentNameHint: string | null, hints?: FetcherHints) => Promise<string>;
5511
+ audit_log: (hints?: FetcherHints) => Promise<string>;
5512
+ sentinel_findings: (hints?: FetcherHints) => Promise<string>;
5513
+ anomaly_alerts: (hints?: FetcherHints) => Promise<string>;
5514
+ recent_receipts: (hints?: FetcherHints) => Promise<string>;
5515
+ verascore_deltas: (hints?: FetcherHints) => Promise<string>;
5339
5516
  }
5340
5517
  /**
5341
5518
  * Auxiliary classifier the caller may supply for the LLM-assist fallback
@@ -5514,6 +5691,26 @@ interface OperatorChatServiceDeps {
5514
5691
  * Defaults to `DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET` (2000).
5515
5692
  */
5516
5693
  conciergeDynamicContextBudget?: number;
5694
+ /**
5695
+ * WP-V1.3-9 Tau-4: read-only registry view consumed by the operator-
5696
+ * query grammar to extract agent-name parameters. Wired from
5697
+ * `HubAgentRegistrySource.list()` at the hub-service layer; absent
5698
+ * services skip agent-name extraction (the grammar still resolves
5699
+ * time ranges and event types). The registry is read at every
5700
+ * `sendConcierge` call so newly-wrapped agents are visible without
5701
+ * service reconstruction.
5702
+ */
5703
+ conciergeAgentRegistry?: AgentRegistryView;
5704
+ /**
5705
+ * WP-V1.3-9 Tau-4: optional LLM-assist completion for low-confidence
5706
+ * grammar parses. When wired alongside the substrate selector, the
5707
+ * service routes the call through the substrate selector at the
5708
+ * `concierge` surface, so the grammar fallback shares the operator's
5709
+ * substrate choice and never opens a new outbound surface. Distinct
5710
+ * from `conciergeContextLlmAssist`, which classifies queries into
5711
+ * fetcher categories; this hook completes structured grammar fields.
5712
+ */
5713
+ conciergeGrammarLlmAssist?: LlmAssistGrammarCompletion;
5517
5714
  }
5518
5715
  declare class OperatorChatService {
5519
5716
  private store;
@@ -5532,6 +5729,8 @@ declare class OperatorChatService {
5532
5729
  private contextFetchers?;
5533
5730
  private contextLlmAssist?;
5534
5731
  private dynamicContextBudget;
5732
+ private agentRegistry?;
5733
+ private grammarLlmAssist?;
5535
5734
  /**
5536
5735
  * In-memory thread_id assigned to the active concierge session.
5537
5736
  * The first sendConcierge call after construction allocates a fresh
@@ -5646,8 +5845,20 @@ declare class OperatorChatService {
5646
5845
  * proceeds with no fold. Returns the rendered section + the list of
5647
5846
  * categories whose data made it into the section (used for the
5648
5847
  * round-trip audit emission).
5848
+ *
5849
+ * Tau-4: receives the pre-parsed `ParsedQuery` and forwards it as the
5850
+ * `parsed` opt to `foldContext`, so fetchers see the structured
5851
+ * `FetcherHints` derived from it.
5649
5852
  */
5650
5853
  private runDynamicContextFold;
5854
+ /**
5855
+ * WP-V1.3-9 Tau-4: parse the (PII-filtered) operator query into a
5856
+ * `ParsedQuery`. Routes through the LLM-assist completion hook when
5857
+ * configured and the rule-based parse is below
5858
+ * `LLM_ASSIST_THRESHOLD`. Always returns a parse object (never
5859
+ * throws) so the audit emission can carry the result unconditionally.
5860
+ */
5861
+ private runGrammarParse;
5651
5862
  /**
5652
5863
  * Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
5653
5864
  * of the fold path so the dynamic-context handler stays readable.
@@ -6329,6 +6540,22 @@ interface AggregatedApproval {
6329
6540
  * Upsilon-3.
6330
6541
  */
6331
6542
  enforcement_chain?: EnforcementLayerEvent[];
6543
+ /**
6544
+ * Aggregator revision number stamped at entry creation. Stable across
6545
+ * status transitions. Used by the v1.4 mobile companion sync API to
6546
+ * separate "added" from "changed" deltas (created_at_revision >
6547
+ * sinceRevision means added; otherwise it is a status change).
6548
+ * v1.3 Upsilon-4.
6549
+ */
6550
+ created_at_revision?: number;
6551
+ /**
6552
+ * Aggregator revision number stamped at the last mutation (creation,
6553
+ * resolution, expiration). Sync-API consumers compare against the
6554
+ * revision they last saw to compute the delta. Monotonically
6555
+ * increasing per fortress; gaps are normal (one bump per mutation).
6556
+ * v1.3 Upsilon-4.
6557
+ */
6558
+ last_modified_revision?: number;
6332
6559
  }
6333
6560
  /**
6334
6561
  * Source-context resolver. Called once per ingest to map gate context to
@@ -6385,11 +6612,35 @@ type ApprovalAggregatorUnsubscribe = () => void;
6385
6612
  interface ApprovalAggregatorEmit {
6386
6613
  /**
6387
6614
  * `aggregated` on first ingest, `resolved` on a status leaving pending,
6388
- * `deduped` when an ingest dropped because the same dedup key was seen.
6615
+ * `deduped` when an ingest dropped because the same dedup key was seen,
6616
+ * `removed` when an entry was pruned (v1.3 Upsilon-4).
6389
6617
  */
6390
- type: "aggregated" | "resolved" | "deduped";
6618
+ type: "aggregated" | "resolved" | "deduped" | "removed";
6391
6619
  entry: AggregatedApproval;
6392
6620
  }
6621
+ /**
6622
+ * Sync-API delta returned by `getSync()`. v1.3 Upsilon-4. Mobile
6623
+ * companions poll this for cheap state-sync without re-fetching the
6624
+ * full inbox. The `revision` field on the response is the aggregator's
6625
+ * current revision; pass it back as `sinceRevision` on the next call.
6626
+ *
6627
+ * `added` carries entries created after `sinceRevision`. `changed`
6628
+ * carries entries that existed at `sinceRevision` but have transitioned
6629
+ * status (resolved, expired) since. `removed` carries the
6630
+ * aggregator_ids of entries pruned after `sinceRevision`.
6631
+ *
6632
+ * Tombstone caveat: removal tombstones live in-process memory. Server
6633
+ * restart clears them. Mobile clients reconnecting after the server
6634
+ * restarted MUST re-bootstrap from `list()` rather than rely on the
6635
+ * sync delta. The v1.4 mobile companion build wires the bootstrap-on-
6636
+ * reconnect flow; v1.3 documents the constraint.
6637
+ */
6638
+ interface ApprovalAggregatorSyncDelta {
6639
+ revision: number;
6640
+ added: AggregatedApproval[];
6641
+ changed: AggregatedApproval[];
6642
+ removed: string[];
6643
+ }
6393
6644
  /**
6394
6645
  * Constructor dependencies. `pendingTtlMs` and `maxListLimit` default to
6395
6646
  * coordinator-CTO defaults; tests pass overrides for deterministic timing.
@@ -6464,12 +6715,63 @@ declare class ApprovalAggregator {
6464
6715
  private hydrated;
6465
6716
  /** Active SSE listeners. */
6466
6717
  private readonly listeners;
6718
+ /**
6719
+ * Monotonic revision counter, bumped on every mutation (ingest of new
6720
+ * entry, resolve, expire, delete). Hydrated from max(last_modified_revision)
6721
+ * across persisted entries on first read; in-memory after that. v1.3
6722
+ * Upsilon-4.
6723
+ */
6724
+ private currentRevision;
6725
+ /**
6726
+ * Removal tombstones: aggregator_id -> revision at removal. Used by the
6727
+ * sync API to surface "removed" entries to mobile consumers between
6728
+ * polls. In-memory only; server restart clears tombstones (mobile
6729
+ * bootstraps via `list()` on reconnect). v1.3 Upsilon-4.
6730
+ */
6731
+ private readonly removedTombstones;
6467
6732
  constructor(deps: ApprovalAggregatorDeps);
6468
6733
  /**
6469
6734
  * Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
6470
6735
  * use this to forward aggregator emissions to the dashboard.
6471
6736
  */
6472
6737
  onEvent(listener: (event: ApprovalAggregatorEmit) => void): ApprovalAggregatorUnsubscribe;
6738
+ /**
6739
+ * Current aggregator revision. v1.3 Upsilon-4. Mobile companions
6740
+ * poll the lightweight `/revision` route to detect that something
6741
+ * changed before fetching a full sync delta.
6742
+ */
6743
+ getRevision(): Promise<number>;
6744
+ /**
6745
+ * Compute a delta since `sinceRevision`. v1.3 Upsilon-4. Mobile
6746
+ * clients poll this for cheap state-sync. Behavior:
6747
+ * - `added`: entries whose `created_at_revision > sinceRevision`.
6748
+ * - `changed`: entries that existed at `sinceRevision` but had a
6749
+ * status transition (resolve, expire) since.
6750
+ * - `removed`: aggregator_ids deleted after `sinceRevision`.
6751
+ * - `revision`: current aggregator revision; pass this back as
6752
+ * `sinceRevision` on the next call.
6753
+ *
6754
+ * `limit` caps the total count returned across all three lists,
6755
+ * prioritized as added -> changed -> removed (newer-state first).
6756
+ * When more changes exist than fit, the next call with the returned
6757
+ * revision will pick up the rest because each entry's
6758
+ * last_modified_revision is unchanged by truncation.
6759
+ */
6760
+ getSync(opts?: {
6761
+ sinceRevision?: number;
6762
+ limit?: number;
6763
+ }): Promise<ApprovalAggregatorSyncDelta>;
6764
+ /**
6765
+ * Delete an entry. Drops the in-memory record, the persisted bundle,
6766
+ * and the at-rest payload (if a payload store is wired). Records a
6767
+ * tombstone with the new revision so sync-API consumers see a
6768
+ * `removed` delta. Returns true when an entry was deleted, false on
6769
+ * unknown id. v1.3 Upsilon-4. Reserved for v1.4+ retention housekeeping;
6770
+ * Upsilon-4 ships the surface so mobile sync-API tests can exercise the
6771
+ * removal path.
6772
+ */
6773
+ deleteEntry(aggregatorId: string): Promise<boolean>;
6774
+ private nextRevision;
6473
6775
  /**
6474
6776
  * Ingest a gate event. Returns the aggregator entry on first sight,
6475
6777
  * `null` when deduped. Resolution events update the existing record;