@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.cjs
CHANGED
|
@@ -4922,7 +4922,8 @@ var SIGNATURE_SCHEME_V1 = "ed25519-v1";
|
|
|
4922
4922
|
var RESERVED_EVENT_TYPE_PREFIXES = [
|
|
4923
4923
|
"EXTENSION_",
|
|
4924
4924
|
"cross_fortress_",
|
|
4925
|
-
"multi_master_"
|
|
4925
|
+
"multi_master_",
|
|
4926
|
+
"cross_harness_approval_"
|
|
4926
4927
|
];
|
|
4927
4928
|
function isReservedEventType(s) {
|
|
4928
4929
|
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
@@ -9102,9 +9103,9 @@ function fingerprintDID(did) {
|
|
|
9102
9103
|
return `${raw.slice(0, 6)}\u2026${raw.slice(-6)}`;
|
|
9103
9104
|
}
|
|
9104
9105
|
function countInjectionsToday(audit) {
|
|
9105
|
-
const
|
|
9106
|
-
|
|
9107
|
-
const cutoff =
|
|
9106
|
+
const startOfDay2 = /* @__PURE__ */ new Date();
|
|
9107
|
+
startOfDay2.setHours(0, 0, 0, 0);
|
|
9108
|
+
const cutoff = startOfDay2.getTime();
|
|
9108
9109
|
return audit.filter((e) => {
|
|
9109
9110
|
const ts = new Date(e.timestamp).getTime();
|
|
9110
9111
|
if (isNaN(ts) || ts < cutoff) return false;
|
|
@@ -9118,9 +9119,9 @@ var PROOF_CREATION_OPS = /* @__PURE__ */ new Set([
|
|
|
9118
9119
|
"proof_commitment"
|
|
9119
9120
|
]);
|
|
9120
9121
|
function countProofsToday(audit) {
|
|
9121
|
-
const
|
|
9122
|
-
|
|
9123
|
-
const cutoff =
|
|
9122
|
+
const startOfDay2 = /* @__PURE__ */ new Date();
|
|
9123
|
+
startOfDay2.setHours(0, 0, 0, 0);
|
|
9124
|
+
const cutoff = startOfDay2.getTime();
|
|
9124
9125
|
return audit.filter((e) => {
|
|
9125
9126
|
if (e.layer !== "l3") return false;
|
|
9126
9127
|
if (!PROOF_CREATION_OPS.has(e.operation)) return false;
|
|
@@ -16319,6 +16320,24 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16319
16320
|
await handleStream2(deps, res);
|
|
16320
16321
|
return true;
|
|
16321
16322
|
}
|
|
16323
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/revision`) {
|
|
16324
|
+
const revision = await deps.aggregator.getRevision();
|
|
16325
|
+
writeJSON4(res, 200, { ok: true, data: { revision } });
|
|
16326
|
+
return true;
|
|
16327
|
+
}
|
|
16328
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/sync`) {
|
|
16329
|
+
const sinceRaw = url.searchParams.get("since_revision");
|
|
16330
|
+
const sinceParsed = sinceRaw === null ? 0 : Number.parseInt(sinceRaw, 10);
|
|
16331
|
+
const sinceRevision = Number.isFinite(sinceParsed) && sinceParsed >= 0 ? sinceParsed : 0;
|
|
16332
|
+
const limit = parseLimit2(
|
|
16333
|
+
url.searchParams.get("limit"),
|
|
16334
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16335
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16336
|
+
);
|
|
16337
|
+
const delta = await deps.aggregator.getSync({ sinceRevision, limit });
|
|
16338
|
+
writeJSON4(res, 200, { ok: true, data: delta });
|
|
16339
|
+
return true;
|
|
16340
|
+
}
|
|
16322
16341
|
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16323
16342
|
const limit = parseLimit2(
|
|
16324
16343
|
url.searchParams.get("limit"),
|
|
@@ -19414,6 +19433,20 @@ var ApprovalAggregator = class {
|
|
|
19414
19433
|
hydrated = false;
|
|
19415
19434
|
/** Active SSE listeners. */
|
|
19416
19435
|
listeners = /* @__PURE__ */ new Set();
|
|
19436
|
+
/**
|
|
19437
|
+
* Monotonic revision counter, bumped on every mutation (ingest of new
|
|
19438
|
+
* entry, resolve, expire, delete). Hydrated from max(last_modified_revision)
|
|
19439
|
+
* across persisted entries on first read; in-memory after that. v1.3
|
|
19440
|
+
* Upsilon-4.
|
|
19441
|
+
*/
|
|
19442
|
+
currentRevision = 0;
|
|
19443
|
+
/**
|
|
19444
|
+
* Removal tombstones: aggregator_id -> revision at removal. Used by the
|
|
19445
|
+
* sync API to surface "removed" entries to mobile consumers between
|
|
19446
|
+
* polls. In-memory only; server restart clears tombstones (mobile
|
|
19447
|
+
* bootstraps via `list()` on reconnect). v1.3 Upsilon-4.
|
|
19448
|
+
*/
|
|
19449
|
+
removedTombstones = /* @__PURE__ */ new Map();
|
|
19417
19450
|
constructor(deps) {
|
|
19418
19451
|
this.storage = deps.storage;
|
|
19419
19452
|
this.encryptionKey = derivePurposeKey(
|
|
@@ -19448,6 +19481,113 @@ var ApprovalAggregator = class {
|
|
|
19448
19481
|
this.listeners.add(listener);
|
|
19449
19482
|
return () => this.listeners.delete(listener);
|
|
19450
19483
|
}
|
|
19484
|
+
/**
|
|
19485
|
+
* Current aggregator revision. v1.3 Upsilon-4. Mobile companions
|
|
19486
|
+
* poll the lightweight `/revision` route to detect that something
|
|
19487
|
+
* changed before fetching a full sync delta.
|
|
19488
|
+
*/
|
|
19489
|
+
async getRevision() {
|
|
19490
|
+
await this.hydrate();
|
|
19491
|
+
return this.currentRevision;
|
|
19492
|
+
}
|
|
19493
|
+
/**
|
|
19494
|
+
* Compute a delta since `sinceRevision`. v1.3 Upsilon-4. Mobile
|
|
19495
|
+
* clients poll this for cheap state-sync. Behavior:
|
|
19496
|
+
* - `added`: entries whose `created_at_revision > sinceRevision`.
|
|
19497
|
+
* - `changed`: entries that existed at `sinceRevision` but had a
|
|
19498
|
+
* status transition (resolve, expire) since.
|
|
19499
|
+
* - `removed`: aggregator_ids deleted after `sinceRevision`.
|
|
19500
|
+
* - `revision`: current aggregator revision; pass this back as
|
|
19501
|
+
* `sinceRevision` on the next call.
|
|
19502
|
+
*
|
|
19503
|
+
* `limit` caps the total count returned across all three lists,
|
|
19504
|
+
* prioritized as added -> changed -> removed (newer-state first).
|
|
19505
|
+
* When more changes exist than fit, the next call with the returned
|
|
19506
|
+
* revision will pick up the rest because each entry's
|
|
19507
|
+
* last_modified_revision is unchanged by truncation.
|
|
19508
|
+
*/
|
|
19509
|
+
async getSync(opts) {
|
|
19510
|
+
await this.hydrate();
|
|
19511
|
+
await this.expireStale();
|
|
19512
|
+
const sinceRevision = opts?.sinceRevision ?? 0;
|
|
19513
|
+
const cap = Math.min(
|
|
19514
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19515
|
+
this.maxListLimit
|
|
19516
|
+
);
|
|
19517
|
+
const added = [];
|
|
19518
|
+
const changed = [];
|
|
19519
|
+
for (const entry of this.entries.values()) {
|
|
19520
|
+
const lastMod = entry.last_modified_revision ?? 0;
|
|
19521
|
+
if (lastMod <= sinceRevision) continue;
|
|
19522
|
+
const createdRev = entry.created_at_revision ?? 0;
|
|
19523
|
+
if (createdRev > sinceRevision) {
|
|
19524
|
+
added.push(entry);
|
|
19525
|
+
} else {
|
|
19526
|
+
changed.push(entry);
|
|
19527
|
+
}
|
|
19528
|
+
}
|
|
19529
|
+
const removed = [];
|
|
19530
|
+
for (const [id, rev] of this.removedTombstones) {
|
|
19531
|
+
if (rev > sinceRevision) removed.push(id);
|
|
19532
|
+
}
|
|
19533
|
+
added.sort(
|
|
19534
|
+
(a, b) => (a.last_modified_revision ?? 0) - (b.last_modified_revision ?? 0)
|
|
19535
|
+
);
|
|
19536
|
+
changed.sort(
|
|
19537
|
+
(a, b) => (a.last_modified_revision ?? 0) - (b.last_modified_revision ?? 0)
|
|
19538
|
+
);
|
|
19539
|
+
let remaining = cap;
|
|
19540
|
+
const addedOut = added.slice(0, Math.max(0, remaining));
|
|
19541
|
+
remaining -= addedOut.length;
|
|
19542
|
+
const changedOut = changed.slice(0, Math.max(0, remaining));
|
|
19543
|
+
remaining -= changedOut.length;
|
|
19544
|
+
const removedOut = removed.slice(0, Math.max(0, remaining));
|
|
19545
|
+
return {
|
|
19546
|
+
revision: this.currentRevision,
|
|
19547
|
+
added: addedOut,
|
|
19548
|
+
changed: changedOut,
|
|
19549
|
+
removed: removedOut
|
|
19550
|
+
};
|
|
19551
|
+
}
|
|
19552
|
+
/**
|
|
19553
|
+
* Delete an entry. Drops the in-memory record, the persisted bundle,
|
|
19554
|
+
* and the at-rest payload (if a payload store is wired). Records a
|
|
19555
|
+
* tombstone with the new revision so sync-API consumers see a
|
|
19556
|
+
* `removed` delta. Returns true when an entry was deleted, false on
|
|
19557
|
+
* unknown id. v1.3 Upsilon-4. Reserved for v1.4+ retention housekeeping;
|
|
19558
|
+
* Upsilon-4 ships the surface so mobile sync-API tests can exercise the
|
|
19559
|
+
* removal path.
|
|
19560
|
+
*/
|
|
19561
|
+
async deleteEntry(aggregatorId) {
|
|
19562
|
+
await this.hydrate();
|
|
19563
|
+
const entry = this.entries.get(aggregatorId);
|
|
19564
|
+
if (!entry) return false;
|
|
19565
|
+
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19566
|
+
this.entries.delete(aggregatorId);
|
|
19567
|
+
this.dedupIndex.delete(dedupKey);
|
|
19568
|
+
this.fullPayloads.delete(aggregatorId);
|
|
19569
|
+
for (const [corr, id] of this.correlationIndex) {
|
|
19570
|
+
if (id === aggregatorId) this.correlationIndex.delete(corr);
|
|
19571
|
+
}
|
|
19572
|
+
try {
|
|
19573
|
+
await this.storage.delete(APPROVAL_AGGREGATOR_NAMESPACE, aggregatorId);
|
|
19574
|
+
} catch {
|
|
19575
|
+
}
|
|
19576
|
+
if (this.payloadStore) {
|
|
19577
|
+
try {
|
|
19578
|
+
await this.payloadStore.deletePayload(aggregatorId);
|
|
19579
|
+
} catch {
|
|
19580
|
+
}
|
|
19581
|
+
}
|
|
19582
|
+
const revision = this.nextRevision();
|
|
19583
|
+
this.removedTombstones.set(aggregatorId, revision);
|
|
19584
|
+
this.emit({ type: "removed", entry: { ...entry } });
|
|
19585
|
+
return true;
|
|
19586
|
+
}
|
|
19587
|
+
nextRevision() {
|
|
19588
|
+
this.currentRevision += 1;
|
|
19589
|
+
return this.currentRevision;
|
|
19590
|
+
}
|
|
19451
19591
|
/**
|
|
19452
19592
|
* Ingest a gate event. Returns the aggregator entry on first sight,
|
|
19453
19593
|
* `null` when deduped. Resolution events update the existing record;
|
|
@@ -19658,6 +19798,7 @@ var ApprovalAggregator = class {
|
|
|
19658
19798
|
entry.status = decision;
|
|
19659
19799
|
entry.resolved_at = this.now().toISOString();
|
|
19660
19800
|
entry.resolved_by = operatorId;
|
|
19801
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19661
19802
|
await this.persist(entry);
|
|
19662
19803
|
this.auditLog.append(
|
|
19663
19804
|
"l2",
|
|
@@ -19709,6 +19850,7 @@ var ApprovalAggregator = class {
|
|
|
19709
19850
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19710
19851
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19711
19852
|
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19853
|
+
const revision = this.nextRevision();
|
|
19712
19854
|
const entry = {
|
|
19713
19855
|
aggregator_id: id,
|
|
19714
19856
|
source_harness: ctx.source_harness,
|
|
@@ -19720,6 +19862,8 @@ var ApprovalAggregator = class {
|
|
|
19720
19862
|
status: "pending",
|
|
19721
19863
|
created_at: now.toISOString(),
|
|
19722
19864
|
expires_at: expires.toISOString(),
|
|
19865
|
+
created_at_revision: revision,
|
|
19866
|
+
last_modified_revision: revision,
|
|
19723
19867
|
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19724
19868
|
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19725
19869
|
};
|
|
@@ -19762,6 +19906,7 @@ var ApprovalAggregator = class {
|
|
|
19762
19906
|
entry.status = status;
|
|
19763
19907
|
entry.resolved_at = event.resolution.decided_at;
|
|
19764
19908
|
entry.resolved_by = event.resolution.decided_by;
|
|
19909
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19765
19910
|
await this.persist(entry);
|
|
19766
19911
|
this.auditLog.append(
|
|
19767
19912
|
"l2",
|
|
@@ -19824,6 +19969,7 @@ var ApprovalAggregator = class {
|
|
|
19824
19969
|
entry.status = "expired";
|
|
19825
19970
|
entry.resolved_at = this.now().toISOString();
|
|
19826
19971
|
entry.resolved_by = "system_ttl";
|
|
19972
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19827
19973
|
await this.persist(entry);
|
|
19828
19974
|
this.auditLog.append(
|
|
19829
19975
|
"l2",
|
|
@@ -19870,6 +20016,10 @@ var ApprovalAggregator = class {
|
|
|
19870
20016
|
this.entries.set(entry.aggregator_id, entry);
|
|
19871
20017
|
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19872
20018
|
this.dedupIndex.set(dedupKey, entry.aggregator_id);
|
|
20019
|
+
const lastMod = entry.last_modified_revision ?? 0;
|
|
20020
|
+
if (lastMod > this.currentRevision) {
|
|
20021
|
+
this.currentRevision = lastMod;
|
|
20022
|
+
}
|
|
19873
20023
|
} catch {
|
|
19874
20024
|
}
|
|
19875
20025
|
}
|
|
@@ -32320,6 +32470,13 @@ init_encoding();
|
|
|
32320
32470
|
// src/chat/operator-chat-audit-events.ts
|
|
32321
32471
|
var OPERATOR_CHAT_OPS = {
|
|
32322
32472
|
CONCIERGE_CHAT: "operator_concierge_chat",
|
|
32473
|
+
/**
|
|
32474
|
+
* Click-to-inspect panel opened on an agent row. Repurposed from the
|
|
32475
|
+
* direct-agent session-open audit event in the v1.2 reshape; the click
|
|
32476
|
+
* affordance now opens an inspect/approve panel (recent activity +
|
|
32477
|
+
* pending approvals + policy summary) instead of a chat session.
|
|
32478
|
+
*/
|
|
32479
|
+
AGENT_INSPECT_PANEL_OPENED: "agent_inspect_panel_opened",
|
|
32323
32480
|
/**
|
|
32324
32481
|
* Operator viewed concierge thread history (WP-V1.3-9 Tau-1). Emitted
|
|
32325
32482
|
* when the operator hits the list-threads or read-thread route. Body
|
|
@@ -32488,9 +32645,10 @@ function isTrivialQuery(query) {
|
|
|
32488
32645
|
if (norm.length < 8) return true;
|
|
32489
32646
|
return TRIVIAL_GREETINGS.has(norm);
|
|
32490
32647
|
}
|
|
32491
|
-
function classifyQuery(query) {
|
|
32648
|
+
function classifyQuery(query, parsedGrammar) {
|
|
32492
32649
|
const normalized = query.toLowerCase();
|
|
32493
32650
|
const matches = [];
|
|
32651
|
+
const grammarAgent = parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null;
|
|
32494
32652
|
for (const spec of CATEGORY_KEYWORDS) {
|
|
32495
32653
|
const matchedPhrases = [];
|
|
32496
32654
|
for (const pattern of spec.patterns) {
|
|
@@ -32502,11 +32660,14 @@ function classifyQuery(query) {
|
|
|
32502
32660
|
}
|
|
32503
32661
|
if (matchedPhrases.length === 0) continue;
|
|
32504
32662
|
const confidence = Math.min(1, 0.4 + 0.3 * matchedPhrases.length);
|
|
32663
|
+
const wantsAgentHint = spec.category === "agent_state" || spec.category === "agent_activity";
|
|
32664
|
+
const agent_name_hint = wantsAgentHint ? grammarAgent ?? extractAgentNameHint(query) : null;
|
|
32505
32665
|
matches.push({
|
|
32506
32666
|
category: spec.category,
|
|
32507
32667
|
confidence,
|
|
32508
32668
|
matched_keywords: matchedPhrases,
|
|
32509
|
-
agent_name_hint
|
|
32669
|
+
agent_name_hint,
|
|
32670
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32510
32671
|
});
|
|
32511
32672
|
}
|
|
32512
32673
|
matches.sort((a, b) => {
|
|
@@ -32515,6 +32676,25 @@ function classifyQuery(query) {
|
|
|
32515
32676
|
});
|
|
32516
32677
|
return matches;
|
|
32517
32678
|
}
|
|
32679
|
+
function fetcherHintsFromGrammar(parsed) {
|
|
32680
|
+
if (!parsed) return void 0;
|
|
32681
|
+
const hasTime = parsed.time_range !== null;
|
|
32682
|
+
const hasAgents = parsed.agent_names.length > 0;
|
|
32683
|
+
const hasEvents = parsed.event_types.length > 0;
|
|
32684
|
+
if (!hasTime && !hasAgents && !hasEvents) return void 0;
|
|
32685
|
+
const hints = {};
|
|
32686
|
+
if (parsed.time_range) {
|
|
32687
|
+
const range = parsed.time_range;
|
|
32688
|
+
hints.time_range = {
|
|
32689
|
+
start: range.start,
|
|
32690
|
+
end: range.end,
|
|
32691
|
+
...range.relative_label !== void 0 ? { relative_label: range.relative_label } : {}
|
|
32692
|
+
};
|
|
32693
|
+
}
|
|
32694
|
+
if (hasAgents) hints.agent_names = parsed.agent_names;
|
|
32695
|
+
if (hasEvents) hints.event_types = parsed.event_types;
|
|
32696
|
+
return hints;
|
|
32697
|
+
}
|
|
32518
32698
|
function approxTokenLen(text) {
|
|
32519
32699
|
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32520
32700
|
}
|
|
@@ -32528,42 +32708,45 @@ var CATEGORY_LABELS = {
|
|
|
32528
32708
|
recent_receipts: "Recent receipts",
|
|
32529
32709
|
verascore_deltas: "Verascore deltas"
|
|
32530
32710
|
};
|
|
32531
|
-
async function runFetcher(match, fetchers) {
|
|
32711
|
+
async function runFetcher(match, fetchers, hints) {
|
|
32532
32712
|
switch (match.category) {
|
|
32533
32713
|
case "templates":
|
|
32534
|
-
return fetchers.templates();
|
|
32714
|
+
return fetchers.templates(hints);
|
|
32535
32715
|
case "agent_state":
|
|
32536
|
-
return fetchers.agent_state(match.agent_name_hint);
|
|
32716
|
+
return fetchers.agent_state(match.agent_name_hint, hints);
|
|
32537
32717
|
case "agent_activity":
|
|
32538
|
-
return fetchers.agent_activity(match.agent_name_hint);
|
|
32718
|
+
return fetchers.agent_activity(match.agent_name_hint, hints);
|
|
32539
32719
|
case "audit_log":
|
|
32540
|
-
return fetchers.audit_log();
|
|
32720
|
+
return fetchers.audit_log(hints);
|
|
32541
32721
|
case "sentinel_findings":
|
|
32542
|
-
return fetchers.sentinel_findings();
|
|
32722
|
+
return fetchers.sentinel_findings(hints);
|
|
32543
32723
|
case "anomaly_alerts":
|
|
32544
|
-
return fetchers.anomaly_alerts();
|
|
32724
|
+
return fetchers.anomaly_alerts(hints);
|
|
32545
32725
|
case "recent_receipts":
|
|
32546
|
-
return fetchers.recent_receipts();
|
|
32726
|
+
return fetchers.recent_receipts(hints);
|
|
32547
32727
|
case "verascore_deltas":
|
|
32548
|
-
return fetchers.verascore_deltas();
|
|
32728
|
+
return fetchers.verascore_deltas(hints);
|
|
32549
32729
|
}
|
|
32550
32730
|
}
|
|
32551
|
-
function trivialMatch(category) {
|
|
32731
|
+
function trivialMatch(category, parsedGrammar) {
|
|
32552
32732
|
return {
|
|
32553
32733
|
category,
|
|
32554
32734
|
confidence: 0.5,
|
|
32555
32735
|
matched_keywords: ["llm-assist"],
|
|
32556
|
-
agent_name_hint: null
|
|
32736
|
+
agent_name_hint: parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null,
|
|
32737
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32557
32738
|
};
|
|
32558
32739
|
}
|
|
32559
32740
|
async function foldContext(query, fetchers, opts) {
|
|
32560
32741
|
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32561
|
-
|
|
32742
|
+
const parsed = opts?.parsed ?? null;
|
|
32743
|
+
const hints = fetcherHintsFromGrammar(parsed);
|
|
32744
|
+
let matches = classifyQuery(query, parsed);
|
|
32562
32745
|
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32563
32746
|
try {
|
|
32564
32747
|
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32565
32748
|
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32566
|
-
matches = [trivialMatch(picked)];
|
|
32749
|
+
matches = [trivialMatch(picked, parsed)];
|
|
32567
32750
|
}
|
|
32568
32751
|
} catch {
|
|
32569
32752
|
}
|
|
@@ -32574,7 +32757,7 @@ async function foldContext(query, fetchers, opts) {
|
|
|
32574
32757
|
const attempts = [];
|
|
32575
32758
|
for (const match of matches) {
|
|
32576
32759
|
try {
|
|
32577
|
-
const text = await runFetcher(match, fetchers);
|
|
32760
|
+
const text = await runFetcher(match, fetchers, hints);
|
|
32578
32761
|
const trimmed = text.trim();
|
|
32579
32762
|
if (trimmed.length > 0) {
|
|
32580
32763
|
attempts.push({ category: match.category, text: trimmed });
|
|
@@ -32616,6 +32799,520 @@ ${blocks.join("\n\n")}`;
|
|
|
32616
32799
|
};
|
|
32617
32800
|
}
|
|
32618
32801
|
|
|
32802
|
+
// src/composition/constants.ts
|
|
32803
|
+
var COMPOSITION_EVENT_TYPES = [
|
|
32804
|
+
"composition_receipt_packed",
|
|
32805
|
+
"composition_receipt_verified",
|
|
32806
|
+
"composition_mandate_verified",
|
|
32807
|
+
"composition_verascore_published",
|
|
32808
|
+
"composition_sidecar_spawned",
|
|
32809
|
+
"composition_sidecar_crashed",
|
|
32810
|
+
"composition_sidecar_recovered",
|
|
32811
|
+
"composition_degraded",
|
|
32812
|
+
"composition_recovered"
|
|
32813
|
+
];
|
|
32814
|
+
|
|
32815
|
+
// src/chat/concierge-query-grammar.ts
|
|
32816
|
+
var CANONICAL_AUDIT_EVENT_CLASSES = [
|
|
32817
|
+
// Lifecycle / policy
|
|
32818
|
+
"policy_change",
|
|
32819
|
+
"approval_request",
|
|
32820
|
+
"audit_truncate",
|
|
32821
|
+
"lockdown",
|
|
32822
|
+
"unwrap",
|
|
32823
|
+
// Exit bundle (Tier 1)
|
|
32824
|
+
"exit_bundle_export",
|
|
32825
|
+
"exit_bundle_import_activate",
|
|
32826
|
+
"exit_bundle_rekey",
|
|
32827
|
+
// Cross-harness approval aggregator
|
|
32828
|
+
"cross_harness_approval_aggregated",
|
|
32829
|
+
"cross_harness_approval_resolved",
|
|
32830
|
+
"cross_harness_approval_deduped",
|
|
32831
|
+
"cross_harness_approval_payload_decrypted",
|
|
32832
|
+
"cross_harness_approval_audit_trail_viewed",
|
|
32833
|
+
"cross_harness_approval_replayed",
|
|
32834
|
+
// Composition (full set from constants.ts)
|
|
32835
|
+
...COMPOSITION_EVENT_TYPES,
|
|
32836
|
+
// Operator chat / concierge (full set from OPERATOR_CHAT_OPS)
|
|
32837
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CHAT,
|
|
32838
|
+
OPERATOR_CHAT_OPS.AGENT_INSPECT_PANEL_OPENED,
|
|
32839
|
+
OPERATOR_CHAT_OPS.CONCIERGE_HISTORY_READ,
|
|
32840
|
+
OPERATOR_CHAT_OPS.CONCIERGE_THREAD_DELETED,
|
|
32841
|
+
OPERATOR_CHAT_OPS.CONCIERGE_MEMORY_READ_FAILED,
|
|
32842
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
32843
|
+
// Bridge / commitment
|
|
32844
|
+
"bridge_commit",
|
|
32845
|
+
"bridge_verify",
|
|
32846
|
+
"bridge_attest",
|
|
32847
|
+
"proof_commitment",
|
|
32848
|
+
"proof_reveal",
|
|
32849
|
+
// Reputation
|
|
32850
|
+
"reputation_export",
|
|
32851
|
+
"reputation_import",
|
|
32852
|
+
"reputation_publish",
|
|
32853
|
+
"reputation_record",
|
|
32854
|
+
"reputation_query"
|
|
32855
|
+
];
|
|
32856
|
+
var EVENT_SYNONYMS = [
|
|
32857
|
+
{ phrase: "approvals", canonical: ["approval_request", "cross_harness_approval_aggregated", "cross_harness_approval_resolved"] },
|
|
32858
|
+
{ phrase: "approval", canonical: ["approval_request"] },
|
|
32859
|
+
{ phrase: "policy changes", canonical: ["policy_change"] },
|
|
32860
|
+
{ phrase: "policy change", canonical: ["policy_change"] },
|
|
32861
|
+
{ phrase: "policy edits", canonical: ["policy_change"] },
|
|
32862
|
+
{ phrase: "lockdowns", canonical: ["lockdown"] },
|
|
32863
|
+
{ phrase: "exit bundles", canonical: ["exit_bundle_export", "exit_bundle_import_activate"] },
|
|
32864
|
+
{ phrase: "exit bundle", canonical: ["exit_bundle_export"] },
|
|
32865
|
+
{ phrase: "audit truncations", canonical: ["audit_truncate"] },
|
|
32866
|
+
{ phrase: "audit truncation", canonical: ["audit_truncate"] },
|
|
32867
|
+
{ phrase: "compositions", canonical: ["composition_receipt_packed", "composition_receipt_verified"] },
|
|
32868
|
+
{ phrase: "receipts", canonical: ["composition_receipt_packed", "composition_receipt_verified"] },
|
|
32869
|
+
{ phrase: "receipt verifications", canonical: ["composition_receipt_verified"] },
|
|
32870
|
+
{ phrase: "concierge chats", canonical: [OPERATOR_CHAT_OPS.CONCIERGE_CHAT] },
|
|
32871
|
+
{ phrase: "cross harness approvals", canonical: ["cross_harness_approval_aggregated", "cross_harness_approval_resolved"] }
|
|
32872
|
+
];
|
|
32873
|
+
var MS_PER_HOUR = 60 * 60 * 1e3;
|
|
32874
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
32875
|
+
var NUMBER_WORDS = {
|
|
32876
|
+
a: 1,
|
|
32877
|
+
an: 1,
|
|
32878
|
+
one: 1,
|
|
32879
|
+
two: 2,
|
|
32880
|
+
three: 3,
|
|
32881
|
+
four: 4,
|
|
32882
|
+
five: 5,
|
|
32883
|
+
six: 6,
|
|
32884
|
+
seven: 7,
|
|
32885
|
+
eight: 8,
|
|
32886
|
+
nine: 9,
|
|
32887
|
+
ten: 10,
|
|
32888
|
+
twelve: 12,
|
|
32889
|
+
twentyfour: 24
|
|
32890
|
+
};
|
|
32891
|
+
function resolveTimeRange(query, now) {
|
|
32892
|
+
const normalized = query.trim();
|
|
32893
|
+
const lower = normalized.toLowerCase();
|
|
32894
|
+
const fromTo = lower.match(
|
|
32895
|
+
/\b(?:from|between)\s+(.+?)\s+(?:to|and|-|until)\s+([\w:.\-+t /]+)/i
|
|
32896
|
+
);
|
|
32897
|
+
if (fromTo) {
|
|
32898
|
+
const aSlice = fromTo[1];
|
|
32899
|
+
const bSlice = fromTo[2];
|
|
32900
|
+
if (aSlice !== void 0 && bSlice !== void 0) {
|
|
32901
|
+
const a = parseInstant(aSlice, now);
|
|
32902
|
+
const b = parseInstant(bSlice, now);
|
|
32903
|
+
if (a && b) {
|
|
32904
|
+
const start = a.getTime() <= b.getTime() ? a : b;
|
|
32905
|
+
const end = a.getTime() <= b.getTime() ? b : a;
|
|
32906
|
+
return {
|
|
32907
|
+
range: { start, end },
|
|
32908
|
+
matchedSubstring: fromTo[0]
|
|
32909
|
+
};
|
|
32910
|
+
}
|
|
32911
|
+
}
|
|
32912
|
+
}
|
|
32913
|
+
const sinceMatch = lower.match(/\bsince\s+([\w:.\-+t /]+)/i);
|
|
32914
|
+
if (sinceMatch) {
|
|
32915
|
+
const slice = sinceMatch[1];
|
|
32916
|
+
if (slice !== void 0) {
|
|
32917
|
+
const start = parseInstant(slice, now);
|
|
32918
|
+
if (start) {
|
|
32919
|
+
return {
|
|
32920
|
+
range: { start, end: now },
|
|
32921
|
+
matchedSubstring: sinceMatch[0]
|
|
32922
|
+
};
|
|
32923
|
+
}
|
|
32924
|
+
}
|
|
32925
|
+
}
|
|
32926
|
+
if (/\byesterday\b/.test(lower)) {
|
|
32927
|
+
const startOfToday = startOfDay(now);
|
|
32928
|
+
const start = new Date(startOfToday.getTime() - MS_PER_DAY);
|
|
32929
|
+
const end = new Date(startOfToday.getTime() - 1);
|
|
32930
|
+
return {
|
|
32931
|
+
range: { start, end, relative_label: "yesterday" },
|
|
32932
|
+
matchedSubstring: "yesterday"
|
|
32933
|
+
};
|
|
32934
|
+
}
|
|
32935
|
+
if (/\btoday\b/.test(lower)) {
|
|
32936
|
+
return {
|
|
32937
|
+
range: {
|
|
32938
|
+
start: startOfDay(now),
|
|
32939
|
+
end: now,
|
|
32940
|
+
relative_label: "today"
|
|
32941
|
+
},
|
|
32942
|
+
matchedSubstring: "today"
|
|
32943
|
+
};
|
|
32944
|
+
}
|
|
32945
|
+
const compactHours = lower.match(/\blast\s+(\d+)\s*h\b/i);
|
|
32946
|
+
if (compactHours) {
|
|
32947
|
+
const tok = compactHours[1];
|
|
32948
|
+
if (tok !== void 0) {
|
|
32949
|
+
const n = Number.parseInt(tok, 10);
|
|
32950
|
+
if (Number.isFinite(n) && n > 0) {
|
|
32951
|
+
const start = new Date(now.getTime() - n * MS_PER_HOUR);
|
|
32952
|
+
return {
|
|
32953
|
+
range: { start, end: now, relative_label: `last ${n}h` },
|
|
32954
|
+
matchedSubstring: compactHours[0]
|
|
32955
|
+
};
|
|
32956
|
+
}
|
|
32957
|
+
}
|
|
32958
|
+
}
|
|
32959
|
+
const hoursMatch = lower.match(
|
|
32960
|
+
/\b(?:past|last)\s+([\w]+|\d+)\s*(?:hr\b|hrs\b|hour|hours)/i
|
|
32961
|
+
);
|
|
32962
|
+
if (hoursMatch) {
|
|
32963
|
+
const tok = hoursMatch[1];
|
|
32964
|
+
if (tok !== void 0) {
|
|
32965
|
+
const n = parseCount(tok);
|
|
32966
|
+
if (n !== null && n > 0) {
|
|
32967
|
+
const start = new Date(now.getTime() - n * MS_PER_HOUR);
|
|
32968
|
+
return {
|
|
32969
|
+
range: { start, end: now, relative_label: `past ${n} hour${n === 1 ? "" : "s"}` },
|
|
32970
|
+
matchedSubstring: hoursMatch[0]
|
|
32971
|
+
};
|
|
32972
|
+
}
|
|
32973
|
+
}
|
|
32974
|
+
}
|
|
32975
|
+
if (/\b(?:past|last)\s+hour\b/.test(lower)) {
|
|
32976
|
+
const start = new Date(now.getTime() - MS_PER_HOUR);
|
|
32977
|
+
return {
|
|
32978
|
+
range: { start, end: now, relative_label: "past hour" },
|
|
32979
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+hour\b/i)[0]
|
|
32980
|
+
};
|
|
32981
|
+
}
|
|
32982
|
+
const daysMatch = lower.match(
|
|
32983
|
+
/\b(?:past|last)\s+([\w]+|\d+)\s*(?:d\b|day|days)/i
|
|
32984
|
+
);
|
|
32985
|
+
if (daysMatch) {
|
|
32986
|
+
const tok = daysMatch[1];
|
|
32987
|
+
if (tok !== void 0) {
|
|
32988
|
+
const n = parseCount(tok);
|
|
32989
|
+
if (n !== null && n > 0) {
|
|
32990
|
+
const start = new Date(now.getTime() - n * MS_PER_DAY);
|
|
32991
|
+
return {
|
|
32992
|
+
range: { start, end: now, relative_label: `past ${n} day${n === 1 ? "" : "s"}` },
|
|
32993
|
+
matchedSubstring: daysMatch[0]
|
|
32994
|
+
};
|
|
32995
|
+
}
|
|
32996
|
+
}
|
|
32997
|
+
}
|
|
32998
|
+
if (/\b(?:past|last)\s+day\b/.test(lower)) {
|
|
32999
|
+
const start = new Date(now.getTime() - MS_PER_DAY);
|
|
33000
|
+
return {
|
|
33001
|
+
range: { start, end: now, relative_label: "past day" },
|
|
33002
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+day\b/i)[0]
|
|
33003
|
+
};
|
|
33004
|
+
}
|
|
33005
|
+
if (/\bthis\s+week\b/.test(lower)) {
|
|
33006
|
+
const start = startOfWeek(now);
|
|
33007
|
+
return {
|
|
33008
|
+
range: { start, end: now, relative_label: "this week" },
|
|
33009
|
+
matchedSubstring: lower.match(/\bthis\s+week\b/i)[0]
|
|
33010
|
+
};
|
|
33011
|
+
}
|
|
33012
|
+
if (/\b(?:past|last)\s+week\b/.test(lower)) {
|
|
33013
|
+
const start = new Date(now.getTime() - 7 * MS_PER_DAY);
|
|
33014
|
+
return {
|
|
33015
|
+
range: { start, end: now, relative_label: "past week" },
|
|
33016
|
+
matchedSubstring: lower.match(/\b(?:past|last)\s+week\b/i)[0]
|
|
33017
|
+
};
|
|
33018
|
+
}
|
|
33019
|
+
const isoMatch = normalized.match(
|
|
33020
|
+
/\b(\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2})?(?:Z|[+-]\d{2}:\d{2})?)?)\b/
|
|
33021
|
+
);
|
|
33022
|
+
if (isoMatch) {
|
|
33023
|
+
const tok = isoMatch[1];
|
|
33024
|
+
if (tok !== void 0) {
|
|
33025
|
+
const parsed = parseInstant(tok, now);
|
|
33026
|
+
if (parsed) {
|
|
33027
|
+
const isDateOnly = /^\d{4}-\d{2}-\d{2}$/.test(tok);
|
|
33028
|
+
if (isDateOnly) {
|
|
33029
|
+
return {
|
|
33030
|
+
range: {
|
|
33031
|
+
start: parsed,
|
|
33032
|
+
end: new Date(parsed.getTime() + MS_PER_DAY - 1)
|
|
33033
|
+
},
|
|
33034
|
+
matchedSubstring: tok
|
|
33035
|
+
};
|
|
33036
|
+
}
|
|
33037
|
+
return {
|
|
33038
|
+
range: {
|
|
33039
|
+
start: new Date(parsed.getTime() - 30 * 60 * 1e3),
|
|
33040
|
+
end: new Date(parsed.getTime() + 30 * 60 * 1e3)
|
|
33041
|
+
},
|
|
33042
|
+
matchedSubstring: tok
|
|
33043
|
+
};
|
|
33044
|
+
}
|
|
33045
|
+
}
|
|
33046
|
+
}
|
|
33047
|
+
return null;
|
|
33048
|
+
}
|
|
33049
|
+
function parseInstant(token, now) {
|
|
33050
|
+
const trimmed = token.trim().replace(/[,.!?;]+$/g, "");
|
|
33051
|
+
if (!trimmed) return null;
|
|
33052
|
+
const lower = trimmed.toLowerCase();
|
|
33053
|
+
if (lower === "now") return now;
|
|
33054
|
+
if (lower === "today") return startOfDay(now);
|
|
33055
|
+
if (lower === "yesterday") {
|
|
33056
|
+
return new Date(startOfDay(now).getTime() - MS_PER_DAY);
|
|
33057
|
+
}
|
|
33058
|
+
const isoLike = trimmed.replace(" ", "T");
|
|
33059
|
+
const parsed = new Date(isoLike);
|
|
33060
|
+
if (!Number.isNaN(parsed.getTime())) return parsed;
|
|
33061
|
+
return null;
|
|
33062
|
+
}
|
|
33063
|
+
function parseCount(token) {
|
|
33064
|
+
const lower = token.toLowerCase();
|
|
33065
|
+
if (/^\d+$/.test(lower)) {
|
|
33066
|
+
const n = Number.parseInt(lower, 10);
|
|
33067
|
+
return Number.isFinite(n) ? n : null;
|
|
33068
|
+
}
|
|
33069
|
+
return NUMBER_WORDS[lower] ?? null;
|
|
33070
|
+
}
|
|
33071
|
+
function startOfDay(d) {
|
|
33072
|
+
const out = new Date(d);
|
|
33073
|
+
out.setHours(0, 0, 0, 0);
|
|
33074
|
+
return out;
|
|
33075
|
+
}
|
|
33076
|
+
function startOfWeek(d) {
|
|
33077
|
+
const out = startOfDay(d);
|
|
33078
|
+
const dayOfWeek = out.getDay();
|
|
33079
|
+
const offsetToMonday = (dayOfWeek + 6) % 7;
|
|
33080
|
+
out.setDate(out.getDate() - offsetToMonday);
|
|
33081
|
+
return out;
|
|
33082
|
+
}
|
|
33083
|
+
function listFromRegistry(registry) {
|
|
33084
|
+
if (!registry) return [];
|
|
33085
|
+
if (Array.isArray(registry)) return registry;
|
|
33086
|
+
if (typeof registry.list === "function") {
|
|
33087
|
+
return registry.list();
|
|
33088
|
+
}
|
|
33089
|
+
return [];
|
|
33090
|
+
}
|
|
33091
|
+
function extractAgentNames(query, registry) {
|
|
33092
|
+
const records = listFromRegistry(registry);
|
|
33093
|
+
if (records.length === 0) return { matched: [], flagged: false };
|
|
33094
|
+
const lowerQuery = query.toLowerCase();
|
|
33095
|
+
const compactQuery = lowerQuery.replace(/[\s_-]+/g, "");
|
|
33096
|
+
const matched = [];
|
|
33097
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33098
|
+
for (const rec of records) {
|
|
33099
|
+
const id = rec.agent_id;
|
|
33100
|
+
if (!id || seen.has(id)) continue;
|
|
33101
|
+
const idLower = id.toLowerCase();
|
|
33102
|
+
if (idLower.length < 3) continue;
|
|
33103
|
+
const idCompact = idLower.replace(/[\s_-]+/g, "");
|
|
33104
|
+
const wordRe = new RegExp(
|
|
33105
|
+
`\\b${idLower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
|
|
33106
|
+
"i"
|
|
33107
|
+
);
|
|
33108
|
+
if (wordRe.test(query)) {
|
|
33109
|
+
matched.push(id);
|
|
33110
|
+
seen.add(id);
|
|
33111
|
+
continue;
|
|
33112
|
+
}
|
|
33113
|
+
if (idCompact.length >= 4 && compactQuery.includes(idCompact)) {
|
|
33114
|
+
matched.push(id);
|
|
33115
|
+
seen.add(id);
|
|
33116
|
+
}
|
|
33117
|
+
}
|
|
33118
|
+
const agentMention = lowerQuery.match(/\bagent\s+([a-z0-9_-]{3,40})/i);
|
|
33119
|
+
const flagged = matched.length === 0 && agentMention !== null && agentMention[1] !== void 0 && !records.some((r) => r.agent_id.toLowerCase() === agentMention[1]?.toLowerCase());
|
|
33120
|
+
return { matched, flagged };
|
|
33121
|
+
}
|
|
33122
|
+
function escapeRegex(s) {
|
|
33123
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33124
|
+
}
|
|
33125
|
+
function extractEventTypes(query, enumValues) {
|
|
33126
|
+
const lower = query.toLowerCase();
|
|
33127
|
+
const matched = [];
|
|
33128
|
+
const seen = /* @__PURE__ */ new Set();
|
|
33129
|
+
for (const ev of enumValues) {
|
|
33130
|
+
if (seen.has(ev)) continue;
|
|
33131
|
+
const re = new RegExp(`\\b${escapeRegex(ev)}\\b`, "i");
|
|
33132
|
+
if (re.test(query)) {
|
|
33133
|
+
matched.push(ev);
|
|
33134
|
+
seen.add(ev);
|
|
33135
|
+
}
|
|
33136
|
+
}
|
|
33137
|
+
for (const syn of EVENT_SYNONYMS) {
|
|
33138
|
+
const re = new RegExp(
|
|
33139
|
+
`\\b${syn.phrase.split(/\s+/).map(escapeRegex).join("\\s+")}\\b`,
|
|
33140
|
+
"i"
|
|
33141
|
+
);
|
|
33142
|
+
if (re.test(query)) {
|
|
33143
|
+
for (const c of syn.canonical) {
|
|
33144
|
+
if (seen.has(c)) continue;
|
|
33145
|
+
if (!enumValues.includes(c)) continue;
|
|
33146
|
+
matched.push(c);
|
|
33147
|
+
seen.add(c);
|
|
33148
|
+
}
|
|
33149
|
+
}
|
|
33150
|
+
}
|
|
33151
|
+
const globMatches = lower.match(/\b([a-z_]+)_\*/g) ?? [];
|
|
33152
|
+
for (const glob of globMatches) {
|
|
33153
|
+
const prefix = glob.slice(0, -2);
|
|
33154
|
+
for (const ev of enumValues) {
|
|
33155
|
+
if (seen.has(ev)) continue;
|
|
33156
|
+
if (ev.startsWith(prefix)) {
|
|
33157
|
+
matched.push(ev);
|
|
33158
|
+
seen.add(ev);
|
|
33159
|
+
}
|
|
33160
|
+
}
|
|
33161
|
+
}
|
|
33162
|
+
const eventNounMention = /\b(?:event|events|class|classes)\b/i.test(query) && matched.length === 0;
|
|
33163
|
+
return { matched, flagged: eventNounMention };
|
|
33164
|
+
}
|
|
33165
|
+
function deriveIntentPhrase(query, stripTokens) {
|
|
33166
|
+
let out = query;
|
|
33167
|
+
for (const tok of stripTokens) {
|
|
33168
|
+
if (!tok) continue;
|
|
33169
|
+
const re = new RegExp(escapeRegex(tok), "gi");
|
|
33170
|
+
out = out.replace(re, " ");
|
|
33171
|
+
}
|
|
33172
|
+
return out.replace(/\s+/g, " ").trim();
|
|
33173
|
+
}
|
|
33174
|
+
function computeConfidence(parsed) {
|
|
33175
|
+
const dims = [
|
|
33176
|
+
{ present: parsed.hasTimeMention, resolved: parsed.timeResolved },
|
|
33177
|
+
{ present: parsed.hasAgentMention, resolved: parsed.agentResolved },
|
|
33178
|
+
{ present: parsed.hasEventMention, resolved: parsed.eventResolved }
|
|
33179
|
+
];
|
|
33180
|
+
const present = dims.filter((d) => d.present);
|
|
33181
|
+
let base;
|
|
33182
|
+
if (present.length === 0) {
|
|
33183
|
+
base = parsed.intentEmpty ? 0 : 0.3;
|
|
33184
|
+
} else {
|
|
33185
|
+
const resolved = present.filter((d) => d.resolved).length;
|
|
33186
|
+
base = resolved / present.length;
|
|
33187
|
+
}
|
|
33188
|
+
const adjusted = base - 0.15 * parsed.ambiguityCount;
|
|
33189
|
+
if (adjusted < 0) return 0;
|
|
33190
|
+
if (adjusted > 1) return 1;
|
|
33191
|
+
return adjusted;
|
|
33192
|
+
}
|
|
33193
|
+
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;
|
|
33194
|
+
var AGENT_MENTION_PROBE = /\bagent[s]?\b/i;
|
|
33195
|
+
var EVENT_MENTION_PROBE = /\b(event|events|class|classes|approvals?|policy)\b/i;
|
|
33196
|
+
function parseQuery(query, opts) {
|
|
33197
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
33198
|
+
const enumValues = opts?.eventClassEnum ?? CANONICAL_AUDIT_EVENT_CLASSES;
|
|
33199
|
+
const original = query ?? "";
|
|
33200
|
+
const trimmed = original.trim();
|
|
33201
|
+
if (trimmed.length === 0) {
|
|
33202
|
+
return {
|
|
33203
|
+
time_range: null,
|
|
33204
|
+
agent_names: [],
|
|
33205
|
+
event_types: [],
|
|
33206
|
+
intent_phrase: "",
|
|
33207
|
+
ambiguity_flags: ["no_signal_extracted"],
|
|
33208
|
+
parse_confidence: 0
|
|
33209
|
+
};
|
|
33210
|
+
}
|
|
33211
|
+
const ambiguity_flags = /* @__PURE__ */ new Set();
|
|
33212
|
+
const timeMatch = resolveTimeRange(trimmed, now);
|
|
33213
|
+
const hasTimeMention = TIME_MENTION_PROBE.test(trimmed);
|
|
33214
|
+
if (hasTimeMention && !timeMatch) {
|
|
33215
|
+
ambiguity_flags.add("unknown_time_token");
|
|
33216
|
+
}
|
|
33217
|
+
const agentResult = extractAgentNames(trimmed, opts?.registry);
|
|
33218
|
+
if (agentResult.flagged) {
|
|
33219
|
+
ambiguity_flags.add("unknown_agent_token");
|
|
33220
|
+
}
|
|
33221
|
+
const hasAgentMention = AGENT_MENTION_PROBE.test(trimmed);
|
|
33222
|
+
const eventResult = extractEventTypes(trimmed, enumValues);
|
|
33223
|
+
const hasEventMention = EVENT_MENTION_PROBE.test(trimmed);
|
|
33224
|
+
if (eventResult.flagged) {
|
|
33225
|
+
ambiguity_flags.add("unknown_event_token");
|
|
33226
|
+
}
|
|
33227
|
+
const stripTokens = [];
|
|
33228
|
+
if (timeMatch) stripTokens.push(timeMatch.matchedSubstring);
|
|
33229
|
+
for (const name of agentResult.matched) stripTokens.push(name);
|
|
33230
|
+
for (const ev of eventResult.matched) {
|
|
33231
|
+
if (trimmed.toLowerCase().includes(ev.toLowerCase())) {
|
|
33232
|
+
stripTokens.push(ev);
|
|
33233
|
+
}
|
|
33234
|
+
}
|
|
33235
|
+
const intent_phrase = deriveIntentPhrase(trimmed, stripTokens);
|
|
33236
|
+
const parse_confidence = computeConfidence({
|
|
33237
|
+
hasTimeMention,
|
|
33238
|
+
timeResolved: timeMatch !== null,
|
|
33239
|
+
hasAgentMention,
|
|
33240
|
+
agentResolved: agentResult.matched.length > 0,
|
|
33241
|
+
hasEventMention,
|
|
33242
|
+
eventResolved: eventResult.matched.length > 0,
|
|
33243
|
+
intentEmpty: intent_phrase.length === 0,
|
|
33244
|
+
ambiguityCount: ambiguity_flags.size
|
|
33245
|
+
});
|
|
33246
|
+
if (timeMatch === null && agentResult.matched.length === 0 && eventResult.matched.length === 0 && intent_phrase.length === 0) {
|
|
33247
|
+
ambiguity_flags.add("no_signal_extracted");
|
|
33248
|
+
}
|
|
33249
|
+
return {
|
|
33250
|
+
time_range: timeMatch ? timeMatch.range : null,
|
|
33251
|
+
agent_names: agentResult.matched,
|
|
33252
|
+
event_types: eventResult.matched,
|
|
33253
|
+
intent_phrase,
|
|
33254
|
+
ambiguity_flags: Array.from(ambiguity_flags),
|
|
33255
|
+
parse_confidence
|
|
33256
|
+
};
|
|
33257
|
+
}
|
|
33258
|
+
var LLM_ASSIST_THRESHOLD = 0.5;
|
|
33259
|
+
function isLowConfidence(parsed) {
|
|
33260
|
+
return parsed.parse_confidence < LLM_ASSIST_THRESHOLD;
|
|
33261
|
+
}
|
|
33262
|
+
async function parseQueryWithLlmAssist(query, llmAssist, opts) {
|
|
33263
|
+
const parsed = parseQuery(query, opts);
|
|
33264
|
+
if (!llmAssist || !isLowConfidence(parsed)) return parsed;
|
|
33265
|
+
let completion;
|
|
33266
|
+
try {
|
|
33267
|
+
completion = await llmAssist(query, parsed);
|
|
33268
|
+
} catch {
|
|
33269
|
+
return parsed;
|
|
33270
|
+
}
|
|
33271
|
+
if (!completion || typeof completion !== "object") return parsed;
|
|
33272
|
+
const merged = { ...parsed };
|
|
33273
|
+
if (parsed.time_range === null && completion.time_range) {
|
|
33274
|
+
merged.time_range = completion.time_range;
|
|
33275
|
+
}
|
|
33276
|
+
if (parsed.agent_names.length === 0 && Array.isArray(completion.agent_names)) {
|
|
33277
|
+
merged.agent_names = completion.agent_names.filter(
|
|
33278
|
+
(s) => typeof s === "string" && s.length > 0
|
|
33279
|
+
);
|
|
33280
|
+
}
|
|
33281
|
+
if (parsed.event_types.length === 0 && Array.isArray(completion.event_types)) {
|
|
33282
|
+
const allowed = new Set(opts?.eventClassEnum ?? CANONICAL_AUDIT_EVENT_CLASSES);
|
|
33283
|
+
merged.event_types = completion.event_types.filter(
|
|
33284
|
+
(s) => typeof s === "string" && allowed.has(s)
|
|
33285
|
+
);
|
|
33286
|
+
}
|
|
33287
|
+
merged.parse_confidence = Math.max(
|
|
33288
|
+
parsed.parse_confidence,
|
|
33289
|
+
computeConfidence({
|
|
33290
|
+
hasTimeMention: TIME_MENTION_PROBE.test(query),
|
|
33291
|
+
timeResolved: merged.time_range !== null,
|
|
33292
|
+
hasAgentMention: AGENT_MENTION_PROBE.test(query),
|
|
33293
|
+
agentResolved: merged.agent_names.length > 0,
|
|
33294
|
+
hasEventMention: EVENT_MENTION_PROBE.test(query),
|
|
33295
|
+
eventResolved: merged.event_types.length > 0,
|
|
33296
|
+
intentEmpty: merged.intent_phrase.length === 0,
|
|
33297
|
+
ambiguityCount: merged.ambiguity_flags.length
|
|
33298
|
+
})
|
|
33299
|
+
);
|
|
33300
|
+
return merged;
|
|
33301
|
+
}
|
|
33302
|
+
function auditSafeSummary(parsed) {
|
|
33303
|
+
return {
|
|
33304
|
+
time_range: parsed.time_range ? {
|
|
33305
|
+
start_iso: parsed.time_range.start.toISOString(),
|
|
33306
|
+
end_iso: parsed.time_range.end.toISOString(),
|
|
33307
|
+
...parsed.time_range.relative_label !== void 0 ? { relative_label: parsed.time_range.relative_label } : {}
|
|
33308
|
+
} : null,
|
|
33309
|
+
agent_names: [...parsed.agent_names],
|
|
33310
|
+
event_types: [...parsed.event_types],
|
|
33311
|
+
ambiguity_flags: [...parsed.ambiguity_flags],
|
|
33312
|
+
parse_confidence: parsed.parse_confidence
|
|
33313
|
+
};
|
|
33314
|
+
}
|
|
33315
|
+
|
|
32619
33316
|
// src/chat/operator-chat-service.ts
|
|
32620
33317
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
32621
33318
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
@@ -32670,6 +33367,8 @@ var OperatorChatService = class {
|
|
|
32670
33367
|
contextFetchers;
|
|
32671
33368
|
contextLlmAssist;
|
|
32672
33369
|
dynamicContextBudget;
|
|
33370
|
+
agentRegistry;
|
|
33371
|
+
grammarLlmAssist;
|
|
32673
33372
|
/**
|
|
32674
33373
|
* In-memory thread_id assigned to the active concierge session.
|
|
32675
33374
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32708,6 +33407,12 @@ var OperatorChatService = class {
|
|
|
32708
33407
|
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
32709
33408
|
}
|
|
32710
33409
|
this.dynamicContextBudget = deps.conciergeDynamicContextBudget !== void 0 && deps.conciergeDynamicContextBudget > 0 ? deps.conciergeDynamicContextBudget : DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET;
|
|
33410
|
+
if (deps.conciergeAgentRegistry) {
|
|
33411
|
+
this.agentRegistry = deps.conciergeAgentRegistry;
|
|
33412
|
+
}
|
|
33413
|
+
if (deps.conciergeGrammarLlmAssist) {
|
|
33414
|
+
this.grammarLlmAssist = deps.conciergeGrammarLlmAssist;
|
|
33415
|
+
}
|
|
32711
33416
|
}
|
|
32712
33417
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32713
33418
|
/**
|
|
@@ -32766,6 +33471,7 @@ var OperatorChatService = class {
|
|
|
32766
33471
|
await this.memory.appendTurn(threadId, "user", trimmed).catch(() => {
|
|
32767
33472
|
});
|
|
32768
33473
|
}
|
|
33474
|
+
const parsedGrammar = await this.runGrammarParse(filterResult.filtered);
|
|
32769
33475
|
const start = Date.now();
|
|
32770
33476
|
let conciergeBody;
|
|
32771
33477
|
let servedBy = "disabled";
|
|
@@ -32784,7 +33490,8 @@ var OperatorChatService = class {
|
|
|
32784
33490
|
outcome = "substrate_disabled";
|
|
32785
33491
|
} else {
|
|
32786
33492
|
const dynamicResult = await this.runDynamicContextFold(
|
|
32787
|
-
filterResult.filtered
|
|
33493
|
+
filterResult.filtered,
|
|
33494
|
+
parsedGrammar
|
|
32788
33495
|
);
|
|
32789
33496
|
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
32790
33497
|
const context = await this.assembleConciergeContext(
|
|
@@ -32855,7 +33562,8 @@ var OperatorChatService = class {
|
|
|
32855
33562
|
...this.memory ? {
|
|
32856
33563
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32857
33564
|
} : {},
|
|
32858
|
-
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {}
|
|
33565
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {},
|
|
33566
|
+
parsed_grammar: auditSafeSummary(parsedGrammar)
|
|
32859
33567
|
};
|
|
32860
33568
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32861
33569
|
return {
|
|
@@ -33070,8 +33778,12 @@ ${inbox}`
|
|
|
33070
33778
|
* proceeds with no fold. Returns the rendered section + the list of
|
|
33071
33779
|
* categories whose data made it into the section (used for the
|
|
33072
33780
|
* round-trip audit emission).
|
|
33781
|
+
*
|
|
33782
|
+
* Tau-4: receives the pre-parsed `ParsedQuery` and forwards it as the
|
|
33783
|
+
* `parsed` opt to `foldContext`, so fetchers see the structured
|
|
33784
|
+
* `FetcherHints` derived from it.
|
|
33073
33785
|
*/
|
|
33074
|
-
async runDynamicContextFold(query) {
|
|
33786
|
+
async runDynamicContextFold(query, parsedGrammar) {
|
|
33075
33787
|
if (!this.contextFetchers) {
|
|
33076
33788
|
return { section: "", categoriesIncluded: [] };
|
|
33077
33789
|
}
|
|
@@ -33080,10 +33792,24 @@ ${inbox}`
|
|
|
33080
33792
|
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33081
33793
|
onFetcherFailure: (category, error) => {
|
|
33082
33794
|
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33083
|
-
}
|
|
33795
|
+
},
|
|
33796
|
+
parsed: parsedGrammar
|
|
33084
33797
|
});
|
|
33085
33798
|
return result;
|
|
33086
33799
|
}
|
|
33800
|
+
/**
|
|
33801
|
+
* WP-V1.3-9 Tau-4: parse the (PII-filtered) operator query into a
|
|
33802
|
+
* `ParsedQuery`. Routes through the LLM-assist completion hook when
|
|
33803
|
+
* configured and the rule-based parse is below
|
|
33804
|
+
* `LLM_ASSIST_THRESHOLD`. Always returns a parse object (never
|
|
33805
|
+
* throws) so the audit emission can carry the result unconditionally.
|
|
33806
|
+
*/
|
|
33807
|
+
async runGrammarParse(query) {
|
|
33808
|
+
return parseQueryWithLlmAssist(query, this.grammarLlmAssist, {
|
|
33809
|
+
...this.agentRegistry !== void 0 ? { registry: this.agentRegistry } : {},
|
|
33810
|
+
eventClassEnum: CANONICAL_AUDIT_EVENT_CLASSES
|
|
33811
|
+
});
|
|
33812
|
+
}
|
|
33087
33813
|
/**
|
|
33088
33814
|
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33089
33815
|
* of the fold path so the dynamic-context handler stays readable.
|