@neuroverseos/governance 0.9.0 → 0.11.0
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/README.md +1 -1
- package/dist/adapters/autoresearch.cjs +19 -1
- package/dist/adapters/autoresearch.d.cts +1 -1
- package/dist/adapters/autoresearch.d.ts +1 -1
- package/dist/adapters/autoresearch.js +2 -2
- package/dist/adapters/deep-agents.cjs +19 -1
- package/dist/adapters/deep-agents.d.cts +2 -2
- package/dist/adapters/deep-agents.d.ts +2 -2
- package/dist/adapters/deep-agents.js +2 -2
- package/dist/adapters/express.cjs +19 -1
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +2 -2
- package/dist/adapters/github.cjs +19 -1
- package/dist/adapters/github.d.cts +2 -2
- package/dist/adapters/github.d.ts +2 -2
- package/dist/adapters/github.js +2 -2
- package/dist/adapters/index.cjs +19 -1
- package/dist/adapters/index.d.cts +2 -2
- package/dist/adapters/index.d.ts +2 -2
- package/dist/adapters/index.js +8 -8
- package/dist/adapters/langchain.cjs +19 -1
- package/dist/adapters/langchain.d.cts +2 -2
- package/dist/adapters/langchain.d.ts +2 -2
- package/dist/adapters/langchain.js +2 -2
- package/dist/adapters/mentraos.cjs +19 -1
- package/dist/adapters/mentraos.d.cts +2 -2
- package/dist/adapters/mentraos.d.ts +2 -2
- package/dist/adapters/mentraos.js +2 -2
- package/dist/adapters/openai.cjs +19 -1
- package/dist/adapters/openai.d.cts +2 -2
- package/dist/adapters/openai.d.ts +2 -2
- package/dist/adapters/openai.js +2 -2
- package/dist/adapters/openclaw.cjs +19 -1
- package/dist/adapters/openclaw.d.cts +2 -2
- package/dist/adapters/openclaw.d.ts +2 -2
- package/dist/adapters/openclaw.js +2 -2
- package/dist/admin/index.cjs +19 -1
- package/dist/admin/index.js +1 -1
- package/dist/audit-CRJOB4CP.js +93 -0
- package/dist/audit-behavior-C62FdRAC.d.cts +100 -0
- package/dist/audit-behavior-DFy7LeYv.d.ts +100 -0
- package/dist/{behavioral-SPWPGYXL.js → behavioral-4TKMHZQZ.js} +2 -2
- package/dist/{chunk-OQU65525.js → chunk-24YW7BHC.js} +1 -1
- package/dist/{chunk-3ZWU7C43.js → chunk-2KTPIE57.js} +494 -14
- package/dist/{chunk-TJ5L2UTE.js → chunk-5K3LATTM.js} +1 -1
- package/dist/{chunk-HDNDL6D5.js → chunk-5LDBYOSJ.js} +1 -1
- package/dist/{chunk-FDPPZLSQ.js → chunk-5ZWKM7MO.js} +1 -1
- package/dist/{chunk-B3IIPTY3.js → chunk-6MB6TMAG.js} +1 -1
- package/dist/{chunk-IOVXB6QN.js → chunk-GXTAHCND.js} +1 -1
- package/dist/{chunk-FKQCPRKI.js → chunk-MAOIHKFO.js} +1 -1
- package/dist/{chunk-ZAF6JH23.js → chunk-MBOW6YXN.js} +19 -1
- package/dist/{chunk-A2UZTLRV.js → chunk-MLXKSX3L.js} +1 -1
- package/dist/{chunk-7FL3U7Z5.js → chunk-MWGEXHOD.js} +1 -1
- package/dist/{chunk-6CV4XG3J.js → chunk-QFDFAWZ6.js} +1 -1
- package/dist/{chunk-2VAWP6FI.js → chunk-RAS62JXV.js} +1 -1
- package/dist/{chunk-OTZU76DH.js → chunk-XAF3CYCW.js} +1 -1
- package/dist/{chunk-T6GMRZWC.js → chunk-XTYQCTDD.js} +1 -1
- package/dist/{chunk-TIXVEPS2.js → chunk-YN7OI5ZV.js} +1 -1
- package/dist/cli/neuroverse.cjs +999 -111
- package/dist/cli/neuroverse.js +16 -12
- package/dist/cli/plan.cjs +18 -0
- package/dist/cli/radiant.cjs +814 -17
- package/dist/cli/radiant.d.cts +44 -1
- package/dist/cli/radiant.d.ts +44 -1
- package/dist/cli/radiant.js +295 -7
- package/dist/cli/run.cjs +18 -0
- package/dist/cli/run.js +4 -4
- package/dist/{decision-flow-IJPNMVQK.js → decision-flow-5VI5YG6A.js} +2 -2
- package/dist/{demo-6W3YXLAX.js → demo-GYX6CYHC.js} +2 -2
- package/dist/engine/guard-engine.cjs +19 -1
- package/dist/engine/guard-engine.d.cts +21 -1
- package/dist/engine/guard-engine.d.ts +21 -1
- package/dist/engine/guard-engine.js +1 -1
- package/dist/{equity-penalties-CCO3GVHS.js → equity-penalties-NOM46NEO.js} +2 -2
- package/dist/{guard-IHJEKHL2.js → guard-PQ3SYV4Y.js} +3 -3
- package/dist/{guard-contract-ddiIPlOg.d.cts → guard-contract-Oznf-Kgq.d.cts} +32 -0
- package/dist/{guard-contract-q6HJAq3Q.d.ts → guard-contract-w_i_6gh-.d.ts} +32 -0
- package/dist/{impact-WIAM66IH.js → impact-LDJLTVRU.js} +3 -3
- package/dist/index.cjs +62 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +49 -8
- package/dist/{mcp-server-CKYBHXWK.js → mcp-server-W3MWSKD7.js} +2 -2
- package/dist/{playground-3TTBN7XD.js → playground-SSZRNUAF.js} +1 -1
- package/dist/radiant/index.cjs +517 -14
- package/dist/radiant/index.d.cts +180 -10
- package/dist/radiant/index.d.ts +180 -10
- package/dist/radiant/index.js +12 -2
- package/dist/{redteam-W644UMWN.js → redteam-KCULS7EW.js} +1 -1
- package/dist/{server-JKUBUK5H.js → server-EGRGGSM2.js} +2 -2
- package/dist/{session-FMAROEIE.js → session-PZLTL22G.js} +2 -2
- package/dist/{shared-PpalGKxc.d.cts → shared-BC8mOpt0.d.cts} +1 -1
- package/dist/{shared-DAzdfWtU.d.ts → shared-CP63gNNW.d.ts} +1 -1
- package/dist/{test-XDB2DH3L.js → test-LIHGWHBA.js} +1 -1
- package/dist/{trace-2YDNAXMK.js → trace-DC3D7XPD.js} +2 -2
- package/examples/radiant-weekly-workflow.yml +4 -1
- package/package.json +1 -1
- /package/dist/{doctor-XEMLO6UA.js → doctor-SIWQGTAO.js} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "./chunk-I4RTIMLX.js";
|
|
7
7
|
import {
|
|
8
8
|
evaluateGuard
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MBOW6YXN.js";
|
|
10
10
|
|
|
11
11
|
// src/radiant/core/compress.ts
|
|
12
12
|
function compressWorldmodel(content) {
|
|
@@ -859,6 +859,257 @@ async function fetchNotionAPI(url, headers, init) {
|
|
|
859
859
|
return await res.json();
|
|
860
860
|
}
|
|
861
861
|
|
|
862
|
+
// src/radiant/adapters/linear.ts
|
|
863
|
+
async function fetchLinearActivity(apiKey, options = {}) {
|
|
864
|
+
const windowDays = options.windowDays ?? 14;
|
|
865
|
+
const maxIssues = options.maxIssues ?? 200;
|
|
866
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
867
|
+
const sinceIso = since.toISOString();
|
|
868
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
869
|
+
const teamFilter = options.teamIds && options.teamIds.length > 0 ? `team: { id: { in: [${options.teamIds.map((t) => JSON.stringify(t)).join(", ")}] } }` : "";
|
|
870
|
+
const issuesQuery = `
|
|
871
|
+
query RadiantIssues($since: DateTimeOrDuration!, $first: Int!) {
|
|
872
|
+
issues(
|
|
873
|
+
filter: {
|
|
874
|
+
updatedAt: { gte: $since }
|
|
875
|
+
${teamFilter}
|
|
876
|
+
}
|
|
877
|
+
first: $first
|
|
878
|
+
orderBy: updatedAt
|
|
879
|
+
) {
|
|
880
|
+
nodes {
|
|
881
|
+
id
|
|
882
|
+
identifier
|
|
883
|
+
title
|
|
884
|
+
url
|
|
885
|
+
createdAt
|
|
886
|
+
updatedAt
|
|
887
|
+
completedAt
|
|
888
|
+
canceledAt
|
|
889
|
+
state { name type }
|
|
890
|
+
assignee { id name email }
|
|
891
|
+
creator { id name }
|
|
892
|
+
team { id name }
|
|
893
|
+
project { id name }
|
|
894
|
+
cycle { id number startsAt endsAt }
|
|
895
|
+
comments(first: 20) {
|
|
896
|
+
nodes { id body createdAt user { id name } }
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
`;
|
|
902
|
+
const cyclesQuery = `
|
|
903
|
+
query RadiantCycles($since: DateTimeOrDuration!, $now: DateTimeOrDuration!) {
|
|
904
|
+
cycles(
|
|
905
|
+
filter: { endsAt: { gte: $since, lte: $now } }
|
|
906
|
+
first: 20
|
|
907
|
+
) {
|
|
908
|
+
nodes {
|
|
909
|
+
id
|
|
910
|
+
number
|
|
911
|
+
startsAt
|
|
912
|
+
endsAt
|
|
913
|
+
issueCountHistory
|
|
914
|
+
completedIssueCountHistory
|
|
915
|
+
team { id name }
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
`;
|
|
920
|
+
const [issuesResponse, cyclesResponse] = await Promise.all([
|
|
921
|
+
fetchLinearGraphQL(apiKey, issuesQuery, {
|
|
922
|
+
since: sinceIso,
|
|
923
|
+
first: maxIssues
|
|
924
|
+
}),
|
|
925
|
+
fetchLinearGraphQL(apiKey, cyclesQuery, {
|
|
926
|
+
since: sinceIso,
|
|
927
|
+
now: nowIso
|
|
928
|
+
})
|
|
929
|
+
]);
|
|
930
|
+
const rawIssues = issuesResponse.issues?.nodes ?? [];
|
|
931
|
+
const rawCycles = cyclesResponse.cycles?.nodes ?? [];
|
|
932
|
+
const events = [];
|
|
933
|
+
const assignees = /* @__PURE__ */ new Set();
|
|
934
|
+
const projects = /* @__PURE__ */ new Map();
|
|
935
|
+
let issuesCreated = 0;
|
|
936
|
+
let issuesCompleted = 0;
|
|
937
|
+
let issuesOpen = 0;
|
|
938
|
+
let issuesStalled = 0;
|
|
939
|
+
let commentsTotal = 0;
|
|
940
|
+
const now = Date.now();
|
|
941
|
+
const stallThresholdMs = 14 * 24 * 60 * 60 * 1e3;
|
|
942
|
+
for (const issue of rawIssues) {
|
|
943
|
+
const created = new Date(issue.createdAt);
|
|
944
|
+
const updated = new Date(issue.updatedAt);
|
|
945
|
+
const completed = issue.completedAt ? new Date(issue.completedAt) : null;
|
|
946
|
+
const assigneeId = issue.assignee?.id ?? "unassigned";
|
|
947
|
+
if (assigneeId !== "unassigned") assignees.add(assigneeId);
|
|
948
|
+
if (issue.project) {
|
|
949
|
+
projects.set(issue.project.name, (projects.get(issue.project.name) ?? 0) + 1);
|
|
950
|
+
}
|
|
951
|
+
const actor = {
|
|
952
|
+
id: assigneeId,
|
|
953
|
+
kind: "human",
|
|
954
|
+
name: issue.assignee?.name ?? "unassigned"
|
|
955
|
+
};
|
|
956
|
+
if (created >= since) {
|
|
957
|
+
issuesCreated++;
|
|
958
|
+
events.push({
|
|
959
|
+
id: `linear-created-${issue.id}`,
|
|
960
|
+
timestamp: issue.createdAt,
|
|
961
|
+
actor: {
|
|
962
|
+
id: issue.creator?.id ?? "unknown",
|
|
963
|
+
kind: "human",
|
|
964
|
+
name: issue.creator?.name ?? "unknown"
|
|
965
|
+
},
|
|
966
|
+
kind: "issue_created",
|
|
967
|
+
content: `[${issue.identifier}] ${issue.title}`,
|
|
968
|
+
metadata: {
|
|
969
|
+
issueId: issue.id,
|
|
970
|
+
url: issue.url,
|
|
971
|
+
team: issue.team?.name,
|
|
972
|
+
project: issue.project?.name,
|
|
973
|
+
state: issue.state?.name
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
if (completed && completed >= since) {
|
|
978
|
+
issuesCompleted++;
|
|
979
|
+
events.push({
|
|
980
|
+
id: `linear-completed-${issue.id}`,
|
|
981
|
+
timestamp: issue.completedAt,
|
|
982
|
+
actor,
|
|
983
|
+
kind: "issue_completed",
|
|
984
|
+
content: `[${issue.identifier}] ${issue.title}`,
|
|
985
|
+
metadata: {
|
|
986
|
+
issueId: issue.id,
|
|
987
|
+
url: issue.url,
|
|
988
|
+
team: issue.team?.name,
|
|
989
|
+
project: issue.project?.name,
|
|
990
|
+
cycleDays: issue.cycle?.startsAt && issue.completedAt ? Math.round(
|
|
991
|
+
(new Date(issue.completedAt).getTime() - new Date(issue.cycle.startsAt).getTime()) / (24 * 60 * 60 * 1e3)
|
|
992
|
+
) : null
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
if (!completed && !issue.canceledAt) {
|
|
997
|
+
issuesOpen++;
|
|
998
|
+
const isInProgress = issue.state?.type === "started";
|
|
999
|
+
const idleMs = now - updated.getTime();
|
|
1000
|
+
if (isInProgress && idleMs > stallThresholdMs) issuesStalled++;
|
|
1001
|
+
}
|
|
1002
|
+
for (const comment of issue.comments?.nodes ?? []) {
|
|
1003
|
+
const commentedAt = new Date(comment.createdAt);
|
|
1004
|
+
if (commentedAt < since) continue;
|
|
1005
|
+
commentsTotal++;
|
|
1006
|
+
events.push({
|
|
1007
|
+
id: `linear-comment-${comment.id}`,
|
|
1008
|
+
timestamp: comment.createdAt,
|
|
1009
|
+
actor: {
|
|
1010
|
+
id: comment.user?.id ?? "unknown",
|
|
1011
|
+
kind: "human",
|
|
1012
|
+
name: comment.user?.name ?? "unknown"
|
|
1013
|
+
},
|
|
1014
|
+
kind: "issue_comment",
|
|
1015
|
+
content: comment.body.slice(0, 280),
|
|
1016
|
+
metadata: {
|
|
1017
|
+
issueId: issue.id,
|
|
1018
|
+
issueIdentifier: issue.identifier,
|
|
1019
|
+
url: issue.url
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
let cycleCompletionRate = null;
|
|
1025
|
+
if (rawCycles.length > 0) {
|
|
1026
|
+
const rates = [];
|
|
1027
|
+
for (const cycle of rawCycles) {
|
|
1028
|
+
const committed = cycle.issueCountHistory?.at(0) ?? 0;
|
|
1029
|
+
const completed = cycle.completedIssueCountHistory?.at(-1) ?? 0;
|
|
1030
|
+
if (committed > 0) rates.push(completed / committed);
|
|
1031
|
+
}
|
|
1032
|
+
if (rates.length > 0) {
|
|
1033
|
+
cycleCompletionRate = Math.round(rates.reduce((a, b) => a + b, 0) / rates.length * 100) / 100;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const topProjects = [...projects.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
|
|
1037
|
+
const signals = {
|
|
1038
|
+
issuesCreated,
|
|
1039
|
+
issuesCompleted,
|
|
1040
|
+
issuesOpen,
|
|
1041
|
+
issuesStalled,
|
|
1042
|
+
cycleCompletionRate,
|
|
1043
|
+
uniqueAssignees: assignees.size,
|
|
1044
|
+
commentsTotal,
|
|
1045
|
+
topProjects
|
|
1046
|
+
};
|
|
1047
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1048
|
+
return { events, signals };
|
|
1049
|
+
}
|
|
1050
|
+
function formatLinearSignalsForPrompt(signals) {
|
|
1051
|
+
if (signals.issuesCreated === 0 && signals.issuesCompleted === 0 && signals.issuesOpen === 0) {
|
|
1052
|
+
return "";
|
|
1053
|
+
}
|
|
1054
|
+
const lines = [
|
|
1055
|
+
"## Linear Activity (planned work vs. shipped outcome)",
|
|
1056
|
+
"",
|
|
1057
|
+
`${signals.issuesCreated} issues created, ${signals.issuesCompleted} completed in window.`,
|
|
1058
|
+
`${signals.issuesOpen} issues still open.`
|
|
1059
|
+
];
|
|
1060
|
+
if (signals.issuesStalled > 0) {
|
|
1061
|
+
lines.push(
|
|
1062
|
+
`${signals.issuesStalled} in-progress issues haven't moved in 14+ days (stalled).`
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
if (signals.cycleCompletionRate !== null) {
|
|
1066
|
+
const pct = Math.round(signals.cycleCompletionRate * 100);
|
|
1067
|
+
lines.push(`Cycles ended in window completed ${pct}% of what was committed.`);
|
|
1068
|
+
}
|
|
1069
|
+
if (signals.uniqueAssignees > 0) {
|
|
1070
|
+
lines.push(`${signals.uniqueAssignees} unique assignees active.`);
|
|
1071
|
+
}
|
|
1072
|
+
if (signals.commentsTotal > 0) {
|
|
1073
|
+
lines.push(`${signals.commentsTotal} comments across issues in window.`);
|
|
1074
|
+
}
|
|
1075
|
+
if (signals.topProjects.length > 0) {
|
|
1076
|
+
lines.push(`Most active projects: ${signals.topProjects.join(", ")}.`);
|
|
1077
|
+
}
|
|
1078
|
+
lines.push("");
|
|
1079
|
+
lines.push("Linear is where the team states what it will build.");
|
|
1080
|
+
lines.push("GitHub is where the team reveals what actually got built.");
|
|
1081
|
+
lines.push("Low completion rate + high creation rate = planning faster than shipping.");
|
|
1082
|
+
lines.push("High stalled count = commitments made but not honored.");
|
|
1083
|
+
lines.push("Compare Linear signals against GitHub to find the stated-vs-shipped gap.");
|
|
1084
|
+
return lines.join("\n");
|
|
1085
|
+
}
|
|
1086
|
+
async function fetchLinearGraphQL(apiKey, query, variables) {
|
|
1087
|
+
const res = await fetch("https://api.linear.app/graphql", {
|
|
1088
|
+
method: "POST",
|
|
1089
|
+
headers: {
|
|
1090
|
+
// Linear accepts the raw API key in Authorization with no "Bearer" prefix.
|
|
1091
|
+
Authorization: apiKey,
|
|
1092
|
+
"Content-Type": "application/json"
|
|
1093
|
+
},
|
|
1094
|
+
body: JSON.stringify({ query, variables })
|
|
1095
|
+
});
|
|
1096
|
+
if (!res.ok) {
|
|
1097
|
+
throw new Error(
|
|
1098
|
+
`Linear API error ${res.status}: ${(await res.text()).slice(0, 300)}`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
const json = await res.json();
|
|
1102
|
+
if (json.errors && json.errors.length > 0) {
|
|
1103
|
+
throw new Error(
|
|
1104
|
+
`Linear GraphQL errors: ${json.errors.map((e) => e.message).join("; ")}`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
if (!json.data) {
|
|
1108
|
+
throw new Error("Linear API returned no data");
|
|
1109
|
+
}
|
|
1110
|
+
return json.data;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
862
1113
|
// src/radiant/core/git-remote.ts
|
|
863
1114
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
864
1115
|
import { join, resolve } from "path";
|
|
@@ -1579,6 +1830,7 @@ async function auditGovernance(events, worldPath) {
|
|
|
1579
1830
|
return emptyAudit(events.length, "Could not load compiled worldmodel for governance audit.");
|
|
1580
1831
|
}
|
|
1581
1832
|
const verdicts = [];
|
|
1833
|
+
const crossings = [];
|
|
1582
1834
|
for (const ce of events) {
|
|
1583
1835
|
const intent = ce.event.content?.slice(0, 500) || ce.event.kind || "activity";
|
|
1584
1836
|
const scope = ce.event.metadata?.scope || void 0;
|
|
@@ -1589,16 +1841,32 @@ async function auditGovernance(events, worldPath) {
|
|
|
1589
1841
|
scope,
|
|
1590
1842
|
actionCategory: mapKindToCategory(ce.event.kind)
|
|
1591
1843
|
},
|
|
1592
|
-
world
|
|
1844
|
+
world,
|
|
1845
|
+
{ mode: "observe" }
|
|
1593
1846
|
);
|
|
1847
|
+
const shadow = result.shadowStatus ?? "ALLOW";
|
|
1594
1848
|
verdicts.push({
|
|
1595
1849
|
eventId: ce.event.id,
|
|
1596
1850
|
domain: ce.domain,
|
|
1597
|
-
status:
|
|
1598
|
-
reason: result.
|
|
1851
|
+
status: shadow,
|
|
1852
|
+
reason: result.shadowReason,
|
|
1599
1853
|
ruleId: result.ruleId,
|
|
1600
1854
|
warning: result.warning
|
|
1601
1855
|
});
|
|
1856
|
+
if (shadow !== "ALLOW") {
|
|
1857
|
+
crossings.push({
|
|
1858
|
+
eventId: ce.event.id,
|
|
1859
|
+
timestamp: ce.event.timestamp,
|
|
1860
|
+
kind: ce.event.kind,
|
|
1861
|
+
actorId: ce.event.actor.id,
|
|
1862
|
+
shadowStatus: shadow,
|
|
1863
|
+
shadowReason: result.shadowReason,
|
|
1864
|
+
ruleId: result.ruleId,
|
|
1865
|
+
excerpt: intent.length > 280 ? intent.slice(0, 279) + "\u2026" : intent,
|
|
1866
|
+
wouldHaveBlocked: true,
|
|
1867
|
+
verdict: result
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1602
1870
|
} catch {
|
|
1603
1871
|
verdicts.push({
|
|
1604
1872
|
eventId: ce.event.id,
|
|
@@ -1617,6 +1885,7 @@ async function auditGovernance(events, worldPath) {
|
|
|
1617
1885
|
human,
|
|
1618
1886
|
cyber,
|
|
1619
1887
|
joint,
|
|
1888
|
+
crossings,
|
|
1620
1889
|
summary
|
|
1621
1890
|
};
|
|
1622
1891
|
}
|
|
@@ -1672,6 +1941,7 @@ function emptyAudit(total, reason) {
|
|
|
1672
1941
|
human: { allow: 0, modify: 0, block: 0, details: [] },
|
|
1673
1942
|
cyber: { allow: 0, modify: 0, block: 0, details: [] },
|
|
1674
1943
|
joint: { allow: 0, modify: 0, block: 0, details: [] },
|
|
1944
|
+
crossings: [],
|
|
1675
1945
|
summary: reason
|
|
1676
1946
|
};
|
|
1677
1947
|
}
|
|
@@ -1836,6 +2106,138 @@ var DEFAULT_SIGNAL_EXTRACTORS = Object.freeze([
|
|
|
1836
2106
|
DECISION_MOMENTUM_EXTRACTOR
|
|
1837
2107
|
]);
|
|
1838
2108
|
|
|
2109
|
+
// src/radiant/core/vocabulary.ts
|
|
2110
|
+
function extractDeclaredVocabulary(worldmodelContent) {
|
|
2111
|
+
const aligned = extractSection(worldmodelContent, "Aligned Behaviors").map(
|
|
2112
|
+
(b) => parseBehavior(b, "aligned")
|
|
2113
|
+
);
|
|
2114
|
+
const drift = extractSection(worldmodelContent, "Drift Behaviors").map(
|
|
2115
|
+
(b) => parseBehavior(b, "drift")
|
|
2116
|
+
);
|
|
2117
|
+
const allNames = [...aligned, ...drift].map((p) => p.name);
|
|
2118
|
+
return { aligned, drift, allNames };
|
|
2119
|
+
}
|
|
2120
|
+
function extractSection(content, header) {
|
|
2121
|
+
const escaped = header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2122
|
+
const pattern = new RegExp(
|
|
2123
|
+
`##\\s+${escaped}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`,
|
|
2124
|
+
"i"
|
|
2125
|
+
);
|
|
2126
|
+
const match = content.match(pattern);
|
|
2127
|
+
if (!match) return [];
|
|
2128
|
+
const body = match[1];
|
|
2129
|
+
const bullets = body.match(/^[-*]\s+.+$/gm);
|
|
2130
|
+
if (!bullets) return [];
|
|
2131
|
+
return bullets.map((b) => b.replace(/^[-*]\s+/, "").trim()).filter((b) => b.length > 0 && !b.startsWith("<!--"));
|
|
2132
|
+
}
|
|
2133
|
+
function parseBehavior(bullet, kind) {
|
|
2134
|
+
const explicit = bullet.match(
|
|
2135
|
+
/^`?([a-z][a-z0-9_]*)`?\s+[—\u2014-]\s+(.+)$/i
|
|
2136
|
+
);
|
|
2137
|
+
if (explicit && isSnakeCase(explicit[1])) {
|
|
2138
|
+
return {
|
|
2139
|
+
name: explicit[1].toLowerCase(),
|
|
2140
|
+
prose: explicit[2].trim(),
|
|
2141
|
+
kind
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
return { name: snakeCaseName(bullet), prose: bullet, kind };
|
|
2145
|
+
}
|
|
2146
|
+
function isSnakeCase(s) {
|
|
2147
|
+
return /^[a-z][a-z0-9_]*$/.test(s);
|
|
2148
|
+
}
|
|
2149
|
+
function snakeCaseName(s) {
|
|
2150
|
+
const base = s.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
2151
|
+
if (base.length <= 60) return base;
|
|
2152
|
+
const truncated = base.slice(0, 60);
|
|
2153
|
+
const lastUnderscore = truncated.lastIndexOf("_");
|
|
2154
|
+
return lastUnderscore > 20 ? truncated.slice(0, lastUnderscore) : truncated;
|
|
2155
|
+
}
|
|
2156
|
+
function matchDeclaredPattern(candidateName, candidateDescription, vocabulary) {
|
|
2157
|
+
const candidateText = `${candidateName.replace(/_/g, " ")} ${candidateDescription}`;
|
|
2158
|
+
const candidateWords = contentWords(candidateText);
|
|
2159
|
+
if (candidateWords.size === 0) return null;
|
|
2160
|
+
let best = null;
|
|
2161
|
+
for (const pattern of [...vocabulary.aligned, ...vocabulary.drift]) {
|
|
2162
|
+
const proseWords = contentWords(pattern.prose);
|
|
2163
|
+
if (proseWords.size === 0) continue;
|
|
2164
|
+
let shared = 0;
|
|
2165
|
+
for (const w of proseWords) {
|
|
2166
|
+
if (candidateWords.has(w)) shared++;
|
|
2167
|
+
}
|
|
2168
|
+
const coverage = shared / proseWords.size;
|
|
2169
|
+
if (shared >= 2 && coverage >= 0.3) {
|
|
2170
|
+
if (!best || coverage > best.score) {
|
|
2171
|
+
best = { pattern, score: coverage };
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
return best?.pattern ?? null;
|
|
2176
|
+
}
|
|
2177
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
2178
|
+
"about",
|
|
2179
|
+
"after",
|
|
2180
|
+
"against",
|
|
2181
|
+
"among",
|
|
2182
|
+
"around",
|
|
2183
|
+
"because",
|
|
2184
|
+
"been",
|
|
2185
|
+
"before",
|
|
2186
|
+
"being",
|
|
2187
|
+
"between",
|
|
2188
|
+
"both",
|
|
2189
|
+
"could",
|
|
2190
|
+
"does",
|
|
2191
|
+
"doing",
|
|
2192
|
+
"during",
|
|
2193
|
+
"each",
|
|
2194
|
+
"from",
|
|
2195
|
+
"further",
|
|
2196
|
+
"have",
|
|
2197
|
+
"having",
|
|
2198
|
+
"into",
|
|
2199
|
+
"itself",
|
|
2200
|
+
"most",
|
|
2201
|
+
"nor",
|
|
2202
|
+
"only",
|
|
2203
|
+
"other",
|
|
2204
|
+
"over",
|
|
2205
|
+
"same",
|
|
2206
|
+
"should",
|
|
2207
|
+
"some",
|
|
2208
|
+
"such",
|
|
2209
|
+
"than",
|
|
2210
|
+
"that",
|
|
2211
|
+
"their",
|
|
2212
|
+
"them",
|
|
2213
|
+
"then",
|
|
2214
|
+
"there",
|
|
2215
|
+
"these",
|
|
2216
|
+
"they",
|
|
2217
|
+
"this",
|
|
2218
|
+
"those",
|
|
2219
|
+
"through",
|
|
2220
|
+
"under",
|
|
2221
|
+
"until",
|
|
2222
|
+
"very",
|
|
2223
|
+
"were",
|
|
2224
|
+
"what",
|
|
2225
|
+
"when",
|
|
2226
|
+
"where",
|
|
2227
|
+
"which",
|
|
2228
|
+
"while",
|
|
2229
|
+
"will",
|
|
2230
|
+
"with",
|
|
2231
|
+
"without",
|
|
2232
|
+
"would",
|
|
2233
|
+
"your",
|
|
2234
|
+
"yours"
|
|
2235
|
+
]);
|
|
2236
|
+
function contentWords(text) {
|
|
2237
|
+
const words = text.toLowerCase().match(/[a-z][a-z0-9_]+/g) ?? [];
|
|
2238
|
+
return new Set(words.filter((w) => w.length > 3 && !STOPWORDS.has(w)));
|
|
2239
|
+
}
|
|
2240
|
+
|
|
1839
2241
|
// src/radiant/types.ts
|
|
1840
2242
|
var DEFAULT_EVIDENCE_GATE = { k: 3, c: 0.5 };
|
|
1841
2243
|
function isScored(s) {
|
|
@@ -1878,7 +2280,11 @@ function scoreComposite(a_L, a_C, a_N) {
|
|
|
1878
2280
|
async function interpretPatterns(input) {
|
|
1879
2281
|
const prompt = buildInterpretationPrompt(input);
|
|
1880
2282
|
const raw = await input.ai.complete(prompt, "Analyze the activity and produce the read.");
|
|
1881
|
-
const
|
|
2283
|
+
const canonicalNames = [
|
|
2284
|
+
...input.canonicalPatterns ?? [],
|
|
2285
|
+
...input.declaredVocabulary?.allNames ?? []
|
|
2286
|
+
];
|
|
2287
|
+
const parsed = parseInterpretation(raw, canonicalNames, input.declaredVocabulary);
|
|
1882
2288
|
return {
|
|
1883
2289
|
patterns: parsed.patterns,
|
|
1884
2290
|
meaning: parsed.meaning,
|
|
@@ -1889,8 +2295,10 @@ async function interpretPatterns(input) {
|
|
|
1889
2295
|
function buildInterpretationPrompt(input) {
|
|
1890
2296
|
const signalSummary = formatSignalSummary(input.signals);
|
|
1891
2297
|
const eventSample = formatEventSample(input.events, 30);
|
|
1892
|
-
const canonicalList = (
|
|
1893
|
-
|
|
2298
|
+
const canonicalList = formatDeclaredVocabulary(
|
|
2299
|
+
input.declaredVocabulary,
|
|
2300
|
+
input.canonicalPatterns ?? []
|
|
2301
|
+
);
|
|
1894
2302
|
const compressedWorld = compressWorldmodel(input.worldmodelContent);
|
|
1895
2303
|
const cl = compressLens(input.lens);
|
|
1896
2304
|
const frame = input.lens.primary_frame;
|
|
@@ -2001,6 +2409,44 @@ Only recommend a move when the evidence actually calls for one.
|
|
|
2001
2409
|
Do NOT use these phrases anywhere in your output:
|
|
2002
2410
|
${forbiddenList}`;
|
|
2003
2411
|
}
|
|
2412
|
+
function formatDeclaredVocabulary(vocabulary, extraNames) {
|
|
2413
|
+
const aligned = vocabulary?.aligned ?? [];
|
|
2414
|
+
const drift = vocabulary?.drift ?? [];
|
|
2415
|
+
if (aligned.length === 0 && drift.length === 0 && extraNames.length === 0) {
|
|
2416
|
+
return 'No patterns have been named yet. Everything you observe is new \u2014 mark it type: "candidate".';
|
|
2417
|
+
}
|
|
2418
|
+
const parts = [];
|
|
2419
|
+
parts.push("## Declared vocabulary (use these names when you see matching evidence)");
|
|
2420
|
+
parts.push("");
|
|
2421
|
+
parts.push(
|
|
2422
|
+
'The worldmodel declares the patterns below. If your observation matches one of these, use the EXACT snake_case name shown and mark type: "canonical" \u2014 do not invent a new name for something that already has one.'
|
|
2423
|
+
);
|
|
2424
|
+
parts.push("");
|
|
2425
|
+
if (aligned.length > 0) {
|
|
2426
|
+
parts.push("### Aligned behaviors (positive patterns)");
|
|
2427
|
+
for (const p of aligned) {
|
|
2428
|
+
parts.push(`- \`${p.name}\` \u2014 ${p.prose}`);
|
|
2429
|
+
}
|
|
2430
|
+
parts.push("");
|
|
2431
|
+
}
|
|
2432
|
+
if (drift.length > 0) {
|
|
2433
|
+
parts.push("### Drift behaviors (negative patterns)");
|
|
2434
|
+
for (const p of drift) {
|
|
2435
|
+
parts.push(`- \`${p.name}\` \u2014 ${p.prose}`);
|
|
2436
|
+
}
|
|
2437
|
+
parts.push("");
|
|
2438
|
+
}
|
|
2439
|
+
if (extraNames.length > 0) {
|
|
2440
|
+
parts.push(
|
|
2441
|
+
`Additional canonical names (from prior runs or caller): ${extraNames.join(", ")}`
|
|
2442
|
+
);
|
|
2443
|
+
parts.push("");
|
|
2444
|
+
}
|
|
2445
|
+
parts.push(
|
|
2446
|
+
'If you observe something genuinely new that matches NO declared pattern, mark it type: "candidate" with a freshly-invented snake_case name.'
|
|
2447
|
+
);
|
|
2448
|
+
return parts.join("\n");
|
|
2449
|
+
}
|
|
2004
2450
|
function formatSignalSummary(signals) {
|
|
2005
2451
|
const lines = [];
|
|
2006
2452
|
const domains = ["life", "cyber", "joint"];
|
|
@@ -2026,7 +2472,7 @@ function formatEventSample(events, maxEvents) {
|
|
|
2026
2472
|
"${content}"`;
|
|
2027
2473
|
}).join("\n");
|
|
2028
2474
|
}
|
|
2029
|
-
function parseInterpretation(raw, canonicalNames) {
|
|
2475
|
+
function parseInterpretation(raw, canonicalNames, vocabulary) {
|
|
2030
2476
|
let meaning = "";
|
|
2031
2477
|
let move = "";
|
|
2032
2478
|
let patternsArray = [];
|
|
@@ -2056,14 +2502,23 @@ function parseInterpretation(raw, canonicalNames) {
|
|
|
2056
2502
|
const patterns = [];
|
|
2057
2503
|
for (const item of patternsArray) {
|
|
2058
2504
|
if (!isPatternLike(item)) continue;
|
|
2059
|
-
const
|
|
2505
|
+
const rawName = String(item.name ?? "unnamed");
|
|
2506
|
+
const description = String(item.description ?? "");
|
|
2060
2507
|
const ev = item.evidence;
|
|
2061
|
-
|
|
2508
|
+
let name = rawName;
|
|
2509
|
+
let isCanonical = item.type === "canonical" || canonicalSet.has(rawName.toLowerCase());
|
|
2510
|
+
if (!isCanonical && vocabulary) {
|
|
2511
|
+
const matched = matchDeclaredPattern(rawName, description, vocabulary);
|
|
2512
|
+
if (matched) {
|
|
2513
|
+
name = matched.name;
|
|
2514
|
+
isCanonical = true;
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2062
2517
|
patterns.push({
|
|
2063
|
-
name
|
|
2518
|
+
name,
|
|
2064
2519
|
type: isCanonical ? "canonical" : "candidate",
|
|
2065
|
-
declaredAs: isCanonical ?
|
|
2066
|
-
description
|
|
2520
|
+
declaredAs: isCanonical ? name : void 0,
|
|
2521
|
+
description,
|
|
2067
2522
|
evidence: {
|
|
2068
2523
|
signals: Array.isArray(ev?.signals) ? ev.signals.map(String) : [],
|
|
2069
2524
|
events: Array.isArray(ev?.events) ? ev.events.map(String) : [],
|
|
@@ -2386,10 +2841,24 @@ Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
|
2386
2841
|
} catch {
|
|
2387
2842
|
}
|
|
2388
2843
|
}
|
|
2844
|
+
const linearKey = process.env.LINEAR_API_KEY;
|
|
2845
|
+
if (linearKey) {
|
|
2846
|
+
try {
|
|
2847
|
+
const linear = await fetchLinearActivity(linearKey, { windowDays });
|
|
2848
|
+
events.push(...linear.events);
|
|
2849
|
+
adapterSignals += "\n\n" + formatLinearSignalsForPrompt(linear.signals);
|
|
2850
|
+
activeAdapters.push("linear");
|
|
2851
|
+
} catch {
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2389
2854
|
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
2855
|
+
if (input.personalUser) {
|
|
2856
|
+
events = filterEventsByUser(events, input.personalUser);
|
|
2857
|
+
}
|
|
2390
2858
|
const classified = classifyEvents(events);
|
|
2391
2859
|
const signals = extractSignals(classified);
|
|
2392
2860
|
const scores = computeScores(signals, input.worldmodelContent !== "");
|
|
2861
|
+
const declaredVocabulary = extractDeclaredVocabulary(worldmodelContent);
|
|
2393
2862
|
const { patterns, meaning, move } = await interpretPatterns({
|
|
2394
2863
|
signals,
|
|
2395
2864
|
events: classified,
|
|
@@ -2397,6 +2866,7 @@ Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
|
2397
2866
|
lens,
|
|
2398
2867
|
ai: input.ai,
|
|
2399
2868
|
canonicalPatterns: input.canonicalPatterns,
|
|
2869
|
+
declaredVocabulary,
|
|
2400
2870
|
statedIntent: [statedIntent, adapterSignals, priorReadContext].filter(Boolean).join("\n\n") || void 0
|
|
2401
2871
|
});
|
|
2402
2872
|
const rewrittenPatterns = patterns.map((p) => lens.rewrite(p));
|
|
@@ -2448,9 +2918,14 @@ Compare stated intent against actual GitHub activity. Gaps = drift.`;
|
|
|
2448
2918
|
scores,
|
|
2449
2919
|
eventCount: events.length,
|
|
2450
2920
|
activeAdapters,
|
|
2451
|
-
worldStack
|
|
2921
|
+
worldStack,
|
|
2922
|
+
governance
|
|
2452
2923
|
};
|
|
2453
2924
|
}
|
|
2925
|
+
function filterEventsByUser(events, username) {
|
|
2926
|
+
const target = username.toLowerCase();
|
|
2927
|
+
return events.filter((e) => e.actor.id.toLowerCase() === target);
|
|
2928
|
+
}
|
|
2454
2929
|
function computeScores(signals, worldmodelLoaded) {
|
|
2455
2930
|
const gate = DEFAULT_EVIDENCE_GATE;
|
|
2456
2931
|
const lifeSignals = signals.filter((s) => s.domain === "life");
|
|
@@ -2560,6 +3035,8 @@ export {
|
|
|
2560
3035
|
formatSlackSignalsForPrompt,
|
|
2561
3036
|
fetchNotionActivity,
|
|
2562
3037
|
formatNotionSignalsForPrompt,
|
|
3038
|
+
fetchLinearActivity,
|
|
3039
|
+
formatLinearSignalsForPrompt,
|
|
2563
3040
|
readOriginRemote,
|
|
2564
3041
|
parseRemoteUrl,
|
|
2565
3042
|
getRepoOrigin,
|
|
@@ -2586,6 +3063,8 @@ export {
|
|
|
2586
3063
|
classifyEvents,
|
|
2587
3064
|
extractSignals,
|
|
2588
3065
|
DEFAULT_SIGNAL_EXTRACTORS,
|
|
3066
|
+
extractDeclaredVocabulary,
|
|
3067
|
+
matchDeclaredPattern,
|
|
2589
3068
|
DEFAULT_EVIDENCE_GATE,
|
|
2590
3069
|
isScored,
|
|
2591
3070
|
isSentinel,
|
|
@@ -2598,6 +3077,7 @@ export {
|
|
|
2598
3077
|
interpretPatterns,
|
|
2599
3078
|
render,
|
|
2600
3079
|
emergent,
|
|
3080
|
+
filterEventsByUser,
|
|
2601
3081
|
createAnthropicAI,
|
|
2602
3082
|
createMockAI
|
|
2603
3083
|
};
|