@neuroverseos/governance 0.7.0 → 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 -4
- package/dist/{chunk-T6EQ7ZBG.js → chunk-MC6O5GV5.js} +122 -11
- package/dist/cli/neuroverse.cjs +106 -21
- package/dist/cli/radiant.cjs +97 -12
- package/dist/cli/radiant.js +23 -5
- package/dist/radiant/index.cjs +531 -10
- package/dist/radiant/index.d.cts +247 -14
- package/dist/radiant/index.d.ts +247 -14
- package/dist/radiant/index.js +409 -1
- package/dist/{server-BXMC5NOE.js → server-DFNY5N5A.js} +1 -1
- package/package.json +1 -1
package/dist/radiant/index.js
CHANGED
|
@@ -13,17 +13,21 @@ import {
|
|
|
13
13
|
emergent,
|
|
14
14
|
extractSignals,
|
|
15
15
|
fetchGitHubActivity,
|
|
16
|
+
fetchGitHubOrgActivity,
|
|
16
17
|
formatExocortexForPrompt,
|
|
17
18
|
formatPriorReadsForPrompt,
|
|
18
19
|
formatScope,
|
|
20
|
+
formatTeamExocorticesForPrompt,
|
|
19
21
|
interpretPatterns,
|
|
20
22
|
isPresent,
|
|
21
23
|
isScored,
|
|
22
24
|
isSentinel,
|
|
23
25
|
loadPriorReads,
|
|
24
26
|
parseRepoScope,
|
|
27
|
+
parseScope,
|
|
25
28
|
presenceAverage,
|
|
26
29
|
readExocortex,
|
|
30
|
+
readTeamExocortices,
|
|
27
31
|
render,
|
|
28
32
|
scoreComposite,
|
|
29
33
|
scoreCyber,
|
|
@@ -33,7 +37,7 @@ import {
|
|
|
33
37
|
think,
|
|
34
38
|
updateKnowledge,
|
|
35
39
|
writeRead
|
|
36
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-MC6O5GV5.js";
|
|
37
41
|
import {
|
|
38
42
|
LENSES,
|
|
39
43
|
aukiBuilderLens,
|
|
@@ -45,6 +49,400 @@ import "../chunk-ZAF6JH23.js";
|
|
|
45
49
|
import "../chunk-QLPTHTVB.js";
|
|
46
50
|
import "../chunk-QWGCMQQD.js";
|
|
47
51
|
|
|
52
|
+
// src/radiant/adapters/discord.ts
|
|
53
|
+
async function fetchDiscordActivity(guildId, token, options = {}) {
|
|
54
|
+
const windowDays = options.windowDays ?? 14;
|
|
55
|
+
const perChannel = options.perChannel ?? 100;
|
|
56
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
57
|
+
const headers = {
|
|
58
|
+
Authorization: `Bot ${token}`,
|
|
59
|
+
"Content-Type": "application/json"
|
|
60
|
+
};
|
|
61
|
+
const channels = await fetchJSON(
|
|
62
|
+
`https://discord.com/api/v10/guilds/${guildId}/channels`,
|
|
63
|
+
headers
|
|
64
|
+
);
|
|
65
|
+
const textChannels = channels.filter((c) => {
|
|
66
|
+
if (c.type !== 0) return false;
|
|
67
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
68
|
+
return options.channelIds.includes(c.id);
|
|
69
|
+
}
|
|
70
|
+
if (options.visibility === "public") {
|
|
71
|
+
return !c.name.startsWith("private-") && !c.nsfw;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
});
|
|
75
|
+
const events = [];
|
|
76
|
+
let totalMessages = 0;
|
|
77
|
+
let helpRequests = 0;
|
|
78
|
+
let unresolvedThreads = 0;
|
|
79
|
+
let newcomerMessages = 0;
|
|
80
|
+
const responseTimes = [];
|
|
81
|
+
const participants = /* @__PURE__ */ new Set();
|
|
82
|
+
const knownParticipants = /* @__PURE__ */ new Set();
|
|
83
|
+
const topicCounts = /* @__PURE__ */ new Map();
|
|
84
|
+
for (const channel of textChannels.slice(0, 15)) {
|
|
85
|
+
try {
|
|
86
|
+
const messages = await fetchJSON(
|
|
87
|
+
`https://discord.com/api/v10/channels/${channel.id}/messages?limit=${perChannel}`,
|
|
88
|
+
headers
|
|
89
|
+
);
|
|
90
|
+
const inWindow = messages.filter(
|
|
91
|
+
(m) => new Date(m.timestamp) >= since
|
|
92
|
+
);
|
|
93
|
+
totalMessages += inWindow.length;
|
|
94
|
+
const topic = channel.name.replace(/-/g, " ");
|
|
95
|
+
topicCounts.set(topic, (topicCounts.get(topic) ?? 0) + inWindow.length);
|
|
96
|
+
for (const msg of inWindow) {
|
|
97
|
+
const actor = mapDiscordUser(msg.author);
|
|
98
|
+
participants.add(actor.id);
|
|
99
|
+
const lowerContent = msg.content.toLowerCase();
|
|
100
|
+
if (lowerContent.includes("help") || lowerContent.includes("stuck") || lowerContent.includes("how do i") || lowerContent.includes("anyone know")) {
|
|
101
|
+
helpRequests++;
|
|
102
|
+
}
|
|
103
|
+
if (msg.referenced_message) {
|
|
104
|
+
const refTime = new Date(msg.referenced_message.timestamp).getTime();
|
|
105
|
+
const msgTime = new Date(msg.timestamp).getTime();
|
|
106
|
+
const diffMinutes = (msgTime - refTime) / 6e4;
|
|
107
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
108
|
+
responseTimes.push(diffMinutes);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
events.push({
|
|
112
|
+
id: `discord-${msg.id}`,
|
|
113
|
+
timestamp: msg.timestamp,
|
|
114
|
+
actor,
|
|
115
|
+
kind: "discord_message",
|
|
116
|
+
content: msg.content.slice(0, 500),
|
|
117
|
+
respondsTo: msg.referenced_message ? {
|
|
118
|
+
eventId: `discord-${msg.referenced_message.id}`,
|
|
119
|
+
actor: mapDiscordUser(msg.referenced_message.author)
|
|
120
|
+
} : void 0,
|
|
121
|
+
metadata: {
|
|
122
|
+
channel: channel.name,
|
|
123
|
+
guildId
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const avgResponseMinutes = responseTimes.length > 0 ? responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length : null;
|
|
131
|
+
const topTopics = [...topicCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([t]) => t);
|
|
132
|
+
const signals = {
|
|
133
|
+
totalMessages,
|
|
134
|
+
activeChannels: textChannels.length,
|
|
135
|
+
uniqueParticipants: participants.size,
|
|
136
|
+
avgResponseMinutes: avgResponseMinutes ? Math.round(avgResponseMinutes) : null,
|
|
137
|
+
helpRequests,
|
|
138
|
+
unresolvedThreads,
|
|
139
|
+
topTopics,
|
|
140
|
+
newcomerMessages
|
|
141
|
+
};
|
|
142
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
143
|
+
return { events, signals };
|
|
144
|
+
}
|
|
145
|
+
function formatDiscordSignalsForPrompt(signals) {
|
|
146
|
+
if (signals.totalMessages === 0) return "";
|
|
147
|
+
const lines = [
|
|
148
|
+
"## Discord Activity (conversational behavior)",
|
|
149
|
+
"",
|
|
150
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
151
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
152
|
+
];
|
|
153
|
+
if (signals.avgResponseMinutes !== null) {
|
|
154
|
+
lines.push(`Average response time: ${signals.avgResponseMinutes} minutes.`);
|
|
155
|
+
}
|
|
156
|
+
if (signals.helpRequests > 0) {
|
|
157
|
+
lines.push(`${signals.helpRequests} help requests detected.`);
|
|
158
|
+
}
|
|
159
|
+
if (signals.unresolvedThreads > 0) {
|
|
160
|
+
lines.push(`${signals.unresolvedThreads} unresolved threads.`);
|
|
161
|
+
}
|
|
162
|
+
if (signals.topTopics.length > 0) {
|
|
163
|
+
lines.push(`Top discussion topics: ${signals.topTopics.join(", ")}.`);
|
|
164
|
+
}
|
|
165
|
+
lines.push("");
|
|
166
|
+
lines.push("Compare conversational activity against GitHub shipping activity.");
|
|
167
|
+
lines.push("Where debates happen in Discord but nothing ships in GitHub, name the gap.");
|
|
168
|
+
lines.push("Where work ships in GitHub but nobody discusses it in Discord, name the visibility gap.");
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
function mapDiscordUser(user) {
|
|
172
|
+
return {
|
|
173
|
+
id: user.username,
|
|
174
|
+
kind: user.bot ? "bot" : "human",
|
|
175
|
+
name: user.username
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function fetchJSON(url, headers) {
|
|
179
|
+
const res = await fetch(url, { headers });
|
|
180
|
+
if (!res.ok) {
|
|
181
|
+
if (res.status === 404 || res.status === 403) return [];
|
|
182
|
+
throw new Error(`Discord API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
183
|
+
}
|
|
184
|
+
return await res.json();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/radiant/adapters/slack.ts
|
|
188
|
+
async function fetchSlackActivity(token, options = {}) {
|
|
189
|
+
const windowDays = options.windowDays ?? 14;
|
|
190
|
+
const perChannel = options.perChannel ?? 100;
|
|
191
|
+
const oldest = String(
|
|
192
|
+
Math.floor((Date.now() - windowDays * 24 * 60 * 60 * 1e3) / 1e3)
|
|
193
|
+
);
|
|
194
|
+
const headers = {
|
|
195
|
+
Authorization: `Bearer ${token}`,
|
|
196
|
+
"Content-Type": "application/json"
|
|
197
|
+
};
|
|
198
|
+
const channelsResponse = await fetchSlackAPI("https://slack.com/api/conversations.list?types=public_channel&limit=200", headers);
|
|
199
|
+
let channels = channelsResponse.channels ?? [];
|
|
200
|
+
if (options.channelIds && options.channelIds.length > 0) {
|
|
201
|
+
const ids = new Set(options.channelIds);
|
|
202
|
+
channels = channels.filter((c) => ids.has(c.id));
|
|
203
|
+
}
|
|
204
|
+
if (options.visibility === "public") {
|
|
205
|
+
channels = channels.filter((c) => !c.is_private && !c.is_archived);
|
|
206
|
+
}
|
|
207
|
+
const events = [];
|
|
208
|
+
let totalMessages = 0;
|
|
209
|
+
let reactionCount = 0;
|
|
210
|
+
let unresolvedThreads = 0;
|
|
211
|
+
const responseTimes = [];
|
|
212
|
+
const participants = /* @__PURE__ */ new Set();
|
|
213
|
+
const externalParticipants = /* @__PURE__ */ new Set();
|
|
214
|
+
const channelMessageCounts = /* @__PURE__ */ new Map();
|
|
215
|
+
for (const channel of channels.slice(0, 15)) {
|
|
216
|
+
try {
|
|
217
|
+
const historyResponse = await fetchSlackAPI(
|
|
218
|
+
`https://slack.com/api/conversations.history?channel=${channel.id}&limit=${perChannel}&oldest=${oldest}`,
|
|
219
|
+
headers
|
|
220
|
+
);
|
|
221
|
+
const messages = historyResponse.messages ?? [];
|
|
222
|
+
totalMessages += messages.length;
|
|
223
|
+
channelMessageCounts.set(channel.name, messages.length);
|
|
224
|
+
for (const msg of messages) {
|
|
225
|
+
if (msg.subtype === "channel_join" || msg.subtype === "channel_leave") continue;
|
|
226
|
+
const actor = mapSlackUser(msg.user ?? "unknown");
|
|
227
|
+
participants.add(actor.id);
|
|
228
|
+
if (msg.reactions) {
|
|
229
|
+
reactionCount += msg.reactions.reduce(
|
|
230
|
+
(sum, r) => sum + (r.count ?? 0),
|
|
231
|
+
0
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (msg.thread_ts && msg.thread_ts !== msg.ts) {
|
|
235
|
+
const parentTs = parseFloat(msg.thread_ts) * 1e3;
|
|
236
|
+
const msgTs = parseFloat(msg.ts) * 1e3;
|
|
237
|
+
const diffMinutes = (msgTs - parentTs) / 6e4;
|
|
238
|
+
if (diffMinutes > 0 && diffMinutes < 10080) {
|
|
239
|
+
responseTimes.push(diffMinutes);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (msg.thread_ts === msg.ts && (!msg.reply_count || msg.reply_count === 0)) {
|
|
243
|
+
if (msg.text && (msg.text.includes("?") || msg.text.toLowerCase().includes("help"))) {
|
|
244
|
+
unresolvedThreads++;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const timestamp = new Date(parseFloat(msg.ts) * 1e3).toISOString();
|
|
248
|
+
events.push({
|
|
249
|
+
id: `slack-${msg.ts}`,
|
|
250
|
+
timestamp,
|
|
251
|
+
actor,
|
|
252
|
+
kind: "slack_message",
|
|
253
|
+
content: (msg.text ?? "").slice(0, 500),
|
|
254
|
+
respondsTo: msg.thread_ts && msg.thread_ts !== msg.ts ? {
|
|
255
|
+
eventId: `slack-${msg.thread_ts}`,
|
|
256
|
+
actor: { id: "thread-parent", kind: "unknown" }
|
|
257
|
+
} : void 0,
|
|
258
|
+
metadata: {
|
|
259
|
+
channel: channel.name,
|
|
260
|
+
isPrivate: channel.is_private,
|
|
261
|
+
hasReactions: (msg.reactions?.length ?? 0) > 0
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const avgResponseMinutes = responseTimes.length > 0 ? Math.round(responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length) : null;
|
|
269
|
+
const topChannels = [...channelMessageCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([name]) => name);
|
|
270
|
+
const signals = {
|
|
271
|
+
totalMessages,
|
|
272
|
+
activeChannels: channelMessageCounts.size,
|
|
273
|
+
uniqueParticipants: participants.size,
|
|
274
|
+
avgResponseMinutes,
|
|
275
|
+
externalParticipants: externalParticipants.size,
|
|
276
|
+
unresolvedThreads,
|
|
277
|
+
topChannels,
|
|
278
|
+
reactionCount
|
|
279
|
+
};
|
|
280
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
281
|
+
return { events, signals };
|
|
282
|
+
}
|
|
283
|
+
function formatSlackSignalsForPrompt(signals) {
|
|
284
|
+
if (signals.totalMessages === 0) return "";
|
|
285
|
+
const lines = [
|
|
286
|
+
"## Slack Activity (external coordination)",
|
|
287
|
+
"",
|
|
288
|
+
`${signals.totalMessages} messages across ${signals.activeChannels} channels.`,
|
|
289
|
+
`${signals.uniqueParticipants} unique participants.`
|
|
290
|
+
];
|
|
291
|
+
if (signals.avgResponseMinutes !== null) {
|
|
292
|
+
lines.push(`Average thread response time: ${signals.avgResponseMinutes} minutes.`);
|
|
293
|
+
}
|
|
294
|
+
if (signals.unresolvedThreads > 0) {
|
|
295
|
+
lines.push(`${signals.unresolvedThreads} questions/threads with no reply.`);
|
|
296
|
+
}
|
|
297
|
+
if (signals.reactionCount > 0) {
|
|
298
|
+
lines.push(`${signals.reactionCount} reactions (engagement signal).`);
|
|
299
|
+
}
|
|
300
|
+
if (signals.topChannels.length > 0) {
|
|
301
|
+
lines.push(`Most active channels: ${signals.topChannels.join(", ")}.`);
|
|
302
|
+
}
|
|
303
|
+
lines.push("");
|
|
304
|
+
lines.push("Slack carries external coordination \u2014 partner and client communication.");
|
|
305
|
+
lines.push("Compare partner engagement against internal activity. Where partners are");
|
|
306
|
+
lines.push("active but internal follow-through is low, name the gap.");
|
|
307
|
+
return lines.join("\n");
|
|
308
|
+
}
|
|
309
|
+
function mapSlackUser(userId) {
|
|
310
|
+
return {
|
|
311
|
+
id: userId,
|
|
312
|
+
kind: "human",
|
|
313
|
+
name: userId
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
async function fetchSlackAPI(url, headers) {
|
|
317
|
+
const res = await fetch(url, { headers });
|
|
318
|
+
if (!res.ok) {
|
|
319
|
+
throw new Error(`Slack API error ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
320
|
+
}
|
|
321
|
+
const data = await res.json();
|
|
322
|
+
if (!data.ok) {
|
|
323
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
324
|
+
}
|
|
325
|
+
return data;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/radiant/adapters/notion.ts
|
|
329
|
+
async function fetchNotionActivity(token, options = {}) {
|
|
330
|
+
const windowDays = options.windowDays ?? 14;
|
|
331
|
+
const maxPages = options.maxPages ?? 100;
|
|
332
|
+
const since = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1e3);
|
|
333
|
+
const headers = {
|
|
334
|
+
Authorization: `Bearer ${token}`,
|
|
335
|
+
"Notion-Version": "2022-06-28",
|
|
336
|
+
"Content-Type": "application/json"
|
|
337
|
+
};
|
|
338
|
+
const searchResponse = await fetchNotionAPI("https://api.notion.com/v1/search", headers, {
|
|
339
|
+
method: "POST",
|
|
340
|
+
body: JSON.stringify({
|
|
341
|
+
filter: { property: "object", value: "page" },
|
|
342
|
+
sort: { direction: "descending", timestamp: "last_edited_time" },
|
|
343
|
+
page_size: maxPages
|
|
344
|
+
})
|
|
345
|
+
});
|
|
346
|
+
const pages = searchResponse.results ?? [];
|
|
347
|
+
const events = [];
|
|
348
|
+
const editors = /* @__PURE__ */ new Set();
|
|
349
|
+
let pagesCreated = 0;
|
|
350
|
+
let pagesUpdated = 0;
|
|
351
|
+
let stalePages = 0;
|
|
352
|
+
const editAges = [];
|
|
353
|
+
const topPages = [];
|
|
354
|
+
const now = Date.now();
|
|
355
|
+
for (const page of pages) {
|
|
356
|
+
const lastEdited = new Date(page.last_edited_time);
|
|
357
|
+
const created = new Date(page.created_time);
|
|
358
|
+
const daysSinceEdit = (now - lastEdited.getTime()) / (24 * 60 * 60 * 1e3);
|
|
359
|
+
editAges.push(daysSinceEdit);
|
|
360
|
+
if (daysSinceEdit > 30) stalePages++;
|
|
361
|
+
const title = extractTitle(page);
|
|
362
|
+
const editorId = page.last_edited_by?.id ?? "unknown";
|
|
363
|
+
editors.add(editorId);
|
|
364
|
+
if (lastEdited >= since) {
|
|
365
|
+
const isNew = created >= since;
|
|
366
|
+
if (isNew) pagesCreated++;
|
|
367
|
+
else pagesUpdated++;
|
|
368
|
+
topPages.push({ title, editedAt: page.last_edited_time });
|
|
369
|
+
events.push({
|
|
370
|
+
id: `notion-${page.id}`,
|
|
371
|
+
timestamp: page.last_edited_time,
|
|
372
|
+
actor: {
|
|
373
|
+
id: editorId,
|
|
374
|
+
kind: "human",
|
|
375
|
+
name: editorId
|
|
376
|
+
},
|
|
377
|
+
kind: isNew ? "doc_created" : "doc_updated",
|
|
378
|
+
content: `${isNew ? "Created" : "Updated"}: ${title}`,
|
|
379
|
+
metadata: {
|
|
380
|
+
pageId: page.id,
|
|
381
|
+
url: page.url,
|
|
382
|
+
createdAt: page.created_time
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const avgDaysSinceEdit = editAges.length > 0 ? Math.round(editAges.reduce((a, b) => a + b, 0) / editAges.length) : null;
|
|
388
|
+
const signals = {
|
|
389
|
+
pagesActive: pagesCreated + pagesUpdated,
|
|
390
|
+
pagesCreated,
|
|
391
|
+
pagesUpdated,
|
|
392
|
+
uniqueEditors: editors.size,
|
|
393
|
+
stalePages,
|
|
394
|
+
avgDaysSinceEdit,
|
|
395
|
+
topPages: topPages.slice(0, 5).map((p) => p.title)
|
|
396
|
+
};
|
|
397
|
+
events.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
398
|
+
return { events, signals };
|
|
399
|
+
}
|
|
400
|
+
function formatNotionSignalsForPrompt(signals) {
|
|
401
|
+
if (signals.pagesActive === 0 && signals.stalePages === 0) return "";
|
|
402
|
+
const lines = [
|
|
403
|
+
"## Notion Activity (documentation behavior)",
|
|
404
|
+
"",
|
|
405
|
+
`${signals.pagesActive} pages active in window (${signals.pagesCreated} created, ${signals.pagesUpdated} updated).`,
|
|
406
|
+
`${signals.uniqueEditors} unique editors.`
|
|
407
|
+
];
|
|
408
|
+
if (signals.stalePages > 0) {
|
|
409
|
+
lines.push(`${signals.stalePages} pages haven't been touched in 30+ days.`);
|
|
410
|
+
}
|
|
411
|
+
if (signals.avgDaysSinceEdit !== null) {
|
|
412
|
+
lines.push(`Average page age since last edit: ${signals.avgDaysSinceEdit} days.`);
|
|
413
|
+
}
|
|
414
|
+
if (signals.topPages.length > 0) {
|
|
415
|
+
lines.push(`Recently active pages: ${signals.topPages.join(", ")}.`);
|
|
416
|
+
}
|
|
417
|
+
lines.push("");
|
|
418
|
+
lines.push("Documentation is how the team crystallizes and shares knowledge.");
|
|
419
|
+
lines.push("High code velocity + low documentation = building without recording.");
|
|
420
|
+
lines.push("High documentation + low code = planning without shipping.");
|
|
421
|
+
lines.push("Compare Notion activity against GitHub and Discord to find the balance.");
|
|
422
|
+
return lines.join("\n");
|
|
423
|
+
}
|
|
424
|
+
function extractTitle(page) {
|
|
425
|
+
for (const prop of Object.values(page.properties)) {
|
|
426
|
+
if (prop.type === "title" && prop.title) {
|
|
427
|
+
return prop.title.map((t) => t.plain_text).join("") || "Untitled";
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return "Untitled";
|
|
431
|
+
}
|
|
432
|
+
async function fetchNotionAPI(url, headers, init) {
|
|
433
|
+
const res = await fetch(url, {
|
|
434
|
+
method: init?.method ?? "GET",
|
|
435
|
+
headers,
|
|
436
|
+
body: init?.body
|
|
437
|
+
});
|
|
438
|
+
if (!res.ok) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`Notion API error ${res.status}: ${(await res.text()).slice(0, 300)}`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
return await res.json();
|
|
444
|
+
}
|
|
445
|
+
|
|
48
446
|
// src/radiant/index.ts
|
|
49
447
|
var RADIANT_PACKAGE_VERSION = "0.0.0";
|
|
50
448
|
export {
|
|
@@ -64,10 +462,18 @@ export {
|
|
|
64
462
|
createMockGitHubAdapter,
|
|
65
463
|
emergent,
|
|
66
464
|
extractSignals,
|
|
465
|
+
fetchDiscordActivity,
|
|
67
466
|
fetchGitHubActivity,
|
|
467
|
+
fetchGitHubOrgActivity,
|
|
468
|
+
fetchNotionActivity,
|
|
469
|
+
fetchSlackActivity,
|
|
470
|
+
formatDiscordSignalsForPrompt,
|
|
68
471
|
formatExocortexForPrompt,
|
|
472
|
+
formatNotionSignalsForPrompt,
|
|
69
473
|
formatPriorReadsForPrompt,
|
|
70
474
|
formatScope,
|
|
475
|
+
formatSlackSignalsForPrompt,
|
|
476
|
+
formatTeamExocorticesForPrompt,
|
|
71
477
|
getLens,
|
|
72
478
|
interpretPatterns,
|
|
73
479
|
isPresent,
|
|
@@ -76,8 +482,10 @@ export {
|
|
|
76
482
|
listLenses,
|
|
77
483
|
loadPriorReads,
|
|
78
484
|
parseRepoScope,
|
|
485
|
+
parseScope,
|
|
79
486
|
presenceAverage,
|
|
80
487
|
readExocortex,
|
|
488
|
+
readTeamExocortices,
|
|
81
489
|
render,
|
|
82
490
|
scoreComposite,
|
|
83
491
|
scoreCyber,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neuroverseos/governance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Deterministic governance engine for AI agents — enforce worlds (permanent rules) and plans (mission constraints) with full audit trace",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|