@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.cjs
CHANGED
|
@@ -16319,6 +16319,27 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16319
16319
|
await handleStream2(deps, res);
|
|
16320
16320
|
return true;
|
|
16321
16321
|
}
|
|
16322
|
+
if (method === "GET" && path === `${APPROVAL_INBOX_API_PREFIX}/history`) {
|
|
16323
|
+
const limit = parseLimit2(
|
|
16324
|
+
url.searchParams.get("limit"),
|
|
16325
|
+
APPROVAL_INBOX_DEFAULT_LIMIT,
|
|
16326
|
+
APPROVAL_INBOX_MAX_LIMIT
|
|
16327
|
+
);
|
|
16328
|
+
const statusRaw = url.searchParams.get("status");
|
|
16329
|
+
const sinceTs = url.searchParams.get("since") ?? void 0;
|
|
16330
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16331
|
+
const filterStatus = statusRaw && isStatusFilter(statusRaw) && statusRaw !== "pending" ? statusRaw : void 0;
|
|
16332
|
+
const entries = await deps.aggregator.getHistory(
|
|
16333
|
+
{
|
|
16334
|
+
limit,
|
|
16335
|
+
...filterStatus !== void 0 ? { status: filterStatus } : {},
|
|
16336
|
+
...sinceTs !== void 0 ? { sinceTs } : {}
|
|
16337
|
+
},
|
|
16338
|
+
operatorId
|
|
16339
|
+
);
|
|
16340
|
+
writeJSON4(res, 200, { ok: true, data: { entries } });
|
|
16341
|
+
return true;
|
|
16342
|
+
}
|
|
16322
16343
|
if (method === "GET" && path === APPROVAL_INBOX_API_PREFIX) {
|
|
16323
16344
|
const limit = parseLimit2(
|
|
16324
16345
|
url.searchParams.get("limit"),
|
|
@@ -16341,11 +16362,39 @@ async function handleApprovalInboxRoute(deps, req, res) {
|
|
|
16341
16362
|
writeJSON4(res, 404, { ok: false, error: "not_found", path });
|
|
16342
16363
|
return true;
|
|
16343
16364
|
}
|
|
16344
|
-
if (method === "GET" && entryMatch.action ===
|
|
16345
|
-
const
|
|
16346
|
-
|
|
16347
|
-
(
|
|
16365
|
+
if (method === "GET" && entryMatch.action === "audit-trail") {
|
|
16366
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16367
|
+
if (!entry) {
|
|
16368
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16369
|
+
return true;
|
|
16370
|
+
}
|
|
16371
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16372
|
+
const trail = await deps.aggregator.getAuditTrail(
|
|
16373
|
+
entryMatch.aggregatorId,
|
|
16374
|
+
operatorId
|
|
16348
16375
|
);
|
|
16376
|
+
writeJSON4(res, 200, { ok: true, data: { entry, audit_trail: trail } });
|
|
16377
|
+
return true;
|
|
16378
|
+
}
|
|
16379
|
+
if (method === "GET" && entryMatch.action === "payload") {
|
|
16380
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16381
|
+
if (!entry) {
|
|
16382
|
+
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16383
|
+
return true;
|
|
16384
|
+
}
|
|
16385
|
+
const operatorId = deps.operatorId ?? APPROVAL_INBOX_OPERATOR_DEFAULT;
|
|
16386
|
+
const payload = await deps.aggregator.getFullPayloadWithAudit(
|
|
16387
|
+
entryMatch.aggregatorId,
|
|
16388
|
+
operatorId
|
|
16389
|
+
);
|
|
16390
|
+
writeJSON4(res, 200, {
|
|
16391
|
+
ok: true,
|
|
16392
|
+
data: { entry, request_payload: payload }
|
|
16393
|
+
});
|
|
16394
|
+
return true;
|
|
16395
|
+
}
|
|
16396
|
+
if (method === "GET" && entryMatch.action === null) {
|
|
16397
|
+
const entry = await deps.aggregator.getEntry(entryMatch.aggregatorId);
|
|
16349
16398
|
if (!entry) {
|
|
16350
16399
|
writeJSON4(res, 404, { ok: false, error: "not_found" });
|
|
16351
16400
|
return true;
|
|
@@ -19332,7 +19381,10 @@ var APPROVAL_AGGREGATOR_HKDF_INFO = "l2-approval-aggregator-v1";
|
|
|
19332
19381
|
var APPROVAL_AGGREGATOR_AUDIT_OPS = {
|
|
19333
19382
|
AGGREGATED: "cross_harness_approval_aggregated",
|
|
19334
19383
|
RESOLVED: "cross_harness_approval_resolved",
|
|
19335
|
-
DEDUPED: "cross_harness_approval_deduped"
|
|
19384
|
+
DEDUPED: "cross_harness_approval_deduped",
|
|
19385
|
+
PAYLOAD_DECRYPTED: "cross_harness_approval_payload_decrypted",
|
|
19386
|
+
AUDIT_TRAIL_VIEWED: "cross_harness_approval_audit_trail_viewed",
|
|
19387
|
+
REPLAYED: "cross_harness_approval_replayed"
|
|
19336
19388
|
};
|
|
19337
19389
|
var DEFAULT_PENDING_TTL_MS = 5 * 60 * 1e3;
|
|
19338
19390
|
var DEFAULT_MAX_LIST_LIMIT = 200;
|
|
@@ -19348,6 +19400,8 @@ var ApprovalAggregator = class {
|
|
|
19348
19400
|
now;
|
|
19349
19401
|
resolveSourceContext;
|
|
19350
19402
|
resolveHubInboxItemId;
|
|
19403
|
+
payloadStore;
|
|
19404
|
+
resolveEnforcementChain;
|
|
19351
19405
|
/** Cached entries by `aggregator_id`. */
|
|
19352
19406
|
entries = /* @__PURE__ */ new Map();
|
|
19353
19407
|
/** Dedup index: `${harness}|${agent}|${audit_id}` -> aggregator_id. */
|
|
@@ -19377,6 +19431,14 @@ var ApprovalAggregator = class {
|
|
|
19377
19431
|
source_agent_id: this.fortressId
|
|
19378
19432
|
}));
|
|
19379
19433
|
this.resolveHubInboxItemId = deps.resolveHubInboxItemId ?? ((_event) => void 0);
|
|
19434
|
+
this.payloadStore = deps.payloadStore ?? null;
|
|
19435
|
+
this.resolveEnforcementChain = deps.resolveEnforcementChain ?? ((event) => [
|
|
19436
|
+
{
|
|
19437
|
+
layer: "l2",
|
|
19438
|
+
event: `approval_required:${event.operation}`,
|
|
19439
|
+
timestamp: event.request_timestamp
|
|
19440
|
+
}
|
|
19441
|
+
]);
|
|
19380
19442
|
}
|
|
19381
19443
|
/**
|
|
19382
19444
|
* Subscribe an event listener. Returns an unsubscribe fn. SSE handlers
|
|
@@ -19427,13 +19489,152 @@ var ApprovalAggregator = class {
|
|
|
19427
19489
|
}
|
|
19428
19490
|
/**
|
|
19429
19491
|
* Return the original (unhashed) request payload for the entry. Returns
|
|
19430
|
-
* `null` when the entry is unknown
|
|
19431
|
-
*
|
|
19492
|
+
* `null` when the entry is unknown. When the in-memory payload map has
|
|
19493
|
+
* been evicted (e.g. after a server restart) and a `payloadStore` was
|
|
19494
|
+
* provided, the at-rest bundle is decrypted and the in-memory map is
|
|
19495
|
+
* refilled. Audit emission lives on the `*WithAudit` variant; this base
|
|
19496
|
+
* accessor is silent so internal callers can read without polluting the
|
|
19497
|
+
* audit trail.
|
|
19432
19498
|
*/
|
|
19433
19499
|
async getFullPayload(aggregatorId) {
|
|
19434
19500
|
await this.hydrate();
|
|
19435
19501
|
if (!this.entries.has(aggregatorId)) return null;
|
|
19436
|
-
|
|
19502
|
+
const cached = this.fullPayloads.get(aggregatorId);
|
|
19503
|
+
if (cached !== void 0) return cached;
|
|
19504
|
+
if (this.payloadStore) {
|
|
19505
|
+
try {
|
|
19506
|
+
const restored = await this.payloadStore.loadPayload(aggregatorId);
|
|
19507
|
+
if (restored !== null) {
|
|
19508
|
+
this.fullPayloads.set(aggregatorId, restored);
|
|
19509
|
+
return restored;
|
|
19510
|
+
}
|
|
19511
|
+
} catch {
|
|
19512
|
+
}
|
|
19513
|
+
}
|
|
19514
|
+
return null;
|
|
19515
|
+
}
|
|
19516
|
+
/**
|
|
19517
|
+
* Return the entry record for the given id, or null when unknown.
|
|
19518
|
+
* Idempotent. v1.3 Upsilon-3.
|
|
19519
|
+
*/
|
|
19520
|
+
async getEntry(aggregatorId) {
|
|
19521
|
+
await this.hydrate();
|
|
19522
|
+
return this.entries.get(aggregatorId) ?? null;
|
|
19523
|
+
}
|
|
19524
|
+
/**
|
|
19525
|
+
* Audited variant of `getFullPayload`. Emits the
|
|
19526
|
+
* `cross_harness_approval_payload_decrypted` audit event before
|
|
19527
|
+
* returning. Used by the operator-facing /payload replay route.
|
|
19528
|
+
* v1.3 Upsilon-3.
|
|
19529
|
+
*/
|
|
19530
|
+
async getFullPayloadWithAudit(aggregatorId, operatorId) {
|
|
19531
|
+
const payload = await this.getFullPayload(aggregatorId);
|
|
19532
|
+
if (payload === null) return null;
|
|
19533
|
+
const entry = this.entries.get(aggregatorId);
|
|
19534
|
+
this.auditLog.append(
|
|
19535
|
+
"l2",
|
|
19536
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.PAYLOAD_DECRYPTED,
|
|
19537
|
+
operatorId,
|
|
19538
|
+
{
|
|
19539
|
+
aggregator_id: aggregatorId,
|
|
19540
|
+
...entry ? {
|
|
19541
|
+
source_harness: entry.source_harness,
|
|
19542
|
+
source_agent_id: entry.source_agent_id,
|
|
19543
|
+
entry_status: entry.status
|
|
19544
|
+
} : {}
|
|
19545
|
+
}
|
|
19546
|
+
);
|
|
19547
|
+
return payload;
|
|
19548
|
+
}
|
|
19549
|
+
/**
|
|
19550
|
+
* Return the audit-log entries that led to and surround this approval.
|
|
19551
|
+
* Best-effort matching: aggregator-side emissions (AGGREGATED, RESOLVED,
|
|
19552
|
+
* DEDUPED, replay events) all carry `details.aggregator_id` and link
|
|
19553
|
+
* directly. Gate-side emissions (`gate_*:operation`) do not carry the
|
|
19554
|
+
* aggregator id at v1.3, so they are matched via timestamp window
|
|
19555
|
+
* (entry.created_at to entry.resolved_at + 1s, or expires_at + 1s while
|
|
19556
|
+
* pending) and operation suffix. Emits AUDIT_TRAIL_VIEWED on call.
|
|
19557
|
+
* v1.3 Upsilon-3.
|
|
19558
|
+
*/
|
|
19559
|
+
async getAuditTrail(aggregatorId, operatorId) {
|
|
19560
|
+
await this.hydrate();
|
|
19561
|
+
const entry = this.entries.get(aggregatorId);
|
|
19562
|
+
if (!entry) {
|
|
19563
|
+
return [];
|
|
19564
|
+
}
|
|
19565
|
+
const sinceMs = Date.parse(entry.created_at) - 1e3;
|
|
19566
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
19567
|
+
const queried = await this.auditLog.query({ since: sinceIso, limit: 1e3 });
|
|
19568
|
+
const operationPart = entry.policy_rule_id.includes(":") ? entry.policy_rule_id.slice(entry.policy_rule_id.indexOf(":") + 1) : entry.policy_rule_id;
|
|
19569
|
+
const lifetimeStart = sinceMs;
|
|
19570
|
+
const lifetimeEnd = entry.resolved_at ? Date.parse(entry.resolved_at) + 1e3 : Date.parse(entry.expires_at) + 1e3;
|
|
19571
|
+
const matches = [];
|
|
19572
|
+
for (const audit of queried.entries) {
|
|
19573
|
+
const detailsId = audit.details !== void 0 ? audit.details["aggregator_id"] : void 0;
|
|
19574
|
+
if (detailsId === aggregatorId) {
|
|
19575
|
+
matches.push(audit);
|
|
19576
|
+
continue;
|
|
19577
|
+
}
|
|
19578
|
+
const auditMs = Date.parse(audit.timestamp);
|
|
19579
|
+
if (auditMs < lifetimeStart || auditMs > lifetimeEnd) continue;
|
|
19580
|
+
if (audit.operation.endsWith(`:${operationPart}`)) {
|
|
19581
|
+
matches.push(audit);
|
|
19582
|
+
}
|
|
19583
|
+
}
|
|
19584
|
+
matches.sort(
|
|
19585
|
+
(a, b) => a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : 0
|
|
19586
|
+
);
|
|
19587
|
+
this.auditLog.append(
|
|
19588
|
+
"l2",
|
|
19589
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.AUDIT_TRAIL_VIEWED,
|
|
19590
|
+
operatorId,
|
|
19591
|
+
{
|
|
19592
|
+
aggregator_id: aggregatorId,
|
|
19593
|
+
entry_status: entry.status,
|
|
19594
|
+
match_count: matches.length
|
|
19595
|
+
}
|
|
19596
|
+
);
|
|
19597
|
+
return matches;
|
|
19598
|
+
}
|
|
19599
|
+
/**
|
|
19600
|
+
* List historical (resolved) approvals. Excludes pending entries by
|
|
19601
|
+
* design: `list()` is the pending-inbox surface and `getHistory()` is
|
|
19602
|
+
* the resolved-replay surface. Emits REPLAYED on each call. v1.3
|
|
19603
|
+
* Upsilon-3.
|
|
19604
|
+
*/
|
|
19605
|
+
async getHistory(opts, operatorId) {
|
|
19606
|
+
await this.hydrate();
|
|
19607
|
+
await this.expireStale();
|
|
19608
|
+
const limit = Math.min(
|
|
19609
|
+
opts?.limit ?? DEFAULT_LIST_PAGE_SIZE,
|
|
19610
|
+
this.maxListLimit
|
|
19611
|
+
);
|
|
19612
|
+
const sinceMs = opts?.sinceTs ? Date.parse(opts.sinceTs) : Number.NEGATIVE_INFINITY;
|
|
19613
|
+
const matching = [];
|
|
19614
|
+
for (const entry of this.entries.values()) {
|
|
19615
|
+
if (entry.status === "pending") continue;
|
|
19616
|
+
if (opts?.status && entry.status !== opts.status) continue;
|
|
19617
|
+
const stamp = Date.parse(entry.resolved_at ?? entry.created_at);
|
|
19618
|
+
if (stamp < sinceMs) continue;
|
|
19619
|
+
matching.push(entry);
|
|
19620
|
+
}
|
|
19621
|
+
matching.sort((a, b) => {
|
|
19622
|
+
const aStamp = a.resolved_at ?? a.created_at;
|
|
19623
|
+
const bStamp = b.resolved_at ?? b.created_at;
|
|
19624
|
+
return bStamp.localeCompare(aStamp);
|
|
19625
|
+
});
|
|
19626
|
+
const sliced = matching.slice(0, limit);
|
|
19627
|
+
this.auditLog.append(
|
|
19628
|
+
"l2",
|
|
19629
|
+
APPROVAL_AGGREGATOR_AUDIT_OPS.REPLAYED,
|
|
19630
|
+
operatorId,
|
|
19631
|
+
{
|
|
19632
|
+
result_count: sliced.length,
|
|
19633
|
+
...opts?.status !== void 0 ? { status_filter: opts.status } : {},
|
|
19634
|
+
...opts?.sinceTs !== void 0 ? { since: opts.sinceTs } : {}
|
|
19635
|
+
}
|
|
19636
|
+
);
|
|
19637
|
+
return sliced;
|
|
19437
19638
|
}
|
|
19438
19639
|
/**
|
|
19439
19640
|
* Resolve an entry. Used by both:
|
|
@@ -19507,6 +19708,7 @@ var ApprovalAggregator = class {
|
|
|
19507
19708
|
const now = this.now();
|
|
19508
19709
|
const expires = new Date(now.getTime() + this.pendingTtlMs);
|
|
19509
19710
|
const hubInboxId = this.resolveHubInboxItemId(event);
|
|
19711
|
+
const enforcementChain = this.resolveEnforcementChain(event);
|
|
19510
19712
|
const entry = {
|
|
19511
19713
|
aggregator_id: id,
|
|
19512
19714
|
source_harness: ctx.source_harness,
|
|
@@ -19518,13 +19720,20 @@ var ApprovalAggregator = class {
|
|
|
19518
19720
|
status: "pending",
|
|
19519
19721
|
created_at: now.toISOString(),
|
|
19520
19722
|
expires_at: expires.toISOString(),
|
|
19521
|
-
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {}
|
|
19723
|
+
...hubInboxId !== void 0 ? { hub_inbox_item_id: hubInboxId } : {},
|
|
19724
|
+
...enforcementChain.length > 0 ? { enforcement_chain: enforcementChain } : {}
|
|
19522
19725
|
};
|
|
19523
19726
|
this.entries.set(id, entry);
|
|
19524
19727
|
this.dedupIndex.set(dedupKey, id);
|
|
19525
19728
|
this.correlationIndex.set(event.correlation_id, id);
|
|
19526
19729
|
this.fullPayloads.set(id, event.context);
|
|
19527
19730
|
await this.persist(entry);
|
|
19731
|
+
if (this.payloadStore) {
|
|
19732
|
+
try {
|
|
19733
|
+
await this.payloadStore.savePayload(id, event.context);
|
|
19734
|
+
} catch {
|
|
19735
|
+
}
|
|
19736
|
+
}
|
|
19528
19737
|
this.auditLog.append(
|
|
19529
19738
|
"l2",
|
|
19530
19739
|
APPROVAL_AGGREGATOR_AUDIT_OPS.AGGREGATED,
|
|
@@ -19833,6 +20042,143 @@ function makeRedirectResolverFromPolicySupplier(supplier) {
|
|
|
19833
20042
|
};
|
|
19834
20043
|
}
|
|
19835
20044
|
|
|
20045
|
+
// src/principal-policy/aggregator-store.ts
|
|
20046
|
+
init_encryption();
|
|
20047
|
+
init_encoding();
|
|
20048
|
+
var AGGREGATOR_PAYLOAD_NAMESPACE = "_approval_aggregator_payloads";
|
|
20049
|
+
var AGGREGATOR_PAYLOAD_KEY_PREFIX = "payload.";
|
|
20050
|
+
var HKDF_INFO = "l2-approval-aggregator-payload-v1";
|
|
20051
|
+
var DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS = 30;
|
|
20052
|
+
var MAX_BUNDLE_BYTES2 = 4 * 1024 * 1024;
|
|
20053
|
+
var AggregatorPayloadStore = class {
|
|
20054
|
+
storage;
|
|
20055
|
+
encryptionKey;
|
|
20056
|
+
fortressId;
|
|
20057
|
+
retentionDays;
|
|
20058
|
+
constructor(opts) {
|
|
20059
|
+
this.storage = opts.storage;
|
|
20060
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO);
|
|
20061
|
+
this.fortressId = opts.fortressId;
|
|
20062
|
+
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_AGGREGATOR_PAYLOAD_RETENTION_DAYS;
|
|
20063
|
+
}
|
|
20064
|
+
/**
|
|
20065
|
+
* Persist `payload` under the given aggregator_id. Idempotent; calling
|
|
20066
|
+
* twice with the same id rewrites the bundle (retention_until is
|
|
20067
|
+
* recomputed). Returns the bundle's retention_until ISO-8601 timestamp
|
|
20068
|
+
* so callers can log it.
|
|
20069
|
+
*/
|
|
20070
|
+
async savePayload(aggregatorId, payload) {
|
|
20071
|
+
const now = /* @__PURE__ */ new Date();
|
|
20072
|
+
const retentionMs = this.retentionDays * 24 * 60 * 60 * 1e3;
|
|
20073
|
+
const retentionUntil = new Date(now.getTime() + retentionMs);
|
|
20074
|
+
const bundle = {
|
|
20075
|
+
version: 1,
|
|
20076
|
+
aggregator_id: aggregatorId,
|
|
20077
|
+
fortress_id: this.fortressId,
|
|
20078
|
+
created_at: now.toISOString(),
|
|
20079
|
+
retention_until: retentionUntil.toISOString(),
|
|
20080
|
+
payload
|
|
20081
|
+
};
|
|
20082
|
+
const aad = stringToBytes(aggregatorId);
|
|
20083
|
+
const plaintext = stringToBytes(JSON.stringify(bundle));
|
|
20084
|
+
const envelope = encrypt(plaintext, this.encryptionKey, aad);
|
|
20085
|
+
await this.storage.write(
|
|
20086
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20087
|
+
payloadKey(aggregatorId),
|
|
20088
|
+
stringToBytes(JSON.stringify(envelope))
|
|
20089
|
+
);
|
|
20090
|
+
return bundle.retention_until;
|
|
20091
|
+
}
|
|
20092
|
+
/**
|
|
20093
|
+
* Read the persisted payload for the aggregator_id. Returns null if no
|
|
20094
|
+
* bundle exists, the bundle is corrupted, or AAD binding fails.
|
|
20095
|
+
*/
|
|
20096
|
+
async loadPayload(aggregatorId) {
|
|
20097
|
+
const key = payloadKey(aggregatorId);
|
|
20098
|
+
let raw;
|
|
20099
|
+
try {
|
|
20100
|
+
raw = await this.storage.read(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20101
|
+
} catch {
|
|
20102
|
+
return null;
|
|
20103
|
+
}
|
|
20104
|
+
if (!raw) return null;
|
|
20105
|
+
if (raw.length > MAX_BUNDLE_BYTES2) return null;
|
|
20106
|
+
try {
|
|
20107
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20108
|
+
const aad = stringToBytes(aggregatorId);
|
|
20109
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20110
|
+
const parsed = JSON.parse(
|
|
20111
|
+
bytesToString(plaintext)
|
|
20112
|
+
);
|
|
20113
|
+
if (parsed.version !== 1) return null;
|
|
20114
|
+
if (parsed.aggregator_id !== aggregatorId) return null;
|
|
20115
|
+
return parsed.payload;
|
|
20116
|
+
} catch {
|
|
20117
|
+
return null;
|
|
20118
|
+
}
|
|
20119
|
+
}
|
|
20120
|
+
/**
|
|
20121
|
+
* Delete the persisted payload. Returns true when a bundle was removed,
|
|
20122
|
+
* false when none existed.
|
|
20123
|
+
*/
|
|
20124
|
+
async deletePayload(aggregatorId) {
|
|
20125
|
+
const key = payloadKey(aggregatorId);
|
|
20126
|
+
const existed = await this.storage.exists(
|
|
20127
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20128
|
+
key
|
|
20129
|
+
);
|
|
20130
|
+
if (!existed) return false;
|
|
20131
|
+
try {
|
|
20132
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, key);
|
|
20133
|
+
} catch {
|
|
20134
|
+
return false;
|
|
20135
|
+
}
|
|
20136
|
+
return true;
|
|
20137
|
+
}
|
|
20138
|
+
/**
|
|
20139
|
+
* Drop expired payload bundles. Returns the count of bundles pruned.
|
|
20140
|
+
* Caller wires this into the cocoon-unlock initialization path.
|
|
20141
|
+
*/
|
|
20142
|
+
async pruneExpired(now) {
|
|
20143
|
+
const cutoff = (now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
20144
|
+
const entries = await this.storage.list(
|
|
20145
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20146
|
+
AGGREGATOR_PAYLOAD_KEY_PREFIX
|
|
20147
|
+
);
|
|
20148
|
+
let pruned = 0;
|
|
20149
|
+
for (const meta of entries) {
|
|
20150
|
+
const aggregatorId = stripKeyPrefix(meta.key);
|
|
20151
|
+
if (aggregatorId === null) continue;
|
|
20152
|
+
const raw = await this.storage.read(
|
|
20153
|
+
AGGREGATOR_PAYLOAD_NAMESPACE,
|
|
20154
|
+
meta.key
|
|
20155
|
+
);
|
|
20156
|
+
if (!raw) continue;
|
|
20157
|
+
try {
|
|
20158
|
+
const envelope = JSON.parse(bytesToString(raw));
|
|
20159
|
+
const aad = stringToBytes(aggregatorId);
|
|
20160
|
+
const plaintext = decrypt(envelope, this.encryptionKey, aad);
|
|
20161
|
+
const parsed = JSON.parse(
|
|
20162
|
+
bytesToString(plaintext)
|
|
20163
|
+
);
|
|
20164
|
+
if (parsed.retention_until <= cutoff) {
|
|
20165
|
+
await this.storage.delete(AGGREGATOR_PAYLOAD_NAMESPACE, meta.key);
|
|
20166
|
+
pruned += 1;
|
|
20167
|
+
}
|
|
20168
|
+
} catch {
|
|
20169
|
+
}
|
|
20170
|
+
}
|
|
20171
|
+
return { pruned };
|
|
20172
|
+
}
|
|
20173
|
+
};
|
|
20174
|
+
function payloadKey(aggregatorId) {
|
|
20175
|
+
return `${AGGREGATOR_PAYLOAD_KEY_PREFIX}${aggregatorId}`;
|
|
20176
|
+
}
|
|
20177
|
+
function stripKeyPrefix(key) {
|
|
20178
|
+
if (!key.startsWith(AGGREGATOR_PAYLOAD_KEY_PREFIX)) return null;
|
|
20179
|
+
return key.slice(AGGREGATOR_PAYLOAD_KEY_PREFIX.length);
|
|
20180
|
+
}
|
|
20181
|
+
|
|
19836
20182
|
// src/principal-policy/tools.ts
|
|
19837
20183
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
19838
20184
|
return [
|
|
@@ -31993,20 +32339,291 @@ var OPERATOR_CHAT_OPS = {
|
|
|
31993
32339
|
* turns; the concierge degrades to single-turn after emitting. Body
|
|
31994
32340
|
* carries thread_id + a stable failure_reason enum.
|
|
31995
32341
|
*/
|
|
31996
|
-
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed"
|
|
32342
|
+
CONCIERGE_MEMORY_READ_FAILED: "operator_concierge_memory_read_failed",
|
|
32343
|
+
/**
|
|
32344
|
+
* Concierge dynamic-context fetcher failed (WP-V1.3-9 Tau-3). Emitted
|
|
32345
|
+
* when a category fetcher throws while assembling the dynamic context
|
|
32346
|
+
* fold. The concierge omits that category and continues; the user-
|
|
32347
|
+
* facing query is never broken. Body carries category + failure_reason.
|
|
32348
|
+
*/
|
|
32349
|
+
CONCIERGE_CONTEXT_FETCHER_FAILED: "operator_concierge_context_fetcher_failed"
|
|
31997
32350
|
};
|
|
31998
32351
|
|
|
31999
32352
|
// src/chat/operator-chat-types.ts
|
|
32000
32353
|
var OPERATOR_CHAT_MAX_THREAD_LENGTH = 500;
|
|
32001
32354
|
var CONCIERGE_THREAD_KEY = "_fortress";
|
|
32002
32355
|
|
|
32356
|
+
// src/chat/concierge-context-router.ts
|
|
32357
|
+
var APPROX_CHARS_PER_TOKEN = 4;
|
|
32358
|
+
var DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET = 2e3;
|
|
32359
|
+
var DYNAMIC_CONTEXT_SECTION_HEADER = "## Live fortress context";
|
|
32360
|
+
var CONTEXT_CATEGORIES = [
|
|
32361
|
+
"templates",
|
|
32362
|
+
"agent_state",
|
|
32363
|
+
"agent_activity",
|
|
32364
|
+
"audit_log",
|
|
32365
|
+
"sentinel_findings",
|
|
32366
|
+
"anomaly_alerts",
|
|
32367
|
+
"recent_receipts",
|
|
32368
|
+
"verascore_deltas"
|
|
32369
|
+
];
|
|
32370
|
+
function phrasePattern(phrase) {
|
|
32371
|
+
const escaped = phrase.toLowerCase().split(/\s+/).map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
|
|
32372
|
+
return { source: `\\b${escaped}\\b`, phrase };
|
|
32373
|
+
}
|
|
32374
|
+
var CATEGORY_KEYWORDS = [
|
|
32375
|
+
{
|
|
32376
|
+
category: "templates",
|
|
32377
|
+
patterns: [
|
|
32378
|
+
"templates",
|
|
32379
|
+
"template",
|
|
32380
|
+
"channel templates",
|
|
32381
|
+
"channel template",
|
|
32382
|
+
"list templates",
|
|
32383
|
+
"available templates",
|
|
32384
|
+
"what templates"
|
|
32385
|
+
].map(phrasePattern)
|
|
32386
|
+
},
|
|
32387
|
+
{
|
|
32388
|
+
category: "agent_state",
|
|
32389
|
+
patterns: [
|
|
32390
|
+
"state",
|
|
32391
|
+
"status",
|
|
32392
|
+
"agent state",
|
|
32393
|
+
"agent status",
|
|
32394
|
+
"status of agent",
|
|
32395
|
+
"status of agents",
|
|
32396
|
+
"state of",
|
|
32397
|
+
"doing"
|
|
32398
|
+
].map(phrasePattern)
|
|
32399
|
+
},
|
|
32400
|
+
{
|
|
32401
|
+
category: "agent_activity",
|
|
32402
|
+
patterns: [
|
|
32403
|
+
"activity",
|
|
32404
|
+
"agent activity",
|
|
32405
|
+
"what did",
|
|
32406
|
+
"recent activity"
|
|
32407
|
+
].map(phrasePattern)
|
|
32408
|
+
},
|
|
32409
|
+
{
|
|
32410
|
+
category: "audit_log",
|
|
32411
|
+
patterns: [
|
|
32412
|
+
"audit log",
|
|
32413
|
+
"audit",
|
|
32414
|
+
"log entry",
|
|
32415
|
+
"log entries",
|
|
32416
|
+
"what happened",
|
|
32417
|
+
"show me events",
|
|
32418
|
+
"event class"
|
|
32419
|
+
].map(phrasePattern)
|
|
32420
|
+
},
|
|
32421
|
+
{
|
|
32422
|
+
category: "sentinel_findings",
|
|
32423
|
+
patterns: [
|
|
32424
|
+
"sentinel",
|
|
32425
|
+
"sentinels",
|
|
32426
|
+
"warning",
|
|
32427
|
+
"warnings",
|
|
32428
|
+
"alert",
|
|
32429
|
+
"alerts",
|
|
32430
|
+
"whats wrong",
|
|
32431
|
+
"what's wrong",
|
|
32432
|
+
"findings"
|
|
32433
|
+
].map(phrasePattern)
|
|
32434
|
+
},
|
|
32435
|
+
{
|
|
32436
|
+
category: "anomaly_alerts",
|
|
32437
|
+
patterns: [
|
|
32438
|
+
"anomaly",
|
|
32439
|
+
"anomalies",
|
|
32440
|
+
"spike",
|
|
32441
|
+
"unusual",
|
|
32442
|
+
"outlier"
|
|
32443
|
+
].map(phrasePattern)
|
|
32444
|
+
},
|
|
32445
|
+
{
|
|
32446
|
+
category: "recent_receipts",
|
|
32447
|
+
patterns: [
|
|
32448
|
+
"receipt",
|
|
32449
|
+
"receipts",
|
|
32450
|
+
"concordia",
|
|
32451
|
+
"commitment",
|
|
32452
|
+
"commitments",
|
|
32453
|
+
"chain",
|
|
32454
|
+
"chains"
|
|
32455
|
+
].map(phrasePattern)
|
|
32456
|
+
},
|
|
32457
|
+
{
|
|
32458
|
+
category: "verascore_deltas",
|
|
32459
|
+
patterns: [
|
|
32460
|
+
"verascore",
|
|
32461
|
+
"vera score",
|
|
32462
|
+
"trust score",
|
|
32463
|
+
"reputation"
|
|
32464
|
+
].map(phrasePattern)
|
|
32465
|
+
}
|
|
32466
|
+
];
|
|
32467
|
+
function extractAgentNameHint(query) {
|
|
32468
|
+
const agentPattern = /\bagent\s+["']?([A-Za-z][\w-]{0,40})["']?/i;
|
|
32469
|
+
const m = query.match(agentPattern);
|
|
32470
|
+
if (m && m[1]) return m[1];
|
|
32471
|
+
const quoted = query.match(/["']([A-Za-z][\w-]{0,40})["']/);
|
|
32472
|
+
if (quoted && quoted[1]) return quoted[1];
|
|
32473
|
+
return null;
|
|
32474
|
+
}
|
|
32475
|
+
var TRIVIAL_GREETINGS = /* @__PURE__ */ new Set([
|
|
32476
|
+
"hi",
|
|
32477
|
+
"hello",
|
|
32478
|
+
"hey",
|
|
32479
|
+
"yo",
|
|
32480
|
+
"ok",
|
|
32481
|
+
"thanks",
|
|
32482
|
+
"thx",
|
|
32483
|
+
"thank you"
|
|
32484
|
+
]);
|
|
32485
|
+
function isTrivialQuery(query) {
|
|
32486
|
+
const norm = query.trim().toLowerCase();
|
|
32487
|
+
if (norm.length === 0) return true;
|
|
32488
|
+
if (norm.length < 8) return true;
|
|
32489
|
+
return TRIVIAL_GREETINGS.has(norm);
|
|
32490
|
+
}
|
|
32491
|
+
function classifyQuery(query) {
|
|
32492
|
+
const normalized = query.toLowerCase();
|
|
32493
|
+
const matches = [];
|
|
32494
|
+
for (const spec of CATEGORY_KEYWORDS) {
|
|
32495
|
+
const matchedPhrases = [];
|
|
32496
|
+
for (const pattern of spec.patterns) {
|
|
32497
|
+
if (matchedPhrases.includes(pattern.phrase)) continue;
|
|
32498
|
+
const re = new RegExp(pattern.source, "i");
|
|
32499
|
+
if (re.test(normalized)) {
|
|
32500
|
+
matchedPhrases.push(pattern.phrase);
|
|
32501
|
+
}
|
|
32502
|
+
}
|
|
32503
|
+
if (matchedPhrases.length === 0) continue;
|
|
32504
|
+
const confidence = Math.min(1, 0.4 + 0.3 * matchedPhrases.length);
|
|
32505
|
+
matches.push({
|
|
32506
|
+
category: spec.category,
|
|
32507
|
+
confidence,
|
|
32508
|
+
matched_keywords: matchedPhrases,
|
|
32509
|
+
agent_name_hint: spec.category === "agent_state" || spec.category === "agent_activity" ? extractAgentNameHint(query) : null
|
|
32510
|
+
});
|
|
32511
|
+
}
|
|
32512
|
+
matches.sort((a, b) => {
|
|
32513
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
32514
|
+
return CONTEXT_CATEGORIES.indexOf(a.category) - CONTEXT_CATEGORIES.indexOf(b.category);
|
|
32515
|
+
});
|
|
32516
|
+
return matches;
|
|
32517
|
+
}
|
|
32518
|
+
function approxTokenLen(text) {
|
|
32519
|
+
return Math.ceil(text.length / APPROX_CHARS_PER_TOKEN);
|
|
32520
|
+
}
|
|
32521
|
+
var CATEGORY_LABELS = {
|
|
32522
|
+
templates: "Templates",
|
|
32523
|
+
agent_state: "Agent state",
|
|
32524
|
+
agent_activity: "Agent activity",
|
|
32525
|
+
audit_log: "Audit log",
|
|
32526
|
+
sentinel_findings: "Sentinel findings",
|
|
32527
|
+
anomaly_alerts: "Anomaly alerts",
|
|
32528
|
+
recent_receipts: "Recent receipts",
|
|
32529
|
+
verascore_deltas: "Verascore deltas"
|
|
32530
|
+
};
|
|
32531
|
+
async function runFetcher(match, fetchers) {
|
|
32532
|
+
switch (match.category) {
|
|
32533
|
+
case "templates":
|
|
32534
|
+
return fetchers.templates();
|
|
32535
|
+
case "agent_state":
|
|
32536
|
+
return fetchers.agent_state(match.agent_name_hint);
|
|
32537
|
+
case "agent_activity":
|
|
32538
|
+
return fetchers.agent_activity(match.agent_name_hint);
|
|
32539
|
+
case "audit_log":
|
|
32540
|
+
return fetchers.audit_log();
|
|
32541
|
+
case "sentinel_findings":
|
|
32542
|
+
return fetchers.sentinel_findings();
|
|
32543
|
+
case "anomaly_alerts":
|
|
32544
|
+
return fetchers.anomaly_alerts();
|
|
32545
|
+
case "recent_receipts":
|
|
32546
|
+
return fetchers.recent_receipts();
|
|
32547
|
+
case "verascore_deltas":
|
|
32548
|
+
return fetchers.verascore_deltas();
|
|
32549
|
+
}
|
|
32550
|
+
}
|
|
32551
|
+
function trivialMatch(category) {
|
|
32552
|
+
return {
|
|
32553
|
+
category,
|
|
32554
|
+
confidence: 0.5,
|
|
32555
|
+
matched_keywords: ["llm-assist"],
|
|
32556
|
+
agent_name_hint: null
|
|
32557
|
+
};
|
|
32558
|
+
}
|
|
32559
|
+
async function foldContext(query, fetchers, opts) {
|
|
32560
|
+
const budget = opts?.maxTokens ?? DEFAULT_DYNAMIC_CONTEXT_TOKEN_BUDGET;
|
|
32561
|
+
let matches = classifyQuery(query);
|
|
32562
|
+
if (matches.length === 0 && !isTrivialQuery(query) && opts?.llmAssistClassify) {
|
|
32563
|
+
try {
|
|
32564
|
+
const picked = await opts.llmAssistClassify(query, CONTEXT_CATEGORIES);
|
|
32565
|
+
if (picked !== "none" && CONTEXT_CATEGORIES.includes(picked)) {
|
|
32566
|
+
matches = [trivialMatch(picked)];
|
|
32567
|
+
}
|
|
32568
|
+
} catch {
|
|
32569
|
+
}
|
|
32570
|
+
}
|
|
32571
|
+
if (matches.length === 0) {
|
|
32572
|
+
return { section: "", categoriesIncluded: [] };
|
|
32573
|
+
}
|
|
32574
|
+
const attempts = [];
|
|
32575
|
+
for (const match of matches) {
|
|
32576
|
+
try {
|
|
32577
|
+
const text = await runFetcher(match, fetchers);
|
|
32578
|
+
const trimmed = text.trim();
|
|
32579
|
+
if (trimmed.length > 0) {
|
|
32580
|
+
attempts.push({ category: match.category, text: trimmed });
|
|
32581
|
+
}
|
|
32582
|
+
} catch (err) {
|
|
32583
|
+
opts?.onFetcherFailure?.(match.category, err);
|
|
32584
|
+
}
|
|
32585
|
+
}
|
|
32586
|
+
if (attempts.length === 0) {
|
|
32587
|
+
return { section: "", categoriesIncluded: [] };
|
|
32588
|
+
}
|
|
32589
|
+
const headerTokens = approxTokenLen(`${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32590
|
+
`);
|
|
32591
|
+
const sepTokens = approxTokenLen("\n\n");
|
|
32592
|
+
let runningTokens = headerTokens;
|
|
32593
|
+
const kept = [];
|
|
32594
|
+
for (const attempt of attempts) {
|
|
32595
|
+
const block = `### ${CATEGORY_LABELS[attempt.category]}
|
|
32596
|
+
${attempt.text}`;
|
|
32597
|
+
const tokens = approxTokenLen(block) + (kept.length > 0 ? sepTokens : 0);
|
|
32598
|
+
if (kept.length === 0) {
|
|
32599
|
+
kept.push(attempt);
|
|
32600
|
+
runningTokens += tokens;
|
|
32601
|
+
continue;
|
|
32602
|
+
}
|
|
32603
|
+
if (runningTokens + tokens > budget) break;
|
|
32604
|
+
kept.push(attempt);
|
|
32605
|
+
runningTokens += tokens;
|
|
32606
|
+
}
|
|
32607
|
+
const blocks = kept.map(
|
|
32608
|
+
(k) => `### ${CATEGORY_LABELS[k.category]}
|
|
32609
|
+
${k.text}`
|
|
32610
|
+
);
|
|
32611
|
+
const section = `${DYNAMIC_CONTEXT_SECTION_HEADER}
|
|
32612
|
+
${blocks.join("\n\n")}`;
|
|
32613
|
+
return {
|
|
32614
|
+
section,
|
|
32615
|
+
categoriesIncluded: kept.map((k) => k.category)
|
|
32616
|
+
};
|
|
32617
|
+
}
|
|
32618
|
+
|
|
32003
32619
|
// src/chat/operator-chat-service.ts
|
|
32004
32620
|
var DEFAULT_CONCIERGE_MAX_TOKENS = 512;
|
|
32005
32621
|
var DEFAULT_CONCIERGE_HISTORY_WINDOW_TURNS = 10;
|
|
32006
32622
|
var DEFAULT_CONCIERGE_HISTORY_FRESHNESS_MS = 24 * 60 * 60 * 1e3;
|
|
32007
32623
|
var DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET = 500;
|
|
32008
32624
|
var DEFAULT_CONCIERGE_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
32009
|
-
|
|
32625
|
+
var DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET = 2e3;
|
|
32626
|
+
function approxTokenLen2(text) {
|
|
32010
32627
|
return Math.ceil(text.length / 4);
|
|
32011
32628
|
}
|
|
32012
32629
|
var SANCTUARY_DOMAIN_REFERENCE = `Castle Architecture (four enforcement layers):
|
|
@@ -32050,6 +32667,9 @@ var OperatorChatService = class {
|
|
|
32050
32667
|
historyTokenBudget;
|
|
32051
32668
|
sessionTtlMs;
|
|
32052
32669
|
clock;
|
|
32670
|
+
contextFetchers;
|
|
32671
|
+
contextLlmAssist;
|
|
32672
|
+
dynamicContextBudget;
|
|
32053
32673
|
/**
|
|
32054
32674
|
* In-memory thread_id assigned to the active concierge session.
|
|
32055
32675
|
* The first sendConcierge call after construction allocates a fresh
|
|
@@ -32081,6 +32701,13 @@ var OperatorChatService = class {
|
|
|
32081
32701
|
this.historyTokenBudget = deps.conciergeHistoryTokenBudget !== void 0 && deps.conciergeHistoryTokenBudget > 0 ? deps.conciergeHistoryTokenBudget : DEFAULT_CONCIERGE_HISTORY_TOKEN_BUDGET;
|
|
32082
32702
|
this.sessionTtlMs = deps.conciergeSessionTtlMs !== void 0 && deps.conciergeSessionTtlMs > 0 ? deps.conciergeSessionTtlMs : DEFAULT_CONCIERGE_SESSION_TTL_MS;
|
|
32083
32703
|
this.clock = deps.conciergeClock ?? (() => Date.now());
|
|
32704
|
+
if (deps.conciergeContextFetchers) {
|
|
32705
|
+
this.contextFetchers = deps.conciergeContextFetchers;
|
|
32706
|
+
}
|
|
32707
|
+
if (deps.conciergeContextLlmAssist) {
|
|
32708
|
+
this.contextLlmAssist = deps.conciergeContextLlmAssist;
|
|
32709
|
+
}
|
|
32710
|
+
this.dynamicContextBudget = deps.conciergeDynamicContextBudget !== void 0 && deps.conciergeDynamicContextBudget > 0 ? deps.conciergeDynamicContextBudget : DEFAULT_CONCIERGE_DYNAMIC_CONTEXT_BUDGET;
|
|
32084
32711
|
}
|
|
32085
32712
|
// ── Concierge ─────────────────────────────────────────────────────────
|
|
32086
32713
|
/**
|
|
@@ -32144,6 +32771,7 @@ var OperatorChatService = class {
|
|
|
32144
32771
|
let servedBy = "disabled";
|
|
32145
32772
|
let displayLabel = "Concierge: substrate not configured";
|
|
32146
32773
|
let outcome = "substrate_disabled";
|
|
32774
|
+
let dynamicCategoriesIncluded = [];
|
|
32147
32775
|
if (!this.substrateSelector) {
|
|
32148
32776
|
conciergeBody = "Concierge unavailable. The substrate selector is not configured for this fortress. Pick a substrate in the Policy center to enable concierge replies.";
|
|
32149
32777
|
} else {
|
|
@@ -32155,7 +32783,14 @@ var OperatorChatService = class {
|
|
|
32155
32783
|
conciergeBody = "Concierge unavailable. The chosen substrate does not support summarization. Pick a different substrate in the Policy center.";
|
|
32156
32784
|
outcome = "substrate_disabled";
|
|
32157
32785
|
} else {
|
|
32158
|
-
const
|
|
32786
|
+
const dynamicResult = await this.runDynamicContextFold(
|
|
32787
|
+
filterResult.filtered
|
|
32788
|
+
);
|
|
32789
|
+
dynamicCategoriesIncluded = dynamicResult.categoriesIncluded;
|
|
32790
|
+
const context = await this.assembleConciergeContext(
|
|
32791
|
+
priorTurns,
|
|
32792
|
+
dynamicResult.section
|
|
32793
|
+
);
|
|
32159
32794
|
const response = await this.substrateSelector.invokeSummarize(
|
|
32160
32795
|
"concierge",
|
|
32161
32796
|
{
|
|
@@ -32219,7 +32854,8 @@ var OperatorChatService = class {
|
|
|
32219
32854
|
...assistantTurnId !== void 0 ? { turn_index: assistantTurnId } : {},
|
|
32220
32855
|
...this.memory ? {
|
|
32221
32856
|
prior_turns_folded: memoryReadFailureReason === null ? priorTurns.length : 0
|
|
32222
|
-
} : {}
|
|
32857
|
+
} : {},
|
|
32858
|
+
...this.contextFetchers ? { dynamic_context_categories: [...dynamicCategoriesIncluded] } : {}
|
|
32223
32859
|
};
|
|
32224
32860
|
this.emit(OPERATOR_CHAT_OPS.CONCIERGE_CHAT, payload, outcome === "ok" ? "success" : "failure");
|
|
32225
32861
|
return {
|
|
@@ -32370,10 +33006,13 @@ var OperatorChatService = class {
|
|
|
32370
33006
|
* ## Sanctuary reference
|
|
32371
33007
|
* <static domain reference block>
|
|
32372
33008
|
*
|
|
33009
|
+
* ## Live fortress context ← WP-V1.3-9 Tau-3, when present
|
|
33010
|
+
* ### <Category>
|
|
33011
|
+
* <fetcher payload>
|
|
33012
|
+
*
|
|
32373
33013
|
* ## Prior conversation ← WP-V1.3-9 Tau-2, when present
|
|
32374
33014
|
* OPERATOR: ...
|
|
32375
33015
|
* CONCIERGE: ...
|
|
32376
|
-
* ---
|
|
32377
33016
|
*
|
|
32378
33017
|
* ## Recent activity
|
|
32379
33018
|
* <recentActivity output>
|
|
@@ -32392,13 +33031,14 @@ var OperatorChatService = class {
|
|
|
32392
33031
|
* if available; the v1.2 selector does not expose one, so structured
|
|
32393
33032
|
* serialization is the canonical path for v1.3.
|
|
32394
33033
|
*/
|
|
32395
|
-
async assembleConciergeContext(priorTurns = []) {
|
|
33034
|
+
async assembleConciergeContext(priorTurns = [], dynamicSection = "") {
|
|
32396
33035
|
const ref = `## Sanctuary reference
|
|
32397
33036
|
${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
32398
33037
|
const priorSection = this.formatPriorTurnsSection(priorTurns);
|
|
32399
33038
|
if (!this.contextProviders) {
|
|
32400
33039
|
return [
|
|
32401
33040
|
ref,
|
|
33041
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32402
33042
|
...priorSection ? [priorSection] : [],
|
|
32403
33043
|
"## Recent activity\n(no providers wired)",
|
|
32404
33044
|
"## Wrapped agents\n(no providers wired)",
|
|
@@ -32412,6 +33052,7 @@ ${SANCTUARY_DOMAIN_REFERENCE}`;
|
|
|
32412
33052
|
]);
|
|
32413
33053
|
return [
|
|
32414
33054
|
ref,
|
|
33055
|
+
...dynamicSection ? [dynamicSection] : [],
|
|
32415
33056
|
...priorSection ? [priorSection] : [],
|
|
32416
33057
|
`## Recent activity
|
|
32417
33058
|
${activity}`,
|
|
@@ -32421,6 +33062,51 @@ ${agents}`,
|
|
|
32421
33062
|
${inbox}`
|
|
32422
33063
|
].join("\n\n");
|
|
32423
33064
|
}
|
|
33065
|
+
/**
|
|
33066
|
+
* Run the WP-V1.3-9 Tau-3 dynamic-context fold for a single round-
|
|
33067
|
+
* trip. Fail-soft on every axis: missing fetchers short-circuit to
|
|
33068
|
+
* an empty fold, fetcher failures emit a per-category audit event
|
|
33069
|
+
* and are omitted from the rendered section, an LLM-assist failure
|
|
33070
|
+
* proceeds with no fold. Returns the rendered section + the list of
|
|
33071
|
+
* categories whose data made it into the section (used for the
|
|
33072
|
+
* round-trip audit emission).
|
|
33073
|
+
*/
|
|
33074
|
+
async runDynamicContextFold(query) {
|
|
33075
|
+
if (!this.contextFetchers) {
|
|
33076
|
+
return { section: "", categoriesIncluded: [] };
|
|
33077
|
+
}
|
|
33078
|
+
const result = await foldContext(query, this.contextFetchers, {
|
|
33079
|
+
maxTokens: this.dynamicContextBudget,
|
|
33080
|
+
...this.contextLlmAssist ? { llmAssistClassify: this.contextLlmAssist } : {},
|
|
33081
|
+
onFetcherFailure: (category, error) => {
|
|
33082
|
+
this.emitContextFetcherFailed(category, classifyFetcherError(error));
|
|
33083
|
+
}
|
|
33084
|
+
});
|
|
33085
|
+
return result;
|
|
33086
|
+
}
|
|
33087
|
+
/**
|
|
33088
|
+
* Emit the WP-V1.3-9 Tau-3 fetcher-failure audit event. Pulled out
|
|
33089
|
+
* of the fold path so the dynamic-context handler stays readable.
|
|
33090
|
+
* Emits with `result: "failure"` since the named category dropped
|
|
33091
|
+
* from the rendered section for this round-trip.
|
|
33092
|
+
*/
|
|
33093
|
+
emitContextFetcherFailed(category, failureReason) {
|
|
33094
|
+
const payload = {
|
|
33095
|
+
version: "1.2",
|
|
33096
|
+
event_id: makeEventId("conc-ctxfail"),
|
|
33097
|
+
emitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33098
|
+
identity_id: this.identityId,
|
|
33099
|
+
kind: "operator_concierge_context_fetcher_failed",
|
|
33100
|
+
surface: "concierge",
|
|
33101
|
+
category,
|
|
33102
|
+
failure_reason: failureReason
|
|
33103
|
+
};
|
|
33104
|
+
this.emit(
|
|
33105
|
+
OPERATOR_CHAT_OPS.CONCIERGE_CONTEXT_FETCHER_FAILED,
|
|
33106
|
+
payload,
|
|
33107
|
+
"failure"
|
|
33108
|
+
);
|
|
33109
|
+
}
|
|
32424
33110
|
/**
|
|
32425
33111
|
* Render the prior-conversation section with token-budget enforcement
|
|
32426
33112
|
* (WP-V1.3-9 Tau-2). Drops oldest turns first when the rendered
|
|
@@ -32431,14 +33117,14 @@ ${inbox}`
|
|
|
32431
33117
|
if (turns.length === 0) return "";
|
|
32432
33118
|
const HEADER = "## Prior conversation";
|
|
32433
33119
|
const lines = turns.map(formatPriorTurnLine);
|
|
32434
|
-
const headerTokens =
|
|
33120
|
+
const headerTokens = approxTokenLen2(`${HEADER}
|
|
32435
33121
|
`);
|
|
32436
|
-
const sepTokens =
|
|
33122
|
+
const sepTokens = approxTokenLen2("\n");
|
|
32437
33123
|
let runningTokens = headerTokens;
|
|
32438
33124
|
let runningLines = [];
|
|
32439
33125
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
32440
33126
|
const line = lines[i];
|
|
32441
|
-
const tokens =
|
|
33127
|
+
const tokens = approxTokenLen2(line) + (runningLines.length > 0 ? sepTokens : 0);
|
|
32442
33128
|
if (runningTokens + tokens > this.historyTokenBudget) break;
|
|
32443
33129
|
runningTokens += tokens;
|
|
32444
33130
|
runningLines.push(line);
|
|
@@ -32462,6 +33148,17 @@ ${runningLines.join("\n")}`;
|
|
|
32462
33148
|
function makeEventId(prefix) {
|
|
32463
33149
|
return `${prefix}-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
32464
33150
|
}
|
|
33151
|
+
function classifyFetcherError(error) {
|
|
33152
|
+
const msg = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
33153
|
+
if (msg.includes("timeout") || msg.includes("timed out")) return "timeout";
|
|
33154
|
+
if (msg.includes("schema") || msg.includes("invalid shape")) {
|
|
33155
|
+
return "schema_mismatch";
|
|
33156
|
+
}
|
|
33157
|
+
if (msg.includes("io") || msg.includes("read") || msg.includes("enoent") || msg.includes("eacces")) {
|
|
33158
|
+
return "io_failed";
|
|
33159
|
+
}
|
|
33160
|
+
return "unknown";
|
|
33161
|
+
}
|
|
32465
33162
|
function formatPriorTurnLine(turn) {
|
|
32466
33163
|
const label = turn.role === "user" ? "OPERATOR" : "CONCIERGE";
|
|
32467
33164
|
return `${label}: ${turn.content}`;
|
|
@@ -32474,7 +33171,7 @@ function hashOf(input) {
|
|
|
32474
33171
|
init_encryption();
|
|
32475
33172
|
init_encoding();
|
|
32476
33173
|
var OPERATOR_CHAT_NAMESPACE = "_chat";
|
|
32477
|
-
var
|
|
33174
|
+
var HKDF_INFO2 = "operator-chat-store-v1";
|
|
32478
33175
|
function chatStorageKey(surface, threadKey) {
|
|
32479
33176
|
return `${surface}.${threadKey}`;
|
|
32480
33177
|
}
|
|
@@ -32483,7 +33180,7 @@ var OperatorChatStore = class {
|
|
|
32483
33180
|
encryptionKey;
|
|
32484
33181
|
constructor(storage, masterKey) {
|
|
32485
33182
|
this.storage = storage;
|
|
32486
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33183
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO2);
|
|
32487
33184
|
}
|
|
32488
33185
|
/**
|
|
32489
33186
|
* Load a thread. Returns null if no record exists or if the on-disk
|
|
@@ -32567,9 +33264,9 @@ init_encryption();
|
|
|
32567
33264
|
init_encoding();
|
|
32568
33265
|
var CONCIERGE_MEMORY_NAMESPACE = "_chat";
|
|
32569
33266
|
var CONCIERGE_MEMORY_KEY_PREFIX = "concierge_memory.";
|
|
32570
|
-
var
|
|
33267
|
+
var HKDF_INFO3 = "concierge-memory-store-v1";
|
|
32571
33268
|
var DEFAULT_CONCIERGE_RETENTION_DAYS = 30;
|
|
32572
|
-
var
|
|
33269
|
+
var MAX_BUNDLE_BYTES3 = 4 * 1024 * 1024;
|
|
32573
33270
|
var ConciergeMemoryStore = class {
|
|
32574
33271
|
storage;
|
|
32575
33272
|
encryptionKey;
|
|
@@ -32578,7 +33275,7 @@ var ConciergeMemoryStore = class {
|
|
|
32578
33275
|
locks;
|
|
32579
33276
|
constructor(opts) {
|
|
32580
33277
|
this.storage = opts.storage;
|
|
32581
|
-
this.encryptionKey = derivePurposeKey(opts.masterKey,
|
|
33278
|
+
this.encryptionKey = derivePurposeKey(opts.masterKey, HKDF_INFO3);
|
|
32582
33279
|
this.fortressId = opts.fortressId;
|
|
32583
33280
|
this.retentionDays = opts.retentionDays !== void 0 && opts.retentionDays > 0 ? opts.retentionDays : DEFAULT_CONCIERGE_RETENTION_DAYS;
|
|
32584
33281
|
this.locks = /* @__PURE__ */ new Map();
|
|
@@ -32657,7 +33354,7 @@ var ConciergeMemoryStore = class {
|
|
|
32657
33354
|
return { ok: false, reason: "io_failed" };
|
|
32658
33355
|
}
|
|
32659
33356
|
if (!raw) return { ok: true, turns: [] };
|
|
32660
|
-
if (raw.length >
|
|
33357
|
+
if (raw.length > MAX_BUNDLE_BYTES3) {
|
|
32661
33358
|
return { ok: false, reason: "oversize_bundle" };
|
|
32662
33359
|
}
|
|
32663
33360
|
let envelope;
|
|
@@ -32704,7 +33401,7 @@ var ConciergeMemoryStore = class {
|
|
|
32704
33401
|
);
|
|
32705
33402
|
const summaries = [];
|
|
32706
33403
|
for (const meta of entries) {
|
|
32707
|
-
const threadId =
|
|
33404
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32708
33405
|
if (threadId === null) continue;
|
|
32709
33406
|
const bundle = await this.loadBundle(threadId);
|
|
32710
33407
|
if (!bundle || bundle.turns.length === 0) continue;
|
|
@@ -32757,7 +33454,7 @@ var ConciergeMemoryStore = class {
|
|
|
32757
33454
|
);
|
|
32758
33455
|
let pruned = 0;
|
|
32759
33456
|
for (const meta of entries) {
|
|
32760
|
-
const threadId =
|
|
33457
|
+
const threadId = stripKeyPrefix2(meta.key);
|
|
32761
33458
|
if (threadId === null) continue;
|
|
32762
33459
|
pruned += await this.withLock(threadId, async () => {
|
|
32763
33460
|
const bundle = await this.loadBundle(threadId);
|
|
@@ -32788,7 +33485,7 @@ var ConciergeMemoryStore = class {
|
|
|
32788
33485
|
return null;
|
|
32789
33486
|
}
|
|
32790
33487
|
if (!raw) return null;
|
|
32791
|
-
if (raw.length >
|
|
33488
|
+
if (raw.length > MAX_BUNDLE_BYTES3) return null;
|
|
32792
33489
|
try {
|
|
32793
33490
|
const envelope = JSON.parse(bytesToString(raw));
|
|
32794
33491
|
const aad = stringToBytes(threadId);
|
|
@@ -32841,7 +33538,7 @@ var ConciergeMemoryStore = class {
|
|
|
32841
33538
|
function bundleKey(threadId) {
|
|
32842
33539
|
return `${CONCIERGE_MEMORY_KEY_PREFIX}${threadId}`;
|
|
32843
33540
|
}
|
|
32844
|
-
function
|
|
33541
|
+
function stripKeyPrefix2(key) {
|
|
32845
33542
|
if (!key.startsWith(CONCIERGE_MEMORY_KEY_PREFIX)) return null;
|
|
32846
33543
|
return key.slice(CONCIERGE_MEMORY_KEY_PREFIX.length);
|
|
32847
33544
|
}
|
|
@@ -32910,7 +33607,18 @@ function buildV11Bindings(inputs) {
|
|
|
32910
33607
|
registry
|
|
32911
33608
|
}),
|
|
32912
33609
|
conciergePiiFilter: buildConciergePiiFilter(),
|
|
32913
|
-
conciergeMemory
|
|
33610
|
+
conciergeMemory,
|
|
33611
|
+
conciergeContextFetchers: buildConciergeContextFetchers({
|
|
33612
|
+
auditLog: inputs.auditLog,
|
|
33613
|
+
identityId: inputs.identityId,
|
|
33614
|
+
registry
|
|
33615
|
+
}),
|
|
33616
|
+
...inputs.intelligenceSelector ? {
|
|
33617
|
+
conciergeContextLlmAssist: buildConciergeContextLlmAssist({
|
|
33618
|
+
selector: inputs.intelligenceSelector,
|
|
33619
|
+
identityId: inputs.identityId
|
|
33620
|
+
})
|
|
33621
|
+
} : {}
|
|
32914
33622
|
});
|
|
32915
33623
|
}
|
|
32916
33624
|
const hubService = new HubService({
|
|
@@ -32971,6 +33679,107 @@ function buildConciergeContextProviders(args) {
|
|
|
32971
33679
|
}
|
|
32972
33680
|
};
|
|
32973
33681
|
}
|
|
33682
|
+
function buildConciergeContextFetchers(args) {
|
|
33683
|
+
const empty = async () => "";
|
|
33684
|
+
return {
|
|
33685
|
+
templates: async () => {
|
|
33686
|
+
const entries = listTemplates();
|
|
33687
|
+
if (entries.length === 0) return "(no templates installed)";
|
|
33688
|
+
const lines = entries.map((e) => {
|
|
33689
|
+
const m = e.metadata;
|
|
33690
|
+
return `${m.name} (tier ${m.tier}, channel ${m.channel}, target ${m.target_archetype})`;
|
|
33691
|
+
});
|
|
33692
|
+
return lines.join("\n");
|
|
33693
|
+
},
|
|
33694
|
+
agent_state: async (agentNameHint) => {
|
|
33695
|
+
const records = args.registry.list({ identity_id: args.identityId });
|
|
33696
|
+
if (records.length === 0) return "(no wrapped agents)";
|
|
33697
|
+
const filtered = agentNameHint ? records.filter(
|
|
33698
|
+
(r) => r.agent_id.toLowerCase().includes(agentNameHint.toLowerCase()) || r.harness.toLowerCase().includes(agentNameHint.toLowerCase())
|
|
33699
|
+
) : records;
|
|
33700
|
+
const target = filtered.length > 0 ? filtered : records;
|
|
33701
|
+
const lines = target.slice(0, 20).map((r) => {
|
|
33702
|
+
const tmpl = typeof r.channel_template_id === "string" ? r.channel_template_id : "no_template";
|
|
33703
|
+
return `${r.agent_id} harness=${r.harness} status=${r.status} template=${tmpl}`;
|
|
33704
|
+
});
|
|
33705
|
+
return lines.join("\n");
|
|
33706
|
+
},
|
|
33707
|
+
agent_activity: async (agentNameHint) => {
|
|
33708
|
+
const result = await args.auditLog.query({ limit: 50 });
|
|
33709
|
+
const owned = result.entries.filter(
|
|
33710
|
+
(e) => e.identity_id === args.identityId
|
|
33711
|
+
);
|
|
33712
|
+
const filtered = agentNameHint ? owned.filter((e) => {
|
|
33713
|
+
const agentId = e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : "";
|
|
33714
|
+
return agentId.toLowerCase().includes(agentNameHint.toLowerCase());
|
|
33715
|
+
}) : owned;
|
|
33716
|
+
const tail = (filtered.length > 0 ? filtered : owned).slice(-20);
|
|
33717
|
+
if (tail.length === 0) return "(no activity)";
|
|
33718
|
+
return tail.map((e) => {
|
|
33719
|
+
const agentId = (e.details && typeof e.details["agent_id"] === "string" ? e.details["agent_id"] : null) ?? "_fortress";
|
|
33720
|
+
return `${e.timestamp} ${e.layer}.${e.operation} agent=${agentId} result=${e.result}`;
|
|
33721
|
+
}).join("\n");
|
|
33722
|
+
},
|
|
33723
|
+
audit_log: async () => {
|
|
33724
|
+
const result = await args.auditLog.query({ limit: 30 });
|
|
33725
|
+
const owned = result.entries.filter(
|
|
33726
|
+
(e) => e.identity_id === args.identityId
|
|
33727
|
+
);
|
|
33728
|
+
if (owned.length === 0) return "(no audit log entries)";
|
|
33729
|
+
return owned.slice(-30).map(
|
|
33730
|
+
(e) => `${e.timestamp} ${e.layer}.${e.operation} result=${e.result}`
|
|
33731
|
+
).join("\n");
|
|
33732
|
+
},
|
|
33733
|
+
sentinel_findings: empty,
|
|
33734
|
+
anomaly_alerts: empty,
|
|
33735
|
+
recent_receipts: async () => {
|
|
33736
|
+
const result = await args.auditLog.query({ limit: 100 });
|
|
33737
|
+
const owned = result.entries.filter(
|
|
33738
|
+
(e) => e.identity_id === args.identityId && e.operation.startsWith("composition_")
|
|
33739
|
+
);
|
|
33740
|
+
if (owned.length === 0) return "(no recent composition events)";
|
|
33741
|
+
return owned.slice(-15).map((e) => `${e.timestamp} ${e.operation} result=${e.result}`).join("\n");
|
|
33742
|
+
},
|
|
33743
|
+
verascore_deltas: empty
|
|
33744
|
+
};
|
|
33745
|
+
}
|
|
33746
|
+
function buildConciergeContextLlmAssist(args) {
|
|
33747
|
+
return async (query, categories) => {
|
|
33748
|
+
const labelList = categories.map((c) => `- ${c}`).join("\n");
|
|
33749
|
+
const prompt = `You are a router. Classify the operator's query into one of the categories below or "none".
|
|
33750
|
+
Reply with exactly one token: one category name or "none".
|
|
33751
|
+
|
|
33752
|
+
Categories:
|
|
33753
|
+
${labelList}
|
|
33754
|
+
|
|
33755
|
+
Query: ${query}
|
|
33756
|
+
|
|
33757
|
+
Category:`;
|
|
33758
|
+
try {
|
|
33759
|
+
const handle = await args.selector.getSubstrate("concierge");
|
|
33760
|
+
if (!handle.capability.summarize) return "none";
|
|
33761
|
+
const response = await args.selector.invokeSummarize("concierge", {
|
|
33762
|
+
kind: "summarize",
|
|
33763
|
+
context: prompt,
|
|
33764
|
+
query: "Output the single category token.",
|
|
33765
|
+
maxTokens: 16
|
|
33766
|
+
});
|
|
33767
|
+
if (response.failureClass || response.body.kind !== "summarize") {
|
|
33768
|
+
return "none";
|
|
33769
|
+
}
|
|
33770
|
+
const raw = response.body.text.trim().toLowerCase();
|
|
33771
|
+
const head = raw.split(/\s|[.,!?:;]/)[0] ?? "";
|
|
33772
|
+
const normalized = head.replace(/[^a-z_]/g, "");
|
|
33773
|
+
const known = categories;
|
|
33774
|
+
if (known.includes(normalized)) {
|
|
33775
|
+
return normalized;
|
|
33776
|
+
}
|
|
33777
|
+
return "none";
|
|
33778
|
+
} catch {
|
|
33779
|
+
return "none";
|
|
33780
|
+
}
|
|
33781
|
+
};
|
|
33782
|
+
}
|
|
32974
33783
|
function buildConciergePiiFilter() {
|
|
32975
33784
|
return {
|
|
32976
33785
|
filter(input) {
|
|
@@ -33102,13 +33911,13 @@ init_encryption();
|
|
|
33102
33911
|
init_encoding();
|
|
33103
33912
|
var INTELLIGENCE_NAMESPACE = "_intelligence";
|
|
33104
33913
|
var SUBSTRATE_CONFIG_KEY = "substrate-config";
|
|
33105
|
-
var
|
|
33914
|
+
var HKDF_INFO4 = "intelligence-substrate-config";
|
|
33106
33915
|
var IntelligenceConfigStore = class {
|
|
33107
33916
|
storage;
|
|
33108
33917
|
encryptionKey;
|
|
33109
33918
|
constructor(storage, masterKey) {
|
|
33110
33919
|
this.storage = storage;
|
|
33111
|
-
this.encryptionKey = derivePurposeKey(masterKey,
|
|
33920
|
+
this.encryptionKey = derivePurposeKey(masterKey, HKDF_INFO4);
|
|
33112
33921
|
}
|
|
33113
33922
|
/**
|
|
33114
33923
|
* Load the operator's substrate config from disk. Returns the config
|
|
@@ -37054,12 +37863,18 @@ ${err.message}
|
|
|
37054
37863
|
} : void 0;
|
|
37055
37864
|
const fortressIdForAggregator = fortressIdFromStoragePath(config.storage_path);
|
|
37056
37865
|
const aggregatorIdentityId = identityManager.getPrimaryIdentityId() ?? `fortress:${config.storage_path}`;
|
|
37866
|
+
const aggregatorPayloadStore = new AggregatorPayloadStore({
|
|
37867
|
+
storage,
|
|
37868
|
+
masterKey,
|
|
37869
|
+
fortressId: fortressIdForAggregator
|
|
37870
|
+
});
|
|
37057
37871
|
const approvalAggregator = new ApprovalAggregator({
|
|
37058
37872
|
storage,
|
|
37059
37873
|
masterKey,
|
|
37060
37874
|
auditLog,
|
|
37061
37875
|
identityId: aggregatorIdentityId,
|
|
37062
|
-
fortressId: fortressIdForAggregator
|
|
37876
|
+
fortressId: fortressIdForAggregator,
|
|
37877
|
+
payloadStore: aggregatorPayloadStore
|
|
37063
37878
|
});
|
|
37064
37879
|
const wrappedApprovalChannel = new AggregatorBackedChannel({
|
|
37065
37880
|
underlying: approvalChannel,
|