@neuroverseos/governance 0.6.1 → 0.8.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 +43 -7
- package/dist/{chunk-AEVT7DSZ.js → chunk-MC6O5GV5.js} +486 -12
- package/dist/cli/neuroverse.cjs +868 -142
- package/dist/cli/radiant.cjs +2173 -149
- package/dist/cli/radiant.js +29 -4
- package/dist/radiant/index.cjs +2083 -13
- package/dist/radiant/index.d.cts +392 -21
- package/dist/radiant/index.d.ts +392 -21
- package/dist/radiant/index.js +426 -3
- package/dist/server-DFNY5N5A.js +271 -0
- package/package.json +1 -1
package/dist/radiant/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/radiant/index.ts
|
|
@@ -24,35 +34,51 @@ __export(radiant_exports, {
|
|
|
24
34
|
DEFAULT_SIGNAL_EXTRACTORS: () => DEFAULT_SIGNAL_EXTRACTORS,
|
|
25
35
|
LENSES: () => LENSES,
|
|
26
36
|
RADIANT_PACKAGE_VERSION: () => RADIANT_PACKAGE_VERSION,
|
|
37
|
+
auditGovernance: () => auditGovernance,
|
|
27
38
|
aukiBuilderLens: () => aukiBuilderLens,
|
|
28
39
|
checkForbiddenPhrases: () => checkForbiddenPhrases,
|
|
29
40
|
classifyActorDomain: () => classifyActorDomain,
|
|
30
41
|
classifyEvents: () => classifyEvents,
|
|
31
42
|
composeSystemPrompt: () => composeSystemPrompt,
|
|
43
|
+
computePersistence: () => computePersistence,
|
|
32
44
|
createAnthropicAI: () => createAnthropicAI,
|
|
33
45
|
createMockAI: () => createMockAI,
|
|
34
46
|
createMockGitHubAdapter: () => createMockGitHubAdapter,
|
|
35
47
|
emergent: () => emergent,
|
|
36
48
|
extractSignals: () => extractSignals,
|
|
49
|
+
fetchDiscordActivity: () => fetchDiscordActivity,
|
|
37
50
|
fetchGitHubActivity: () => fetchGitHubActivity,
|
|
51
|
+
fetchGitHubOrgActivity: () => fetchGitHubOrgActivity,
|
|
52
|
+
fetchNotionActivity: () => fetchNotionActivity,
|
|
53
|
+
fetchSlackActivity: () => fetchSlackActivity,
|
|
54
|
+
formatDiscordSignalsForPrompt: () => formatDiscordSignalsForPrompt,
|
|
38
55
|
formatExocortexForPrompt: () => formatExocortexForPrompt,
|
|
56
|
+
formatNotionSignalsForPrompt: () => formatNotionSignalsForPrompt,
|
|
57
|
+
formatPriorReadsForPrompt: () => formatPriorReadsForPrompt,
|
|
39
58
|
formatScope: () => formatScope,
|
|
59
|
+
formatSlackSignalsForPrompt: () => formatSlackSignalsForPrompt,
|
|
60
|
+
formatTeamExocorticesForPrompt: () => formatTeamExocorticesForPrompt,
|
|
40
61
|
getLens: () => getLens,
|
|
41
62
|
interpretPatterns: () => interpretPatterns,
|
|
42
63
|
isPresent: () => isPresent,
|
|
43
64
|
isScored: () => isScored,
|
|
44
65
|
isSentinel: () => isSentinel,
|
|
45
66
|
listLenses: () => listLenses,
|
|
67
|
+
loadPriorReads: () => loadPriorReads,
|
|
46
68
|
parseRepoScope: () => parseRepoScope,
|
|
69
|
+
parseScope: () => parseScope,
|
|
47
70
|
presenceAverage: () => presenceAverage,
|
|
48
71
|
readExocortex: () => readExocortex,
|
|
72
|
+
readTeamExocortices: () => readTeamExocortices,
|
|
49
73
|
render: () => render,
|
|
50
74
|
scoreComposite: () => scoreComposite,
|
|
51
75
|
scoreCyber: () => scoreCyber,
|
|
52
76
|
scoreLife: () => scoreLife,
|
|
53
77
|
scoreNeuroVerse: () => scoreNeuroVerse,
|
|
54
78
|
summarizeExocortex: () => summarizeExocortex,
|
|
55
|
-
think: () => think
|
|
79
|
+
think: () => think,
|
|
80
|
+
updateKnowledge: () => updateKnowledge,
|
|
81
|
+
writeRead: () => writeRead
|
|
56
82
|
});
|
|
57
83
|
module.exports = __toCommonJS(radiant_exports);
|
|
58
84
|
|
|
@@ -831,17 +857,30 @@ function createMockAI(fixedResponse) {
|
|
|
831
857
|
}
|
|
832
858
|
|
|
833
859
|
// src/radiant/core/scopes.ts
|
|
834
|
-
function
|
|
860
|
+
function parseScope(scope) {
|
|
835
861
|
const cleaned = scope.replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\.git$/, "").replace(/\/$/, "");
|
|
836
|
-
const parts = cleaned.split("/");
|
|
837
|
-
if (parts.length
|
|
862
|
+
const parts = cleaned.split("/").filter(Boolean);
|
|
863
|
+
if (parts.length === 0 || !parts[0]) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
`Cannot parse scope: "${scope}". Expected "owner/repo" or "owner".`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
if (parts.length === 1) {
|
|
869
|
+
return { type: "org", owner: parts[0] };
|
|
870
|
+
}
|
|
871
|
+
return { type: "repo", owner: parts[0], repo: parts[1] };
|
|
872
|
+
}
|
|
873
|
+
function parseRepoScope(scope) {
|
|
874
|
+
const parsed = parseScope(scope);
|
|
875
|
+
if (parsed.type === "org") {
|
|
838
876
|
throw new Error(
|
|
839
|
-
`
|
|
877
|
+
`Expected "owner/repo" but got org-level scope "${parsed.owner}". Use parseScope() for org-level.`
|
|
840
878
|
);
|
|
841
879
|
}
|
|
842
|
-
return
|
|
880
|
+
return parsed;
|
|
843
881
|
}
|
|
844
882
|
function formatScope(scope) {
|
|
883
|
+
if (scope.type === "org") return `${scope.owner} (org)`;
|
|
845
884
|
return `${scope.owner}/${scope.repo}`;
|
|
846
885
|
}
|
|
847
886
|
|
|
@@ -1025,6 +1064,40 @@ async function fetchJSON(url, headers) {
|
|
|
1025
1064
|
}
|
|
1026
1065
|
return await res.json();
|
|
1027
1066
|
}
|
|
1067
|
+
async function fetchGitHubOrgActivity(scope, token, options = {}) {
|
|
1068
|
+
const perPage = options.perPage ?? 100;
|
|
1069
|
+
const headers = {
|
|
1070
|
+
Authorization: `token ${token}`,
|
|
1071
|
+
Accept: "application/vnd.github.v3+json",
|
|
1072
|
+
"User-Agent": "neuroverseos-radiant"
|
|
1073
|
+
};
|
|
1074
|
+
const repos = await fetchJSON(
|
|
1075
|
+
`https://api.github.com/orgs/${scope.owner}/repos?sort=pushed&direction=desc&per_page=${perPage}`,
|
|
1076
|
+
headers
|
|
1077
|
+
);
|
|
1078
|
+
const windowDays = options.windowDays ?? 14;
|
|
1079
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1080
|
+
const activeRepos = repos.filter(
|
|
1081
|
+
(r) => new Date(r.pushed_at) >= since
|
|
1082
|
+
);
|
|
1083
|
+
const cappedRepos = activeRepos.slice(0, 10);
|
|
1084
|
+
const allEvents = [];
|
|
1085
|
+
const repoNames = [];
|
|
1086
|
+
for (const repo of cappedRepos) {
|
|
1087
|
+
const [owner, repoName] = repo.full_name.split("/");
|
|
1088
|
+
try {
|
|
1089
|
+
const repoScope = { type: "repo", owner, repo: repoName };
|
|
1090
|
+
const events = await fetchGitHubActivity(repoScope, token, options);
|
|
1091
|
+
allEvents.push(...events);
|
|
1092
|
+
if (events.length > 0) repoNames.push(repo.full_name);
|
|
1093
|
+
} catch {
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
allEvents.sort(
|
|
1097
|
+
(a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp)
|
|
1098
|
+
);
|
|
1099
|
+
return { events: allEvents, repos: repoNames };
|
|
1100
|
+
}
|
|
1028
1101
|
function createMockGitHubAdapter(fixedEvents) {
|
|
1029
1102
|
return async () => fixedEvents;
|
|
1030
1103
|
}
|
|
@@ -1101,6 +1174,46 @@ ${ctx.methods}`);
|
|
|
1101
1174
|
}
|
|
1102
1175
|
return sections.join("\n\n");
|
|
1103
1176
|
}
|
|
1177
|
+
function readTeamExocortices(teamDir) {
|
|
1178
|
+
const dir = (0, import_path.resolve)(teamDir);
|
|
1179
|
+
if (!(0, import_fs.existsSync)(dir)) return [];
|
|
1180
|
+
const entries = (0, import_fs.readdirSync)(dir);
|
|
1181
|
+
const results = [];
|
|
1182
|
+
for (const entry of entries) {
|
|
1183
|
+
const entryPath = (0, import_path.join)(dir, entry);
|
|
1184
|
+
try {
|
|
1185
|
+
const stat = (0, import_fs.statSync)(entryPath);
|
|
1186
|
+
if (stat.isDirectory()) {
|
|
1187
|
+
const ctx = readExocortex(entryPath);
|
|
1188
|
+
if (ctx.filesLoaded > 0) {
|
|
1189
|
+
results.push({ name: entry, context: ctx });
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
} catch {
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return results;
|
|
1196
|
+
}
|
|
1197
|
+
function formatTeamExocorticesForPrompt(team) {
|
|
1198
|
+
if (team.length === 0) return "";
|
|
1199
|
+
const sections = [
|
|
1200
|
+
"## Team Intent (cross-exocortex read)",
|
|
1201
|
+
"",
|
|
1202
|
+
`Reading ${team.length} team members' exocortices. Compare each person's`,
|
|
1203
|
+
"stated intent against the observed activity AND against each other.",
|
|
1204
|
+
"Surface: duplicate focus, missing coverage, silent pivots,",
|
|
1205
|
+
"and areas where no one is carrying the work.",
|
|
1206
|
+
""
|
|
1207
|
+
];
|
|
1208
|
+
for (const { name, context } of team) {
|
|
1209
|
+
sections.push(`### ${name}`);
|
|
1210
|
+
if (context.attention) sections.push(`**Attention:** ${context.attention.split("\n")[0]}`);
|
|
1211
|
+
if (context.goals) sections.push(`**Goals:** ${context.goals.split("\n").slice(0, 3).join("; ")}`);
|
|
1212
|
+
if (context.sprint) sections.push(`**Sprint:** ${context.sprint.split("\n").slice(0, 3).join("; ")}`);
|
|
1213
|
+
sections.push("");
|
|
1214
|
+
}
|
|
1215
|
+
return sections.join("\n");
|
|
1216
|
+
}
|
|
1104
1217
|
function summarizeExocortex(ctx) {
|
|
1105
1218
|
if (ctx.filesLoaded === 0) return "no exocortex files found";
|
|
1106
1219
|
const loaded = [];
|
|
@@ -1113,6 +1226,400 @@ function summarizeExocortex(ctx) {
|
|
|
1113
1226
|
return `${loaded.join(", ")} (${ctx.filesLoaded} files)`;
|
|
1114
1227
|
}
|
|
1115
1228
|
|
|
1229
|
+
// src/radiant/adapters/discord.ts
|
|
1230
|
+
async function fetchDiscordActivity(guildId, token, options = {}) {
|
|
1231
|
+
const windowDays = options.windowDays ?? 14;
|
|
1232
|
+
const perChannel = options.perChannel ?? 100;
|
|
1233
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1234
|
+
const headers = {
|
|
1235
|
+
Authorization: `Bot ${token}`,
|
|
1236
|
+
"Content-Type": "application/json"
|
|
1237
|
+
};
|
|
1238
|
+
const channels = await fetchJSON2(
|
|
1239
|
+
`https://discord.com/api/v10/guilds/${guildId}/channels`,
|
|
1240
|
+
headers
|
|
1241
|
+
);
|
|
1242
|
+
const textChannels = channels.filter((c) => {
|
|
1243
|
+
if (c.type !== 0) return false;
|
|
1244
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
1245
|
+
return options.channelIds.includes(c.id);
|
|
1246
|
+
}
|
|
1247
|
+
if (options.visibility === "public") {
|
|
1248
|
+
return !c.name.startsWith("private-") && !c.nsfw;
|
|
1249
|
+
}
|
|
1250
|
+
return true;
|
|
1251
|
+
});
|
|
1252
|
+
const events = [];
|
|
1253
|
+
let totalMessages = 0;
|
|
1254
|
+
let helpRequests = 0;
|
|
1255
|
+
let unresolvedThreads = 0;
|
|
1256
|
+
let newcomerMessages = 0;
|
|
1257
|
+
const responseTimes = [];
|
|
1258
|
+
const participants = /* @__PURE__ */ new Set();
|
|
1259
|
+
const knownParticipants = /* @__PURE__ */ new Set();
|
|
1260
|
+
const topicCounts = /* @__PURE__ */ new Map();
|
|
1261
|
+
for (const channel of textChannels.slice(0, 15)) {
|
|
1262
|
+
try {
|
|
1263
|
+
const messages = await fetchJSON2(
|
|
1264
|
+
`https://discord.com/api/v10/channels/${channel.id}/messages?limit=${perChannel}`,
|
|
1265
|
+
headers
|
|
1266
|
+
);
|
|
1267
|
+
const inWindow = messages.filter(
|
|
1268
|
+
(m) => new Date(m.timestamp) >= since
|
|
1269
|
+
);
|
|
1270
|
+
totalMessages += inWindow.length;
|
|
1271
|
+
const topic = channel.name.replace(/-/g, " ");
|
|
1272
|
+
topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + inWindow.length);
|
|
1273
|
+
for (const msg of inWindow) {
|
|
1274
|
+
const actor = mapDiscordUser(msg.author);
|
|
1275
|
+
participants.add(actor.id);
|
|
1276
|
+
const lowerContent = msg.content.toLowerCase();
|
|
1277
|
+
if (lowerContent.includes("help") || lowerContent.includes("stuck") || lowerContent.includes("how do i") || lowerContent.includes("anyone know")) {
|
|
1278
|
+
helpRequests++;
|
|
1279
|
+
}
|
|
1280
|
+
if (msg.referenced_message) {
|
|
1281
|
+
const refTime = new Date(msg.referenced_message.timestamp).getTime();
|
|
1282
|
+
const msgTime = new Date(msg.timestamp).getTime();
|
|
1283
|
+
const diffMinutes = (msgTime - refTime) / 6e4;
|
|
1284
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
1285
|
+
responseTimes.push(diffMinutes);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
events.push({
|
|
1289
|
+
id: `discord-${msg.id}`,
|
|
1290
|
+
timestamp: msg.timestamp,
|
|
1291
|
+
actor,
|
|
1292
|
+
kind: "discord_message",
|
|
1293
|
+
content: msg.content.slice(0, 500),
|
|
1294
|
+
respondsTo: msg.referenced_message ? {
|
|
1295
|
+
eventId: `discord-${msg.referenced_message.id}`,
|
|
1296
|
+
actor: mapDiscordUser(msg.referenced_message.author)
|
|
1297
|
+
} : void 0,
|
|
1298
|
+
metadata: {
|
|
1299
|
+
channel: channel.name,
|
|
1300
|
+
guildId
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
} catch {
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
const avgResponseMinutes = responseTimes.length > 0 ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length : null;
|
|
1308
|
+
const topTopics = [...topicCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t]) => t);
|
|
1309
|
+
const signals = {
|
|
1310
|
+
totalMessages,
|
|
1311
|
+
activeChannels: textChannels.length,
|
|
1312
|
+
uniqueParticipants: participants.size,
|
|
1313
|
+
avgResponseMinutes: avgResponseMinutes ? Math.round(avgResponseMinutes) : null,
|
|
1314
|
+
helpRequests,
|
|
1315
|
+
unresolvedThreads,
|
|
1316
|
+
topTopics,
|
|
1317
|
+
newcomerMessages
|
|
1318
|
+
};
|
|
1319
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1320
|
+
return { events, signals };
|
|
1321
|
+
}
|
|
1322
|
+
function formatDiscordSignalsForPrompt(signals) {
|
|
1323
|
+
if (signals.totalMessages === 0) return "";
|
|
1324
|
+
const lines = [
|
|
1325
|
+
"## Discord Activity (conversational behavior)",
|
|
1326
|
+
"",
|
|
1327
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
1328
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
1329
|
+
];
|
|
1330
|
+
if (signals.avgResponseMinutes !== null) {
|
|
1331
|
+
lines.push(`Average response time: ${signals.avgResponseMinutes} minutes.`);
|
|
1332
|
+
}
|
|
1333
|
+
if (signals.helpRequests > 0) {
|
|
1334
|
+
lines.push(`${signals.helpRequests} help requests detected.`);
|
|
1335
|
+
}
|
|
1336
|
+
if (signals.unresolvedThreads > 0) {
|
|
1337
|
+
lines.push(`${signals.unresolvedThreads} unresolved threads.`);
|
|
1338
|
+
}
|
|
1339
|
+
if (signals.topTopics.length > 0) {
|
|
1340
|
+
lines.push(`Top discussion topics: ${signals.topTopics.join(", ")}.`);
|
|
1341
|
+
}
|
|
1342
|
+
lines.push("");
|
|
1343
|
+
lines.push("Compare conversational activity against GitHub shipping activity.");
|
|
1344
|
+
lines.push("Where debates happen in Discord but nothing ships in GitHub, name the gap.");
|
|
1345
|
+
lines.push("Where work ships in GitHub but nobody discusses it in Discord, name the visibility gap.");
|
|
1346
|
+
return lines.join("\n");
|
|
1347
|
+
}
|
|
1348
|
+
function mapDiscordUser(user) {
|
|
1349
|
+
return {
|
|
1350
|
+
id: user.username,
|
|
1351
|
+
kind: user.bot ? "bot" : "human",
|
|
1352
|
+
name: user.username
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
async function fetchJSON2(url, headers) {
|
|
1356
|
+
const res = await fetch(url, { headers });
|
|
1357
|
+
if (!res.ok) {
|
|
1358
|
+
if (res.status === 404 || res.status === 403) return [];
|
|
1359
|
+
throw new Error(`Discord API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
1360
|
+
}
|
|
1361
|
+
return await res.json();
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// src/radiant/adapters/slack.ts
|
|
1365
|
+
async function fetchSlackActivity(token, options = {}) {
|
|
1366
|
+
const windowDays = options.windowDays ?? 14;
|
|
1367
|
+
const perChannel = options.perChannel ?? 100;
|
|
1368
|
+
const oldest = String(
|
|
1369
|
+
Math.floor((Date.now() - windowDays * 24 * 60 * 60 * 1e3) / 1e3)
|
|
1370
|
+
);
|
|
1371
|
+
const headers = {
|
|
1372
|
+
Authorization: `Bearer ${token}`,
|
|
1373
|
+
"Content-Type": "application/json"
|
|
1374
|
+
};
|
|
1375
|
+
const channelsResponse = await fetchSlackAPI("https://slack.com/api/conversations.list?types=public_channel&limit=200", headers);
|
|
1376
|
+
let channels = channelsResponse.channels ?? [];
|
|
1377
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
1378
|
+
const ids = new Set(options.channelIds);
|
|
1379
|
+
channels = channels.filter((c) => ids.has(c.id));
|
|
1380
|
+
}
|
|
1381
|
+
if (options.visibility === "public") {
|
|
1382
|
+
channels = channels.filter((c) => !c.is_private && !c.is_archived);
|
|
1383
|
+
}
|
|
1384
|
+
const events = [];
|
|
1385
|
+
let totalMessages = 0;
|
|
1386
|
+
let reactionCount = 0;
|
|
1387
|
+
let unresolvedThreads = 0;
|
|
1388
|
+
const responseTimes = [];
|
|
1389
|
+
const participants = /* @__PURE__ */ new Set();
|
|
1390
|
+
const externalParticipants = /* @__PURE__ */ new Set();
|
|
1391
|
+
const channelMessageCounts = /* @__PURE__ */ new Map();
|
|
1392
|
+
for (const channel of channels.slice(0, 15)) {
|
|
1393
|
+
try {
|
|
1394
|
+
const historyResponse = await fetchSlackAPI(
|
|
1395
|
+
`https://slack.com/api/conversations.history?channel=${channel.id}&limit=${perChannel}&oldest=${oldest}`,
|
|
1396
|
+
headers
|
|
1397
|
+
);
|
|
1398
|
+
const messages = historyResponse.messages ?? [];
|
|
1399
|
+
totalMessages += messages.length;
|
|
1400
|
+
channelMessageCounts.set(channel.name, messages.length);
|
|
1401
|
+
for (const msg of messages) {
|
|
1402
|
+
if (msg.subtype === "channel_join" || msg.subtype === "channel_leave") continue;
|
|
1403
|
+
const actor = mapSlackUser(msg.user ?? "unknown");
|
|
1404
|
+
participants.add(actor.id);
|
|
1405
|
+
if (msg.reactions) {
|
|
1406
|
+
reactionCount += msg.reactions.reduce(
|
|
1407
|
+
(sum, r) => sum + (r.count ?? 0),
|
|
1408
|
+
0
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
if (msg.thread_ts && msg.thread_ts !== msg.ts) {
|
|
1412
|
+
const parentTs = parseFloat(msg.thread_ts) * 1e3;
|
|
1413
|
+
const msgTs = parseFloat(msg.ts) * 1e3;
|
|
1414
|
+
const diffMinutes = (msgTs - parentTs) / 6e4;
|
|
1415
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
1416
|
+
responseTimes.push(diffMinutes);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
if (msg.thread_ts === msg.ts && (!msg.reply_count || msg.reply_count === 0)) {
|
|
1420
|
+
if (msg.text && (msg.text.includes("?") || msg.text.toLowerCase().includes("help"))) {
|
|
1421
|
+
unresolvedThreads++;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const timestamp = new Date(parseFloat(msg.ts) * 1e3).toISOString();
|
|
1425
|
+
events.push({
|
|
1426
|
+
id: `slack-${msg.ts}`,
|
|
1427
|
+
timestamp,
|
|
1428
|
+
actor,
|
|
1429
|
+
kind: "slack_message",
|
|
1430
|
+
content: (msg.text ?? "").slice(0, 500),
|
|
1431
|
+
respondsTo: msg.thread_ts && msg.thread_ts !== msg.ts ? {
|
|
1432
|
+
eventId: `slack-${msg.thread_ts}`,
|
|
1433
|
+
actor: { id: "thread-parent", kind: "unknown" }
|
|
1434
|
+
} : void 0,
|
|
1435
|
+
metadata: {
|
|
1436
|
+
channel: channel.name,
|
|
1437
|
+
isPrivate: channel.is_private,
|
|
1438
|
+
hasReactions: (msg.reactions?.length ?? 0) > 0
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
} catch {
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const avgResponseMinutes = responseTimes.length > 0 ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length) : null;
|
|
1446
|
+
const topChannels = [...channelMessageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
|
|
1447
|
+
const signals = {
|
|
1448
|
+
totalMessages,
|
|
1449
|
+
activeChannels: channelMessageCounts.size,
|
|
1450
|
+
uniqueParticipants: participants.size,
|
|
1451
|
+
avgResponseMinutes,
|
|
1452
|
+
externalParticipants: externalParticipants.size,
|
|
1453
|
+
unresolvedThreads,
|
|
1454
|
+
topChannels,
|
|
1455
|
+
reactionCount
|
|
1456
|
+
};
|
|
1457
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1458
|
+
return { events, signals };
|
|
1459
|
+
}
|
|
1460
|
+
function formatSlackSignalsForPrompt(signals) {
|
|
1461
|
+
if (signals.totalMessages === 0) return "";
|
|
1462
|
+
const lines = [
|
|
1463
|
+
"## Slack Activity (external coordination)",
|
|
1464
|
+
"",
|
|
1465
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
1466
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
1467
|
+
];
|
|
1468
|
+
if (signals.avgResponseMinutes !== null) {
|
|
1469
|
+
lines.push(`Average thread response time: ${signals.avgResponseMinutes} minutes.`);
|
|
1470
|
+
}
|
|
1471
|
+
if (signals.unresolvedThreads > 0) {
|
|
1472
|
+
lines.push(`${signals.unresolvedThreads} questions/threads with no reply.`);
|
|
1473
|
+
}
|
|
1474
|
+
if (signals.reactionCount > 0) {
|
|
1475
|
+
lines.push(`${signals.reactionCount} reactions (engagement signal).`);
|
|
1476
|
+
}
|
|
1477
|
+
if (signals.topChannels.length > 0) {
|
|
1478
|
+
lines.push(`Most active channels: ${signals.topChannels.join(", ")}.`);
|
|
1479
|
+
}
|
|
1480
|
+
lines.push("");
|
|
1481
|
+
lines.push("Slack carries external coordination \u2014 partner and client communication.");
|
|
1482
|
+
lines.push("Compare partner engagement against internal activity. Where partners are");
|
|
1483
|
+
lines.push("active but internal follow-through is low, name the gap.");
|
|
1484
|
+
return lines.join("\n");
|
|
1485
|
+
}
|
|
1486
|
+
function mapSlackUser(userId) {
|
|
1487
|
+
return {
|
|
1488
|
+
id: userId,
|
|
1489
|
+
kind: "human",
|
|
1490
|
+
name: userId
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
async function fetchSlackAPI(url, headers) {
|
|
1494
|
+
const res = await fetch(url, { headers });
|
|
1495
|
+
if (!res.ok) {
|
|
1496
|
+
throw new Error(`Slack API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
1497
|
+
}
|
|
1498
|
+
const data = await res.json();
|
|
1499
|
+
if (!data.ok) {
|
|
1500
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
1501
|
+
}
|
|
1502
|
+
return data;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// src/radiant/adapters/notion.ts
|
|
1506
|
+
async function fetchNotionActivity(token, options = {}) {
|
|
1507
|
+
const windowDays = options.windowDays ?? 14;
|
|
1508
|
+
const maxPages = options.maxPages ?? 100;
|
|
1509
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
1510
|
+
const headers = {
|
|
1511
|
+
Authorization: `Bearer ${token}`,
|
|
1512
|
+
"Notion-Version": "2022-06-28",
|
|
1513
|
+
"Content-Type": "application/json"
|
|
1514
|
+
};
|
|
1515
|
+
const searchResponse = await fetchNotionAPI("https://api.notion.com/v1/search", headers, {
|
|
1516
|
+
method: "POST",
|
|
1517
|
+
body: JSON.stringify({
|
|
1518
|
+
filter: { property: "object", value: "page" },
|
|
1519
|
+
sort: { direction: "descending", timestamp: "last_edited_time" },
|
|
1520
|
+
page_size: maxPages
|
|
1521
|
+
})
|
|
1522
|
+
});
|
|
1523
|
+
const pages = searchResponse.results ?? [];
|
|
1524
|
+
const events = [];
|
|
1525
|
+
const editors = /* @__PURE__ */ new Set();
|
|
1526
|
+
let pagesCreated = 0;
|
|
1527
|
+
let pagesUpdated = 0;
|
|
1528
|
+
let stalePages = 0;
|
|
1529
|
+
const editAges = [];
|
|
1530
|
+
const topPages = [];
|
|
1531
|
+
const now = Date.now();
|
|
1532
|
+
for (const page of pages) {
|
|
1533
|
+
const lastEdited = new Date(page.last_edited_time);
|
|
1534
|
+
const created = new Date(page.created_time);
|
|
1535
|
+
const daysSinceEdit = (now - lastEdited.getTime()) / (24 * 60 * 60 * 1e3);
|
|
1536
|
+
editAges.push(daysSinceEdit);
|
|
1537
|
+
if (daysSinceEdit > 30) stalePages++;
|
|
1538
|
+
const title = extractTitle(page);
|
|
1539
|
+
const editorId = page.last_edited_by?.id ?? "unknown";
|
|
1540
|
+
editors.add(editorId);
|
|
1541
|
+
if (lastEdited >= since) {
|
|
1542
|
+
const isNew = created >= since;
|
|
1543
|
+
if (isNew) pagesCreated++;
|
|
1544
|
+
else pagesUpdated++;
|
|
1545
|
+
topPages.push({ title, editedAt: page.last_edited_time });
|
|
1546
|
+
events.push({
|
|
1547
|
+
id: `notion-${page.id}`,
|
|
1548
|
+
timestamp: page.last_edited_time,
|
|
1549
|
+
actor: {
|
|
1550
|
+
id: editorId,
|
|
1551
|
+
kind: "human",
|
|
1552
|
+
name: editorId
|
|
1553
|
+
},
|
|
1554
|
+
kind: isNew ? "doc_created" : "doc_updated",
|
|
1555
|
+
content: `${isNew ? "Created" : "Updated"}: ${title}`,
|
|
1556
|
+
metadata: {
|
|
1557
|
+
pageId: page.id,
|
|
1558
|
+
url: page.url,
|
|
1559
|
+
createdAt: page.created_time
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const avgDaysSinceEdit = editAges.length > 0 ? Math.round(editAges.reduce((a, b) => a + b, 0) / editAges.length) : null;
|
|
1565
|
+
const signals = {
|
|
1566
|
+
pagesActive: pagesCreated + pagesUpdated,
|
|
1567
|
+
pagesCreated,
|
|
1568
|
+
pagesUpdated,
|
|
1569
|
+
uniqueEditors: editors.size,
|
|
1570
|
+
stalePages,
|
|
1571
|
+
avgDaysSinceEdit,
|
|
1572
|
+
topPages: topPages.slice(0, 5).map((p) => p.title)
|
|
1573
|
+
};
|
|
1574
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
1575
|
+
return { events, signals };
|
|
1576
|
+
}
|
|
1577
|
+
function formatNotionSignalsForPrompt(signals) {
|
|
1578
|
+
if (signals.pagesActive === 0 && signals.stalePages === 0) return "";
|
|
1579
|
+
const lines = [
|
|
1580
|
+
"## Notion Activity (documentation behavior)",
|
|
1581
|
+
"",
|
|
1582
|
+
`${signals.pagesActive} pages active in window (${signals.pagesCreated} created, ${signals.pagesUpdated} updated).`,
|
|
1583
|
+
`${signals.uniqueEditors} unique editors.`
|
|
1584
|
+
];
|
|
1585
|
+
if (signals.stalePages > 0) {
|
|
1586
|
+
lines.push(`${signals.stalePages} pages haven't been touched in 30+ days.`);
|
|
1587
|
+
}
|
|
1588
|
+
if (signals.avgDaysSinceEdit !== null) {
|
|
1589
|
+
lines.push(`Average page age since last edit: ${signals.avgDaysSinceEdit} days.`);
|
|
1590
|
+
}
|
|
1591
|
+
if (signals.topPages.length > 0) {
|
|
1592
|
+
lines.push(`Recently active pages: ${signals.topPages.join(", ")}.`);
|
|
1593
|
+
}
|
|
1594
|
+
lines.push("");
|
|
1595
|
+
lines.push("Documentation is how the team crystallizes and shares knowledge.");
|
|
1596
|
+
lines.push("High code velocity + low documentation = building without recording.");
|
|
1597
|
+
lines.push("High documentation + low code = planning without shipping.");
|
|
1598
|
+
lines.push("Compare Notion activity against GitHub and Discord to find the balance.");
|
|
1599
|
+
return lines.join("\n");
|
|
1600
|
+
}
|
|
1601
|
+
function extractTitle(page) {
|
|
1602
|
+
for (const prop of Object.values(page.properties)) {
|
|
1603
|
+
if (prop.type === "title" && prop.title) {
|
|
1604
|
+
return prop.title.map((t) => t.plain_text).join("") || "Untitled";
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return "Untitled";
|
|
1608
|
+
}
|
|
1609
|
+
async function fetchNotionAPI(url, headers, init) {
|
|
1610
|
+
const res = await fetch(url, {
|
|
1611
|
+
method: init?.method ?? "GET",
|
|
1612
|
+
headers,
|
|
1613
|
+
body: init?.body
|
|
1614
|
+
});
|
|
1615
|
+
if (!res.ok) {
|
|
1616
|
+
throw new Error(
|
|
1617
|
+
`Notion API error ${res.status}: ${(await res.text()).slice(0, 300)}`
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
return await res.json();
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1116
1623
|
// src/radiant/core/patterns.ts
|
|
1117
1624
|
async function interpretPatterns(input) {
|
|
1118
1625
|
const prompt = buildInterpretationPrompt(input);
|
|
@@ -1184,6 +1691,14 @@ ${jargonTable}
|
|
|
1184
1691
|
|
|
1185
1692
|
For example: don't say "update the worldmodel." Say "add a line to your strategy file."
|
|
1186
1693
|
|
|
1694
|
+
## When the same invariant keeps firing
|
|
1695
|
+
|
|
1696
|
+
If the prior read history or the current evidence shows the same worldmodel invariant being triggered repeatedly (by the same side \u2014 human or AI), name it in MEANING and ask the real question:
|
|
1697
|
+
|
|
1698
|
+
"This invariant has been tested N times across M reads. Always on the [human/AI] side. Either the team needs alignment on WHY this rule exists \u2014 or the team is telling you something the worldmodel hasn't absorbed yet."
|
|
1699
|
+
|
|
1700
|
+
Don't just say "invariant held." Say what it means that people keep pushing against the same wall.
|
|
1701
|
+
|
|
1187
1702
|
## Health is a valid read
|
|
1188
1703
|
|
|
1189
1704
|
If the activity is healthy and aligned with the worldmodel, SAY SO. Don't fabricate problems. Over-prescription is a voice failure. Legitimate outputs include:
|
|
@@ -1212,7 +1727,7 @@ Only recommend a move when the evidence actually calls for one.
|
|
|
1212
1727
|
}
|
|
1213
1728
|
],
|
|
1214
1729
|
"meaning": "3-5 sentences. Weave the patterns into ONE strategic thesis. Compress. The reader should finish this paragraph and understand the one thing that matters most in this read. Plain English \u2014 no system jargon.",
|
|
1215
|
-
"move": "1-3 direct imperatives, OR explicit 'nothing to act on' if the read is healthy. Do not fabricate urgency.
|
|
1730
|
+
"move": "1-3 direct imperatives, OR explicit 'nothing to act on' if the read is healthy. Do not fabricate urgency. When a candidate pattern has high confidence (>0.6), tell the reader EXACTLY where to declare it: 'If you want Radiant to track [pattern_name] over time, add it to auki-strategy.worldmodel.md under Evolution Layer \u2192 Drift Behaviors (or Aligned Behaviors if it is positive). If you don't, Radiant will rediscover it from scratch next run.' Be specific about the file and section \u2014 don't make them guess."
|
|
1216
1731
|
}
|
|
1217
1732
|
\`\`\`
|
|
1218
1733
|
|
|
@@ -1368,6 +1883,27 @@ Lens: ${input.lens.name}`
|
|
|
1368
1883
|
` Composite: ${formatScore(input.scores.R)}`
|
|
1369
1884
|
].join("\n");
|
|
1370
1885
|
sections.push(alignBlock);
|
|
1886
|
+
if (input.governance && input.governance.totalEvents > 0) {
|
|
1887
|
+
const gov = input.governance;
|
|
1888
|
+
const govLines = ["GOVERNANCE", "", ` ${gov.summary}`];
|
|
1889
|
+
const showSide = (label, side) => {
|
|
1890
|
+
if (side.allow + side.modify + side.block === 0) return;
|
|
1891
|
+
govLines.push("");
|
|
1892
|
+
govLines.push(` ${label}:`);
|
|
1893
|
+
govLines.push(` ${side.allow} ALLOW \xB7 ${side.modify} MODIFY \xB7 ${side.block} BLOCK`);
|
|
1894
|
+
for (const d of side.details.slice(0, 3)) {
|
|
1895
|
+
const reason = d.reason ? ` \u2192 ${d.reason}` : "";
|
|
1896
|
+
govLines.push(` ${d.status}: ${d.eventId}${reason}`);
|
|
1897
|
+
}
|
|
1898
|
+
if (side.details.length > 3) {
|
|
1899
|
+
govLines.push(` ... and ${side.details.length - 3} more`);
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
showSide("Human side", gov.human);
|
|
1903
|
+
showSide("AI side", gov.cyber);
|
|
1904
|
+
showSide("Human\u2013AI joint", gov.joint);
|
|
1905
|
+
sections.push(govLines.join("\n"));
|
|
1906
|
+
}
|
|
1371
1907
|
sections.push(renderDepth(input.priorReadCount ?? 0, input.windowDays));
|
|
1372
1908
|
return sections.join("\n\n");
|
|
1373
1909
|
}
|
|
@@ -1531,6 +2067,1481 @@ function serializeYAML(obj, indent = 0) {
|
|
|
1531
2067
|
`;
|
|
1532
2068
|
}
|
|
1533
2069
|
|
|
2070
|
+
// src/engine/text-utils.ts
|
|
2071
|
+
function normalizeEventText(event) {
|
|
2072
|
+
return [
|
|
2073
|
+
event.intent,
|
|
2074
|
+
event.tool ?? "",
|
|
2075
|
+
event.scope ?? ""
|
|
2076
|
+
].join(" ").toLowerCase();
|
|
2077
|
+
}
|
|
2078
|
+
function extractKeywords(text, minLength = 3) {
|
|
2079
|
+
return text.toLowerCase().split(/\s+/).filter((w) => w.length > minLength);
|
|
2080
|
+
}
|
|
2081
|
+
function matchesAllKeywords(eventText, ruleText) {
|
|
2082
|
+
const keywords = extractKeywords(ruleText);
|
|
2083
|
+
if (keywords.length === 0) return false;
|
|
2084
|
+
return keywords.every((kw) => eventText.includes(kw));
|
|
2085
|
+
}
|
|
2086
|
+
function matchesKeywordThreshold(eventText, ruleText, threshold = 0.5) {
|
|
2087
|
+
const keywords = extractKeywords(ruleText);
|
|
2088
|
+
if (keywords.length === 0) return false;
|
|
2089
|
+
const matched = keywords.filter((kw) => eventText.includes(kw));
|
|
2090
|
+
return matched.length >= Math.ceil(keywords.length * threshold);
|
|
2091
|
+
}
|
|
2092
|
+
function tokenSimilarity(a, b) {
|
|
2093
|
+
const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
2094
|
+
const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
2095
|
+
if (tokensA.size === 0 || tokensB.size === 0) return 0;
|
|
2096
|
+
let intersection = 0;
|
|
2097
|
+
for (const t of tokensA) {
|
|
2098
|
+
if (tokensB.has(t)) intersection++;
|
|
2099
|
+
}
|
|
2100
|
+
const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
|
|
2101
|
+
return union > 0 ? intersection / union : 0;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// src/engine/plan-engine.ts
|
|
2105
|
+
function keywordMatch(eventText, step) {
|
|
2106
|
+
const stepText = [
|
|
2107
|
+
step.label,
|
|
2108
|
+
step.description ?? "",
|
|
2109
|
+
...step.tags ?? []
|
|
2110
|
+
].join(" ");
|
|
2111
|
+
return matchesKeywordThreshold(eventText, stepText, 0.5);
|
|
2112
|
+
}
|
|
2113
|
+
function tokenSimilarity2(a, b) {
|
|
2114
|
+
return tokenSimilarity(a, b);
|
|
2115
|
+
}
|
|
2116
|
+
function findMatchingStep(eventText, event, steps) {
|
|
2117
|
+
const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
|
|
2118
|
+
if (pendingOrActive.length === 0) {
|
|
2119
|
+
return { matched: null, closest: null, closestScore: 0 };
|
|
2120
|
+
}
|
|
2121
|
+
for (const step of pendingOrActive) {
|
|
2122
|
+
if (keywordMatch(eventText, step)) {
|
|
2123
|
+
if (step.tools && event.tool && !step.tools.includes(event.tool)) {
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
return { matched: step, closest: step, closestScore: 1 };
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
const intentText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ");
|
|
2130
|
+
let bestStep = null;
|
|
2131
|
+
let bestScore = 0;
|
|
2132
|
+
for (const step of pendingOrActive) {
|
|
2133
|
+
const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
|
|
2134
|
+
const score = tokenSimilarity2(intentText, stepText);
|
|
2135
|
+
if (score > bestScore) {
|
|
2136
|
+
bestScore = score;
|
|
2137
|
+
bestStep = step;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const SIMILARITY_THRESHOLD = 0.35;
|
|
2141
|
+
if (bestScore >= SIMILARITY_THRESHOLD && bestStep) {
|
|
2142
|
+
if (bestStep.tools && event.tool && !bestStep.tools.includes(event.tool)) {
|
|
2143
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
2144
|
+
}
|
|
2145
|
+
return { matched: bestStep, closest: bestStep, closestScore: bestScore };
|
|
2146
|
+
}
|
|
2147
|
+
return { matched: null, closest: bestStep, closestScore: bestScore };
|
|
2148
|
+
}
|
|
2149
|
+
function isSequenceValid(step, plan) {
|
|
2150
|
+
if (!plan.sequential) return true;
|
|
2151
|
+
if (!step.requires || step.requires.length === 0) return true;
|
|
2152
|
+
return step.requires.every((reqId) => {
|
|
2153
|
+
const reqStep = plan.steps.find((s) => s.id === reqId);
|
|
2154
|
+
return reqStep?.status === "completed";
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
function checkConstraints(event, eventText, constraints) {
|
|
2158
|
+
const checks = [];
|
|
2159
|
+
for (const constraint of constraints) {
|
|
2160
|
+
if (constraint.type === "approval") {
|
|
2161
|
+
if (constraint.trigger && eventText.includes(constraint.trigger.substring(0, 10).toLowerCase())) {
|
|
2162
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
2163
|
+
return { violated: constraint, checks };
|
|
2164
|
+
}
|
|
2165
|
+
const keywords = constraint.description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
2166
|
+
const relevant = keywords.some((kw) => eventText.includes(kw));
|
|
2167
|
+
if (relevant) {
|
|
2168
|
+
checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
|
|
2169
|
+
return { violated: constraint, checks };
|
|
2170
|
+
}
|
|
2171
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
2172
|
+
continue;
|
|
2173
|
+
}
|
|
2174
|
+
if (constraint.type === "scope" && constraint.trigger) {
|
|
2175
|
+
const keywords = extractKeywords(constraint.trigger);
|
|
2176
|
+
const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
|
|
2177
|
+
checks.push({
|
|
2178
|
+
constraintId: constraint.id,
|
|
2179
|
+
passed: !violated,
|
|
2180
|
+
reason: violated ? constraint.description : void 0
|
|
2181
|
+
});
|
|
2182
|
+
if (violated) {
|
|
2183
|
+
return { violated: constraint, checks };
|
|
2184
|
+
}
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
checks.push({ constraintId: constraint.id, passed: true });
|
|
2188
|
+
}
|
|
2189
|
+
return { violated: null, checks };
|
|
2190
|
+
}
|
|
2191
|
+
function getPlanProgress(plan) {
|
|
2192
|
+
const completed = plan.steps.filter((s) => s.status === "completed").length;
|
|
2193
|
+
const total = plan.steps.length;
|
|
2194
|
+
return {
|
|
2195
|
+
completed,
|
|
2196
|
+
total,
|
|
2197
|
+
percentage: total > 0 ? Math.round(completed / total * 100) : 0
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
function evaluatePlan(event, plan) {
|
|
2201
|
+
const progress = getPlanProgress(plan);
|
|
2202
|
+
if (plan.expires_at) {
|
|
2203
|
+
const expiresAt = new Date(plan.expires_at).getTime();
|
|
2204
|
+
if (Date.now() > expiresAt) {
|
|
2205
|
+
return {
|
|
2206
|
+
allowed: true,
|
|
2207
|
+
status: "PLAN_COMPLETE",
|
|
2208
|
+
reason: "Plan has expired.",
|
|
2209
|
+
progress
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
if (progress.completed === progress.total) {
|
|
2214
|
+
return {
|
|
2215
|
+
allowed: true,
|
|
2216
|
+
status: "PLAN_COMPLETE",
|
|
2217
|
+
reason: "All plan steps are completed.",
|
|
2218
|
+
progress
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
const eventText = normalizeEventText(event);
|
|
2222
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
2223
|
+
if (!matched) {
|
|
2224
|
+
return {
|
|
2225
|
+
allowed: false,
|
|
2226
|
+
status: "OFF_PLAN",
|
|
2227
|
+
reason: "Action does not match any plan step.",
|
|
2228
|
+
closestStep: closest?.label,
|
|
2229
|
+
similarityScore: closestScore,
|
|
2230
|
+
progress
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
if (!isSequenceValid(matched, plan)) {
|
|
2234
|
+
const pendingDeps = (matched.requires ?? []).filter((reqId) => plan.steps.find((s) => s.id === reqId)?.status !== "completed").join(", ");
|
|
2235
|
+
return {
|
|
2236
|
+
allowed: false,
|
|
2237
|
+
status: "OFF_PLAN",
|
|
2238
|
+
reason: `Step "${matched.label}" requires completion of: ${pendingDeps}`,
|
|
2239
|
+
matchedStep: matched.id,
|
|
2240
|
+
progress
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
const { violated } = checkConstraints(event, eventText, plan.constraints);
|
|
2244
|
+
if (violated) {
|
|
2245
|
+
return {
|
|
2246
|
+
allowed: false,
|
|
2247
|
+
status: "CONSTRAINT_VIOLATED",
|
|
2248
|
+
reason: violated.description,
|
|
2249
|
+
matchedStep: matched.id,
|
|
2250
|
+
progress
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
return {
|
|
2254
|
+
allowed: true,
|
|
2255
|
+
status: "ON_PLAN",
|
|
2256
|
+
reason: `Matches step: ${matched.label}`,
|
|
2257
|
+
matchedStep: matched.id,
|
|
2258
|
+
progress
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
function buildPlanCheck(event, plan, verdict) {
|
|
2262
|
+
const eventText = normalizeEventText(event);
|
|
2263
|
+
const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
|
|
2264
|
+
const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
|
|
2265
|
+
const progress = getPlanProgress(plan);
|
|
2266
|
+
return {
|
|
2267
|
+
planId: plan.plan_id,
|
|
2268
|
+
matched: !!matched,
|
|
2269
|
+
matchedStepId: matched?.id,
|
|
2270
|
+
matchedStepLabel: matched?.label,
|
|
2271
|
+
closestStepId: !matched ? closest?.id : void 0,
|
|
2272
|
+
closestStepLabel: !matched ? closest?.label : void 0,
|
|
2273
|
+
similarityScore: !matched ? closestScore : void 0,
|
|
2274
|
+
sequenceValid: matched ? isSequenceValid(matched, plan) : void 0,
|
|
2275
|
+
constraintsChecked: constraintChecks,
|
|
2276
|
+
progress: { completed: progress.completed, total: progress.total }
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// src/engine/guard-engine.ts
|
|
2281
|
+
var PROMPT_INJECTION_PATTERNS = [
|
|
2282
|
+
// Instruction override
|
|
2283
|
+
{ pattern: /ignore\s+(previous|all|prior|above)\s+(instructions?|rules?)/i, label: "ignore-instructions" },
|
|
2284
|
+
{ pattern: /disregard\s+(your|the)\s+(rules|constraints)/i, label: "disregard-rules" },
|
|
2285
|
+
{ pattern: /new\s+instructions?:/i, label: "new-instructions" },
|
|
2286
|
+
// Identity manipulation
|
|
2287
|
+
{ pattern: /you\s+are\s+now/i, label: "identity-override" },
|
|
2288
|
+
{ pattern: /new\s+persona/i, label: "new-persona" },
|
|
2289
|
+
{ pattern: /act\s+as\s+if/i, label: "act-as-if" },
|
|
2290
|
+
{ pattern: /pretend\s+(you|to\s+be|you\s+are\s+unrestricted)/i, label: "pretend-to-be" },
|
|
2291
|
+
// Context reset
|
|
2292
|
+
{ pattern: /forget\s+(everything|all|your)/i, label: "forget-context" },
|
|
2293
|
+
{ pattern: /system\s*:\s*override/i, label: "system-override" },
|
|
2294
|
+
// Constraint bypass
|
|
2295
|
+
{ pattern: /override\s+(your|the)\s+(programming|constraints)/i, label: "override-constraints" },
|
|
2296
|
+
{ pattern: /bypass\s+(your|the)\s+(filters|constraints|rules)/i, label: "bypass-filters" },
|
|
2297
|
+
// Prompt extraction
|
|
2298
|
+
{ pattern: /system\s+prompt/i, label: "system-prompt-probe" },
|
|
2299
|
+
{ pattern: /reveal\s+your\s+(instructions?|prompt|rules)/i, label: "reveal-instructions" },
|
|
2300
|
+
// Known jailbreak terms
|
|
2301
|
+
{ pattern: /jailbreak/i, label: "jailbreak" },
|
|
2302
|
+
{ pattern: /DAN\s+mode/i, label: "dan-mode" },
|
|
2303
|
+
{ pattern: /developer\s+mode/i, label: "developer-mode" }
|
|
2304
|
+
];
|
|
2305
|
+
var EXECUTION_CLAIM_PATTERNS = [
|
|
2306
|
+
{ pattern: /I have (executed|completed|performed|done|made|created|sent|deleted|modified|updated)/i, label: "claim-i-have" },
|
|
2307
|
+
{ pattern: /Successfully (created|deleted|modified|updated|sent|executed|performed)/i, label: "claim-successfully" },
|
|
2308
|
+
{ pattern: /The file has been/i, label: "claim-file-modified" },
|
|
2309
|
+
{ pattern: /I've made the changes/i, label: "claim-made-changes" },
|
|
2310
|
+
{ pattern: /I('ve| have) (sent|posted|submitted|uploaded|downloaded)/i, label: "claim-sent" },
|
|
2311
|
+
{ pattern: /Your (email|message|file|request) has been (sent|submitted)/i, label: "claim-your-sent" },
|
|
2312
|
+
{ pattern: /Transaction complete/i, label: "claim-transaction" },
|
|
2313
|
+
{ pattern: /Order placed/i, label: "claim-order" },
|
|
2314
|
+
{ pattern: /Payment processed/i, label: "claim-payment" }
|
|
2315
|
+
];
|
|
2316
|
+
var EXECUTION_INTENT_PATTERNS = [
|
|
2317
|
+
{ pattern: /^(execute|run|perform|do this)/i, label: "intent-execute" },
|
|
2318
|
+
{ pattern: /^(create|write|delete|modify) (a |the )?(file|folder|document)/i, label: "intent-file-ops" },
|
|
2319
|
+
{ pattern: /^(send|post|submit) (a |an |the )?(email|message|tweet|post)/i, label: "intent-send" },
|
|
2320
|
+
{ pattern: /^(search|look up|browse) (the )?web/i, label: "intent-web-search" },
|
|
2321
|
+
{ pattern: /^(make|call|invoke) (a |an )?(api|http|rest) (call|request)/i, label: "intent-api-call" },
|
|
2322
|
+
{ pattern: /^(buy|purchase|order|pay|transfer|send money)/i, label: "intent-financial" },
|
|
2323
|
+
{ pattern: /^(book|schedule|reserve)/i, label: "intent-booking" },
|
|
2324
|
+
{ pattern: /^(download|upload|save to|export to)/i, label: "intent-transfer" }
|
|
2325
|
+
];
|
|
2326
|
+
var SCOPE_ESCAPE_PATTERNS = [
|
|
2327
|
+
{ pattern: /\.\.\//, label: "parent-traversal" },
|
|
2328
|
+
{ pattern: /^\/(?!home|project|workspace)/i, label: "absolute-path-outside-safe" },
|
|
2329
|
+
{ pattern: /~\//, label: "home-directory" },
|
|
2330
|
+
{ pattern: /\/etc\//i, label: "system-config" },
|
|
2331
|
+
{ pattern: /\/usr\//i, label: "system-binaries" },
|
|
2332
|
+
{ pattern: /\/var\//i, label: "system-variable-data" }
|
|
2333
|
+
];
|
|
2334
|
+
var NEUTRAL_MESSAGES = {
|
|
2335
|
+
"prompt-injection": "This input contains patterns that could alter agent behavior.",
|
|
2336
|
+
"scope-escape": "This action would affect resources outside the declared scope.",
|
|
2337
|
+
"execution-claim": "This response claims to have performed an action.",
|
|
2338
|
+
"execution-intent": "This input requests execution in a thinking-only environment.",
|
|
2339
|
+
"delete": "This action would remove files. Confirmation needed.",
|
|
2340
|
+
"write-external": "This action would write outside the project folder.",
|
|
2341
|
+
"network-mutate": "This action would send data to an external service.",
|
|
2342
|
+
"credential-access": "This action would access stored credentials."
|
|
2343
|
+
};
|
|
2344
|
+
function levelRequiresConfirmation(level, actionType) {
|
|
2345
|
+
if (level === "strict") return true;
|
|
2346
|
+
if (level === "standard") {
|
|
2347
|
+
return actionType === "delete" || actionType === "credential-access";
|
|
2348
|
+
}
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
function isExternalScope(scope) {
|
|
2352
|
+
const internalPatterns = [
|
|
2353
|
+
/^\.?\/?src\//i,
|
|
2354
|
+
/^\.?\/?lib\//i,
|
|
2355
|
+
/^\.?\/?app\//i,
|
|
2356
|
+
/^\.?\/?components\//i,
|
|
2357
|
+
/^\.?\/?pages\//i,
|
|
2358
|
+
/^\.?\/?public\//i,
|
|
2359
|
+
/^\.?\/?assets\//i,
|
|
2360
|
+
/^\.\//
|
|
2361
|
+
];
|
|
2362
|
+
return !internalPatterns.some((p) => p.test(scope));
|
|
2363
|
+
}
|
|
2364
|
+
var MAX_INPUT_LENGTH = 1e5;
|
|
2365
|
+
function evaluateGuard(event, world, options = {}) {
|
|
2366
|
+
const startTime = performance.now();
|
|
2367
|
+
const level = options.level ?? "standard";
|
|
2368
|
+
const includeTrace = options.trace ?? false;
|
|
2369
|
+
if (!event.intent || typeof event.intent !== "string") {
|
|
2370
|
+
return {
|
|
2371
|
+
status: "BLOCK",
|
|
2372
|
+
reason: "GuardEvent.intent is required and must be a string",
|
|
2373
|
+
ruleId: "safety-input-validation",
|
|
2374
|
+
evidence: {
|
|
2375
|
+
worldId: world.world?.world_id ?? "",
|
|
2376
|
+
worldName: world.world?.name ?? "",
|
|
2377
|
+
worldVersion: world.world?.version ?? "",
|
|
2378
|
+
evaluatedAt: Date.now(),
|
|
2379
|
+
invariantsSatisfied: 0,
|
|
2380
|
+
invariantsTotal: 0,
|
|
2381
|
+
guardsMatched: [],
|
|
2382
|
+
rulesMatched: [],
|
|
2383
|
+
enforcementLevel: level
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
const inputLength = event.intent.length + (event.tool?.length ?? 0) + (event.scope?.length ?? 0) + (event.payload ? JSON.stringify(event.payload).length : 0);
|
|
2388
|
+
if (inputLength > MAX_INPUT_LENGTH) {
|
|
2389
|
+
return {
|
|
2390
|
+
status: "BLOCK",
|
|
2391
|
+
reason: `Input exceeds maximum allowed length (${MAX_INPUT_LENGTH} characters)`,
|
|
2392
|
+
ruleId: "safety-input-length",
|
|
2393
|
+
evidence: {
|
|
2394
|
+
worldId: world.world?.world_id ?? "",
|
|
2395
|
+
worldName: world.world?.name ?? "",
|
|
2396
|
+
worldVersion: world.world?.version ?? "",
|
|
2397
|
+
evaluatedAt: Date.now(),
|
|
2398
|
+
invariantsSatisfied: 0,
|
|
2399
|
+
invariantsTotal: 0,
|
|
2400
|
+
guardsMatched: [],
|
|
2401
|
+
rulesMatched: [],
|
|
2402
|
+
enforcementLevel: level
|
|
2403
|
+
}
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
const eventText = normalizeEventText(event);
|
|
2407
|
+
const invariantChecks = [];
|
|
2408
|
+
const safetyChecks = [];
|
|
2409
|
+
let planCheckResult;
|
|
2410
|
+
const roleChecks = [];
|
|
2411
|
+
const guardChecks = [];
|
|
2412
|
+
const kernelRuleChecks = [];
|
|
2413
|
+
const levelChecks = [];
|
|
2414
|
+
let decidingLayer = "default-allow";
|
|
2415
|
+
let decidingId;
|
|
2416
|
+
const guardsMatched = [];
|
|
2417
|
+
const rulesMatched = [];
|
|
2418
|
+
if (options.emergencyOverride) {
|
|
2419
|
+
checkInvariantCoverage(world, invariantChecks);
|
|
2420
|
+
return buildVerdict(
|
|
2421
|
+
"ALLOW",
|
|
2422
|
+
void 0,
|
|
2423
|
+
"emergency-override",
|
|
2424
|
+
"Emergency override active \u2014 all governance rules suspended. Platform constraints still apply.",
|
|
2425
|
+
world,
|
|
2426
|
+
level,
|
|
2427
|
+
invariantChecks,
|
|
2428
|
+
guardsMatched,
|
|
2429
|
+
rulesMatched,
|
|
2430
|
+
includeTrace ? buildTrace(
|
|
2431
|
+
invariantChecks,
|
|
2432
|
+
safetyChecks,
|
|
2433
|
+
planCheckResult,
|
|
2434
|
+
roleChecks,
|
|
2435
|
+
guardChecks,
|
|
2436
|
+
kernelRuleChecks,
|
|
2437
|
+
levelChecks,
|
|
2438
|
+
"session-allowlist",
|
|
2439
|
+
"emergency-override",
|
|
2440
|
+
startTime
|
|
2441
|
+
) : void 0,
|
|
2442
|
+
event.intent
|
|
2443
|
+
);
|
|
2444
|
+
}
|
|
2445
|
+
checkInvariantCoverage(world, invariantChecks);
|
|
2446
|
+
if (event.roleId && options.agentStates) {
|
|
2447
|
+
const agentState = options.agentStates.get(event.roleId);
|
|
2448
|
+
if (agentState && agentState.cooldownRemaining > 0) {
|
|
2449
|
+
decidingLayer = "safety";
|
|
2450
|
+
decidingId = `penalize-cooldown-${event.roleId}`;
|
|
2451
|
+
const verdict = buildVerdict(
|
|
2452
|
+
"PENALIZE",
|
|
2453
|
+
`Agent "${event.roleId}" is frozen for ${agentState.cooldownRemaining} more round(s) due to prior penalty.`,
|
|
2454
|
+
`penalize-cooldown-${event.roleId}`,
|
|
2455
|
+
void 0,
|
|
2456
|
+
world,
|
|
2457
|
+
level,
|
|
2458
|
+
invariantChecks,
|
|
2459
|
+
guardsMatched,
|
|
2460
|
+
rulesMatched,
|
|
2461
|
+
includeTrace ? buildTrace(
|
|
2462
|
+
invariantChecks,
|
|
2463
|
+
safetyChecks,
|
|
2464
|
+
planCheckResult,
|
|
2465
|
+
roleChecks,
|
|
2466
|
+
guardChecks,
|
|
2467
|
+
kernelRuleChecks,
|
|
2468
|
+
levelChecks,
|
|
2469
|
+
decidingLayer,
|
|
2470
|
+
decidingId,
|
|
2471
|
+
startTime
|
|
2472
|
+
) : void 0
|
|
2473
|
+
);
|
|
2474
|
+
verdict.intentRecord = {
|
|
2475
|
+
originalIntent: event.intent,
|
|
2476
|
+
finalAction: "blocked (agent frozen)",
|
|
2477
|
+
enforcement: "PENALIZE",
|
|
2478
|
+
consequence: { type: "freeze", rounds: agentState.cooldownRemaining, description: "Agent still in cooldown from prior penalty" }
|
|
2479
|
+
};
|
|
2480
|
+
return verdict;
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
if (options.sessionAllowlist) {
|
|
2484
|
+
const key = eventToAllowlistKey(event);
|
|
2485
|
+
if (options.sessionAllowlist.has(key)) {
|
|
2486
|
+
decidingLayer = "session-allowlist";
|
|
2487
|
+
decidingId = `allowlist:${key}`;
|
|
2488
|
+
return buildVerdict(
|
|
2489
|
+
"ALLOW",
|
|
2490
|
+
void 0,
|
|
2491
|
+
`allowlist:${key}`,
|
|
2492
|
+
void 0,
|
|
2493
|
+
world,
|
|
2494
|
+
level,
|
|
2495
|
+
invariantChecks,
|
|
2496
|
+
guardsMatched,
|
|
2497
|
+
rulesMatched,
|
|
2498
|
+
includeTrace ? buildTrace(
|
|
2499
|
+
invariantChecks,
|
|
2500
|
+
safetyChecks,
|
|
2501
|
+
planCheckResult,
|
|
2502
|
+
roleChecks,
|
|
2503
|
+
guardChecks,
|
|
2504
|
+
kernelRuleChecks,
|
|
2505
|
+
levelChecks,
|
|
2506
|
+
decidingLayer,
|
|
2507
|
+
decidingId,
|
|
2508
|
+
startTime
|
|
2509
|
+
) : void 0,
|
|
2510
|
+
event.intent
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
const safetyVerdict = checkSafety(event, eventText, safetyChecks);
|
|
2515
|
+
if (safetyVerdict) {
|
|
2516
|
+
decidingLayer = "safety";
|
|
2517
|
+
decidingId = safetyVerdict.ruleId;
|
|
2518
|
+
return buildVerdict(
|
|
2519
|
+
safetyVerdict.status,
|
|
2520
|
+
safetyVerdict.reason,
|
|
2521
|
+
safetyVerdict.ruleId,
|
|
2522
|
+
void 0,
|
|
2523
|
+
world,
|
|
2524
|
+
level,
|
|
2525
|
+
invariantChecks,
|
|
2526
|
+
guardsMatched,
|
|
2527
|
+
rulesMatched,
|
|
2528
|
+
includeTrace ? buildTrace(
|
|
2529
|
+
invariantChecks,
|
|
2530
|
+
safetyChecks,
|
|
2531
|
+
planCheckResult,
|
|
2532
|
+
roleChecks,
|
|
2533
|
+
guardChecks,
|
|
2534
|
+
kernelRuleChecks,
|
|
2535
|
+
levelChecks,
|
|
2536
|
+
decidingLayer,
|
|
2537
|
+
decidingId,
|
|
2538
|
+
startTime
|
|
2539
|
+
) : void 0,
|
|
2540
|
+
event.intent
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
if (options.plan) {
|
|
2544
|
+
const planVerdict = evaluatePlan(event, options.plan);
|
|
2545
|
+
planCheckResult = buildPlanCheck(event, options.plan, planVerdict);
|
|
2546
|
+
if (!planVerdict.allowed && planVerdict.status !== "PLAN_COMPLETE") {
|
|
2547
|
+
decidingLayer = "plan-enforcement";
|
|
2548
|
+
decidingId = `plan-${options.plan.plan_id}`;
|
|
2549
|
+
const planStatus = planVerdict.status === "CONSTRAINT_VIOLATED" ? "PAUSE" : "BLOCK";
|
|
2550
|
+
let reason = planVerdict.reason ?? "Action blocked by plan.";
|
|
2551
|
+
if (planVerdict.status === "OFF_PLAN" && planVerdict.closestStep) {
|
|
2552
|
+
reason += ` Closest step: "${planVerdict.closestStep}" (similarity: ${(planVerdict.similarityScore ?? 0).toFixed(2)})`;
|
|
2553
|
+
}
|
|
2554
|
+
return buildVerdict(
|
|
2555
|
+
planStatus,
|
|
2556
|
+
reason,
|
|
2557
|
+
`plan-${options.plan.plan_id}`,
|
|
2558
|
+
void 0,
|
|
2559
|
+
world,
|
|
2560
|
+
level,
|
|
2561
|
+
invariantChecks,
|
|
2562
|
+
guardsMatched,
|
|
2563
|
+
rulesMatched,
|
|
2564
|
+
includeTrace ? buildTrace(
|
|
2565
|
+
invariantChecks,
|
|
2566
|
+
safetyChecks,
|
|
2567
|
+
planCheckResult,
|
|
2568
|
+
roleChecks,
|
|
2569
|
+
guardChecks,
|
|
2570
|
+
kernelRuleChecks,
|
|
2571
|
+
levelChecks,
|
|
2572
|
+
decidingLayer,
|
|
2573
|
+
decidingId,
|
|
2574
|
+
startTime
|
|
2575
|
+
) : void 0,
|
|
2576
|
+
event.intent
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
const roleVerdict = checkRoleRules(event, eventText, world, roleChecks);
|
|
2581
|
+
if (roleVerdict) {
|
|
2582
|
+
decidingLayer = "role";
|
|
2583
|
+
decidingId = roleVerdict.ruleId;
|
|
2584
|
+
return buildVerdict(
|
|
2585
|
+
roleVerdict.status,
|
|
2586
|
+
roleVerdict.reason,
|
|
2587
|
+
roleVerdict.ruleId,
|
|
2588
|
+
void 0,
|
|
2589
|
+
world,
|
|
2590
|
+
level,
|
|
2591
|
+
invariantChecks,
|
|
2592
|
+
guardsMatched,
|
|
2593
|
+
rulesMatched,
|
|
2594
|
+
includeTrace ? buildTrace(
|
|
2595
|
+
invariantChecks,
|
|
2596
|
+
safetyChecks,
|
|
2597
|
+
planCheckResult,
|
|
2598
|
+
roleChecks,
|
|
2599
|
+
guardChecks,
|
|
2600
|
+
kernelRuleChecks,
|
|
2601
|
+
levelChecks,
|
|
2602
|
+
decidingLayer,
|
|
2603
|
+
decidingId,
|
|
2604
|
+
startTime
|
|
2605
|
+
) : void 0,
|
|
2606
|
+
event.intent
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
const guardVerdict = checkGuards(event, eventText, world, guardChecks, guardsMatched);
|
|
2610
|
+
if (guardVerdict) {
|
|
2611
|
+
if (guardVerdict.status !== "ALLOW") {
|
|
2612
|
+
decidingLayer = "guard";
|
|
2613
|
+
decidingId = guardVerdict.ruleId;
|
|
2614
|
+
const intentRecord = {
|
|
2615
|
+
originalIntent: event.intent,
|
|
2616
|
+
finalAction: guardVerdict.status === "MODIFY" ? guardVerdict.modifiedTo ?? "modified" : guardVerdict.status === "PENALIZE" ? "blocked + penalized" : guardVerdict.status === "REWARD" ? event.intent : guardVerdict.status === "NEUTRAL" ? event.intent : guardVerdict.status === "BLOCK" ? "blocked" : "paused",
|
|
2617
|
+
ruleApplied: guardVerdict.ruleId,
|
|
2618
|
+
enforcement: guardVerdict.status,
|
|
2619
|
+
modifiedTo: guardVerdict.modifiedTo,
|
|
2620
|
+
consequence: guardVerdict.consequence,
|
|
2621
|
+
reward: guardVerdict.reward
|
|
2622
|
+
};
|
|
2623
|
+
const verdict = buildVerdict(
|
|
2624
|
+
guardVerdict.status,
|
|
2625
|
+
guardVerdict.reason,
|
|
2626
|
+
guardVerdict.ruleId,
|
|
2627
|
+
void 0,
|
|
2628
|
+
world,
|
|
2629
|
+
level,
|
|
2630
|
+
invariantChecks,
|
|
2631
|
+
guardsMatched,
|
|
2632
|
+
rulesMatched,
|
|
2633
|
+
includeTrace ? buildTrace(
|
|
2634
|
+
invariantChecks,
|
|
2635
|
+
safetyChecks,
|
|
2636
|
+
planCheckResult,
|
|
2637
|
+
roleChecks,
|
|
2638
|
+
guardChecks,
|
|
2639
|
+
kernelRuleChecks,
|
|
2640
|
+
levelChecks,
|
|
2641
|
+
decidingLayer,
|
|
2642
|
+
decidingId,
|
|
2643
|
+
startTime
|
|
2644
|
+
) : void 0,
|
|
2645
|
+
event.intent
|
|
2646
|
+
);
|
|
2647
|
+
verdict.intentRecord = intentRecord;
|
|
2648
|
+
if (guardVerdict.consequence) verdict.consequence = guardVerdict.consequence;
|
|
2649
|
+
if (guardVerdict.reward) verdict.reward = guardVerdict.reward;
|
|
2650
|
+
return verdict;
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
const kernelVerdict = checkKernelRules(eventText, world, kernelRuleChecks, rulesMatched);
|
|
2654
|
+
if (kernelVerdict) {
|
|
2655
|
+
decidingLayer = "kernel-rule";
|
|
2656
|
+
decidingId = kernelVerdict.ruleId;
|
|
2657
|
+
return buildVerdict(
|
|
2658
|
+
kernelVerdict.status,
|
|
2659
|
+
kernelVerdict.reason,
|
|
2660
|
+
kernelVerdict.ruleId,
|
|
2661
|
+
void 0,
|
|
2662
|
+
world,
|
|
2663
|
+
level,
|
|
2664
|
+
invariantChecks,
|
|
2665
|
+
guardsMatched,
|
|
2666
|
+
rulesMatched,
|
|
2667
|
+
includeTrace ? buildTrace(
|
|
2668
|
+
invariantChecks,
|
|
2669
|
+
safetyChecks,
|
|
2670
|
+
planCheckResult,
|
|
2671
|
+
roleChecks,
|
|
2672
|
+
guardChecks,
|
|
2673
|
+
kernelRuleChecks,
|
|
2674
|
+
levelChecks,
|
|
2675
|
+
decidingLayer,
|
|
2676
|
+
decidingId,
|
|
2677
|
+
startTime
|
|
2678
|
+
) : void 0,
|
|
2679
|
+
event.intent
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
const levelVerdict = checkLevelConstraints(event, level, levelChecks);
|
|
2683
|
+
if (levelVerdict) {
|
|
2684
|
+
decidingLayer = "level-constraint";
|
|
2685
|
+
decidingId = levelVerdict.ruleId;
|
|
2686
|
+
return buildVerdict(
|
|
2687
|
+
levelVerdict.status,
|
|
2688
|
+
levelVerdict.reason,
|
|
2689
|
+
levelVerdict.ruleId,
|
|
2690
|
+
void 0,
|
|
2691
|
+
world,
|
|
2692
|
+
level,
|
|
2693
|
+
invariantChecks,
|
|
2694
|
+
guardsMatched,
|
|
2695
|
+
rulesMatched,
|
|
2696
|
+
includeTrace ? buildTrace(
|
|
2697
|
+
invariantChecks,
|
|
2698
|
+
safetyChecks,
|
|
2699
|
+
planCheckResult,
|
|
2700
|
+
roleChecks,
|
|
2701
|
+
guardChecks,
|
|
2702
|
+
kernelRuleChecks,
|
|
2703
|
+
levelChecks,
|
|
2704
|
+
decidingLayer,
|
|
2705
|
+
decidingId,
|
|
2706
|
+
startTime
|
|
2707
|
+
) : void 0,
|
|
2708
|
+
event.intent
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2711
|
+
const warning = guardVerdict?.warning;
|
|
2712
|
+
return buildVerdict(
|
|
2713
|
+
"ALLOW",
|
|
2714
|
+
void 0,
|
|
2715
|
+
void 0,
|
|
2716
|
+
warning,
|
|
2717
|
+
world,
|
|
2718
|
+
level,
|
|
2719
|
+
invariantChecks,
|
|
2720
|
+
guardsMatched,
|
|
2721
|
+
rulesMatched,
|
|
2722
|
+
includeTrace ? buildTrace(
|
|
2723
|
+
invariantChecks,
|
|
2724
|
+
safetyChecks,
|
|
2725
|
+
planCheckResult,
|
|
2726
|
+
roleChecks,
|
|
2727
|
+
guardChecks,
|
|
2728
|
+
kernelRuleChecks,
|
|
2729
|
+
levelChecks,
|
|
2730
|
+
decidingLayer,
|
|
2731
|
+
decidingId,
|
|
2732
|
+
startTime
|
|
2733
|
+
) : void 0,
|
|
2734
|
+
event.intent
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
function checkInvariantCoverage(world, checks) {
|
|
2738
|
+
const invariants = world.invariants ?? [];
|
|
2739
|
+
const guards = world.guards?.guards ?? [];
|
|
2740
|
+
for (const invariant of invariants) {
|
|
2741
|
+
const coveringGuard = guards.find(
|
|
2742
|
+
(g) => g.invariant_ref === invariant.id && g.immutable
|
|
2743
|
+
);
|
|
2744
|
+
checks.push({
|
|
2745
|
+
invariantId: invariant.id,
|
|
2746
|
+
label: invariant.label,
|
|
2747
|
+
hasGuardCoverage: !!coveringGuard,
|
|
2748
|
+
coveringGuardId: coveringGuard?.id
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
function checkSafety(event, eventText, checks) {
|
|
2753
|
+
const textToCheck = event.intent + (event.payload ? JSON.stringify(event.payload) : "");
|
|
2754
|
+
for (const { pattern, label } of PROMPT_INJECTION_PATTERNS) {
|
|
2755
|
+
const triggered = pattern.test(textToCheck);
|
|
2756
|
+
checks.push({
|
|
2757
|
+
checkType: "prompt-injection",
|
|
2758
|
+
triggered,
|
|
2759
|
+
matchedPattern: triggered ? label : void 0
|
|
2760
|
+
});
|
|
2761
|
+
if (triggered) {
|
|
2762
|
+
for (const remaining of PROMPT_INJECTION_PATTERNS.filter((p) => p.label !== label)) {
|
|
2763
|
+
checks.push({
|
|
2764
|
+
checkType: "prompt-injection",
|
|
2765
|
+
triggered: remaining.pattern.test(textToCheck),
|
|
2766
|
+
matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
return {
|
|
2770
|
+
status: "PAUSE",
|
|
2771
|
+
reason: NEUTRAL_MESSAGES["prompt-injection"],
|
|
2772
|
+
ruleId: `safety-injection-${label}`
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
const scopeToCheck = event.scope ?? event.intent;
|
|
2777
|
+
for (const { pattern, label } of SCOPE_ESCAPE_PATTERNS) {
|
|
2778
|
+
const triggered = pattern.test(scopeToCheck);
|
|
2779
|
+
checks.push({
|
|
2780
|
+
checkType: "scope-escape",
|
|
2781
|
+
triggered,
|
|
2782
|
+
matchedPattern: triggered ? label : void 0
|
|
2783
|
+
});
|
|
2784
|
+
if (triggered) {
|
|
2785
|
+
for (const remaining of SCOPE_ESCAPE_PATTERNS.filter((p) => p.label !== label)) {
|
|
2786
|
+
checks.push({
|
|
2787
|
+
checkType: "scope-escape",
|
|
2788
|
+
triggered: remaining.pattern.test(scopeToCheck),
|
|
2789
|
+
matchedPattern: remaining.pattern.test(scopeToCheck) ? remaining.label : void 0
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2792
|
+
return {
|
|
2793
|
+
status: "PAUSE",
|
|
2794
|
+
reason: NEUTRAL_MESSAGES["scope-escape"],
|
|
2795
|
+
ruleId: `safety-scope-${label}`
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
if (event.direction === "output") {
|
|
2800
|
+
for (const { pattern, label } of EXECUTION_CLAIM_PATTERNS) {
|
|
2801
|
+
const triggered = pattern.test(textToCheck);
|
|
2802
|
+
checks.push({
|
|
2803
|
+
checkType: "execution-claim",
|
|
2804
|
+
triggered,
|
|
2805
|
+
matchedPattern: triggered ? label : void 0
|
|
2806
|
+
});
|
|
2807
|
+
if (triggered) {
|
|
2808
|
+
for (const remaining of EXECUTION_CLAIM_PATTERNS.filter((p) => p.label !== label)) {
|
|
2809
|
+
checks.push({
|
|
2810
|
+
checkType: "execution-claim",
|
|
2811
|
+
triggered: remaining.pattern.test(textToCheck),
|
|
2812
|
+
matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
return {
|
|
2816
|
+
status: "PAUSE",
|
|
2817
|
+
reason: NEUTRAL_MESSAGES["execution-claim"],
|
|
2818
|
+
ruleId: `safety-execution-claim-${label}`
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
if (event.direction === "input") {
|
|
2824
|
+
const intentTrimmed = event.intent.trim();
|
|
2825
|
+
for (const { pattern, label } of EXECUTION_INTENT_PATTERNS) {
|
|
2826
|
+
const triggered = pattern.test(intentTrimmed);
|
|
2827
|
+
checks.push({
|
|
2828
|
+
checkType: "execution-intent",
|
|
2829
|
+
triggered,
|
|
2830
|
+
matchedPattern: triggered ? label : void 0
|
|
2831
|
+
});
|
|
2832
|
+
if (triggered) {
|
|
2833
|
+
for (const remaining of EXECUTION_INTENT_PATTERNS.filter((p) => p.label !== label)) {
|
|
2834
|
+
checks.push({
|
|
2835
|
+
checkType: "execution-intent",
|
|
2836
|
+
triggered: remaining.pattern.test(intentTrimmed),
|
|
2837
|
+
matchedPattern: remaining.pattern.test(intentTrimmed) ? remaining.label : void 0
|
|
2838
|
+
});
|
|
2839
|
+
}
|
|
2840
|
+
return {
|
|
2841
|
+
status: "PAUSE",
|
|
2842
|
+
reason: NEUTRAL_MESSAGES["execution-intent"],
|
|
2843
|
+
ruleId: `safety-execution-intent-${label}`
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
return null;
|
|
2849
|
+
}
|
|
2850
|
+
function checkRoleRules(event, eventText, world, checks) {
|
|
2851
|
+
if (!event.roleId || !world.roles) return null;
|
|
2852
|
+
const role = world.roles.roles.find((r) => r.id === event.roleId);
|
|
2853
|
+
if (!role) return null;
|
|
2854
|
+
if (role.requiresApproval) {
|
|
2855
|
+
checks.push({
|
|
2856
|
+
roleId: role.id,
|
|
2857
|
+
roleName: role.name,
|
|
2858
|
+
rule: "All actions require approval",
|
|
2859
|
+
ruleType: "requiresApproval",
|
|
2860
|
+
matched: true
|
|
2861
|
+
});
|
|
2862
|
+
return {
|
|
2863
|
+
status: "PAUSE",
|
|
2864
|
+
reason: `Role "${role.name}" requires approval for all actions.`,
|
|
2865
|
+
ruleId: `role-${role.id}-requires-approval`
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
for (const rule of role.cannotDo) {
|
|
2869
|
+
const matched = matchesKeywords(eventText, rule);
|
|
2870
|
+
checks.push({
|
|
2871
|
+
roleId: role.id,
|
|
2872
|
+
roleName: role.name,
|
|
2873
|
+
rule,
|
|
2874
|
+
ruleType: "cannotDo",
|
|
2875
|
+
matched
|
|
2876
|
+
});
|
|
2877
|
+
if (matched) {
|
|
2878
|
+
return {
|
|
2879
|
+
status: "BLOCK",
|
|
2880
|
+
reason: `Role "${role.name}" cannot: ${rule}`,
|
|
2881
|
+
ruleId: `role-${role.id}-cannotdo`
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
for (const rule of role.canDo) {
|
|
2886
|
+
checks.push({
|
|
2887
|
+
roleId: role.id,
|
|
2888
|
+
roleName: role.name,
|
|
2889
|
+
rule,
|
|
2890
|
+
ruleType: "canDo",
|
|
2891
|
+
matched: matchesKeywords(eventText, rule)
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
return null;
|
|
2895
|
+
}
|
|
2896
|
+
function checkGuards(event, eventText, world, checks, guardsMatched) {
|
|
2897
|
+
if (!world.guards) return null;
|
|
2898
|
+
const guardsConfig = world.guards;
|
|
2899
|
+
let warnResult = null;
|
|
2900
|
+
const compiledPatterns = /* @__PURE__ */ new Map();
|
|
2901
|
+
for (const [key, def] of Object.entries(guardsConfig.intent_vocabulary)) {
|
|
2902
|
+
try {
|
|
2903
|
+
compiledPatterns.set(key, new RegExp(def.pattern, "i"));
|
|
2904
|
+
} catch {
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
const eventTool = (event.tool ?? "").toLowerCase();
|
|
2908
|
+
for (const guard of guardsConfig.guards) {
|
|
2909
|
+
if (guard.appliesTo && guard.appliesTo.length > 0) {
|
|
2910
|
+
const normalizedAppliesTo = guard.appliesTo.map((t) => t.toLowerCase());
|
|
2911
|
+
if (!normalizedAppliesTo.includes(eventTool)) {
|
|
2912
|
+
continue;
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
const enabled = guard.immutable || guard.default_enabled !== false;
|
|
2916
|
+
const matchedPatterns = [];
|
|
2917
|
+
for (const patternKey of guard.intent_patterns) {
|
|
2918
|
+
const regex = compiledPatterns.get(patternKey);
|
|
2919
|
+
if (regex?.test(eventText)) {
|
|
2920
|
+
matchedPatterns.push(patternKey);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
const matched = matchedPatterns.length > 0 && enabled;
|
|
2924
|
+
let roleGated = false;
|
|
2925
|
+
if (matched && guard.required_roles && guard.required_roles.length > 0 && event.roleId && guard.required_roles.includes(event.roleId)) {
|
|
2926
|
+
roleGated = true;
|
|
2927
|
+
}
|
|
2928
|
+
checks.push({
|
|
2929
|
+
guardId: guard.id,
|
|
2930
|
+
label: guard.label,
|
|
2931
|
+
category: guard.category,
|
|
2932
|
+
enabled,
|
|
2933
|
+
matched: matched && !roleGated,
|
|
2934
|
+
enforcement: guard.enforcement,
|
|
2935
|
+
matchedPatterns,
|
|
2936
|
+
roleGated
|
|
2937
|
+
});
|
|
2938
|
+
if (!matched || roleGated) continue;
|
|
2939
|
+
guardsMatched.push(guard.id);
|
|
2940
|
+
const actionMode = guard.player_modes?.action ?? guard.enforcement;
|
|
2941
|
+
const reason = guard.redirect ? `${guard.description} \u2014 ${guard.redirect}` : guard.description;
|
|
2942
|
+
if (actionMode === "block") {
|
|
2943
|
+
return { status: "BLOCK", reason, ruleId: `guard-${guard.id}` };
|
|
2944
|
+
}
|
|
2945
|
+
if (actionMode === "pause") {
|
|
2946
|
+
return { status: "PAUSE", reason, ruleId: `guard-${guard.id}` };
|
|
2947
|
+
}
|
|
2948
|
+
if (actionMode === "penalize") {
|
|
2949
|
+
const consequence = guard.consequence ? { ...guard.consequence } : { type: "freeze", rounds: 1, description: `Penalized for violating: ${guard.label}` };
|
|
2950
|
+
return { status: "PENALIZE", reason, ruleId: `guard-${guard.id}`, consequence };
|
|
2951
|
+
}
|
|
2952
|
+
if (actionMode === "reward") {
|
|
2953
|
+
const reward = guard.reward ? { ...guard.reward } : { type: "boost_influence", magnitude: 0.1, description: `Rewarded for: ${guard.label}` };
|
|
2954
|
+
return { status: "REWARD", reason, ruleId: `guard-${guard.id}`, reward };
|
|
2955
|
+
}
|
|
2956
|
+
if (actionMode === "modify") {
|
|
2957
|
+
const modifiedTo = guard.modify_to ?? guard.redirect ?? "hold";
|
|
2958
|
+
return { status: "MODIFY", reason: `${reason} \u2192 Modified to: ${modifiedTo}`, ruleId: `guard-${guard.id}`, modifiedTo };
|
|
2959
|
+
}
|
|
2960
|
+
if (actionMode === "neutral") {
|
|
2961
|
+
return { status: "NEUTRAL", reason, ruleId: `guard-${guard.id}` };
|
|
2962
|
+
}
|
|
2963
|
+
if (actionMode === "warn" && !warnResult) {
|
|
2964
|
+
warnResult = { status: "ALLOW", warning: reason, ruleId: `guard-${guard.id}` };
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
return warnResult;
|
|
2968
|
+
}
|
|
2969
|
+
function checkKernelRules(eventText, world, checks, rulesMatched) {
|
|
2970
|
+
if (!world.kernel) return null;
|
|
2971
|
+
const forbidden = world.kernel.input_boundaries?.forbidden_patterns ?? [];
|
|
2972
|
+
const output = world.kernel.output_boundaries?.forbidden_patterns ?? [];
|
|
2973
|
+
for (const rule of forbidden) {
|
|
2974
|
+
let matched = false;
|
|
2975
|
+
let matchMethod = "none";
|
|
2976
|
+
if (rule.pattern) {
|
|
2977
|
+
try {
|
|
2978
|
+
matched = new RegExp(rule.pattern, "i").test(eventText);
|
|
2979
|
+
matchMethod = "pattern";
|
|
2980
|
+
} catch {
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
if (!matched && rule.reason) {
|
|
2984
|
+
matched = matchesKeywords(eventText, rule.reason);
|
|
2985
|
+
if (matched) matchMethod = "keyword";
|
|
2986
|
+
}
|
|
2987
|
+
checks.push({
|
|
2988
|
+
ruleId: rule.id,
|
|
2989
|
+
text: rule.reason,
|
|
2990
|
+
category: "forbidden",
|
|
2991
|
+
matched,
|
|
2992
|
+
matchMethod
|
|
2993
|
+
});
|
|
2994
|
+
if (matched) {
|
|
2995
|
+
rulesMatched.push(rule.id);
|
|
2996
|
+
if (rule.action === "BLOCK") {
|
|
2997
|
+
return {
|
|
2998
|
+
status: "BLOCK",
|
|
2999
|
+
reason: rule.reason,
|
|
3000
|
+
ruleId: `kernel-${rule.id}`
|
|
3001
|
+
};
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
return null;
|
|
3006
|
+
}
|
|
3007
|
+
function checkLevelConstraints(event, level, checks) {
|
|
3008
|
+
if (level === "basic") return null;
|
|
3009
|
+
const intent = event.intent.toLowerCase();
|
|
3010
|
+
const tool = (event.tool ?? "").toLowerCase();
|
|
3011
|
+
const isDelete = intent.includes("delete") || intent.includes("remove") || intent.includes("rm ") || tool === "delete";
|
|
3012
|
+
const deleteTriggered = isDelete && levelRequiresConfirmation(level, "delete");
|
|
3013
|
+
checks.push({
|
|
3014
|
+
checkType: "delete",
|
|
3015
|
+
level,
|
|
3016
|
+
triggered: deleteTriggered,
|
|
3017
|
+
reason: deleteTriggered ? NEUTRAL_MESSAGES["delete"] : void 0
|
|
3018
|
+
});
|
|
3019
|
+
if (deleteTriggered) {
|
|
3020
|
+
return { status: "PAUSE", reason: NEUTRAL_MESSAGES["delete"], ruleId: "level-delete-check" };
|
|
3021
|
+
}
|
|
3022
|
+
const isExternal = event.scope ? isExternalScope(event.scope) : false;
|
|
3023
|
+
const externalTriggered = isExternal && levelRequiresConfirmation(level, "write-external");
|
|
3024
|
+
checks.push({
|
|
3025
|
+
checkType: "write-external",
|
|
3026
|
+
level,
|
|
3027
|
+
triggered: externalTriggered,
|
|
3028
|
+
reason: externalTriggered ? NEUTRAL_MESSAGES["write-external"] : void 0
|
|
3029
|
+
});
|
|
3030
|
+
if (externalTriggered) {
|
|
3031
|
+
return { status: "PAUSE", reason: NEUTRAL_MESSAGES["write-external"], ruleId: "level-external-write-check" };
|
|
3032
|
+
}
|
|
3033
|
+
const isNetwork = tool === "http" || tool === "fetch" || tool === "request" || intent.includes("post ") || intent.includes("sending");
|
|
3034
|
+
const networkTriggered = isNetwork && levelRequiresConfirmation(level, "network-mutate");
|
|
3035
|
+
checks.push({
|
|
3036
|
+
checkType: "network-mutate",
|
|
3037
|
+
level,
|
|
3038
|
+
triggered: networkTriggered,
|
|
3039
|
+
reason: networkTriggered ? NEUTRAL_MESSAGES["network-mutate"] : void 0
|
|
3040
|
+
});
|
|
3041
|
+
if (networkTriggered) {
|
|
3042
|
+
return { status: "PAUSE", reason: NEUTRAL_MESSAGES["network-mutate"], ruleId: "level-network-mutate-check" };
|
|
3043
|
+
}
|
|
3044
|
+
const isCredential = intent.includes("credential") || intent.includes("password") || intent.includes("secret") || intent.includes("api key") || intent.includes("token");
|
|
3045
|
+
const credentialTriggered = isCredential && levelRequiresConfirmation(level, "credential-access");
|
|
3046
|
+
checks.push({
|
|
3047
|
+
checkType: "credential-access",
|
|
3048
|
+
level,
|
|
3049
|
+
triggered: credentialTriggered,
|
|
3050
|
+
reason: credentialTriggered ? NEUTRAL_MESSAGES["credential-access"] : void 0
|
|
3051
|
+
});
|
|
3052
|
+
if (credentialTriggered) {
|
|
3053
|
+
return { status: "PAUSE", reason: NEUTRAL_MESSAGES["credential-access"], ruleId: "level-credential-check" };
|
|
3054
|
+
}
|
|
3055
|
+
const irreversibleTriggered = !!event.irreversible && level !== "basic";
|
|
3056
|
+
checks.push({
|
|
3057
|
+
checkType: "irreversible",
|
|
3058
|
+
level,
|
|
3059
|
+
triggered: irreversibleTriggered,
|
|
3060
|
+
reason: irreversibleTriggered ? "This action is marked as irreversible." : void 0
|
|
3061
|
+
});
|
|
3062
|
+
if (irreversibleTriggered) {
|
|
3063
|
+
return {
|
|
3064
|
+
status: "PAUSE",
|
|
3065
|
+
reason: "This action is marked as irreversible.",
|
|
3066
|
+
ruleId: "level-irreversible-check"
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
return null;
|
|
3070
|
+
}
|
|
3071
|
+
function matchesKeywords(eventText, ruleText) {
|
|
3072
|
+
return matchesAllKeywords(eventText, ruleText);
|
|
3073
|
+
}
|
|
3074
|
+
function eventToAllowlistKey(event) {
|
|
3075
|
+
return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
|
|
3076
|
+
}
|
|
3077
|
+
function buildTrace(invariantChecks, safetyChecks, planCheck, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
|
|
3078
|
+
const trace = {
|
|
3079
|
+
invariantChecks,
|
|
3080
|
+
safetyChecks,
|
|
3081
|
+
roleChecks,
|
|
3082
|
+
guardChecks,
|
|
3083
|
+
kernelRuleChecks,
|
|
3084
|
+
levelChecks,
|
|
3085
|
+
precedenceResolution: {
|
|
3086
|
+
decidingLayer,
|
|
3087
|
+
decidingId,
|
|
3088
|
+
strategy: "first-match-wins",
|
|
3089
|
+
chainOrder: [
|
|
3090
|
+
"invariant-coverage",
|
|
3091
|
+
"session-allowlist",
|
|
3092
|
+
"safety-injection",
|
|
3093
|
+
"safety-scope-escape",
|
|
3094
|
+
"safety-execution-claim",
|
|
3095
|
+
"safety-execution-intent",
|
|
3096
|
+
"plan-enforcement",
|
|
3097
|
+
"role-rules",
|
|
3098
|
+
"declarative-guards",
|
|
3099
|
+
"kernel-rules",
|
|
3100
|
+
"level-constraints",
|
|
3101
|
+
"default-allow"
|
|
3102
|
+
]
|
|
3103
|
+
},
|
|
3104
|
+
durationMs: performance.now() - startTime
|
|
3105
|
+
};
|
|
3106
|
+
if (planCheck) {
|
|
3107
|
+
trace.planCheck = planCheck;
|
|
3108
|
+
}
|
|
3109
|
+
return trace;
|
|
3110
|
+
}
|
|
3111
|
+
function buildVerdict(status, reason, ruleId, warning, world, level, invariantChecks, guardsMatched, rulesMatched, trace, eventIntent) {
|
|
3112
|
+
const evidence = {
|
|
3113
|
+
worldId: world.world.world_id,
|
|
3114
|
+
worldName: world.world.name,
|
|
3115
|
+
worldVersion: world.world.version,
|
|
3116
|
+
evaluatedAt: Date.now(),
|
|
3117
|
+
invariantsSatisfied: invariantChecks.filter((c) => c.hasGuardCoverage).length,
|
|
3118
|
+
invariantsTotal: invariantChecks.length,
|
|
3119
|
+
guardsMatched,
|
|
3120
|
+
rulesMatched,
|
|
3121
|
+
enforcementLevel: level
|
|
3122
|
+
};
|
|
3123
|
+
const verdict = {
|
|
3124
|
+
status,
|
|
3125
|
+
evidence
|
|
3126
|
+
};
|
|
3127
|
+
if (reason) verdict.reason = reason;
|
|
3128
|
+
if (ruleId) verdict.ruleId = ruleId;
|
|
3129
|
+
if (warning) verdict.warning = warning;
|
|
3130
|
+
if (trace) verdict.trace = trace;
|
|
3131
|
+
verdict.event = verdictToEvent(status, eventIntent);
|
|
3132
|
+
return verdict;
|
|
3133
|
+
}
|
|
3134
|
+
function verdictToEvent(status, intent) {
|
|
3135
|
+
const statusEventMap = {
|
|
3136
|
+
ALLOW: "action_allowed",
|
|
3137
|
+
BLOCK: "action_blocked",
|
|
3138
|
+
PAUSE: "action_paused",
|
|
3139
|
+
MODIFY: "action_modified",
|
|
3140
|
+
PENALIZE: "action_penalized",
|
|
3141
|
+
REWARD: "action_rewarded",
|
|
3142
|
+
NEUTRAL: "action_neutral"
|
|
3143
|
+
};
|
|
3144
|
+
return {
|
|
3145
|
+
type: intent || statusEventMap[status] || "unknown_action",
|
|
3146
|
+
actor: "agent",
|
|
3147
|
+
source: "guard",
|
|
3148
|
+
timestamp: Date.now(),
|
|
3149
|
+
guardStatus: status
|
|
3150
|
+
};
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
// src/loader/world-loader.ts
|
|
3154
|
+
async function loadWorldFromDirectory(dirPath) {
|
|
3155
|
+
const { readFile } = await import("fs/promises");
|
|
3156
|
+
const { join: join3 } = await import("path");
|
|
3157
|
+
const { readdirSync: readdirSync3 } = await import("fs");
|
|
3158
|
+
async function readJson(filename) {
|
|
3159
|
+
const filePath = join3(dirPath, filename);
|
|
3160
|
+
try {
|
|
3161
|
+
const content = await readFile(filePath, "utf-8");
|
|
3162
|
+
return JSON.parse(content);
|
|
3163
|
+
} catch (err) {
|
|
3164
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
3165
|
+
return void 0;
|
|
3166
|
+
}
|
|
3167
|
+
process.stderr.write(
|
|
3168
|
+
`[neuroverse] Warning: Failed to read ${filename}: ${err instanceof Error ? err.message : String(err)}
|
|
3169
|
+
`
|
|
3170
|
+
);
|
|
3171
|
+
return void 0;
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
const worldJson = await readJson("world.json");
|
|
3175
|
+
if (!worldJson) {
|
|
3176
|
+
throw new Error(`Cannot read world.json in ${dirPath}`);
|
|
3177
|
+
}
|
|
3178
|
+
const invariantsJson = await readJson("invariants.json");
|
|
3179
|
+
const assumptionsJson = await readJson("assumptions.json");
|
|
3180
|
+
const stateSchemaJson = await readJson("state-schema.json");
|
|
3181
|
+
const gatesJson = await readJson("gates.json");
|
|
3182
|
+
const outcomesJson = await readJson("outcomes.json");
|
|
3183
|
+
const guardsJson = await readJson("guards.json");
|
|
3184
|
+
const rolesJson = await readJson("roles.json");
|
|
3185
|
+
const kernelJson = await readJson("kernel.json");
|
|
3186
|
+
const metadataJson = await readJson("metadata.json");
|
|
3187
|
+
const rules = [];
|
|
3188
|
+
try {
|
|
3189
|
+
const rulesDir = join3(dirPath, "rules");
|
|
3190
|
+
const ruleFiles = readdirSync3(rulesDir).filter((f) => f.endsWith(".json")).sort();
|
|
3191
|
+
for (const file of ruleFiles) {
|
|
3192
|
+
try {
|
|
3193
|
+
const content = await readFile(join3(rulesDir, file), "utf-8");
|
|
3194
|
+
rules.push(JSON.parse(content));
|
|
3195
|
+
} catch (err) {
|
|
3196
|
+
process.stderr.write(
|
|
3197
|
+
`[neuroverse] Warning: Failed to parse rule ${file}: ${err instanceof Error ? err.message : String(err)}
|
|
3198
|
+
`
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
} catch (err) {
|
|
3203
|
+
if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) {
|
|
3204
|
+
process.stderr.write(
|
|
3205
|
+
`[neuroverse] Warning: Failed to read rules directory: ${err instanceof Error ? err.message : String(err)}
|
|
3206
|
+
`
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
return {
|
|
3211
|
+
world: worldJson,
|
|
3212
|
+
invariants: invariantsJson?.invariants ?? [],
|
|
3213
|
+
assumptions: assumptionsJson ?? { profiles: {}, parameter_definitions: {} },
|
|
3214
|
+
stateSchema: stateSchemaJson ?? { variables: {}, presets: {} },
|
|
3215
|
+
rules,
|
|
3216
|
+
gates: gatesJson ?? {
|
|
3217
|
+
viability_classification: [],
|
|
3218
|
+
structural_override: { description: "", enforcement: "mandatory" },
|
|
3219
|
+
sustainability_threshold: 0,
|
|
3220
|
+
collapse_visual: { background: "", text: "", border: "", label: "" }
|
|
3221
|
+
},
|
|
3222
|
+
outcomes: outcomesJson ?? {
|
|
3223
|
+
computed_outcomes: [],
|
|
3224
|
+
comparison_layout: { primary_card: "", status_badge: "", structural_indicators: [] }
|
|
3225
|
+
},
|
|
3226
|
+
guards: guardsJson,
|
|
3227
|
+
roles: rolesJson,
|
|
3228
|
+
kernel: kernelJson,
|
|
3229
|
+
metadata: metadataJson ?? {
|
|
3230
|
+
format_version: "1.0.0",
|
|
3231
|
+
created_at: "",
|
|
3232
|
+
last_modified: "",
|
|
3233
|
+
authoring_method: "manual-authoring"
|
|
3234
|
+
}
|
|
3235
|
+
};
|
|
3236
|
+
}
|
|
3237
|
+
async function loadWorld(worldPath) {
|
|
3238
|
+
const { stat } = await import("fs/promises");
|
|
3239
|
+
const info = await stat(worldPath);
|
|
3240
|
+
if (info.isDirectory()) {
|
|
3241
|
+
return loadWorldFromDirectory(worldPath);
|
|
3242
|
+
}
|
|
3243
|
+
throw new Error(`Cannot load world from: ${worldPath} \u2014 expected a directory`);
|
|
3244
|
+
}
|
|
3245
|
+
|
|
3246
|
+
// src/radiant/core/governance.ts
|
|
3247
|
+
async function auditGovernance(events, worldPath) {
|
|
3248
|
+
let world;
|
|
3249
|
+
try {
|
|
3250
|
+
world = await loadWorld(worldPath);
|
|
3251
|
+
} catch {
|
|
3252
|
+
return emptyAudit(events.length, "Could not load compiled worldmodel for governance audit.");
|
|
3253
|
+
}
|
|
3254
|
+
const verdicts = [];
|
|
3255
|
+
for (const ce of events) {
|
|
3256
|
+
const intent = ce.event.content?.slice(0, 500) || ce.event.kind || "activity";
|
|
3257
|
+
const scope = ce.event.metadata?.scope || void 0;
|
|
3258
|
+
try {
|
|
3259
|
+
const result = evaluateGuard(
|
|
3260
|
+
{
|
|
3261
|
+
intent,
|
|
3262
|
+
scope,
|
|
3263
|
+
actionCategory: mapKindToCategory(ce.event.kind)
|
|
3264
|
+
},
|
|
3265
|
+
world
|
|
3266
|
+
);
|
|
3267
|
+
verdicts.push({
|
|
3268
|
+
eventId: ce.event.id,
|
|
3269
|
+
domain: ce.domain,
|
|
3270
|
+
status: result.status,
|
|
3271
|
+
reason: result.reason,
|
|
3272
|
+
ruleId: result.ruleId,
|
|
3273
|
+
warning: result.warning
|
|
3274
|
+
});
|
|
3275
|
+
} catch {
|
|
3276
|
+
verdicts.push({
|
|
3277
|
+
eventId: ce.event.id,
|
|
3278
|
+
domain: ce.domain,
|
|
3279
|
+
status: "ALLOW",
|
|
3280
|
+
reason: "guard evaluation skipped (error)"
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
const human = bucketVerdicts(verdicts.filter((v) => v.domain === "life"));
|
|
3285
|
+
const cyber = bucketVerdicts(verdicts.filter((v) => v.domain === "cyber"));
|
|
3286
|
+
const joint = bucketVerdicts(verdicts.filter((v) => v.domain === "joint"));
|
|
3287
|
+
const summary = buildSummary(human, cyber, joint, events.length);
|
|
3288
|
+
return {
|
|
3289
|
+
totalEvents: events.length,
|
|
3290
|
+
human,
|
|
3291
|
+
cyber,
|
|
3292
|
+
joint,
|
|
3293
|
+
summary
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
function bucketVerdicts(verdicts) {
|
|
3297
|
+
const nonAllow = verdicts.filter((v) => v.status !== "ALLOW");
|
|
3298
|
+
return {
|
|
3299
|
+
allow: verdicts.filter((v) => v.status === "ALLOW").length,
|
|
3300
|
+
modify: verdicts.filter((v) => v.status === "MODIFY").length,
|
|
3301
|
+
block: verdicts.filter((v) => v.status === "BLOCK").length,
|
|
3302
|
+
details: nonAllow
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
function buildSummary(human, cyber, joint, total) {
|
|
3306
|
+
const humanTriggered = human.modify + human.block;
|
|
3307
|
+
const cyberTriggered = cyber.modify + cyber.block;
|
|
3308
|
+
const jointTriggered = joint.modify + joint.block;
|
|
3309
|
+
const totalTriggered = humanTriggered + cyberTriggered + jointTriggered;
|
|
3310
|
+
if (totalTriggered === 0) {
|
|
3311
|
+
return `${total} events evaluated. All passed. The cocoon held \u2014 no governance triggered.`;
|
|
3312
|
+
}
|
|
3313
|
+
const parts = [];
|
|
3314
|
+
parts.push(`${total} events evaluated. ${totalTriggered} triggered governance.`);
|
|
3315
|
+
if (humanTriggered > 0) {
|
|
3316
|
+
parts.push(`Human side: ${humanTriggered} (${human.block} blocked, ${human.modify} modified).`);
|
|
3317
|
+
}
|
|
3318
|
+
if (cyberTriggered > 0) {
|
|
3319
|
+
parts.push(`AI side: ${cyberTriggered} (${cyber.block} blocked, ${cyber.modify} modified).`);
|
|
3320
|
+
}
|
|
3321
|
+
if (jointTriggered > 0) {
|
|
3322
|
+
parts.push(`Joint work: ${jointTriggered} (${joint.block} blocked, ${joint.modify} modified).`);
|
|
3323
|
+
}
|
|
3324
|
+
if (humanTriggered > 0 && cyberTriggered > 0) {
|
|
3325
|
+
const ratio = humanTriggered / cyberTriggered;
|
|
3326
|
+
if (ratio > 2) {
|
|
3327
|
+
parts.push("Human side is testing the frame more than AI. Either the worldmodel needs calibrating for human workflows, or humans are genuinely drifting.");
|
|
3328
|
+
} else if (ratio < 0.5) {
|
|
3329
|
+
parts.push("AI side is testing the frame more than humans. Check whether the AI's output patterns match the declared invariants.");
|
|
3330
|
+
} else {
|
|
3331
|
+
parts.push("Roughly balanced between human and AI \u2014 both sides are testing the frame.");
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
return parts.join(" ");
|
|
3335
|
+
}
|
|
3336
|
+
function mapKindToCategory(kind) {
|
|
3337
|
+
if (!kind) return "other";
|
|
3338
|
+
if (kind.includes("commit") || kind.includes("merge")) return "write";
|
|
3339
|
+
if (kind.includes("review") || kind.includes("comment")) return "read";
|
|
3340
|
+
return "other";
|
|
3341
|
+
}
|
|
3342
|
+
function emptyAudit(total, reason) {
|
|
3343
|
+
return {
|
|
3344
|
+
totalEvents: total,
|
|
3345
|
+
human: { allow: 0, modify: 0, block: 0, details: [] },
|
|
3346
|
+
cyber: { allow: 0, modify: 0, block: 0, details: [] },
|
|
3347
|
+
joint: { allow: 0, modify: 0, block: 0, details: [] },
|
|
3348
|
+
summary: reason
|
|
3349
|
+
};
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
// src/radiant/memory/palace.ts
|
|
3353
|
+
var import_fs2 = require("fs");
|
|
3354
|
+
var import_path2 = require("path");
|
|
3355
|
+
function writeRead(exocortexDir, frontmatter, text) {
|
|
3356
|
+
const dir = (0, import_path2.resolve)(exocortexDir, "radiant", "reads");
|
|
3357
|
+
(0, import_fs2.mkdirSync)(dir, { recursive: true });
|
|
3358
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3359
|
+
const filename = `${date}.md`;
|
|
3360
|
+
const filepath = (0, import_path2.join)(dir, filename);
|
|
3361
|
+
const content = `${frontmatter}
|
|
3362
|
+
|
|
3363
|
+
${text}
|
|
3364
|
+
`;
|
|
3365
|
+
(0, import_fs2.writeFileSync)(filepath, content, "utf-8");
|
|
3366
|
+
return filepath;
|
|
3367
|
+
}
|
|
3368
|
+
function updateKnowledge(exocortexDir, persistence, options) {
|
|
3369
|
+
const dir = (0, import_path2.resolve)(exocortexDir, "radiant");
|
|
3370
|
+
(0, import_fs2.mkdirSync)(dir, { recursive: true });
|
|
3371
|
+
const filepath = (0, import_path2.join)(dir, "knowledge.md");
|
|
3372
|
+
const totalReads = options?.totalReads ?? 0;
|
|
3373
|
+
const existingUntriggered = loadUntriggeredCounts(filepath);
|
|
3374
|
+
const lines = [
|
|
3375
|
+
"# Radiant \u2014 Accumulated Behavioral Knowledge",
|
|
3376
|
+
"",
|
|
3377
|
+
`Last updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
3378
|
+
`Total reads: ${totalReads}`,
|
|
3379
|
+
"",
|
|
3380
|
+
"---",
|
|
3381
|
+
"",
|
|
3382
|
+
"## Evolution proposals",
|
|
3383
|
+
""
|
|
3384
|
+
];
|
|
3385
|
+
const addCandidates = persistence.filter((p) => p.occurrences >= 3);
|
|
3386
|
+
if (addCandidates.length > 0) {
|
|
3387
|
+
lines.push("### Consider adding");
|
|
3388
|
+
lines.push("");
|
|
3389
|
+
lines.push("These candidate patterns keep recurring. They are not yet in the worldmodel.");
|
|
3390
|
+
lines.push("If they represent real behavioral patterns worth tracking, add them.");
|
|
3391
|
+
lines.push("If they were temporary, dismiss them.");
|
|
3392
|
+
lines.push("");
|
|
3393
|
+
for (const p of addCandidates) {
|
|
3394
|
+
lines.push(
|
|
3395
|
+
`- **${p.name}** \u2014 observed ${p.occurrences} times (${p.dates.join(", ")}). Add to auki-strategy.worldmodel.md \u2192 Evolution Layer \u2192 Drift Behaviors (if concerning) or Aligned Behaviors (if healthy).`
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
lines.push("");
|
|
3399
|
+
}
|
|
3400
|
+
if (options?.declaredItems && options.declaredItems.length > 0) {
|
|
3401
|
+
const triggered = new Set(options.triggeredItems ?? []);
|
|
3402
|
+
const removeCandidates = [];
|
|
3403
|
+
for (const item of options.declaredItems) {
|
|
3404
|
+
if (!triggered.has(item.name)) {
|
|
3405
|
+
const prior = existingUntriggered.get(item.name) ?? 0;
|
|
3406
|
+
const count = prior + 1;
|
|
3407
|
+
existingUntriggered.set(item.name, count);
|
|
3408
|
+
if (count >= 4) {
|
|
3409
|
+
removeCandidates.push({ item, weeksSilent: count });
|
|
3410
|
+
}
|
|
3411
|
+
} else {
|
|
3412
|
+
existingUntriggered.set(item.name, 0);
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
if (removeCandidates.length > 0) {
|
|
3416
|
+
lines.push("### Consider removing");
|
|
3417
|
+
lines.push("");
|
|
3418
|
+
lines.push("These items are declared in the worldmodel but haven't triggered in multiple reads.");
|
|
3419
|
+
lines.push("Either the team has internalized them (the rule is redundant) or they haven't been tested.");
|
|
3420
|
+
lines.push("A lean worldmodel with 5 sharp invariants is stronger than a bloated one with 20.");
|
|
3421
|
+
lines.push("");
|
|
3422
|
+
for (const { item, weeksSilent } of removeCandidates) {
|
|
3423
|
+
lines.push(
|
|
3424
|
+
`- **${item.name}** (${item.type}) \u2014 hasn't triggered in ${weeksSilent} reads. Internalized or untested? Review whether it still earns its place.`
|
|
3425
|
+
);
|
|
3426
|
+
}
|
|
3427
|
+
lines.push("");
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
if (options?.triggeredItems && options.triggeredItems.length > 0) {
|
|
3431
|
+
lines.push("### Keep (recently active)");
|
|
3432
|
+
lines.push("");
|
|
3433
|
+
lines.push("These worldmodel items triggered governance in the most recent read. They're earning their place.");
|
|
3434
|
+
lines.push("");
|
|
3435
|
+
for (const name of options.triggeredItems) {
|
|
3436
|
+
lines.push(`- **${name}** \u2014 triggered this read. Holding.`);
|
|
3437
|
+
}
|
|
3438
|
+
lines.push("");
|
|
3439
|
+
}
|
|
3440
|
+
lines.push("---");
|
|
3441
|
+
lines.push("");
|
|
3442
|
+
lines.push("## Pattern persistence (all observed)");
|
|
3443
|
+
lines.push("");
|
|
3444
|
+
for (const p of persistence) {
|
|
3445
|
+
const status = p.occurrences >= 4 ? "**persistent**" : p.occurrences >= 2 ? "recurring" : "observed once";
|
|
3446
|
+
lines.push(`### ${p.name}`);
|
|
3447
|
+
lines.push(`- Status: ${status}`);
|
|
3448
|
+
lines.push(`- Observed ${p.occurrences} time${p.occurrences > 1 ? "s" : ""}: ${p.dates.join(", ")}`);
|
|
3449
|
+
lines.push("");
|
|
3450
|
+
}
|
|
3451
|
+
lines.push("---");
|
|
3452
|
+
lines.push("");
|
|
3453
|
+
lines.push("<!-- untriggered_counts (machine-readable, do not edit)");
|
|
3454
|
+
for (const [name, count] of existingUntriggered.entries()) {
|
|
3455
|
+
lines.push(`${name}=${count}`);
|
|
3456
|
+
}
|
|
3457
|
+
lines.push("-->");
|
|
3458
|
+
(0, import_fs2.writeFileSync)(filepath, lines.join("\n"), "utf-8");
|
|
3459
|
+
return filepath;
|
|
3460
|
+
}
|
|
3461
|
+
function loadUntriggeredCounts(filepath) {
|
|
3462
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3463
|
+
if (!(0, import_fs2.existsSync)(filepath)) return counts;
|
|
3464
|
+
try {
|
|
3465
|
+
const content = (0, import_fs2.readFileSync)(filepath, "utf-8");
|
|
3466
|
+
const match = content.match(
|
|
3467
|
+
/<!-- untriggered_counts[\s\S]*?-->/
|
|
3468
|
+
);
|
|
3469
|
+
if (match) {
|
|
3470
|
+
const lines = match[0].split("\n");
|
|
3471
|
+
for (const line of lines) {
|
|
3472
|
+
const eq = line.match(/^([^=]+)=(\d+)$/);
|
|
3473
|
+
if (eq) {
|
|
3474
|
+
counts.set(eq[1], parseInt(eq[2], 10));
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
} catch {
|
|
3479
|
+
}
|
|
3480
|
+
return counts;
|
|
3481
|
+
}
|
|
3482
|
+
function loadPriorReads(exocortexDir) {
|
|
3483
|
+
const dir = (0, import_path2.resolve)(exocortexDir, "radiant", "reads");
|
|
3484
|
+
if (!(0, import_fs2.existsSync)(dir)) return [];
|
|
3485
|
+
const files = (0, import_fs2.readdirSync)(dir).filter((f) => f.endsWith(".md")).sort();
|
|
3486
|
+
const reads = [];
|
|
3487
|
+
for (const filename of files) {
|
|
3488
|
+
const filepath = (0, import_path2.join)(dir, filename);
|
|
3489
|
+
const content = (0, import_fs2.readFileSync)(filepath, "utf-8");
|
|
3490
|
+
const date = filename.replace(".md", "");
|
|
3491
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
3492
|
+
const frontmatter = fmMatch ? fmMatch[1] : "";
|
|
3493
|
+
const patternNames = [];
|
|
3494
|
+
const nameMatches = frontmatter.matchAll(/- name: ["']?([^"'\n]+)["']?/g);
|
|
3495
|
+
for (const m of nameMatches) {
|
|
3496
|
+
patternNames.push(m[1].trim());
|
|
3497
|
+
}
|
|
3498
|
+
reads.push({ date, filename, patternNames, frontmatter });
|
|
3499
|
+
}
|
|
3500
|
+
return reads;
|
|
3501
|
+
}
|
|
3502
|
+
function computePersistence(priorReads, currentPatternNames) {
|
|
3503
|
+
const allPatterns = /* @__PURE__ */ new Map();
|
|
3504
|
+
for (const read of priorReads) {
|
|
3505
|
+
for (const name of read.patternNames) {
|
|
3506
|
+
const dates = allPatterns.get(name) ?? [];
|
|
3507
|
+
if (!dates.includes(read.date)) dates.push(read.date);
|
|
3508
|
+
allPatterns.set(name, dates);
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3512
|
+
for (const name of currentPatternNames) {
|
|
3513
|
+
const dates = allPatterns.get(name) ?? [];
|
|
3514
|
+
if (!dates.includes(today)) dates.push(today);
|
|
3515
|
+
allPatterns.set(name, dates);
|
|
3516
|
+
}
|
|
3517
|
+
return Array.from(allPatterns.entries()).map(([name, dates]) => ({
|
|
3518
|
+
name,
|
|
3519
|
+
occurrences: dates.length,
|
|
3520
|
+
dates: dates.sort()
|
|
3521
|
+
})).sort((a, b) => b.occurrences - a.occurrences);
|
|
3522
|
+
}
|
|
3523
|
+
function formatPriorReadsForPrompt(priorReads) {
|
|
3524
|
+
if (priorReads.length === 0) return "";
|
|
3525
|
+
const lines = [
|
|
3526
|
+
"## Prior Radiant reads (history)",
|
|
3527
|
+
"",
|
|
3528
|
+
`Radiant has run ${priorReads.length} time${priorReads.length > 1 ? "s" : ""} before on this scope.`,
|
|
3529
|
+
"If you see patterns that appeared in prior reads, note their persistence.",
|
|
3530
|
+
"Patterns that recur across 3+ reads are strong candidates for declaration in the strategy file.",
|
|
3531
|
+
""
|
|
3532
|
+
];
|
|
3533
|
+
for (const read of priorReads.slice(-4)) {
|
|
3534
|
+
lines.push(`### Read from ${read.date}`);
|
|
3535
|
+
if (read.patternNames.length > 0) {
|
|
3536
|
+
lines.push(`Patterns observed: ${read.patternNames.join(", ")}`);
|
|
3537
|
+
} else {
|
|
3538
|
+
lines.push("No patterns extracted from frontmatter.");
|
|
3539
|
+
}
|
|
3540
|
+
lines.push("");
|
|
3541
|
+
}
|
|
3542
|
+
return lines.join("\n");
|
|
3543
|
+
}
|
|
3544
|
+
|
|
1534
3545
|
// src/radiant/commands/think.ts
|
|
1535
3546
|
async function think(input) {
|
|
1536
3547
|
const lens = resolveLens(input.lensId);
|
|
@@ -1568,14 +3579,31 @@ async function emergent(input) {
|
|
|
1568
3579
|
const windowDays = input.windowDays ?? 14;
|
|
1569
3580
|
let statedIntent;
|
|
1570
3581
|
let exocortexContext;
|
|
3582
|
+
let priorReadContext = "";
|
|
1571
3583
|
if (input.exocortexPath) {
|
|
1572
3584
|
exocortexContext = readExocortex(input.exocortexPath);
|
|
1573
3585
|
const formatted = formatExocortexForPrompt(exocortexContext);
|
|
1574
3586
|
if (formatted) statedIntent = formatted;
|
|
3587
|
+
const priorReads = loadPriorReads(input.exocortexPath);
|
|
3588
|
+
if (priorReads.length > 0) {
|
|
3589
|
+
priorReadContext = formatPriorReadsForPrompt(priorReads);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
let events;
|
|
3593
|
+
let orgRepos;
|
|
3594
|
+
if (input.scope.type === "org") {
|
|
3595
|
+
const orgResult = await fetchGitHubOrgActivity(
|
|
3596
|
+
input.scope,
|
|
3597
|
+
input.githubToken,
|
|
3598
|
+
{ windowDays }
|
|
3599
|
+
);
|
|
3600
|
+
events = orgResult.events;
|
|
3601
|
+
orgRepos = orgResult.repos;
|
|
3602
|
+
} else {
|
|
3603
|
+
events = await fetchGitHubActivity(input.scope, input.githubToken, {
|
|
3604
|
+
windowDays
|
|
3605
|
+
});
|
|
1575
3606
|
}
|
|
1576
|
-
const events = await fetchGitHubActivity(input.scope, input.githubToken, {
|
|
1577
|
-
windowDays
|
|
1578
|
-
});
|
|
1579
3607
|
const classified = classifyEvents(events);
|
|
1580
3608
|
const signals = extractSignals(classified);
|
|
1581
3609
|
const scores = computeScores(signals, input.worldmodelContent !== "");
|
|
@@ -1586,11 +3614,18 @@ async function emergent(input) {
|
|
|
1586
3614
|
lens,
|
|
1587
3615
|
ai: input.ai,
|
|
1588
3616
|
canonicalPatterns: input.canonicalPatterns,
|
|
1589
|
-
statedIntent
|
|
3617
|
+
statedIntent: statedIntent ? statedIntent + (priorReadContext ? "\n\n" + priorReadContext : "") : priorReadContext || void 0
|
|
1590
3618
|
});
|
|
1591
3619
|
const rewrittenPatterns = patterns.map((p) => lens.rewrite(p));
|
|
1592
3620
|
const allDescriptions = rewrittenPatterns.map((p) => p.description).join("\n");
|
|
1593
3621
|
const voiceViolations = checkForbiddenPhrases(lens, allDescriptions);
|
|
3622
|
+
let governance;
|
|
3623
|
+
if (input.worldPath) {
|
|
3624
|
+
try {
|
|
3625
|
+
governance = await auditGovernance(classified, input.worldPath);
|
|
3626
|
+
} catch {
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
1594
3629
|
const rendered = render({
|
|
1595
3630
|
scope: input.scope,
|
|
1596
3631
|
windowDays,
|
|
@@ -1600,8 +3635,27 @@ async function emergent(input) {
|
|
|
1600
3635
|
scores,
|
|
1601
3636
|
lens,
|
|
1602
3637
|
meaning: meaning || void 0,
|
|
1603
|
-
move: move || void 0
|
|
3638
|
+
move: move || void 0,
|
|
3639
|
+
governance
|
|
1604
3640
|
});
|
|
3641
|
+
if (input.exocortexPath) {
|
|
3642
|
+
try {
|
|
3643
|
+
const readPath = writeRead(input.exocortexPath, rendered.frontmatter, rendered.text);
|
|
3644
|
+
const priorReads = loadPriorReads(input.exocortexPath);
|
|
3645
|
+
const currentPatternNames = rewrittenPatterns.map((p) => p.name);
|
|
3646
|
+
const persistence = computePersistence(priorReads, currentPatternNames);
|
|
3647
|
+
const triggeredItems = governance ? [
|
|
3648
|
+
...governance.human.details.map((d) => d.ruleId).filter(Boolean),
|
|
3649
|
+
...governance.cyber.details.map((d) => d.ruleId).filter(Boolean),
|
|
3650
|
+
...governance.joint.details.map((d) => d.ruleId).filter(Boolean)
|
|
3651
|
+
] : [];
|
|
3652
|
+
updateKnowledge(input.exocortexPath, persistence, {
|
|
3653
|
+
triggeredItems,
|
|
3654
|
+
totalReads: priorReads.length + 1
|
|
3655
|
+
});
|
|
3656
|
+
} catch {
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
1605
3659
|
return {
|
|
1606
3660
|
text: rendered.text,
|
|
1607
3661
|
frontmatter: rendered.frontmatter,
|
|
@@ -1668,33 +3722,49 @@ var RADIANT_PACKAGE_VERSION = "0.0.0";
|
|
|
1668
3722
|
DEFAULT_SIGNAL_EXTRACTORS,
|
|
1669
3723
|
LENSES,
|
|
1670
3724
|
RADIANT_PACKAGE_VERSION,
|
|
3725
|
+
auditGovernance,
|
|
1671
3726
|
aukiBuilderLens,
|
|
1672
3727
|
checkForbiddenPhrases,
|
|
1673
3728
|
classifyActorDomain,
|
|
1674
3729
|
classifyEvents,
|
|
1675
3730
|
composeSystemPrompt,
|
|
3731
|
+
computePersistence,
|
|
1676
3732
|
createAnthropicAI,
|
|
1677
3733
|
createMockAI,
|
|
1678
3734
|
createMockGitHubAdapter,
|
|
1679
3735
|
emergent,
|
|
1680
3736
|
extractSignals,
|
|
3737
|
+
fetchDiscordActivity,
|
|
1681
3738
|
fetchGitHubActivity,
|
|
3739
|
+
fetchGitHubOrgActivity,
|
|
3740
|
+
fetchNotionActivity,
|
|
3741
|
+
fetchSlackActivity,
|
|
3742
|
+
formatDiscordSignalsForPrompt,
|
|
1682
3743
|
formatExocortexForPrompt,
|
|
3744
|
+
formatNotionSignalsForPrompt,
|
|
3745
|
+
formatPriorReadsForPrompt,
|
|
1683
3746
|
formatScope,
|
|
3747
|
+
formatSlackSignalsForPrompt,
|
|
3748
|
+
formatTeamExocorticesForPrompt,
|
|
1684
3749
|
getLens,
|
|
1685
3750
|
interpretPatterns,
|
|
1686
3751
|
isPresent,
|
|
1687
3752
|
isScored,
|
|
1688
3753
|
isSentinel,
|
|
1689
3754
|
listLenses,
|
|
3755
|
+
loadPriorReads,
|
|
1690
3756
|
parseRepoScope,
|
|
3757
|
+
parseScope,
|
|
1691
3758
|
presenceAverage,
|
|
1692
3759
|
readExocortex,
|
|
3760
|
+
readTeamExocortices,
|
|
1693
3761
|
render,
|
|
1694
3762
|
scoreComposite,
|
|
1695
3763
|
scoreCyber,
|
|
1696
3764
|
scoreLife,
|
|
1697
3765
|
scoreNeuroVerse,
|
|
1698
3766
|
summarizeExocortex,
|
|
1699
|
-
think
|
|
3767
|
+
think,
|
|
3768
|
+
updateKnowledge,
|
|
3769
|
+
writeRead
|
|
1700
3770
|
});
|