@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/cli.cjs +763 -30
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +763 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +753 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +312 -10
- package/dist/index.d.ts +312 -10
- package/dist/index.js +753 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4915,7 +4915,8 @@ var SIGNATURE_SCHEME_V1 = "ed25519-v1";
|
|
|
4915
4915
|
var RESERVED_EVENT_TYPE_PREFIXES = [
|
|
4916
4916
|
"EXTENSION_",
|
|
4917
4917
|
"cross_fortress_",
|
|
4918
|
-
"multi_master_"
|
|
4918
|
+
"multi_master_",
|
|
4919
|
+
"cross_harness_approval_"
|
|
4919
4920
|
];
|
|
4920
4921
|
function isReservedEventType(s) {
|
|
4921
4922
|
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
@@ -9095,9 +9096,9 @@ function fingerprintDID(did) {
|
|
|
9095
9096
|
return `${raw.slice(0, 6)}\u2026${raw.slice(-6)}`;
|
|
9096
9097
|
}
|
|
9097
9098
|
function countInjectionsToday(audit) {
|
|
9098
|
-
const
|
|
9099
|
-
|
|
9100
|
-
const cutoff =
|
|
9099
|
+
const startOfDay2 = /* @__PURE__ */ new Date();
|
|
9100
|
+
startOfDay2.setHours(0, 0, 0, 0);
|
|
9101
|
+
const cutoff = startOfDay2.getTime();
|
|
9101
9102
|
return audit.filter((e) => {
|
|
9102
9103
|
const ts = new Date(e.timestamp).getTime();
|
|
9103
9104
|
if (isNaN(ts) || ts < cutoff) return false;
|
|
@@ -9111,9 +9112,9 @@ var PROOF_CREATION_OPS = /* @__PURE__ */ new Set([
|
|
|
9111
9112
|
"proof_commitment"
|
|
9112
9113
|
]);
|
|
9113
9114
|
function countProofsToday(audit) {
|
|
9114
|
-
const
|
|
9115
|
-
|
|
9116
|
-
const cutoff =
|
|
9115
|
+
const startOfDay2 = /* @__PURE__ */ new Date();
|
|
9116
|
+
startOfDay2.setHours(0, 0, 0, 0);
|
|
9117
|
+
const cutoff = startOfDay2.getTime();
|
|
9117
9118
|
return audit.filter((e) => {
|
|
9118
9119
|
if (e.layer !== "l3") return false;
|
|
9119
9120
|
if (!PROOF_CREATION_OPS.has(e.operation)) return false;
|
|
@@ -16312,6 +16313,24 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16312
16313
|
await handleStream2(deps, res);
|
|
16313
16314
|
return true;
|
|
16314
16315
|
}
|
|
16316
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/revision`) {
|
|
16317
|
+
const revision = await deps.aggregator.getRevision();
|
|
16318
|
+
writeJSON4(res, 200, { ok: true, data: { revision } });
|
|
16319
|
+
return true;
|
|
16320
|
+
}
|
|
16321
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/sync`) {
|
|
16322
|
+
const sinceRaw = url.searchParams.get("since_revision");
|
|
16323
|
+
const sinceParsed = sinceRaw === null ? 0 : Number.parseInt(sinceRaw, 10);
|
|
16324
|
+
const sinceRevision = Number.isFinite(sinceParsed) && sinceParsed >= 0 ? sinceParsed : 0;
|
|
16325
|
+
const limit = parseLimit2(
|
|
16326
|
+
url.searchParams.get("limit"),
|
|
16327
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16328
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16329
|
+
);
|
|
16330
|
+
const delta = await deps.aggregator.getSync({ sinceRevision, limit });
|
|
16331
|
+
writeJSON4(res, 200, { ok: true, data: delta });
|
|
16332
|
+
return true;
|
|
16333
|
+
}
|
|
16315
16334
|
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16316
16335
|
const limit = parseLimit2(
|
|
16317
16336
|
url.searchParams.get("limit"),
|
|
@@ -19407,6 +19426,20 @@ var ApprovalAggregator = class {
|
|
|
19407
19426
|
hydrated = false;
|
|
19408
19427
|
/** Active SSE listeners. */
|
|
19409
19428
|
listeners = /* @__PURE__ */ new Set();
|
|
19429
|
+
/**
|
|
19430
|
+
* Monotonic revision counter, bumped on every mutation (ingest of new
|
|
19431
|
+
* entry, resolve, expire, delete). Hydrated from max(last_modified_revision)
|
|
19432
|
+
* across persisted entries on first read; in-memory after that. v1.3
|
|
19433
|
+
* Upsilon-4.
|
|
19434
|
+
*/
|
|
19435
|
+
currentRevision = 0;
|
|
19436
|
+
/**
|
|
19437
|
+
* Removal tombstones: aggregator_id -> revision at removal. Used by the
|
|
19438
|
+
* sync API to surface "removed" entries to mobile consumers between
|
|
19439
|
+
* polls. In-memory only; server restart clears tombstones (mobile
|
|
19440
|
+
* bootstraps via `list()` on reconnect). v1.3 Upsilon-4.
|
|
19441
|
+
*/
|
|
19442
|
+
removedTombstones = /* @__PURE__ */ new Map();
|
|
19410
19443
|
constructor(deps) {
|
|
19411
19444
|
this.storage = deps.storage;
|
|
19412
19445
|
this.encryptionKey = derivePurposeKey(
|
|
@@ -19441,6 +19474,113 @@ var ApprovalAggregator = class {
|
|
|
19441
19474
|
this.listeners.add(listener);
|
|
19442
19475
|
return () => this.listeners.delete(listener);
|
|
19443
19476
|
}
|
|
19477
|
+
/**
|
|
19478
|
+
* Current aggregator revision. v1.3 Upsilon-4. Mobile companions
|
|
19479
|
+
* poll the lightweight `/revision` route to detect that something
|
|
19480
|
+
* changed before fetching a full sync delta.
|
|
19481
|
+
*/
|
|
19482
|
+
async getRevision() {
|
|
19483
|
+
await this.hydrate();
|
|
19484
|
+
return this.currentRevision;
|
|
19485
|
+
}
|
|
19486
|
+
/**
|
|
19487
|
+
* Compute a delta since `sinceRevision`. v1.3 Upsilon-4. Mobile
|
|
19488
|
+
* clients poll this for cheap state-sync. Behavior:
|
|
19489
|
+
* - `added`: entries whose `created_at_revision > sinceRevision`.
|
|
19490
|
+
* - `changed`: entries that existed at `sinceRevision` but had a
|
|
19491
|
+
* status transition (resolve, expire) since.
|
|
19492
|
+
* - `removed`: aggregator_ids deleted after `sinceRevision`.
|
|
19493
|
+
* - `revision`: current aggregator revision; pass this back as
|
|
19494
|
+
* `sinceRevision` on the next call.
|
|
19495
|
+
*
|
|
19496
|
+
* `limit` caps the total count returned across all three lists,
|
|
19497
|
+
* prioritized as added -> changed -> removed (newer-state first).
|
|
19498
|
+
* When more changes exist than fit, the next call with the returned
|
|
19499
|
+
* revision will pick up the rest because each entry's
|
|
19500
|
+
* last_modified_revision is unchanged by truncation.
|
|
19501
|
+
*/
|
|
19502
|
+
async getSync(opts) {
|
|
19503
|
+
await this.hydrate();
|
|
19504
|
+
await this.expireStale();
|
|
19505
|
+
const sinceRevision = opts?.sinceRevision ?? 0;
|
|
19506
|
+
const cap = Math.min(
|
|
19507
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19508
|
+
this.maxListLimit
|
|
19509
|
+
);
|
|
19510
|
+
const added = [];
|
|
19511
|
+
const changed = [];
|
|
19512
|
+
for (const entry of this.entries.values()) {
|
|
19513
|
+
const lastMod = entry.last_modified_revision ?? 0;
|
|
19514
|
+
if (lastMod <= sinceRevision) continue;
|
|
19515
|
+
const createdRev = entry.created_at_revision ?? 0;
|
|
19516
|
+
if (createdRev > sinceRevision) {
|
|
19517
|
+
added.push(entry);
|
|
19518
|
+
} else {
|
|
19519
|
+
changed.push(entry);
|
|
19520
|
+
}
|
|
19521
|
+
}
|
|
19522
|
+
const removed = [];
|
|
19523
|
+
for (const [id, rev] of this.removedTombstones) {
|
|
19524
|
+
if (rev > sinceRevision) removed.push(id);
|
|
19525
|
+
}
|
|
19526
|
+
added.sort(
|
|
19527
|
+
(a, b) => (a.last_modified_revision ?? 0) - (b.last_modified_revision ?? 0)
|
|
19528
|
+
);
|
|
19529
|
+
changed.sort(
|
|
19530
|
+
(a, b) => (a.last_modified_revision ?? 0) - (b.last_modified_revision ?? 0)
|
|
19531
|
+
);
|
|
19532
|
+
let remaining = cap;
|
|
19533
|
+
const addedOut = added.slice(0, Math.max(0, remaining));
|
|
19534
|
+
remaining -= addedOut.length;
|
|
19535
|
+
const changedOut = changed.slice(0, Math.max(0, remaining));
|
|
19536
|
+
remaining -= changedOut.length;
|
|
19537
|
+
const removedOut = removed.slice(0, Math.max(0, remaining));
|
|
19538
|
+
return {
|
|
19539
|
+
revision: this.currentRevision,
|
|
19540
|
+
added: addedOut,
|
|
19541
|
+
changed: changedOut,
|
|
19542
|
+
removed: removedOut
|
|
19543
|
+
};
|
|
19544
|
+
}
|
|
19545
|
+
/**
|
|
19546
|
+
* Delete an entry. Drops the in-memory record, the persisted bundle,
|
|
19547
|
+
* and the at-rest payload (if a payload store is wired). Records a
|
|
19548
|
+
* tombstone with the new revision so sync-API consumers see a
|
|
19549
|
+
* `removed` delta. Returns true when an entry was deleted, false on
|
|
19550
|
+
* unknown id. v1.3 Upsilon-4. Reserved for v1.4+ retention housekeeping;
|
|
19551
|
+
* Upsilon-4 ships the surface so mobile sync-API tests can exercise the
|
|
19552
|
+
* removal path.
|
|
19553
|
+
*/
|
|
19554
|
+
async deleteEntry(aggregatorId) {
|
|
19555
|
+
await this.hydrate();
|
|
19556
|
+
const entry = this.entries.get(aggregatorId);
|
|
19557
|
+
if (!entry) return false;
|
|
19558
|
+
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19559
|
+
this.entries.delete(aggregatorId);
|
|
19560
|
+
this.dedupIndex.delete(dedupKey);
|
|
19561
|
+
this.fullPayloads.delete(aggregatorId);
|
|
19562
|
+
for (const [corr, id] of this.correlationIndex) {
|
|
19563
|
+
if (id === aggregatorId) this.correlationIndex.delete(corr);
|
|
19564
|
+
}
|
|
19565
|
+
try {
|
|
19566
|
+
await this.storage.delete(APPROVAL_AGGREGATOR_NAMESPACE, aggregatorId);
|
|
19567
|
+
} catch {
|
|
19568
|
+
}
|
|
19569
|
+
if (this.payloadStore) {
|
|
19570
|
+
try {
|
|
19571
|
+
await this.payloadStore.deletePayload(aggregatorId);
|
|
19572
|
+
} catch {
|
|
19573
|
+
}
|
|
19574
|
+
}
|
|
19575
|
+
const revision = this.nextRevision();
|
|
19576
|
+
this.removedTombstones.set(aggregatorId, revision);
|
|
19577
|
+
this.emit({ type: "removed", entry: { ...entry } });
|
|
19578
|
+
return true;
|
|
19579
|
+
}
|
|
19580
|
+
nextRevision() {
|
|
19581
|
+
this.currentRevision += 1;
|
|
19582
|
+
return this.currentRevision;
|
|
19583
|
+
}
|
|
19444
19584
|
/**
|
|
19445
19585
|
* Ingest a gate event. Returns the aggregator entry on first sight,
|
|
19446
19586
|
* `null` when deduped. Resolution events update the existing record;
|
|
@@ -19651,6 +19791,7 @@ var ApprovalAggregator = class {
|
|
|
19651
19791
|
entry.status = decision;
|
|
19652
19792
|
entry.resolved_at = this.now().toISOString();
|
|
19653
19793
|
entry.resolved_by = operatorId;
|
|
19794
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19654
19795
|
await this.persist(entry);
|
|
19655
19796
|
this.auditLog.append(
|
|
19656
19797
|
"l2",
|
|
@@ -19702,6 +19843,7 @@ var ApprovalAggregator = class {
|
|
|
19702
19843
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19703
19844
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19704
19845
|
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19846
|
+
const revision = this.nextRevision();
|
|
19705
19847
|
const entry = {
|
|
19706
19848
|
aggregator_id: id,
|
|
19707
19849
|
source_harness: ctx.source_harness,
|
|
@@ -19713,6 +19855,8 @@ var ApprovalAggregator = class {
|
|
|
19713
19855
|
status: "pending",
|
|
19714
19856
|
created_at: now.toISOString(),
|
|
19715
19857
|
expires_at: expires.toISOString(),
|
|
19858
|
+
created_at_revision: revision,
|
|
19859
|
+
last_modified_revision: revision,
|
|
19716
19860
|
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19717
19861
|
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19718
19862
|
};
|
|
@@ -19755,6 +19899,7 @@ var ApprovalAggregator = class {
|
|
|
19755
19899
|
entry.status = status;
|
|
19756
19900
|
entry.resolved_at = event.resolution.decided_at;
|
|
19757
19901
|
entry.resolved_by = event.resolution.decided_by;
|
|
19902
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19758
19903
|
await this.persist(entry);
|
|
19759
19904
|
this.auditLog.append(
|
|
19760
19905
|
"l2",
|
|
@@ -19817,6 +19962,7 @@ var ApprovalAggregator = class {
|
|
|
19817
19962
|
entry.status = "expired";
|
|
19818
19963
|
entry.resolved_at = this.now().toISOString();
|
|
19819
19964
|
entry.resolved_by = "system_ttl";
|
|
19965
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19820
19966
|
await this.persist(entry);
|
|
19821
19967
|
this.auditLog.append(
|
|
19822
19968
|
"l2",
|
|
@@ -19863,6 +20009,10 @@ var ApprovalAggregator = class {
|
|
|
19863
20009
|
this.entries.set(entry.aggregator_id, entry);
|
|
19864
20010
|
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19865
20011
|
this.dedupIndex.set(dedupKey, entry.aggregator_id);
|
|
20012
|
+
const lastMod = entry.last_modified_revision ?? 0;
|
|
20013
|
+
if (lastMod > this.currentRevision) {
|
|
20014
|
+
this.currentRevision = lastMod;
|
|
20015
|
+
}
|
|
19866
20016
|
} catch {
|
|
19867
20017
|
}
|
|
19868
20018
|
}
|
|
@@ -32313,6 +32463,13 @@ init_encoding();
|
|
|
32313
32463
|
// src/chat/operator-chat-audit-events.ts
|
|
32314
32464
|
var OPERATOR_CHAT_OPS = {
|
|
32315
32465
|
CONCIERGE_CHAT: "operator_concierge_chat",
|
|
32466
|
+
/**
|
|
32467
|
+
* Click-to-inspect panel opened on an agent row. Repurposed from the
|
|
32468
|
+
* direct-agent session-open audit event in the v1.2 reshape; the click
|
|
32469
|
+
* affordance now opens an inspect/approve panel (recent activity +
|
|
32470
|
+
* pending approvals + policy summary) instead of a chat session.
|
|
32471
|
+
*/
|
|
32472
|
+
AGENT_INSPECT_PANEL_OPENED: "agent_inspect_panel_opened",
|
|
32316
32473
|
/**
|
|
32317
32474
|
* Operator viewed concierge thread history (WP-V1.3-9 Tau-1). Emitted
|
|
32318
32475
|
* when the operator hits the list-threads or read-thread route. Body
|
|
@@ -32481,9 +32638,10 @@ function isTrivialQuery(query) {
|
|
|
32481
32638
|
if (norm.length < 8) return true;
|
|
32482
32639
|
return TRIVIAL_GREETINGS.has(norm);
|
|
32483
32640
|
}
|
|
32484
|
-
function classifyQuery(query) {
|
|
32641
|
+
function classifyQuery(query, parsedGrammar) {
|
|
32485
32642
|
const normalized = query.toLowerCase();
|
|
32486
32643
|
const matches = [];
|
|
32644
|
+
const grammarAgent = parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null;
|
|
32487
32645
|
for (const spec of CATEGORY_KEYWORDS) {
|
|
32488
32646
|
const matchedPhrases = [];
|
|
32489
32647
|
for (const pattern of spec.patterns) {
|
|
@@ -32495,11 +32653,14 @@ function classifyQuery(query) {
|
|
|
32495
32653
|
}
|
|
32496
32654
|
if (matchedPhrases.length === 0) continue;
|
|
32497
32655
|
const confidence = Math.min(1, 0.4 + 0.3 * matchedPhrases.length);
|
|
32656
|
+
const wantsAgentHint = spec.category === "agent_state" || spec.category === "agent_activity";
|
|
32657
|
+
const agent_name_hint = wantsAgentHint ? grammarAgent ?? extractAgentNameHint(query) : null;
|
|
32498
32658
|
matches.push({
|
|
32499
32659
|
category: spec.category,
|
|
32500
32660
|
confidence,
|
|
32501
32661
|
matched_keywords: matchedPhrases,
|
|
32502
|
-
agent_name_hint
|
|
32662
|
+
agent_name_hint,
|
|
32663
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32503
32664
|
});
|
|
32504
32665
|
}
|
|
32505
32666
|
matches.sort((a, b) => {
|
|
@@ -32508,6 +32669,25 @@ function classifyQuery(query) {
|
|
|
32508
32669
|
});
|
|
32509
32670
|
return matches;
|
|
32510
32671
|
}
|
|
32672
|
+
function fetcherHintsFromGrammar(parsed) {
|
|
32673
|
+
if (!parsed) return void 0;
|
|
32674
|
+
const hasTime = parsed.time_range !== null;
|
|
32675
|
+
const hasAgents = parsed.agent_names.length > 0;
|
|
32676
|
+
const hasEvents = parsed.event_types.length > 0;
|
|
32677
|
+
if (!hasTime && !hasAgents && !hasEvents) return void 0;
|
|
32678
|
+
const hints = {};
|
|
32679
|
+
if (parsed.time_range) {
|
|
32680
|
+
const range = parsed.time_range;
|
|
32681
|
+
hints.time_range = {
|
|
32682
|
+
start: range.start,
|
|
32683
|
+
end: range.end,
|
|
32684
|
+
...range.relative_label !== void 0 ? { relative_label: range.relative_label } : {}
|
|
32685
|
+
};
|
|
32686
|
+
}
|
|
32687
|
+
if (hasAgents) hints.agent_names = parsed.agent_names;
|
|
32688
|
+
if (hasEvents) hints.event_types = parsed.event_types;
|
|
32689
|
+
return hints;
|
|
32690
|
+
}
|
|
32511
32691
|
function approxTokenLen(text) {
|
|
32512
32692
|
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32513
32693
|
}
|
|
@@ -32521,42 +32701,45 @@ var CATEGORY_LABELS = {
|
|
|
32521
32701
|
recent_receipts: "Recent receipts",
|
|
32522
32702
|
verascore_deltas: "Verascore deltas"
|
|
32523
32703
|
};
|
|
32524
|
-
async function runFetcher(match, fetchers) {
|
|
32704
|
+
async function runFetcher(match, fetchers, hints) {
|
|
32525
32705
|
switch (match.category) {
|
|
32526
32706
|
case "templates":
|
|
32527
|
-
return fetchers.templates();
|
|
32707
|
+
return fetchers.templates(hints);
|
|
32528
32708
|
case "agent_state":
|
|
32529
|
-
return fetchers.agent_state(match.agent_name_hint);
|
|
32709
|
+
return fetchers.agent_state(match.agent_name_hint, hints);
|
|
32530
32710
|
case "agent_activity":
|
|
32531
|
-
return fetchers.agent_activity(match.agent_name_hint);
|
|
32711
|
+
return fetchers.agent_activity(match.agent_name_hint, hints);
|
|
32532
32712
|
case "audit_log":
|
|
32533
|
-
return fetchers.audit_log();
|
|
32713
|
+
return fetchers.audit_log(hints);
|
|
32534
32714
|
case "sentinel_findings":
|
|
32535
|
-
return fetchers.sentinel_findings();
|
|
32715
|
+
return fetchers.sentinel_findings(hints);
|
|
32536
32716
|
case "anomaly_alerts":
|
|
32537
|
-
return fetchers.anomaly_alerts();
|
|
32717
|
+
return fetchers.anomaly_alerts(hints);
|
|
32538
32718
|
case "recent_receipts":
|
|
32539
|
-
return fetchers.recent_receipts();
|
|
32719
|
+
return fetchers.recent_receipts(hints);
|
|
32540
32720
|
case "verascore_deltas":
|
|
32541
|
-
return fetchers.verascore_deltas();
|
|
32721
|
+
return fetchers.verascore_deltas(hints);
|
|
32542
32722
|
}
|
|
32543
32723
|
}
|
|
32544
|
-
function trivialMatch(category) {
|
|
32724
|
+
function trivialMatch(category, parsedGrammar) {
|
|
32545
32725
|
return {
|
|
32546
32726
|
category,
|
|
32547
32727
|
confidence: 0.5,
|
|
32548
32728
|
matched_keywords: ["llm-assist"],
|
|
32549
|
-
agent_name_hint: null
|
|
32729
|
+
agent_name_hint: parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null,
|
|
32730
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32550
32731
|
};
|
|
32551
32732
|
}
|
|
32552
32733
|
async function foldContext(query, fetchers, opts) {
|
|
32553
32734
|
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32554
|
-
|
|
32735
|
+
const parsed = opts?.parsed ?? null;
|
|
32736
|
+
const hints = fetcherHintsFromGrammar(parsed);
|
|
32737
|
+
let matches = classifyQuery(query, parsed);
|
|
32555
32738
|
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32556
32739
|
try {
|
|
32557
32740
|
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32558
32741
|
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32559
|
-
matches = [trivialMatch(picked)];
|
|
32742
|
+
matches = [trivialMatch(picked, parsed)];
|
|
32560
32743
|
}
|
|
32561
32744
|
} catch {
|
|
32562
32745
|
}
|
|
@@ -32567,7 +32750,7 @@ async function foldContext(query, fetchers, opts) {
|
|
|
32567
32750
|
const attempts = [];
|
|
32568
32751
|
for (const match of matches) {
|
|
32569
32752
|
try {
|
|
32570
|
-
const text = await runFetcher(match, fetchers);
|
|
32753
|
+
const text = await runFetcher(match, fetchers, hints);
|
|
32571
32754
|
const trimmed = text.trim();
|
|
32572
32755
|
if (trimmed.length > 0) {
|
|
32573
32756
|
attempts.push({ category: match.category, text: trimmed });
|
|
@@ -32609,6 +32792,520 @@ ${blocks.join("\n\n")}`;
|
|
|
32609
32792
|
};
|
|
32610
32793
|
}
|
|
32611
32794
|
|
|
32795
|
+
// src/composition/constants.ts
|
|
32796
|
+
var COMPOSITION_EVENT_TYPES = [
|
|
32797
|
+
"composition_receipt_packed",
|
|
32798
|
+
"composition_receipt_verified",
|
|
32799
|
+
"composition_mandate_verified",
|
|
32800
|
+
"composition_verascore_published",
|
|
32801
|
+
"composition_sidecar_spawned",
|
|
32802
|
+
"composition_sidecar_crashed",
|
|
32803
|
+
"composition_sidecar_recovered",
|
|
32804
|
+
"composition_degraded",
|
|
32805
|
+
"composition_recovered"
|
|
32806
|
+
];
|
|
32807
|
+
|
|
32808
|
+
// src/chat/concierge-query-grammar.ts
|
|
32809
|
+
var CANONICAL_AUDIT_EVENT_CLASSES = [
|
|
32810
|
+
// Lifecycle / policy
|
|
32811
|
+
"policy_change",
|
|
32812
|
+
"approval_request",
|
|
32813
|
+
"audit_truncate",
|
|
32814
|
+
"lockdown",
|
|
32815
|
+
"unwrap",
|
|
32816
|
+
// Exit bundle (Tier 1)
|
|
32817
|
+
"exit_bundle_export",
|
|
32818
|
+
"exit_bundle_import_activate",
|
|
32819
|
+
"exit_bundle_rekey",
|
|
32820
|
+
// Cross-harness approval aggregator
|
|
32821
|
+
"cross_harness_approval_aggregated",
|
|
32822
|
+
"cross_harness_approval_resolved",
|
|
32823
|
+
"cross_harness_approval_deduped",
|
|
32824
|
+
"cross_harness_approval_payload_decrypted",
|
|
32825
|
+
"cross_harness_approval_audit_trail_viewed",
|
|
32826
|
+
"cross_harness_approval_replayed",
|
|
32827
|
+
// Composition (full set from constants.ts)
|
|
32828
|
+
...COMPOSITION_EVENT_TYPES,
|
|
32829
|
+
// Operator chat / concierge (full set from OPERATOR_CHAT_OPS)
|
|
32830
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CHAT,
|
|
32831
|
+
OPERATOR_CHAT_OPS.AGENT_INSPECT_PANEL_OPENED,
|
|
32832
|
+
OPERATOR_CHAT_OPS.CONCIERGE_HISTORY_READ,
|
|
32833
|
+
OPERATOR_CHAT_OPS.CONCIERGE_THREAD_DELETED,
|
|
32834
|
+
OPERATOR_CHAT_OPS.CONCIERGE_MEMORY_READ_FAILED,
|
|
32835
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
32836
|
+
// Bridge / commitment
|
|
32837
|
+
"bridge_commit",
|
|
32838
|
+
"bridge_verify",
|
|
32839
|
+
"bridge_attest",
|
|
32840
|
+
"proof_commitment",
|
|
32841
|
+
"proof_reveal",
|
|
32842
|
+
// Reputation
|
|
32843
|
+
"reputation_export",
|
|
32844
|
+
"reputation_import",
|
|
32845
|
+
"reputation_publish",
|
|
32846
|
+
"reputation_record",
|
|
32847
|
+
"reputation_query"
|
|
32848
|
+
];
|
|
32849
|
+
var EVENT_SYNONYMS = [
|
|
32850
|
+
{ phrase: "approvals", canonical: ["approval_request", "cross_harness_approval_aggregated", "cross_harness_approval_resolved"] },
|
|
32851
|
+
{ phrase: "approval", canonical: ["approval_request"] },
|
|
32852
|
+
{ phrase: "policy changes", canonical: ["policy_change"] },
|
|
32853
|
+
{ phrase: "policy change", canonical: ["policy_change"] },
|
|
32854
|
+
{ phrase: "policy edits", canonical: ["policy_change"] },
|
|
32855
|
+
{ phrase: "lockdowns", canonical: ["lockdown"] },
|
|
32856
|
+
{ phrase: "exit bundles", canonical: ["exit_bundle_export", "exit_bundle_import_activate"] },
|
|
32857
|
+
{ phrase: "exit bundle", canonical: ["exit_bundle_export"] },
|
|
32858
|
+
{ phrase: "audit truncations", canonical: ["audit_truncate"] },
|
|
32859
|
+
{ phrase: "audit truncation", canonical: ["audit_truncate"] },
|
|
32860
|
+
{ phrase: "compositions", canonical: ["composition_receipt_packed", "composition_receipt_verified"] },
|
|
32861
|
+
{ phrase: "receipts", canonical: ["composition_receipt_packed", "composition_receipt_verified"] },
|
|
32862
|
+
{ phrase: "receipt verifications", canonical: ["composition_receipt_verified"] },
|
|
32863
|
+
{ phrase: "concierge chats", canonical: [OPERATOR_CHAT_OPS.CONCIERGE_CHAT] },
|
|
32864
|
+
{ phrase: "cross harness approvals", canonical: ["cross_harness_approval_aggregated", "cross_harness_approval_resolved"] }
|
|
32865
|
+
];
|
|
32866
|
+
var MS_PER_HOUR = 60 * 60 * 1e3;
|
|
32867
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
32868
|
+
var NUMBER_WORDS = {
|
|
32869
|
+
a: 1,
|
|
32870
|
+
an: 1,
|
|
32871
|
+
one: 1,
|
|
32872
|
+
two: 2,
|
|
32873
|
+
three: 3,
|
|
32874
|
+
four: 4,
|
|
32875
|
+
five: 5,
|
|
32876
|
+
six: 6,
|
|
32877
|
+
seven: 7,
|
|
32878
|
+
eight: 8,
|
|
32879
|
+
nine: 9,
|
|
32880
|
+
ten: 10,
|
|
32881
|
+
twelve: 12,
|
|
32882
|
+
twentyfour: 24
|
|
32883
|
+
};
|
|
32884
|
+
function resolveTimeRange(query, now) {
|
|
32885
|
+
const normalized = query.trim();
|
|
32886
|
+
const lower = normalized.toLowerCase();
|
|
32887
|
+
const fromTo = lower.match(
|
|
32888
|
+
/\b(?:from|between)\s+(.+?)\s+(?:to|and|-|until)\s+([\w:.\-+t /]+)/i
|
|
32889
|
+
);
|
|
32890
|
+
if (fromTo) {
|
|
32891
|
+
const aSlice = fromTo[1];
|
|
32892
|
+
const bSlice = fromTo[2];
|
|
32893
|
+
if (aSlice !== void 0 && bSlice !== void 0) {
|
|
32894
|
+
const a = parseInstant(aSlice, now);
|
|
32895
|
+
const b = parseInstant(bSlice, now);
|
|
32896
|
+
if (a && b) {
|
|
32897
|
+
const start = a.getTime() <= b.getTime() ? a : b;
|
|
32898
|
+
const end = a.getTime() <= b.getTime() ? b : a;
|
|
32899
|
+
return {
|
|
32900
|
+
range: { start, end },
|
|
32901
|
+
matchedSubstring: fromTo[0]
|
|
32902
|
+
};
|
|
32903
|
+
}
|
|
32904
|
+
}
|
|
32905
|
+
}
|
|
32906
|
+
const sinceMatch = lower.match(/\bsince\s+([\w:.\-+t /]+)/i);
|
|
32907
|
+
if (sinceMatch) {
|
|
32908
|
+
const slice = sinceMatch[1];
|
|
32909
|
+
if (slice !== void 0) {
|
|
32910
|
+
const start = parseInstant(slice, now);
|
|
32911
|
+
if (start) {
|
|
32912
|
+
return {
|
|
32913
|
+
range: { start, end: now },
|
|
32914
|
+
matchedSubstring: sinceMatch[0]
|
|
32915
|
+
};
|
|
32916
|
+
}
|
|
32917
|
+
}
|
|
32918
|
+
}
|
|
32919
|
+
if (/\byesterday\b/.test(lower)) {
|
|
32920
|
+
const startOfToday = startOfDay(now);
|
|
32921
|
+
const start = new Date(startOfToday.getTime() - MS_PER_DAY);
|
|
32922
|
+
const end = new Date(startOfToday.getTime() - 1);
|
|
32923
|
+
return {
|
|
32924
|
+
range: { start, end, relative_label: "yesterday" },
|
|
32925
|
+
matchedSubstring: "yesterday"
|
|
32926
|
+
};
|
|
32927
|
+
}
|
|
32928
|
+
if (/\btoday\b/.test(lower)) {
|
|
32929
|
+
return {
|
|
32930
|
+
range: {
|
|
32931
|
+
start: startOfDay(now),
|
|
32932
|
+
end: now,
|
|
32933
|
+
relative_label: "today"
|
|
32934
|
+
},
|
|
32935
|
+
matchedSubstring: "today"
|
|
32936
|
+
};
|
|
32937
|
+
}
|
|
32938
|
+
const compactHours = lower.match(/\blast\s+(\d+)\s*h\b/i);
|
|
32939
|
+
if (compactHours) {
|
|
32940
|
+
const tok = compactHours[1];
|
|
32941
|
+
if (tok !== void 0) {
|
|
32942
|
+
const n = Number.parseInt(tok, 10);
|
|
32943
|
+
if (Number.isFinite(n) && n > 0) {
|
|
32944
|
+
const start = new Date(now.getTime() - n * MS_PER_HOUR);
|
|
32945
|
+
return {
|
|
32946
|
+
range: { start, end: now, relative_label: `last ${n}h` },
|
|
32947
|
+
matchedSubstring: compactHours[0]
|
|
32948
|
+
};
|
|
32949
|
+
}
|
|
32950
|
+
}
|
|
32951
|
+
}
|
|
32952
|
+
const hoursMatch = lower.match(
|
|
32953
|
+
/\b(?:past|last)\s+([\w]+|\d+)\s*(?:hr\b|hrs\b|hour|hours)/i
|
|
32954
|
+
);
|
|
32955
|
+
if (hoursMatch) {
|
|
32956
|
+
const tok = hoursMatch[1];
|
|
32957
|
+
if (tok !== void 0) {
|
|
32958
|
+
const n = parseCount(tok);
|
|
32959
|
+
if (n !== null && n > 0) {
|
|
32960
|
+
const start = new Date(now.getTime() - n * MS_PER_HOUR);
|
|
32961
|
+
return {
|
|
32962
|
+
range: { start, end: now, relative_label: `past ${n} hour${n === 1 ? "" : "s"}` },
|
|
32963
|
+
matchedSubstring: hoursMatch[0]
|
|
32964
|
+
};
|
|
32965
|
+
}
|
|
32966
|
+
}
|
|
32967
|
+
}
|
|
32968
|
+
if (/\b(?:past|last)\s+hour\b/.test(lower)) {
|
|
32969
|
+
const start = new Date(now.getTime() - MS_PER_HOUR);
|
|
32970
|
+
return {
|
|
32971
|
+
range: { start, end: now, relative_label: "past hour" },
|
|
32972
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+hour\b/i)[0]
|
|
32973
|
+
};
|
|
32974
|
+
}
|
|
32975
|
+
const daysMatch = lower.match(
|
|
32976
|
+
/\b(?:past|last)\s+([\w]+|\d+)\s*(?:d\b|day|days)/i
|
|
32977
|
+
);
|
|
32978
|
+
if (daysMatch) {
|
|
32979
|
+
const tok = daysMatch[1];
|
|
32980
|
+
if (tok !== void 0) {
|
|
32981
|
+
const n = parseCount(tok);
|
|
32982
|
+
if (n !== null && n > 0) {
|
|
32983
|
+
const start = new Date(now.getTime() - n * MS_PER_DAY);
|
|
32984
|
+
return {
|
|
32985
|
+
range: { start, end: now, relative_label: `past ${n} day${n === 1 ? "" : "s"}` },
|
|
32986
|
+
matchedSubstring: daysMatch[0]
|
|
32987
|
+
};
|
|
32988
|
+
}
|
|
32989
|
+
}
|
|
32990
|
+
}
|
|
32991
|
+
if (/\b(?:past|last)\s+day\b/.test(lower)) {
|
|
32992
|
+
const start = new Date(now.getTime() - MS_PER_DAY);
|
|
32993
|
+
return {
|
|
32994
|
+
range: { start, end: now, relative_label: "past day" },
|
|
32995
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+day\b/i)[0]
|
|
32996
|
+
};
|
|
32997
|
+
}
|
|
32998
|
+
if (/\bthis\s+week\b/.test(lower)) {
|
|
32999
|
+
const start = startOfWeek(now);
|
|
33000
|
+
return {
|
|
33001
|
+
range: { start, end: now, relative_label: "this week" },
|
|
33002
|
+
matchedSubstring: lower.match(/\bthis\s+week\b/i)[0]
|
|
33003
|
+
};
|
|
33004
|
+
}
|
|
33005
|
+
if (/\b(?:past|last)\s+week\b/.test(lower)) {
|
|
33006
|
+
const start = new Date(now.getTime() - 7 * MS_PER_DAY);
|
|
33007
|
+
return {
|
|
33008
|
+
range: { start, end: now, relative_label: "past week" },
|
|
33009
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+week\b/i)[0]
|
|
33010
|
+
};
|
|
33011
|
+
}
|
|
33012
|
+
const isoMatch = normalized.match(
|
|
33013
|
+
/\b(\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2})?(?:Z|[+-]\d{2}:\d{2})?)?)\b/
|
|
33014
|
+
);
|
|
33015
|
+
if (isoMatch) {
|
|
33016
|
+
const tok = isoMatch[1];
|
|
33017
|
+
if (tok !== void 0) {
|
|
33018
|
+
const parsed = parseInstant(tok, now);
|
|
33019
|
+
if (parsed) {
|
|
33020
|
+
const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(tok);
|
|
33021
|
+
if (isDateOnly) {
|
|
33022
|
+
return {
|
|
33023
|
+
range: {
|
|
33024
|
+
start: parsed,
|
|
33025
|
+
end: new Date(parsed.getTime() + MS_PER_DAY - 1)
|
|
33026
|
+
},
|
|
33027
|
+
matchedSubstring: tok
|
|
33028
|
+
};
|
|
33029
|
+
}
|
|
33030
|
+
return {
|
|
33031
|
+
range: {
|
|
33032
|
+
start: new Date(parsed.getTime() - 30 * 60 * 1e3),
|
|
33033
|
+
end: new Date(parsed.getTime() + 30 * 60 * 1e3)
|
|
33034
|
+
},
|
|
33035
|
+
matchedSubstring: tok
|
|
33036
|
+
};
|
|
33037
|
+
}
|
|
33038
|
+
}
|
|
33039
|
+
}
|
|
33040
|
+
return null;
|
|
33041
|
+
}
|
|
33042
|
+
function parseInstant(token, now) {
|
|
33043
|
+
const trimmed = token.trim().replace(/[,.!?;]+$/g, "");
|
|
33044
|
+
if (!trimmed) return null;
|
|
33045
|
+
const lower = trimmed.toLowerCase();
|
|
33046
|
+
if (lower === "now") return now;
|
|
33047
|
+
if (lower === "today") return startOfDay(now);
|
|
33048
|
+
if (lower === "yesterday") {
|
|
33049
|
+
return new Date(startOfDay(now).getTime() - MS_PER_DAY);
|
|
33050
|
+
}
|
|
33051
|
+
const isoLike = trimmed.replace(" ", "T");
|
|
33052
|
+
const parsed = new Date(isoLike);
|
|
33053
|
+
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
33054
|
+
return null;
|
|
33055
|
+
}
|
|
33056
|
+
function parseCount(token) {
|
|
33057
|
+
const lower = token.toLowerCase();
|
|
33058
|
+
if (/^\d+$/.test(lower)) {
|
|
33059
|
+
const n = Number.parseInt(lower, 10);
|
|
33060
|
+
return Number.isFinite(n) ? n : null;
|
|
33061
|
+
}
|
|
33062
|
+
return NUMBER_WORDS[lower] ?? null;
|
|
33063
|
+
}
|
|
33064
|
+
function startOfDay(d) {
|
|
33065
|
+
const out = new Date(d);
|
|
33066
|
+
out.setHours(0, 0, 0, 0);
|
|
33067
|
+
return out;
|
|
33068
|
+
}
|
|
33069
|
+
function startOfWeek(d) {
|
|
33070
|
+
const out = startOfDay(d);
|
|
33071
|
+
const dayOfWeek = out.getDay();
|
|
33072
|
+
const offsetToMonday = (dayOfWeek + 6) % 7;
|
|
33073
|
+
out.setDate(out.getDate() - offsetToMonday);
|
|
33074
|
+
return out;
|
|
33075
|
+
}
|
|
33076
|
+
function listFromRegistry(registry) {
|
|
33077
|
+
if (!registry) return [];
|
|
33078
|
+
if (Array.isArray(registry)) return registry;
|
|
33079
|
+
if (typeof registry.list === "function") {
|
|
33080
|
+
return registry.list();
|
|
33081
|
+
}
|
|
33082
|
+
return [];
|
|
33083
|
+
}
|
|
33084
|
+
function extractAgentNames(query, registry) {
|
|
33085
|
+
const records = listFromRegistry(registry);
|
|
33086
|
+
if (records.length === 0) return { matched: [], flagged: false };
|
|
33087
|
+
const lowerQuery = query.toLowerCase();
|
|
33088
|
+
const compactQuery = lowerQuery.replace(/[\s_-]+/g, "");
|
|
33089
|
+
const matched = [];
|
|
33090
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33091
|
+
for (const rec of records) {
|
|
33092
|
+
const id = rec.agent_id;
|
|
33093
|
+
if (!id || seen.has(id)) continue;
|
|
33094
|
+
const idLower = id.toLowerCase();
|
|
33095
|
+
if (idLower.length < 3) continue;
|
|
33096
|
+
const idCompact = idLower.replace(/[\s_-]+/g, "");
|
|
33097
|
+
const wordRe = new RegExp(
|
|
33098
|
+
`\\b${idLower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
|
|
33099
|
+
"i"
|
|
33100
|
+
);
|
|
33101
|
+
if (wordRe.test(query)) {
|
|
33102
|
+
matched.push(id);
|
|
33103
|
+
seen.add(id);
|
|
33104
|
+
continue;
|
|
33105
|
+
}
|
|
33106
|
+
if (idCompact.length >= 4 && compactQuery.includes(idCompact)) {
|
|
33107
|
+
matched.push(id);
|
|
33108
|
+
seen.add(id);
|
|
33109
|
+
}
|
|
33110
|
+
}
|
|
33111
|
+
const agentMention = lowerQuery.match(/\bagent\s+([a-z0-9_-]{3,40})/i);
|
|
33112
|
+
const flagged = matched.length === 0 && agentMention !== null && agentMention[1] !== void 0 && !records.some((r) => r.agent_id.toLowerCase() === agentMention[1]?.toLowerCase());
|
|
33113
|
+
return { matched, flagged };
|
|
33114
|
+
}
|
|
33115
|
+
function escapeRegex(s) {
|
|
33116
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33117
|
+
}
|
|
33118
|
+
function extractEventTypes(query, enumValues) {
|
|
33119
|
+
const lower = query.toLowerCase();
|
|
33120
|
+
const matched = [];
|
|
33121
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33122
|
+
for (const ev of enumValues) {
|
|
33123
|
+
if (seen.has(ev)) continue;
|
|
33124
|
+
const re = new RegExp(`\\b${escapeRegex(ev)}\\b`, "i");
|
|
33125
|
+
if (re.test(query)) {
|
|
33126
|
+
matched.push(ev);
|
|
33127
|
+
seen.add(ev);
|
|
33128
|
+
}
|
|
33129
|
+
}
|
|
33130
|
+
for (const syn of EVENT_SYNONYMS) {
|
|
33131
|
+
const re = new RegExp(
|
|
33132
|
+
`\\b${syn.phrase.split(/\s+/).map(escapeRegex).join("\\s+")}\\b`,
|
|
33133
|
+
"i"
|
|
33134
|
+
);
|
|
33135
|
+
if (re.test(query)) {
|
|
33136
|
+
for (const c of syn.canonical) {
|
|
33137
|
+
if (seen.has(c)) continue;
|
|
33138
|
+
if (!enumValues.includes(c)) continue;
|
|
33139
|
+
matched.push(c);
|
|
33140
|
+
seen.add(c);
|
|
33141
|
+
}
|
|
33142
|
+
}
|
|
33143
|
+
}
|
|
33144
|
+
const globMatches = lower.match(/\b([a-z_]+)_\*/g) ?? [];
|
|
33145
|
+
for (const glob of globMatches) {
|
|
33146
|
+
const prefix = glob.slice(0, -2);
|
|
33147
|
+
for (const ev of enumValues) {
|
|
33148
|
+
if (seen.has(ev)) continue;
|
|
33149
|
+
if (ev.startsWith(prefix)) {
|
|
33150
|
+
matched.push(ev);
|
|
33151
|
+
seen.add(ev);
|
|
33152
|
+
}
|
|
33153
|
+
}
|
|
33154
|
+
}
|
|
33155
|
+
const eventNounMention = /\b(?:event|events|class|classes)\b/i.test(query) && matched.length === 0;
|
|
33156
|
+
return { matched, flagged: eventNounMention };
|
|
33157
|
+
}
|
|
33158
|
+
function deriveIntentPhrase(query, stripTokens) {
|
|
33159
|
+
let out = query;
|
|
33160
|
+
for (const tok of stripTokens) {
|
|
33161
|
+
if (!tok) continue;
|
|
33162
|
+
const re = new RegExp(escapeRegex(tok), "gi");
|
|
33163
|
+
out = out.replace(re, " ");
|
|
33164
|
+
}
|
|
33165
|
+
return out.replace(/\s+/g, " ").trim();
|
|
33166
|
+
}
|
|
33167
|
+
function computeConfidence(parsed) {
|
|
33168
|
+
const dims = [
|
|
33169
|
+
{ present: parsed.hasTimeMention, resolved: parsed.timeResolved },
|
|
33170
|
+
{ present: parsed.hasAgentMention, resolved: parsed.agentResolved },
|
|
33171
|
+
{ present: parsed.hasEventMention, resolved: parsed.eventResolved }
|
|
33172
|
+
];
|
|
33173
|
+
const present = dims.filter((d) => d.present);
|
|
33174
|
+
let base;
|
|
33175
|
+
if (present.length === 0) {
|
|
33176
|
+
base = parsed.intentEmpty ? 0 : 0.3;
|
|
33177
|
+
} else {
|
|
33178
|
+
const resolved = present.filter((d) => d.resolved).length;
|
|
33179
|
+
base = resolved / present.length;
|
|
33180
|
+
}
|
|
33181
|
+
const adjusted = base - 0.15 * parsed.ambiguityCount;
|
|
33182
|
+
if (adjusted < 0) return 0;
|
|
33183
|
+
if (adjusted > 1) return 1;
|
|
33184
|
+
return adjusted;
|
|
33185
|
+
}
|
|
33186
|
+
var TIME_MENTION_PROBE = /\b(yesterday|today|now|past|last|this\s+week|this\s+month|since|from|between|\d{4}-\d{2}-\d{2})\b/i;
|
|
33187
|
+
var AGENT_MENTION_PROBE = /\bagent[s]?\b/i;
|
|
33188
|
+
var EVENT_MENTION_PROBE = /\b(event|events|class|classes|approvals?|policy)\b/i;
|
|
33189
|
+
function parseQuery(query, opts) {
|
|
33190
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
33191
|
+
const enumValues = opts?.eventClassEnum ?? CANONICAL_AUDIT_EVENT_CLASSES;
|
|
33192
|
+
const original = query ?? "";
|
|
33193
|
+
const trimmed = original.trim();
|
|
33194
|
+
if (trimmed.length === 0) {
|
|
33195
|
+
return {
|
|
33196
|
+
time_range: null,
|
|
33197
|
+
agent_names: [],
|
|
33198
|
+
event_types: [],
|
|
33199
|
+
intent_phrase: "",
|
|
33200
|
+
ambiguity_flags: ["no_signal_extracted"],
|
|
33201
|
+
parse_confidence: 0
|
|
33202
|
+
};
|
|
33203
|
+
}
|
|
33204
|
+
const ambiguity_flags = /* @__PURE__ */ new Set();
|
|
33205
|
+
const timeMatch = resolveTimeRange(trimmed, now);
|
|
33206
|
+
const hasTimeMention = TIME_MENTION_PROBE.test(trimmed);
|
|
33207
|
+
if (hasTimeMention && !timeMatch) {
|
|
33208
|
+
ambiguity_flags.add("unknown_time_token");
|
|
33209
|
+
}
|
|
33210
|
+
const agentResult = extractAgentNames(trimmed, opts?.registry);
|
|
33211
|
+
if (agentResult.flagged) {
|
|
33212
|
+
ambiguity_flags.add("unknown_agent_token");
|
|
33213
|
+
}
|
|
33214
|
+
const hasAgentMention = AGENT_MENTION_PROBE.test(trimmed);
|
|
33215
|
+
const eventResult = extractEventTypes(trimmed, enumValues);
|
|
33216
|
+
const hasEventMention = EVENT_MENTION_PROBE.test(trimmed);
|
|
33217
|
+
if (eventResult.flagged) {
|
|
33218
|
+
ambiguity_flags.add("unknown_event_token");
|
|
33219
|
+
}
|
|
33220
|
+
const stripTokens = [];
|
|
33221
|
+
if (timeMatch) stripTokens.push(timeMatch.matchedSubstring);
|
|
33222
|
+
for (const name of agentResult.matched) stripTokens.push(name);
|
|
33223
|
+
for (const ev of eventResult.matched) {
|
|
33224
|
+
if (trimmed.toLowerCase().includes(ev.toLowerCase())) {
|
|
33225
|
+
stripTokens.push(ev);
|
|
33226
|
+
}
|
|
33227
|
+
}
|
|
33228
|
+
const intent_phrase = deriveIntentPhrase(trimmed, stripTokens);
|
|
33229
|
+
const parse_confidence = computeConfidence({
|
|
33230
|
+
hasTimeMention,
|
|
33231
|
+
timeResolved: timeMatch !== null,
|
|
33232
|
+
hasAgentMention,
|
|
33233
|
+
agentResolved: agentResult.matched.length > 0,
|
|
33234
|
+
hasEventMention,
|
|
33235
|
+
eventResolved: eventResult.matched.length > 0,
|
|
33236
|
+
intentEmpty: intent_phrase.length === 0,
|
|
33237
|
+
ambiguityCount: ambiguity_flags.size
|
|
33238
|
+
});
|
|
33239
|
+
if (timeMatch === null && agentResult.matched.length === 0 && eventResult.matched.length === 0 && intent_phrase.length === 0) {
|
|
33240
|
+
ambiguity_flags.add("no_signal_extracted");
|
|
33241
|
+
}
|
|
33242
|
+
return {
|
|
33243
|
+
time_range: timeMatch ? timeMatch.range : null,
|
|
33244
|
+
agent_names: agentResult.matched,
|
|
33245
|
+
event_types: eventResult.matched,
|
|
33246
|
+
intent_phrase,
|
|
33247
|
+
ambiguity_flags: Array.from(ambiguity_flags),
|
|
33248
|
+
parse_confidence
|
|
33249
|
+
};
|
|
33250
|
+
}
|
|
33251
|
+
var LLM_ASSIST_THRESHOLD = 0.5;
|
|
33252
|
+
function isLowConfidence(parsed) {
|
|
33253
|
+
return parsed.parse_confidence < LLM_ASSIST_THRESHOLD;
|
|
33254
|
+
}
|
|
33255
|
+
async function parseQueryWithLlmAssist(query, llmAssist, opts) {
|
|
33256
|
+
const parsed = parseQuery(query, opts);
|
|
33257
|
+
if (!llmAssist || !isLowConfidence(parsed)) return parsed;
|
|
33258
|
+
let completion;
|
|
33259
|
+
try {
|
|
33260
|
+
completion = await llmAssist(query, parsed);
|
|
33261
|
+
} catch {
|
|
33262
|
+
return parsed;
|
|
33263
|
+
}
|
|
33264
|
+
if (!completion || typeof completion !== "object") return parsed;
|
|
33265
|
+
const merged = { ...parsed };
|
|
33266
|
+
if (parsed.time_range === null && completion.time_range) {
|
|
33267
|
+
merged.time_range = completion.time_range;
|
|
33268
|
+
}
|
|
33269
|
+
if (parsed.agent_names.length === 0 && Array.isArray(completion.agent_names)) {
|
|
33270
|
+
merged.agent_names = completion.agent_names.filter(
|
|
33271
|
+
(s) => typeof s === "string" && s.length > 0
|
|
33272
|
+
);
|
|
33273
|
+
}
|
|
33274
|
+
if (parsed.event_types.length === 0 && Array.isArray(completion.event_types)) {
|
|
33275
|
+
const allowed = new Set(opts?.eventClassEnum ?? CANONICAL_AUDIT_EVENT_CLASSES);
|
|
33276
|
+
merged.event_types = completion.event_types.filter(
|
|
33277
|
+
(s) => typeof s === "string" && allowed.has(s)
|
|
33278
|
+
);
|
|
33279
|
+
}
|
|
33280
|
+
merged.parse_confidence = Math.max(
|
|
33281
|
+
parsed.parse_confidence,
|
|
33282
|
+
computeConfidence({
|
|
33283
|
+
hasTimeMention: TIME_MENTION_PROBE.test(query),
|
|
33284
|
+
timeResolved: merged.time_range !== null,
|
|
33285
|
+
hasAgentMention: AGENT_MENTION_PROBE.test(query),
|
|
33286
|
+
agentResolved: merged.agent_names.length > 0,
|
|
33287
|
+
hasEventMention: EVENT_MENTION_PROBE.test(query),
|
|
33288
|
+
eventResolved: merged.event_types.length > 0,
|
|
33289
|
+
intentEmpty: merged.intent_phrase.length === 0,
|
|
33290
|
+
ambiguityCount: merged.ambiguity_flags.length
|
|
33291
|
+
})
|
|
33292
|
+
);
|
|
33293
|
+
return merged;
|
|
33294
|
+
}
|
|
33295
|
+
function auditSafeSummary(parsed) {
|
|
33296
|
+
return {
|
|
33297
|
+
time_range: parsed.time_range ? {
|
|
33298
|
+
start_iso: parsed.time_range.start.toISOString(),
|
|
33299
|
+
end_iso: parsed.time_range.end.toISOString(),
|
|
33300
|
+
...parsed.time_range.relative_label !== void 0 ? { relative_label: parsed.time_range.relative_label } : {}
|
|
33301
|
+
} : null,
|
|
33302
|
+
agent_names: [...parsed.agent_names],
|
|
33303
|
+
event_types: [...parsed.event_types],
|
|
33304
|
+
ambiguity_flags: [...parsed.ambiguity_flags],
|
|
33305
|
+
parse_confidence: parsed.parse_confidence
|
|
33306
|
+
};
|
|
33307
|
+
}
|
|
33308
|
+
|
|
32612
33309
|
// src/chat/operator-chat-service.ts
|
|
32613
33310
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
32614
33311
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
@@ -32663,6 +33360,8 @@ var OperatorChatService = class {
|
|
|
32663
33360
|
contextFetchers;
|
|
32664
33361
|
contextLlmAssist;
|
|
32665
33362
|
dynamicContextBudget;
|
|
33363
|
+
agentRegistry;
|
|
33364
|
+
grammarLlmAssist;
|
|
32666
33365
|
/**
|
|
32667
33366
|
* In-memory thread_id assigned to the active concierge session.
|
|
32668
33367
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32701,6 +33400,12 @@ var OperatorChatService = class {
|
|
|
32701
33400
|
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
32702
33401
|
}
|
|
32703
33402
|
this.dynamicContextBudget = deps.conciergeDynamicContextBudget !== void 0 && deps.conciergeDynamicContextBudget > 0 ? deps.conciergeDynamicContextBudget : DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET;
|
|
33403
|
+
if (deps.conciergeAgentRegistry) {
|
|
33404
|
+
this.agentRegistry = deps.conciergeAgentRegistry;
|
|
33405
|
+
}
|
|
33406
|
+
if (deps.conciergeGrammarLlmAssist) {
|
|
33407
|
+
this.grammarLlmAssist = deps.conciergeGrammarLlmAssist;
|
|
33408
|
+
}
|
|
32704
33409
|
}
|
|
32705
33410
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32706
33411
|
/**
|
|
@@ -32759,6 +33464,7 @@ var OperatorChatService = class {
|
|
|
32759
33464
|
await this.memory.appendTurn(threadId, "user", trimmed).catch(() => {
|
|
32760
33465
|
});
|
|
32761
33466
|
}
|
|
33467
|
+
const parsedGrammar = await this.runGrammarParse(filterResult.filtered);
|
|
32762
33468
|
const start = Date.now();
|
|
32763
33469
|
let conciergeBody;
|
|
32764
33470
|
let servedBy = "disabled";
|
|
@@ -32777,7 +33483,8 @@ var OperatorChatService = class {
|
|
|
32777
33483
|
outcome = "substrate_disabled";
|
|
32778
33484
|
} else {
|
|
32779
33485
|
const dynamicResult = await this.runDynamicContextFold(
|
|
32780
|
-
filterResult.filtered
|
|
33486
|
+
filterResult.filtered,
|
|
33487
|
+
parsedGrammar
|
|
32781
33488
|
);
|
|
32782
33489
|
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
32783
33490
|
const context = await this.assembleConciergeContext(
|
|
@@ -32848,7 +33555,8 @@ var OperatorChatService = class {
|
|
|
32848
33555
|
...this.memory ? {
|
|
32849
33556
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32850
33557
|
} : {},
|
|
32851
|
-
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {}
|
|
33558
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {},
|
|
33559
|
+
parsed_grammar: auditSafeSummary(parsedGrammar)
|
|
32852
33560
|
};
|
|
32853
33561
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32854
33562
|
return {
|
|
@@ -33063,8 +33771,12 @@ ${inbox}`
|
|
|
33063
33771
|
* proceeds with no fold. Returns the rendered section + the list of
|
|
33064
33772
|
* categories whose data made it into the section (used for the
|
|
33065
33773
|
* round-trip audit emission).
|
|
33774
|
+
*
|
|
33775
|
+
* Tau-4: receives the pre-parsed `ParsedQuery` and forwards it as the
|
|
33776
|
+
* `parsed` opt to `foldContext`, so fetchers see the structured
|
|
33777
|
+
* `FetcherHints` derived from it.
|
|
33066
33778
|
*/
|
|
33067
|
-
async runDynamicContextFold(query) {
|
|
33779
|
+
async runDynamicContextFold(query, parsedGrammar) {
|
|
33068
33780
|
if (!this.contextFetchers) {
|
|
33069
33781
|
return { section: "", categoriesIncluded: [] };
|
|
33070
33782
|
}
|
|
@@ -33073,10 +33785,24 @@ ${inbox}`
|
|
|
33073
33785
|
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33074
33786
|
onFetcherFailure: (category, error) => {
|
|
33075
33787
|
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33076
|
-
}
|
|
33788
|
+
},
|
|
33789
|
+
parsed: parsedGrammar
|
|
33077
33790
|
});
|
|
33078
33791
|
return result;
|
|
33079
33792
|
}
|
|
33793
|
+
/**
|
|
33794
|
+
* WP-V1.3-9 Tau-4: parse the (PII-filtered) operator query into a
|
|
33795
|
+
* `ParsedQuery`. Routes through the LLM-assist completion hook when
|
|
33796
|
+
* configured and the rule-based parse is below
|
|
33797
|
+
* `LLM_ASSIST_THRESHOLD`. Always returns a parse object (never
|
|
33798
|
+
* throws) so the audit emission can carry the result unconditionally.
|
|
33799
|
+
*/
|
|
33800
|
+
async runGrammarParse(query) {
|
|
33801
|
+
return parseQueryWithLlmAssist(query, this.grammarLlmAssist, {
|
|
33802
|
+
...this.agentRegistry !== void 0 ? { registry: this.agentRegistry } : {},
|
|
33803
|
+
eventClassEnum: CANONICAL_AUDIT_EVENT_CLASSES
|
|
33804
|
+
});
|
|
33805
|
+
}
|
|
33080
33806
|
/**
|
|
33081
33807
|
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33082
33808
|
* of the fold path so the dynamic-context handler stays readable.
|