@sanctuary-framework/mcp-server 1.2.5 → 1.2.6
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 +864 -35
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +864 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +847 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +239 -3
- package/dist/index.d.ts +239 -3
- package/dist/index.js +847 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -16312,6 +16312,27 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16312
16312
|
await handleStream2(deps, res);
|
|
16313
16313
|
return true;
|
|
16314
16314
|
}
|
|
16315
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16316
|
+
const limit = parseLimit2(
|
|
16317
|
+
url.searchParams.get("limit"),
|
|
16318
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16319
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16320
|
+
);
|
|
16321
|
+
const statusRaw = url.searchParams.get("status");
|
|
16322
|
+
const sinceTs = url.searchParams.get("since") ?? void 0;
|
|
16323
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16324
|
+
const filterStatus = statusRaw && isStatusFilter(statusRaw) && statusRaw !== "pending" ? statusRaw : void 0;
|
|
16325
|
+
const entries = await deps.aggregator.getHistory(
|
|
16326
|
+
{
|
|
16327
|
+
limit,
|
|
16328
|
+
...filterStatus !== void 0 ? { status: filterStatus } : {},
|
|
16329
|
+
...sinceTs !== void 0 ? { sinceTs } : {}
|
|
16330
|
+
},
|
|
16331
|
+
operatorId
|
|
16332
|
+
);
|
|
16333
|
+
writeJSON4(res, 200, { ok: true, data: { entries } });
|
|
16334
|
+
return true;
|
|
16335
|
+
}
|
|
16315
16336
|
if (method === "GET" && path === APPROVAL_INBOX_API_PREFIX) {
|
|
16316
16337
|
const limit = parseLimit2(
|
|
16317
16338
|
url.searchParams.get("limit"),
|
|
@@ -16334,11 +16355,39 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16334
16355
|
writeJSON4(res, 404, { ok: false, error: "not_found", path });
|
|
16335
16356
|
return true;
|
|
16336
16357
|
}
|
|
16337
|
-
if (method === "GET" && entryMatch.action ===
|
|
16338
|
-
const
|
|
16339
|
-
|
|
16340
|
-
(
|
|
16358
|
+
if (method === "GET" && entryMatch.action === "audit-trail") {
|
|
16359
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16360
|
+
if (!entry) {
|
|
16361
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16362
|
+
return true;
|
|
16363
|
+
}
|
|
16364
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16365
|
+
const trail = await deps.aggregator.getAuditTrail(
|
|
16366
|
+
entryMatch.aggregatorId,
|
|
16367
|
+
operatorId
|
|
16341
16368
|
);
|
|
16369
|
+
writeJSON4(res, 200, { ok: true, data: { entry, audit_trail: trail } });
|
|
16370
|
+
return true;
|
|
16371
|
+
}
|
|
16372
|
+
if (method === "GET" && entryMatch.action === "payload") {
|
|
16373
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16374
|
+
if (!entry) {
|
|
16375
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16376
|
+
return true;
|
|
16377
|
+
}
|
|
16378
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16379
|
+
const payload = await deps.aggregator.getFullPayloadWithAudit(
|
|
16380
|
+
entryMatch.aggregatorId,
|
|
16381
|
+
operatorId
|
|
16382
|
+
);
|
|
16383
|
+
writeJSON4(res, 200, {
|
|
16384
|
+
ok: true,
|
|
16385
|
+
data: { entry, request_payload: payload }
|
|
16386
|
+
});
|
|
16387
|
+
return true;
|
|
16388
|
+
}
|
|
16389
|
+
if (method === "GET" && entryMatch.action === null) {
|
|
16390
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16342
16391
|
if (!entry) {
|
|
16343
16392
|
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16344
16393
|
return true;
|
|
@@ -19325,7 +19374,10 @@ var APPROVAL_AGGREGATOR_HKDF_INFO = "l2-approval-aggregator-v1";
|
|
|
19325
19374
|
var APPROVAL_AGGREGATOR_AUDIT_OPS = {
|
|
19326
19375
|
AGGREGATED: "cross_harness_approval_aggregated",
|
|
19327
19376
|
RESOLVED: "cross_harness_approval_resolved",
|
|
19328
|
-
DEDUPED: "cross_harness_approval_deduped"
|
|
19377
|
+
DEDUPED: "cross_harness_approval_deduped",
|
|
19378
|
+
PAYLOAD_DECRYPTED: "cross_harness_approval_payload_decrypted",
|
|
19379
|
+
AUDIT_TRAIL_VIEWED: "cross_harness_approval_audit_trail_viewed",
|
|
19380
|
+
REPLAYED: "cross_harness_approval_replayed"
|
|
19329
19381
|
};
|
|
19330
19382
|
var DEFAULT_PENDING_TTL_MS = 5 * 60 * 1e3;
|
|
19331
19383
|
var DEFAULT_MAX_LIST_LIMIT = 200;
|
|
@@ -19341,6 +19393,8 @@ var ApprovalAggregator = class {
|
|
|
19341
19393
|
now;
|
|
19342
19394
|
resolveSourceContext;
|
|
19343
19395
|
resolveHubInboxItemId;
|
|
19396
|
+
payloadStore;
|
|
19397
|
+
resolveEnforcementChain;
|
|
19344
19398
|
/** Cached entries by `aggregator_id`. */
|
|
19345
19399
|
entries = /* @__PURE__ */ new Map();
|
|
19346
19400
|
/** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
|
|
@@ -19370,6 +19424,14 @@ var ApprovalAggregator = class {
|
|
|
19370
19424
|
source_agent_id: this.fortressId
|
|
19371
19425
|
}));
|
|
19372
19426
|
this.resolveHubInboxItemId = deps.resolveHubInboxItemId ?? ((_event) => void 0);
|
|
19427
|
+
this.payloadStore = deps.payloadStore ?? null;
|
|
19428
|
+
this.resolveEnforcementChain = deps.resolveEnforcementChain ?? ((event) => [
|
|
19429
|
+
{
|
|
19430
|
+
layer: "l2",
|
|
19431
|
+
event: `approval_required:${event.operation}`,
|
|
19432
|
+
timestamp: event.request_timestamp
|
|
19433
|
+
}
|
|
19434
|
+
]);
|
|
19373
19435
|
}
|
|
19374
19436
|
/**
|
|
19375
19437
|
* Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
|
|
@@ -19420,13 +19482,152 @@ var ApprovalAggregator = class {
|
|
|
19420
19482
|
}
|
|
19421
19483
|
/**
|
|
19422
19484
|
* Return the original (unhashed) request payload for the entry. Returns
|
|
19423
|
-
* `null` when the entry is unknown
|
|
19424
|
-
*
|
|
19485
|
+
* `null` when the entry is unknown. When the in-memory payload map has
|
|
19486
|
+
* been evicted (e.g. after a server restart) and a `payloadStore` was
|
|
19487
|
+
* provided, the at-rest bundle is decrypted and the in-memory map is
|
|
19488
|
+
* refilled. Audit emission lives on the `*WithAudit` variant; this base
|
|
19489
|
+
* accessor is silent so internal callers can read without polluting the
|
|
19490
|
+
* audit trail.
|
|
19425
19491
|
*/
|
|
19426
19492
|
async getFullPayload(aggregatorId) {
|
|
19427
19493
|
await this.hydrate();
|
|
19428
19494
|
if (!this.entries.has(aggregatorId)) return null;
|
|
19429
|
-
|
|
19495
|
+
const cached = this.fullPayloads.get(aggregatorId);
|
|
19496
|
+
if (cached !== void 0) return cached;
|
|
19497
|
+
if (this.payloadStore) {
|
|
19498
|
+
try {
|
|
19499
|
+
const restored = await this.payloadStore.loadPayload(aggregatorId);
|
|
19500
|
+
if (restored !== null) {
|
|
19501
|
+
this.fullPayloads.set(aggregatorId, restored);
|
|
19502
|
+
return restored;
|
|
19503
|
+
}
|
|
19504
|
+
} catch {
|
|
19505
|
+
}
|
|
19506
|
+
}
|
|
19507
|
+
return null;
|
|
19508
|
+
}
|
|
19509
|
+
/**
|
|
19510
|
+
* Return the entry record for the given id, or null when unknown.
|
|
19511
|
+
* Idempotent. v1.3 Upsilon-3.
|
|
19512
|
+
*/
|
|
19513
|
+
async getEntry(aggregatorId) {
|
|
19514
|
+
await this.hydrate();
|
|
19515
|
+
return this.entries.get(aggregatorId) ?? null;
|
|
19516
|
+
}
|
|
19517
|
+
/**
|
|
19518
|
+
* Audited variant of `getFullPayload`. Emits the
|
|
19519
|
+
* `cross_harness_approval_payload_decrypted` audit event before
|
|
19520
|
+
* returning. Used by the operator-facing /payload replay route.
|
|
19521
|
+
* v1.3 Upsilon-3.
|
|
19522
|
+
*/
|
|
19523
|
+
async getFullPayloadWithAudit(aggregatorId, operatorId) {
|
|
19524
|
+
const payload = await this.getFullPayload(aggregatorId);
|
|
19525
|
+
if (payload === null) return null;
|
|
19526
|
+
const entry = this.entries.get(aggregatorId);
|
|
19527
|
+
this.auditLog.append(
|
|
19528
|
+
"l2",
|
|
19529
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.PAYLOAD_DECRYPTED,
|
|
19530
|
+
operatorId,
|
|
19531
|
+
{
|
|
19532
|
+
aggregator_id: aggregatorId,
|
|
19533
|
+
...entry ? {
|
|
19534
|
+
source_harness: entry.source_harness,
|
|
19535
|
+
source_agent_id: entry.source_agent_id,
|
|
19536
|
+
entry_status: entry.status
|
|
19537
|
+
} : {}
|
|
19538
|
+
}
|
|
19539
|
+
);
|
|
19540
|
+
return payload;
|
|
19541
|
+
}
|
|
19542
|
+
/**
|
|
19543
|
+
* Return the audit-log entries that led to and surround this approval.
|
|
19544
|
+
* Best-effort matching: aggregator-side emissions (AGGREGATED, RESOLVED,
|
|
19545
|
+
* DEDUPED, replay events) all carry `details.aggregator_id` and link
|
|
19546
|
+
* directly. Gate-side emissions (`gate_*:operation`) do not carry the
|
|
19547
|
+
* aggregator id at v1.3, so they are matched via timestamp window
|
|
19548
|
+
* (entry.created_at to entry.resolved_at + 1s, or expires_at + 1s while
|
|
19549
|
+
* pending) and operation suffix. Emits AUDIT_TRAIL_VIEWED on call.
|
|
19550
|
+
* v1.3 Upsilon-3.
|
|
19551
|
+
*/
|
|
19552
|
+
async getAuditTrail(aggregatorId, operatorId) {
|
|
19553
|
+
await this.hydrate();
|
|
19554
|
+
const entry = this.entries.get(aggregatorId);
|
|
19555
|
+
if (!entry) {
|
|
19556
|
+
return [];
|
|
19557
|
+
}
|
|
19558
|
+
const sinceMs = Date.parse(entry.created_at) - 1e3;
|
|
19559
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
19560
|
+
const queried = await this.auditLog.query({ since: sinceIso, limit: 1e3 });
|
|
19561
|
+
const operationPart = entry.policy_rule_id.includes(":") ? entry.policy_rule_id.slice(entry.policy_rule_id.indexOf(":") + 1) : entry.policy_rule_id;
|
|
19562
|
+
const lifetimeStart = sinceMs;
|
|
19563
|
+
const lifetimeEnd = entry.resolved_at ? Date.parse(entry.resolved_at) + 1e3 : Date.parse(entry.expires_at) + 1e3;
|
|
19564
|
+
const matches = [];
|
|
19565
|
+
for (const audit of queried.entries) {
|
|
19566
|
+
const detailsId = audit.details !== void 0 ? audit.details["aggregator_id"] : void 0;
|
|
19567
|
+
if (detailsId === aggregatorId) {
|
|
19568
|
+
matches.push(audit);
|
|
19569
|
+
continue;
|
|
19570
|
+
}
|
|
19571
|
+
const auditMs = Date.parse(audit.timestamp);
|
|
19572
|
+
if (auditMs < lifetimeStart || auditMs > lifetimeEnd) continue;
|
|
19573
|
+
if (audit.operation.endsWith(`:${operationPart}`)) {
|
|
19574
|
+
matches.push(audit);
|
|
19575
|
+
}
|
|
19576
|
+
}
|
|
19577
|
+
matches.sort(
|
|
19578
|
+
(a, b) => a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0
|
|
19579
|
+
);
|
|
19580
|
+
this.auditLog.append(
|
|
19581
|
+
"l2",
|
|
19582
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.AUDIT_TRAIL_VIEWED,
|
|
19583
|
+
operatorId,
|
|
19584
|
+
{
|
|
19585
|
+
aggregator_id: aggregatorId,
|
|
19586
|
+
entry_status: entry.status,
|
|
19587
|
+
match_count: matches.length
|
|
19588
|
+
}
|
|
19589
|
+
);
|
|
19590
|
+
return matches;
|
|
19591
|
+
}
|
|
19592
|
+
/**
|
|
19593
|
+
* List historical (resolved) approvals. Excludes pending entries by
|
|
19594
|
+
* design: `list()` is the pending-inbox surface and `getHistory()` is
|
|
19595
|
+
* the resolved-replay surface. Emits REPLAYED on each call. v1.3
|
|
19596
|
+
* Upsilon-3.
|
|
19597
|
+
*/
|
|
19598
|
+
async getHistory(opts, operatorId) {
|
|
19599
|
+
await this.hydrate();
|
|
19600
|
+
await this.expireStale();
|
|
19601
|
+
const limit = Math.min(
|
|
19602
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19603
|
+
this.maxListLimit
|
|
19604
|
+
);
|
|
19605
|
+
const sinceMs = opts?.sinceTs ? Date.parse(opts.sinceTs) : Number.NEGATIVE_INFINITY;
|
|
19606
|
+
const matching = [];
|
|
19607
|
+
for (const entry of this.entries.values()) {
|
|
19608
|
+
if (entry.status === "pending") continue;
|
|
19609
|
+
if (opts?.status && entry.status !== opts.status) continue;
|
|
19610
|
+
const stamp = Date.parse(entry.resolved_at ?? entry.created_at);
|
|
19611
|
+
if (stamp < sinceMs) continue;
|
|
19612
|
+
matching.push(entry);
|
|
19613
|
+
}
|
|
19614
|
+
matching.sort((a, b) => {
|
|
19615
|
+
const aStamp = a.resolved_at ?? a.created_at;
|
|
19616
|
+
const bStamp = b.resolved_at ?? b.created_at;
|
|
19617
|
+
return bStamp.localeCompare(aStamp);
|
|
19618
|
+
});
|
|
19619
|
+
const sliced = matching.slice(0, limit);
|
|
19620
|
+
this.auditLog.append(
|
|
19621
|
+
"l2",
|
|
19622
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.REPLAYED,
|
|
19623
|
+
operatorId,
|
|
19624
|
+
{
|
|
19625
|
+
result_count: sliced.length,
|
|
19626
|
+
...opts?.status !== void 0 ? { status_filter: opts.status } : {},
|
|
19627
|
+
...opts?.sinceTs !== void 0 ? { since: opts.sinceTs } : {}
|
|
19628
|
+
}
|
|
19629
|
+
);
|
|
19630
|
+
return sliced;
|
|
19430
19631
|
}
|
|
19431
19632
|
/**
|
|
19432
19633
|
* Resolve an entry. Used by both:
|
|
@@ -19500,6 +19701,7 @@ var ApprovalAggregator = class {
|
|
|
19500
19701
|
const now = this.now();
|
|
19501
19702
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19502
19703
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19704
|
+
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19503
19705
|
const entry = {
|
|
19504
19706
|
aggregator_id: id,
|
|
19505
19707
|
source_harness: ctx.source_harness,
|
|
@@ -19511,13 +19713,20 @@ var ApprovalAggregator = class {
|
|
|
19511
19713
|
status: "pending",
|
|
19512
19714
|
created_at: now.toISOString(),
|
|
19513
19715
|
expires_at: expires.toISOString(),
|
|
19514
|
-
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {}
|
|
19716
|
+
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19717
|
+
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19515
19718
|
};
|
|
19516
19719
|
this.entries.set(id, entry);
|
|
19517
19720
|
this.dedupIndex.set(dedupKey, id);
|
|
19518
19721
|
this.correlationIndex.set(event.correlation_id, id);
|
|
19519
19722
|
this.fullPayloads.set(id, event.context);
|
|
19520
19723
|
await this.persist(entry);
|
|
19724
|
+
if (this.payloadStore) {
|
|
19725
|
+
try {
|
|
19726
|
+
await this.payloadStore.savePayload(id, event.context);
|
|
19727
|
+
} catch {
|
|
19728
|
+
}
|
|
19729
|
+
}
|
|
19521
19730
|
this.auditLog.append(
|
|
19522
19731
|
"l2",
|
|
19523
19732
|
APPROVAL_AGGREGATOR_AUDIT_OPS.AGGREGATED,
|
|
@@ -19826,6 +20035,143 @@ function makeRedirectResolverFromPolicySupplier(supplier) {
|
|
|
19826
20035
|
};
|
|
19827
20036
|
}
|
|
19828
20037
|
|
|
20038
|
+
// src/principal-policy/aggregator-store.ts
|
|
20039
|
+
init_encryption();
|
|
20040
|
+
init_encoding();
|
|
20041
|
+
var AGGREGATOR_PAYLOAD_NAMESPACE = "_approval_aggregator_payloads";
|
|
20042
|
+
var AGGREGATOR_PAYLOAD_KEY_PREFIX = "payload.";
|
|
20043
|
+
var HKDF_INFO = "l2-approval-aggregator-payload-v1";
|
|
20044
|
+
var DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS = 30;
|
|
20045
|
+
var MAX_BUNDLE_BYTES2 = 4 * 1024 * 1024;
|
|
20046
|
+
var AggregatorPayloadStore = class {
|
|
20047
|
+
storage;
|
|
20048
|
+
encryptionKey;
|
|
20049
|
+
fortressId;
|
|
20050
|
+
retentionDays;
|
|
20051
|
+
constructor(opts) {
|
|
20052
|
+
this.storage = opts.storage;
|
|
20053
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO);
|
|
20054
|
+
this.fortressId = opts.fortressId;
|
|
20055
|
+
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS;
|
|
20056
|
+
}
|
|
20057
|
+
/**
|
|
20058
|
+
* Persist `payload` under the given aggregator_id. Idempotent; calling
|
|
20059
|
+
* twice with the same id rewrites the bundle (retention_until is
|
|
20060
|
+
* recomputed). Returns the bundle's retention_until ISO-8601 timestamp
|
|
20061
|
+
* so callers can log it.
|
|
20062
|
+
*/
|
|
20063
|
+
async savePayload(aggregatorId, payload) {
|
|
20064
|
+
const now = /* @__PURE__ */ new Date();
|
|
20065
|
+
const retentionMs = this.retentionDays * 24 * 60 * 60 * 1e3;
|
|
20066
|
+
const retentionUntil = new Date(now.getTime() + retentionMs);
|
|
20067
|
+
const bundle = {
|
|
20068
|
+
version: 1,
|
|
20069
|
+
aggregator_id: aggregatorId,
|
|
20070
|
+
fortress_id: this.fortressId,
|
|
20071
|
+
created_at: now.toISOString(),
|
|
20072
|
+
retention_until: retentionUntil.toISOString(),
|
|
20073
|
+
payload
|
|
20074
|
+
};
|
|
20075
|
+
const aad = stringToBytes(aggregatorId);
|
|
20076
|
+
const plaintext = stringToBytes(JSON.stringify(bundle));
|
|
20077
|
+
const envelope = encrypt(plaintext, this.encryptionKey, aad);
|
|
20078
|
+
await this.storage.write(
|
|
20079
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20080
|
+
payloadKey(aggregatorId),
|
|
20081
|
+
stringToBytes(JSON.stringify(envelope))
|
|
20082
|
+
);
|
|
20083
|
+
return bundle.retention_until;
|
|
20084
|
+
}
|
|
20085
|
+
/**
|
|
20086
|
+
* Read the persisted payload for the aggregator_id. Returns null if no
|
|
20087
|
+
* bundle exists, the bundle is corrupted, or AAD binding fails.
|
|
20088
|
+
*/
|
|
20089
|
+
async loadPayload(aggregatorId) {
|
|
20090
|
+
const key = payloadKey(aggregatorId);
|
|
20091
|
+
let raw;
|
|
20092
|
+
try {
|
|
20093
|
+
raw = await this.storage.read(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20094
|
+
} catch {
|
|
20095
|
+
return null;
|
|
20096
|
+
}
|
|
20097
|
+
if (!raw) return null;
|
|
20098
|
+
if (raw.length > MAX_BUNDLE_BYTES2) return null;
|
|
20099
|
+
try {
|
|
20100
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20101
|
+
const aad = stringToBytes(aggregatorId);
|
|
20102
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20103
|
+
const parsed = JSON.parse(
|
|
20104
|
+
bytesToString(plaintext)
|
|
20105
|
+
);
|
|
20106
|
+
if (parsed.version !== 1) return null;
|
|
20107
|
+
if (parsed.aggregator_id !== aggregatorId) return null;
|
|
20108
|
+
return parsed.payload;
|
|
20109
|
+
} catch {
|
|
20110
|
+
return null;
|
|
20111
|
+
}
|
|
20112
|
+
}
|
|
20113
|
+
/**
|
|
20114
|
+
* Delete the persisted payload. Returns true when a bundle was removed,
|
|
20115
|
+
* false when none existed.
|
|
20116
|
+
*/
|
|
20117
|
+
async deletePayload(aggregatorId) {
|
|
20118
|
+
const key = payloadKey(aggregatorId);
|
|
20119
|
+
const existed = await this.storage.exists(
|
|
20120
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20121
|
+
key
|
|
20122
|
+
);
|
|
20123
|
+
if (!existed) return false;
|
|
20124
|
+
try {
|
|
20125
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20126
|
+
} catch {
|
|
20127
|
+
return false;
|
|
20128
|
+
}
|
|
20129
|
+
return true;
|
|
20130
|
+
}
|
|
20131
|
+
/**
|
|
20132
|
+
* Drop expired payload bundles. Returns the count of bundles pruned.
|
|
20133
|
+
* Caller wires this into the cocoon-unlock initialization path.
|
|
20134
|
+
*/
|
|
20135
|
+
async pruneExpired(now) {
|
|
20136
|
+
const cutoff = (now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
20137
|
+
const entries = await this.storage.list(
|
|
20138
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20139
|
+
AGGREGATOR_PAYLOAD_KEY_PREFIX
|
|
20140
|
+
);
|
|
20141
|
+
let pruned = 0;
|
|
20142
|
+
for (const meta of entries) {
|
|
20143
|
+
const aggregatorId = stripKeyPrefix(meta.key);
|
|
20144
|
+
if (aggregatorId === null) continue;
|
|
20145
|
+
const raw = await this.storage.read(
|
|
20146
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20147
|
+
meta.key
|
|
20148
|
+
);
|
|
20149
|
+
if (!raw) continue;
|
|
20150
|
+
try {
|
|
20151
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20152
|
+
const aad = stringToBytes(aggregatorId);
|
|
20153
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20154
|
+
const parsed = JSON.parse(
|
|
20155
|
+
bytesToString(plaintext)
|
|
20156
|
+
);
|
|
20157
|
+
if (parsed.retention_until <= cutoff) {
|
|
20158
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, meta.key);
|
|
20159
|
+
pruned += 1;
|
|
20160
|
+
}
|
|
20161
|
+
} catch {
|
|
20162
|
+
}
|
|
20163
|
+
}
|
|
20164
|
+
return { pruned };
|
|
20165
|
+
}
|
|
20166
|
+
};
|
|
20167
|
+
function payloadKey(aggregatorId) {
|
|
20168
|
+
return `${AGGREGATOR_PAYLOAD_KEY_PREFIX}${aggregatorId}`;
|
|
20169
|
+
}
|
|
20170
|
+
function stripKeyPrefix(key) {
|
|
20171
|
+
if (!key.startsWith(AGGREGATOR_PAYLOAD_KEY_PREFIX)) return null;
|
|
20172
|
+
return key.slice(AGGREGATOR_PAYLOAD_KEY_PREFIX.length);
|
|
20173
|
+
}
|
|
20174
|
+
|
|
19829
20175
|
// src/principal-policy/tools.ts
|
|
19830
20176
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
19831
20177
|
return [
|
|
@@ -31986,20 +32332,291 @@ var OPERATOR_CHAT_OPS = {
|
|
|
31986
32332
|
* turns; the concierge degrades to single-turn after emitting. Body
|
|
31987
32333
|
* carries thread_id + a stable failure_reason enum.
|
|
31988
32334
|
*/
|
|
31989
|
-
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed"
|
|
32335
|
+
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed",
|
|
32336
|
+
/**
|
|
32337
|
+
* Concierge dynamic-context fetcher failed (WP-V1.3-9 Tau-3). Emitted
|
|
32338
|
+
* when a category fetcher throws while assembling the dynamic context
|
|
32339
|
+
* fold. The concierge omits that category and continues; the user-
|
|
32340
|
+
* facing query is never broken. Body carries category + failure_reason.
|
|
32341
|
+
*/
|
|
32342
|
+
CONCIERGE_CONTEXT_FETCHER_FAILED: "operator_concierge_context_fetcher_failed"
|
|
31990
32343
|
};
|
|
31991
32344
|
|
|
31992
32345
|
// src/chat/operator-chat-types.ts
|
|
31993
32346
|
var OPERATOR_CHAT_MAX_THREAD_LENGTH = 500;
|
|
31994
32347
|
var CONCIERGE_THREAD_KEY = "_fortress";
|
|
31995
32348
|
|
|
32349
|
+
// src/chat/concierge-context-router.ts
|
|
32350
|
+
var APPROX_CHARS_PER_TOKEN = 4;
|
|
32351
|
+
var DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET = 2e3;
|
|
32352
|
+
var DYNAMIC_CONTEXT_SECTION_HEADER = "## Live fortress context";
|
|
32353
|
+
var CONTEXT_CATEGORIES = [
|
|
32354
|
+
"templates",
|
|
32355
|
+
"agent_state",
|
|
32356
|
+
"agent_activity",
|
|
32357
|
+
"audit_log",
|
|
32358
|
+
"sentinel_findings",
|
|
32359
|
+
"anomaly_alerts",
|
|
32360
|
+
"recent_receipts",
|
|
32361
|
+
"verascore_deltas"
|
|
32362
|
+
];
|
|
32363
|
+
function phrasePattern(phrase) {
|
|
32364
|
+
const escaped = phrase.toLowerCase().split(/\s+/).map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
|
|
32365
|
+
return { source: `\\b${escaped}\\b`, phrase };
|
|
32366
|
+
}
|
|
32367
|
+
var CATEGORY_KEYWORDS = [
|
|
32368
|
+
{
|
|
32369
|
+
category: "templates",
|
|
32370
|
+
patterns: [
|
|
32371
|
+
"templates",
|
|
32372
|
+
"template",
|
|
32373
|
+
"channel templates",
|
|
32374
|
+
"channel template",
|
|
32375
|
+
"list templates",
|
|
32376
|
+
"available templates",
|
|
32377
|
+
"what templates"
|
|
32378
|
+
].map(phrasePattern)
|
|
32379
|
+
},
|
|
32380
|
+
{
|
|
32381
|
+
category: "agent_state",
|
|
32382
|
+
patterns: [
|
|
32383
|
+
"state",
|
|
32384
|
+
"status",
|
|
32385
|
+
"agent state",
|
|
32386
|
+
"agent status",
|
|
32387
|
+
"status of agent",
|
|
32388
|
+
"status of agents",
|
|
32389
|
+
"state of",
|
|
32390
|
+
"doing"
|
|
32391
|
+
].map(phrasePattern)
|
|
32392
|
+
},
|
|
32393
|
+
{
|
|
32394
|
+
category: "agent_activity",
|
|
32395
|
+
patterns: [
|
|
32396
|
+
"activity",
|
|
32397
|
+
"agent activity",
|
|
32398
|
+
"what did",
|
|
32399
|
+
"recent activity"
|
|
32400
|
+
].map(phrasePattern)
|
|
32401
|
+
},
|
|
32402
|
+
{
|
|
32403
|
+
category: "audit_log",
|
|
32404
|
+
patterns: [
|
|
32405
|
+
"audit log",
|
|
32406
|
+
"audit",
|
|
32407
|
+
"log entry",
|
|
32408
|
+
"log entries",
|
|
32409
|
+
"what happened",
|
|
32410
|
+
"show me events",
|
|
32411
|
+
"event class"
|
|
32412
|
+
].map(phrasePattern)
|
|
32413
|
+
},
|
|
32414
|
+
{
|
|
32415
|
+
category: "sentinel_findings",
|
|
32416
|
+
patterns: [
|
|
32417
|
+
"sentinel",
|
|
32418
|
+
"sentinels",
|
|
32419
|
+
"warning",
|
|
32420
|
+
"warnings",
|
|
32421
|
+
"alert",
|
|
32422
|
+
"alerts",
|
|
32423
|
+
"whats wrong",
|
|
32424
|
+
"what's wrong",
|
|
32425
|
+
"findings"
|
|
32426
|
+
].map(phrasePattern)
|
|
32427
|
+
},
|
|
32428
|
+
{
|
|
32429
|
+
category: "anomaly_alerts",
|
|
32430
|
+
patterns: [
|
|
32431
|
+
"anomaly",
|
|
32432
|
+
"anomalies",
|
|
32433
|
+
"spike",
|
|
32434
|
+
"unusual",
|
|
32435
|
+
"outlier"
|
|
32436
|
+
].map(phrasePattern)
|
|
32437
|
+
},
|
|
32438
|
+
{
|
|
32439
|
+
category: "recent_receipts",
|
|
32440
|
+
patterns: [
|
|
32441
|
+
"receipt",
|
|
32442
|
+
"receipts",
|
|
32443
|
+
"concordia",
|
|
32444
|
+
"commitment",
|
|
32445
|
+
"commitments",
|
|
32446
|
+
"chain",
|
|
32447
|
+
"chains"
|
|
32448
|
+
].map(phrasePattern)
|
|
32449
|
+
},
|
|
32450
|
+
{
|
|
32451
|
+
category: "verascore_deltas",
|
|
32452
|
+
patterns: [
|
|
32453
|
+
"verascore",
|
|
32454
|
+
"vera score",
|
|
32455
|
+
"trust score",
|
|
32456
|
+
"reputation"
|
|
32457
|
+
].map(phrasePattern)
|
|
32458
|
+
}
|
|
32459
|
+
];
|
|
32460
|
+
function extractAgentNameHint(query) {
|
|
32461
|
+
const agentPattern = /\bagent\s+["']?([A-Za-z][\w-]{0,40})["']?/i;
|
|
32462
|
+
const m = query.match(agentPattern);
|
|
32463
|
+
if (m && m[1]) return m[1];
|
|
32464
|
+
const quoted = query.match(/["']([A-Za-z][\w-]{0,40})["']/);
|
|
32465
|
+
if (quoted && quoted[1]) return quoted[1];
|
|
32466
|
+
return null;
|
|
32467
|
+
}
|
|
32468
|
+
var TRIVIAL_GREETINGS = /* @__PURE__ */ new Set([
|
|
32469
|
+
"hi",
|
|
32470
|
+
"hello",
|
|
32471
|
+
"hey",
|
|
32472
|
+
"yo",
|
|
32473
|
+
"ok",
|
|
32474
|
+
"thanks",
|
|
32475
|
+
"thx",
|
|
32476
|
+
"thank you"
|
|
32477
|
+
]);
|
|
32478
|
+
function isTrivialQuery(query) {
|
|
32479
|
+
const norm = query.trim().toLowerCase();
|
|
32480
|
+
if (norm.length === 0) return true;
|
|
32481
|
+
if (norm.length < 8) return true;
|
|
32482
|
+
return TRIVIAL_GREETINGS.has(norm);
|
|
32483
|
+
}
|
|
32484
|
+
function classifyQuery(query) {
|
|
32485
|
+
const normalized = query.toLowerCase();
|
|
32486
|
+
const matches = [];
|
|
32487
|
+
for (const spec of CATEGORY_KEYWORDS) {
|
|
32488
|
+
const matchedPhrases = [];
|
|
32489
|
+
for (const pattern of spec.patterns) {
|
|
32490
|
+
if (matchedPhrases.includes(pattern.phrase)) continue;
|
|
32491
|
+
const re = new RegExp(pattern.source, "i");
|
|
32492
|
+
if (re.test(normalized)) {
|
|
32493
|
+
matchedPhrases.push(pattern.phrase);
|
|
32494
|
+
}
|
|
32495
|
+
}
|
|
32496
|
+
if (matchedPhrases.length === 0) continue;
|
|
32497
|
+
const confidence = Math.min(1, 0.4 + 0.3 * matchedPhrases.length);
|
|
32498
|
+
matches.push({
|
|
32499
|
+
category: spec.category,
|
|
32500
|
+
confidence,
|
|
32501
|
+
matched_keywords: matchedPhrases,
|
|
32502
|
+
agent_name_hint: spec.category === "agent_state" || spec.category === "agent_activity" ? extractAgentNameHint(query) : null
|
|
32503
|
+
});
|
|
32504
|
+
}
|
|
32505
|
+
matches.sort((a, b) => {
|
|
32506
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
32507
|
+
return CONTEXT_CATEGORIES.indexOf(a.category) - CONTEXT_CATEGORIES.indexOf(b.category);
|
|
32508
|
+
});
|
|
32509
|
+
return matches;
|
|
32510
|
+
}
|
|
32511
|
+
function approxTokenLen(text) {
|
|
32512
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32513
|
+
}
|
|
32514
|
+
var CATEGORY_LABELS = {
|
|
32515
|
+
templates: "Templates",
|
|
32516
|
+
agent_state: "Agent state",
|
|
32517
|
+
agent_activity: "Agent activity",
|
|
32518
|
+
audit_log: "Audit log",
|
|
32519
|
+
sentinel_findings: "Sentinel findings",
|
|
32520
|
+
anomaly_alerts: "Anomaly alerts",
|
|
32521
|
+
recent_receipts: "Recent receipts",
|
|
32522
|
+
verascore_deltas: "Verascore deltas"
|
|
32523
|
+
};
|
|
32524
|
+
async function runFetcher(match, fetchers) {
|
|
32525
|
+
switch (match.category) {
|
|
32526
|
+
case "templates":
|
|
32527
|
+
return fetchers.templates();
|
|
32528
|
+
case "agent_state":
|
|
32529
|
+
return fetchers.agent_state(match.agent_name_hint);
|
|
32530
|
+
case "agent_activity":
|
|
32531
|
+
return fetchers.agent_activity(match.agent_name_hint);
|
|
32532
|
+
case "audit_log":
|
|
32533
|
+
return fetchers.audit_log();
|
|
32534
|
+
case "sentinel_findings":
|
|
32535
|
+
return fetchers.sentinel_findings();
|
|
32536
|
+
case "anomaly_alerts":
|
|
32537
|
+
return fetchers.anomaly_alerts();
|
|
32538
|
+
case "recent_receipts":
|
|
32539
|
+
return fetchers.recent_receipts();
|
|
32540
|
+
case "verascore_deltas":
|
|
32541
|
+
return fetchers.verascore_deltas();
|
|
32542
|
+
}
|
|
32543
|
+
}
|
|
32544
|
+
function trivialMatch(category) {
|
|
32545
|
+
return {
|
|
32546
|
+
category,
|
|
32547
|
+
confidence: 0.5,
|
|
32548
|
+
matched_keywords: ["llm-assist"],
|
|
32549
|
+
agent_name_hint: null
|
|
32550
|
+
};
|
|
32551
|
+
}
|
|
32552
|
+
async function foldContext(query, fetchers, opts) {
|
|
32553
|
+
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32554
|
+
let matches = classifyQuery(query);
|
|
32555
|
+
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32556
|
+
try {
|
|
32557
|
+
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32558
|
+
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32559
|
+
matches = [trivialMatch(picked)];
|
|
32560
|
+
}
|
|
32561
|
+
} catch {
|
|
32562
|
+
}
|
|
32563
|
+
}
|
|
32564
|
+
if (matches.length === 0) {
|
|
32565
|
+
return { section: "", categoriesIncluded: [] };
|
|
32566
|
+
}
|
|
32567
|
+
const attempts = [];
|
|
32568
|
+
for (const match of matches) {
|
|
32569
|
+
try {
|
|
32570
|
+
const text = await runFetcher(match, fetchers);
|
|
32571
|
+
const trimmed = text.trim();
|
|
32572
|
+
if (trimmed.length > 0) {
|
|
32573
|
+
attempts.push({ category: match.category, text: trimmed });
|
|
32574
|
+
}
|
|
32575
|
+
} catch (err) {
|
|
32576
|
+
opts?.onFetcherFailure?.(match.category, err);
|
|
32577
|
+
}
|
|
32578
|
+
}
|
|
32579
|
+
if (attempts.length === 0) {
|
|
32580
|
+
return { section: "", categoriesIncluded: [] };
|
|
32581
|
+
}
|
|
32582
|
+
const headerTokens = approxTokenLen(`${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32583
|
+
`);
|
|
32584
|
+
const sepTokens = approxTokenLen("\n\n");
|
|
32585
|
+
let runningTokens = headerTokens;
|
|
32586
|
+
const kept = [];
|
|
32587
|
+
for (const attempt of attempts) {
|
|
32588
|
+
const block = `### ${CATEGORY_LABELS[attempt.category]}
|
|
32589
|
+
${attempt.text}`;
|
|
32590
|
+
const tokens = approxTokenLen(block) + (kept.length > 0 ? sepTokens : 0);
|
|
32591
|
+
if (kept.length === 0) {
|
|
32592
|
+
kept.push(attempt);
|
|
32593
|
+
runningTokens += tokens;
|
|
32594
|
+
continue;
|
|
32595
|
+
}
|
|
32596
|
+
if (runningTokens + tokens > budget) break;
|
|
32597
|
+
kept.push(attempt);
|
|
32598
|
+
runningTokens += tokens;
|
|
32599
|
+
}
|
|
32600
|
+
const blocks = kept.map(
|
|
32601
|
+
(k) => `### ${CATEGORY_LABELS[k.category]}
|
|
32602
|
+
${k.text}`
|
|
32603
|
+
);
|
|
32604
|
+
const section = `${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32605
|
+
${blocks.join("\n\n")}`;
|
|
32606
|
+
return {
|
|
32607
|
+
section,
|
|
32608
|
+
categoriesIncluded: kept.map((k) => k.category)
|
|
32609
|
+
};
|
|
32610
|
+
}
|
|
32611
|
+
|
|
31996
32612
|
// src/chat/operator-chat-service.ts
|
|
31997
32613
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
31998
32614
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
31999
32615
|
var DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS = 24 * 60 * 60 * 1e3;
|
|
32000
32616
|
var DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET = 500;
|
|
32001
32617
|
var DEFAULT_CONCIERGE_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32002
|
-
|
|
32618
|
+
var DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET = 2e3;
|
|
32619
|
+
function approxTokenLen2(text) {
|
|
32003
32620
|
return Math.ceil(text.length / 4);
|
|
32004
32621
|
}
|
|
32005
32622
|
var SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
|
|
@@ -32043,6 +32660,9 @@ var OperatorChatService = class {
|
|
|
32043
32660
|
historyTokenBudget;
|
|
32044
32661
|
sessionTtlMs;
|
|
32045
32662
|
clock;
|
|
32663
|
+
contextFetchers;
|
|
32664
|
+
contextLlmAssist;
|
|
32665
|
+
dynamicContextBudget;
|
|
32046
32666
|
/**
|
|
32047
32667
|
* In-memory thread_id assigned to the active concierge session.
|
|
32048
32668
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32074,6 +32694,13 @@ var OperatorChatService = class {
|
|
|
32074
32694
|
this.historyTokenBudget = deps.conciergeHistoryTokenBudget !== void 0 && deps.conciergeHistoryTokenBudget > 0 ? deps.conciergeHistoryTokenBudget : DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET;
|
|
32075
32695
|
this.sessionTtlMs = deps.conciergeSessionTtlMs !== void 0 && deps.conciergeSessionTtlMs > 0 ? deps.conciergeSessionTtlMs : DEFAULT_CONCIERGE_SESSION_TTL_MS;
|
|
32076
32696
|
this.clock = deps.conciergeClock ?? (() => Date.now());
|
|
32697
|
+
if (deps.conciergeContextFetchers) {
|
|
32698
|
+
this.contextFetchers = deps.conciergeContextFetchers;
|
|
32699
|
+
}
|
|
32700
|
+
if (deps.conciergeContextLlmAssist) {
|
|
32701
|
+
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
32702
|
+
}
|
|
32703
|
+
this.dynamicContextBudget = deps.conciergeDynamicContextBudget !== void 0 && deps.conciergeDynamicContextBudget > 0 ? deps.conciergeDynamicContextBudget : DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET;
|
|
32077
32704
|
}
|
|
32078
32705
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32079
32706
|
/**
|
|
@@ -32137,6 +32764,7 @@ var OperatorChatService = class {
|
|
|
32137
32764
|
let servedBy = "disabled";
|
|
32138
32765
|
let displayLabel = "Concierge: substrate not configured";
|
|
32139
32766
|
let outcome = "substrate_disabled";
|
|
32767
|
+
let dynamicCategoriesIncluded = [];
|
|
32140
32768
|
if (!this.substrateSelector) {
|
|
32141
32769
|
conciergeBody = "Concierge unavailable. The substrate selector is not configured for this fortress. Pick a substrate in the Policy center to enable concierge replies.";
|
|
32142
32770
|
} else {
|
|
@@ -32148,7 +32776,14 @@ var OperatorChatService = class {
|
|
|
32148
32776
|
conciergeBody = "Concierge unavailable. The chosen substrate does not support summarization. Pick a different substrate in the Policy center.";
|
|
32149
32777
|
outcome = "substrate_disabled";
|
|
32150
32778
|
} else {
|
|
32151
|
-
const
|
|
32779
|
+
const dynamicResult = await this.runDynamicContextFold(
|
|
32780
|
+
filterResult.filtered
|
|
32781
|
+
);
|
|
32782
|
+
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
32783
|
+
const context = await this.assembleConciergeContext(
|
|
32784
|
+
priorTurns,
|
|
32785
|
+
dynamicResult.section
|
|
32786
|
+
);
|
|
32152
32787
|
const response = await this.substrateSelector.invokeSummarize(
|
|
32153
32788
|
"concierge",
|
|
32154
32789
|
{
|
|
@@ -32212,7 +32847,8 @@ var OperatorChatService = class {
|
|
|
32212
32847
|
...assistantTurnId !== void 0 ? { turn_index: assistantTurnId } : {},
|
|
32213
32848
|
...this.memory ? {
|
|
32214
32849
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32215
|
-
} : {}
|
|
32850
|
+
} : {},
|
|
32851
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {}
|
|
32216
32852
|
};
|
|
32217
32853
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32218
32854
|
return {
|
|
@@ -32363,10 +32999,13 @@ var OperatorChatService = class {
|
|
|
32363
32999
|
* ## Sanctuary reference
|
|
32364
33000
|
* <static domain reference block>
|
|
32365
33001
|
*
|
|
33002
|
+
* ## Live fortress context ← WP-V1.3-9 Tau-3, when present
|
|
33003
|
+
* ### <Category>
|
|
33004
|
+
* <fetcher payload>
|
|
33005
|
+
*
|
|
32366
33006
|
* ## Prior conversation ← WP-V1.3-9 Tau-2, when present
|
|
32367
33007
|
* OPERATOR: ...
|
|
32368
33008
|
* CONCIERGE: ...
|
|
32369
|
-
* ---
|
|
32370
33009
|
*
|
|
32371
33010
|
* ## Recent activity
|
|
32372
33011
|
* <recentActivity output>
|
|
@@ -32385,13 +33024,14 @@ var OperatorChatService = class {
|
|
|
32385
33024
|
* if available; the v1.2 selector does not expose one, so structured
|
|
32386
33025
|
* serialization is the canonical path for v1.3.
|
|
32387
33026
|
*/
|
|
32388
|
-
async assembleConciergeContext(priorTurns = []) {
|
|
33027
|
+
async assembleConciergeContext(priorTurns = [], dynamicSection = "") {
|
|
32389
33028
|
const ref = `## Sanctuary reference
|
|
32390
33029
|
${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
32391
33030
|
const priorSection = this.formatPriorTurnsSection(priorTurns);
|
|
32392
33031
|
if (!this.contextProviders) {
|
|
32393
33032
|
return [
|
|
32394
33033
|
ref,
|
|
33034
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32395
33035
|
...priorSection ? [priorSection] : [],
|
|
32396
33036
|
"## Recent activity\n(no providers wired)",
|
|
32397
33037
|
"## Wrapped agents\n(no providers wired)",
|
|
@@ -32405,6 +33045,7 @@ ${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
|
32405
33045
|
]);
|
|
32406
33046
|
return [
|
|
32407
33047
|
ref,
|
|
33048
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32408
33049
|
...priorSection ? [priorSection] : [],
|
|
32409
33050
|
`## Recent activity
|
|
32410
33051
|
${activity}`,
|
|
@@ -32414,6 +33055,51 @@ ${agents}`,
|
|
|
32414
33055
|
${inbox}`
|
|
32415
33056
|
].join("\n\n");
|
|
32416
33057
|
}
|
|
33058
|
+
/**
|
|
33059
|
+
* Run the WP-V1.3-9 Tau-3 dynamic-context fold for a single round-
|
|
33060
|
+
* trip. Fail-soft on every axis: missing fetchers short-circuit to
|
|
33061
|
+
* an empty fold, fetcher failures emit a per-category audit event
|
|
33062
|
+
* and are omitted from the rendered section, an LLM-assist failure
|
|
33063
|
+
* proceeds with no fold. Returns the rendered section + the list of
|
|
33064
|
+
* categories whose data made it into the section (used for the
|
|
33065
|
+
* round-trip audit emission).
|
|
33066
|
+
*/
|
|
33067
|
+
async runDynamicContextFold(query) {
|
|
33068
|
+
if (!this.contextFetchers) {
|
|
33069
|
+
return { section: "", categoriesIncluded: [] };
|
|
33070
|
+
}
|
|
33071
|
+
const result = await foldContext(query, this.contextFetchers, {
|
|
33072
|
+
maxTokens: this.dynamicContextBudget,
|
|
33073
|
+
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33074
|
+
onFetcherFailure: (category, error) => {
|
|
33075
|
+
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33076
|
+
}
|
|
33077
|
+
});
|
|
33078
|
+
return result;
|
|
33079
|
+
}
|
|
33080
|
+
/**
|
|
33081
|
+
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33082
|
+
* of the fold path so the dynamic-context handler stays readable.
|
|
33083
|
+
* Emits with `result: "failure"` since the named category dropped
|
|
33084
|
+
* from the rendered section for this round-trip.
|
|
33085
|
+
*/
|
|
33086
|
+
emitContextFetcherFailed(category, failureReason) {
|
|
33087
|
+
const payload = {
|
|
33088
|
+
version: "1.2",
|
|
33089
|
+
event_id: makeEventId("conc-ctxfail"),
|
|
33090
|
+
emitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33091
|
+
identity_id: this.identityId,
|
|
33092
|
+
kind: "operator_concierge_context_fetcher_failed",
|
|
33093
|
+
surface: "concierge",
|
|
33094
|
+
category,
|
|
33095
|
+
failure_reason: failureReason
|
|
33096
|
+
};
|
|
33097
|
+
this.emit(
|
|
33098
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
33099
|
+
payload,
|
|
33100
|
+
"failure"
|
|
33101
|
+
);
|
|
33102
|
+
}
|
|
32417
33103
|
/**
|
|
32418
33104
|
* Render the prior-conversation section with token-budget enforcement
|
|
32419
33105
|
* (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
|
|
@@ -32424,14 +33110,14 @@ ${inbox}`
|
|
|
32424
33110
|
if (turns.length === 0) return "";
|
|
32425
33111
|
const HEADER = "## Prior conversation";
|
|
32426
33112
|
const lines = turns.map(formatPriorTurnLine);
|
|
32427
|
-
const headerTokens =
|
|
33113
|
+
const headerTokens = approxTokenLen2(`${HEADER}
|
|
32428
33114
|
`);
|
|
32429
|
-
const sepTokens =
|
|
33115
|
+
const sepTokens = approxTokenLen2("\n");
|
|
32430
33116
|
let runningTokens = headerTokens;
|
|
32431
33117
|
let runningLines = [];
|
|
32432
33118
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
32433
33119
|
const line = lines[i];
|
|
32434
|
-
const tokens =
|
|
33120
|
+
const tokens = approxTokenLen2(line) + (runningLines.length > 0 ? sepTokens : 0);
|
|
32435
33121
|
if (runningTokens + tokens > this.historyTokenBudget) break;
|
|
32436
33122
|
runningTokens += tokens;
|
|
32437
33123
|
runningLines.push(line);
|
|
@@ -32455,6 +33141,17 @@ ${runningLines.join("\n")}`;
|
|
|
32455
33141
|
function makeEventId(prefix) {
|
|
32456
33142
|
return `${prefix}-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
32457
33143
|
}
|
|
33144
|
+
function classifyFetcherError(error) {
|
|
33145
|
+
const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
33146
|
+
if (msg.includes("timeout") || msg.includes("timed out")) return "timeout";
|
|
33147
|
+
if (msg.includes("schema") || msg.includes("invalid shape")) {
|
|
33148
|
+
return "schema_mismatch";
|
|
33149
|
+
}
|
|
33150
|
+
if (msg.includes("io") || msg.includes("read") || msg.includes("enoent") || msg.includes("eacces")) {
|
|
33151
|
+
return "io_failed";
|
|
33152
|
+
}
|
|
33153
|
+
return "unknown";
|
|
33154
|
+
}
|
|
32458
33155
|
function formatPriorTurnLine(turn) {
|
|
32459
33156
|
const label = turn.role === "user" ? "OPERATOR" : "CONCIERGE";
|
|
32460
33157
|
return `${label}: ${turn.content}`;
|
|
@@ -32467,7 +33164,7 @@ function hashOf(input) {
|
|
|
32467
33164
|
init_encryption();
|
|
32468
33165
|
init_encoding();
|
|
32469
33166
|
var OPERATOR_CHAT_NAMESPACE = "_chat";
|
|
32470
|
-
var
|
|
33167
|
+
var HKDF_INFO2 = "operator-chat-store-v1";
|
|
32471
33168
|
function chatStorageKey(surface, threadKey) {
|
|
32472
33169
|
return `${surface}.${threadKey}`;
|
|
32473
33170
|
}
|
|
@@ -32476,7 +33173,7 @@ var OperatorChatStore = class {
|
|
|
32476
33173
|
encryptionKey;
|
|
32477
33174
|
constructor(storage, masterKey) {
|
|
32478
33175
|
this.storage = storage;
|
|
32479
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33176
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO2);
|
|
32480
33177
|
}
|
|
32481
33178
|
/**
|
|
32482
33179
|
* Load a thread. Returns null if no record exists or if the on-disk
|
|
@@ -32560,9 +33257,9 @@ init_encryption();
|
|
|
32560
33257
|
init_encoding();
|
|
32561
33258
|
var CONCIERGE_MEMORY_NAMESPACE = "_chat";
|
|
32562
33259
|
var CONCIERGE_MEMORY_KEY_PREFIX = "concierge_memory.";
|
|
32563
|
-
var
|
|
33260
|
+
var HKDF_INFO3 = "concierge-memory-store-v1";
|
|
32564
33261
|
var DEFAULT_CONCIERGE_RETENTION_DAYS = 30;
|
|
32565
|
-
var
|
|
33262
|
+
var MAX_BUNDLE_BYTES3 = 4 * 1024 * 1024;
|
|
32566
33263
|
var ConciergeMemoryStore = class {
|
|
32567
33264
|
storage;
|
|
32568
33265
|
encryptionKey;
|
|
@@ -32571,7 +33268,7 @@ var ConciergeMemoryStore = class {
|
|
|
32571
33268
|
locks;
|
|
32572
33269
|
constructor(opts) {
|
|
32573
33270
|
this.storage = opts.storage;
|
|
32574
|
-
this.encryptionKey = derivePurposeKey(opts.masterKey,
|
|
33271
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO3);
|
|
32575
33272
|
this.fortressId = opts.fortressId;
|
|
32576
33273
|
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_CONCIERGE_RETENTION_DAYS;
|
|
32577
33274
|
this.locks = /* @__PURE__ */ new Map();
|
|
@@ -32650,7 +33347,7 @@ var ConciergeMemoryStore = class {
|
|
|
32650
33347
|
return { ok: false, reason: "io_failed" };
|
|
32651
33348
|
}
|
|
32652
33349
|
if (!raw) return { ok: true, turns: [] };
|
|
32653
|
-
if (raw.length >
|
|
33350
|
+
if (raw.length > MAX_BUNDLE_BYTES3) {
|
|
32654
33351
|
return { ok: false, reason: "oversize_bundle" };
|
|
32655
33352
|
}
|
|
32656
33353
|
let envelope;
|
|
@@ -32697,7 +33394,7 @@ var ConciergeMemoryStore = class {
|
|
|
32697
33394
|
);
|
|
32698
33395
|
const summaries = [];
|
|
32699
33396
|
for (const meta of entries) {
|
|
32700
|
-
const threadId =
|
|
33397
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32701
33398
|
if (threadId === null) continue;
|
|
32702
33399
|
const bundle = await this.loadBundle(threadId);
|
|
32703
33400
|
if (!bundle || bundle.turns.length === 0) continue;
|
|
@@ -32750,7 +33447,7 @@ var ConciergeMemoryStore = class {
|
|
|
32750
33447
|
);
|
|
32751
33448
|
let pruned = 0;
|
|
32752
33449
|
for (const meta of entries) {
|
|
32753
|
-
const threadId =
|
|
33450
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32754
33451
|
if (threadId === null) continue;
|
|
32755
33452
|
pruned += await this.withLock(threadId, async () => {
|
|
32756
33453
|
const bundle = await this.loadBundle(threadId);
|
|
@@ -32781,7 +33478,7 @@ var ConciergeMemoryStore = class {
|
|
|
32781
33478
|
return null;
|
|
32782
33479
|
}
|
|
32783
33480
|
if (!raw) return null;
|
|
32784
|
-
if (raw.length >
|
|
33481
|
+
if (raw.length > MAX_BUNDLE_BYTES3) return null;
|
|
32785
33482
|
try {
|
|
32786
33483
|
const envelope = JSON.parse(bytesToString(raw));
|
|
32787
33484
|
const aad = stringToBytes(threadId);
|
|
@@ -32834,7 +33531,7 @@ var ConciergeMemoryStore = class {
|
|
|
32834
33531
|
function bundleKey(threadId) {
|
|
32835
33532
|
return `${CONCIERGE_MEMORY_KEY_PREFIX}${threadId}`;
|
|
32836
33533
|
}
|
|
32837
|
-
function
|
|
33534
|
+
function stripKeyPrefix2(key) {
|
|
32838
33535
|
if (!key.startsWith(CONCIERGE_MEMORY_KEY_PREFIX)) return null;
|
|
32839
33536
|
return key.slice(CONCIERGE_MEMORY_KEY_PREFIX.length);
|
|
32840
33537
|
}
|
|
@@ -32903,7 +33600,18 @@ function buildV11Bindings(inputs) {
|
|
|
32903
33600
|
registry
|
|
32904
33601
|
}),
|
|
32905
33602
|
conciergePiiFilter: buildConciergePiiFilter(),
|
|
32906
|
-
conciergeMemory
|
|
33603
|
+
conciergeMemory,
|
|
33604
|
+
conciergeContextFetchers: buildConciergeContextFetchers({
|
|
33605
|
+
auditLog: inputs.auditLog,
|
|
33606
|
+
identityId: inputs.identityId,
|
|
33607
|
+
registry
|
|
33608
|
+
}),
|
|
33609
|
+
...inputs.intelligenceSelector ? {
|
|
33610
|
+
conciergeContextLlmAssist: buildConciergeContextLlmAssist({
|
|
33611
|
+
selector: inputs.intelligenceSelector,
|
|
33612
|
+
identityId: inputs.identityId
|
|
33613
|
+
})
|
|
33614
|
+
} : {}
|
|
32907
33615
|
});
|
|
32908
33616
|
}
|
|
32909
33617
|
const hubService = new HubService({
|
|
@@ -32964,6 +33672,107 @@ function buildConciergeContextProviders(args) {
|
|
|
32964
33672
|
}
|
|
32965
33673
|
};
|
|
32966
33674
|
}
|
|
33675
|
+
function buildConciergeContextFetchers(args) {
|
|
33676
|
+
const empty = async () => "";
|
|
33677
|
+
return {
|
|
33678
|
+
templates: async () => {
|
|
33679
|
+
const entries = listTemplates();
|
|
33680
|
+
if (entries.length === 0) return "(no templates installed)";
|
|
33681
|
+
const lines = entries.map((e) => {
|
|
33682
|
+
const m = e.metadata;
|
|
33683
|
+
return `${m.name} (tier ${m.tier}, channel ${m.channel}, target ${m.target_archetype})`;
|
|
33684
|
+
});
|
|
33685
|
+
return lines.join("\n");
|
|
33686
|
+
},
|
|
33687
|
+
agent_state: async (agentNameHint) => {
|
|
33688
|
+
const records = args.registry.list({ identity_id: args.identityId });
|
|
33689
|
+
if (records.length === 0) return "(no wrapped agents)";
|
|
33690
|
+
const filtered = agentNameHint ? records.filter(
|
|
33691
|
+
(r) => r.agent_id.toLowerCase().includes(agentNameHint.toLowerCase()) || r.harness.toLowerCase().includes(agentNameHint.toLowerCase())
|
|
33692
|
+
) : records;
|
|
33693
|
+
const target = filtered.length > 0 ? filtered : records;
|
|
33694
|
+
const lines = target.slice(0, 20).map((r) => {
|
|
33695
|
+
const tmpl = typeof r.channel_template_id === "string" ? r.channel_template_id : "no_template";
|
|
33696
|
+
return `${r.agent_id} harness=${r.harness} status=${r.status} template=${tmpl}`;
|
|
33697
|
+
});
|
|
33698
|
+
return lines.join("\n");
|
|
33699
|
+
},
|
|
33700
|
+
agent_activity: async (agentNameHint) => {
|
|
33701
|
+
const result = await args.auditLog.query({ limit: 50 });
|
|
33702
|
+
const owned = result.entries.filter(
|
|
33703
|
+
(e) => e.identity_id === args.identityId
|
|
33704
|
+
);
|
|
33705
|
+
const filtered = agentNameHint ? owned.filter((e) => {
|
|
33706
|
+
const agentId = e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : "";
|
|
33707
|
+
return agentId.toLowerCase().includes(agentNameHint.toLowerCase());
|
|
33708
|
+
}) : owned;
|
|
33709
|
+
const tail = (filtered.length > 0 ? filtered : owned).slice(-20);
|
|
33710
|
+
if (tail.length === 0) return "(no activity)";
|
|
33711
|
+
return tail.map((e) => {
|
|
33712
|
+
const agentId = (e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : null) ?? "_fortress";
|
|
33713
|
+
return `${e.timestamp} ${e.layer}.${e.operation} agent=${agentId} result=${e.result}`;
|
|
33714
|
+
}).join("\n");
|
|
33715
|
+
},
|
|
33716
|
+
audit_log: async () => {
|
|
33717
|
+
const result = await args.auditLog.query({ limit: 30 });
|
|
33718
|
+
const owned = result.entries.filter(
|
|
33719
|
+
(e) => e.identity_id === args.identityId
|
|
33720
|
+
);
|
|
33721
|
+
if (owned.length === 0) return "(no audit log entries)";
|
|
33722
|
+
return owned.slice(-30).map(
|
|
33723
|
+
(e) => `${e.timestamp} ${e.layer}.${e.operation} result=${e.result}`
|
|
33724
|
+
).join("\n");
|
|
33725
|
+
},
|
|
33726
|
+
sentinel_findings: empty,
|
|
33727
|
+
anomaly_alerts: empty,
|
|
33728
|
+
recent_receipts: async () => {
|
|
33729
|
+
const result = await args.auditLog.query({ limit: 100 });
|
|
33730
|
+
const owned = result.entries.filter(
|
|
33731
|
+
(e) => e.identity_id === args.identityId && e.operation.startsWith("composition_")
|
|
33732
|
+
);
|
|
33733
|
+
if (owned.length === 0) return "(no recent composition events)";
|
|
33734
|
+
return owned.slice(-15).map((e) => `${e.timestamp} ${e.operation} result=${e.result}`).join("\n");
|
|
33735
|
+
},
|
|
33736
|
+
verascore_deltas: empty
|
|
33737
|
+
};
|
|
33738
|
+
}
|
|
33739
|
+
function buildConciergeContextLlmAssist(args) {
|
|
33740
|
+
return async (query, categories) => {
|
|
33741
|
+
const labelList = categories.map((c) => `- ${c}`).join("\n");
|
|
33742
|
+
const prompt = `You are a router. Classify the operator's query into one of the categories below or "none".
|
|
33743
|
+
Reply with exactly one token: one category name or "none".
|
|
33744
|
+
|
|
33745
|
+
Categories:
|
|
33746
|
+
${labelList}
|
|
33747
|
+
|
|
33748
|
+
Query: ${query}
|
|
33749
|
+
|
|
33750
|
+
Category:`;
|
|
33751
|
+
try {
|
|
33752
|
+
const handle = await args.selector.getSubstrate("concierge");
|
|
33753
|
+
if (!handle.capability.summarize) return "none";
|
|
33754
|
+
const response = await args.selector.invokeSummarize("concierge", {
|
|
33755
|
+
kind: "summarize",
|
|
33756
|
+
context: prompt,
|
|
33757
|
+
query: "Output the single category token.",
|
|
33758
|
+
maxTokens: 16
|
|
33759
|
+
});
|
|
33760
|
+
if (response.failureClass || response.body.kind !== "summarize") {
|
|
33761
|
+
return "none";
|
|
33762
|
+
}
|
|
33763
|
+
const raw = response.body.text.trim().toLowerCase();
|
|
33764
|
+
const head = raw.split(/\s|[.,!?:;]/)[0] ?? "";
|
|
33765
|
+
const normalized = head.replace(/[^a-z_]/g, "");
|
|
33766
|
+
const known = categories;
|
|
33767
|
+
if (known.includes(normalized)) {
|
|
33768
|
+
return normalized;
|
|
33769
|
+
}
|
|
33770
|
+
return "none";
|
|
33771
|
+
} catch {
|
|
33772
|
+
return "none";
|
|
33773
|
+
}
|
|
33774
|
+
};
|
|
33775
|
+
}
|
|
32967
33776
|
function buildConciergePiiFilter() {
|
|
32968
33777
|
return {
|
|
32969
33778
|
filter(input) {
|
|
@@ -33095,13 +33904,13 @@ init_encryption();
|
|
|
33095
33904
|
init_encoding();
|
|
33096
33905
|
var INTELLIGENCE_NAMESPACE = "_intelligence";
|
|
33097
33906
|
var SUBSTRATE_CONFIG_KEY = "substrate-config";
|
|
33098
|
-
var
|
|
33907
|
+
var HKDF_INFO4 = "intelligence-substrate-config";
|
|
33099
33908
|
var IntelligenceConfigStore = class {
|
|
33100
33909
|
storage;
|
|
33101
33910
|
encryptionKey;
|
|
33102
33911
|
constructor(storage, masterKey) {
|
|
33103
33912
|
this.storage = storage;
|
|
33104
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33913
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO4);
|
|
33105
33914
|
}
|
|
33106
33915
|
/**
|
|
33107
33916
|
* Load the operator's substrate config from disk. Returns the config
|
|
@@ -37047,12 +37856,18 @@ ${err.message}
|
|
|
37047
37856
|
} : void 0;
|
|
37048
37857
|
const fortressIdForAggregator = fortressIdFromStoragePath(config.storage_path);
|
|
37049
37858
|
const aggregatorIdentityId = identityManager.getPrimaryIdentityId() ?? `fortress:${config.storage_path}`;
|
|
37859
|
+
const aggregatorPayloadStore = new AggregatorPayloadStore({
|
|
37860
|
+
storage,
|
|
37861
|
+
masterKey,
|
|
37862
|
+
fortressId: fortressIdForAggregator
|
|
37863
|
+
});
|
|
37050
37864
|
const approvalAggregator = new ApprovalAggregator({
|
|
37051
37865
|
storage,
|
|
37052
37866
|
masterKey,
|
|
37053
37867
|
auditLog,
|
|
37054
37868
|
identityId: aggregatorIdentityId,
|
|
37055
|
-
fortressId: fortressIdForAggregator
|
|
37869
|
+
fortressId: fortressIdForAggregator,
|
|
37870
|
+
payloadStore: aggregatorPayloadStore
|
|
37056
37871
|
});
|
|
37057
37872
|
const wrappedApprovalChannel = new AggregatorBackedChannel({
|
|
37058
37873
|
underlying: approvalChannel,
|