@sanctuary-framework/mcp-server 1.2.11 → 1.2.13
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 +2375 -201
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2375 -201
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1238 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +388 -0
- package/dist/index.d.ts +388 -0
- package/dist/index.js +1238 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -16707,13 +16707,418 @@ function crossHarnessSummary(details, sender) {
|
|
|
16707
16707
|
return `${sender} -> operator approval`;
|
|
16708
16708
|
}
|
|
16709
16709
|
var COORDINATION_VIEW_AUDIT_OPS = {
|
|
16710
|
+
/** v1.3 Omega-1: operator opened the chronological handoff list. */
|
|
16710
16711
|
VIEW_OPENED: "operator_coordination_view_opened",
|
|
16711
|
-
|
|
16712
|
+
/** v1.3 Omega-1: operator drilled into a single handoff for detail. */
|
|
16713
|
+
ENTRY_DRILLED: "operator_handoff_entry_drilled",
|
|
16714
|
+
/**
|
|
16715
|
+
* v1.3 Omega-3: operator opened the Workflows sibling-view (list of
|
|
16716
|
+
* multi-handoff workflows grouped by `workflow-grouper`). Mirrors
|
|
16717
|
+
* VIEW_OPENED's shape so the dashboard activity feed can group both
|
|
16718
|
+
* as "operator coordination surfaces."
|
|
16719
|
+
*/
|
|
16720
|
+
WORKFLOW_VIEW_OPENED: "operator_workflow_view_opened",
|
|
16721
|
+
/**
|
|
16722
|
+
* v1.3 Omega-3: operator drilled into a single workflow for its
|
|
16723
|
+
* timeline + member-handoffs detail. Mirrors ENTRY_DRILLED's shape.
|
|
16724
|
+
*/
|
|
16725
|
+
WORKFLOW_DRILLED: "operator_workflow_drilled",
|
|
16726
|
+
/**
|
|
16727
|
+
* v1.3 Omega-3: server-side state transition observed on a
|
|
16728
|
+
* workflow (e.g., in_progress -> stalled). Emitted by the route
|
|
16729
|
+
* layer after the state tracker diffs against its prior snapshot.
|
|
16730
|
+
* Distinct from the operator-action events above: this records what
|
|
16731
|
+
* the workflow itself is doing, not what the operator clicked.
|
|
16732
|
+
*/
|
|
16733
|
+
WORKFLOW_STATE_CHANGED: "coordination_workflow_state_changed"
|
|
16712
16734
|
};
|
|
16713
16735
|
|
|
16736
|
+
// src/coordination/context-transfer-extractor.ts
|
|
16737
|
+
var SUMMARY_MAX_CHARS = 240;
|
|
16738
|
+
var CATEGORY_VALUES = [
|
|
16739
|
+
"memory",
|
|
16740
|
+
"credentials",
|
|
16741
|
+
"plans",
|
|
16742
|
+
"outputs",
|
|
16743
|
+
"audit-refs",
|
|
16744
|
+
"other"
|
|
16745
|
+
];
|
|
16746
|
+
async function extractContextTransferBreakdown(detail, deps = {}) {
|
|
16747
|
+
const pathA = tryStructuredPath(detail);
|
|
16748
|
+
if (pathA) return pathA;
|
|
16749
|
+
const pathB = tryCompositionPath(detail);
|
|
16750
|
+
if (pathB) return pathB;
|
|
16751
|
+
const pathC = tryHeuristicPath(detail);
|
|
16752
|
+
if (pathC.confidence >= 0.5 || !deps.substrateSelector) {
|
|
16753
|
+
return pathC;
|
|
16754
|
+
}
|
|
16755
|
+
const assist = await tryLlmAssistPath(detail, deps.substrateSelector);
|
|
16756
|
+
return assist ?? pathC;
|
|
16757
|
+
}
|
|
16758
|
+
function tryStructuredPath(detail) {
|
|
16759
|
+
const details = sourceDetails(detail.source_audit_entry);
|
|
16760
|
+
if (!details) return null;
|
|
16761
|
+
const transferredRaw = details["transferred"];
|
|
16762
|
+
const withheldRaw = details["withheld"];
|
|
16763
|
+
if (transferredRaw === void 0 && withheldRaw === void 0) return null;
|
|
16764
|
+
const transferred = parseExplicitContextItems(transferredRaw);
|
|
16765
|
+
const withheld = parseExplicitContextItems(withheldRaw);
|
|
16766
|
+
return {
|
|
16767
|
+
handoff_entry_id: detail.entry.entry_id,
|
|
16768
|
+
transferred,
|
|
16769
|
+
withheld,
|
|
16770
|
+
source: "structured",
|
|
16771
|
+
confidence: 1
|
|
16772
|
+
};
|
|
16773
|
+
}
|
|
16774
|
+
function tryCompositionPath(detail) {
|
|
16775
|
+
const op = detail.source_audit_entry.operation;
|
|
16776
|
+
if (!op.startsWith("composition_completed")) return null;
|
|
16777
|
+
const details = sourceDetails(detail.source_audit_entry);
|
|
16778
|
+
if (!details) return null;
|
|
16779
|
+
const receiptRaw = details["receipt"];
|
|
16780
|
+
const sourceStateRaw = details["source_state_snapshot"];
|
|
16781
|
+
if (receiptRaw === void 0) return null;
|
|
16782
|
+
const transferred = parseExplicitContextItems(receiptRaw);
|
|
16783
|
+
const withheld = [];
|
|
16784
|
+
if (Array.isArray(sourceStateRaw)) {
|
|
16785
|
+
const transferredKeys = new Set(
|
|
16786
|
+
transferred.map((t) => `${t.category}:${t.summary}`)
|
|
16787
|
+
);
|
|
16788
|
+
for (const item of parseExplicitContextItems(sourceStateRaw)) {
|
|
16789
|
+
const key = `${item.category}:${item.summary}`;
|
|
16790
|
+
if (!transferredKeys.has(key)) withheld.push(item);
|
|
16791
|
+
}
|
|
16792
|
+
}
|
|
16793
|
+
return {
|
|
16794
|
+
handoff_entry_id: detail.entry.entry_id,
|
|
16795
|
+
transferred,
|
|
16796
|
+
withheld,
|
|
16797
|
+
source: "composition",
|
|
16798
|
+
confidence: 0.9
|
|
16799
|
+
};
|
|
16800
|
+
}
|
|
16801
|
+
function tryHeuristicPath(detail) {
|
|
16802
|
+
const entry = detail.entry;
|
|
16803
|
+
const audit = detail.source_audit_entry;
|
|
16804
|
+
const details = sourceDetails(audit);
|
|
16805
|
+
if (audit.operation === "cross_harness_approval_aggregated") {
|
|
16806
|
+
const ruleId = optString2(details, "policy_rule_id");
|
|
16807
|
+
if (ruleId) {
|
|
16808
|
+
const category = categoryFromPolicyRuleId(ruleId);
|
|
16809
|
+
const summary = `${ruleId} (${entry.source_agent_id} -> operator)`;
|
|
16810
|
+
return {
|
|
16811
|
+
handoff_entry_id: entry.entry_id,
|
|
16812
|
+
transferred: [
|
|
16813
|
+
{
|
|
16814
|
+
category,
|
|
16815
|
+
summary: truncate(summary, SUMMARY_MAX_CHARS),
|
|
16816
|
+
size_hint: "minimal"
|
|
16817
|
+
}
|
|
16818
|
+
],
|
|
16819
|
+
withheld: [],
|
|
16820
|
+
source: "heuristic",
|
|
16821
|
+
confidence: 0.5
|
|
16822
|
+
};
|
|
16823
|
+
}
|
|
16824
|
+
}
|
|
16825
|
+
if (audit.operation === "v1.1_local_handoff") {
|
|
16826
|
+
const reasonClass = optString2(details, "reason_class");
|
|
16827
|
+
const newStatus = optString2(details, "new_status");
|
|
16828
|
+
const previousStatus = optString2(details, "previous_status");
|
|
16829
|
+
const transferred = [];
|
|
16830
|
+
const withheld = [];
|
|
16831
|
+
if (newStatus === "denied" || newStatus === "failed") {
|
|
16832
|
+
withheld.push({
|
|
16833
|
+
category: "other",
|
|
16834
|
+
summary: truncate(
|
|
16835
|
+
`handoff ${newStatus}${reasonClass ? ` (${reasonClass})` : ""}: ${entry.source_agent_id} -> ${entry.target_agent_id}`,
|
|
16836
|
+
SUMMARY_MAX_CHARS
|
|
16837
|
+
),
|
|
16838
|
+
size_hint: "minimal"
|
|
16839
|
+
});
|
|
16840
|
+
} else if (newStatus === "accepted" || newStatus === "completed") {
|
|
16841
|
+
transferred.push({
|
|
16842
|
+
category: "other",
|
|
16843
|
+
summary: truncate(
|
|
16844
|
+
`handoff ${newStatus}${previousStatus ? ` (from ${previousStatus})` : ""}: ${entry.source_agent_id} -> ${entry.target_agent_id}`,
|
|
16845
|
+
SUMMARY_MAX_CHARS
|
|
16846
|
+
),
|
|
16847
|
+
size_hint: "small"
|
|
16848
|
+
});
|
|
16849
|
+
} else {
|
|
16850
|
+
transferred.push({
|
|
16851
|
+
category: "other",
|
|
16852
|
+
summary: truncate(
|
|
16853
|
+
`handoff ${entry.source_agent_id} -> ${entry.target_agent_id}${newStatus ? ` (${newStatus})` : ""}`,
|
|
16854
|
+
SUMMARY_MAX_CHARS
|
|
16855
|
+
),
|
|
16856
|
+
size_hint: "minimal"
|
|
16857
|
+
});
|
|
16858
|
+
}
|
|
16859
|
+
return {
|
|
16860
|
+
handoff_entry_id: entry.entry_id,
|
|
16861
|
+
transferred,
|
|
16862
|
+
withheld,
|
|
16863
|
+
source: "heuristic",
|
|
16864
|
+
confidence: reasonClass || newStatus ? 0.5 : 0.3
|
|
16865
|
+
};
|
|
16866
|
+
}
|
|
16867
|
+
return {
|
|
16868
|
+
handoff_entry_id: entry.entry_id,
|
|
16869
|
+
transferred: [
|
|
16870
|
+
{
|
|
16871
|
+
category: "other",
|
|
16872
|
+
summary: truncate(
|
|
16873
|
+
`handoff ${entry.source_agent_id} -> ${entry.target_agent_id}`,
|
|
16874
|
+
SUMMARY_MAX_CHARS
|
|
16875
|
+
),
|
|
16876
|
+
size_hint: "minimal"
|
|
16877
|
+
}
|
|
16878
|
+
],
|
|
16879
|
+
withheld: [],
|
|
16880
|
+
source: "heuristic",
|
|
16881
|
+
confidence: 0.3
|
|
16882
|
+
};
|
|
16883
|
+
}
|
|
16884
|
+
async function tryLlmAssistPath(detail, selector) {
|
|
16885
|
+
const entry = detail.entry;
|
|
16886
|
+
const audit = detail.source_audit_entry;
|
|
16887
|
+
const probe = `event=${audit.operation} sender=${entry.source_agent_id} target=${entry.target_agent_id} summary=${entry.context_transfer_summary}`;
|
|
16888
|
+
try {
|
|
16889
|
+
const response = await selector.invokeClassify("sentinel-scoring", {
|
|
16890
|
+
kind: "classify",
|
|
16891
|
+
items: [probe],
|
|
16892
|
+
categories: [...CATEGORY_VALUES]
|
|
16893
|
+
});
|
|
16894
|
+
if (response.body.kind !== "classify") return null;
|
|
16895
|
+
const top = response.body.results[0];
|
|
16896
|
+
if (!top || !isCategory(top.category) || top.confidence < 0.4) {
|
|
16897
|
+
return null;
|
|
16898
|
+
}
|
|
16899
|
+
return {
|
|
16900
|
+
handoff_entry_id: entry.entry_id,
|
|
16901
|
+
transferred: [
|
|
16902
|
+
{
|
|
16903
|
+
category: top.category,
|
|
16904
|
+
summary: truncate(
|
|
16905
|
+
`LLM-classified handoff ${entry.source_agent_id} -> ${entry.target_agent_id}: ${top.category} (confidence ${top.confidence.toFixed(2)})`,
|
|
16906
|
+
SUMMARY_MAX_CHARS
|
|
16907
|
+
),
|
|
16908
|
+
size_hint: "minimal"
|
|
16909
|
+
}
|
|
16910
|
+
],
|
|
16911
|
+
withheld: [],
|
|
16912
|
+
source: "llm-assist",
|
|
16913
|
+
confidence: 0.6
|
|
16914
|
+
};
|
|
16915
|
+
} catch {
|
|
16916
|
+
return null;
|
|
16917
|
+
}
|
|
16918
|
+
}
|
|
16919
|
+
function sourceDetails(audit) {
|
|
16920
|
+
return audit.details;
|
|
16921
|
+
}
|
|
16922
|
+
function optString2(details, key) {
|
|
16923
|
+
if (!details) return null;
|
|
16924
|
+
const value = details[key];
|
|
16925
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
16926
|
+
return value;
|
|
16927
|
+
}
|
|
16928
|
+
function isCategory(value) {
|
|
16929
|
+
return CATEGORY_VALUES.includes(value);
|
|
16930
|
+
}
|
|
16931
|
+
function truncate(s, cap) {
|
|
16932
|
+
return s.length <= cap ? s : `${s.slice(0, cap - 3)}...`;
|
|
16933
|
+
}
|
|
16934
|
+
function parseExplicitContextItems(raw) {
|
|
16935
|
+
if (raw === null || raw === void 0) return [];
|
|
16936
|
+
if (Array.isArray(raw)) {
|
|
16937
|
+
const out = [];
|
|
16938
|
+
for (const entry of raw) {
|
|
16939
|
+
if (typeof entry === "string") {
|
|
16940
|
+
out.push({
|
|
16941
|
+
category: "other",
|
|
16942
|
+
summary: truncate(entry, SUMMARY_MAX_CHARS),
|
|
16943
|
+
size_hint: "minimal"
|
|
16944
|
+
});
|
|
16945
|
+
continue;
|
|
16946
|
+
}
|
|
16947
|
+
if (entry && typeof entry === "object") {
|
|
16948
|
+
const obj = entry;
|
|
16949
|
+
const category = isCategoryValue(obj["category"]) ? obj["category"] : "other";
|
|
16950
|
+
const summary = typeof obj["summary"] === "string" ? truncate(obj["summary"], SUMMARY_MAX_CHARS) : "(unspecified)";
|
|
16951
|
+
const sizeHint = isSizeHintValue(obj["size_hint"]) ? obj["size_hint"] : "minimal";
|
|
16952
|
+
out.push({ category, summary, size_hint: sizeHint });
|
|
16953
|
+
}
|
|
16954
|
+
}
|
|
16955
|
+
return out;
|
|
16956
|
+
}
|
|
16957
|
+
if (typeof raw === "object" && raw !== null) {
|
|
16958
|
+
const out = [];
|
|
16959
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
16960
|
+
const category = isCategoryValue(k) ? k : "other";
|
|
16961
|
+
if (Array.isArray(v)) {
|
|
16962
|
+
for (const item of v) {
|
|
16963
|
+
if (typeof item === "string") {
|
|
16964
|
+
out.push({
|
|
16965
|
+
category,
|
|
16966
|
+
summary: truncate(item, SUMMARY_MAX_CHARS),
|
|
16967
|
+
size_hint: "minimal"
|
|
16968
|
+
});
|
|
16969
|
+
}
|
|
16970
|
+
}
|
|
16971
|
+
}
|
|
16972
|
+
}
|
|
16973
|
+
return out;
|
|
16974
|
+
}
|
|
16975
|
+
return [];
|
|
16976
|
+
}
|
|
16977
|
+
function isCategoryValue(v) {
|
|
16978
|
+
return typeof v === "string" && CATEGORY_VALUES.includes(v);
|
|
16979
|
+
}
|
|
16980
|
+
function isSizeHintValue(v) {
|
|
16981
|
+
return typeof v === "string" && (v === "minimal" || v === "small" || v === "medium" || v === "large");
|
|
16982
|
+
}
|
|
16983
|
+
function categoryFromPolicyRuleId(ruleId) {
|
|
16984
|
+
const lower = ruleId.toLowerCase();
|
|
16985
|
+
if (lower.includes("credential") || lower.includes("broker_secret")) {
|
|
16986
|
+
return "credentials";
|
|
16987
|
+
}
|
|
16988
|
+
if (lower.includes("memory") || lower.includes("state_read")) {
|
|
16989
|
+
return "memory";
|
|
16990
|
+
}
|
|
16991
|
+
if (lower.includes("plan")) {
|
|
16992
|
+
return "plans";
|
|
16993
|
+
}
|
|
16994
|
+
if (lower.includes("export") || lower.includes("output")) {
|
|
16995
|
+
return "outputs";
|
|
16996
|
+
}
|
|
16997
|
+
if (lower.includes("audit")) {
|
|
16998
|
+
return "audit-refs";
|
|
16999
|
+
}
|
|
17000
|
+
return "other";
|
|
17001
|
+
}
|
|
17002
|
+
var CONTEXT_TRANSFER_AUDIT_OPS = {
|
|
17003
|
+
DECODED: "operator_handoff_context_transfer_decoded"
|
|
17004
|
+
};
|
|
17005
|
+
var HEURISTIC_WINDOW_MS = 5 * 60 * 1e3;
|
|
17006
|
+
var STALL_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
|
|
17007
|
+
var CYCLE_COMPLETION_MIN_HOPS = 2;
|
|
17008
|
+
function groupHandoffsIntoWorkflows(handoffs, opts) {
|
|
17009
|
+
if (handoffs.length === 0) return [];
|
|
17010
|
+
const now = opts?.now ?? /* @__PURE__ */ new Date();
|
|
17011
|
+
const linkedGroups = /* @__PURE__ */ new Map();
|
|
17012
|
+
const unlinked = [];
|
|
17013
|
+
for (const h of handoffs) {
|
|
17014
|
+
if (h.workflow_link !== null && h.workflow_link.length > 0) {
|
|
17015
|
+
let bucket = linkedGroups.get(h.workflow_link);
|
|
17016
|
+
if (!bucket) {
|
|
17017
|
+
bucket = [];
|
|
17018
|
+
linkedGroups.set(h.workflow_link, bucket);
|
|
17019
|
+
}
|
|
17020
|
+
bucket.push(h);
|
|
17021
|
+
} else {
|
|
17022
|
+
unlinked.push(h);
|
|
17023
|
+
}
|
|
17024
|
+
}
|
|
17025
|
+
const sortedUnlinked = [...unlinked].sort(
|
|
17026
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17027
|
+
);
|
|
17028
|
+
const heuristicChains = [];
|
|
17029
|
+
for (const h of sortedUnlinked) {
|
|
17030
|
+
const joinedIdx = findExtendableChain(heuristicChains, h);
|
|
17031
|
+
if (joinedIdx !== null) {
|
|
17032
|
+
heuristicChains[joinedIdx].push(h);
|
|
17033
|
+
} else {
|
|
17034
|
+
heuristicChains.push([h]);
|
|
17035
|
+
}
|
|
17036
|
+
}
|
|
17037
|
+
const workflows = [];
|
|
17038
|
+
for (const members of linkedGroups.values()) {
|
|
17039
|
+
workflows.push(materialize(members, now));
|
|
17040
|
+
}
|
|
17041
|
+
for (const members of heuristicChains) {
|
|
17042
|
+
workflows.push(materialize(members, now));
|
|
17043
|
+
}
|
|
17044
|
+
workflows.sort(
|
|
17045
|
+
(a, b) => a.last_activity_at < b.last_activity_at ? 1 : -1
|
|
17046
|
+
);
|
|
17047
|
+
return workflows;
|
|
17048
|
+
}
|
|
17049
|
+
function determineWorkflowState(members, now) {
|
|
17050
|
+
if (members.length === 0) return "unknown";
|
|
17051
|
+
const sorted = [...members].sort(
|
|
17052
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17053
|
+
);
|
|
17054
|
+
const last = sorted[sorted.length - 1];
|
|
17055
|
+
const root = sorted[0];
|
|
17056
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17057
|
+
if (!Number.isFinite(lastMs)) return "unknown";
|
|
17058
|
+
if (last.target_agent_id === OPERATOR_PSEUDO_AGENT) {
|
|
17059
|
+
return "completed";
|
|
17060
|
+
}
|
|
17061
|
+
if (sorted.length > CYCLE_COMPLETION_MIN_HOPS && last.target_agent_id === root.source_agent_id) {
|
|
17062
|
+
return "completed";
|
|
17063
|
+
}
|
|
17064
|
+
const ageMs = now.getTime() - lastMs;
|
|
17065
|
+
if (ageMs > STALL_THRESHOLD_MS) {
|
|
17066
|
+
return "stalled";
|
|
17067
|
+
}
|
|
17068
|
+
return "in_progress";
|
|
17069
|
+
}
|
|
17070
|
+
function workflowIdFromRoot(rootEntryId) {
|
|
17071
|
+
return crypto.createHash("sha256").update(`workflow:${rootEntryId}`).digest("hex").slice(0, 32);
|
|
17072
|
+
}
|
|
17073
|
+
function findExtendableChain(chains, h) {
|
|
17074
|
+
const hMs = Date.parse(h.observed_at);
|
|
17075
|
+
if (!Number.isFinite(hMs)) return null;
|
|
17076
|
+
let bestIdx = null;
|
|
17077
|
+
let bestGapMs = Number.POSITIVE_INFINITY;
|
|
17078
|
+
for (let i = 0; i < chains.length; i += 1) {
|
|
17079
|
+
const chain = chains[i];
|
|
17080
|
+
const last = chain[chain.length - 1];
|
|
17081
|
+
const lastMs = Date.parse(last.observed_at);
|
|
17082
|
+
if (!Number.isFinite(lastMs)) continue;
|
|
17083
|
+
const gapMs = Math.abs(hMs - lastMs);
|
|
17084
|
+
if (gapMs > HEURISTIC_WINDOW_MS) continue;
|
|
17085
|
+
if (!sharesAgent(last, h)) continue;
|
|
17086
|
+
if (gapMs < bestGapMs) {
|
|
17087
|
+
bestGapMs = gapMs;
|
|
17088
|
+
bestIdx = i;
|
|
17089
|
+
}
|
|
17090
|
+
}
|
|
17091
|
+
return bestIdx;
|
|
17092
|
+
}
|
|
17093
|
+
function sharesAgent(a, b) {
|
|
17094
|
+
return a.source_agent_id === b.source_agent_id || a.source_agent_id === b.target_agent_id || a.target_agent_id === b.source_agent_id || a.target_agent_id === b.target_agent_id;
|
|
17095
|
+
}
|
|
17096
|
+
function materialize(members, now) {
|
|
17097
|
+
const sorted = [...members].sort(
|
|
17098
|
+
(a, b) => a.observed_at < b.observed_at ? -1 : 1
|
|
17099
|
+
);
|
|
17100
|
+
const root = sorted[0];
|
|
17101
|
+
const last = sorted[sorted.length - 1];
|
|
17102
|
+
const involved = /* @__PURE__ */ new Set();
|
|
17103
|
+
for (const h of sorted) {
|
|
17104
|
+
if (h.source_agent_id) involved.add(h.source_agent_id);
|
|
17105
|
+
if (h.target_agent_id) involved.add(h.target_agent_id);
|
|
17106
|
+
}
|
|
17107
|
+
return {
|
|
17108
|
+
workflow_id: workflowIdFromRoot(root.entry_id),
|
|
17109
|
+
root_handoff: root,
|
|
17110
|
+
member_handoffs: sorted,
|
|
17111
|
+
state: determineWorkflowState(sorted, now),
|
|
17112
|
+
started_at: root.observed_at,
|
|
17113
|
+
last_activity_at: last.observed_at,
|
|
17114
|
+
involved_agents: [...involved].sort()
|
|
17115
|
+
};
|
|
17116
|
+
}
|
|
17117
|
+
|
|
16714
17118
|
// src/coordination/handoff-routes.ts
|
|
16715
17119
|
var COORDINATION_API_PREFIX = "/api/coordination";
|
|
16716
17120
|
var COORDINATION_HANDOFFS_PREFIX = "/api/coordination/handoffs";
|
|
17121
|
+
var COORDINATION_WORKFLOWS_PREFIX = "/api/coordination/workflows";
|
|
16717
17122
|
var COORDINATION_LIST_DEFAULT_LIMIT = 50;
|
|
16718
17123
|
var COORDINATION_LIST_MAX_LIMIT = 500;
|
|
16719
17124
|
var HandoffEventBridge = class {
|
|
@@ -16752,6 +17157,101 @@ function matchEntryRoute2(path) {
|
|
|
16752
17157
|
if (rest.includes("/")) return null;
|
|
16753
17158
|
return { entryId: decodeURIComponent(rest) };
|
|
16754
17159
|
}
|
|
17160
|
+
function matchWorkflowRoute(path) {
|
|
17161
|
+
const prefix = `${COORDINATION_WORKFLOWS_PREFIX}/`;
|
|
17162
|
+
if (!path.startsWith(prefix)) return null;
|
|
17163
|
+
const rest = path.slice(prefix.length);
|
|
17164
|
+
if (rest.length === 0 || rest === "stream") return null;
|
|
17165
|
+
if (rest.includes("/")) return null;
|
|
17166
|
+
return { workflowId: decodeURIComponent(rest) };
|
|
17167
|
+
}
|
|
17168
|
+
async function computeWorkflowsAndTrackTransitions(deps) {
|
|
17169
|
+
const handoffs = await deps.handoffLog.query({ limit: 500 });
|
|
17170
|
+
const workflows = groupHandoffsIntoWorkflows(handoffs, {
|
|
17171
|
+
...deps.now !== void 0 ? { now: deps.now() } : {}
|
|
17172
|
+
});
|
|
17173
|
+
const transitions = deps.workflowStateTracker ? deps.workflowStateTracker.observe(workflows) : [];
|
|
17174
|
+
for (const change of transitions) {
|
|
17175
|
+
deps.auditLog.append(
|
|
17176
|
+
"l2",
|
|
17177
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_STATE_CHANGED,
|
|
17178
|
+
deps.operatorId,
|
|
17179
|
+
{
|
|
17180
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17181
|
+
workflow_id: change.workflow_id,
|
|
17182
|
+
previous_state: change.previous_state,
|
|
17183
|
+
new_state: change.new_state
|
|
17184
|
+
}
|
|
17185
|
+
);
|
|
17186
|
+
}
|
|
17187
|
+
return { workflows, transitions };
|
|
17188
|
+
}
|
|
17189
|
+
function filterWorkflowList(workflows, opts) {
|
|
17190
|
+
let filtered = workflows;
|
|
17191
|
+
if (opts.state) {
|
|
17192
|
+
filtered = filtered.filter((w) => w.state === opts.state);
|
|
17193
|
+
}
|
|
17194
|
+
if (opts.agentId) {
|
|
17195
|
+
filtered = filtered.filter((w) => w.involved_agents.includes(opts.agentId));
|
|
17196
|
+
}
|
|
17197
|
+
if (opts.since) {
|
|
17198
|
+
filtered = filtered.filter((w) => w.last_activity_at >= opts.since);
|
|
17199
|
+
}
|
|
17200
|
+
return filtered.slice(0, opts.limit);
|
|
17201
|
+
}
|
|
17202
|
+
function isWorkflowState(value) {
|
|
17203
|
+
return value === "in_progress" || value === "completed" || value === "stalled" || value === "unknown";
|
|
17204
|
+
}
|
|
17205
|
+
async function handleWorkflowStream(deps, res) {
|
|
17206
|
+
res.writeHead(200, {
|
|
17207
|
+
"Content-Type": "text/event-stream",
|
|
17208
|
+
"Cache-Control": "no-cache, no-transform",
|
|
17209
|
+
Connection: "keep-alive",
|
|
17210
|
+
"X-Accel-Buffering": "no"
|
|
17211
|
+
});
|
|
17212
|
+
const initial = await computeWorkflowsAndTrackTransitions(deps);
|
|
17213
|
+
res.write(
|
|
17214
|
+
`event: workflow_snapshot
|
|
17215
|
+
data: ${JSON.stringify({ workflows: initial.workflows })}
|
|
17216
|
+
|
|
17217
|
+
`
|
|
17218
|
+
);
|
|
17219
|
+
if (initial.transitions.length > 0) {
|
|
17220
|
+
res.write(
|
|
17221
|
+
`event: workflow_state_changed
|
|
17222
|
+
data: ${JSON.stringify({ transitions: initial.transitions })}
|
|
17223
|
+
|
|
17224
|
+
`
|
|
17225
|
+
);
|
|
17226
|
+
}
|
|
17227
|
+
const unsubscribe = deps.events.subscribe(() => {
|
|
17228
|
+
void (async () => {
|
|
17229
|
+
try {
|
|
17230
|
+
const tick = await computeWorkflowsAndTrackTransitions(deps);
|
|
17231
|
+
res.write(
|
|
17232
|
+
`event: workflow_snapshot
|
|
17233
|
+
data: ${JSON.stringify({ workflows: tick.workflows })}
|
|
17234
|
+
|
|
17235
|
+
`
|
|
17236
|
+
);
|
|
17237
|
+
if (tick.transitions.length > 0) {
|
|
17238
|
+
res.write(
|
|
17239
|
+
`event: workflow_state_changed
|
|
17240
|
+
data: ${JSON.stringify({ transitions: tick.transitions })}
|
|
17241
|
+
|
|
17242
|
+
`
|
|
17243
|
+
);
|
|
17244
|
+
}
|
|
17245
|
+
} catch {
|
|
17246
|
+
}
|
|
17247
|
+
})();
|
|
17248
|
+
});
|
|
17249
|
+
const cleanup = () => {
|
|
17250
|
+
unsubscribe();
|
|
17251
|
+
};
|
|
17252
|
+
res.on("close", cleanup);
|
|
17253
|
+
res.on("error", cleanup);
|
|
17254
|
+
}
|
|
16755
17255
|
async function handleStream3(deps, res) {
|
|
16756
17256
|
res.writeHead(200, {
|
|
16757
17257
|
"Content-Type": "text/event-stream",
|
|
@@ -16835,6 +17335,67 @@ async function handleCoordinationRoute(deps, req, res) {
|
|
|
16835
17335
|
writeJSON6(res, 200, { ok: true, data: { entries } });
|
|
16836
17336
|
return true;
|
|
16837
17337
|
}
|
|
17338
|
+
if (method === "GET" && path === `${COORDINATION_WORKFLOWS_PREFIX}/stream`) {
|
|
17339
|
+
await handleWorkflowStream(deps, res);
|
|
17340
|
+
return true;
|
|
17341
|
+
}
|
|
17342
|
+
if (method === "GET" && path === COORDINATION_WORKFLOWS_PREFIX) {
|
|
17343
|
+
const limit = parseLimit4(
|
|
17344
|
+
url.searchParams.get("limit"),
|
|
17345
|
+
COORDINATION_LIST_DEFAULT_LIMIT,
|
|
17346
|
+
COORDINATION_LIST_MAX_LIMIT
|
|
17347
|
+
);
|
|
17348
|
+
const rawState = url.searchParams.get("state");
|
|
17349
|
+
const state = rawState && isWorkflowState(rawState) ? rawState : void 0;
|
|
17350
|
+
const since = url.searchParams.get("since") ?? void 0;
|
|
17351
|
+
const agentId = url.searchParams.get("agent_id") ?? void 0;
|
|
17352
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17353
|
+
const filtered = filterWorkflowList(computed.workflows, {
|
|
17354
|
+
...state !== void 0 ? { state } : {},
|
|
17355
|
+
...agentId !== void 0 ? { agentId } : {},
|
|
17356
|
+
...since !== void 0 ? { since } : {},
|
|
17357
|
+
limit
|
|
17358
|
+
});
|
|
17359
|
+
deps.auditLog.append(
|
|
17360
|
+
"l2",
|
|
17361
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_VIEW_OPENED,
|
|
17362
|
+
deps.operatorId,
|
|
17363
|
+
{
|
|
17364
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17365
|
+
result_count: filtered.length,
|
|
17366
|
+
...state !== void 0 ? { state } : {},
|
|
17367
|
+
...agentId !== void 0 ? { agent_id: agentId } : {},
|
|
17368
|
+
...since !== void 0 ? { since } : {}
|
|
17369
|
+
}
|
|
17370
|
+
);
|
|
17371
|
+
writeJSON6(res, 200, { ok: true, data: { workflows: filtered } });
|
|
17372
|
+
return true;
|
|
17373
|
+
}
|
|
17374
|
+
const workflowMatch = matchWorkflowRoute(path);
|
|
17375
|
+
if (method === "GET" && workflowMatch) {
|
|
17376
|
+
const computed = await computeWorkflowsAndTrackTransitions(deps);
|
|
17377
|
+
const wf = computed.workflows.find(
|
|
17378
|
+
(w) => w.workflow_id === workflowMatch.workflowId
|
|
17379
|
+
);
|
|
17380
|
+
if (!wf) {
|
|
17381
|
+
writeJSON6(res, 404, { ok: false, error: "not_found" });
|
|
17382
|
+
return true;
|
|
17383
|
+
}
|
|
17384
|
+
deps.auditLog.append(
|
|
17385
|
+
"l2",
|
|
17386
|
+
COORDINATION_VIEW_AUDIT_OPS.WORKFLOW_DRILLED,
|
|
17387
|
+
deps.operatorId,
|
|
17388
|
+
{
|
|
17389
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17390
|
+
workflow_id: wf.workflow_id,
|
|
17391
|
+
state: wf.state,
|
|
17392
|
+
member_count: wf.member_handoffs.length,
|
|
17393
|
+
involved_agent_count: wf.involved_agents.length
|
|
17394
|
+
}
|
|
17395
|
+
);
|
|
17396
|
+
writeJSON6(res, 200, { ok: true, data: { workflow: wf } });
|
|
17397
|
+
return true;
|
|
17398
|
+
}
|
|
16838
17399
|
const entryMatch = matchEntryRoute2(path);
|
|
16839
17400
|
if (method === "GET" && entryMatch) {
|
|
16840
17401
|
const detail = await deps.handoffLog.getEntry(entryMatch.entryId);
|
|
@@ -16854,7 +17415,29 @@ async function handleCoordinationRoute(deps, req, res) {
|
|
|
16854
17415
|
target_agent_id: detail.entry.target_agent_id
|
|
16855
17416
|
}
|
|
16856
17417
|
);
|
|
16857
|
-
|
|
17418
|
+
let breakdown = null;
|
|
17419
|
+
try {
|
|
17420
|
+
breakdown = await extractContextTransferBreakdown(
|
|
17421
|
+
detail,
|
|
17422
|
+
deps.contextTransfer ?? {}
|
|
17423
|
+
);
|
|
17424
|
+
deps.auditLog.append(
|
|
17425
|
+
"l2",
|
|
17426
|
+
CONTEXT_TRANSFER_AUDIT_OPS.DECODED,
|
|
17427
|
+
deps.operatorId,
|
|
17428
|
+
{
|
|
17429
|
+
fortress_id: deps.handoffLog.getFortressId(),
|
|
17430
|
+
entry_id: detail.entry.entry_id,
|
|
17431
|
+
extractor_path: breakdown.source,
|
|
17432
|
+
confidence: breakdown.confidence,
|
|
17433
|
+
transferred_count: breakdown.transferred.length,
|
|
17434
|
+
withheld_count: breakdown.withheld.length
|
|
17435
|
+
}
|
|
17436
|
+
);
|
|
17437
|
+
} catch {
|
|
17438
|
+
}
|
|
17439
|
+
const responseData = breakdown !== null ? { ...detail, context_transfer_breakdown: breakdown } : detail;
|
|
17440
|
+
writeJSON6(res, 200, { ok: true, data: responseData });
|
|
16858
17441
|
return true;
|
|
16859
17442
|
}
|
|
16860
17443
|
writeJSON6(res, 404, { ok: false, error: "not_found", path });
|
|
@@ -16954,6 +17537,8 @@ var DashboardApprovalChannel = class {
|
|
|
16954
17537
|
*/
|
|
16955
17538
|
handoffLog = null;
|
|
16956
17539
|
handoffEventBridge = null;
|
|
17540
|
+
handoffContextTransfer = null;
|
|
17541
|
+
workflowStateTracker = null;
|
|
16957
17542
|
handoffAuditLog = null;
|
|
16958
17543
|
handoffOperatorId = null;
|
|
16959
17544
|
constructor(config) {
|
|
@@ -17034,6 +17619,8 @@ var DashboardApprovalChannel = class {
|
|
|
17034
17619
|
this.handoffEventBridge = opts.eventBridge ?? null;
|
|
17035
17620
|
this.handoffAuditLog = opts.auditLog ?? null;
|
|
17036
17621
|
this.handoffOperatorId = opts.operatorId ?? null;
|
|
17622
|
+
this.handoffContextTransfer = opts.contextTransfer ?? null;
|
|
17623
|
+
this.workflowStateTracker = opts.workflowStateTracker ?? null;
|
|
17037
17624
|
}
|
|
17038
17625
|
/**
|
|
17039
17626
|
* v1.3 WP-V1.3-10 dispatch entry point. Called from `handleRequest`
|
|
@@ -17091,7 +17678,9 @@ var DashboardApprovalChannel = class {
|
|
|
17091
17678
|
handoffLog: this.handoffLog,
|
|
17092
17679
|
auditLog: this.handoffAuditLog,
|
|
17093
17680
|
operatorId: this.handoffOperatorId ?? this.identityManager?.getPrimaryIdentityId() ?? "operator_dashboard",
|
|
17094
|
-
events: this.handoffEventBridge
|
|
17681
|
+
events: this.handoffEventBridge,
|
|
17682
|
+
...this.handoffContextTransfer !== null ? { contextTransfer: this.handoffContextTransfer } : {},
|
|
17683
|
+
...this.workflowStateTracker !== null ? { workflowStateTracker: this.workflowStateTracker } : {}
|
|
17095
17684
|
},
|
|
17096
17685
|
req,
|
|
17097
17686
|
res
|
|
@@ -21311,13 +21900,33 @@ var SentinelDispatcher = class {
|
|
|
21311
21900
|
}
|
|
21312
21901
|
}
|
|
21313
21902
|
};
|
|
21903
|
+
|
|
21904
|
+
// src/anomaly-detection/classifier-state-store.ts
|
|
21905
|
+
init_encryption();
|
|
21906
|
+
init_encoding();
|
|
21907
|
+
|
|
21908
|
+
// src/anomaly-detection/classifiers/cusum.ts
|
|
21909
|
+
var CUSUM_CLASSIFIER_ID = "cusum";
|
|
21910
|
+
|
|
21911
|
+
// src/anomaly-detection/classifiers/psi.ts
|
|
21912
|
+
var PSI_CLASSIFIER_ID = "psi";
|
|
21913
|
+
|
|
21914
|
+
// src/anomaly-detection/anomaly-pipeline.ts
|
|
21314
21915
|
var ANOMALY_AUDIT_OPS = {
|
|
21315
21916
|
DETECTOR_REGISTERED: "anomaly_detector_registered",
|
|
21316
21917
|
DETECTOR_UNREGISTERED: "anomaly_detector_unregistered",
|
|
21317
21918
|
FINDING_EMITTED: "anomaly_finding_emitted",
|
|
21318
21919
|
EVALUATION_FAILED: "anomaly_evaluation_failed",
|
|
21319
21920
|
TRAINING_COMPLETED: "anomaly_training_completed",
|
|
21320
|
-
TRAINING_FAILED: "anomaly_training_failed"
|
|
21921
|
+
TRAINING_FAILED: "anomaly_training_failed",
|
|
21922
|
+
/** Chi-2: a classifier was attached to an existing detector. */
|
|
21923
|
+
CLASSIFIER_SUBSCRIBED: "anomaly_classifier_subscribed",
|
|
21924
|
+
/** Chi-2: a classifier was detached from an existing detector. */
|
|
21925
|
+
CLASSIFIER_UNSUBSCRIBED: "anomaly_classifier_unsubscribed",
|
|
21926
|
+
/** Chi-2: CUSUM-flagged mean-shift drift on a per-agent feature. */
|
|
21927
|
+
CUSUM_DRIFT_DETECTED: "anomaly_cusum_drift_detected",
|
|
21928
|
+
/** Chi-2: PSI-flagged distribution shift on a per-agent feature. */
|
|
21929
|
+
PSI_DISTRIBUTION_SHIFT_DETECTED: "anomaly_psi_distribution_shift_detected"
|
|
21321
21930
|
};
|
|
21322
21931
|
var DEFAULT_TICK_INTERVAL_MS2 = 6e4;
|
|
21323
21932
|
var AnomalyPipelineDispatcher = class {
|
|
@@ -21408,34 +22017,37 @@ var AnomalyPipelineDispatcher = class {
|
|
|
21408
22017
|
const stamped = await this.routeFinding(detectorId, raw);
|
|
21409
22018
|
findings.push(stamped);
|
|
21410
22019
|
}
|
|
21411
|
-
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
|
|
21416
|
-
|
|
21417
|
-
|
|
21418
|
-
|
|
21419
|
-
|
|
21420
|
-
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21432
|
-
|
|
21433
|
-
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
|
|
22020
|
+
for (const classifier of detector.getAllClassifiers()) {
|
|
22021
|
+
try {
|
|
22022
|
+
const trainingResult = await classifier.train();
|
|
22023
|
+
this.auditLog.append(
|
|
22024
|
+
"l2",
|
|
22025
|
+
ANOMALY_AUDIT_OPS.TRAINING_COMPLETED,
|
|
22026
|
+
this.identityId,
|
|
22027
|
+
{
|
|
22028
|
+
detector_id: detectorId,
|
|
22029
|
+
classifier_id: classifier.classifierId,
|
|
22030
|
+
trained_at: trainingResult.trained_at,
|
|
22031
|
+
sample_count: trainingResult.sample_count,
|
|
22032
|
+
agent_count: trainingResult.agent_count,
|
|
22033
|
+
fortress_id: this.fortressId
|
|
22034
|
+
}
|
|
22035
|
+
);
|
|
22036
|
+
} catch (trainErr) {
|
|
22037
|
+
const message = trainErr instanceof Error ? trainErr.message : String(trainErr);
|
|
22038
|
+
this.auditLog.append(
|
|
22039
|
+
"l2",
|
|
22040
|
+
ANOMALY_AUDIT_OPS.TRAINING_FAILED,
|
|
22041
|
+
this.identityId,
|
|
22042
|
+
{
|
|
22043
|
+
detector_id: detectorId,
|
|
22044
|
+
classifier_id: classifier.classifierId,
|
|
22045
|
+
error_message: message,
|
|
22046
|
+
fortress_id: this.fortressId
|
|
22047
|
+
},
|
|
22048
|
+
"failure"
|
|
22049
|
+
);
|
|
22050
|
+
}
|
|
21439
22051
|
}
|
|
21440
22052
|
} catch (err) {
|
|
21441
22053
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -21498,6 +22110,7 @@ var AnomalyPipelineDispatcher = class {
|
|
|
21498
22110
|
observed_at: raw.observed_at || this.now().toISOString()
|
|
21499
22111
|
};
|
|
21500
22112
|
await this.findingStore.saveFinding(stamped);
|
|
22113
|
+
const classifierId = stamped.details["classifier_id"] ?? null;
|
|
21501
22114
|
this.auditLog.append(
|
|
21502
22115
|
"l2",
|
|
21503
22116
|
ANOMALY_AUDIT_OPS.FINDING_EMITTED,
|
|
@@ -21507,13 +22120,79 @@ var AnomalyPipelineDispatcher = class {
|
|
|
21507
22120
|
finding_id: stamped.finding_id,
|
|
21508
22121
|
severity: stamped.severity,
|
|
21509
22122
|
anomaly_score: stamped.details["anomaly_score"] ?? null,
|
|
22123
|
+
...classifierId !== null ? { classifier_id: classifierId } : {},
|
|
21510
22124
|
...stamped.agent_id !== void 0 ? { agent_id: stamped.agent_id } : {},
|
|
21511
22125
|
fortress_id: this.fortressId
|
|
21512
22126
|
}
|
|
21513
22127
|
);
|
|
22128
|
+
const specificOp = classifierSpecificAuditOp(classifierId);
|
|
22129
|
+
if (specificOp !== null) {
|
|
22130
|
+
this.auditLog.append("l2", specificOp, this.identityId, {
|
|
22131
|
+
detector_id: detectorId,
|
|
22132
|
+
finding_id: stamped.finding_id,
|
|
22133
|
+
severity: stamped.severity,
|
|
22134
|
+
anomaly_score: stamped.details["anomaly_score"] ?? null,
|
|
22135
|
+
...stamped.agent_id !== void 0 ? { agent_id: stamped.agent_id } : {},
|
|
22136
|
+
fortress_id: this.fortressId
|
|
22137
|
+
});
|
|
22138
|
+
}
|
|
21514
22139
|
this.emit({ type: "finding", finding: stamped });
|
|
21515
22140
|
return stamped;
|
|
21516
22141
|
}
|
|
22142
|
+
/**
|
|
22143
|
+
* Chi-2: attach an additional classifier to an already-registered
|
|
22144
|
+
* detector. Emits ANOMALY_CLASSIFIER_SUBSCRIBED on success. The
|
|
22145
|
+
* factory is called with the fortress AnomalyContext so the
|
|
22146
|
+
* classifier can build its own state-store binding. Idempotent: a
|
|
22147
|
+
* second call with the same classifierId returns false.
|
|
22148
|
+
*/
|
|
22149
|
+
async addClassifierToDetector(detectorId, factory) {
|
|
22150
|
+
const detector = this.detectors.get(detectorId);
|
|
22151
|
+
if (!detector) return false;
|
|
22152
|
+
const context = {
|
|
22153
|
+
fortressId: this.fortressId,
|
|
22154
|
+
auditLog: this.auditLog,
|
|
22155
|
+
storage: this.storage,
|
|
22156
|
+
masterKey: this.masterKey,
|
|
22157
|
+
now: this.now
|
|
22158
|
+
};
|
|
22159
|
+
const classifier = factory(context);
|
|
22160
|
+
const added = detector.addClassifier(classifier);
|
|
22161
|
+
if (!added) return false;
|
|
22162
|
+
this.auditLog.append(
|
|
22163
|
+
"l2",
|
|
22164
|
+
ANOMALY_AUDIT_OPS.CLASSIFIER_SUBSCRIBED,
|
|
22165
|
+
this.identityId,
|
|
22166
|
+
{
|
|
22167
|
+
detector_id: detectorId,
|
|
22168
|
+
classifier_id: classifier.classifierId,
|
|
22169
|
+
fortress_id: this.fortressId
|
|
22170
|
+
}
|
|
22171
|
+
);
|
|
22172
|
+
return true;
|
|
22173
|
+
}
|
|
22174
|
+
/**
|
|
22175
|
+
* Chi-2: detach an additional classifier from an already-registered
|
|
22176
|
+
* detector. Emits ANOMALY_CLASSIFIER_UNSUBSCRIBED on success. The
|
|
22177
|
+
* primary classifier cannot be detached (returns false).
|
|
22178
|
+
*/
|
|
22179
|
+
async removeClassifierFromDetector(detectorId, classifierId) {
|
|
22180
|
+
const detector = this.detectors.get(detectorId);
|
|
22181
|
+
if (!detector) return false;
|
|
22182
|
+
const removed = detector.removeClassifier(classifierId);
|
|
22183
|
+
if (!removed) return false;
|
|
22184
|
+
this.auditLog.append(
|
|
22185
|
+
"l2",
|
|
22186
|
+
ANOMALY_AUDIT_OPS.CLASSIFIER_UNSUBSCRIBED,
|
|
22187
|
+
this.identityId,
|
|
22188
|
+
{
|
|
22189
|
+
detector_id: detectorId,
|
|
22190
|
+
classifier_id: classifierId,
|
|
22191
|
+
fortress_id: this.fortressId
|
|
22192
|
+
}
|
|
22193
|
+
);
|
|
22194
|
+
return true;
|
|
22195
|
+
}
|
|
21517
22196
|
emit(event) {
|
|
21518
22197
|
for (const listener of this.listeners) {
|
|
21519
22198
|
try {
|
|
@@ -21523,6 +22202,80 @@ var AnomalyPipelineDispatcher = class {
|
|
|
21523
22202
|
}
|
|
21524
22203
|
}
|
|
21525
22204
|
};
|
|
22205
|
+
function classifierSpecificAuditOp(classifierId) {
|
|
22206
|
+
if (classifierId === null) return null;
|
|
22207
|
+
if (classifierId === CUSUM_CLASSIFIER_ID) {
|
|
22208
|
+
return ANOMALY_AUDIT_OPS.CUSUM_DRIFT_DETECTED;
|
|
22209
|
+
}
|
|
22210
|
+
if (classifierId === PSI_CLASSIFIER_ID) {
|
|
22211
|
+
return ANOMALY_AUDIT_OPS.PSI_DISTRIBUTION_SHIFT_DETECTED;
|
|
22212
|
+
}
|
|
22213
|
+
return null;
|
|
22214
|
+
}
|
|
22215
|
+
|
|
22216
|
+
// src/coordination/workflow-state-tracker.ts
|
|
22217
|
+
var WorkflowStateTracker = class {
|
|
22218
|
+
states = /* @__PURE__ */ new Map();
|
|
22219
|
+
now;
|
|
22220
|
+
constructor(opts) {
|
|
22221
|
+
this.now = opts?.now ?? (() => /* @__PURE__ */ new Date());
|
|
22222
|
+
}
|
|
22223
|
+
/**
|
|
22224
|
+
* Diff the supplied workflow list against the last-observed states.
|
|
22225
|
+
* Returns the set of transitions detected this call; the tracker
|
|
22226
|
+
* mutates its internal map to reflect the new states.
|
|
22227
|
+
*
|
|
22228
|
+
* Transitions emitted:
|
|
22229
|
+
* - First observation of a workflow (`previous_state` is the
|
|
22230
|
+
* sentinel `unobserved`). Lets the route handler audit-emit
|
|
22231
|
+
* the initial state so the operator sees workflows as they
|
|
22232
|
+
* surface, not only when they change.
|
|
22233
|
+
* - Subsequent observation where `previous_state !== new_state`.
|
|
22234
|
+
*/
|
|
22235
|
+
observe(workflows) {
|
|
22236
|
+
const out = [];
|
|
22237
|
+
const observedAt = this.now().toISOString();
|
|
22238
|
+
for (const wf of workflows) {
|
|
22239
|
+
const prior = this.states.get(wf.workflow_id);
|
|
22240
|
+
if (prior === void 0) {
|
|
22241
|
+
out.push({
|
|
22242
|
+
workflow_id: wf.workflow_id,
|
|
22243
|
+
previous_state: "unobserved",
|
|
22244
|
+
new_state: wf.state,
|
|
22245
|
+
observed_at: observedAt
|
|
22246
|
+
});
|
|
22247
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22248
|
+
continue;
|
|
22249
|
+
}
|
|
22250
|
+
if (prior !== wf.state) {
|
|
22251
|
+
out.push({
|
|
22252
|
+
workflow_id: wf.workflow_id,
|
|
22253
|
+
previous_state: prior,
|
|
22254
|
+
new_state: wf.state,
|
|
22255
|
+
observed_at: observedAt
|
|
22256
|
+
});
|
|
22257
|
+
this.states.set(wf.workflow_id, wf.state);
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
return out;
|
|
22261
|
+
}
|
|
22262
|
+
/**
|
|
22263
|
+
* Drop a workflow's recorded state. Surfaced for tests + future
|
|
22264
|
+
* "operator dismissed this workflow" affordance; not currently
|
|
22265
|
+
* called by the production wiring.
|
|
22266
|
+
*/
|
|
22267
|
+
forget(workflowId) {
|
|
22268
|
+
this.states.delete(workflowId);
|
|
22269
|
+
}
|
|
22270
|
+
/** Reset the tracker. Tests use this between runs. */
|
|
22271
|
+
reset() {
|
|
22272
|
+
this.states.clear();
|
|
22273
|
+
}
|
|
22274
|
+
/** Read-only view of the current snapshot. Useful for diagnostics. */
|
|
22275
|
+
snapshot() {
|
|
22276
|
+
return new Map(this.states);
|
|
22277
|
+
}
|
|
22278
|
+
};
|
|
21526
22279
|
|
|
21527
22280
|
// src/sentinel/sentinel.ts
|
|
21528
22281
|
var Sentinel = class {
|
|
@@ -24757,7 +25510,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
24757
25510
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24758
25511
|
const canonicalBytes = canonicalize2(outcome);
|
|
24759
25512
|
const canonicalString = new TextDecoder().decode(canonicalBytes);
|
|
24760
|
-
const
|
|
25513
|
+
const sha25612 = createCommitment(canonicalString);
|
|
24761
25514
|
let pedersenData;
|
|
24762
25515
|
if (includePedersen && Number.isInteger(outcome.rounds) && outcome.rounds >= 0) {
|
|
24763
25516
|
const pedersen = createPedersenCommitment(outcome.rounds);
|
|
@@ -24769,7 +25522,7 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
24769
25522
|
const commitmentPayload = {
|
|
24770
25523
|
bridge_commitment_id: commitmentId,
|
|
24771
25524
|
session_id: outcome.session_id,
|
|
24772
|
-
sha256_commitment:
|
|
25525
|
+
sha256_commitment: sha25612.commitment,
|
|
24773
25526
|
terms_hash: outcome.terms_hash,
|
|
24774
25527
|
committer_did: identity.did,
|
|
24775
25528
|
committed_at: now,
|
|
@@ -24780,8 +25533,8 @@ function createBridgeCommitment(outcome, identity, identityEncryptionKey, includ
|
|
|
24780
25533
|
return {
|
|
24781
25534
|
bridge_commitment_id: commitmentId,
|
|
24782
25535
|
session_id: outcome.session_id,
|
|
24783
|
-
sha256_commitment:
|
|
24784
|
-
blinding_factor:
|
|
25536
|
+
sha256_commitment: sha25612.commitment,
|
|
25537
|
+
blinding_factor: sha25612.blinding_factor,
|
|
24785
25538
|
committer_did: identity.did,
|
|
24786
25539
|
signature: toBase64url(signature),
|
|
24787
25540
|
pedersen_commitment: pedersenData,
|
|
@@ -38202,6 +38955,136 @@ function tryParseClassification3(text) {
|
|
|
38202
38955
|
}
|
|
38203
38956
|
}
|
|
38204
38957
|
|
|
38958
|
+
// src/query-anonymity/header-strip.ts
|
|
38959
|
+
var QUERY_ANONYMITY_AUDIT_OPS = {
|
|
38960
|
+
HEADERS_STRIPPED: "query_anonymity_headers_stripped"
|
|
38961
|
+
};
|
|
38962
|
+
var CANONICAL_STRIP_LIST = [
|
|
38963
|
+
// Browser / runtime fingerprinting.
|
|
38964
|
+
{ name: "user-agent", reason: "user-agent" },
|
|
38965
|
+
{ name: "sec-ch-ua", reason: "fingerprintable-extension" },
|
|
38966
|
+
{ name: "sec-ch-ua-mobile", reason: "fingerprintable-extension" },
|
|
38967
|
+
{ name: "sec-ch-ua-platform", reason: "fingerprintable-extension" },
|
|
38968
|
+
{ name: "sec-ch-ua-platform-version", reason: "fingerprintable-extension" },
|
|
38969
|
+
{ name: "sec-ch-ua-arch", reason: "fingerprintable-extension" },
|
|
38970
|
+
{ name: "sec-ch-ua-bitness", reason: "fingerprintable-extension" },
|
|
38971
|
+
{ name: "sec-ch-ua-model", reason: "fingerprintable-extension" },
|
|
38972
|
+
{ name: "sec-ch-ua-full-version-list", reason: "fingerprintable-extension" },
|
|
38973
|
+
// Locale fingerprint.
|
|
38974
|
+
{ name: "accept-language", reason: "locale-fingerprint" },
|
|
38975
|
+
// Request-origin leak.
|
|
38976
|
+
{ name: "referer", reason: "leaking-network-info" },
|
|
38977
|
+
{ name: "referrer-policy", reason: "leaking-network-info" },
|
|
38978
|
+
{ name: "origin", reason: "leaking-network-info" },
|
|
38979
|
+
// Forwarded-by / IP-derived network info.
|
|
38980
|
+
{ name: "via", reason: "leaking-network-info" },
|
|
38981
|
+
{ name: "forwarded", reason: "leaking-network-info" },
|
|
38982
|
+
{ name: "x-forwarded-for", reason: "leaking-network-info" },
|
|
38983
|
+
{ name: "x-real-ip", reason: "leaking-network-info" },
|
|
38984
|
+
{ name: "x-client-ip", reason: "leaking-network-info" },
|
|
38985
|
+
// DNT / GPC are technically anti-tracking signals but they
|
|
38986
|
+
// themselves form a fingerprint (operators who set DNT=1 are a
|
|
38987
|
+
// smaller subset). Strip to keep the substrate ignorant of
|
|
38988
|
+
// operator preferences.
|
|
38989
|
+
{ name: "dnt", reason: "unnecessary-metadata" },
|
|
38990
|
+
{ name: "sec-gpc", reason: "unnecessary-metadata" }
|
|
38991
|
+
];
|
|
38992
|
+
var REQUIRED_HEADERS = [
|
|
38993
|
+
"authorization",
|
|
38994
|
+
"content-type",
|
|
38995
|
+
"content-length",
|
|
38996
|
+
"host",
|
|
38997
|
+
"accept",
|
|
38998
|
+
"x-api-key",
|
|
38999
|
+
// Anthropic API auth
|
|
39000
|
+
"anthropic-version",
|
|
39001
|
+
// Anthropic API contract version
|
|
39002
|
+
"anthropic-beta",
|
|
39003
|
+
// optional Anthropic beta opt-in
|
|
39004
|
+
"openai-organization",
|
|
39005
|
+
// optional OpenAI org id
|
|
39006
|
+
"x-stainless-package-version",
|
|
39007
|
+
// allowed for Anthropic + OpenAI SDK contract compat
|
|
39008
|
+
"x-goog-api-key",
|
|
39009
|
+
// Google AI Studio
|
|
39010
|
+
"x-goog-user-project"
|
|
39011
|
+
// Google AI Studio
|
|
39012
|
+
];
|
|
39013
|
+
var REQUIRED_HEADER_SET = new Set(
|
|
39014
|
+
REQUIRED_HEADERS.map((h) => h.toLowerCase())
|
|
39015
|
+
);
|
|
39016
|
+
var STRIP_REASON_BY_NAME = new Map(
|
|
39017
|
+
CANONICAL_STRIP_LIST.map((h) => [h.name.toLowerCase(), h.reason])
|
|
39018
|
+
);
|
|
39019
|
+
function stripHeaders(headers) {
|
|
39020
|
+
const stripped = {};
|
|
39021
|
+
const removed = [];
|
|
39022
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
39023
|
+
const lower = name.toLowerCase();
|
|
39024
|
+
if (REQUIRED_HEADER_SET.has(lower)) {
|
|
39025
|
+
stripped[name] = value;
|
|
39026
|
+
continue;
|
|
39027
|
+
}
|
|
39028
|
+
const reason = STRIP_REASON_BY_NAME.get(lower);
|
|
39029
|
+
if (reason !== void 0) {
|
|
39030
|
+
removed.push({ name, reason });
|
|
39031
|
+
continue;
|
|
39032
|
+
}
|
|
39033
|
+
stripped[name] = value;
|
|
39034
|
+
}
|
|
39035
|
+
return { stripped, removed };
|
|
39036
|
+
}
|
|
39037
|
+
function defeatUndiciDefaultsInto(headers) {
|
|
39038
|
+
if (headers["user-agent"] === void 0 && headers["User-Agent"] === void 0) {
|
|
39039
|
+
headers["User-Agent"] = "";
|
|
39040
|
+
}
|
|
39041
|
+
if (headers["accept-language"] === void 0 && headers["Accept-Language"] === void 0) {
|
|
39042
|
+
headers["Accept-Language"] = "";
|
|
39043
|
+
}
|
|
39044
|
+
return headers;
|
|
39045
|
+
}
|
|
39046
|
+
function createAnonymizedFetch(baseFetch, onAudit) {
|
|
39047
|
+
const wrapped = async (input, init) => {
|
|
39048
|
+
const headers = normalizeHeadersInit(init?.headers);
|
|
39049
|
+
const result = stripHeaders(headers);
|
|
39050
|
+
defeatUndiciDefaultsInto(result.stripped);
|
|
39051
|
+
const preservedRequired = Object.keys(result.stripped).filter(
|
|
39052
|
+
(k) => REQUIRED_HEADER_SET.has(k.toLowerCase())
|
|
39053
|
+
);
|
|
39054
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
39055
|
+
const method = init?.method ?? (input instanceof Request ? input.method : "GET");
|
|
39056
|
+
if (onAudit) {
|
|
39057
|
+
onAudit({
|
|
39058
|
+
url,
|
|
39059
|
+
method,
|
|
39060
|
+
stripped_count: result.removed.length,
|
|
39061
|
+
removed: result.removed,
|
|
39062
|
+
required_preserved: preservedRequired
|
|
39063
|
+
});
|
|
39064
|
+
}
|
|
39065
|
+
return baseFetch(input, { ...init, headers: result.stripped });
|
|
39066
|
+
};
|
|
39067
|
+
return wrapped;
|
|
39068
|
+
}
|
|
39069
|
+
function normalizeHeadersInit(raw) {
|
|
39070
|
+
if (raw === void 0) return {};
|
|
39071
|
+
if (typeof Headers !== "undefined" && raw instanceof Headers) {
|
|
39072
|
+
const out = {};
|
|
39073
|
+
raw.forEach((value, key) => {
|
|
39074
|
+
out[key] = value;
|
|
39075
|
+
});
|
|
39076
|
+
return out;
|
|
39077
|
+
}
|
|
39078
|
+
if (Array.isArray(raw)) {
|
|
39079
|
+
const out = {};
|
|
39080
|
+
for (const [k, v] of raw) {
|
|
39081
|
+
if (k !== void 0 && v !== void 0) out[k] = v;
|
|
39082
|
+
}
|
|
39083
|
+
return out;
|
|
39084
|
+
}
|
|
39085
|
+
return { ...raw };
|
|
39086
|
+
}
|
|
39087
|
+
|
|
38205
39088
|
// src/intelligence/substrates/hybrid/per-surface-router.ts
|
|
38206
39089
|
function resolveHybridChoice(rules, surface) {
|
|
38207
39090
|
if (!rules) return null;
|
|
@@ -38262,7 +39145,21 @@ var SubstrateSelector = class {
|
|
|
38262
39145
|
this.auditLog = cfg.auditLog;
|
|
38263
39146
|
this.identityId = cfg.identityId;
|
|
38264
39147
|
this.redactor = cfg.redactor ?? IDENTITY_REDACTOR;
|
|
38265
|
-
|
|
39148
|
+
const baseFetch = cfg.fetchImpl ?? globalThis.fetch;
|
|
39149
|
+
this.fetchImpl = createAnonymizedFetch(baseFetch, (event) => {
|
|
39150
|
+
this.auditLog.append(
|
|
39151
|
+
"l2",
|
|
39152
|
+
QUERY_ANONYMITY_AUDIT_OPS.HEADERS_STRIPPED,
|
|
39153
|
+
this.identityId,
|
|
39154
|
+
{
|
|
39155
|
+
url: event.url,
|
|
39156
|
+
method: event.method,
|
|
39157
|
+
stripped_count: event.stripped_count,
|
|
39158
|
+
removed: event.removed,
|
|
39159
|
+
required_preserved: event.required_preserved
|
|
39160
|
+
}
|
|
39161
|
+
);
|
|
39162
|
+
});
|
|
38266
39163
|
this.config = buildDefaultConfig();
|
|
38267
39164
|
}
|
|
38268
39165
|
/**
|
|
@@ -39188,6 +40085,150 @@ var EXIT_BUNDLE_ARTIFACT_KINDS = [
|
|
|
39188
40085
|
"placeholder_vault_metadata"
|
|
39189
40086
|
];
|
|
39190
40087
|
|
|
40088
|
+
// src/recognition/did-web.ts
|
|
40089
|
+
init_encoding();
|
|
40090
|
+
init_hashing();
|
|
40091
|
+
var DEFAULT_TIMEOUT_MS4 = 5e3;
|
|
40092
|
+
var HOST_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
40093
|
+
async function resolveDidWeb(did, opts) {
|
|
40094
|
+
const parsed = parseDidWeb(did);
|
|
40095
|
+
const url = didToUrl(parsed);
|
|
40096
|
+
if (!opts.allowed_hosts.includes(parsed.authority_host)) {
|
|
40097
|
+
return {
|
|
40098
|
+
ok: false,
|
|
40099
|
+
failure: "host_not_allowed",
|
|
40100
|
+
message: `did-web: authority_host '${parsed.authority_host}' is not in the operator's allowed_hosts allowlist; resolution refused (no-outbound-by-default)`,
|
|
40101
|
+
url
|
|
40102
|
+
};
|
|
40103
|
+
}
|
|
40104
|
+
const timeoutMs = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS4;
|
|
40105
|
+
const fetcher = opts.fetcher ?? defaultFetcher;
|
|
40106
|
+
const controller = new AbortController();
|
|
40107
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
40108
|
+
let response;
|
|
40109
|
+
try {
|
|
40110
|
+
response = await fetcher(url, { signal: controller.signal });
|
|
40111
|
+
} catch (err) {
|
|
40112
|
+
clearTimeout(timer);
|
|
40113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40114
|
+
if (controller.signal.aborted) {
|
|
40115
|
+
return {
|
|
40116
|
+
ok: false,
|
|
40117
|
+
failure: "timeout",
|
|
40118
|
+
message: `did-web: resolution exceeded ${timeoutMs}ms`,
|
|
40119
|
+
url
|
|
40120
|
+
};
|
|
40121
|
+
}
|
|
40122
|
+
return {
|
|
40123
|
+
ok: false,
|
|
40124
|
+
failure: "fetch_failed",
|
|
40125
|
+
message: `did-web: fetch error: ${message}`,
|
|
40126
|
+
url
|
|
40127
|
+
};
|
|
40128
|
+
}
|
|
40129
|
+
clearTimeout(timer);
|
|
40130
|
+
if (response.status === 404) {
|
|
40131
|
+
return {
|
|
40132
|
+
ok: false,
|
|
40133
|
+
failure: "not_found",
|
|
40134
|
+
message: `did-web: 404 from authority host`,
|
|
40135
|
+
url
|
|
40136
|
+
};
|
|
40137
|
+
}
|
|
40138
|
+
if (!response.ok) {
|
|
40139
|
+
return {
|
|
40140
|
+
ok: false,
|
|
40141
|
+
failure: "fetch_failed",
|
|
40142
|
+
message: `did-web: authority host returned ${response.status}`,
|
|
40143
|
+
url
|
|
40144
|
+
};
|
|
40145
|
+
}
|
|
40146
|
+
let body;
|
|
40147
|
+
try {
|
|
40148
|
+
body = await response.json();
|
|
40149
|
+
} catch (err) {
|
|
40150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40151
|
+
return {
|
|
40152
|
+
ok: false,
|
|
40153
|
+
failure: "invalid_json",
|
|
40154
|
+
message: `did-web: invalid JSON: ${message}`,
|
|
40155
|
+
url
|
|
40156
|
+
};
|
|
40157
|
+
}
|
|
40158
|
+
if (!isDidDocument(body, did)) {
|
|
40159
|
+
return {
|
|
40160
|
+
ok: false,
|
|
40161
|
+
failure: "invalid_json",
|
|
40162
|
+
message: `did-web: response body is not a valid DID Document for ${did}`,
|
|
40163
|
+
url
|
|
40164
|
+
};
|
|
40165
|
+
}
|
|
40166
|
+
if (opts.expected_public_key !== void 0) {
|
|
40167
|
+
const expectedX = toBase64url(opts.expected_public_key);
|
|
40168
|
+
const actualX = body.verificationMethod[0]?.publicKeyJwk.x;
|
|
40169
|
+
if (actualX !== expectedX) {
|
|
40170
|
+
return {
|
|
40171
|
+
ok: false,
|
|
40172
|
+
failure: "signature_mismatch",
|
|
40173
|
+
message: `did-web: verificationMethod public key does not match expected key`,
|
|
40174
|
+
url
|
|
40175
|
+
};
|
|
40176
|
+
}
|
|
40177
|
+
}
|
|
40178
|
+
return { ok: true, did_document: body, url };
|
|
40179
|
+
}
|
|
40180
|
+
function parseDidWeb(did) {
|
|
40181
|
+
if (!did.startsWith("did:web:")) {
|
|
40182
|
+
throw new Error(`did-web: '${did}' is not a did:web identifier`);
|
|
40183
|
+
}
|
|
40184
|
+
const rest = did.slice("did:web:".length);
|
|
40185
|
+
const segments = rest.split(":");
|
|
40186
|
+
const authorityHost = segments[0];
|
|
40187
|
+
if (!HOST_RE.test(authorityHost)) {
|
|
40188
|
+
throw new Error(`did-web: '${authorityHost}' is not a valid DNS host`);
|
|
40189
|
+
}
|
|
40190
|
+
const parsed = { authority_host: authorityHost };
|
|
40191
|
+
if (segments.length === 1) return parsed;
|
|
40192
|
+
if (segments.length === 5 && segments[1] === "fortress" && segments[3] === "agent") {
|
|
40193
|
+
parsed.fortress_id = segments[2];
|
|
40194
|
+
parsed.agent_label = segments[4];
|
|
40195
|
+
return parsed;
|
|
40196
|
+
}
|
|
40197
|
+
throw new Error(
|
|
40198
|
+
`did-web: '${did}' does not match the supported shapes (bare did:web:<host> or did:web:<host>:fortress:<fid>:agent:<alabel>)`
|
|
40199
|
+
);
|
|
40200
|
+
}
|
|
40201
|
+
function didToUrl(parsed) {
|
|
40202
|
+
if (parsed.fortress_id === void 0 || parsed.agent_label === void 0) {
|
|
40203
|
+
return `https://${parsed.authority_host}/.well-known/did.json`;
|
|
40204
|
+
}
|
|
40205
|
+
return `https://${parsed.authority_host}/fortress/${parsed.fortress_id}/agent/${parsed.agent_label}/did.json`;
|
|
40206
|
+
}
|
|
40207
|
+
function isDidDocument(value, expectedDid) {
|
|
40208
|
+
if (!value || typeof value !== "object") return false;
|
|
40209
|
+
const v = value;
|
|
40210
|
+
if (v["id"] !== expectedDid) return false;
|
|
40211
|
+
if (!Array.isArray(v["@context"])) return false;
|
|
40212
|
+
const vm = v["verificationMethod"];
|
|
40213
|
+
if (!Array.isArray(vm) || vm.length === 0) return false;
|
|
40214
|
+
const first = vm[0];
|
|
40215
|
+
if (!first || typeof first["id"] !== "string") return false;
|
|
40216
|
+
const jwk = first["publicKeyJwk"];
|
|
40217
|
+
if (!jwk || jwk["kty"] !== "OKP" || jwk["crv"] !== "Ed25519") return false;
|
|
40218
|
+
if (typeof jwk["x"] !== "string") return false;
|
|
40219
|
+
if (!Array.isArray(v["authentication"])) return false;
|
|
40220
|
+
if (!Array.isArray(v["assertionMethod"])) return false;
|
|
40221
|
+
return true;
|
|
40222
|
+
}
|
|
40223
|
+
async function defaultFetcher(url, init) {
|
|
40224
|
+
const response = await fetch(url, init);
|
|
40225
|
+
return {
|
|
40226
|
+
ok: response.ok,
|
|
40227
|
+
status: response.status,
|
|
40228
|
+
json: () => response.json()
|
|
40229
|
+
};
|
|
40230
|
+
}
|
|
40231
|
+
|
|
39191
40232
|
// src/exit/bundle.ts
|
|
39192
40233
|
init_hashing();
|
|
39193
40234
|
init_encoding();
|
|
@@ -39612,6 +40653,11 @@ async function verifyExitBundle(bundleDir, options = {}) {
|
|
|
39612
40653
|
|
|
39613
40654
|
// src/exit/bundle.ts
|
|
39614
40655
|
var ARTIFACT_DIR = "artifacts";
|
|
40656
|
+
var EXIT_BUNDLE_DID_WEB_AUDIT_OPS = {
|
|
40657
|
+
EXPORT_INCLUDED: "exit_bundle_did_web_export_included",
|
|
40658
|
+
IMPORT_VERIFIED: "exit_bundle_did_web_import_verified",
|
|
40659
|
+
AUTHORITY_HOST: "exit_bundle_did_web_authority_host"
|
|
40660
|
+
};
|
|
39615
40661
|
var EXIT_IMPORT_NAMESPACE = "_exit_imports";
|
|
39616
40662
|
var EXIT_PUBLIC_IDENTITIES_NAMESPACE = "_exit_public_identities";
|
|
39617
40663
|
var EXIT_AUDIT_RECEIPTS_NAMESPACE = "_exit_audit_receipts";
|
|
@@ -39903,6 +40949,7 @@ async function exportExitBundle(opts) {
|
|
|
39903
40949
|
"placeholder_vault_metadata"
|
|
39904
40950
|
)
|
|
39905
40951
|
);
|
|
40952
|
+
const didWebBinding = validateExportDidWeb(opts.didWeb);
|
|
39906
40953
|
const body = {
|
|
39907
40954
|
manifest_version: EXIT_BUNDLE_MANIFEST_VERSION,
|
|
39908
40955
|
exported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -39910,7 +40957,8 @@ async function exportExitBundle(opts) {
|
|
|
39910
40957
|
identity_id: identity.identity_id,
|
|
39911
40958
|
fortress_id: identity.did,
|
|
39912
40959
|
fortress_master_pubkey: identity.public_key,
|
|
39913
|
-
did: identity.did
|
|
40960
|
+
did: identity.did,
|
|
40961
|
+
...didWebBinding !== void 0 ? { did_web: didWebBinding } : {}
|
|
39914
40962
|
},
|
|
39915
40963
|
source_sanctuary_version: opts.config?.version ?? SANCTUARY_VERSION,
|
|
39916
40964
|
artifacts,
|
|
@@ -39932,6 +40980,18 @@ async function exportExitBundle(opts) {
|
|
|
39932
40980
|
};
|
|
39933
40981
|
const manifestBytes = jsonBytes(manifest);
|
|
39934
40982
|
await promises.writeFile(path.join(bundleDir, "manifest.json"), manifestBytes, { mode: 384 });
|
|
40983
|
+
if (didWebBinding !== void 0) {
|
|
40984
|
+
opts.auditLog.append(
|
|
40985
|
+
"l1",
|
|
40986
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.EXPORT_INCLUDED,
|
|
40987
|
+
identity.identity_id,
|
|
40988
|
+
{
|
|
40989
|
+
approval_id: exportApprovalAuditId,
|
|
40990
|
+
identifier: didWebBinding.identifier,
|
|
40991
|
+
authority_host: didWebBinding.authority_host
|
|
40992
|
+
}
|
|
40993
|
+
);
|
|
40994
|
+
}
|
|
39935
40995
|
await opts.auditLog.flush();
|
|
39936
40996
|
return {
|
|
39937
40997
|
bundle_dir: bundleDir,
|
|
@@ -39943,6 +41003,30 @@ async function exportExitBundle(opts) {
|
|
|
39943
41003
|
]
|
|
39944
41004
|
};
|
|
39945
41005
|
}
|
|
41006
|
+
function validateExportDidWeb(binding) {
|
|
41007
|
+
if (binding === void 0) return void 0;
|
|
41008
|
+
if (!binding.identifier || typeof binding.identifier !== "string") {
|
|
41009
|
+
throw new Error(
|
|
41010
|
+
"exit-bundle: did_web.identifier must be a non-empty did:web URI"
|
|
41011
|
+
);
|
|
41012
|
+
}
|
|
41013
|
+
if (!binding.authority_host || typeof binding.authority_host !== "string") {
|
|
41014
|
+
throw new Error(
|
|
41015
|
+
"exit-bundle: did_web.authority_host must be a non-empty DNS host"
|
|
41016
|
+
);
|
|
41017
|
+
}
|
|
41018
|
+
const parsed = parseDidWeb(binding.identifier);
|
|
41019
|
+
if (parsed.authority_host.toLowerCase() !== binding.authority_host.toLowerCase()) {
|
|
41020
|
+
throw new Error(
|
|
41021
|
+
`exit-bundle: did_web.identifier authority host '${parsed.authority_host}' does not match did_web.authority_host '${binding.authority_host}'`
|
|
41022
|
+
);
|
|
41023
|
+
}
|
|
41024
|
+
return {
|
|
41025
|
+
identifier: binding.identifier,
|
|
41026
|
+
authority_host: binding.authority_host,
|
|
41027
|
+
...binding.published_at !== void 0 ? { published_at: binding.published_at } : {}
|
|
41028
|
+
};
|
|
41029
|
+
}
|
|
39946
41030
|
function publicKeysFromIdentityArtifact(identityArtifact) {
|
|
39947
41031
|
const pubkey = fromBase64url(identityArtifact.bundle.publicKey);
|
|
39948
41032
|
return {
|
|
@@ -40157,6 +41241,87 @@ async function importExitBundle(opts) {
|
|
|
40157
41241
|
};
|
|
40158
41242
|
}
|
|
40159
41243
|
const manifest = await readManifest(opts.bundleDir);
|
|
41244
|
+
const importWarnings = [];
|
|
41245
|
+
const manifestDidWeb = manifest.body.identity_binding.did_web;
|
|
41246
|
+
if (manifestDidWeb !== void 0 && !opts.skipDidWebVerify) {
|
|
41247
|
+
const expectedPublicKey = fromBase64url(
|
|
41248
|
+
manifest.body.identity_binding.fortress_master_pubkey
|
|
41249
|
+
);
|
|
41250
|
+
const resolveOpts = {
|
|
41251
|
+
allowed_hosts: opts.didWebAllowedHosts ?? [],
|
|
41252
|
+
expected_public_key: expectedPublicKey,
|
|
41253
|
+
...opts.didWebFetcher !== void 0 ? { fetcher: opts.didWebFetcher } : {},
|
|
41254
|
+
...opts.didWebTimeoutMs !== void 0 ? { timeout_ms: opts.didWebTimeoutMs } : {}
|
|
41255
|
+
};
|
|
41256
|
+
const resolution = await resolveDidWeb(
|
|
41257
|
+
manifestDidWeb.identifier,
|
|
41258
|
+
resolveOpts
|
|
41259
|
+
);
|
|
41260
|
+
const authorityHost = manifestDidWeb.authority_host;
|
|
41261
|
+
opts.auditLog.append(
|
|
41262
|
+
"l1",
|
|
41263
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.AUTHORITY_HOST,
|
|
41264
|
+
manifest.body.identity_binding.identity_id,
|
|
41265
|
+
{ authority_host: authorityHost, identifier: manifestDidWeb.identifier }
|
|
41266
|
+
);
|
|
41267
|
+
if (resolution.ok) {
|
|
41268
|
+
opts.auditLog.append(
|
|
41269
|
+
"l1",
|
|
41270
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41271
|
+
manifest.body.identity_binding.identity_id,
|
|
41272
|
+
{
|
|
41273
|
+
outcome: "success",
|
|
41274
|
+
identifier: manifestDidWeb.identifier,
|
|
41275
|
+
authority_host: authorityHost,
|
|
41276
|
+
resolved_url: resolution.url
|
|
41277
|
+
}
|
|
41278
|
+
);
|
|
41279
|
+
} else if (resolution.failure === "signature_mismatch") {
|
|
41280
|
+
opts.auditLog.append(
|
|
41281
|
+
"l1",
|
|
41282
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41283
|
+
manifest.body.identity_binding.identity_id,
|
|
41284
|
+
{
|
|
41285
|
+
outcome: "mismatch",
|
|
41286
|
+
identifier: manifestDidWeb.identifier,
|
|
41287
|
+
authority_host: authorityHost,
|
|
41288
|
+
resolved_url: resolution.url
|
|
41289
|
+
}
|
|
41290
|
+
);
|
|
41291
|
+
await opts.auditLog.flush();
|
|
41292
|
+
throw new ExitBundleImportError(
|
|
41293
|
+
"did_web_mismatch",
|
|
41294
|
+
`did:web cross-check failed: the DID Document at ${resolution.url} resolved successfully, but the verificationMethod public key did not match the manifest's claimed fortress_master_pubkey. The bundle's claimed origin (${manifestDidWeb.identifier}) is inconsistent with the published DID Document. To proceed anyway with the manifest signature alone, re-run import with --skip-did-web-verify.`
|
|
41295
|
+
);
|
|
41296
|
+
} else {
|
|
41297
|
+
opts.auditLog.append(
|
|
41298
|
+
"l1",
|
|
41299
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41300
|
+
manifest.body.identity_binding.identity_id,
|
|
41301
|
+
{
|
|
41302
|
+
outcome: "resolution_failure",
|
|
41303
|
+
failure: resolution.failure,
|
|
41304
|
+
identifier: manifestDidWeb.identifier,
|
|
41305
|
+
authority_host: authorityHost,
|
|
41306
|
+
resolved_url: resolution.url
|
|
41307
|
+
}
|
|
41308
|
+
);
|
|
41309
|
+
importWarnings.push(
|
|
41310
|
+
`did:web resolution failed (${resolution.failure}): ${resolution.message}. Import proceeded with manifest-signature verification alone; recognition-layer cross-check was skipped. Re-run with --did-web-allowed-host=<host> to enable resolution, or --skip-did-web-verify to skip deliberately.`
|
|
41311
|
+
);
|
|
41312
|
+
}
|
|
41313
|
+
} else if (manifestDidWeb !== void 0 && opts.skipDidWebVerify) {
|
|
41314
|
+
opts.auditLog.append(
|
|
41315
|
+
"l1",
|
|
41316
|
+
EXIT_BUNDLE_DID_WEB_AUDIT_OPS.IMPORT_VERIFIED,
|
|
41317
|
+
manifest.body.identity_binding.identity_id,
|
|
41318
|
+
{
|
|
41319
|
+
outcome: "skipped",
|
|
41320
|
+
identifier: manifestDidWeb.identifier,
|
|
41321
|
+
authority_host: manifestDidWeb.authority_host
|
|
41322
|
+
}
|
|
41323
|
+
);
|
|
41324
|
+
}
|
|
40160
41325
|
const identityArtifact = await loadExitArtifact(
|
|
40161
41326
|
opts.bundleDir,
|
|
40162
41327
|
manifest,
|
|
@@ -40221,7 +41386,7 @@ async function importExitBundle(opts) {
|
|
|
40221
41386
|
unverifiable_attestations: verification.reputation?.unverifiable_attestations ?? 0
|
|
40222
41387
|
},
|
|
40223
41388
|
staged_artifacts: [],
|
|
40224
|
-
warnings: verification.warnings,
|
|
41389
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40225
41390
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40226
41391
|
};
|
|
40227
41392
|
}
|
|
@@ -40388,7 +41553,7 @@ async function importExitBundle(opts) {
|
|
|
40388
41553
|
state: stateResult,
|
|
40389
41554
|
reputation: reputationResult,
|
|
40390
41555
|
staged_artifacts: stagedArtifacts,
|
|
40391
|
-
warnings: verification.warnings,
|
|
41556
|
+
warnings: [...verification.warnings, ...importWarnings],
|
|
40392
41557
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
40393
41558
|
};
|
|
40394
41559
|
}
|
|
@@ -40611,6 +41776,26 @@ ${policyErr.message}
|
|
|
40611
41776
|
}
|
|
40612
41777
|
throw policyErr;
|
|
40613
41778
|
}
|
|
41779
|
+
const includeDidWebFlag = flagValue(argv, "--include-did-web");
|
|
41780
|
+
const includeDidWebDisabled = includeDidWebFlag === "false";
|
|
41781
|
+
const didWebIdentifier = flagValue(argv, "--did-web");
|
|
41782
|
+
const didWebAuthorityHost = flagValue(argv, "--did-web-authority-host");
|
|
41783
|
+
const didWebPublishedAt = flagValue(argv, "--did-web-published-at");
|
|
41784
|
+
let exportDidWeb;
|
|
41785
|
+
if (!includeDidWebDisabled && didWebIdentifier !== void 0) {
|
|
41786
|
+
if (didWebAuthorityHost === void 0) {
|
|
41787
|
+
write(
|
|
41788
|
+
err,
|
|
41789
|
+
"Error: --did-web requires --did-web-authority-host=<host>\n"
|
|
41790
|
+
);
|
|
41791
|
+
return 2;
|
|
41792
|
+
}
|
|
41793
|
+
exportDidWeb = {
|
|
41794
|
+
identifier: didWebIdentifier,
|
|
41795
|
+
authority_host: didWebAuthorityHost,
|
|
41796
|
+
...didWebPublishedAt !== void 0 ? { published_at: didWebPublishedAt } : {}
|
|
41797
|
+
};
|
|
41798
|
+
}
|
|
40614
41799
|
const result = await exportExitBundle({
|
|
40615
41800
|
bundleDir: outDir,
|
|
40616
41801
|
storage: ctx.storage,
|
|
@@ -40622,7 +41807,8 @@ ${policyErr.message}
|
|
|
40622
41807
|
config,
|
|
40623
41808
|
stateStoragePath: ctx.stateStoragePath,
|
|
40624
41809
|
stateNamespaces: repeatedFlagValues(argv, "--state-namespace"),
|
|
40625
|
-
keySource: ctx.keySource
|
|
41810
|
+
keySource: ctx.keySource,
|
|
41811
|
+
...exportDidWeb !== void 0 ? { didWeb: exportDidWeb } : {}
|
|
40626
41812
|
});
|
|
40627
41813
|
if (json) write(out, JSON.stringify(result, null, 2) + "\n");
|
|
40628
41814
|
else {
|
|
@@ -40700,6 +41886,11 @@ ${policyErr.message}
|
|
|
40700
41886
|
write(err, "--conflict must be skip, overwrite, or version\n");
|
|
40701
41887
|
return 2;
|
|
40702
41888
|
}
|
|
41889
|
+
const didWebAllowedHosts = repeatedFlagValues(
|
|
41890
|
+
argv,
|
|
41891
|
+
"--did-web-allowed-host"
|
|
41892
|
+
);
|
|
41893
|
+
const skipDidWebVerify = hasFlag(argv, "--skip-did-web-verify");
|
|
40703
41894
|
let result;
|
|
40704
41895
|
try {
|
|
40705
41896
|
result = await importExitBundle({
|
|
@@ -40715,7 +41906,9 @@ ${policyErr.message}
|
|
|
40715
41906
|
conflictResolution: conflict,
|
|
40716
41907
|
sourcePassphrase: flagValue(argv, "--source-passphrase"),
|
|
40717
41908
|
sourceRecoveryKey: flagValue(argv, "--source-recovery-key"),
|
|
40718
|
-
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id")
|
|
41909
|
+
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id"),
|
|
41910
|
+
...didWebAllowedHosts.length > 0 ? { didWebAllowedHosts } : {},
|
|
41911
|
+
skipDidWebVerify
|
|
40719
41912
|
});
|
|
40720
41913
|
} catch (e) {
|
|
40721
41914
|
if (e instanceof InvalidExitBundleError) {
|
|
@@ -41452,12 +42645,14 @@ ${err.message}
|
|
|
41452
42645
|
fortressId: fortressIdForAggregator
|
|
41453
42646
|
});
|
|
41454
42647
|
const handoffEventBridge = new HandoffEventBridge();
|
|
42648
|
+
const workflowStateTracker = new WorkflowStateTracker();
|
|
41455
42649
|
if (dashboard) {
|
|
41456
42650
|
dashboard.setHandoffLog({
|
|
41457
42651
|
handoffLog,
|
|
41458
42652
|
eventBridge: handoffEventBridge,
|
|
41459
42653
|
auditLog,
|
|
41460
|
-
operatorId: aggregatorIdentityId
|
|
42654
|
+
operatorId: aggregatorIdentityId,
|
|
42655
|
+
workflowStateTracker
|
|
41461
42656
|
});
|
|
41462
42657
|
}
|
|
41463
42658
|
const policyTools = createPrincipalPolicyTools(policy, baseline, auditLog);
|