@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.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,45 @@ 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
|
+
}
|
|
16341
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16342
|
+
const limit = parseLimit2(
|
|
16343
|
+
url.searchParams.get("limit"),
|
|
16344
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16345
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16346
|
+
);
|
|
16347
|
+
const statusRaw = url.searchParams.get("status");
|
|
16348
|
+
const sinceTs = url.searchParams.get("since") ?? void 0;
|
|
16349
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16350
|
+
const filterStatus = statusRaw && isStatusFilter(statusRaw) && statusRaw !== "pending" ? statusRaw : void 0;
|
|
16351
|
+
const entries = await deps.aggregator.getHistory(
|
|
16352
|
+
{
|
|
16353
|
+
limit,
|
|
16354
|
+
...filterStatus !== void 0 ? { status: filterStatus } : {},
|
|
16355
|
+
...sinceTs !== void 0 ? { sinceTs } : {}
|
|
16356
|
+
},
|
|
16357
|
+
operatorId
|
|
16358
|
+
);
|
|
16359
|
+
writeJSON4(res, 200, { ok: true, data: { entries } });
|
|
16360
|
+
return true;
|
|
16361
|
+
}
|
|
16322
16362
|
if (method === "GET" && path === APPROVAL_INBOX_API_PREFIX) {
|
|
16323
16363
|
const limit = parseLimit2(
|
|
16324
16364
|
url.searchParams.get("limit"),
|
|
@@ -16341,11 +16381,39 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16341
16381
|
writeJSON4(res, 404, { ok: false, error: "not_found", path });
|
|
16342
16382
|
return true;
|
|
16343
16383
|
}
|
|
16344
|
-
if (method === "GET" && entryMatch.action ===
|
|
16345
|
-
const
|
|
16346
|
-
|
|
16347
|
-
(
|
|
16384
|
+
if (method === "GET" && entryMatch.action === "audit-trail") {
|
|
16385
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16386
|
+
if (!entry) {
|
|
16387
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16388
|
+
return true;
|
|
16389
|
+
}
|
|
16390
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16391
|
+
const trail = await deps.aggregator.getAuditTrail(
|
|
16392
|
+
entryMatch.aggregatorId,
|
|
16393
|
+
operatorId
|
|
16348
16394
|
);
|
|
16395
|
+
writeJSON4(res, 200, { ok: true, data: { entry, audit_trail: trail } });
|
|
16396
|
+
return true;
|
|
16397
|
+
}
|
|
16398
|
+
if (method === "GET" && entryMatch.action === "payload") {
|
|
16399
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16400
|
+
if (!entry) {
|
|
16401
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16402
|
+
return true;
|
|
16403
|
+
}
|
|
16404
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16405
|
+
const payload = await deps.aggregator.getFullPayloadWithAudit(
|
|
16406
|
+
entryMatch.aggregatorId,
|
|
16407
|
+
operatorId
|
|
16408
|
+
);
|
|
16409
|
+
writeJSON4(res, 200, {
|
|
16410
|
+
ok: true,
|
|
16411
|
+
data: { entry, request_payload: payload }
|
|
16412
|
+
});
|
|
16413
|
+
return true;
|
|
16414
|
+
}
|
|
16415
|
+
if (method === "GET" && entryMatch.action === null) {
|
|
16416
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16349
16417
|
if (!entry) {
|
|
16350
16418
|
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16351
16419
|
return true;
|
|
@@ -19332,7 +19400,10 @@ var APPROVAL_AGGREGATOR_HKDF_INFO = "l2-approval-aggregator-v1";
|
|
|
19332
19400
|
var APPROVAL_AGGREGATOR_AUDIT_OPS = {
|
|
19333
19401
|
AGGREGATED: "cross_harness_approval_aggregated",
|
|
19334
19402
|
RESOLVED: "cross_harness_approval_resolved",
|
|
19335
|
-
DEDUPED: "cross_harness_approval_deduped"
|
|
19403
|
+
DEDUPED: "cross_harness_approval_deduped",
|
|
19404
|
+
PAYLOAD_DECRYPTED: "cross_harness_approval_payload_decrypted",
|
|
19405
|
+
AUDIT_TRAIL_VIEWED: "cross_harness_approval_audit_trail_viewed",
|
|
19406
|
+
REPLAYED: "cross_harness_approval_replayed"
|
|
19336
19407
|
};
|
|
19337
19408
|
var DEFAULT_PENDING_TTL_MS = 5 * 60 * 1e3;
|
|
19338
19409
|
var DEFAULT_MAX_LIST_LIMIT = 200;
|
|
@@ -19348,6 +19419,8 @@ var ApprovalAggregator = class {
|
|
|
19348
19419
|
now;
|
|
19349
19420
|
resolveSourceContext;
|
|
19350
19421
|
resolveHubInboxItemId;
|
|
19422
|
+
payloadStore;
|
|
19423
|
+
resolveEnforcementChain;
|
|
19351
19424
|
/** Cached entries by `aggregator_id`. */
|
|
19352
19425
|
entries = /* @__PURE__ */ new Map();
|
|
19353
19426
|
/** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
|
|
@@ -19360,6 +19433,20 @@ var ApprovalAggregator = class {
|
|
|
19360
19433
|
hydrated = false;
|
|
19361
19434
|
/** Active SSE listeners. */
|
|
19362
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();
|
|
19363
19450
|
constructor(deps) {
|
|
19364
19451
|
this.storage = deps.storage;
|
|
19365
19452
|
this.encryptionKey = derivePurposeKey(
|
|
@@ -19377,6 +19464,14 @@ var ApprovalAggregator = class {
|
|
|
19377
19464
|
source_agent_id: this.fortressId
|
|
19378
19465
|
}));
|
|
19379
19466
|
this.resolveHubInboxItemId = deps.resolveHubInboxItemId ?? ((_event) => void 0);
|
|
19467
|
+
this.payloadStore = deps.payloadStore ?? null;
|
|
19468
|
+
this.resolveEnforcementChain = deps.resolveEnforcementChain ?? ((event) => [
|
|
19469
|
+
{
|
|
19470
|
+
layer: "l2",
|
|
19471
|
+
event: `approval_required:${event.operation}`,
|
|
19472
|
+
timestamp: event.request_timestamp
|
|
19473
|
+
}
|
|
19474
|
+
]);
|
|
19380
19475
|
}
|
|
19381
19476
|
/**
|
|
19382
19477
|
* Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
|
|
@@ -19386,6 +19481,113 @@ var ApprovalAggregator = class {
|
|
|
19386
19481
|
this.listeners.add(listener);
|
|
19387
19482
|
return () => this.listeners.delete(listener);
|
|
19388
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
|
+
}
|
|
19389
19591
|
/**
|
|
19390
19592
|
* Ingest a gate event. Returns the aggregator entry on first sight,
|
|
19391
19593
|
* `null` when deduped. Resolution events update the existing record;
|
|
@@ -19427,13 +19629,152 @@ var ApprovalAggregator = class {
|
|
|
19427
19629
|
}
|
|
19428
19630
|
/**
|
|
19429
19631
|
* Return the original (unhashed) request payload for the entry. Returns
|
|
19430
|
-
* `null` when the entry is unknown
|
|
19431
|
-
*
|
|
19632
|
+
* `null` when the entry is unknown. When the in-memory payload map has
|
|
19633
|
+
* been evicted (e.g. after a server restart) and a `payloadStore` was
|
|
19634
|
+
* provided, the at-rest bundle is decrypted and the in-memory map is
|
|
19635
|
+
* refilled. Audit emission lives on the `*WithAudit` variant; this base
|
|
19636
|
+
* accessor is silent so internal callers can read without polluting the
|
|
19637
|
+
* audit trail.
|
|
19432
19638
|
*/
|
|
19433
19639
|
async getFullPayload(aggregatorId) {
|
|
19434
19640
|
await this.hydrate();
|
|
19435
19641
|
if (!this.entries.has(aggregatorId)) return null;
|
|
19436
|
-
|
|
19642
|
+
const cached = this.fullPayloads.get(aggregatorId);
|
|
19643
|
+
if (cached !== void 0) return cached;
|
|
19644
|
+
if (this.payloadStore) {
|
|
19645
|
+
try {
|
|
19646
|
+
const restored = await this.payloadStore.loadPayload(aggregatorId);
|
|
19647
|
+
if (restored !== null) {
|
|
19648
|
+
this.fullPayloads.set(aggregatorId, restored);
|
|
19649
|
+
return restored;
|
|
19650
|
+
}
|
|
19651
|
+
} catch {
|
|
19652
|
+
}
|
|
19653
|
+
}
|
|
19654
|
+
return null;
|
|
19655
|
+
}
|
|
19656
|
+
/**
|
|
19657
|
+
* Return the entry record for the given id, or null when unknown.
|
|
19658
|
+
* Idempotent. v1.3 Upsilon-3.
|
|
19659
|
+
*/
|
|
19660
|
+
async getEntry(aggregatorId) {
|
|
19661
|
+
await this.hydrate();
|
|
19662
|
+
return this.entries.get(aggregatorId) ?? null;
|
|
19663
|
+
}
|
|
19664
|
+
/**
|
|
19665
|
+
* Audited variant of `getFullPayload`. Emits the
|
|
19666
|
+
* `cross_harness_approval_payload_decrypted` audit event before
|
|
19667
|
+
* returning. Used by the operator-facing /payload replay route.
|
|
19668
|
+
* v1.3 Upsilon-3.
|
|
19669
|
+
*/
|
|
19670
|
+
async getFullPayloadWithAudit(aggregatorId, operatorId) {
|
|
19671
|
+
const payload = await this.getFullPayload(aggregatorId);
|
|
19672
|
+
if (payload === null) return null;
|
|
19673
|
+
const entry = this.entries.get(aggregatorId);
|
|
19674
|
+
this.auditLog.append(
|
|
19675
|
+
"l2",
|
|
19676
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.PAYLOAD_DECRYPTED,
|
|
19677
|
+
operatorId,
|
|
19678
|
+
{
|
|
19679
|
+
aggregator_id: aggregatorId,
|
|
19680
|
+
...entry ? {
|
|
19681
|
+
source_harness: entry.source_harness,
|
|
19682
|
+
source_agent_id: entry.source_agent_id,
|
|
19683
|
+
entry_status: entry.status
|
|
19684
|
+
} : {}
|
|
19685
|
+
}
|
|
19686
|
+
);
|
|
19687
|
+
return payload;
|
|
19688
|
+
}
|
|
19689
|
+
/**
|
|
19690
|
+
* Return the audit-log entries that led to and surround this approval.
|
|
19691
|
+
* Best-effort matching: aggregator-side emissions (AGGREGATED, RESOLVED,
|
|
19692
|
+
* DEDUPED, replay events) all carry `details.aggregator_id` and link
|
|
19693
|
+
* directly. Gate-side emissions (`gate_*:operation`) do not carry the
|
|
19694
|
+
* aggregator id at v1.3, so they are matched via timestamp window
|
|
19695
|
+
* (entry.created_at to entry.resolved_at + 1s, or expires_at + 1s while
|
|
19696
|
+
* pending) and operation suffix. Emits AUDIT_TRAIL_VIEWED on call.
|
|
19697
|
+
* v1.3 Upsilon-3.
|
|
19698
|
+
*/
|
|
19699
|
+
async getAuditTrail(aggregatorId, operatorId) {
|
|
19700
|
+
await this.hydrate();
|
|
19701
|
+
const entry = this.entries.get(aggregatorId);
|
|
19702
|
+
if (!entry) {
|
|
19703
|
+
return [];
|
|
19704
|
+
}
|
|
19705
|
+
const sinceMs = Date.parse(entry.created_at) - 1e3;
|
|
19706
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
19707
|
+
const queried = await this.auditLog.query({ since: sinceIso, limit: 1e3 });
|
|
19708
|
+
const operationPart = entry.policy_rule_id.includes(":") ? entry.policy_rule_id.slice(entry.policy_rule_id.indexOf(":") + 1) : entry.policy_rule_id;
|
|
19709
|
+
const lifetimeStart = sinceMs;
|
|
19710
|
+
const lifetimeEnd = entry.resolved_at ? Date.parse(entry.resolved_at) + 1e3 : Date.parse(entry.expires_at) + 1e3;
|
|
19711
|
+
const matches = [];
|
|
19712
|
+
for (const audit of queried.entries) {
|
|
19713
|
+
const detailsId = audit.details !== void 0 ? audit.details["aggregator_id"] : void 0;
|
|
19714
|
+
if (detailsId === aggregatorId) {
|
|
19715
|
+
matches.push(audit);
|
|
19716
|
+
continue;
|
|
19717
|
+
}
|
|
19718
|
+
const auditMs = Date.parse(audit.timestamp);
|
|
19719
|
+
if (auditMs < lifetimeStart || auditMs > lifetimeEnd) continue;
|
|
19720
|
+
if (audit.operation.endsWith(`:${operationPart}`)) {
|
|
19721
|
+
matches.push(audit);
|
|
19722
|
+
}
|
|
19723
|
+
}
|
|
19724
|
+
matches.sort(
|
|
19725
|
+
(a, b) => a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0
|
|
19726
|
+
);
|
|
19727
|
+
this.auditLog.append(
|
|
19728
|
+
"l2",
|
|
19729
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.AUDIT_TRAIL_VIEWED,
|
|
19730
|
+
operatorId,
|
|
19731
|
+
{
|
|
19732
|
+
aggregator_id: aggregatorId,
|
|
19733
|
+
entry_status: entry.status,
|
|
19734
|
+
match_count: matches.length
|
|
19735
|
+
}
|
|
19736
|
+
);
|
|
19737
|
+
return matches;
|
|
19738
|
+
}
|
|
19739
|
+
/**
|
|
19740
|
+
* List historical (resolved) approvals. Excludes pending entries by
|
|
19741
|
+
* design: `list()` is the pending-inbox surface and `getHistory()` is
|
|
19742
|
+
* the resolved-replay surface. Emits REPLAYED on each call. v1.3
|
|
19743
|
+
* Upsilon-3.
|
|
19744
|
+
*/
|
|
19745
|
+
async getHistory(opts, operatorId) {
|
|
19746
|
+
await this.hydrate();
|
|
19747
|
+
await this.expireStale();
|
|
19748
|
+
const limit = Math.min(
|
|
19749
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19750
|
+
this.maxListLimit
|
|
19751
|
+
);
|
|
19752
|
+
const sinceMs = opts?.sinceTs ? Date.parse(opts.sinceTs) : Number.NEGATIVE_INFINITY;
|
|
19753
|
+
const matching = [];
|
|
19754
|
+
for (const entry of this.entries.values()) {
|
|
19755
|
+
if (entry.status === "pending") continue;
|
|
19756
|
+
if (opts?.status && entry.status !== opts.status) continue;
|
|
19757
|
+
const stamp = Date.parse(entry.resolved_at ?? entry.created_at);
|
|
19758
|
+
if (stamp < sinceMs) continue;
|
|
19759
|
+
matching.push(entry);
|
|
19760
|
+
}
|
|
19761
|
+
matching.sort((a, b) => {
|
|
19762
|
+
const aStamp = a.resolved_at ?? a.created_at;
|
|
19763
|
+
const bStamp = b.resolved_at ?? b.created_at;
|
|
19764
|
+
return bStamp.localeCompare(aStamp);
|
|
19765
|
+
});
|
|
19766
|
+
const sliced = matching.slice(0, limit);
|
|
19767
|
+
this.auditLog.append(
|
|
19768
|
+
"l2",
|
|
19769
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.REPLAYED,
|
|
19770
|
+
operatorId,
|
|
19771
|
+
{
|
|
19772
|
+
result_count: sliced.length,
|
|
19773
|
+
...opts?.status !== void 0 ? { status_filter: opts.status } : {},
|
|
19774
|
+
...opts?.sinceTs !== void 0 ? { since: opts.sinceTs } : {}
|
|
19775
|
+
}
|
|
19776
|
+
);
|
|
19777
|
+
return sliced;
|
|
19437
19778
|
}
|
|
19438
19779
|
/**
|
|
19439
19780
|
* Resolve an entry. Used by both:
|
|
@@ -19457,6 +19798,7 @@ var ApprovalAggregator = class {
|
|
|
19457
19798
|
entry.status = decision;
|
|
19458
19799
|
entry.resolved_at = this.now().toISOString();
|
|
19459
19800
|
entry.resolved_by = operatorId;
|
|
19801
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19460
19802
|
await this.persist(entry);
|
|
19461
19803
|
this.auditLog.append(
|
|
19462
19804
|
"l2",
|
|
@@ -19507,6 +19849,8 @@ var ApprovalAggregator = class {
|
|
|
19507
19849
|
const now = this.now();
|
|
19508
19850
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19509
19851
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19852
|
+
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19853
|
+
const revision = this.nextRevision();
|
|
19510
19854
|
const entry = {
|
|
19511
19855
|
aggregator_id: id,
|
|
19512
19856
|
source_harness: ctx.source_harness,
|
|
@@ -19518,13 +19862,22 @@ var ApprovalAggregator = class {
|
|
|
19518
19862
|
status: "pending",
|
|
19519
19863
|
created_at: now.toISOString(),
|
|
19520
19864
|
expires_at: expires.toISOString(),
|
|
19521
|
-
|
|
19865
|
+
created_at_revision: revision,
|
|
19866
|
+
last_modified_revision: revision,
|
|
19867
|
+
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19868
|
+
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19522
19869
|
};
|
|
19523
19870
|
this.entries.set(id, entry);
|
|
19524
19871
|
this.dedupIndex.set(dedupKey, id);
|
|
19525
19872
|
this.correlationIndex.set(event.correlation_id, id);
|
|
19526
19873
|
this.fullPayloads.set(id, event.context);
|
|
19527
19874
|
await this.persist(entry);
|
|
19875
|
+
if (this.payloadStore) {
|
|
19876
|
+
try {
|
|
19877
|
+
await this.payloadStore.savePayload(id, event.context);
|
|
19878
|
+
} catch {
|
|
19879
|
+
}
|
|
19880
|
+
}
|
|
19528
19881
|
this.auditLog.append(
|
|
19529
19882
|
"l2",
|
|
19530
19883
|
APPROVAL_AGGREGATOR_AUDIT_OPS.AGGREGATED,
|
|
@@ -19553,6 +19906,7 @@ var ApprovalAggregator = class {
|
|
|
19553
19906
|
entry.status = status;
|
|
19554
19907
|
entry.resolved_at = event.resolution.decided_at;
|
|
19555
19908
|
entry.resolved_by = event.resolution.decided_by;
|
|
19909
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19556
19910
|
await this.persist(entry);
|
|
19557
19911
|
this.auditLog.append(
|
|
19558
19912
|
"l2",
|
|
@@ -19615,6 +19969,7 @@ var ApprovalAggregator = class {
|
|
|
19615
19969
|
entry.status = "expired";
|
|
19616
19970
|
entry.resolved_at = this.now().toISOString();
|
|
19617
19971
|
entry.resolved_by = "system_ttl";
|
|
19972
|
+
entry.last_modified_revision = this.nextRevision();
|
|
19618
19973
|
await this.persist(entry);
|
|
19619
19974
|
this.auditLog.append(
|
|
19620
19975
|
"l2",
|
|
@@ -19661,6 +20016,10 @@ var ApprovalAggregator = class {
|
|
|
19661
20016
|
this.entries.set(entry.aggregator_id, entry);
|
|
19662
20017
|
const dedupKey = `${entry.source_harness}|${entry.source_agent_id}|${entry.audit_log_entry_id}`;
|
|
19663
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
|
+
}
|
|
19664
20023
|
} catch {
|
|
19665
20024
|
}
|
|
19666
20025
|
}
|
|
@@ -19833,6 +20192,143 @@ function makeRedirectResolverFromPolicySupplier(supplier) {
|
|
|
19833
20192
|
};
|
|
19834
20193
|
}
|
|
19835
20194
|
|
|
20195
|
+
// src/principal-policy/aggregator-store.ts
|
|
20196
|
+
init_encryption();
|
|
20197
|
+
init_encoding();
|
|
20198
|
+
var AGGREGATOR_PAYLOAD_NAMESPACE = "_approval_aggregator_payloads";
|
|
20199
|
+
var AGGREGATOR_PAYLOAD_KEY_PREFIX = "payload.";
|
|
20200
|
+
var HKDF_INFO = "l2-approval-aggregator-payload-v1";
|
|
20201
|
+
var DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS = 30;
|
|
20202
|
+
var MAX_BUNDLE_BYTES2 = 4 * 1024 * 1024;
|
|
20203
|
+
var AggregatorPayloadStore = class {
|
|
20204
|
+
storage;
|
|
20205
|
+
encryptionKey;
|
|
20206
|
+
fortressId;
|
|
20207
|
+
retentionDays;
|
|
20208
|
+
constructor(opts) {
|
|
20209
|
+
this.storage = opts.storage;
|
|
20210
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO);
|
|
20211
|
+
this.fortressId = opts.fortressId;
|
|
20212
|
+
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS;
|
|
20213
|
+
}
|
|
20214
|
+
/**
|
|
20215
|
+
* Persist `payload` under the given aggregator_id. Idempotent; calling
|
|
20216
|
+
* twice with the same id rewrites the bundle (retention_until is
|
|
20217
|
+
* recomputed). Returns the bundle's retention_until ISO-8601 timestamp
|
|
20218
|
+
* so callers can log it.
|
|
20219
|
+
*/
|
|
20220
|
+
async savePayload(aggregatorId, payload) {
|
|
20221
|
+
const now = /* @__PURE__ */ new Date();
|
|
20222
|
+
const retentionMs = this.retentionDays * 24 * 60 * 60 * 1e3;
|
|
20223
|
+
const retentionUntil = new Date(now.getTime() + retentionMs);
|
|
20224
|
+
const bundle = {
|
|
20225
|
+
version: 1,
|
|
20226
|
+
aggregator_id: aggregatorId,
|
|
20227
|
+
fortress_id: this.fortressId,
|
|
20228
|
+
created_at: now.toISOString(),
|
|
20229
|
+
retention_until: retentionUntil.toISOString(),
|
|
20230
|
+
payload
|
|
20231
|
+
};
|
|
20232
|
+
const aad = stringToBytes(aggregatorId);
|
|
20233
|
+
const plaintext = stringToBytes(JSON.stringify(bundle));
|
|
20234
|
+
const envelope = encrypt(plaintext, this.encryptionKey, aad);
|
|
20235
|
+
await this.storage.write(
|
|
20236
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20237
|
+
payloadKey(aggregatorId),
|
|
20238
|
+
stringToBytes(JSON.stringify(envelope))
|
|
20239
|
+
);
|
|
20240
|
+
return bundle.retention_until;
|
|
20241
|
+
}
|
|
20242
|
+
/**
|
|
20243
|
+
* Read the persisted payload for the aggregator_id. Returns null if no
|
|
20244
|
+
* bundle exists, the bundle is corrupted, or AAD binding fails.
|
|
20245
|
+
*/
|
|
20246
|
+
async loadPayload(aggregatorId) {
|
|
20247
|
+
const key = payloadKey(aggregatorId);
|
|
20248
|
+
let raw;
|
|
20249
|
+
try {
|
|
20250
|
+
raw = await this.storage.read(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20251
|
+
} catch {
|
|
20252
|
+
return null;
|
|
20253
|
+
}
|
|
20254
|
+
if (!raw) return null;
|
|
20255
|
+
if (raw.length > MAX_BUNDLE_BYTES2) return null;
|
|
20256
|
+
try {
|
|
20257
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20258
|
+
const aad = stringToBytes(aggregatorId);
|
|
20259
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20260
|
+
const parsed = JSON.parse(
|
|
20261
|
+
bytesToString(plaintext)
|
|
20262
|
+
);
|
|
20263
|
+
if (parsed.version !== 1) return null;
|
|
20264
|
+
if (parsed.aggregator_id !== aggregatorId) return null;
|
|
20265
|
+
return parsed.payload;
|
|
20266
|
+
} catch {
|
|
20267
|
+
return null;
|
|
20268
|
+
}
|
|
20269
|
+
}
|
|
20270
|
+
/**
|
|
20271
|
+
* Delete the persisted payload. Returns true when a bundle was removed,
|
|
20272
|
+
* false when none existed.
|
|
20273
|
+
*/
|
|
20274
|
+
async deletePayload(aggregatorId) {
|
|
20275
|
+
const key = payloadKey(aggregatorId);
|
|
20276
|
+
const existed = await this.storage.exists(
|
|
20277
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20278
|
+
key
|
|
20279
|
+
);
|
|
20280
|
+
if (!existed) return false;
|
|
20281
|
+
try {
|
|
20282
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20283
|
+
} catch {
|
|
20284
|
+
return false;
|
|
20285
|
+
}
|
|
20286
|
+
return true;
|
|
20287
|
+
}
|
|
20288
|
+
/**
|
|
20289
|
+
* Drop expired payload bundles. Returns the count of bundles pruned.
|
|
20290
|
+
* Caller wires this into the cocoon-unlock initialization path.
|
|
20291
|
+
*/
|
|
20292
|
+
async pruneExpired(now) {
|
|
20293
|
+
const cutoff = (now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
20294
|
+
const entries = await this.storage.list(
|
|
20295
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20296
|
+
AGGREGATOR_PAYLOAD_KEY_PREFIX
|
|
20297
|
+
);
|
|
20298
|
+
let pruned = 0;
|
|
20299
|
+
for (const meta of entries) {
|
|
20300
|
+
const aggregatorId = stripKeyPrefix(meta.key);
|
|
20301
|
+
if (aggregatorId === null) continue;
|
|
20302
|
+
const raw = await this.storage.read(
|
|
20303
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20304
|
+
meta.key
|
|
20305
|
+
);
|
|
20306
|
+
if (!raw) continue;
|
|
20307
|
+
try {
|
|
20308
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20309
|
+
const aad = stringToBytes(aggregatorId);
|
|
20310
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20311
|
+
const parsed = JSON.parse(
|
|
20312
|
+
bytesToString(plaintext)
|
|
20313
|
+
);
|
|
20314
|
+
if (parsed.retention_until <= cutoff) {
|
|
20315
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, meta.key);
|
|
20316
|
+
pruned += 1;
|
|
20317
|
+
}
|
|
20318
|
+
} catch {
|
|
20319
|
+
}
|
|
20320
|
+
}
|
|
20321
|
+
return { pruned };
|
|
20322
|
+
}
|
|
20323
|
+
};
|
|
20324
|
+
function payloadKey(aggregatorId) {
|
|
20325
|
+
return `${AGGREGATOR_PAYLOAD_KEY_PREFIX}${aggregatorId}`;
|
|
20326
|
+
}
|
|
20327
|
+
function stripKeyPrefix(key) {
|
|
20328
|
+
if (!key.startsWith(AGGREGATOR_PAYLOAD_KEY_PREFIX)) return null;
|
|
20329
|
+
return key.slice(AGGREGATOR_PAYLOAD_KEY_PREFIX.length);
|
|
20330
|
+
}
|
|
20331
|
+
|
|
19836
20332
|
// src/principal-policy/tools.ts
|
|
19837
20333
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
19838
20334
|
return [
|
|
@@ -31974,6 +32470,13 @@ init_encoding();
|
|
|
31974
32470
|
// src/chat/operator-chat-audit-events.ts
|
|
31975
32471
|
var OPERATOR_CHAT_OPS = {
|
|
31976
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",
|
|
31977
32480
|
/**
|
|
31978
32481
|
* Operator viewed concierge thread history (WP-V1.3-9 Tau-1). Emitted
|
|
31979
32482
|
* when the operator hits the list-threads or read-thread route. Body
|
|
@@ -31993,20 +32496,831 @@ var OPERATOR_CHAT_OPS = {
|
|
|
31993
32496
|
* turns; the concierge degrades to single-turn after emitting. Body
|
|
31994
32497
|
* carries thread_id + a stable failure_reason enum.
|
|
31995
32498
|
*/
|
|
31996
|
-
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed"
|
|
32499
|
+
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed",
|
|
32500
|
+
/**
|
|
32501
|
+
* Concierge dynamic-context fetcher failed (WP-V1.3-9 Tau-3). Emitted
|
|
32502
|
+
* when a category fetcher throws while assembling the dynamic context
|
|
32503
|
+
* fold. The concierge omits that category and continues; the user-
|
|
32504
|
+
* facing query is never broken. Body carries category + failure_reason.
|
|
32505
|
+
*/
|
|
32506
|
+
CONCIERGE_CONTEXT_FETCHER_FAILED: "operator_concierge_context_fetcher_failed"
|
|
31997
32507
|
};
|
|
31998
32508
|
|
|
31999
32509
|
// src/chat/operator-chat-types.ts
|
|
32000
32510
|
var OPERATOR_CHAT_MAX_THREAD_LENGTH = 500;
|
|
32001
32511
|
var CONCIERGE_THREAD_KEY = "_fortress";
|
|
32002
32512
|
|
|
32513
|
+
// src/chat/concierge-context-router.ts
|
|
32514
|
+
var APPROX_CHARS_PER_TOKEN = 4;
|
|
32515
|
+
var DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET = 2e3;
|
|
32516
|
+
var DYNAMIC_CONTEXT_SECTION_HEADER = "## Live fortress context";
|
|
32517
|
+
var CONTEXT_CATEGORIES = [
|
|
32518
|
+
"templates",
|
|
32519
|
+
"agent_state",
|
|
32520
|
+
"agent_activity",
|
|
32521
|
+
"audit_log",
|
|
32522
|
+
"sentinel_findings",
|
|
32523
|
+
"anomaly_alerts",
|
|
32524
|
+
"recent_receipts",
|
|
32525
|
+
"verascore_deltas"
|
|
32526
|
+
];
|
|
32527
|
+
function phrasePattern(phrase) {
|
|
32528
|
+
const escaped = phrase.toLowerCase().split(/\s+/).map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
|
|
32529
|
+
return { source: `\\b${escaped}\\b`, phrase };
|
|
32530
|
+
}
|
|
32531
|
+
var CATEGORY_KEYWORDS = [
|
|
32532
|
+
{
|
|
32533
|
+
category: "templates",
|
|
32534
|
+
patterns: [
|
|
32535
|
+
"templates",
|
|
32536
|
+
"template",
|
|
32537
|
+
"channel templates",
|
|
32538
|
+
"channel template",
|
|
32539
|
+
"list templates",
|
|
32540
|
+
"available templates",
|
|
32541
|
+
"what templates"
|
|
32542
|
+
].map(phrasePattern)
|
|
32543
|
+
},
|
|
32544
|
+
{
|
|
32545
|
+
category: "agent_state",
|
|
32546
|
+
patterns: [
|
|
32547
|
+
"state",
|
|
32548
|
+
"status",
|
|
32549
|
+
"agent state",
|
|
32550
|
+
"agent status",
|
|
32551
|
+
"status of agent",
|
|
32552
|
+
"status of agents",
|
|
32553
|
+
"state of",
|
|
32554
|
+
"doing"
|
|
32555
|
+
].map(phrasePattern)
|
|
32556
|
+
},
|
|
32557
|
+
{
|
|
32558
|
+
category: "agent_activity",
|
|
32559
|
+
patterns: [
|
|
32560
|
+
"activity",
|
|
32561
|
+
"agent activity",
|
|
32562
|
+
"what did",
|
|
32563
|
+
"recent activity"
|
|
32564
|
+
].map(phrasePattern)
|
|
32565
|
+
},
|
|
32566
|
+
{
|
|
32567
|
+
category: "audit_log",
|
|
32568
|
+
patterns: [
|
|
32569
|
+
"audit log",
|
|
32570
|
+
"audit",
|
|
32571
|
+
"log entry",
|
|
32572
|
+
"log entries",
|
|
32573
|
+
"what happened",
|
|
32574
|
+
"show me events",
|
|
32575
|
+
"event class"
|
|
32576
|
+
].map(phrasePattern)
|
|
32577
|
+
},
|
|
32578
|
+
{
|
|
32579
|
+
category: "sentinel_findings",
|
|
32580
|
+
patterns: [
|
|
32581
|
+
"sentinel",
|
|
32582
|
+
"sentinels",
|
|
32583
|
+
"warning",
|
|
32584
|
+
"warnings",
|
|
32585
|
+
"alert",
|
|
32586
|
+
"alerts",
|
|
32587
|
+
"whats wrong",
|
|
32588
|
+
"what's wrong",
|
|
32589
|
+
"findings"
|
|
32590
|
+
].map(phrasePattern)
|
|
32591
|
+
},
|
|
32592
|
+
{
|
|
32593
|
+
category: "anomaly_alerts",
|
|
32594
|
+
patterns: [
|
|
32595
|
+
"anomaly",
|
|
32596
|
+
"anomalies",
|
|
32597
|
+
"spike",
|
|
32598
|
+
"unusual",
|
|
32599
|
+
"outlier"
|
|
32600
|
+
].map(phrasePattern)
|
|
32601
|
+
},
|
|
32602
|
+
{
|
|
32603
|
+
category: "recent_receipts",
|
|
32604
|
+
patterns: [
|
|
32605
|
+
"receipt",
|
|
32606
|
+
"receipts",
|
|
32607
|
+
"concordia",
|
|
32608
|
+
"commitment",
|
|
32609
|
+
"commitments",
|
|
32610
|
+
"chain",
|
|
32611
|
+
"chains"
|
|
32612
|
+
].map(phrasePattern)
|
|
32613
|
+
},
|
|
32614
|
+
{
|
|
32615
|
+
category: "verascore_deltas",
|
|
32616
|
+
patterns: [
|
|
32617
|
+
"verascore",
|
|
32618
|
+
"vera score",
|
|
32619
|
+
"trust score",
|
|
32620
|
+
"reputation"
|
|
32621
|
+
].map(phrasePattern)
|
|
32622
|
+
}
|
|
32623
|
+
];
|
|
32624
|
+
function extractAgentNameHint(query) {
|
|
32625
|
+
const agentPattern = /\bagent\s+["']?([A-Za-z][\w-]{0,40})["']?/i;
|
|
32626
|
+
const m = query.match(agentPattern);
|
|
32627
|
+
if (m && m[1]) return m[1];
|
|
32628
|
+
const quoted = query.match(/["']([A-Za-z][\w-]{0,40})["']/);
|
|
32629
|
+
if (quoted && quoted[1]) return quoted[1];
|
|
32630
|
+
return null;
|
|
32631
|
+
}
|
|
32632
|
+
var TRIVIAL_GREETINGS = /* @__PURE__ */ new Set([
|
|
32633
|
+
"hi",
|
|
32634
|
+
"hello",
|
|
32635
|
+
"hey",
|
|
32636
|
+
"yo",
|
|
32637
|
+
"ok",
|
|
32638
|
+
"thanks",
|
|
32639
|
+
"thx",
|
|
32640
|
+
"thank you"
|
|
32641
|
+
]);
|
|
32642
|
+
function isTrivialQuery(query) {
|
|
32643
|
+
const norm = query.trim().toLowerCase();
|
|
32644
|
+
if (norm.length === 0) return true;
|
|
32645
|
+
if (norm.length < 8) return true;
|
|
32646
|
+
return TRIVIAL_GREETINGS.has(norm);
|
|
32647
|
+
}
|
|
32648
|
+
function classifyQuery(query, parsedGrammar) {
|
|
32649
|
+
const normalized = query.toLowerCase();
|
|
32650
|
+
const matches = [];
|
|
32651
|
+
const grammarAgent = parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null;
|
|
32652
|
+
for (const spec of CATEGORY_KEYWORDS) {
|
|
32653
|
+
const matchedPhrases = [];
|
|
32654
|
+
for (const pattern of spec.patterns) {
|
|
32655
|
+
if (matchedPhrases.includes(pattern.phrase)) continue;
|
|
32656
|
+
const re = new RegExp(pattern.source, "i");
|
|
32657
|
+
if (re.test(normalized)) {
|
|
32658
|
+
matchedPhrases.push(pattern.phrase);
|
|
32659
|
+
}
|
|
32660
|
+
}
|
|
32661
|
+
if (matchedPhrases.length === 0) continue;
|
|
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;
|
|
32665
|
+
matches.push({
|
|
32666
|
+
category: spec.category,
|
|
32667
|
+
confidence,
|
|
32668
|
+
matched_keywords: matchedPhrases,
|
|
32669
|
+
agent_name_hint,
|
|
32670
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32671
|
+
});
|
|
32672
|
+
}
|
|
32673
|
+
matches.sort((a, b) => {
|
|
32674
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
32675
|
+
return CONTEXT_CATEGORIES.indexOf(a.category) - CONTEXT_CATEGORIES.indexOf(b.category);
|
|
32676
|
+
});
|
|
32677
|
+
return matches;
|
|
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
|
+
}
|
|
32698
|
+
function approxTokenLen(text) {
|
|
32699
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32700
|
+
}
|
|
32701
|
+
var CATEGORY_LABELS = {
|
|
32702
|
+
templates: "Templates",
|
|
32703
|
+
agent_state: "Agent state",
|
|
32704
|
+
agent_activity: "Agent activity",
|
|
32705
|
+
audit_log: "Audit log",
|
|
32706
|
+
sentinel_findings: "Sentinel findings",
|
|
32707
|
+
anomaly_alerts: "Anomaly alerts",
|
|
32708
|
+
recent_receipts: "Recent receipts",
|
|
32709
|
+
verascore_deltas: "Verascore deltas"
|
|
32710
|
+
};
|
|
32711
|
+
async function runFetcher(match, fetchers, hints) {
|
|
32712
|
+
switch (match.category) {
|
|
32713
|
+
case "templates":
|
|
32714
|
+
return fetchers.templates(hints);
|
|
32715
|
+
case "agent_state":
|
|
32716
|
+
return fetchers.agent_state(match.agent_name_hint, hints);
|
|
32717
|
+
case "agent_activity":
|
|
32718
|
+
return fetchers.agent_activity(match.agent_name_hint, hints);
|
|
32719
|
+
case "audit_log":
|
|
32720
|
+
return fetchers.audit_log(hints);
|
|
32721
|
+
case "sentinel_findings":
|
|
32722
|
+
return fetchers.sentinel_findings(hints);
|
|
32723
|
+
case "anomaly_alerts":
|
|
32724
|
+
return fetchers.anomaly_alerts(hints);
|
|
32725
|
+
case "recent_receipts":
|
|
32726
|
+
return fetchers.recent_receipts(hints);
|
|
32727
|
+
case "verascore_deltas":
|
|
32728
|
+
return fetchers.verascore_deltas(hints);
|
|
32729
|
+
}
|
|
32730
|
+
}
|
|
32731
|
+
function trivialMatch(category, parsedGrammar) {
|
|
32732
|
+
return {
|
|
32733
|
+
category,
|
|
32734
|
+
confidence: 0.5,
|
|
32735
|
+
matched_keywords: ["llm-assist"],
|
|
32736
|
+
agent_name_hint: parsedGrammar && parsedGrammar.agent_names.length > 0 ? parsedGrammar.agent_names[0] ?? null : null,
|
|
32737
|
+
...parsedGrammar !== void 0 ? { query_grammar: parsedGrammar } : {}
|
|
32738
|
+
};
|
|
32739
|
+
}
|
|
32740
|
+
async function foldContext(query, fetchers, opts) {
|
|
32741
|
+
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32742
|
+
const parsed = opts?.parsed ?? null;
|
|
32743
|
+
const hints = fetcherHintsFromGrammar(parsed);
|
|
32744
|
+
let matches = classifyQuery(query, parsed);
|
|
32745
|
+
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32746
|
+
try {
|
|
32747
|
+
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32748
|
+
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32749
|
+
matches = [trivialMatch(picked, parsed)];
|
|
32750
|
+
}
|
|
32751
|
+
} catch {
|
|
32752
|
+
}
|
|
32753
|
+
}
|
|
32754
|
+
if (matches.length === 0) {
|
|
32755
|
+
return { section: "", categoriesIncluded: [] };
|
|
32756
|
+
}
|
|
32757
|
+
const attempts = [];
|
|
32758
|
+
for (const match of matches) {
|
|
32759
|
+
try {
|
|
32760
|
+
const text = await runFetcher(match, fetchers, hints);
|
|
32761
|
+
const trimmed = text.trim();
|
|
32762
|
+
if (trimmed.length > 0) {
|
|
32763
|
+
attempts.push({ category: match.category, text: trimmed });
|
|
32764
|
+
}
|
|
32765
|
+
} catch (err) {
|
|
32766
|
+
opts?.onFetcherFailure?.(match.category, err);
|
|
32767
|
+
}
|
|
32768
|
+
}
|
|
32769
|
+
if (attempts.length === 0) {
|
|
32770
|
+
return { section: "", categoriesIncluded: [] };
|
|
32771
|
+
}
|
|
32772
|
+
const headerTokens = approxTokenLen(`${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32773
|
+
`);
|
|
32774
|
+
const sepTokens = approxTokenLen("\n\n");
|
|
32775
|
+
let runningTokens = headerTokens;
|
|
32776
|
+
const kept = [];
|
|
32777
|
+
for (const attempt of attempts) {
|
|
32778
|
+
const block = `### ${CATEGORY_LABELS[attempt.category]}
|
|
32779
|
+
${attempt.text}`;
|
|
32780
|
+
const tokens = approxTokenLen(block) + (kept.length > 0 ? sepTokens : 0);
|
|
32781
|
+
if (kept.length === 0) {
|
|
32782
|
+
kept.push(attempt);
|
|
32783
|
+
runningTokens += tokens;
|
|
32784
|
+
continue;
|
|
32785
|
+
}
|
|
32786
|
+
if (runningTokens + tokens > budget) break;
|
|
32787
|
+
kept.push(attempt);
|
|
32788
|
+
runningTokens += tokens;
|
|
32789
|
+
}
|
|
32790
|
+
const blocks = kept.map(
|
|
32791
|
+
(k) => `### ${CATEGORY_LABELS[k.category]}
|
|
32792
|
+
${k.text}`
|
|
32793
|
+
);
|
|
32794
|
+
const section = `${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32795
|
+
${blocks.join("\n\n")}`;
|
|
32796
|
+
return {
|
|
32797
|
+
section,
|
|
32798
|
+
categoriesIncluded: kept.map((k) => k.category)
|
|
32799
|
+
};
|
|
32800
|
+
}
|
|
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
|
+
|
|
32003
33316
|
// src/chat/operator-chat-service.ts
|
|
32004
33317
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
32005
33318
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
32006
33319
|
var DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS = 24 * 60 * 60 * 1e3;
|
|
32007
33320
|
var DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET = 500;
|
|
32008
33321
|
var DEFAULT_CONCIERGE_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32009
|
-
|
|
33322
|
+
var DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET = 2e3;
|
|
33323
|
+
function approxTokenLen2(text) {
|
|
32010
33324
|
return Math.ceil(text.length / 4);
|
|
32011
33325
|
}
|
|
32012
33326
|
var SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
|
|
@@ -32050,6 +33364,11 @@ var OperatorChatService = class {
|
|
|
32050
33364
|
historyTokenBudget;
|
|
32051
33365
|
sessionTtlMs;
|
|
32052
33366
|
clock;
|
|
33367
|
+
contextFetchers;
|
|
33368
|
+
contextLlmAssist;
|
|
33369
|
+
dynamicContextBudget;
|
|
33370
|
+
agentRegistry;
|
|
33371
|
+
grammarLlmAssist;
|
|
32053
33372
|
/**
|
|
32054
33373
|
* In-memory thread_id assigned to the active concierge session.
|
|
32055
33374
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32081,6 +33400,19 @@ var OperatorChatService = class {
|
|
|
32081
33400
|
this.historyTokenBudget = deps.conciergeHistoryTokenBudget !== void 0 && deps.conciergeHistoryTokenBudget > 0 ? deps.conciergeHistoryTokenBudget : DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET;
|
|
32082
33401
|
this.sessionTtlMs = deps.conciergeSessionTtlMs !== void 0 && deps.conciergeSessionTtlMs > 0 ? deps.conciergeSessionTtlMs : DEFAULT_CONCIERGE_SESSION_TTL_MS;
|
|
32083
33402
|
this.clock = deps.conciergeClock ?? (() => Date.now());
|
|
33403
|
+
if (deps.conciergeContextFetchers) {
|
|
33404
|
+
this.contextFetchers = deps.conciergeContextFetchers;
|
|
33405
|
+
}
|
|
33406
|
+
if (deps.conciergeContextLlmAssist) {
|
|
33407
|
+
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
33408
|
+
}
|
|
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
|
+
}
|
|
32084
33416
|
}
|
|
32085
33417
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32086
33418
|
/**
|
|
@@ -32139,11 +33471,13 @@ var OperatorChatService = class {
|
|
|
32139
33471
|
await this.memory.appendTurn(threadId, "user", trimmed).catch(() => {
|
|
32140
33472
|
});
|
|
32141
33473
|
}
|
|
33474
|
+
const parsedGrammar = await this.runGrammarParse(filterResult.filtered);
|
|
32142
33475
|
const start = Date.now();
|
|
32143
33476
|
let conciergeBody;
|
|
32144
33477
|
let servedBy = "disabled";
|
|
32145
33478
|
let displayLabel = "Concierge: substrate not configured";
|
|
32146
33479
|
let outcome = "substrate_disabled";
|
|
33480
|
+
let dynamicCategoriesIncluded = [];
|
|
32147
33481
|
if (!this.substrateSelector) {
|
|
32148
33482
|
conciergeBody = "Concierge unavailable. The substrate selector is not configured for this fortress. Pick a substrate in the Policy center to enable concierge replies.";
|
|
32149
33483
|
} else {
|
|
@@ -32155,7 +33489,15 @@ var OperatorChatService = class {
|
|
|
32155
33489
|
conciergeBody = "Concierge unavailable. The chosen substrate does not support summarization. Pick a different substrate in the Policy center.";
|
|
32156
33490
|
outcome = "substrate_disabled";
|
|
32157
33491
|
} else {
|
|
32158
|
-
const
|
|
33492
|
+
const dynamicResult = await this.runDynamicContextFold(
|
|
33493
|
+
filterResult.filtered,
|
|
33494
|
+
parsedGrammar
|
|
33495
|
+
);
|
|
33496
|
+
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
33497
|
+
const context = await this.assembleConciergeContext(
|
|
33498
|
+
priorTurns,
|
|
33499
|
+
dynamicResult.section
|
|
33500
|
+
);
|
|
32159
33501
|
const response = await this.substrateSelector.invokeSummarize(
|
|
32160
33502
|
"concierge",
|
|
32161
33503
|
{
|
|
@@ -32219,7 +33561,9 @@ var OperatorChatService = class {
|
|
|
32219
33561
|
...assistantTurnId !== void 0 ? { turn_index: assistantTurnId } : {},
|
|
32220
33562
|
...this.memory ? {
|
|
32221
33563
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32222
|
-
} : {}
|
|
33564
|
+
} : {},
|
|
33565
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {},
|
|
33566
|
+
parsed_grammar: auditSafeSummary(parsedGrammar)
|
|
32223
33567
|
};
|
|
32224
33568
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32225
33569
|
return {
|
|
@@ -32370,10 +33714,13 @@ var OperatorChatService = class {
|
|
|
32370
33714
|
* ## Sanctuary reference
|
|
32371
33715
|
* <static domain reference block>
|
|
32372
33716
|
*
|
|
33717
|
+
* ## Live fortress context ← WP-V1.3-9 Tau-3, when present
|
|
33718
|
+
* ### <Category>
|
|
33719
|
+
* <fetcher payload>
|
|
33720
|
+
*
|
|
32373
33721
|
* ## Prior conversation ← WP-V1.3-9 Tau-2, when present
|
|
32374
33722
|
* OPERATOR: ...
|
|
32375
33723
|
* CONCIERGE: ...
|
|
32376
|
-
* ---
|
|
32377
33724
|
*
|
|
32378
33725
|
* ## Recent activity
|
|
32379
33726
|
* <recentActivity output>
|
|
@@ -32392,13 +33739,14 @@ var OperatorChatService = class {
|
|
|
32392
33739
|
* if available; the v1.2 selector does not expose one, so structured
|
|
32393
33740
|
* serialization is the canonical path for v1.3.
|
|
32394
33741
|
*/
|
|
32395
|
-
async assembleConciergeContext(priorTurns = []) {
|
|
33742
|
+
async assembleConciergeContext(priorTurns = [], dynamicSection = "") {
|
|
32396
33743
|
const ref = `## Sanctuary reference
|
|
32397
33744
|
${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
32398
33745
|
const priorSection = this.formatPriorTurnsSection(priorTurns);
|
|
32399
33746
|
if (!this.contextProviders) {
|
|
32400
33747
|
return [
|
|
32401
33748
|
ref,
|
|
33749
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32402
33750
|
...priorSection ? [priorSection] : [],
|
|
32403
33751
|
"## Recent activity\n(no providers wired)",
|
|
32404
33752
|
"## Wrapped agents\n(no providers wired)",
|
|
@@ -32412,6 +33760,7 @@ ${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
|
32412
33760
|
]);
|
|
32413
33761
|
return [
|
|
32414
33762
|
ref,
|
|
33763
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32415
33764
|
...priorSection ? [priorSection] : [],
|
|
32416
33765
|
`## Recent activity
|
|
32417
33766
|
${activity}`,
|
|
@@ -32421,6 +33770,69 @@ ${agents}`,
|
|
|
32421
33770
|
${inbox}`
|
|
32422
33771
|
].join("\n\n");
|
|
32423
33772
|
}
|
|
33773
|
+
/**
|
|
33774
|
+
* Run the WP-V1.3-9 Tau-3 dynamic-context fold for a single round-
|
|
33775
|
+
* trip. Fail-soft on every axis: missing fetchers short-circuit to
|
|
33776
|
+
* an empty fold, fetcher failures emit a per-category audit event
|
|
33777
|
+
* and are omitted from the rendered section, an LLM-assist failure
|
|
33778
|
+
* proceeds with no fold. Returns the rendered section + the list of
|
|
33779
|
+
* categories whose data made it into the section (used for the
|
|
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.
|
|
33785
|
+
*/
|
|
33786
|
+
async runDynamicContextFold(query, parsedGrammar) {
|
|
33787
|
+
if (!this.contextFetchers) {
|
|
33788
|
+
return { section: "", categoriesIncluded: [] };
|
|
33789
|
+
}
|
|
33790
|
+
const result = await foldContext(query, this.contextFetchers, {
|
|
33791
|
+
maxTokens: this.dynamicContextBudget,
|
|
33792
|
+
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33793
|
+
onFetcherFailure: (category, error) => {
|
|
33794
|
+
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33795
|
+
},
|
|
33796
|
+
parsed: parsedGrammar
|
|
33797
|
+
});
|
|
33798
|
+
return result;
|
|
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
|
+
}
|
|
33813
|
+
/**
|
|
33814
|
+
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33815
|
+
* of the fold path so the dynamic-context handler stays readable.
|
|
33816
|
+
* Emits with `result: "failure"` since the named category dropped
|
|
33817
|
+
* from the rendered section for this round-trip.
|
|
33818
|
+
*/
|
|
33819
|
+
emitContextFetcherFailed(category, failureReason) {
|
|
33820
|
+
const payload = {
|
|
33821
|
+
version: "1.2",
|
|
33822
|
+
event_id: makeEventId("conc-ctxfail"),
|
|
33823
|
+
emitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33824
|
+
identity_id: this.identityId,
|
|
33825
|
+
kind: "operator_concierge_context_fetcher_failed",
|
|
33826
|
+
surface: "concierge",
|
|
33827
|
+
category,
|
|
33828
|
+
failure_reason: failureReason
|
|
33829
|
+
};
|
|
33830
|
+
this.emit(
|
|
33831
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
33832
|
+
payload,
|
|
33833
|
+
"failure"
|
|
33834
|
+
);
|
|
33835
|
+
}
|
|
32424
33836
|
/**
|
|
32425
33837
|
* Render the prior-conversation section with token-budget enforcement
|
|
32426
33838
|
* (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
|
|
@@ -32431,14 +33843,14 @@ ${inbox}`
|
|
|
32431
33843
|
if (turns.length === 0) return "";
|
|
32432
33844
|
const HEADER = "## Prior conversation";
|
|
32433
33845
|
const lines = turns.map(formatPriorTurnLine);
|
|
32434
|
-
const headerTokens =
|
|
33846
|
+
const headerTokens = approxTokenLen2(`${HEADER}
|
|
32435
33847
|
`);
|
|
32436
|
-
const sepTokens =
|
|
33848
|
+
const sepTokens = approxTokenLen2("\n");
|
|
32437
33849
|
let runningTokens = headerTokens;
|
|
32438
33850
|
let runningLines = [];
|
|
32439
33851
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
32440
33852
|
const line = lines[i];
|
|
32441
|
-
const tokens =
|
|
33853
|
+
const tokens = approxTokenLen2(line) + (runningLines.length > 0 ? sepTokens : 0);
|
|
32442
33854
|
if (runningTokens + tokens > this.historyTokenBudget) break;
|
|
32443
33855
|
runningTokens += tokens;
|
|
32444
33856
|
runningLines.push(line);
|
|
@@ -32462,6 +33874,17 @@ ${runningLines.join("\n")}`;
|
|
|
32462
33874
|
function makeEventId(prefix) {
|
|
32463
33875
|
return `${prefix}-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
32464
33876
|
}
|
|
33877
|
+
function classifyFetcherError(error) {
|
|
33878
|
+
const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
33879
|
+
if (msg.includes("timeout") || msg.includes("timed out")) return "timeout";
|
|
33880
|
+
if (msg.includes("schema") || msg.includes("invalid shape")) {
|
|
33881
|
+
return "schema_mismatch";
|
|
33882
|
+
}
|
|
33883
|
+
if (msg.includes("io") || msg.includes("read") || msg.includes("enoent") || msg.includes("eacces")) {
|
|
33884
|
+
return "io_failed";
|
|
33885
|
+
}
|
|
33886
|
+
return "unknown";
|
|
33887
|
+
}
|
|
32465
33888
|
function formatPriorTurnLine(turn) {
|
|
32466
33889
|
const label = turn.role === "user" ? "OPERATOR" : "CONCIERGE";
|
|
32467
33890
|
return `${label}: ${turn.content}`;
|
|
@@ -32474,7 +33897,7 @@ function hashOf(input) {
|
|
|
32474
33897
|
init_encryption();
|
|
32475
33898
|
init_encoding();
|
|
32476
33899
|
var OPERATOR_CHAT_NAMESPACE = "_chat";
|
|
32477
|
-
var
|
|
33900
|
+
var HKDF_INFO2 = "operator-chat-store-v1";
|
|
32478
33901
|
function chatStorageKey(surface, threadKey) {
|
|
32479
33902
|
return `${surface}.${threadKey}`;
|
|
32480
33903
|
}
|
|
@@ -32483,7 +33906,7 @@ var OperatorChatStore = class {
|
|
|
32483
33906
|
encryptionKey;
|
|
32484
33907
|
constructor(storage, masterKey) {
|
|
32485
33908
|
this.storage = storage;
|
|
32486
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33909
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO2);
|
|
32487
33910
|
}
|
|
32488
33911
|
/**
|
|
32489
33912
|
* Load a thread. Returns null if no record exists or if the on-disk
|
|
@@ -32567,9 +33990,9 @@ init_encryption();
|
|
|
32567
33990
|
init_encoding();
|
|
32568
33991
|
var CONCIERGE_MEMORY_NAMESPACE = "_chat";
|
|
32569
33992
|
var CONCIERGE_MEMORY_KEY_PREFIX = "concierge_memory.";
|
|
32570
|
-
var
|
|
33993
|
+
var HKDF_INFO3 = "concierge-memory-store-v1";
|
|
32571
33994
|
var DEFAULT_CONCIERGE_RETENTION_DAYS = 30;
|
|
32572
|
-
var
|
|
33995
|
+
var MAX_BUNDLE_BYTES3 = 4 * 1024 * 1024;
|
|
32573
33996
|
var ConciergeMemoryStore = class {
|
|
32574
33997
|
storage;
|
|
32575
33998
|
encryptionKey;
|
|
@@ -32578,7 +34001,7 @@ var ConciergeMemoryStore = class {
|
|
|
32578
34001
|
locks;
|
|
32579
34002
|
constructor(opts) {
|
|
32580
34003
|
this.storage = opts.storage;
|
|
32581
|
-
this.encryptionKey = derivePurposeKey(opts.masterKey,
|
|
34004
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO3);
|
|
32582
34005
|
this.fortressId = opts.fortressId;
|
|
32583
34006
|
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_CONCIERGE_RETENTION_DAYS;
|
|
32584
34007
|
this.locks = /* @__PURE__ */ new Map();
|
|
@@ -32657,7 +34080,7 @@ var ConciergeMemoryStore = class {
|
|
|
32657
34080
|
return { ok: false, reason: "io_failed" };
|
|
32658
34081
|
}
|
|
32659
34082
|
if (!raw) return { ok: true, turns: [] };
|
|
32660
|
-
if (raw.length >
|
|
34083
|
+
if (raw.length > MAX_BUNDLE_BYTES3) {
|
|
32661
34084
|
return { ok: false, reason: "oversize_bundle" };
|
|
32662
34085
|
}
|
|
32663
34086
|
let envelope;
|
|
@@ -32704,7 +34127,7 @@ var ConciergeMemoryStore = class {
|
|
|
32704
34127
|
);
|
|
32705
34128
|
const summaries = [];
|
|
32706
34129
|
for (const meta of entries) {
|
|
32707
|
-
const threadId =
|
|
34130
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32708
34131
|
if (threadId === null) continue;
|
|
32709
34132
|
const bundle = await this.loadBundle(threadId);
|
|
32710
34133
|
if (!bundle || bundle.turns.length === 0) continue;
|
|
@@ -32757,7 +34180,7 @@ var ConciergeMemoryStore = class {
|
|
|
32757
34180
|
);
|
|
32758
34181
|
let pruned = 0;
|
|
32759
34182
|
for (const meta of entries) {
|
|
32760
|
-
const threadId =
|
|
34183
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32761
34184
|
if (threadId === null) continue;
|
|
32762
34185
|
pruned += await this.withLock(threadId, async () => {
|
|
32763
34186
|
const bundle = await this.loadBundle(threadId);
|
|
@@ -32788,7 +34211,7 @@ var ConciergeMemoryStore = class {
|
|
|
32788
34211
|
return null;
|
|
32789
34212
|
}
|
|
32790
34213
|
if (!raw) return null;
|
|
32791
|
-
if (raw.length >
|
|
34214
|
+
if (raw.length > MAX_BUNDLE_BYTES3) return null;
|
|
32792
34215
|
try {
|
|
32793
34216
|
const envelope = JSON.parse(bytesToString(raw));
|
|
32794
34217
|
const aad = stringToBytes(threadId);
|
|
@@ -32841,7 +34264,7 @@ var ConciergeMemoryStore = class {
|
|
|
32841
34264
|
function bundleKey(threadId) {
|
|
32842
34265
|
return `${CONCIERGE_MEMORY_KEY_PREFIX}${threadId}`;
|
|
32843
34266
|
}
|
|
32844
|
-
function
|
|
34267
|
+
function stripKeyPrefix2(key) {
|
|
32845
34268
|
if (!key.startsWith(CONCIERGE_MEMORY_KEY_PREFIX)) return null;
|
|
32846
34269
|
return key.slice(CONCIERGE_MEMORY_KEY_PREFIX.length);
|
|
32847
34270
|
}
|
|
@@ -32910,7 +34333,18 @@ function buildV11Bindings(inputs) {
|
|
|
32910
34333
|
registry
|
|
32911
34334
|
}),
|
|
32912
34335
|
conciergePiiFilter: buildConciergePiiFilter(),
|
|
32913
|
-
conciergeMemory
|
|
34336
|
+
conciergeMemory,
|
|
34337
|
+
conciergeContextFetchers: buildConciergeContextFetchers({
|
|
34338
|
+
auditLog: inputs.auditLog,
|
|
34339
|
+
identityId: inputs.identityId,
|
|
34340
|
+
registry
|
|
34341
|
+
}),
|
|
34342
|
+
...inputs.intelligenceSelector ? {
|
|
34343
|
+
conciergeContextLlmAssist: buildConciergeContextLlmAssist({
|
|
34344
|
+
selector: inputs.intelligenceSelector,
|
|
34345
|
+
identityId: inputs.identityId
|
|
34346
|
+
})
|
|
34347
|
+
} : {}
|
|
32914
34348
|
});
|
|
32915
34349
|
}
|
|
32916
34350
|
const hubService = new HubService({
|
|
@@ -32971,6 +34405,107 @@ function buildConciergeContextProviders(args) {
|
|
|
32971
34405
|
}
|
|
32972
34406
|
};
|
|
32973
34407
|
}
|
|
34408
|
+
function buildConciergeContextFetchers(args) {
|
|
34409
|
+
const empty = async () => "";
|
|
34410
|
+
return {
|
|
34411
|
+
templates: async () => {
|
|
34412
|
+
const entries = listTemplates();
|
|
34413
|
+
if (entries.length === 0) return "(no templates installed)";
|
|
34414
|
+
const lines = entries.map((e) => {
|
|
34415
|
+
const m = e.metadata;
|
|
34416
|
+
return `${m.name} (tier ${m.tier}, channel ${m.channel}, target ${m.target_archetype})`;
|
|
34417
|
+
});
|
|
34418
|
+
return lines.join("\n");
|
|
34419
|
+
},
|
|
34420
|
+
agent_state: async (agentNameHint) => {
|
|
34421
|
+
const records = args.registry.list({ identity_id: args.identityId });
|
|
34422
|
+
if (records.length === 0) return "(no wrapped agents)";
|
|
34423
|
+
const filtered = agentNameHint ? records.filter(
|
|
34424
|
+
(r) => r.agent_id.toLowerCase().includes(agentNameHint.toLowerCase()) || r.harness.toLowerCase().includes(agentNameHint.toLowerCase())
|
|
34425
|
+
) : records;
|
|
34426
|
+
const target = filtered.length > 0 ? filtered : records;
|
|
34427
|
+
const lines = target.slice(0, 20).map((r) => {
|
|
34428
|
+
const tmpl = typeof r.channel_template_id === "string" ? r.channel_template_id : "no_template";
|
|
34429
|
+
return `${r.agent_id} harness=${r.harness} status=${r.status} template=${tmpl}`;
|
|
34430
|
+
});
|
|
34431
|
+
return lines.join("\n");
|
|
34432
|
+
},
|
|
34433
|
+
agent_activity: async (agentNameHint) => {
|
|
34434
|
+
const result = await args.auditLog.query({ limit: 50 });
|
|
34435
|
+
const owned = result.entries.filter(
|
|
34436
|
+
(e) => e.identity_id === args.identityId
|
|
34437
|
+
);
|
|
34438
|
+
const filtered = agentNameHint ? owned.filter((e) => {
|
|
34439
|
+
const agentId = e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : "";
|
|
34440
|
+
return agentId.toLowerCase().includes(agentNameHint.toLowerCase());
|
|
34441
|
+
}) : owned;
|
|
34442
|
+
const tail = (filtered.length > 0 ? filtered : owned).slice(-20);
|
|
34443
|
+
if (tail.length === 0) return "(no activity)";
|
|
34444
|
+
return tail.map((e) => {
|
|
34445
|
+
const agentId = (e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : null) ?? "_fortress";
|
|
34446
|
+
return `${e.timestamp} ${e.layer}.${e.operation} agent=${agentId} result=${e.result}`;
|
|
34447
|
+
}).join("\n");
|
|
34448
|
+
},
|
|
34449
|
+
audit_log: async () => {
|
|
34450
|
+
const result = await args.auditLog.query({ limit: 30 });
|
|
34451
|
+
const owned = result.entries.filter(
|
|
34452
|
+
(e) => e.identity_id === args.identityId
|
|
34453
|
+
);
|
|
34454
|
+
if (owned.length === 0) return "(no audit log entries)";
|
|
34455
|
+
return owned.slice(-30).map(
|
|
34456
|
+
(e) => `${e.timestamp} ${e.layer}.${e.operation} result=${e.result}`
|
|
34457
|
+
).join("\n");
|
|
34458
|
+
},
|
|
34459
|
+
sentinel_findings: empty,
|
|
34460
|
+
anomaly_alerts: empty,
|
|
34461
|
+
recent_receipts: async () => {
|
|
34462
|
+
const result = await args.auditLog.query({ limit: 100 });
|
|
34463
|
+
const owned = result.entries.filter(
|
|
34464
|
+
(e) => e.identity_id === args.identityId && e.operation.startsWith("composition_")
|
|
34465
|
+
);
|
|
34466
|
+
if (owned.length === 0) return "(no recent composition events)";
|
|
34467
|
+
return owned.slice(-15).map((e) => `${e.timestamp} ${e.operation} result=${e.result}`).join("\n");
|
|
34468
|
+
},
|
|
34469
|
+
verascore_deltas: empty
|
|
34470
|
+
};
|
|
34471
|
+
}
|
|
34472
|
+
function buildConciergeContextLlmAssist(args) {
|
|
34473
|
+
return async (query, categories) => {
|
|
34474
|
+
const labelList = categories.map((c) => `- ${c}`).join("\n");
|
|
34475
|
+
const prompt = `You are a router. Classify the operator's query into one of the categories below or "none".
|
|
34476
|
+
Reply with exactly one token: one category name or "none".
|
|
34477
|
+
|
|
34478
|
+
Categories:
|
|
34479
|
+
${labelList}
|
|
34480
|
+
|
|
34481
|
+
Query: ${query}
|
|
34482
|
+
|
|
34483
|
+
Category:`;
|
|
34484
|
+
try {
|
|
34485
|
+
const handle = await args.selector.getSubstrate("concierge");
|
|
34486
|
+
if (!handle.capability.summarize) return "none";
|
|
34487
|
+
const response = await args.selector.invokeSummarize("concierge", {
|
|
34488
|
+
kind: "summarize",
|
|
34489
|
+
context: prompt,
|
|
34490
|
+
query: "Output the single category token.",
|
|
34491
|
+
maxTokens: 16
|
|
34492
|
+
});
|
|
34493
|
+
if (response.failureClass || response.body.kind !== "summarize") {
|
|
34494
|
+
return "none";
|
|
34495
|
+
}
|
|
34496
|
+
const raw = response.body.text.trim().toLowerCase();
|
|
34497
|
+
const head = raw.split(/\s|[.,!?:;]/)[0] ?? "";
|
|
34498
|
+
const normalized = head.replace(/[^a-z_]/g, "");
|
|
34499
|
+
const known = categories;
|
|
34500
|
+
if (known.includes(normalized)) {
|
|
34501
|
+
return normalized;
|
|
34502
|
+
}
|
|
34503
|
+
return "none";
|
|
34504
|
+
} catch {
|
|
34505
|
+
return "none";
|
|
34506
|
+
}
|
|
34507
|
+
};
|
|
34508
|
+
}
|
|
32974
34509
|
function buildConciergePiiFilter() {
|
|
32975
34510
|
return {
|
|
32976
34511
|
filter(input) {
|
|
@@ -33102,13 +34637,13 @@ init_encryption();
|
|
|
33102
34637
|
init_encoding();
|
|
33103
34638
|
var INTELLIGENCE_NAMESPACE = "_intelligence";
|
|
33104
34639
|
var SUBSTRATE_CONFIG_KEY = "substrate-config";
|
|
33105
|
-
var
|
|
34640
|
+
var HKDF_INFO4 = "intelligence-substrate-config";
|
|
33106
34641
|
var IntelligenceConfigStore = class {
|
|
33107
34642
|
storage;
|
|
33108
34643
|
encryptionKey;
|
|
33109
34644
|
constructor(storage, masterKey) {
|
|
33110
34645
|
this.storage = storage;
|
|
33111
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
34646
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO4);
|
|
33112
34647
|
}
|
|
33113
34648
|
/**
|
|
33114
34649
|
* Load the operator's substrate config from disk. Returns the config
|
|
@@ -37054,12 +38589,18 @@ ${err.message}
|
|
|
37054
38589
|
} : void 0;
|
|
37055
38590
|
const fortressIdForAggregator = fortressIdFromStoragePath(config.storage_path);
|
|
37056
38591
|
const aggregatorIdentityId = identityManager.getPrimaryIdentityId() ?? `fortress:${config.storage_path}`;
|
|
38592
|
+
const aggregatorPayloadStore = new AggregatorPayloadStore({
|
|
38593
|
+
storage,
|
|
38594
|
+
masterKey,
|
|
38595
|
+
fortressId: fortressIdForAggregator
|
|
38596
|
+
});
|
|
37057
38597
|
const approvalAggregator = new ApprovalAggregator({
|
|
37058
38598
|
storage,
|
|
37059
38599
|
masterKey,
|
|
37060
38600
|
auditLog,
|
|
37061
38601
|
identityId: aggregatorIdentityId,
|
|
37062
|
-
fortressId: fortressIdForAggregator
|
|
38602
|
+
fortressId: fortressIdForAggregator,
|
|
38603
|
+
payloadStore: aggregatorPayloadStore
|
|
37063
38604
|
});
|
|
37064
38605
|
const wrappedApprovalChannel = new AggregatorBackedChannel({
|
|
37065
38606
|
underlying: approvalChannel,
|