@sanctuary-framework/mcp-server 1.2.5 → 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 +1607 -45
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1607 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1580 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +543 -5
- package/dist/index.d.ts +543 -5
- package/dist/index.js +1580 -39
- 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,45 @@ 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
|
+
}
|
|
16334
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16335
|
+
const limit = parseLimit2(
|
|
16336
|
+
url.searchParams.get("limit"),
|
|
16337
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16338
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16339
|
+
);
|
|
16340
|
+
const statusRaw = url.searchParams.get("status");
|
|
16341
|
+
const sinceTs = url.searchParams.get("since") ?? void 0;
|
|
16342
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16343
|
+
const filterStatus = statusRaw && isStatusFilter(statusRaw) && statusRaw !== "pending" ? statusRaw : void 0;
|
|
16344
|
+
const entries = await deps.aggregator.getHistory(
|
|
16345
|
+
{
|
|
16346
|
+
limit,
|
|
16347
|
+
...filterStatus !== void 0 ? { status: filterStatus } : {},
|
|
16348
|
+
...sinceTs !== void 0 ? { sinceTs } : {}
|
|
16349
|
+
},
|
|
16350
|
+
operatorId
|
|
16351
|
+
);
|
|
16352
|
+
writeJSON4(res, 200, { ok: true, data: { entries } });
|
|
16353
|
+
return true;
|
|
16354
|
+
}
|
|
16315
16355
|
if (method === "GET" && path === APPROVAL_INBOX_API_PREFIX) {
|
|
16316
16356
|
const limit = parseLimit2(
|
|
16317
16357
|
url.searchParams.get("limit"),
|
|
@@ -16334,11 +16374,39 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16334
16374
|
writeJSON4(res, 404, { ok: false, error: "not_found", path });
|
|
16335
16375
|
return true;
|
|
16336
16376
|
}
|
|
16337
|
-
if (method === "GET" && entryMatch.action ===
|
|
16338
|
-
const
|
|
16339
|
-
|
|
16340
|
-
(
|
|
16377
|
+
if (method === "GET" && entryMatch.action === "audit-trail") {
|
|
16378
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16379
|
+
if (!entry) {
|
|
16380
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16381
|
+
return true;
|
|
16382
|
+
}
|
|
16383
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16384
|
+
const trail = await deps.aggregator.getAuditTrail(
|
|
16385
|
+
entryMatch.aggregatorId,
|
|
16386
|
+
operatorId
|
|
16341
16387
|
);
|
|
16388
|
+
writeJSON4(res, 200, { ok: true, data: { entry, audit_trail: trail } });
|
|
16389
|
+
return true;
|
|
16390
|
+
}
|
|
16391
|
+
if (method === "GET" && entryMatch.action === "payload") {
|
|
16392
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16393
|
+
if (!entry) {
|
|
16394
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16395
|
+
return true;
|
|
16396
|
+
}
|
|
16397
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16398
|
+
const payload = await deps.aggregator.getFullPayloadWithAudit(
|
|
16399
|
+
entryMatch.aggregatorId,
|
|
16400
|
+
operatorId
|
|
16401
|
+
);
|
|
16402
|
+
writeJSON4(res, 200, {
|
|
16403
|
+
ok: true,
|
|
16404
|
+
data: { entry, request_payload: payload }
|
|
16405
|
+
});
|
|
16406
|
+
return true;
|
|
16407
|
+
}
|
|
16408
|
+
if (method === "GET" && entryMatch.action === null) {
|
|
16409
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16342
16410
|
if (!entry) {
|
|
16343
16411
|
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16344
16412
|
return true;
|
|
@@ -19325,7 +19393,10 @@ var APPROVAL_AGGREGATOR_HKDF_INFO = "l2-approval-aggregator-v1";
|
|
|
19325
19393
|
var APPROVAL_AGGREGATOR_AUDIT_OPS = {
|
|
19326
19394
|
AGGREGATED: "cross_harness_approval_aggregated",
|
|
19327
19395
|
RESOLVED: "cross_harness_approval_resolved",
|
|
19328
|
-
DEDUPED: "cross_harness_approval_deduped"
|
|
19396
|
+
DEDUPED: "cross_harness_approval_deduped",
|
|
19397
|
+
PAYLOAD_DECRYPTED: "cross_harness_approval_payload_decrypted",
|
|
19398
|
+
AUDIT_TRAIL_VIEWED: "cross_harness_approval_audit_trail_viewed",
|
|
19399
|
+
REPLAYED: "cross_harness_approval_replayed"
|
|
19329
19400
|
};
|
|
19330
19401
|
var DEFAULT_PENDING_TTL_MS = 5 * 60 * 1e3;
|
|
19331
19402
|
var DEFAULT_MAX_LIST_LIMIT = 200;
|
|
@@ -19341,6 +19412,8 @@ var ApprovalAggregator = class {
|
|
|
19341
19412
|
now;
|
|
19342
19413
|
resolveSourceContext;
|
|
19343
19414
|
resolveHubInboxItemId;
|
|
19415
|
+
payloadStore;
|
|
19416
|
+
resolveEnforcementChain;
|
|
19344
19417
|
/** Cached entries by `aggregator_id`. */
|
|
19345
19418
|
entries = /* @__PURE__ */ new Map();
|
|
19346
19419
|
/** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
|
|
@@ -19353,6 +19426,20 @@ var ApprovalAggregator = class {
|
|
|
19353
19426
|
hydrated = false;
|
|
19354
19427
|
/** Active SSE listeners. */
|
|
19355
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();
|
|
19356
19443
|
constructor(deps) {
|
|
19357
19444
|
this.storage = deps.storage;
|
|
19358
19445
|
this.encryptionKey = derivePurposeKey(
|
|
@@ -19370,6 +19457,14 @@ var ApprovalAggregator = class {
|
|
|
19370
19457
|
source_agent_id: this.fortressId
|
|
19371
19458
|
}));
|
|
19372
19459
|
this.resolveHubInboxItemId = deps.resolveHubInboxItemId ?? ((_event) => void 0);
|
|
19460
|
+
this.payloadStore = deps.payloadStore ?? null;
|
|
19461
|
+
this.resolveEnforcementChain = deps.resolveEnforcementChain ?? ((event) => [
|
|
19462
|
+
{
|
|
19463
|
+
layer: "l2",
|
|
19464
|
+
event: `approval_required:${event.operation}`,
|
|
19465
|
+
timestamp: event.request_timestamp
|
|
19466
|
+
}
|
|
19467
|
+
]);
|
|
19373
19468
|
}
|
|
19374
19469
|
/**
|
|
19375
19470
|
* Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
|
|
@@ -19379,6 +19474,113 @@ var ApprovalAggregator = class {
|
|
|
19379
19474
|
this.listeners.add(listener);
|
|
19380
19475
|
return () => this.listeners.delete(listener);
|
|
19381
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
|
+
}
|
|
19382
19584
|
/**
|
|
19383
19585
|
* Ingest a gate event. Returns the aggregator entry on first sight,
|
|
19384
19586
|
* `null` when deduped. Resolution events update the existing record;
|
|
@@ -19420,13 +19622,152 @@ var ApprovalAggregator = class {
|
|
|
19420
19622
|
}
|
|
19421
19623
|
/**
|
|
19422
19624
|
* Return the original (unhashed) request payload for the entry. Returns
|
|
19423
|
-
* `null` when the entry is unknown
|
|
19424
|
-
*
|
|
19625
|
+
* `null` when the entry is unknown. When the in-memory payload map has
|
|
19626
|
+
* been evicted (e.g. after a server restart) and a `payloadStore` was
|
|
19627
|
+
* provided, the at-rest bundle is decrypted and the in-memory map is
|
|
19628
|
+
* refilled. Audit emission lives on the `*WithAudit` variant; this base
|
|
19629
|
+
* accessor is silent so internal callers can read without polluting the
|
|
19630
|
+
* audit trail.
|
|
19425
19631
|
*/
|
|
19426
19632
|
async getFullPayload(aggregatorId) {
|
|
19427
19633
|
await this.hydrate();
|
|
19428
19634
|
if (!this.entries.has(aggregatorId)) return null;
|
|
19429
|
-
|
|
19635
|
+
const cached = this.fullPayloads.get(aggregatorId);
|
|
19636
|
+
if (cached !== void 0) return cached;
|
|
19637
|
+
if (this.payloadStore) {
|
|
19638
|
+
try {
|
|
19639
|
+
const restored = await this.payloadStore.loadPayload(aggregatorId);
|
|
19640
|
+
if (restored !== null) {
|
|
19641
|
+
this.fullPayloads.set(aggregatorId, restored);
|
|
19642
|
+
return restored;
|
|
19643
|
+
}
|
|
19644
|
+
} catch {
|
|
19645
|
+
}
|
|
19646
|
+
}
|
|
19647
|
+
return null;
|
|
19648
|
+
}
|
|
19649
|
+
/**
|
|
19650
|
+
* Return the entry record for the given id, or null when unknown.
|
|
19651
|
+
* Idempotent. v1.3 Upsilon-3.
|
|
19652
|
+
*/
|
|
19653
|
+
async getEntry(aggregatorId) {
|
|
19654
|
+
await this.hydrate();
|
|
19655
|
+
return this.entries.get(aggregatorId) ?? null;
|
|
19656
|
+
}
|
|
19657
|
+
/**
|
|
19658
|
+
* Audited variant of `getFullPayload`. Emits the
|
|
19659
|
+
* `cross_harness_approval_payload_decrypted` audit event before
|
|
19660
|
+
* returning. Used by the operator-facing /payload replay route.
|
|
19661
|
+
* v1.3 Upsilon-3.
|
|
19662
|
+
*/
|
|
19663
|
+
async getFullPayloadWithAudit(aggregatorId, operatorId) {
|
|
19664
|
+
const payload = await this.getFullPayload(aggregatorId);
|
|
19665
|
+
if (payload === null) return null;
|
|
19666
|
+
const entry = this.entries.get(aggregatorId);
|
|
19667
|
+
this.auditLog.append(
|
|
19668
|
+
"l2",
|
|
19669
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.PAYLOAD_DECRYPTED,
|
|
19670
|
+
operatorId,
|
|
19671
|
+
{
|
|
19672
|
+
aggregator_id: aggregatorId,
|
|
19673
|
+
...entry ? {
|
|
19674
|
+
source_harness: entry.source_harness,
|
|
19675
|
+
source_agent_id: entry.source_agent_id,
|
|
19676
|
+
entry_status: entry.status
|
|
19677
|
+
} : {}
|
|
19678
|
+
}
|
|
19679
|
+
);
|
|
19680
|
+
return payload;
|
|
19681
|
+
}
|
|
19682
|
+
/**
|
|
19683
|
+
* Return the audit-log entries that led to and surround this approval.
|
|
19684
|
+
* Best-effort matching: aggregator-side emissions (AGGREGATED, RESOLVED,
|
|
19685
|
+
* DEDUPED, replay events) all carry `details.aggregator_id` and link
|
|
19686
|
+
* directly. Gate-side emissions (`gate_*:operation`) do not carry the
|
|
19687
|
+
* aggregator id at v1.3, so they are matched via timestamp window
|
|
19688
|
+
* (entry.created_at to entry.resolved_at + 1s, or expires_at + 1s while
|
|
19689
|
+
* pending) and operation suffix. Emits AUDIT_TRAIL_VIEWED on call.
|
|
19690
|
+
* v1.3 Upsilon-3.
|
|
19691
|
+
*/
|
|
19692
|
+
async getAuditTrail(aggregatorId, operatorId) {
|
|
19693
|
+
await this.hydrate();
|
|
19694
|
+
const entry = this.entries.get(aggregatorId);
|
|
19695
|
+
if (!entry) {
|
|
19696
|
+
return [];
|
|
19697
|
+
}
|
|
19698
|
+
const sinceMs = Date.parse(entry.created_at) - 1e3;
|
|
19699
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
19700
|
+
const queried = await this.auditLog.query({ since: sinceIso, limit: 1e3 });
|
|
19701
|
+
const operationPart = entry.policy_rule_id.includes(":") ? entry.policy_rule_id.slice(entry.policy_rule_id.indexOf(":") + 1) : entry.policy_rule_id;
|
|
19702
|
+
const lifetimeStart = sinceMs;
|
|
19703
|
+
const lifetimeEnd = entry.resolved_at ? Date.parse(entry.resolved_at) + 1e3 : Date.parse(entry.expires_at) + 1e3;
|
|
19704
|
+
const matches = [];
|
|
19705
|
+
for (const audit of queried.entries) {
|
|
19706
|
+
const detailsId = audit.details !== void 0 ? audit.details["aggregator_id"] : void 0;
|
|
19707
|
+
if (detailsId === aggregatorId) {
|
|
19708
|
+
matches.push(audit);
|
|
19709
|
+
continue;
|
|
19710
|
+
}
|
|
19711
|
+
const auditMs = Date.parse(audit.timestamp);
|
|
19712
|
+
if (auditMs < lifetimeStart || auditMs > lifetimeEnd) continue;
|
|
19713
|
+
if (audit.operation.endsWith(`:${operationPart}`)) {
|
|
19714
|
+
matches.push(audit);
|
|
19715
|
+
}
|
|
19716
|
+
}
|
|
19717
|
+
matches.sort(
|
|
19718
|
+
(a, b) => a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0
|
|
19719
|
+
);
|
|
19720
|
+
this.auditLog.append(
|
|
19721
|
+
"l2",
|
|
19722
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.AUDIT_TRAIL_VIEWED,
|
|
19723
|
+
operatorId,
|
|
19724
|
+
{
|
|
19725
|
+
aggregator_id: aggregatorId,
|
|
19726
|
+
entry_status: entry.status,
|
|
19727
|
+
match_count: matches.length
|
|
19728
|
+
}
|
|
19729
|
+
);
|
|
19730
|
+
return matches;
|
|
19731
|
+
}
|
|
19732
|
+
/**
|
|
19733
|
+
* List historical (resolved) approvals. Excludes pending entries by
|
|
19734
|
+
* design: `list()` is the pending-inbox surface and `getHistory()` is
|
|
19735
|
+
* the resolved-replay surface. Emits REPLAYED on each call. v1.3
|
|
19736
|
+
* Upsilon-3.
|
|
19737
|
+
*/
|
|
19738
|
+
async getHistory(opts, operatorId) {
|
|
19739
|
+
await this.hydrate();
|
|
19740
|
+
await this.expireStale();
|
|
19741
|
+
const limit = Math.min(
|
|
19742
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19743
|
+
this.maxListLimit
|
|
19744
|
+
);
|
|
19745
|
+
const sinceMs = opts?.sinceTs ? Date.parse(opts.sinceTs) : Number.NEGATIVE_INFINITY;
|
|
19746
|
+
const matching = [];
|
|
19747
|
+
for (const entry of this.entries.values()) {
|
|
19748
|
+
if (entry.status === "pending") continue;
|
|
19749
|
+
if (opts?.status && entry.status !== opts.status) continue;
|
|
19750
|
+
const stamp = Date.parse(entry.resolved_at ?? entry.created_at);
|
|
19751
|
+
if (stamp < sinceMs) continue;
|
|
19752
|
+
matching.push(entry);
|
|
19753
|
+
}
|
|
19754
|
+
matching.sort((a, b) => {
|
|
19755
|
+
const aStamp = a.resolved_at ?? a.created_at;
|
|
19756
|
+
const bStamp = b.resolved_at ?? b.created_at;
|
|
19757
|
+
return bStamp.localeCompare(aStamp);
|
|
19758
|
+
});
|
|
19759
|
+
const sliced = matching.slice(0, limit);
|
|
19760
|
+
this.auditLog.append(
|
|
19761
|
+
"l2",
|
|
19762
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.REPLAYED,
|
|
19763
|
+
operatorId,
|
|
19764
|
+
{
|
|
19765
|
+
result_count: sliced.length,
|
|
19766
|
+
...opts?.status !== void 0 ? { status_filter: opts.status } : {},
|
|
19767
|
+
...opts?.sinceTs !== void 0 ? { since: opts.sinceTs } : {}
|
|
19768
|
+
}
|
|
19769
|
+
);
|
|
19770
|
+
return sliced;
|
|
19430
19771
|
}
|
|
19431
19772
|
/**
|
|
19432
19773
|
* Resolve an entry. Used by both:
|
|
@@ -19450,6 +19791,7 @@ var ApprovalAggregator = class {
|
|
|
19450
19791
|
entry.status = decision;
|
|
19451
19792
|
entry.resolved_at = this.now().toISOString();
|
|
19452
19793
|
entry.resolved_by = operatorId;
|
|
19794
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19453
19795
|
await this.persist(entry);
|
|
19454
19796
|
this.auditLog.append(
|
|
19455
19797
|
"l2",
|
|
@@ -19500,6 +19842,8 @@ var ApprovalAggregator = class {
|
|
|
19500
19842
|
const now = this.now();
|
|
19501
19843
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19502
19844
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19845
|
+
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19846
|
+
const revision = this.nextRevision();
|
|
19503
19847
|
const entry = {
|
|
19504
19848
|
aggregator_id: id,
|
|
19505
19849
|
source_harness: ctx.source_harness,
|
|
@@ -19511,13 +19855,22 @@ var ApprovalAggregator = class {
|
|
|
19511
19855
|
status: "pending",
|
|
19512
19856
|
created_at: now.toISOString(),
|
|
19513
19857
|
expires_at: expires.toISOString(),
|
|
19514
|
-
|
|
19858
|
+
created_at_revision: revision,
|
|
19859
|
+
last_modified_revision: revision,
|
|
19860
|
+
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19861
|
+
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19515
19862
|
};
|
|
19516
19863
|
this.entries.set(id, entry);
|
|
19517
19864
|
this.dedupIndex.set(dedupKey, id);
|
|
19518
19865
|
this.correlationIndex.set(event.correlation_id, id);
|
|
19519
19866
|
this.fullPayloads.set(id, event.context);
|
|
19520
19867
|
await this.persist(entry);
|
|
19868
|
+
if (this.payloadStore) {
|
|
19869
|
+
try {
|
|
19870
|
+
await this.payloadStore.savePayload(id, event.context);
|
|
19871
|
+
} catch {
|
|
19872
|
+
}
|
|
19873
|
+
}
|
|
19521
19874
|
this.auditLog.append(
|
|
19522
19875
|
"l2",
|
|
19523
19876
|
APPROVAL_AGGREGATOR_AUDIT_OPS.AGGREGATED,
|
|
@@ -19546,6 +19899,7 @@ var ApprovalAggregator = class {
|
|
|
19546
19899
|
entry.status = status;
|
|
19547
19900
|
entry.resolved_at = event.resolution.decided_at;
|
|
19548
19901
|
entry.resolved_by = event.resolution.decided_by;
|
|
19902
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19549
19903
|
await this.persist(entry);
|
|
19550
19904
|
this.auditLog.append(
|
|
19551
19905
|
"l2",
|
|
@@ -19608,6 +19962,7 @@ var ApprovalAggregator = class {
|
|
|
19608
19962
|
entry.status = "expired";
|
|
19609
19963
|
entry.resolved_at = this.now().toISOString();
|
|
19610
19964
|
entry.resolved_by = "system_ttl";
|
|
19965
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19611
19966
|
await this.persist(entry);
|
|
19612
19967
|
this.auditLog.append(
|
|
19613
19968
|
"l2",
|
|
@@ -19654,6 +20009,10 @@ var ApprovalAggregator = class {
|
|
|
19654
20009
|
this.entries.set(entry.aggregator_id, entry);
|
|
19655
20010
|
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19656
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
|
+
}
|
|
19657
20016
|
} catch {
|
|
19658
20017
|
}
|
|
19659
20018
|
}
|
|
@@ -19826,6 +20185,143 @@ function makeRedirectResolverFromPolicySupplier(supplier) {
|
|
|
19826
20185
|
};
|
|
19827
20186
|
}
|
|
19828
20187
|
|
|
20188
|
+
// src/principal-policy/aggregator-store.ts
|
|
20189
|
+
init_encryption();
|
|
20190
|
+
init_encoding();
|
|
20191
|
+
var AGGREGATOR_PAYLOAD_NAMESPACE = "_approval_aggregator_payloads";
|
|
20192
|
+
var AGGREGATOR_PAYLOAD_KEY_PREFIX = "payload.";
|
|
20193
|
+
var HKDF_INFO = "l2-approval-aggregator-payload-v1";
|
|
20194
|
+
var DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS = 30;
|
|
20195
|
+
var MAX_BUNDLE_BYTES2 = 4 * 1024 * 1024;
|
|
20196
|
+
var AggregatorPayloadStore = class {
|
|
20197
|
+
storage;
|
|
20198
|
+
encryptionKey;
|
|
20199
|
+
fortressId;
|
|
20200
|
+
retentionDays;
|
|
20201
|
+
constructor(opts) {
|
|
20202
|
+
this.storage = opts.storage;
|
|
20203
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO);
|
|
20204
|
+
this.fortressId = opts.fortressId;
|
|
20205
|
+
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS;
|
|
20206
|
+
}
|
|
20207
|
+
/**
|
|
20208
|
+
* Persist `payload` under the given aggregator_id. Idempotent; calling
|
|
20209
|
+
* twice with the same id rewrites the bundle (retention_until is
|
|
20210
|
+
* recomputed). Returns the bundle's retention_until ISO-8601 timestamp
|
|
20211
|
+
* so callers can log it.
|
|
20212
|
+
*/
|
|
20213
|
+
async savePayload(aggregatorId, payload) {
|
|
20214
|
+
const now = /* @__PURE__ */ new Date();
|
|
20215
|
+
const retentionMs = this.retentionDays * 24 * 60 * 60 * 1e3;
|
|
20216
|
+
const retentionUntil = new Date(now.getTime() + retentionMs);
|
|
20217
|
+
const bundle = {
|
|
20218
|
+
version: 1,
|
|
20219
|
+
aggregator_id: aggregatorId,
|
|
20220
|
+
fortress_id: this.fortressId,
|
|
20221
|
+
created_at: now.toISOString(),
|
|
20222
|
+
retention_until: retentionUntil.toISOString(),
|
|
20223
|
+
payload
|
|
20224
|
+
};
|
|
20225
|
+
const aad = stringToBytes(aggregatorId);
|
|
20226
|
+
const plaintext = stringToBytes(JSON.stringify(bundle));
|
|
20227
|
+
const envelope = encrypt(plaintext, this.encryptionKey, aad);
|
|
20228
|
+
await this.storage.write(
|
|
20229
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20230
|
+
payloadKey(aggregatorId),
|
|
20231
|
+
stringToBytes(JSON.stringify(envelope))
|
|
20232
|
+
);
|
|
20233
|
+
return bundle.retention_until;
|
|
20234
|
+
}
|
|
20235
|
+
/**
|
|
20236
|
+
* Read the persisted payload for the aggregator_id. Returns null if no
|
|
20237
|
+
* bundle exists, the bundle is corrupted, or AAD binding fails.
|
|
20238
|
+
*/
|
|
20239
|
+
async loadPayload(aggregatorId) {
|
|
20240
|
+
const key = payloadKey(aggregatorId);
|
|
20241
|
+
let raw;
|
|
20242
|
+
try {
|
|
20243
|
+
raw = await this.storage.read(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20244
|
+
} catch {
|
|
20245
|
+
return null;
|
|
20246
|
+
}
|
|
20247
|
+
if (!raw) return null;
|
|
20248
|
+
if (raw.length > MAX_BUNDLE_BYTES2) return null;
|
|
20249
|
+
try {
|
|
20250
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20251
|
+
const aad = stringToBytes(aggregatorId);
|
|
20252
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20253
|
+
const parsed = JSON.parse(
|
|
20254
|
+
bytesToString(plaintext)
|
|
20255
|
+
);
|
|
20256
|
+
if (parsed.version !== 1) return null;
|
|
20257
|
+
if (parsed.aggregator_id !== aggregatorId) return null;
|
|
20258
|
+
return parsed.payload;
|
|
20259
|
+
} catch {
|
|
20260
|
+
return null;
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
20263
|
+
/**
|
|
20264
|
+
* Delete the persisted payload. Returns true when a bundle was removed,
|
|
20265
|
+
* false when none existed.
|
|
20266
|
+
*/
|
|
20267
|
+
async deletePayload(aggregatorId) {
|
|
20268
|
+
const key = payloadKey(aggregatorId);
|
|
20269
|
+
const existed = await this.storage.exists(
|
|
20270
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20271
|
+
key
|
|
20272
|
+
);
|
|
20273
|
+
if (!existed) return false;
|
|
20274
|
+
try {
|
|
20275
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20276
|
+
} catch {
|
|
20277
|
+
return false;
|
|
20278
|
+
}
|
|
20279
|
+
return true;
|
|
20280
|
+
}
|
|
20281
|
+
/**
|
|
20282
|
+
* Drop expired payload bundles. Returns the count of bundles pruned.
|
|
20283
|
+
* Caller wires this into the cocoon-unlock initialization path.
|
|
20284
|
+
*/
|
|
20285
|
+
async pruneExpired(now) {
|
|
20286
|
+
const cutoff = (now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
20287
|
+
const entries = await this.storage.list(
|
|
20288
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20289
|
+
AGGREGATOR_PAYLOAD_KEY_PREFIX
|
|
20290
|
+
);
|
|
20291
|
+
let pruned = 0;
|
|
20292
|
+
for (const meta of entries) {
|
|
20293
|
+
const aggregatorId = stripKeyPrefix(meta.key);
|
|
20294
|
+
if (aggregatorId === null) continue;
|
|
20295
|
+
const raw = await this.storage.read(
|
|
20296
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20297
|
+
meta.key
|
|
20298
|
+
);
|
|
20299
|
+
if (!raw) continue;
|
|
20300
|
+
try {
|
|
20301
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20302
|
+
const aad = stringToBytes(aggregatorId);
|
|
20303
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20304
|
+
const parsed = JSON.parse(
|
|
20305
|
+
bytesToString(plaintext)
|
|
20306
|
+
);
|
|
20307
|
+
if (parsed.retention_until <= cutoff) {
|
|
20308
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, meta.key);
|
|
20309
|
+
pruned += 1;
|
|
20310
|
+
}
|
|
20311
|
+
} catch {
|
|
20312
|
+
}
|
|
20313
|
+
}
|
|
20314
|
+
return { pruned };
|
|
20315
|
+
}
|
|
20316
|
+
};
|
|
20317
|
+
function payloadKey(aggregatorId) {
|
|
20318
|
+
return `${AGGREGATOR_PAYLOAD_KEY_PREFIX}${aggregatorId}`;
|
|
20319
|
+
}
|
|
20320
|
+
function stripKeyPrefix(key) {
|
|
20321
|
+
if (!key.startsWith(AGGREGATOR_PAYLOAD_KEY_PREFIX)) return null;
|
|
20322
|
+
return key.slice(AGGREGATOR_PAYLOAD_KEY_PREFIX.length);
|
|
20323
|
+
}
|
|
20324
|
+
|
|
19829
20325
|
// src/principal-policy/tools.ts
|
|
19830
20326
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
19831
20327
|
return [
|
|
@@ -31967,6 +32463,13 @@ init_encoding();
|
|
|
31967
32463
|
// src/chat/operator-chat-audit-events.ts
|
|
31968
32464
|
var OPERATOR_CHAT_OPS = {
|
|
31969
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",
|
|
31970
32473
|
/**
|
|
31971
32474
|
* Operator viewed concierge thread history (WP-V1.3-9 Tau-1). Emitted
|
|
31972
32475
|
* when the operator hits the list-threads or read-thread route. Body
|
|
@@ -31986,20 +32489,831 @@ var OPERATOR_CHAT_OPS = {
|
|
|
31986
32489
|
* turns; the concierge degrades to single-turn after emitting. Body
|
|
31987
32490
|
* carries thread_id + a stable failure_reason enum.
|
|
31988
32491
|
*/
|
|
31989
|
-
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed"
|
|
32492
|
+
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed",
|
|
32493
|
+
/**
|
|
32494
|
+
* Concierge dynamic-context fetcher failed (WP-V1.3-9 Tau-3). Emitted
|
|
32495
|
+
* when a category fetcher throws while assembling the dynamic context
|
|
32496
|
+
* fold. The concierge omits that category and continues; the user-
|
|
32497
|
+
* facing query is never broken. Body carries category + failure_reason.
|
|
32498
|
+
*/
|
|
32499
|
+
CONCIERGE_CONTEXT_FETCHER_FAILED: "operator_concierge_context_fetcher_failed"
|
|
31990
32500
|
};
|
|
31991
32501
|
|
|
31992
32502
|
// src/chat/operator-chat-types.ts
|
|
31993
32503
|
var OPERATOR_CHAT_MAX_THREAD_LENGTH = 500;
|
|
31994
32504
|
var CONCIERGE_THREAD_KEY = "_fortress";
|
|
31995
32505
|
|
|
32506
|
+
// src/chat/concierge-context-router.ts
|
|
32507
|
+
var APPROX_CHARS_PER_TOKEN = 4;
|
|
32508
|
+
var DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET = 2e3;
|
|
32509
|
+
var DYNAMIC_CONTEXT_SECTION_HEADER = "## Live fortress context";
|
|
32510
|
+
var CONTEXT_CATEGORIES = [
|
|
32511
|
+
"templates",
|
|
32512
|
+
"agent_state",
|
|
32513
|
+
"agent_activity",
|
|
32514
|
+
"audit_log",
|
|
32515
|
+
"sentinel_findings",
|
|
32516
|
+
"anomaly_alerts",
|
|
32517
|
+
"recent_receipts",
|
|
32518
|
+
"verascore_deltas"
|
|
32519
|
+
];
|
|
32520
|
+
function phrasePattern(phrase) {
|
|
32521
|
+
const escaped = phrase.toLowerCase().split(/\s+/).map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
|
|
32522
|
+
return { source: `\\b${escaped}\\b`, phrase };
|
|
32523
|
+
}
|
|
32524
|
+
var CATEGORY_KEYWORDS = [
|
|
32525
|
+
{
|
|
32526
|
+
category: "templates",
|
|
32527
|
+
patterns: [
|
|
32528
|
+
"templates",
|
|
32529
|
+
"template",
|
|
32530
|
+
"channel templates",
|
|
32531
|
+
"channel template",
|
|
32532
|
+
"list templates",
|
|
32533
|
+
"available templates",
|
|
32534
|
+
"what templates"
|
|
32535
|
+
].map(phrasePattern)
|
|
32536
|
+
},
|
|
32537
|
+
{
|
|
32538
|
+
category: "agent_state",
|
|
32539
|
+
patterns: [
|
|
32540
|
+
"state",
|
|
32541
|
+
"status",
|
|
32542
|
+
"agent state",
|
|
32543
|
+
"agent status",
|
|
32544
|
+
"status of agent",
|
|
32545
|
+
"status of agents",
|
|
32546
|
+
"state of",
|
|
32547
|
+
"doing"
|
|
32548
|
+
].map(phrasePattern)
|
|
32549
|
+
},
|
|
32550
|
+
{
|
|
32551
|
+
category: "agent_activity",
|
|
32552
|
+
patterns: [
|
|
32553
|
+
"activity",
|
|
32554
|
+
"agent activity",
|
|
32555
|
+
"what did",
|
|
32556
|
+
"recent activity"
|
|
32557
|
+
].map(phrasePattern)
|
|
32558
|
+
},
|
|
32559
|
+
{
|
|
32560
|
+
category: "audit_log",
|
|
32561
|
+
patterns: [
|
|
32562
|
+
"audit log",
|
|
32563
|
+
"audit",
|
|
32564
|
+
"log entry",
|
|
32565
|
+
"log entries",
|
|
32566
|
+
"what happened",
|
|
32567
|
+
"show me events",
|
|
32568
|
+
"event class"
|
|
32569
|
+
].map(phrasePattern)
|
|
32570
|
+
},
|
|
32571
|
+
{
|
|
32572
|
+
category: "sentinel_findings",
|
|
32573
|
+
patterns: [
|
|
32574
|
+
"sentinel",
|
|
32575
|
+
"sentinels",
|
|
32576
|
+
"warning",
|
|
32577
|
+
"warnings",
|
|
32578
|
+
"alert",
|
|
32579
|
+
"alerts",
|
|
32580
|
+
"whats wrong",
|
|
32581
|
+
"what's wrong",
|
|
32582
|
+
"findings"
|
|
32583
|
+
].map(phrasePattern)
|
|
32584
|
+
},
|
|
32585
|
+
{
|
|
32586
|
+
category: "anomaly_alerts",
|
|
32587
|
+
patterns: [
|
|
32588
|
+
"anomaly",
|
|
32589
|
+
"anomalies",
|
|
32590
|
+
"spike",
|
|
32591
|
+
"unusual",
|
|
32592
|
+
"outlier"
|
|
32593
|
+
].map(phrasePattern)
|
|
32594
|
+
},
|
|
32595
|
+
{
|
|
32596
|
+
category: "recent_receipts",
|
|
32597
|
+
patterns: [
|
|
32598
|
+
"receipt",
|
|
32599
|
+
"receipts",
|
|
32600
|
+
"concordia",
|
|
32601
|
+
"commitment",
|
|
32602
|
+
"commitments",
|
|
32603
|
+
"chain",
|
|
32604
|
+
"chains"
|
|
32605
|
+
].map(phrasePattern)
|
|
32606
|
+
},
|
|
32607
|
+
{
|
|
32608
|
+
category: "verascore_deltas",
|
|
32609
|
+
patterns: [
|
|
32610
|
+
"verascore",
|
|
32611
|
+
"vera score",
|
|
32612
|
+
"trust score",
|
|
32613
|
+
"reputation"
|
|
32614
|
+
].map(phrasePattern)
|
|
32615
|
+
}
|
|
32616
|
+
];
|
|
32617
|
+
function extractAgentNameHint(query) {
|
|
32618
|
+
const agentPattern = /\bagent\s+["']?([A-Za-z][\w-]{0,40})["']?/i;
|
|
32619
|
+
const m = query.match(agentPattern);
|
|
32620
|
+
if (m && m[1]) return m[1];
|
|
32621
|
+
const quoted = query.match(/["']([A-Za-z][\w-]{0,40})["']/);
|
|
32622
|
+
if (quoted && quoted[1]) return quoted[1];
|
|
32623
|
+
return null;
|
|
32624
|
+
}
|
|
32625
|
+
var TRIVIAL_GREETINGS = /* @__PURE__ */ new Set([
|
|
32626
|
+
"hi",
|
|
32627
|
+
"hello",
|
|
32628
|
+
"hey",
|
|
32629
|
+
"yo",
|
|
32630
|
+
"ok",
|
|
32631
|
+
"thanks",
|
|
32632
|
+
"thx",
|
|
32633
|
+
"thank you"
|
|
32634
|
+
]);
|
|
32635
|
+
function isTrivialQuery(query) {
|
|
32636
|
+
const norm = query.trim().toLowerCase();
|
|
32637
|
+
if (norm.length === 0) return true;
|
|
32638
|
+
if (norm.length < 8) return true;
|
|
32639
|
+
return TRIVIAL_GREETINGS.has(norm);
|
|
32640
|
+
}
|
|
32641
|
+
function classifyQuery(query, parsedGrammar) {
|
|
32642
|
+
const normalized = query.toLowerCase();
|
|
32643
|
+
const matches = [];
|
|
32644
|
+
const grammarAgent = parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null;
|
|
32645
|
+
for (const spec of CATEGORY_KEYWORDS) {
|
|
32646
|
+
const matchedPhrases = [];
|
|
32647
|
+
for (const pattern of spec.patterns) {
|
|
32648
|
+
if (matchedPhrases.includes(pattern.phrase)) continue;
|
|
32649
|
+
const re = new RegExp(pattern.source, "i");
|
|
32650
|
+
if (re.test(normalized)) {
|
|
32651
|
+
matchedPhrases.push(pattern.phrase);
|
|
32652
|
+
}
|
|
32653
|
+
}
|
|
32654
|
+
if (matchedPhrases.length === 0) continue;
|
|
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;
|
|
32658
|
+
matches.push({
|
|
32659
|
+
category: spec.category,
|
|
32660
|
+
confidence,
|
|
32661
|
+
matched_keywords: matchedPhrases,
|
|
32662
|
+
agent_name_hint,
|
|
32663
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32664
|
+
});
|
|
32665
|
+
}
|
|
32666
|
+
matches.sort((a, b) => {
|
|
32667
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
32668
|
+
return CONTEXT_CATEGORIES.indexOf(a.category) - CONTEXT_CATEGORIES.indexOf(b.category);
|
|
32669
|
+
});
|
|
32670
|
+
return matches;
|
|
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
|
+
}
|
|
32691
|
+
function approxTokenLen(text) {
|
|
32692
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32693
|
+
}
|
|
32694
|
+
var CATEGORY_LABELS = {
|
|
32695
|
+
templates: "Templates",
|
|
32696
|
+
agent_state: "Agent state",
|
|
32697
|
+
agent_activity: "Agent activity",
|
|
32698
|
+
audit_log: "Audit log",
|
|
32699
|
+
sentinel_findings: "Sentinel findings",
|
|
32700
|
+
anomaly_alerts: "Anomaly alerts",
|
|
32701
|
+
recent_receipts: "Recent receipts",
|
|
32702
|
+
verascore_deltas: "Verascore deltas"
|
|
32703
|
+
};
|
|
32704
|
+
async function runFetcher(match, fetchers, hints) {
|
|
32705
|
+
switch (match.category) {
|
|
32706
|
+
case "templates":
|
|
32707
|
+
return fetchers.templates(hints);
|
|
32708
|
+
case "agent_state":
|
|
32709
|
+
return fetchers.agent_state(match.agent_name_hint, hints);
|
|
32710
|
+
case "agent_activity":
|
|
32711
|
+
return fetchers.agent_activity(match.agent_name_hint, hints);
|
|
32712
|
+
case "audit_log":
|
|
32713
|
+
return fetchers.audit_log(hints);
|
|
32714
|
+
case "sentinel_findings":
|
|
32715
|
+
return fetchers.sentinel_findings(hints);
|
|
32716
|
+
case "anomaly_alerts":
|
|
32717
|
+
return fetchers.anomaly_alerts(hints);
|
|
32718
|
+
case "recent_receipts":
|
|
32719
|
+
return fetchers.recent_receipts(hints);
|
|
32720
|
+
case "verascore_deltas":
|
|
32721
|
+
return fetchers.verascore_deltas(hints);
|
|
32722
|
+
}
|
|
32723
|
+
}
|
|
32724
|
+
function trivialMatch(category, parsedGrammar) {
|
|
32725
|
+
return {
|
|
32726
|
+
category,
|
|
32727
|
+
confidence: 0.5,
|
|
32728
|
+
matched_keywords: ["llm-assist"],
|
|
32729
|
+
agent_name_hint: parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null,
|
|
32730
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32731
|
+
};
|
|
32732
|
+
}
|
|
32733
|
+
async function foldContext(query, fetchers, opts) {
|
|
32734
|
+
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32735
|
+
const parsed = opts?.parsed ?? null;
|
|
32736
|
+
const hints = fetcherHintsFromGrammar(parsed);
|
|
32737
|
+
let matches = classifyQuery(query, parsed);
|
|
32738
|
+
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32739
|
+
try {
|
|
32740
|
+
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32741
|
+
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32742
|
+
matches = [trivialMatch(picked, parsed)];
|
|
32743
|
+
}
|
|
32744
|
+
} catch {
|
|
32745
|
+
}
|
|
32746
|
+
}
|
|
32747
|
+
if (matches.length === 0) {
|
|
32748
|
+
return { section: "", categoriesIncluded: [] };
|
|
32749
|
+
}
|
|
32750
|
+
const attempts = [];
|
|
32751
|
+
for (const match of matches) {
|
|
32752
|
+
try {
|
|
32753
|
+
const text = await runFetcher(match, fetchers, hints);
|
|
32754
|
+
const trimmed = text.trim();
|
|
32755
|
+
if (trimmed.length > 0) {
|
|
32756
|
+
attempts.push({ category: match.category, text: trimmed });
|
|
32757
|
+
}
|
|
32758
|
+
} catch (err) {
|
|
32759
|
+
opts?.onFetcherFailure?.(match.category, err);
|
|
32760
|
+
}
|
|
32761
|
+
}
|
|
32762
|
+
if (attempts.length === 0) {
|
|
32763
|
+
return { section: "", categoriesIncluded: [] };
|
|
32764
|
+
}
|
|
32765
|
+
const headerTokens = approxTokenLen(`${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32766
|
+
`);
|
|
32767
|
+
const sepTokens = approxTokenLen("\n\n");
|
|
32768
|
+
let runningTokens = headerTokens;
|
|
32769
|
+
const kept = [];
|
|
32770
|
+
for (const attempt of attempts) {
|
|
32771
|
+
const block = `### ${CATEGORY_LABELS[attempt.category]}
|
|
32772
|
+
${attempt.text}`;
|
|
32773
|
+
const tokens = approxTokenLen(block) + (kept.length > 0 ? sepTokens : 0);
|
|
32774
|
+
if (kept.length === 0) {
|
|
32775
|
+
kept.push(attempt);
|
|
32776
|
+
runningTokens += tokens;
|
|
32777
|
+
continue;
|
|
32778
|
+
}
|
|
32779
|
+
if (runningTokens + tokens > budget) break;
|
|
32780
|
+
kept.push(attempt);
|
|
32781
|
+
runningTokens += tokens;
|
|
32782
|
+
}
|
|
32783
|
+
const blocks = kept.map(
|
|
32784
|
+
(k) => `### ${CATEGORY_LABELS[k.category]}
|
|
32785
|
+
${k.text}`
|
|
32786
|
+
);
|
|
32787
|
+
const section = `${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32788
|
+
${blocks.join("\n\n")}`;
|
|
32789
|
+
return {
|
|
32790
|
+
section,
|
|
32791
|
+
categoriesIncluded: kept.map((k) => k.category)
|
|
32792
|
+
};
|
|
32793
|
+
}
|
|
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
|
+
|
|
31996
33309
|
// src/chat/operator-chat-service.ts
|
|
31997
33310
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
31998
33311
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
31999
33312
|
var DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS = 24 * 60 * 60 * 1e3;
|
|
32000
33313
|
var DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET = 500;
|
|
32001
33314
|
var DEFAULT_CONCIERGE_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32002
|
-
|
|
33315
|
+
var DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET = 2e3;
|
|
33316
|
+
function approxTokenLen2(text) {
|
|
32003
33317
|
return Math.ceil(text.length / 4);
|
|
32004
33318
|
}
|
|
32005
33319
|
var SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
|
|
@@ -32043,6 +33357,11 @@ var OperatorChatService = class {
|
|
|
32043
33357
|
historyTokenBudget;
|
|
32044
33358
|
sessionTtlMs;
|
|
32045
33359
|
clock;
|
|
33360
|
+
contextFetchers;
|
|
33361
|
+
contextLlmAssist;
|
|
33362
|
+
dynamicContextBudget;
|
|
33363
|
+
agentRegistry;
|
|
33364
|
+
grammarLlmAssist;
|
|
32046
33365
|
/**
|
|
32047
33366
|
* In-memory thread_id assigned to the active concierge session.
|
|
32048
33367
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32074,6 +33393,19 @@ var OperatorChatService = class {
|
|
|
32074
33393
|
this.historyTokenBudget = deps.conciergeHistoryTokenBudget !== void 0 && deps.conciergeHistoryTokenBudget > 0 ? deps.conciergeHistoryTokenBudget : DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET;
|
|
32075
33394
|
this.sessionTtlMs = deps.conciergeSessionTtlMs !== void 0 && deps.conciergeSessionTtlMs > 0 ? deps.conciergeSessionTtlMs : DEFAULT_CONCIERGE_SESSION_TTL_MS;
|
|
32076
33395
|
this.clock = deps.conciergeClock ?? (() => Date.now());
|
|
33396
|
+
if (deps.conciergeContextFetchers) {
|
|
33397
|
+
this.contextFetchers = deps.conciergeContextFetchers;
|
|
33398
|
+
}
|
|
33399
|
+
if (deps.conciergeContextLlmAssist) {
|
|
33400
|
+
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
33401
|
+
}
|
|
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
|
+
}
|
|
32077
33409
|
}
|
|
32078
33410
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32079
33411
|
/**
|
|
@@ -32132,11 +33464,13 @@ var OperatorChatService = class {
|
|
|
32132
33464
|
await this.memory.appendTurn(threadId, "user", trimmed).catch(() => {
|
|
32133
33465
|
});
|
|
32134
33466
|
}
|
|
33467
|
+
const parsedGrammar = await this.runGrammarParse(filterResult.filtered);
|
|
32135
33468
|
const start = Date.now();
|
|
32136
33469
|
let conciergeBody;
|
|
32137
33470
|
let servedBy = "disabled";
|
|
32138
33471
|
let displayLabel = "Concierge: substrate not configured";
|
|
32139
33472
|
let outcome = "substrate_disabled";
|
|
33473
|
+
let dynamicCategoriesIncluded = [];
|
|
32140
33474
|
if (!this.substrateSelector) {
|
|
32141
33475
|
conciergeBody = "Concierge unavailable. The substrate selector is not configured for this fortress. Pick a substrate in the Policy center to enable concierge replies.";
|
|
32142
33476
|
} else {
|
|
@@ -32148,7 +33482,15 @@ var OperatorChatService = class {
|
|
|
32148
33482
|
conciergeBody = "Concierge unavailable. The chosen substrate does not support summarization. Pick a different substrate in the Policy center.";
|
|
32149
33483
|
outcome = "substrate_disabled";
|
|
32150
33484
|
} else {
|
|
32151
|
-
const
|
|
33485
|
+
const dynamicResult = await this.runDynamicContextFold(
|
|
33486
|
+
filterResult.filtered,
|
|
33487
|
+
parsedGrammar
|
|
33488
|
+
);
|
|
33489
|
+
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
33490
|
+
const context = await this.assembleConciergeContext(
|
|
33491
|
+
priorTurns,
|
|
33492
|
+
dynamicResult.section
|
|
33493
|
+
);
|
|
32152
33494
|
const response = await this.substrateSelector.invokeSummarize(
|
|
32153
33495
|
"concierge",
|
|
32154
33496
|
{
|
|
@@ -32212,7 +33554,9 @@ var OperatorChatService = class {
|
|
|
32212
33554
|
...assistantTurnId !== void 0 ? { turn_index: assistantTurnId } : {},
|
|
32213
33555
|
...this.memory ? {
|
|
32214
33556
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32215
|
-
} : {}
|
|
33557
|
+
} : {},
|
|
33558
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {},
|
|
33559
|
+
parsed_grammar: auditSafeSummary(parsedGrammar)
|
|
32216
33560
|
};
|
|
32217
33561
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32218
33562
|
return {
|
|
@@ -32363,10 +33707,13 @@ var OperatorChatService = class {
|
|
|
32363
33707
|
* ## Sanctuary reference
|
|
32364
33708
|
* <static domain reference block>
|
|
32365
33709
|
*
|
|
33710
|
+
* ## Live fortress context ← WP-V1.3-9 Tau-3, when present
|
|
33711
|
+
* ### <Category>
|
|
33712
|
+
* <fetcher payload>
|
|
33713
|
+
*
|
|
32366
33714
|
* ## Prior conversation ← WP-V1.3-9 Tau-2, when present
|
|
32367
33715
|
* OPERATOR: ...
|
|
32368
33716
|
* CONCIERGE: ...
|
|
32369
|
-
* ---
|
|
32370
33717
|
*
|
|
32371
33718
|
* ## Recent activity
|
|
32372
33719
|
* <recentActivity output>
|
|
@@ -32385,13 +33732,14 @@ var OperatorChatService = class {
|
|
|
32385
33732
|
* if available; the v1.2 selector does not expose one, so structured
|
|
32386
33733
|
* serialization is the canonical path for v1.3.
|
|
32387
33734
|
*/
|
|
32388
|
-
async assembleConciergeContext(priorTurns = []) {
|
|
33735
|
+
async assembleConciergeContext(priorTurns = [], dynamicSection = "") {
|
|
32389
33736
|
const ref = `## Sanctuary reference
|
|
32390
33737
|
${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
32391
33738
|
const priorSection = this.formatPriorTurnsSection(priorTurns);
|
|
32392
33739
|
if (!this.contextProviders) {
|
|
32393
33740
|
return [
|
|
32394
33741
|
ref,
|
|
33742
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32395
33743
|
...priorSection ? [priorSection] : [],
|
|
32396
33744
|
"## Recent activity\n(no providers wired)",
|
|
32397
33745
|
"## Wrapped agents\n(no providers wired)",
|
|
@@ -32405,6 +33753,7 @@ ${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
|
32405
33753
|
]);
|
|
32406
33754
|
return [
|
|
32407
33755
|
ref,
|
|
33756
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32408
33757
|
...priorSection ? [priorSection] : [],
|
|
32409
33758
|
`## Recent activity
|
|
32410
33759
|
${activity}`,
|
|
@@ -32414,6 +33763,69 @@ ${agents}`,
|
|
|
32414
33763
|
${inbox}`
|
|
32415
33764
|
].join("\n\n");
|
|
32416
33765
|
}
|
|
33766
|
+
/**
|
|
33767
|
+
* Run the WP-V1.3-9 Tau-3 dynamic-context fold for a single round-
|
|
33768
|
+
* trip. Fail-soft on every axis: missing fetchers short-circuit to
|
|
33769
|
+
* an empty fold, fetcher failures emit a per-category audit event
|
|
33770
|
+
* and are omitted from the rendered section, an LLM-assist failure
|
|
33771
|
+
* proceeds with no fold. Returns the rendered section + the list of
|
|
33772
|
+
* categories whose data made it into the section (used for the
|
|
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.
|
|
33778
|
+
*/
|
|
33779
|
+
async runDynamicContextFold(query, parsedGrammar) {
|
|
33780
|
+
if (!this.contextFetchers) {
|
|
33781
|
+
return { section: "", categoriesIncluded: [] };
|
|
33782
|
+
}
|
|
33783
|
+
const result = await foldContext(query, this.contextFetchers, {
|
|
33784
|
+
maxTokens: this.dynamicContextBudget,
|
|
33785
|
+
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33786
|
+
onFetcherFailure: (category, error) => {
|
|
33787
|
+
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33788
|
+
},
|
|
33789
|
+
parsed: parsedGrammar
|
|
33790
|
+
});
|
|
33791
|
+
return result;
|
|
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
|
+
}
|
|
33806
|
+
/**
|
|
33807
|
+
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33808
|
+
* of the fold path so the dynamic-context handler stays readable.
|
|
33809
|
+
* Emits with `result: "failure"` since the named category dropped
|
|
33810
|
+
* from the rendered section for this round-trip.
|
|
33811
|
+
*/
|
|
33812
|
+
emitContextFetcherFailed(category, failureReason) {
|
|
33813
|
+
const payload = {
|
|
33814
|
+
version: "1.2",
|
|
33815
|
+
event_id: makeEventId("conc-ctxfail"),
|
|
33816
|
+
emitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33817
|
+
identity_id: this.identityId,
|
|
33818
|
+
kind: "operator_concierge_context_fetcher_failed",
|
|
33819
|
+
surface: "concierge",
|
|
33820
|
+
category,
|
|
33821
|
+
failure_reason: failureReason
|
|
33822
|
+
};
|
|
33823
|
+
this.emit(
|
|
33824
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
33825
|
+
payload,
|
|
33826
|
+
"failure"
|
|
33827
|
+
);
|
|
33828
|
+
}
|
|
32417
33829
|
/**
|
|
32418
33830
|
* Render the prior-conversation section with token-budget enforcement
|
|
32419
33831
|
* (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
|
|
@@ -32424,14 +33836,14 @@ ${inbox}`
|
|
|
32424
33836
|
if (turns.length === 0) return "";
|
|
32425
33837
|
const HEADER = "## Prior conversation";
|
|
32426
33838
|
const lines = turns.map(formatPriorTurnLine);
|
|
32427
|
-
const headerTokens =
|
|
33839
|
+
const headerTokens = approxTokenLen2(`${HEADER}
|
|
32428
33840
|
`);
|
|
32429
|
-
const sepTokens =
|
|
33841
|
+
const sepTokens = approxTokenLen2("\n");
|
|
32430
33842
|
let runningTokens = headerTokens;
|
|
32431
33843
|
let runningLines = [];
|
|
32432
33844
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
32433
33845
|
const line = lines[i];
|
|
32434
|
-
const tokens =
|
|
33846
|
+
const tokens = approxTokenLen2(line) + (runningLines.length > 0 ? sepTokens : 0);
|
|
32435
33847
|
if (runningTokens + tokens > this.historyTokenBudget) break;
|
|
32436
33848
|
runningTokens += tokens;
|
|
32437
33849
|
runningLines.push(line);
|
|
@@ -32455,6 +33867,17 @@ ${runningLines.join("\n")}`;
|
|
|
32455
33867
|
function makeEventId(prefix) {
|
|
32456
33868
|
return `${prefix}-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
32457
33869
|
}
|
|
33870
|
+
function classifyFetcherError(error) {
|
|
33871
|
+
const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
33872
|
+
if (msg.includes("timeout") || msg.includes("timed out")) return "timeout";
|
|
33873
|
+
if (msg.includes("schema") || msg.includes("invalid shape")) {
|
|
33874
|
+
return "schema_mismatch";
|
|
33875
|
+
}
|
|
33876
|
+
if (msg.includes("io") || msg.includes("read") || msg.includes("enoent") || msg.includes("eacces")) {
|
|
33877
|
+
return "io_failed";
|
|
33878
|
+
}
|
|
33879
|
+
return "unknown";
|
|
33880
|
+
}
|
|
32458
33881
|
function formatPriorTurnLine(turn) {
|
|
32459
33882
|
const label = turn.role === "user" ? "OPERATOR" : "CONCIERGE";
|
|
32460
33883
|
return `${label}: ${turn.content}`;
|
|
@@ -32467,7 +33890,7 @@ function hashOf(input) {
|
|
|
32467
33890
|
init_encryption();
|
|
32468
33891
|
init_encoding();
|
|
32469
33892
|
var OPERATOR_CHAT_NAMESPACE = "_chat";
|
|
32470
|
-
var
|
|
33893
|
+
var HKDF_INFO2 = "operator-chat-store-v1";
|
|
32471
33894
|
function chatStorageKey(surface, threadKey) {
|
|
32472
33895
|
return `${surface}.${threadKey}`;
|
|
32473
33896
|
}
|
|
@@ -32476,7 +33899,7 @@ var OperatorChatStore = class {
|
|
|
32476
33899
|
encryptionKey;
|
|
32477
33900
|
constructor(storage, masterKey) {
|
|
32478
33901
|
this.storage = storage;
|
|
32479
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33902
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO2);
|
|
32480
33903
|
}
|
|
32481
33904
|
/**
|
|
32482
33905
|
* Load a thread. Returns null if no record exists or if the on-disk
|
|
@@ -32560,9 +33983,9 @@ init_encryption();
|
|
|
32560
33983
|
init_encoding();
|
|
32561
33984
|
var CONCIERGE_MEMORY_NAMESPACE = "_chat";
|
|
32562
33985
|
var CONCIERGE_MEMORY_KEY_PREFIX = "concierge_memory.";
|
|
32563
|
-
var
|
|
33986
|
+
var HKDF_INFO3 = "concierge-memory-store-v1";
|
|
32564
33987
|
var DEFAULT_CONCIERGE_RETENTION_DAYS = 30;
|
|
32565
|
-
var
|
|
33988
|
+
var MAX_BUNDLE_BYTES3 = 4 * 1024 * 1024;
|
|
32566
33989
|
var ConciergeMemoryStore = class {
|
|
32567
33990
|
storage;
|
|
32568
33991
|
encryptionKey;
|
|
@@ -32571,7 +33994,7 @@ var ConciergeMemoryStore = class {
|
|
|
32571
33994
|
locks;
|
|
32572
33995
|
constructor(opts) {
|
|
32573
33996
|
this.storage = opts.storage;
|
|
32574
|
-
this.encryptionKey = derivePurposeKey(opts.masterKey,
|
|
33997
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO3);
|
|
32575
33998
|
this.fortressId = opts.fortressId;
|
|
32576
33999
|
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_CONCIERGE_RETENTION_DAYS;
|
|
32577
34000
|
this.locks = /* @__PURE__ */ new Map();
|
|
@@ -32650,7 +34073,7 @@ var ConciergeMemoryStore = class {
|
|
|
32650
34073
|
return { ok: false, reason: "io_failed" };
|
|
32651
34074
|
}
|
|
32652
34075
|
if (!raw) return { ok: true, turns: [] };
|
|
32653
|
-
if (raw.length >
|
|
34076
|
+
if (raw.length > MAX_BUNDLE_BYTES3) {
|
|
32654
34077
|
return { ok: false, reason: "oversize_bundle" };
|
|
32655
34078
|
}
|
|
32656
34079
|
let envelope;
|
|
@@ -32697,7 +34120,7 @@ var ConciergeMemoryStore = class {
|
|
|
32697
34120
|
);
|
|
32698
34121
|
const summaries = [];
|
|
32699
34122
|
for (const meta of entries) {
|
|
32700
|
-
const threadId =
|
|
34123
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32701
34124
|
if (threadId === null) continue;
|
|
32702
34125
|
const bundle = await this.loadBundle(threadId);
|
|
32703
34126
|
if (!bundle || bundle.turns.length === 0) continue;
|
|
@@ -32750,7 +34173,7 @@ var ConciergeMemoryStore = class {
|
|
|
32750
34173
|
);
|
|
32751
34174
|
let pruned = 0;
|
|
32752
34175
|
for (const meta of entries) {
|
|
32753
|
-
const threadId =
|
|
34176
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32754
34177
|
if (threadId === null) continue;
|
|
32755
34178
|
pruned += await this.withLock(threadId, async () => {
|
|
32756
34179
|
const bundle = await this.loadBundle(threadId);
|
|
@@ -32781,7 +34204,7 @@ var ConciergeMemoryStore = class {
|
|
|
32781
34204
|
return null;
|
|
32782
34205
|
}
|
|
32783
34206
|
if (!raw) return null;
|
|
32784
|
-
if (raw.length >
|
|
34207
|
+
if (raw.length > MAX_BUNDLE_BYTES3) return null;
|
|
32785
34208
|
try {
|
|
32786
34209
|
const envelope = JSON.parse(bytesToString(raw));
|
|
32787
34210
|
const aad = stringToBytes(threadId);
|
|
@@ -32834,7 +34257,7 @@ var ConciergeMemoryStore = class {
|
|
|
32834
34257
|
function bundleKey(threadId) {
|
|
32835
34258
|
return `${CONCIERGE_MEMORY_KEY_PREFIX}${threadId}`;
|
|
32836
34259
|
}
|
|
32837
|
-
function
|
|
34260
|
+
function stripKeyPrefix2(key) {
|
|
32838
34261
|
if (!key.startsWith(CONCIERGE_MEMORY_KEY_PREFIX)) return null;
|
|
32839
34262
|
return key.slice(CONCIERGE_MEMORY_KEY_PREFIX.length);
|
|
32840
34263
|
}
|
|
@@ -32903,7 +34326,18 @@ function buildV11Bindings(inputs) {
|
|
|
32903
34326
|
registry
|
|
32904
34327
|
}),
|
|
32905
34328
|
conciergePiiFilter: buildConciergePiiFilter(),
|
|
32906
|
-
conciergeMemory
|
|
34329
|
+
conciergeMemory,
|
|
34330
|
+
conciergeContextFetchers: buildConciergeContextFetchers({
|
|
34331
|
+
auditLog: inputs.auditLog,
|
|
34332
|
+
identityId: inputs.identityId,
|
|
34333
|
+
registry
|
|
34334
|
+
}),
|
|
34335
|
+
...inputs.intelligenceSelector ? {
|
|
34336
|
+
conciergeContextLlmAssist: buildConciergeContextLlmAssist({
|
|
34337
|
+
selector: inputs.intelligenceSelector,
|
|
34338
|
+
identityId: inputs.identityId
|
|
34339
|
+
})
|
|
34340
|
+
} : {}
|
|
32907
34341
|
});
|
|
32908
34342
|
}
|
|
32909
34343
|
const hubService = new HubService({
|
|
@@ -32964,6 +34398,107 @@ function buildConciergeContextProviders(args) {
|
|
|
32964
34398
|
}
|
|
32965
34399
|
};
|
|
32966
34400
|
}
|
|
34401
|
+
function buildConciergeContextFetchers(args) {
|
|
34402
|
+
const empty = async () => "";
|
|
34403
|
+
return {
|
|
34404
|
+
templates: async () => {
|
|
34405
|
+
const entries = listTemplates();
|
|
34406
|
+
if (entries.length === 0) return "(no templates installed)";
|
|
34407
|
+
const lines = entries.map((e) => {
|
|
34408
|
+
const m = e.metadata;
|
|
34409
|
+
return `${m.name} (tier ${m.tier}, channel ${m.channel}, target ${m.target_archetype})`;
|
|
34410
|
+
});
|
|
34411
|
+
return lines.join("\n");
|
|
34412
|
+
},
|
|
34413
|
+
agent_state: async (agentNameHint) => {
|
|
34414
|
+
const records = args.registry.list({ identity_id: args.identityId });
|
|
34415
|
+
if (records.length === 0) return "(no wrapped agents)";
|
|
34416
|
+
const filtered = agentNameHint ? records.filter(
|
|
34417
|
+
(r) => r.agent_id.toLowerCase().includes(agentNameHint.toLowerCase()) || r.harness.toLowerCase().includes(agentNameHint.toLowerCase())
|
|
34418
|
+
) : records;
|
|
34419
|
+
const target = filtered.length > 0 ? filtered : records;
|
|
34420
|
+
const lines = target.slice(0, 20).map((r) => {
|
|
34421
|
+
const tmpl = typeof r.channel_template_id === "string" ? r.channel_template_id : "no_template";
|
|
34422
|
+
return `${r.agent_id} harness=${r.harness} status=${r.status} template=${tmpl}`;
|
|
34423
|
+
});
|
|
34424
|
+
return lines.join("\n");
|
|
34425
|
+
},
|
|
34426
|
+
agent_activity: async (agentNameHint) => {
|
|
34427
|
+
const result = await args.auditLog.query({ limit: 50 });
|
|
34428
|
+
const owned = result.entries.filter(
|
|
34429
|
+
(e) => e.identity_id === args.identityId
|
|
34430
|
+
);
|
|
34431
|
+
const filtered = agentNameHint ? owned.filter((e) => {
|
|
34432
|
+
const agentId = e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : "";
|
|
34433
|
+
return agentId.toLowerCase().includes(agentNameHint.toLowerCase());
|
|
34434
|
+
}) : owned;
|
|
34435
|
+
const tail = (filtered.length > 0 ? filtered : owned).slice(-20);
|
|
34436
|
+
if (tail.length === 0) return "(no activity)";
|
|
34437
|
+
return tail.map((e) => {
|
|
34438
|
+
const agentId = (e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : null) ?? "_fortress";
|
|
34439
|
+
return `${e.timestamp} ${e.layer}.${e.operation} agent=${agentId} result=${e.result}`;
|
|
34440
|
+
}).join("\n");
|
|
34441
|
+
},
|
|
34442
|
+
audit_log: async () => {
|
|
34443
|
+
const result = await args.auditLog.query({ limit: 30 });
|
|
34444
|
+
const owned = result.entries.filter(
|
|
34445
|
+
(e) => e.identity_id === args.identityId
|
|
34446
|
+
);
|
|
34447
|
+
if (owned.length === 0) return "(no audit log entries)";
|
|
34448
|
+
return owned.slice(-30).map(
|
|
34449
|
+
(e) => `${e.timestamp} ${e.layer}.${e.operation} result=${e.result}`
|
|
34450
|
+
).join("\n");
|
|
34451
|
+
},
|
|
34452
|
+
sentinel_findings: empty,
|
|
34453
|
+
anomaly_alerts: empty,
|
|
34454
|
+
recent_receipts: async () => {
|
|
34455
|
+
const result = await args.auditLog.query({ limit: 100 });
|
|
34456
|
+
const owned = result.entries.filter(
|
|
34457
|
+
(e) => e.identity_id === args.identityId && e.operation.startsWith("composition_")
|
|
34458
|
+
);
|
|
34459
|
+
if (owned.length === 0) return "(no recent composition events)";
|
|
34460
|
+
return owned.slice(-15).map((e) => `${e.timestamp} ${e.operation} result=${e.result}`).join("\n");
|
|
34461
|
+
},
|
|
34462
|
+
verascore_deltas: empty
|
|
34463
|
+
};
|
|
34464
|
+
}
|
|
34465
|
+
function buildConciergeContextLlmAssist(args) {
|
|
34466
|
+
return async (query, categories) => {
|
|
34467
|
+
const labelList = categories.map((c) => `- ${c}`).join("\n");
|
|
34468
|
+
const prompt = `You are a router. Classify the operator's query into one of the categories below or "none".
|
|
34469
|
+
Reply with exactly one token: one category name or "none".
|
|
34470
|
+
|
|
34471
|
+
Categories:
|
|
34472
|
+
${labelList}
|
|
34473
|
+
|
|
34474
|
+
Query: ${query}
|
|
34475
|
+
|
|
34476
|
+
Category:`;
|
|
34477
|
+
try {
|
|
34478
|
+
const handle = await args.selector.getSubstrate("concierge");
|
|
34479
|
+
if (!handle.capability.summarize) return "none";
|
|
34480
|
+
const response = await args.selector.invokeSummarize("concierge", {
|
|
34481
|
+
kind: "summarize",
|
|
34482
|
+
context: prompt,
|
|
34483
|
+
query: "Output the single category token.",
|
|
34484
|
+
maxTokens: 16
|
|
34485
|
+
});
|
|
34486
|
+
if (response.failureClass || response.body.kind !== "summarize") {
|
|
34487
|
+
return "none";
|
|
34488
|
+
}
|
|
34489
|
+
const raw = response.body.text.trim().toLowerCase();
|
|
34490
|
+
const head = raw.split(/\s|[.,!?:;]/)[0] ?? "";
|
|
34491
|
+
const normalized = head.replace(/[^a-z_]/g, "");
|
|
34492
|
+
const known = categories;
|
|
34493
|
+
if (known.includes(normalized)) {
|
|
34494
|
+
return normalized;
|
|
34495
|
+
}
|
|
34496
|
+
return "none";
|
|
34497
|
+
} catch {
|
|
34498
|
+
return "none";
|
|
34499
|
+
}
|
|
34500
|
+
};
|
|
34501
|
+
}
|
|
32967
34502
|
function buildConciergePiiFilter() {
|
|
32968
34503
|
return {
|
|
32969
34504
|
filter(input) {
|
|
@@ -33095,13 +34630,13 @@ init_encryption();
|
|
|
33095
34630
|
init_encoding();
|
|
33096
34631
|
var INTELLIGENCE_NAMESPACE = "_intelligence";
|
|
33097
34632
|
var SUBSTRATE_CONFIG_KEY = "substrate-config";
|
|
33098
|
-
var
|
|
34633
|
+
var HKDF_INFO4 = "intelligence-substrate-config";
|
|
33099
34634
|
var IntelligenceConfigStore = class {
|
|
33100
34635
|
storage;
|
|
33101
34636
|
encryptionKey;
|
|
33102
34637
|
constructor(storage, masterKey) {
|
|
33103
34638
|
this.storage = storage;
|
|
33104
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
34639
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO4);
|
|
33105
34640
|
}
|
|
33106
34641
|
/**
|
|
33107
34642
|
* Load the operator's substrate config from disk. Returns the config
|
|
@@ -37047,12 +38582,18 @@ ${err.message}
|
|
|
37047
38582
|
} : void 0;
|
|
37048
38583
|
const fortressIdForAggregator = fortressIdFromStoragePath(config.storage_path);
|
|
37049
38584
|
const aggregatorIdentityId = identityManager.getPrimaryIdentityId() ?? `fortress:${config.storage_path}`;
|
|
38585
|
+
const aggregatorPayloadStore = new AggregatorPayloadStore({
|
|
38586
|
+
storage,
|
|
38587
|
+
masterKey,
|
|
38588
|
+
fortressId: fortressIdForAggregator
|
|
38589
|
+
});
|
|
37050
38590
|
const approvalAggregator = new ApprovalAggregator({
|
|
37051
38591
|
storage,
|
|
37052
38592
|
masterKey,
|
|
37053
38593
|
auditLog,
|
|
37054
38594
|
identityId: aggregatorIdentityId,
|
|
37055
|
-
fortressId: fortressIdForAggregator
|
|
38595
|
+
fortressId: fortressIdForAggregator,
|
|
38596
|
+
payloadStore: aggregatorPayloadStore
|
|
37056
38597
|
});
|
|
37057
38598
|
const wrappedApprovalChannel = new AggregatorBackedChannel({
|
|
37058
38599
|
underlying: approvalChannel,
|